summaryrefslogtreecommitdiffstats
path: root/db
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@wolfbeast.com>2019-12-07 10:20:41 +0100
committerwolfbeast <mcwerewolf@wolfbeast.com>2019-12-07 10:20:41 +0100
commit0fddf6e728ddea66a463e1ccd007aa9d48498905 (patch)
tree65e28a16bbfcf1747ca41a6a808136ee578735d9 /db
parent210d6a87a2759887ce286288ab0815cbd0439e5a (diff)
parent18159927e8f37a1858f9757803b20744fcfff505 (diff)
downloadUXP-0fddf6e728ddea66a463e1ccd007aa9d48498905.tar
UXP-0fddf6e728ddea66a463e1ccd007aa9d48498905.tar.gz
UXP-0fddf6e728ddea66a463e1ccd007aa9d48498905.tar.lz
UXP-0fddf6e728ddea66a463e1ccd007aa9d48498905.tar.xz
UXP-0fddf6e728ddea66a463e1ccd007aa9d48498905.zip
Merge branch 'release' into Pale_Moon-release
Diffstat (limited to 'db')
-rw-r--r--db/mork/build/moz.build24
-rw-r--r--db/mork/build/nsIMdbFactoryFactory.h30
-rw-r--r--db/mork/build/nsMorkCID.h21
-rw-r--r--db/mork/build/nsMorkFactory.cpp56
-rw-r--r--db/mork/moz.build11
-rw-r--r--db/mork/public/mdb.h2512
-rw-r--r--db/mork/public/moz.build9
-rw-r--r--db/mork/src/mork.h247
-rw-r--r--db/mork/src/morkArray.cpp297
-rw-r--r--db/mork/src/morkArray.h98
-rw-r--r--db/mork/src/morkAtom.cpp523
-rw-r--r--db/mork/src/morkAtom.h365
-rw-r--r--db/mork/src/morkAtomMap.cpp427
-rw-r--r--db/mork/src/morkAtomMap.h365
-rw-r--r--db/mork/src/morkAtomSpace.cpp270
-rw-r--r--db/mork/src/morkAtomSpace.h219
-rw-r--r--db/mork/src/morkBead.cpp425
-rw-r--r--db/mork/src/morkBead.h245
-rw-r--r--db/mork/src/morkBlob.cpp110
-rw-r--r--db/mork/src/morkBlob.h141
-rw-r--r--db/mork/src/morkBuilder.cpp1031
-rw-r--r--db/mork/src/morkBuilder.h303
-rw-r--r--db/mork/src/morkCell.cpp114
-rw-r--r--db/mork/src/morkCell.h89
-rw-r--r--db/mork/src/morkCellObject.cpp530
-rw-r--r--db/mork/src/morkCellObject.h173
-rw-r--r--db/mork/src/morkCh.cpp233
-rw-r--r--db/mork/src/morkCh.h126
-rw-r--r--db/mork/src/morkConfig.cpp201
-rw-r--r--db/mork/src/morkConfig.h157
-rw-r--r--db/mork/src/morkCursor.cpp201
-rw-r--r--db/mork/src/morkCursor.h127
-rw-r--r--db/mork/src/morkDeque.cpp288
-rw-r--r--db/mork/src/morkDeque.h239
-rw-r--r--db/mork/src/morkEnv.cpp615
-rw-r--r--db/mork/src/morkEnv.h218
-rw-r--r--db/mork/src/morkFactory.cpp610
-rw-r--r--db/mork/src/morkFactory.h203
-rw-r--r--db/mork/src/morkFile.cpp874
-rw-r--r--db/mork/src/morkFile.h355
-rw-r--r--db/mork/src/morkHandle.cpp423
-rw-r--r--db/mork/src/morkHandle.h176
-rw-r--r--db/mork/src/morkIntMap.cpp238
-rw-r--r--db/mork/src/morkIntMap.h145
-rw-r--r--db/mork/src/morkMap.cpp953
-rw-r--r--db/mork/src/morkMap.h394
-rw-r--r--db/mork/src/morkNode.cpp592
-rw-r--r--db/mork/src/morkNode.h292
-rw-r--r--db/mork/src/morkNodeMap.cpp155
-rw-r--r--db/mork/src/morkNodeMap.h98
-rw-r--r--db/mork/src/morkObject.cpp206
-rw-r--r--db/mork/src/morkObject.h143
-rw-r--r--db/mork/src/morkParser.cpp1568
-rw-r--r--db/mork/src/morkParser.h533
-rw-r--r--db/mork/src/morkPool.cpp552
-rw-r--r--db/mork/src/morkPool.h152
-rw-r--r--db/mork/src/morkPortTableCursor.cpp430
-rw-r--r--db/mork/src/morkPortTableCursor.h141
-rw-r--r--db/mork/src/morkProbeMap.cpp1234
-rw-r--r--db/mork/src/morkProbeMap.h431
-rw-r--r--db/mork/src/morkQuickSort.cpp187
-rw-r--r--db/mork/src/morkQuickSort.h25
-rw-r--r--db/mork/src/morkRow.cpp939
-rw-r--r--db/mork/src/morkRow.h226
-rw-r--r--db/mork/src/morkRowCellCursor.cpp253
-rw-r--r--db/mork/src/morkRowCellCursor.h116
-rw-r--r--db/mork/src/morkRowMap.cpp297
-rw-r--r--db/mork/src/morkRowMap.h211
-rw-r--r--db/mork/src/morkRowObject.cpp622
-rw-r--r--db/mork/src/morkRowObject.h200
-rw-r--r--db/mork/src/morkRowSpace.cpp632
-rw-r--r--db/mork/src/morkRowSpace.h233
-rw-r--r--db/mork/src/morkSearchRowCursor.cpp169
-rw-r--r--db/mork/src/morkSearchRowCursor.h105
-rw-r--r--db/mork/src/morkSink.cpp292
-rw-r--r--db/mork/src/morkSink.h161
-rw-r--r--db/mork/src/morkSpace.cpp152
-rw-r--r--db/mork/src/morkSpace.h110
-rw-r--r--db/mork/src/morkStore.cpp2290
-rw-r--r--db/mork/src/morkStore.h768
-rw-r--r--db/mork/src/morkStream.cpp859
-rw-r--r--db/mork/src/morkStream.h251
-rw-r--r--db/mork/src/morkTable.cpp1610
-rw-r--r--db/mork/src/morkTable.h729
-rw-r--r--db/mork/src/morkTableRowCursor.cpp493
-rw-r--r--db/mork/src/morkTableRowCursor.h146
-rw-r--r--db/mork/src/morkThumb.cpp523
-rw-r--r--db/mork/src/morkThumb.h180
-rw-r--r--db/mork/src/morkUniqRowCursor.h94
-rw-r--r--db/mork/src/morkWriter.cpp2206
-rw-r--r--db/mork/src/morkWriter.h343
-rw-r--r--db/mork/src/morkYarn.cpp75
-rw-r--r--db/mork/src/morkYarn.h75
-rw-r--r--db/mork/src/morkZone.cpp527
-rw-r--r--db/mork/src/morkZone.h321
-rw-r--r--db/mork/src/moz.build55
-rw-r--r--db/mork/src/orkinHeap.cpp84
-rw-r--r--db/mork/src/orkinHeap.h52
-rw-r--r--db/moz.build11
-rw-r--r--db/sqlite3/src/sqlite3.c9191
-rw-r--r--db/sqlite3/src/sqlite3.h81
101 files changed, 43866 insertions, 3966 deletions
diff --git a/db/mork/build/moz.build b/db/mork/build/moz.build
new file mode 100644
index 000000000..f77162cef
--- /dev/null
+++ b/db/mork/build/moz.build
@@ -0,0 +1,24 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsIMdbFactoryFactory.h',
+ 'nsMorkCID.h',
+]
+
+SOURCES += [
+ 'nsMorkFactory.cpp',
+]
+
+if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']:
+ XPCOMBinaryComponent('mork')
+ USE_LIBS += [
+ 'nspr',
+ 'xpcomglue_s',
+ 'xul',
+ ]
+else:
+ Library('mork')
+ FINAL_LIBRARY = 'xul'
diff --git a/db/mork/build/nsIMdbFactoryFactory.h b/db/mork/build/nsIMdbFactoryFactory.h
new file mode 100644
index 000000000..8b5294396
--- /dev/null
+++ b/db/mork/build/nsIMdbFactoryFactory.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIMdbFactoryFactory_h__
+#define nsIMdbFactoryFactory_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+class nsIMdbFactory;
+
+// 2794D0B7-E740-47a4-91C0-3E4FCB95B806
+#define NS_IMDBFACTORYFACTORY_IID \
+{ 0x2794d0b7, 0xe740, 0x47a4, { 0x91, 0xc0, 0x3e, 0x4f, 0xcb, 0x95, 0xb8, 0x6 } }
+
+// because Mork doesn't support XPCOM, we have to wrap the mdb factory interface
+// with an interface that gives you an mdb factory.
+class nsIMdbFactoryService : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORYFACTORY_IID)
+ NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactoryService, NS_IMDBFACTORYFACTORY_IID)
+
+#endif
diff --git a/db/mork/build/nsMorkCID.h b/db/mork/build/nsMorkCID.h
new file mode 100644
index 000000000..79d7a6074
--- /dev/null
+++ b/db/mork/build/nsMorkCID.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMorkCID_h__
+#define nsMorkCID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+#define NS_MORK_CONTRACTID \
+ "@mozilla.org/db/mork;1"
+
+// 36d90300-27f5-11d3-8d74-00805f8a6617
+#define NS_MORK_CID \
+{ 0x36d90300, 0x27f5, 0x11d3, \
+ { 0x8d, 0x74, 0x00, 0x80, 0x5f, 0x8a, 0x66, 0x17 } }
+
+#endif
diff --git a/db/mork/build/nsMorkFactory.cpp b/db/mork/build/nsMorkFactory.cpp
new file mode 100644
index 000000000..6ca358293
--- /dev/null
+++ b/db/mork/build/nsMorkFactory.cpp
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsCOMPtr.h"
+#include "nsMorkCID.h"
+#include "nsIMdbFactoryFactory.h"
+#include "mdb.h"
+
+class nsMorkFactoryService final : public nsIMdbFactoryService
+{
+public:
+ nsMorkFactoryService() {};
+ // nsISupports methods
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD GetMdbFactory(nsIMdbFactory **aFactory) override;
+
+protected:
+ ~nsMorkFactoryService() {}
+ nsCOMPtr<nsIMdbFactory> mMdbFactory;
+};
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkFactoryService)
+
+NS_DEFINE_NAMED_CID(NS_MORK_CID);
+
+const mozilla::Module::CIDEntry kMorkCIDs[] = {
+ { &kNS_MORK_CID, false, NULL, nsMorkFactoryServiceConstructor },
+ { NULL }
+};
+
+const mozilla::Module::ContractIDEntry kMorkContracts[] = {
+ { NS_MORK_CONTRACTID, &kNS_MORK_CID },
+ { NULL }
+};
+
+static const mozilla::Module kMorkModule = {
+ mozilla::Module::kVersion,
+ kMorkCIDs,
+ kMorkContracts
+};
+
+NSMODULE_DEFN(nsMorkModule) = &kMorkModule;
+
+NS_IMPL_ISUPPORTS(nsMorkFactoryService, nsIMdbFactoryService)
+
+NS_IMETHODIMP nsMorkFactoryService::GetMdbFactory(nsIMdbFactory **aFactory)
+{
+ if (!mMdbFactory)
+ mMdbFactory = MakeMdbFactory();
+ NS_IF_ADDREF(*aFactory = mMdbFactory);
+ return *aFactory ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
diff --git a/db/mork/moz.build b/db/mork/moz.build
new file mode 100644
index 000000000..a4cc91c19
--- /dev/null
+++ b/db/mork/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if not CONFIG['NSS_DISABLE_DBM'] and CONFIG['MOZ_MORK']:
+ DIRS += [
+ 'public',
+ 'src',
+ 'build',
+ ]
diff --git a/db/mork/public/mdb.h b/db/mork/public/mdb.h
new file mode 100644
index 000000000..cd2b61293
--- /dev/null
+++ b/db/mork/public/mdb.h
@@ -0,0 +1,2512 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Blake Ross (blake@blakeross.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#define _MDB_ 1
+
+#include "nscore.h"
+#include "nsISupports.h"
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { %%%%% begin scalar typedefs %%%%%
+typedef unsigned char mdb_u1; // make sure this is one byte
+typedef unsigned short mdb_u2; // make sure this is two bytes
+typedef short mdb_i2; // make sure this is two bytes
+typedef uint32_t mdb_u4; // make sure this is four bytes
+typedef int32_t mdb_i4; // make sure this is four bytes
+typedef PRWord mdb_ip; // make sure sizeof(mdb_ip) == sizeof(void*)
+
+typedef mdb_u1 mdb_bool; // unsigned byte with zero=false, nonzero=true
+
+/* canonical boolean constants provided only for code clarity: */
+#define mdbBool_kTrue ((mdb_bool) 1) /* actually any nonzero means true */
+#define mdbBool_kFalse ((mdb_bool) 0) /* only zero means false */
+
+typedef mdb_u4 mdb_id; // unsigned object identity in a scope
+typedef mdb_id mdb_rid; // unsigned row identity inside scope
+typedef mdb_id mdb_tid; // unsigned table identity inside scope
+typedef mdb_u4 mdb_token; // unsigned token for atomized string
+typedef mdb_token mdb_scope; // token used to id scope for rows
+typedef mdb_token mdb_kind; // token used to id kind for tables
+typedef mdb_token mdb_column; // token used to id columns for rows
+typedef mdb_token mdb_cscode; // token used to id charset names
+typedef mdb_u4 mdb_seed; // unsigned collection change counter
+typedef mdb_u4 mdb_count; // unsigned collection member count
+typedef mdb_u4 mdb_size; // unsigned physical media size
+typedef mdb_u4 mdb_fill; // unsigned logical content size
+typedef mdb_u4 mdb_more; // more available bytes for larger buffer
+
+typedef mdb_u2 mork_uses; // 2-byte strong uses count
+typedef mdb_u2 mork_refs; // 2-byte actual reference count
+
+#define mdbId_kNone ((mdb_id) -1) /* never a valid Mork object ID */
+
+typedef mdb_u4 mdb_percent; // 0..100, with values >100 same as 100
+
+typedef mdb_u1 mdb_priority; // 0..9, for a total of ten different values
+
+// sequence position is signed; negative is useful to mean "before first":
+typedef mdb_i4 mdb_pos; // signed zero-based ordinal collection position
+
+#define mdbPos_kBeforeFirst ((mdb_pos) -1) /* any negative is before zero */
+
+// order is also signed, so we can use three states for comparison order:
+typedef mdb_i4 mdb_order; // neg:lessthan, zero:equalto, pos:greaterthan
+
+typedef mdb_order (* mdbAny_Order)(const void* inA, const void* inB,
+ const void* inClosure);
+
+// } %%%%% end scalar typedefs %%%%%
+
+// { %%%%% begin C structs %%%%%
+
+#ifndef mdbScopeStringSet_typedef
+typedef struct mdbScopeStringSet mdbScopeStringSet;
+#define mdbScopeStringSet_typedef 1
+#endif
+
+/*| mdbScopeStringSet: a set of null-terminated C strings that enumerate some
+**| names of row scopes, so that row scopes intended for use by an application
+**| can be declared by an app when trying to open or create a database file.
+**| (We use strings and not tokens because we cannot know the tokens for any
+**| particular db without having first opened the db.) The goal is to inform
+**| a db runtime that scopes not appearing in this list can be given relatively
+**| short shrift in runtime representation, with the expectation that other
+**| scopes will not actually be used. However, a db should still be prepared
+**| to handle accessing row scopes not in this list, rather than raising errors.
+**| But it could be quite expensive to access a row scope not on the list.
+**| Note a zero count for the string set means no such string set is being
+**| specified, and that a db should handle all row scopes efficiently.
+**| (It does NOT mean an app plans to use no content whatsoever.)
+|*/
+#ifndef mdbScopeStringSet_struct
+#define mdbScopeStringSet_struct 1
+struct mdbScopeStringSet { // vector of scopes for use in db opening policy
+ // when mScopeStringSet_Count is zero, this means no scope constraints
+ mdb_count mScopeStringSet_Count; // number of strings in vector below
+ const char** mScopeStringSet_Strings; // null-ended ascii scope strings
+};
+#endif /*mdbScopeStringSet_struct*/
+
+#ifndef mdbOpenPolicy_typedef
+typedef struct mdbOpenPolicy mdbOpenPolicy;
+#define mdbOpenPolicy_typedef 1
+#endif
+
+#ifndef mdbOpenPolicy_struct
+#define mdbOpenPolicy_struct 1
+struct mdbOpenPolicy { // policies affecting db usage for ports and stores
+ mdbScopeStringSet mOpenPolicy_ScopePlan; // predeclare scope usage plan
+ mdb_bool mOpenPolicy_MaxLazy; // nonzero: do least work
+ mdb_bool mOpenPolicy_MinMemory; // nonzero: use least memory
+};
+#endif /*mdbOpenPolicy_struct*/
+
+#ifndef mdbTokenSet_typedef
+typedef struct mdbTokenSet mdbTokenSet;
+#define mdbTokenSet_typedef 1
+#endif
+
+#ifndef mdbTokenSet_struct
+#define mdbTokenSet_struct 1
+struct mdbTokenSet { // array for a set of tokens, and actual slots used
+ mdb_count mTokenSet_Count; // number of token slots in the array
+ mdb_fill mTokenSet_Fill; // the subset of count slots actually used
+ mdb_more mTokenSet_More; // more tokens available for bigger array
+ mdb_token* mTokenSet_Tokens; // array of count mdb_token instances
+};
+#endif /*mdbTokenSet_struct*/
+
+#ifndef mdbUsagePolicy_typedef
+typedef struct mdbUsagePolicy mdbUsagePolicy;
+#define mdbUsagePolicy_typedef 1
+#endif
+
+/*| mdbUsagePolicy: another version of mdbOpenPolicy which uses tokens instead
+**| of scope strings, because usage policies can be constructed for use with a
+**| db that is already open, while an open policy must be constructed before a
+**| db has yet been opened.
+|*/
+#ifndef mdbUsagePolicy_struct
+#define mdbUsagePolicy_struct 1
+struct mdbUsagePolicy { // policies affecting db usage for ports and stores
+ mdbTokenSet mUsagePolicy_ScopePlan; // current scope usage plan
+ mdb_bool mUsagePolicy_MaxLazy; // nonzero: do least work
+ mdb_bool mUsagePolicy_MinMemory; // nonzero: use least memory
+};
+#endif /*mdbUsagePolicy_struct*/
+
+#ifndef mdbOid_typedef
+typedef struct mdbOid mdbOid;
+#define mdbOid_typedef 1
+#endif
+
+#ifndef mdbOid_struct
+#define mdbOid_struct 1
+struct mdbOid { // identity of some row or table inside a database
+ mdb_scope mOid_Scope; // scope token for an id's namespace
+ mdb_id mOid_Id; // identity of object inside scope namespace
+};
+#endif /*mdbOid_struct*/
+
+#ifndef mdbRange_typedef
+typedef struct mdbRange mdbRange;
+#define mdbRange_typedef 1
+#endif
+
+#ifndef mdbRange_struct
+#define mdbRange_struct 1
+struct mdbRange { // range of row positions in a table
+ mdb_pos mRange_FirstPos; // position of first row
+ mdb_pos mRange_LastPos; // position of last row
+};
+#endif /*mdbRange_struct*/
+
+#ifndef mdbColumnSet_typedef
+typedef struct mdbColumnSet mdbColumnSet;
+#define mdbColumnSet_typedef 1
+#endif
+
+#ifndef mdbColumnSet_struct
+#define mdbColumnSet_struct 1
+struct mdbColumnSet { // array of column tokens (just the same as mdbTokenSet)
+ mdb_count mColumnSet_Count; // number of columns
+ mdb_column* mColumnSet_Columns; // count mdb_column instances
+};
+#endif /*mdbColumnSet_struct*/
+
+#ifndef mdbYarn_typedef
+typedef struct mdbYarn mdbYarn;
+#define mdbYarn_typedef 1
+#endif
+
+#ifdef MDB_BEGIN_C_LINKAGE_define
+#define MDB_BEGIN_C_LINKAGE_define 1
+#define MDB_BEGIN_C_LINKAGE extern "C" {
+#define MDB_END_C_LINKAGE }
+#endif /*MDB_BEGIN_C_LINKAGE_define*/
+
+/*| mdbYarn_mGrow: an abstract API for growing the size of a mdbYarn
+**| instance. With respect to a specific API that requires a caller
+**| to supply a string (mdbYarn) that a callee fills with content
+**| that might exceed the specified size, mdbYarn_mGrow is a caller-
+**| supplied means of letting a callee attempt to increase the string
+**| size to become large enough to receive all content available.
+**|
+**|| Grow(): a method for requesting that a yarn instance be made
+**| larger in size. Note that such requests need not be honored, and
+**| need not be honored in full if only partial size growth is desired.
+**| (Note that no nsIMdbEnv instance is passed as argument, although one
+**| might be needed in some circumstances. So if an nsIMdbEnv is needed,
+**| a reference to one might be held inside a mdbYarn member slot.)
+**|
+**|| self: a yarn instance to be grown. Presumably this yarn is
+**| the instance which holds the mYarn_Grow method pointer. Yarn
+**| instancesshould only be passed to grow methods which they were
+**| specifically designed to fit, as indicated by the mYarn_Grow slot.
+**|
+**|| inNewSize: the new desired value for slot mYarn_Size in self.
+**| If mYarn_Size is already this big, then nothing should be done.
+**| If inNewSize is larger than seems feasible or desirable to honor,
+**| then any size restriction policy can be used to grow to some size
+**| greater than mYarn_Size. (Grow() might even grow to a size
+**| greater than inNewSize in order to make the increase in size seem
+**| worthwhile, rather than growing in many smaller steps over time.)
+|*/
+typedef void (* mdbYarn_mGrow)(mdbYarn* self, mdb_size inNewSize);
+// mdbYarn_mGrow methods must be declared with C linkage in C++
+
+/*| mdbYarn: a variable length "string" of arbitrary binary bytes,
+**| whose length is mYarn_Fill, inside a buffer mYarn_Buf that has
+**| at most mYarn_Size byte of physical space.
+**|
+**|| mYarn_Buf: a pointer to space containing content. This slot
+**| might never be nil when mYarn_Size is nonzero, but checks for nil
+**| are recommended anyway.
+**| (Implementations of mdbYarn_mGrow methods should take care to
+**| ensure the existence of a replacement before dropping old Bufs.)
+**| Content in Buf can be anything in any format, but the mYarn_Form
+**| implies the actual format by some caller-to-callee convention.
+**| mYarn_Form==0 implies US-ASCII iso-8859-1 Latin1 string content.
+**|
+**|| mYarn_Size: the physical size of Buf in bytes. Note that if one
+**| intends to terminate a string with a null byte, that it must not
+**| be written at or after mYarn_Buf[mYarn_Size] because this is after
+**| the last byte in the physical buffer space. Size can be zero,
+**| which means the string has no content whatsoever; note that when
+**| Size is zero, this is a suitable reason for Buf==nil as well.
+**|
+**|| mYarn_Fill: the logical content in Buf in bytes, where Fill must
+**| never exceed mYarn_Size. Note that yarn strings might not have a
+**| terminating null byte (since they might not even be C strings), but
+**| when they do, such terminating nulls are considered part of content
+**| and therefore Fill will count such null bytes. So an "empty" C
+**| string will have Fill==1, because content includes one null byte.
+**| Fill does not mean "length" when applied to C strings for this
+**| reason. However, clients using yarns to hold C strings can infer
+**| that length is equal to Fill-1 (but should take care to handle the
+**| case where Fill==0). To be paranoid, one can always copy to a
+**| destination with size exceeding Fill, and place a redundant null
+**| byte in the Fill position when this simplifies matters.
+**|
+**|| mYarn_Form: a designation of content format within mYarn_Buf.
+**| The semantics of this slot are the least well defined, since the
+**| actual meaning is context dependent, to the extent that callers
+**| and callees must agree on format encoding conventions when such
+**| are not standardized in many computing contexts. However, in the
+**| context of a specific mdb database, mYarn_Form is a token for an
+**| atomized string in that database that typically names a preferred
+**| mime type charset designation. If and when mdbYarn is used for
+**| other purposes away from the mdb interface, folks can use another
+**| convention system for encoding content formats. However, in all
+**| contexts is it useful to maintain the convention that Form==0
+**| implies Buf contains US-ASCII iso-8859-1 Latin1 string content.
+**|
+**|| mYarn_Grow: either a mdbYarn_mGrow method, or else nil. When
+**| a mdbYarn_mGrow method is provided, this method can be used to
+**| request a yarn buf size increase. A caller who constructs the
+**| original mdbYarn instance decides whether a grow method is necessary
+**| or desirable, and uses only grow methods suitable for the buffering
+**| nature of a specific mdbYarn instance. (For example, Buf might be a
+**| statically allocated string space which switches to something heap-based
+**| when grown, and subsequent calls to grow the yarn must distinguish the
+**| original static string from heap allocated space, etc.) Note that the
+**| method stored in mYarn_Grow can change, and this might be a common way
+**| to track memory managent changes in policy for mYarn_Buf.
+|*/
+#ifndef mdbYarn_struct
+#define mdbYarn_struct 1
+struct mdbYarn { // buffer with caller space allocation semantics
+ void* mYarn_Buf; // space for holding any binary content
+ mdb_fill mYarn_Fill; // logical content in Buf in bytes
+ mdb_size mYarn_Size; // physical size of Buf in bytes
+ mdb_more mYarn_More; // more available bytes if Buf is bigger
+ mdb_cscode mYarn_Form; // charset format encoding
+ mdbYarn_mGrow mYarn_Grow; // optional method to grow mYarn_Buf
+
+ // Subclasses might add further slots after mYarn_Grow in order to
+ // maintain bookkeeping needs, such as state info about mYarn_Buf.
+};
+#endif /*mdbYarn_struct*/
+
+// } %%%%% end C structs %%%%%
+
+// { %%%%% begin class forward defines %%%%%
+class nsIMdbEnv;
+class nsIMdbObject;
+class nsIMdbErrorHook;
+class nsIMdbThumb;
+class nsIMdbFactory;
+class nsIMdbFile;
+class nsIMdbPort;
+class nsIMdbStore;
+class nsIMdbCursor;
+class nsIMdbPortTableCursor;
+class nsIMdbCollection;
+class nsIMdbTable;
+class nsIMdbTableRowCursor;
+class nsIMdbRow;
+class nsIMdbRowCellCursor;
+class nsIMdbBlob;
+class nsIMdbCell;
+class nsIMdbSorting;
+// } %%%%% end class forward defines %%%%%
+
+
+// { %%%%% begin C++ abstract class interfaces %%%%%
+
+/*| nsIMdbObject: base class for all message db class interfaces
+**|
+**|| factory: all nsIMdbObjects from the same code suite have the same factory
+**|
+**|| refcounting: both strong and weak references, to ensure strong refs are
+**| acyclic, while weak refs can cause cycles. CloseMdbObject() is
+**| called when (strong) use counts hit zero, but clients can call this close
+**| method early for some reason, if absolutely necessary even though it will
+**| thwart the other uses of the same object. Note that implementations must
+**| cope with close methods being called arbitrary numbers of times. The COM
+**| calls to AddRef() and release ref map directly to strong use ref calls,
+**| but the total ref count for COM objects is the sum of weak & strong refs.
+|*/
+
+#define NS_IMDBOBJECT_IID_STR "5533ea4b-14c3-4bef-ac60-22f9e9a49084"
+
+#define NS_IMDBOBJECT_IID \
+{0x5533ea4b, 0x14c3, 0x4bef, \
+{ 0xac, 0x60, 0x22, 0xf9, 0xe9, 0xa4, 0x90, 0x84}}
+
+class nsIMdbObject : public nsISupports { // msg db base class
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBOBJECT_IID)
+// { ===== begin nsIMdbObject methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) = 0;
+ // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+ // } ----- end attribute methods -----
+
+ // { ----- begin factory methods -----
+ NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) = 0;
+ // } ----- end factory methods -----
+
+ // { ----- begin ref counting for well-behaved cyclic graphs -----
+ NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+ mdb_count* outCount) = 0;
+ NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+ mdb_count* outCount) = 0;
+
+ NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) = 0;
+ NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) = 0;
+
+ NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) = 0;
+ NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) = 0;
+
+ NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) = 0; // called at strong refs zero
+ NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) = 0;
+ // } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbObject, NS_IMDBOBJECT_IID)
+
+/*| nsIMdbErrorHook: a base class for clients of this API to subclass, in order
+**| to provide a callback installable in nsIMdbEnv for error notifications. If
+**| apps that subclass nsIMdbErrorHook wish to maintain a reference to the env
+**| that contains the hook, then this should be a weak ref to avoid cycles.
+**|
+**|| OnError: when nsIMdbEnv has an error condition that causes the total count
+**| of errors to increase, then nsIMdbEnv should call OnError() to report the
+**| error in some fashion when an instance of nsIMdbErrorHook is installed. The
+**| variety of string flavors is currently due to the uncertainty here in the
+**| nsIMdbBlob and nsIMdbCell interfaces. (Note that overloading by using the
+**| same method name is not necessary here, and potentially less clear.)
+|*/
+class nsIMdbErrorHook : public nsISupports{ // env callback handler to report errors
+public:
+
+// { ===== begin error methods =====
+ NS_IMETHOD OnErrorString(nsIMdbEnv* ev, const char* inAscii) = 0;
+ NS_IMETHOD OnErrorYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end error methods =====
+
+// { ===== begin warning methods =====
+ NS_IMETHOD OnWarningString(nsIMdbEnv* ev, const char* inAscii) = 0;
+ NS_IMETHOD OnWarningYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end warning methods =====
+
+// { ===== begin abort hint methods =====
+ NS_IMETHOD OnAbortHintString(nsIMdbEnv* ev, const char* inAscii) = 0;
+ NS_IMETHOD OnAbortHintYarn(nsIMdbEnv* ev, const mdbYarn* inYarn) = 0;
+// } ===== end abort hint methods =====
+};
+
+/*| nsIMdbHeap: abstract memory allocation interface.
+**|
+**|| Alloc: return a block at least inSize bytes in size with alignment
+**| suitable for any native type (such as long integers). When no such
+**| block can be allocated, failure is indicated by a null address in
+**| addition to reporting an error in the environment.
+**|
+**|| Free: deallocate a block allocated or resized earlier by the same
+**| heap instance. If the inBlock parameter is nil, the heap should do
+**| nothing (and crashing is strongly discouraged).
+|*/
+class nsIMdbHeap { // caller-supplied memory management interface
+public:
+// { ===== begin nsIMdbHeap methods =====
+ NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+ mdb_size inSize, // requested byte size of new memory block
+ void** outBlock) = 0; // memory block of inSize bytes, or nil
+
+ NS_IMETHOD Free(nsIMdbEnv* ev, // free block from Alloc or Resize()
+ void* ioBlock) = 0; // block to be destroyed/deallocated
+
+ virtual size_t GetUsedSize() = 0;
+
+ virtual ~nsIMdbHeap() {};
+// } ===== end nsIMdbHeap methods =====
+};
+
+/*| nsIMdbCPlusHeap: Alloc() with global ::new(), Free() with global ::delete().
+**| Resize() is done by ::new() followed by ::delete().
+|*/
+class nsIMdbCPlusHeap { // caller-supplied memory management interface
+public:
+// { ===== begin nsIMdbHeap methods =====
+ NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+ mdb_size inSize, // requested size of new memory block
+ void** outBlock); // memory block of inSize bytes, or nil
+
+ NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+ void* inBlock);
+
+ NS_IMETHOD HeapAddStrongRef(nsIMdbEnv* ev);
+ NS_IMETHOD HeapCutStrongRef(nsIMdbEnv* ev);
+// } ===== end nsIMdbHeap methods =====
+};
+
+/*| nsIMdbThumb:
+|*/
+
+
+#define NS_IMDBTHUMB_IID_STR "6d3ad7c1-a809-4e74-8577-49fa9a4562fa"
+
+#define NS_IMDBTHUMB_IID \
+{0x6d3ad7c1, 0xa809, 0x4e74, \
+{ 0x85, 0x77, 0x49, 0xfa, 0x9a, 0x45, 0x62, 0xfa}}
+
+
+class nsIMdbThumb : public nsISupports { // closure for repeating incremental method
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTHUMB_IID)
+
+// { ===== begin nsIMdbThumb methods =====
+ NS_IMETHOD GetProgress(nsIMdbEnv* ev,
+ mdb_count* outTotal, // total somethings to do in operation
+ mdb_count* outCurrent, // subportion of total completed so far
+ mdb_bool* outDone, // is operation finished?
+ mdb_bool* outBroken // is operation irreparably dead and broken?
+ ) = 0;
+
+ NS_IMETHOD DoMore(nsIMdbEnv* ev,
+ mdb_count* outTotal, // total somethings to do in operation
+ mdb_count* outCurrent, // subportion of total completed so far
+ mdb_bool* outDone, // is operation finished?
+ mdb_bool* outBroken // is operation irreparably dead and broken?
+ ) = 0;
+
+ NS_IMETHOD CancelAndBreakThumb( // cancel pending operation
+ nsIMdbEnv* ev) = 0;
+// } ===== end nsIMdbThumb methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbThumb, NS_IMDBTHUMB_IID)
+
+/*| nsIMdbEnv: a context parameter used when calling most abstract db methods.
+**| The main purpose of such an object is to permit a database implementation
+**| to avoid the use of globals to share information between various parts of
+**| the implementation behind the abstract db interface. An environment acts
+**| like a session object for a given calling thread, and callers should use
+**| at least one different nsIMdbEnv instance for each thread calling the API.
+**| While the database implementation might not be threaded, it is highly
+**| desirable that the db be thread-safe if calling threads use distinct
+**| instances of nsIMdbEnv. Callers can stop at one nsIMdbEnv per thread, or they
+**| might decide to make on nsIMdbEnv instance for every nsIMdbPort opened, so that
+**| error information is segregated by database instance. Callers create
+**| instances of nsIMdbEnv by calling the MakeEnv() method in nsIMdbFactory.
+**|
+**|| tracing: an environment might support some kind of tracing, and this
+**| boolean attribute permits such activity to be enabled or disabled.
+**|
+**|| errors: when a call to the abstract db interface returns, a caller might
+**| check the number of outstanding errors to see whether the operation did
+**| actually succeed. Each nsIMdbEnv should have all its errors cleared by a
+**| call to ClearErrors() before making each call to the abstract db API,
+**| because outstanding errors might disable further database actions. (This
+**| is not done inside the db interface, because the db cannot in general know
+**| when a call originates from inside or outside -- only the app knows this.)
+**|
+**|| error hook: callers can install an instance of nsIMdbErrorHook to receive
+**| error notifications whenever the error count increases. The hook can
+**| be uninstalled by passing a null pointer.
+**|
+|*/
+
+#define NS_IMDBENV_IID_STR "a765e46b-efb6-41e6-b75b-c5d6bd710594"
+
+#define NS_IMDBENV_IID \
+{0xa765e46b, 0xefb6, 0x41e6, \
+{ 0xb7, 0x5b, 0xc5, 0xd6, 0xbd, 0x71, 0x05, 0x94}}
+
+class nsIMdbEnv : public nsISupports { // db specific context parameter
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBENV_IID)
+// { ===== begin nsIMdbEnv methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetErrorCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort) = 0;
+ NS_IMETHOD GetWarningCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort) = 0;
+
+ NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) = 0;
+ NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) = 0;
+
+ NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) = 0;
+ NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) = 0;
+
+ NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) = 0;
+ NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) = 0;
+
+ NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) = 0;
+ NS_IMETHOD SetErrorHook(
+ nsIMdbErrorHook* ioErrorHook) = 0; // becomes referenced
+
+ NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) = 0;
+ NS_IMETHOD SetHeap(
+ nsIMdbHeap* ioHeap) = 0; // becomes referenced
+ // } ----- end attribute methods -----
+
+ NS_IMETHOD ClearErrors() = 0; // clear errors beore re-entering db API
+ NS_IMETHOD ClearWarnings() = 0; // clear warnings
+ NS_IMETHOD ClearErrorsAndWarnings() = 0; // clear both errors & warnings
+// } ===== end nsIMdbEnv methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbEnv, NS_IMDBENV_IID)
+
+/*| nsIMdbFactory: the main entry points to the abstract db interface. A DLL
+**| that supports this mdb interface need only have a single exported method
+**| that will return an instance of nsIMdbFactory, so that further methods in
+**| the suite can be accessed from objects returned by nsIMdbFactory methods.
+**|
+**|| mdbYarn: note all nsIMdbFactory subclasses must guarantee null
+**| termination of all strings written into mdbYarn instances, as long as
+**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must
+**| be null terminated. This is more strict behavior than mdbYarn requires,
+**| but it is part of the nsIMdbFactory interface.
+**|
+**|| envs: an environment instance is required as per-thread context for
+**| most of the db method calls, so nsIMdbFactory creates such instances.
+**|
+**|| rows: callers must be able to create row instances that are independent
+**| of storage space that is part of the db content graph. Many interfaces
+**| for data exchange have strictly copy semantics, so that a row instance
+**| has no specific identity inside the db content model, and the text in
+**| cells are an independenty copy of unexposed content inside the db model.
+**| Callers are expected to maintain one or more row instances as a buffer
+**| for staging cell content copied into or out of a table inside the db.
+**| Callers are urged to use an instance of nsIMdbRow created by the nsIMdbFactory
+**| code suite, because reading and writing might be much more efficient than
+**| when using a hand-rolled nsIMdbRow subclass with no relation to the suite.
+**|
+**|| ports: a port is a readonly interface to a specific database file. Most
+**| of the methods to access a db file are suitable for a readonly interface,
+**| so a port is the basic minimum for accessing content. This makes it
+**| possible to read other external formats for import purposes, without
+**| needing the code or competence necessary to write every such format. So
+**| we can write generic import code just once, as long as every format can
+**| show a face based on nsIMdbPort. (However, same suite import can be faster.)
+**| Given a file name and the first 512 bytes of a file, a factory can say if
+**| a port can be opened by this factory. Presumably an app maintains chains
+**| of factories for different suites, and asks each in turn about opening a
+**| a prospective file for reading (as a port) or writing (as a store). I'm
+**| not ready to tackle issues of format fidelity and factory chain ordering.
+**|
+**|| stores: a store is a mutable interface to a specific database file, and
+**| includes the port interface plus any methods particular to writing, which
+**| are few in number. Presumably the set of files that can be opened as
+**| stores is a subset of the set of files that can be opened as ports. A
+**| new store can be created with CreateNewFileStore() by supplying a new
+**| file name which does not yet exist (callers are always responsible for
+**| destroying any existing files before calling this method).
+|*/
+
+#define NS_IMDBFACTORY_IID_STR "2b80395c-b91e-4990-b1a7-023e99ab14e9"
+
+#define NS_IMDBFACTORY_IID \
+{0xf04aa4ab, 0x1fe, 0x4115, \
+{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}}
+
+
+class nsIMdbFactory : public nsISupports { // suite entry points
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFACTORY_IID)
+// { ===== begin nsIMdbFactory methods =====
+
+ // { ----- begin file methods -----
+ NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath,
+ mdb_bool inFrozen, nsIMdbFile** acqFile) = 0;
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be open and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+
+ NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath,
+ nsIMdbFile** acqFile) = 0;
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be created and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+ // } ----- end file methods -----
+
+ // { ----- begin env methods -----
+ NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) = 0; // acquire new env
+ // ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+ // } ----- end env methods -----
+
+ // { ----- begin heap methods -----
+ NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) = 0; // acquire new heap
+ // } ----- end heap methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) = 0; // new row
+ // ioHeap can be nil, causing the heap associated with ev to be used
+ // } ----- end row methods -----
+
+ // { ----- begin port methods -----
+ NS_IMETHOD CanOpenFilePort(
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion) = 0; // informal file format description
+
+ NS_IMETHOD OpenFilePort(
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for readonly import
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental port open
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+ NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+ nsIMdbPort** acqPort) = 0; // acquire new port object
+ // } ----- end port methods -----
+
+ // { ----- begin store methods -----
+ NS_IMETHOD CanOpenFileStore(
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+ mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion) = 0; // informal file format description
+
+ NS_IMETHOD OpenFileStore( // open an existing database
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for general db usage
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental store open
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+
+ NS_IMETHOD
+ ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+ nsIMdbStore** acqStore) = 0; // acquire new db store object
+
+ NS_IMETHOD CreateNewFileStore( // create a new db with minimal content
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // name of file which should not yet exist
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbStore** acqStore) = 0; // acquire new db store object
+ // } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFactory, NS_IMDBFACTORY_IID)
+
+extern "C" nsIMdbFactory* MakeMdbFactory();
+
+/*| nsIMdbFile: abstract file interface resembling the original morkFile
+**| abstract interface (which was in turn modeled on the file interface
+**| from public domain IronDoc). The design of this file interface is
+**| complicated by the fact that some DB's will not find this interface
+**| adequate for all runtime requirements (even though this file API is
+**| enough to implement text-based DB's like Mork). For this reason,
+**| more methods have been added to let a DB library force the file to
+**| become closed so the DB can reopen the file in some other manner.
+**| Folks are encouraged to suggest ways to tune this interface to suit
+**| DB's that cannot manage to pull their maneuvers even given this API.
+**|
+**|| Tell: get the current i/o position in file
+**|
+**|| Seek: change the current i/o position in file
+**|
+**|| Eof: return file's total length in bytes
+**|
+**|| Read: input inSize bytes into outBuf, returning actual transfer size
+**|
+**|| Get: read starting at specific file offset (e.g. Seek(); Read();)
+**|
+**|| Write: output inSize bytes from inBuf, returning actual transfer size
+**|
+**|| Put: write starting at specific file offset (e.g. Seek(); Write();)
+**|
+**|| Flush: if written bytes are buffered, push them to final destination
+**|
+**|| Path: get file path in some string representation. This is intended
+**| either to support the display of file name in a user presentation, or
+**| to support the closing and reopening of the file when the DB needs more
+**| exotic file access than is presented by the nsIMdbFile interface.
+**|
+**|| Steal: tell this file to close any associated i/o stream in the file
+**| system, because the file ioThief intends to reopen the file in order
+**| to provide the MDB implementation with more exotic file access than is
+**| offered by the nsIMdbFile alone. Presumably the thief knows enough
+**| from Path() in order to know which file to reopen. If Steal() is
+**| successful, this file should probably delegate all future calls to
+**| the nsIMdbFile interface down to the thief files, so that even after
+**| the file has been stolen, it can still be read, written, or forcibly
+**| closed (by a call to CloseMdbObject()).
+**|
+**|| Thief: acquire and return thief passed to an earlier call to Steal().
+|*/
+
+#define NS_IMDBFILE_IID_STR "f04aa4ab-1fe7-4115-a4a5-6819dff1103d"
+
+#define NS_IMDBFILE_IID \
+{0xf04aa4ab, 0x1fe, 0x4115, \
+{ 0xa4, 0xa5, 0x68, 0x19, 0xdf, 0xf1, 0x10, 0x3d}}
+
+class nsIMdbFile : public nsISupports { // minimal file interface
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBFILE_IID)
+// { ===== begin nsIMdbFile methods =====
+
+ // { ----- begin pos methods -----
+ NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const = 0;
+ NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) = 0;
+ NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
+ // } ----- end pos methods -----
+
+ // { ----- begin read methods -----
+ NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+ mdb_size* outActualSize) = 0;
+ NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize) = 0;
+ // } ----- end read methods -----
+
+ // { ----- begin write methods -----
+ NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+ mdb_size* outActualSize) = 0;
+ NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize) = 0;
+ NS_IMETHOD Flush(nsIMdbEnv* ev) = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin path methods -----
+ NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) = 0;
+ // } ----- end path methods -----
+
+ // { ----- begin replacement methods -----
+ NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) = 0;
+ NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) = 0;
+ // } ----- end replacement methods -----
+
+ // { ----- begin versioning methods -----
+ NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) = 0;
+ // If this file is a file version branch created by calling AcquireBud(),
+ // BecomeTrunk() causes this file's content to replace the original
+ // file's content, typically by assuming the original file's identity.
+ // This default implementation of BecomeTrunk() does nothing, and this
+ // is appropriate behavior for files which are not branches, and is
+ // also the right behavior for files returned from AcquireBud() which are
+ // in fact the original file that has been truncated down to zero length.
+
+ NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ nsIMdbFile** acqBud) = 0; // acquired file for new version of content
+ // AcquireBud() starts a new "branch" version of the file, empty of content,
+ // so that a new version of the file can be written. This new file
+ // can later be told to BecomeTrunk() the original file, so the branch
+ // created by budding the file will replace the original file. Some
+ // file subclasses might initially take the unsafe but expedient
+ // approach of simply truncating this file down to zero length, and
+ // then returning the same morkFile pointer as this, with an extra
+ // reference count increment. Note that the caller of AcquireBud() is
+ // expected to eventually call CutStrongRef() on the returned file
+ // in order to release the strong reference. High quality versions
+ // of morkFile subclasses will create entirely new files which later
+ // are renamed to become the old file, so that better transactional
+ // behavior is exhibited by the file, so crashes protect old files.
+ // Note that AcquireBud() is an illegal operation on readonly files.
+ // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbFile, NS_IMDBFILE_IID)
+
+/*| nsIMdbPort: a readonly interface to a specific database file. The mutable
+**| nsIMdbStore interface is a subclass that includes writing behavior, but
+**| most of the needed db methods appear in the readonly nsIMdbPort interface.
+**|
+**|| mdbYarn: note all nsIMdbPort and nsIMdbStore subclasses must guarantee null
+**| termination of all strings written into mdbYarn instances, as long as
+**| mYarn_Size and mYarn_Buf are nonzero. Even truncated string values must
+**| be null terminated. This is more strict behavior than mdbYarn requires,
+**| but it is part of the nsIMdbPort and nsIMdbStore interface.
+**|
+**|| attributes: methods are provided to distinguish a readonly port from a
+**| mutable store, and whether a mutable store actually has any dirty content.
+**|
+**|| filepath: the file path used to open the port from the nsIMdbFactory can be
+**| queried and discovered by GetPortFilePath(), which includes format info.
+**|
+**|| export: a port can write itself in other formats, with perhaps a typical
+**| emphasis on text interchange formats used by other systems. A port can be
+**| queried to determine its preferred export interchange format, and a port
+**| can be queried to see whether a specific export format is supported. And
+**| actually exporting a port requires a new destination file name and format.
+**|
+**|| tokens: a port supports queries about atomized strings to map tokens to
+**| strings or strings to token integers. (All atomized strings must be in
+**| US-ASCII iso-8859-1 Latin1 charset encoding.) When a port is actually a
+**| mutable store and a string has not yet been atomized, then StringToToken()
+**| will actually do so and modify the store. The QueryToken() method will not
+**| atomize a string if it has not already been atomized yet, even in stores.
+**|
+**|| tables: other than string tokens, all port content is presented through
+**| tables, which are ordered collections of rows. Tables are identified by
+**| row scope and table kind, which might or might not be unique in a port,
+**| depending on app convention. When tables are effectively unique, then
+**| queries for specific scope and kind pairs will find those tables. To see
+**| all tables that match specific row scope and table kind patterns, even in
+**| the presence of duplicates, every port supports a GetPortTableCursor()
+**| method that returns an iterator over all matching tables. Table kind is
+**| considered scoped inside row scope, so passing a zero for table kind will
+**| find all table kinds for some nonzero row scope. Passing a zero for row
+**| scope will iterate over all tables in the port, in some undefined order.
+**| (A new table can be added to a port using nsIMdbStore::NewTable(), even when
+**| the requested scope and kind combination is already used by other tables.)
+**|
+**|| memory: callers can request that a database use less memory footprint in
+**| several flavors, from an inconsequential idle flavor to a rather drastic
+**| panic flavor. Callers might perform an idle purge very frequently if desired
+**| with very little cost, since only normally scheduled memory management will
+**| be conducted, such as freeing resources for objects scheduled to be dropped.
+**| Callers should perform session memory purges infrequently because they might
+**| involve costly scanning of data structures to removed cached content, and
+**| session purges are recommended only when a caller experiences memory crunch.
+**| Callers should only rarely perform a panic purge, in response to dire memory
+**| straits, since this is likely to make db operations much more expensive
+**| than they would be otherwise. A panic purge asks a database to free as much
+**| memory as possible while staying effective and operational, because a caller
+**| thinks application failure might otherwise occur. (Apps might better close
+**| an open db, so panic purges only make sense when a db is urgently needed.)
+|*/
+class nsIMdbPort : public nsISupports {
+public:
+
+// { ===== begin nsIMdbPort methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+ NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+ NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) = 0;
+
+ NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev,
+ mdbUsagePolicy* ioUsagePolicy) = 0;
+
+ NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev,
+ const mdbUsagePolicy* inUsagePolicy) = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin memory policy methods -----
+ NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled
+ nsIMdbEnv* ev, // context
+ mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+
+ NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease
+ nsIMdbEnv* ev, // context
+ mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+ mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+
+ NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory
+ nsIMdbEnv* ev, // context
+ mdb_size* outEstimatedBytesFreed) = 0; // approximate bytes actually freed
+ // } ----- end memory policy methods -----
+
+ // { ----- begin filepath methods -----
+ NS_IMETHOD GetPortFilePath(
+ nsIMdbEnv* ev, // context
+ mdbYarn* outFilePath, // name of file holding port content
+ mdbYarn* outFormatVersion) = 0; // file format description
+
+ NS_IMETHOD GetPortFile(
+ nsIMdbEnv* ev, // context
+ nsIMdbFile** acqFile) = 0; // acquire file used by port or store
+ // } ----- end filepath methods -----
+
+ // { ----- begin export methods -----
+ NS_IMETHOD BestExportFormat( // determine preferred export format
+ nsIMdbEnv* ev, // context
+ mdbYarn* outFormatVersion) = 0; // file format description
+
+ // some tentative suggested import/export formats
+ // "ns:msg:db:port:format:ldif:ns4.0:passthrough" // necessary
+ // "ns:msg:db:port:format:ldif:ns4.5:utf8" // necessary
+ // "ns:msg:db:port:format:ldif:ns4.5:tabbed"
+ // "ns:msg:db:port:format:ldif:ns4.5:binary" // necessary
+ // "ns:msg:db:port:format:html:ns3.0:addressbook" // necessary
+ // "ns:msg:db:port:format:html:display:verbose"
+ // "ns:msg:db:port:format:html:display:concise"
+ // "ns:msg:db:port:format:mork:zany:verbose" // necessary
+ // "ns:msg:db:port:format:mork:zany:atomized" // necessary
+ // "ns:msg:db:port:format:rdf:xml"
+ // "ns:msg:db:port:format:xml:mork"
+ // "ns:msg:db:port:format:xml:display:verbose"
+ // "ns:msg:db:port:format:xml:display:concise"
+ // "ns:msg:db:port:format:xml:print:verbose" // recommended
+ // "ns:msg:db:port:format:xml:print:concise"
+
+ NS_IMETHOD
+ CanExportToFormat( // can export content in given specific format?
+ nsIMdbEnv* ev, // context
+ const char* inFormatVersion, // file format description
+ mdb_bool* outCanExport) = 0; // whether ExportSource() might succeed
+
+ NS_IMETHOD ExportToFormat( // export content in given specific format
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to receive exported content
+ nsIMdbFile* ioFile, // destination abstract file interface
+ const char* inFormatVersion, // file format description
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental export
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the export will be finished.
+
+ // } ----- end export methods -----
+
+ // { ----- begin token methods -----
+ NS_IMETHOD TokenToString( // return a string name for an integer token
+ nsIMdbEnv* ev, // context
+ mdb_token inToken, // token for inTokenName inside this port
+ mdbYarn* outTokenName) = 0; // the type of table to access
+
+ NS_IMETHOD StringToToken( // return an integer token for scope name
+ nsIMdbEnv* ev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) = 0; // token for inTokenName inside this port
+
+ // String token zero is never used and never supported. If the port
+ // is a mutable store, then StringToToken() to create a new
+ // association of inTokenName with a new integer token if possible.
+ // But a readonly port will return zero for an unknown scope name.
+
+ NS_IMETHOD QueryToken( // like StringToToken(), but without adding
+ nsIMdbEnv* ev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) = 0; // token for inTokenName inside this port
+
+ // QueryToken() will return a string token if one already exists,
+ // but unlike StringToToken(), will not assign a new token if not
+ // already in use.
+
+ // } ----- end token methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD HasRow( // contains a row with the specified oid?
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_bool* outHasRow) = 0; // whether GetRow() might succeed
+
+ NS_IMETHOD GetRowRefCount( // get number of tables that contain a row
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_count* outRefCount) = 0; // number of tables containing inRowKey
+
+ NS_IMETHOD GetRow( // access one row with specific oid
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ nsIMdbRow** acqRow) = 0; // acquire specific row (or null)
+
+ // NS_IMETHOD
+ // GetPortRowCursor( // get cursor for all rows in specific scope
+ // nsIMdbEnv* ev, // context
+ // mdb_scope inRowScope, // row scope for row ids
+ // nsIMdbPortRowCursor** acqCursor) = 0; // all such rows in the port
+
+ NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_column inColumn, // the column to search (and maintain an index)
+ const mdbYarn* inTargetCellValue, // cell value for which to search
+ mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+ nsIMdbRow** acqRow) = 0; // acquire matching row (or nil for no match)
+ // can be null if you only want the oid
+ // FindRow() searches for one row that has a cell in column inColumn with
+ // a contained value with the same form (i.e. charset) and is byte-wise
+ // identical to the blob described by yarn inTargetCellValue. Both content
+ // and form of the yarn must be an exact match to find a matching row.
+ //
+ // (In other words, both a yarn's blob bytes and form are significant. The
+ // form is not expected to vary in columns used for identity anyway. This
+ // is intended to make the cost of FindRow() cheaper for MDB implementors,
+ // since any cell value atomization performed internally must necessarily
+ // make yarn form significant in order to avoid data loss in atomization.)
+ //
+ // FindRow() can lazily create an index on attribute inColumn for all rows
+ // with that attribute in row space scope inRowScope, so that subsequent
+ // calls to FindRow() will perform faster. Such an index might or might
+ // not be persistent (but this seems desirable if it is cheap to do so).
+ // Note that lazy index creation in readonly DBs is not very feasible.
+ //
+ // This FindRow() interface assumes that attribute inColumn is effectively
+ // an alternative means of unique identification for a row in a rowspace,
+ // so correct behavior is only guaranteed when no duplicates for this col
+ // appear in the given set of rows. (If more than one row has the same cell
+ // value in this column, no more than one will be found; and cutting one of
+ // two duplicate rows can cause the index to assume no other such row lives
+ // in the row space, so future calls return nil for negative search results
+ // even though some duplicate row might still live within the rowspace.)
+ //
+ // In other words, the FindRow() implementation is allowed to assume simple
+ // hash tables mapping unqiue column keys to associated row values will be
+ // sufficient, where any duplication is not recorded because only one copy
+ // of a given key need be remembered. Implementors are not required to sort
+ // all rows by the specified column.
+ // } ----- end row methods -----
+
+ // { ----- begin table methods -----
+ NS_IMETHOD HasTable( // supports a table with the specified oid?
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ mdb_bool* outHasTable) = 0; // whether GetTable() might succeed
+
+ NS_IMETHOD GetTable( // access one table with specific oid
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ nsIMdbTable** acqTable) = 0; // acquire specific table (or null)
+
+ NS_IMETHOD HasTableKind( // supports a table of the specified type?
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // rid scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outSupportsTable) = 0; // whether GetTableKind() might succeed
+
+ // row scopes to be supported include the following suggestions:
+ // "ns:msg:db:row:scope:address:cards:all"
+ // "ns:msg:db:row:scope:mail:messages:all"
+ // "ns:msg:db:row:scope:news:articles:all"
+
+ // table kinds to be supported include the following suggestions:
+ // "ns:msg:db:table:kind:address:cards:main"
+ // "ns:msg:db:table:kind:address:lists:all"
+ // "ns:msg:db:table:kind:address:list"
+ // "ns:msg:db:table:kind:news:threads:all"
+ // "ns:msg:db:table:kind:news:thread"
+ // "ns:msg:db:table:kind:mail:threads:all"
+ // "ns:msg:db:table:kind:mail:thread"
+
+ NS_IMETHOD GetTableKind( // access one (random) table of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outMustBeUnique, // whether port can hold only one of these
+ nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows
+
+ NS_IMETHOD
+ GetPortTableCursor( // get cursor for all tables of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ nsIMdbPortTableCursor** acqCursor) = 0; // all such tables in the port
+ // } ----- end table methods -----
+
+
+ // { ----- begin commit methods -----
+
+ NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste?
+ nsIMdbEnv* ev, // context
+ mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+ mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+ mdb_bool* outShould) = 0; // true when about inPercentWaste% is wasted
+ // ShouldCompress() returns true if the store can determine that the file
+ // will shrink by an estimated percentage of inPercentWaste% (or more) if
+ // CompressCommit() is called, because that percentage of the file seems
+ // to be recoverable free space. The granularity is only in terms of
+ // percentage points, and any value over 100 is considered equal to 100.
+ //
+ // If a store only has an approximate idea how much space might be saved
+ // during a compress, then a best guess should be made. For example, the
+ // Mork implementation might keep track of how much file space began with
+ // text content before the first updating transaction, and then consider
+ // all content following the start of the first transaction as potentially
+ // wasted space if it is all updates and not just new content. (This is
+ // a safe assumption in the sense that behavior will stabilize on a low
+ // estimate of wastage after a commit removes all transaction updates.)
+ //
+ // Some db formats might attempt to keep a very accurate reckoning of free
+ // space size, so a very accurate determination can be made. But other db
+ // formats might have difficulty determining size of free space, and might
+ // require some lengthy calculation to answer. This is the reason for
+ // passing in the percentage threshold of interest, so that such lengthy
+ // computations can terminate early as soon as at least inPercentWaste is
+ // found, so that the entire file need not be groveled when unnecessary.
+ // However, we hope implementations will always favor fast but imprecise
+ // heuristic answers instead of extremely slow but very precise answers.
+ //
+ // If the outActualWaste parameter is non-nil, it will be used to return
+ // the actual estimated space wasted as a percentage of file size. (This
+ // parameter is provided so callers need not call repeatedly with altered
+ // inPercentWaste values to isolate the actual wastage figure.) Note the
+ // actual wastage figure returned can exactly equal inPercentWaste even
+ // when this grossly underestimates the real figure involved, if the db
+ // finds it very expensive to determine the extent of wastage after it is
+ // known to at least exceed inPercentWaste. Note we expect that whenever
+ // outShould returns true, that outActualWaste returns >= inPercentWaste.
+ //
+ // The effect of different inPercentWaste values is not very uniform over
+ // the permitted range. For example, 50 represents 50% wastage, or a file
+ // that is about double what it should be ideally. But 99 represents 99%
+ // wastage, or a file that is about ninety-nine times as big as it should
+ // be ideally. In the smaller direction, 25 represents 25% wastage, or
+ // a file that is only 33% larger than it should be ideally.
+ //
+ // Callers can determine what policy they want to use for considering when
+ // a file holds too much wasted space, and express this as a percentage
+ // of total file size to pass as in the inPercentWaste parameter. A zero
+ // likely returns always trivially true, and 100 always trivially false.
+ // The great majority of callers are expected to use values from 25 to 75,
+ // since most plausible thresholds for compressing might fall between the
+ // extremes of 133% of ideal size and 400% of ideal size. (Presumably the
+ // larger a file gets, the more important the percentage waste involved, so
+ // a sliding scale for compress thresholds might use smaller numbers for
+ // much bigger file sizes.)
+
+ // } ----- end commit methods -----
+
+// } ===== end nsIMdbPort methods =====
+};
+
+/*| nsIMdbStore: a mutable interface to a specific database file.
+**|
+**|| tables: one can force a new table to exist in a store with NewTable()
+**| and nonzero values for both row scope and table kind. (If one wishes only
+**| one table of a certain kind, then one might look for it first using the
+**| GetTableKind() method). One can pass inMustBeUnique to force future
+**| users of this store to be unable to create other tables with the same pair
+**| of scope and kind attributes. When inMustBeUnique is true, and the table
+**| with the given scope and kind pair already exists, then the existing one
+**| is returned instead of making a new table. Similarly, if one passes false
+**| for inMustBeUnique, but the table kind has already been marked unique by a
+**| previous user of the store, then the existing unique table is returned.
+**|
+**|| import: all or some of another port's content can be imported by calling
+**| AddPortContent() with a row scope identifying the extent of content to
+**| be imported. A zero row scope will import everything. A nonzero row
+**| scope will only import tables with a matching row scope. Note that one
+**| must somehow find a way to negotiate possible conflicts between existing
+**| row content and imported row content, and this involves a specific kind of
+**| definition for row identity involving either row IDs or unique attributes,
+**| or some combination of these two. At the moment I am just going to wave
+**| my hands, and say the default behavior is to assign all new row identities
+**| to all imported content, which will result in no merging of content; this
+**| must change later because it is unacceptable in some contexts.
+**|
+**|| commits: to manage modifications in a mutable store, very few methods are
+**| really needed to indicate global policy choices that are independent of
+**| the actual modifications that happen in objects at the level of tables,
+**| rows, and cells, etc. The most important policy to specify is which sets
+**| of changes are considered associated in a manner such that they should be
+**| applied together atomically to a given store. We call each such group of
+**| changes a transaction. We handle three different grades of transaction,
+**| but they differ only in semantic significance to the application, and are
+**| not intended to nest. (If small transactions were nested inside large
+**| transactions, that would imply that a single large transaction must be
+**| atomic over all the contained small transactions; but actually we intend
+**| smalls transaction never be undone once committed due to, say, aborting a
+**| transaction of greater significance.) The small, large, and session level
+**| commits have equal granularity, and differ only in risk of loss from the
+**| perspective of an application. Small commits characterize changes that
+**| can be lost with relatively small risk, so small transactions can delay
+**| until later if they are expensive or impractical to commit. Large commits
+**| involve changes that would probably inconvenience users if lost, so the
+**| need to pay costs of writing is rather greater than with small commits.
+**| Session commits are last ditch attempts to save outstanding changes before
+**| stopping the use of a particular database, so there will be no later point
+**| in time to save changes that have been delayed due to possible high cost.
+**| If large commits are never delayed, then a session commit has about the
+**| same performance effect as another large commit; but if small and large
+**| commits are always delayed, then a session commit is likely to be rather
+**| expensive as a runtime cost compared to any earlier database usage.
+**|
+**|| aborts: the only way to abort changes to a store is by closing the store.
+**| So there is no specific method for causing any abort. Stores must discard
+**| all changes made that are uncommitted when a store is closed. This design
+**| choice makes the implementations of tables, rows, and cells much less
+**| complex because they need not maintain a record of undobable changes. When
+**| a store is closed, presumably this precipitates the closure of all tables,
+**| rows, and cells in the store as well. So an application can revert the
+**| state of a store in the user interface by quietly closing and reopening a
+**| store, because this will discard uncommitted changes and show old content.
+**| This implies an app that closes a store will need to send a "scramble"
+**| event notification to any views that depend on old discarded content.
+|*/
+
+#define NS_IMDBSTORE_IID_STR "74d6218d-44b0-43b5-9ebe-69a17dfb562c"
+#define NS_IMDBSTORE_IID \
+{0x74d6218d, 0x44b0, 0x43b5, \
+{0x9e, 0xbe, 0x69, 0xa1, 0x7d, 0xfb, 0x56, 0x2c}}
+
+class nsIMdbStore : public nsIMdbPort {
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBSTORE_IID)
+
+// { ===== begin nsIMdbStore methods =====
+
+ // { ----- begin table methods -----
+ NS_IMETHOD NewTable( // make one new table of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows
+
+ NS_IMETHOD NewTableWithOid( // make one new table of specific type
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // caller assigned oid
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) = 0; // acquire scoped collection of rows
+ // } ----- end table methods -----
+
+ // { ----- begin row scope methods -----
+ NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+
+ NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+
+ NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) = 0; // nonzero if store db assigned specified
+ // } ----- end row scope methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
+ const mdbOid* inOid, // caller assigned oid
+ nsIMdbRow** acqRow) = 0; // create new row
+
+ NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid
+ mdb_scope inRowScope, // row scope for row ids
+ nsIMdbRow** acqRow) = 0; // create new row
+ // Note this row must be added to some table or cell child before the
+ // store is closed in order to make this row persist across sessions.
+
+ // } ----- end row methods -----
+
+ // { ----- begin inport/export methods -----
+ NS_IMETHOD ImportContent( // import content from port
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // scope for rows (or zero for all?)
+ nsIMdbPort* ioPort, // the port with content to add to store
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the import will be finished.
+
+ NS_IMETHOD ImportFile( // import content from port
+ nsIMdbEnv* ev, // context
+ nsIMdbFile* ioFile, // the file with content to add to store
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental import
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the import will be finished.
+ // } ----- end inport/export methods -----
+
+ // { ----- begin hinting methods -----
+ NS_IMETHOD
+ ShareAtomColumnsHint( // advise re shared column content atomizing
+ nsIMdbEnv* ev, // context
+ mdb_scope inScopeHint, // zero, or suggested shared namespace
+ const mdbColumnSet* inColumnSet) = 0; // cols desired tokenized together
+
+ NS_IMETHOD
+ AvoidAtomColumnsHint( // advise column with poor atomizing prospects
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) = 0; // cols with poor atomizing prospects
+ // } ----- end hinting methods -----
+
+ // { ----- begin commit methods -----
+ NS_IMETHOD LargeCommit( // save important changes if at all possible
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ NS_IMETHOD SessionCommit( // save all changes if large commits delayed
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ NS_IMETHOD
+ CompressCommit( // commit and make db physically smaller if possible
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ // } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbStore, NS_IMDBSTORE_IID)
+
+/*| nsIMdbCursor: base cursor class for iterating row cells and table rows
+**|
+**|| count: the number of elements in the collection (table or row)
+**|
+**|| seed: the change count in the underlying collection, which is synced
+**| with the collection when the iteration position is set, and henceforth
+**| acts to show whether the iter has lost collection synchronization, in
+**| case it matters to clients whether any change happens during iteration.
+**|
+**|| pos: the position of the current element in the collection. Negative
+**| means a position logically before the first element. A positive value
+**| equal to count (or larger) implies a position after the last element.
+**| To iterate over all elements, set the position to negative, so subsequent
+**| calls to any 'next' method will access the first collection element.
+**|
+**|| doFailOnSeedOutOfSync: whether a cursor should return an error if the
+**| cursor's snapshot of a table's seed becomes stale with respect the table's
+**| current seed value (which implies the iteration is less than total) in
+**| between to cursor calls that actually access collection content. By
+**| default, a cursor should assume this attribute is false until specified,
+**| so that iterations quietly try to re-sync when they lose coherence.
+|*/
+
+#define NS_IMDBCURSOR_IID_STR "a0c37337-6ebc-474c-90db-e65ea0b850aa"
+
+#define NS_IMDBCURSOR_IID \
+{0xa0c37337, 0x6ebc, 0x474c, \
+{0x90, 0xdb, 0xe6, 0x5e, 0xa0, 0xb8, 0x50, 0xaa}}
+
+class nsIMdbCursor : public nsISupports { // collection iterator
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBCURSOR_IID)
+// { ===== begin nsIMdbCursor methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) = 0; // readonly
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) = 0; // readonly
+
+ NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) = 0; // mutable
+ NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) = 0;
+
+ NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) = 0;
+ NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) = 0;
+ // } ----- end attribute methods -----
+
+// } ===== end nsIMdbCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCursor, NS_IMDBCURSOR_IID)
+
+#define NS_IMDBPORTTABLECURSOR_IID_STR = "f181a41e-933d-49b3-af93-20d3634b8b78"
+
+#define NS_IMDBPORTTABLECURSOR_IID \
+{0xf181a41e, 0x933d, 0x49b3, \
+{0xaf, 0x93, 0x20, 0xd3, 0x63, 0x4b, 0x8b, 0x78}}
+
+/*| nsIMdbPortTableCursor: cursor class for iterating port tables
+**|
+**|| port: the cursor is associated with a specific port, which can be
+**| set to a different port (which resets the position to -1 so the
+**| next table acquired is the first in the port.
+**|
+|*/
+class nsIMdbPortTableCursor : public nsISupports { // table collection iterator
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBPORTTABLECURSOR_IID)
+// { ===== begin nsIMdbPortTableCursor methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) = 0; // sets pos to -1
+ NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) = 0;
+
+ NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1
+ mdb_scope inRowScope) = 0;
+ NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0;
+ // setting row scope to zero iterates over all row scopes in port
+
+ NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1
+ mdb_kind inTableKind) = 0;
+ NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
+ // setting table kind to zero iterates over all table kinds in row scope
+ // } ----- end attribute methods -----
+
+ // { ----- begin table iteration methods -----
+ NS_IMETHOD NextTable( // get table at next position in the db
+ nsIMdbEnv* ev, // context
+ nsIMdbTable** acqTable) = 0; // the next table in the iteration
+ // } ----- end table iteration methods -----
+
+// } ===== end nsIMdbPortTableCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbPortTableCursor,
+ NS_IMDBPORTTABLECURSOR_IID)
+
+/*| nsIMdbCollection: an object that collects a set of other objects as members.
+**| The main purpose of this base class is to unify the perceived semantics
+**| of tables and rows where their collection behavior is similar. This helps
+**| isolate the mechanics of collection behavior from the other semantics that
+**| are more characteristic of rows and tables.
+**|
+**|| count: the number of objects in a collection is the member count. (Some
+**| collection interfaces call this attribute the 'size', but that can be a
+**| little ambiguous, and counting actual members is harder to confuse.)
+**|
+**|| seed: the seed of a collection is a counter for changes in membership in
+**| a specific collection. This seed should change when members are added to
+**| or removed from a collection, but not when a member changes internal state.
+**| The seed should also change whenever the internal collection of members has
+**| a complex state change that reorders member positions (say by sorting) that
+**| would affect the nature of an iteration over that collection of members.
+**| The purpose of a seed is to inform any outstanding collection cursors that
+**| they might be stale, without incurring the cost of broadcasting an event
+**| notification to such cursors, which would need more data structure support.
+**| Presumably a cursor in a particular mdb code suite has much more direct
+**| access to a collection seed member slot that this abstract COM interface,
+**| so this information is intended more for clients outside mdb that want to
+**| make inferences similar to those made by the collection cursors. The seed
+**| value as an integer magnitude is not very important, and callers should not
+**| assume meaningful information can be derived from an integer value beyond
+**| whether it is equal or different from a previous inspection. A seed uses
+**| integers of many bits in order to make the odds of wrapping and becoming
+**| equal to an earlier seed value have probability that is vanishingly small.
+**|
+**|| port: every collection is associated with a specific database instance.
+**|
+**|| cursor: a subclass of nsIMdbCursor suitable for this specific collection
+**| subclass. The ability to GetCursor() from the base nsIMdbCollection class
+**| is not really as useful as getting a more specifically typed cursor more
+**| directly from the base class without any casting involved. So including
+**| this method here is more for conceptual illustration.
+**|
+**|| oid: every collection has an identity that persists from session to
+**| session. Implementations are probably able to distinguish row IDs from
+**| table IDs, but we don't specify anything official in this regard. A
+**| collection has the same identity for the lifetime of the collection,
+**| unless identity is swapped with another collection by means of a call to
+**| BecomeContent(), which is considered a way to swap a new representation
+**| for an old well-known object. (Even so, only content appears to change,
+**| while the identity seems to stay the same.)
+**|
+**|| become: developers can effectively cause two objects to swap identities,
+**| in order to effect a complete swap between what persistent content is
+**| represented by two oids. The caller should consider this a content swap,
+**| and not identity wap, because identities will seem to stay the same while
+**| only content changes. However, implementations will likely do this
+**| internally by swapping identities. Callers must swap content only
+**| between objects of similar type, such as a row with another row, and a
+**| table with another table, because implementations need not support
+**| cross-object swapping because it might break object name spaces.
+**|
+**|| dropping: when a caller expects a row or table will no longer be used, the
+**| caller can tell the collection to 'drop activity', which means the runtime
+**| object can have its internal representation purged to save memory or any
+**| other resource that is being consumed by the collection's representation.
+**| This has no effect on the collection's persistent content or semantics,
+**| and is only considered a runtime effect. After a collection drops
+**| activity, the object should still be as usable as before (because it has
+**| NOT been closed), but further usage can be expensive to re-instate because
+**| it might involve reallocating space and/or re-reading disk space. But
+**| since this future usage is not expected, the caller does not expect to
+**| pay the extra expense. An implementation can choose to implement
+**| 'dropping activity' in different ways, or even not at all if this
+**| operation is not really feasible. Callers cannot ask objects whether they
+**| are 'dropped' or not, so this should be transparent. (Note that
+**| implementors might fear callers do not really know whether future
+**| usage will occur, and therefore might delay the act of dropping until
+**| the near future, until seeing whether the object is used again
+**| immediately elsewhere. Such use soon after the drop request might cause
+**| the drop to be cancelled.)
+|*/
+class nsIMdbCollection : public nsISupports { // sequence of objects
+public:
+
+// { ===== begin nsIMdbCollection methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+ mdb_seed* outSeed) = 0; // member change count
+ NS_IMETHOD GetCount(nsIMdbEnv* ev,
+ mdb_count* outCount) = 0; // member count
+
+ NS_IMETHOD GetPort(nsIMdbEnv* ev,
+ nsIMdbPort** acqPort) = 0; // collection container
+ // } ----- end attribute methods -----
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+ nsIMdbCursor** acqCursor) = 0; // acquire new cursor instance
+ // } ----- end cursor methods -----
+
+ // { ----- begin ID methods -----
+ NS_IMETHOD GetOid(nsIMdbEnv* ev,
+ mdbOid* outOid) = 0; // read object identity
+ NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+ const mdbOid* inOid) = 0; // exchange content
+ // } ----- end ID methods -----
+
+ // { ----- begin activity dropping methods -----
+ NS_IMETHOD DropActivity( // tell collection usage no longer expected
+ nsIMdbEnv* ev) = 0;
+ // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+};
+
+/*| nsIMdbTable: an ordered collection of rows
+**|
+**|| row scope: an integer token for an atomized string in this database
+**| that names a space for row IDs. This attribute of a table is intended
+**| as guidance metainformation that helps with searching a database for
+**| tables that operate on collections of rows of the specific type. By
+**| convention, a table with a specific row scope is expected to focus on
+**| containing rows that belong to that scope, however exceptions are easily
+**| allowed because all rows in a table are known by both row ID and scope.
+**| (A table with zero row scope is never allowed because this would make it
+**| ambiguous to use a zero row scope when iterating over tables in a port to
+**| indicate that all row scopes should be seen by a cursor.)
+**|
+**|| table kind: an integer token for an atomized string in this database
+**| that names a kind of table as a subset of the associated row scope. This
+**| attribute is intended as guidance metainformation to clarify the role of
+**| this table with respect to other tables in the same row scope, and this
+**| also helps search for such tables in a database. By convention, a table
+**| with a specific table kind has a consistent role for containing rows with
+**| respect to other collections of such rows in the same row scope. Also by
+**| convention, at least one table in a row scope has a table kind purporting
+**| to contain ALL the rows that belong in that row scope, so that at least
+**| one table exists that allows all rows in a scope to be interated over.
+**| (A table with zero table kind is never allowed because this would make it
+**| ambiguous to use a zero table kind when iterating over tables in a port to
+**| indicate that all table kinds in a row scope should be seen by a cursor.)
+**|
+**|| port: every table is considered part of some port that contains the
+**| table, so that closing the containing port will cause the table to be
+**| indirectly closed as well. We make it easy to get the containing port for
+**| a table, because the port supports important semantic interfaces that will
+**| affect how content in table is presented; the most important port context
+**| that affects a table is specified by the set of token to string mappings
+**| that affect all tokens used throughout the database, and which drive the
+**| meanings of row scope, table kind, cell columns, etc.
+**|
+**|| cursor: a cursor that iterates over the rows in this table, where rows
+**| have zero-based index positions from zero to count-1. Making a cursor
+**| with negative position will next iterate over the first row in the table.
+**|
+**|| position: given any position from zero to count-1, a table will return
+**| the row ID and row scope for the row at that position. (One can use the
+**| GetRowAllCells() method to read that row, or else use a row cursor to both
+**| get the row at some position and read its content at the same time.) The
+**| position depends on whether a table is sorted, and upon the actual sort.
+**| Note that moving a row's position is only possible in unsorted tables.
+**|
+**|| row set: every table contains a collection of rows, where a member row is
+**| referenced by the table using the row ID and row scope for the row. No
+**| single table owns a given row instance, because rows are effectively ref-
+**| counted and destroyed only when the last table removes a reference to that
+**| particular row. (But a row can be emptied of all content no matter how
+**| many refs exist, and this might be the next best thing to destruction.)
+**| Once a row exists in a least one table (after NewRow() is called), then it
+**| can be added to any other table by calling AddRow(), or removed from any
+**| table by calling CutRow(), or queried as a member by calling HasRow(). A
+**| row can only be added to a table once, and further additions do nothing and
+**| complain not at all. Cutting a row from a table only does something when
+**| the row was actually a member, and otherwise does nothing silently.
+**|
+**|| row ref count: one can query the number of tables (and/or cells)
+**| containing a row as a member or a child.
+**|
+**|| row content: one can access or modify the cell content in a table's row
+**| by moving content to or from an instance of nsIMdbRow. Note that nsIMdbRow
+**| never represents the actual row inside a table, and this is the reason
+**| why nsIMdbRow instances do not have row IDs or row scopes. So an instance
+**| of nsIMdbRow always and only contains a snapshot of some or all content in
+**| past, present, or future persistent row inside a table. This means that
+**| reading and writing rows in tables has strictly copy semantics, and we
+**| currently do not plan any exceptions for specific performance reasons.
+**|
+**|| sorting: note all rows are assumed sorted by row ID as a secondary
+**| sort following the primary column sort, when table rows are sorted.
+**|
+**|| indexes:
+|*/
+
+
+#define NS_IMDBTABLE_IID_STR = "fe11bc98-d02b-4128-9fac-87042fdf9639"
+
+#define NS_IMDBTABLE_IID \
+{0xfe11bc98, 0xd02b, 0x4128, \
+{0x9f, 0xac, 0x87, 0x04, 0x2f, 0xdf, 0x96, 0x39}}
+
+class nsIMdbTable : public nsIMdbCollection { // a collection of rows
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLE_IID)
+// { ===== begin nsIMdbTable methods =====
+
+ // { ----- begin meta attribute methods -----
+ NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) = 0;
+ NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) = 0;
+
+ NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) = 0;
+ NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) = 0;
+
+ NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) = 0;
+
+ NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) = 0;
+ NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) = 0;
+
+ NS_IMETHOD GetMetaRow(
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ mdbOid* outOid, // output meta row oid, can be nil to suppress output
+ nsIMdbRow** acqRow) = 0; // acquire table's unique singleton meta row
+ // The purpose of a meta row is to support the persistent recording of
+ // meta info about a table as cells put into the distinguished meta row.
+ // Each table has exactly one meta row, which is not considered a member
+ // of the collection of rows inside the table. The only way to tell
+ // whether a row is a meta row is by the fact that it is returned by this
+ // GetMetaRow() method from some table. Otherwise nothing distinguishes
+ // a meta row from any other row. A meta row can be used anyplace that
+ // any other row can be used, and can even be put into other tables (or
+ // the same table) as a table member, if this is useful for some reason.
+ // The first attempt to access a table's meta row using GetMetaRow() will
+ // cause the meta row to be created if it did not already exist. When the
+ // meta row is created, it will have the row oid that was previously
+ // requested for this table's meta row; or if no oid was ever explicitly
+ // specified for this meta row, then a unique oid will be generated in
+ // the row scope named "m" (so obviously MDB clients should not
+ // manually allocate any row IDs from that special meta scope namespace).
+ // The meta row oid can be specified either when the table is created, or
+ // else the first time that GetMetaRow() is called, by passing a non-nil
+ // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's
+ // actual oid is returned in outOid (if this is a non-nil pointer), and
+ // it will be different from inOptionalMetaRowOid when the meta row was
+ // already given a different oid earlier.
+ // } ----- end meta attribute methods -----
+
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
+ // } ----- end row position methods -----
+
+ // { ----- begin row position methods -----
+ NS_IMETHOD PosToOid( // get row member for a table position
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ mdbOid* outOid) = 0; // row oid at the specified position
+
+ NS_IMETHOD OidToPos( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
+
+ NS_IMETHOD PosToRow( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
+
+ NS_IMETHOD RowToPos( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_pos* outPos) = 0; // zero-based ordinal position of row in table
+ // } ----- end row position methods -----
+
+ // { ----- begin oid set methods -----
+ NS_IMETHOD AddOid( // make sure the row with inOid is a table member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid) = 0; // row to ensure membership in table
+
+ NS_IMETHOD HasOid( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_bool* outHasOid) = 0; // whether inOid is a member row
+
+ NS_IMETHOD CutOid( // make sure the row with inOid is not a member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid) = 0; // row to remove from table
+ // } ----- end oid set methods -----
+
+ // { ----- begin row set methods -----
+ NS_IMETHOD NewRow( // create a new row instance in table
+ nsIMdbEnv* ev, // context
+ mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs
+ nsIMdbRow** acqRow) = 0; // create new row
+
+ NS_IMETHOD AddRow( // make sure the row with inOid is a table member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) = 0; // row to ensure membership in table
+
+ NS_IMETHOD HasRow( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_bool* outHasRow) = 0; // whether row is a table member
+
+ NS_IMETHOD CutRow( // make sure the row with inOid is not a member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) = 0; // row to remove from table
+
+ NS_IMETHOD CutAllRows( // remove all rows from the table
+ nsIMdbEnv* ev) = 0; // context
+ // } ----- end row set methods -----
+
+ // { ----- begin hinting methods -----
+ NS_IMETHOD SearchColumnsHint( // advise re future expected search cols
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) = 0; // columns likely to be searched
+
+ NS_IMETHOD SortColumnsHint( // advise re future expected sort columns
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) = 0; // columns for likely sort requests
+
+ NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* ev, // context
+ const void* inLabel) = 0; // intend unique address to match end call
+ // If batch starts nest by virtue of nesting calls in the stack, then
+ // the address of a local variable makes a good batch start label that
+ // can be used at batch end time, and such addresses remain unique.
+
+ NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* ev, // context
+ const void* inLabel) = 0; // label matching start label
+ // Suppose a table is maintaining one or many sort orders for a table,
+ // so that every row added to the table must be inserted in each sort,
+ // and every row cut must be removed from each sort. If a db client
+ // intends to make many such changes before needing any information
+ // about the order or positions of rows inside a table, then a client
+ // might tell the table to start batch changes in order to disable
+ // sorting of rows for the interim. Presumably a table will then do
+ // a full sort of all rows at need when the batch changes end, or when
+ // a surprise request occurs for row position during batch changes.
+ // } ----- end hinting methods -----
+
+ // { ----- begin searching methods -----
+ NS_IMETHOD FindRowMatches( // search variable number of sorted cols
+ nsIMdbEnv* ev, // context
+ const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+ nsIMdbTableRowCursor** acqCursor) = 0; // set of matching rows
+
+ NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches()
+ nsIMdbEnv* ev, // context
+ mdb_count* outCount, // context
+ mdbColumnSet* outColSet) = 0; // caller supplied space to put columns
+ // GetSearchColumns() returns the columns actually searched when the
+ // FindRowMatches() method is called. No more than mColumnSet_Count
+ // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+ // indicates how many slots are present in the column array. The
+ // actual number of search column used by the table is returned in
+ // the outCount parameter; if this number exceeds mColumnSet_Count,
+ // then a caller needs a bigger array to read the entire column set.
+ // The minimum of mColumnSet_Count and outCount is the number slots
+ // in mColumnSet_Columns that were actually written by this method.
+ //
+ // Callers are expected to change this set of columns by calls to
+ // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+ // } ----- end searching methods -----
+
+ // { ----- begin sorting methods -----
+ // sorting: note all rows are assumed sorted by row ID as a secondary
+ // sort following the primary column sort, when table rows are sorted.
+
+ NS_IMETHOD
+ CanSortColumn( // query which column is currently used for sorting
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to query sorting potential
+ mdb_bool* outCanSort) = 0; // whether the column can be sorted
+
+ NS_IMETHOD GetSorting( // view same table in particular sorting
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // requested new column for sorting table
+ nsIMdbSorting** acqSorting) = 0; // acquire sorting for column
+
+ NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches()
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+ nsIMdbSorting* ioSorting) = 0; // requested sorting for some column
+ // SetSearchSorting() attempts to inform the table that ioSorting
+ // should be used during calls to FindRowMatches() for searching
+ // the column which is actually sorted by ioSorting. This method
+ // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+ // because otherwise a caller would not be able to override the
+ // comparison ordering method used during searches. Note that some
+ // database implementations might be unable to use an arbitrarily
+ // specified sort order, either due to schema or runtime interface
+ // constraints, in which case ioSorting might not actually be used.
+ // Presumably ioSorting is an instance that was returned from some
+ // earlier call to nsIMdbTable::GetSorting(). A caller can also
+ // use nsIMdbTable::SearchColumnsHint() to specify desired change
+ // in which columns are sorted and searched by FindRowMatches().
+ //
+ // A caller can pass a nil pointer for ioSorting to request that
+ // column inColumn no longer be used at all by FindRowMatches().
+ // But when ioSorting is non-nil, then inColumn should match the
+ // column actually sorted by ioSorting; when these do not agree,
+ // implementations are instructed to give precedence to the column
+ // specified by ioSorting (so this means callers might just pass
+ // zero for inColumn when ioSorting is also provided, since then
+ // inColumn is both redundant and ignored).
+ // } ----- end sorting methods -----
+
+ // { ----- begin moving methods -----
+ // moving a row does nothing unless a table is currently unsorted
+
+ NS_IMETHOD MoveOid( // change position of row in unsorted table
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row inRowId
+ mdb_pos* outActualPos) = 0; // actual new position of row in table
+
+ NS_IMETHOD MoveRow( // change position of row in unsorted table
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row inRowId
+ mdb_pos* outActualPos) = 0; // actual new position of row in table
+ // } ----- end moving methods -----
+
+ // { ----- begin index methods -----
+ NS_IMETHOD AddIndex( // create a sorting index for column if possible
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to sort by index
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index building
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the index addition will be finished.
+
+ NS_IMETHOD CutIndex( // stop supporting a specific column index
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column with index to be removed
+ nsIMdbThumb** acqThumb) = 0; // acquire thumb for incremental index destroy
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the index removal will be finished.
+
+ NS_IMETHOD HasIndex( // query for current presence of a column index
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outHasIndex) = 0; // whether column has index for this column
+
+
+ NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) = 0; // the column to index if ever sorted
+
+ NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outIndexOnSort) = 0; // whether column has index-on-sort enabled
+
+ NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) = 0; // the column to index if ever sorted
+ // } ----- end index methods -----
+
+// } ===== end nsIMdbTable methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTable, NS_IMDBTABLE_IID)
+
+/*| nsIMdbSorting: a view of a table in some particular sort order. This
+**| row order closely resembles a readonly array of rows with the same row
+**| membership as the underlying table, but in a different order than the
+**| table's explicit row order. But the sorting's row membership changes
+**| whenever the table's membership changes (without any notification, so
+**| keep this in mind when modifying the table).
+**|
+**|| table: every sorting is associated with a particular table. You
+**| cannot change which table is used by a sorting (just ask some new
+**| table for a suitable sorting instance instead).
+**|
+**|| compare: the ordering method used by a sorting, wrapped up in a
+**| abstract plug-in interface. When this was never installed by an
+**| explicit call to SetNewCompare(), a compare object is still returned,
+**| and it might match the compare instance returned by the factory method
+**| nsIMdbFactory::MakeCompare(), which represents a default sort order
+**| (which we fervently hope is consistently ASCII byte ordering).
+**|
+**|| cursor: in case callers are more comfortable with a cursor style
+**| of accessing row members, each sorting will happily return a cursor
+**| instance with behavior very similar to a cursor returned from a call
+**| to nsIMdbTable::GetTableRowCursor(), but with different row order.
+**| A cursor should show exactly the same information as the pos methods.
+**|
+**|| pos: the PosToOid() and PosToRow() methods are just like the table
+**| methods of the same name, except they show rows in the sort order of
+**| the sorting, rather than that of the table. These methods are like
+**| readonly array position accessor's, or like a C++ operator[].
+|*/
+class nsIMdbSorting : public nsIMdbObject { // sorting of some table
+public:
+// { ===== begin nsIMdbSorting methods =====
+
+ // { ----- begin attribute methods -----
+ // sorting: note all rows are assumed sorted by row ID as a secondary
+ // sort following the primary column sort, when table rows are sorted.
+
+ NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
+ NS_IMETHOD GetSortColumn( // query which col is currently sorted
+ nsIMdbEnv* ev, // context
+ mdb_column* outColumn) = 0; // col the table uses for sorting (or zero)
+
+ // } ----- end attribute methods -----
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetSortingRowCursor( // make a cursor, starting at inRowPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbTableRowCursor** acqCursor) = 0; // acquire new cursor instance
+ // A cursor interface turning same info as PosToOid() or PosToRow().
+ // } ----- end row position methods -----
+
+ // { ----- begin row position methods -----
+ NS_IMETHOD PosToOid( // get row member for a table position
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ mdbOid* outOid) = 0; // row oid at the specified position
+
+ NS_IMETHOD PosToRow( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbRow** acqRow) = 0; // acquire row at table position inRowPos
+ // } ----- end row position methods -----
+
+// } ===== end nsIMdbSorting methods =====
+};
+
+/*| nsIMdbTableRowCursor: cursor class for iterating table rows
+**|
+**|| table: the cursor is associated with a specific table, which can be
+**| set to a different table (which resets the position to -1 so the
+**| next row acquired is the first in the table.
+**|
+**|| NextRowId: the rows in the table can be iterated by identity alone,
+**| without actually reading the cells of any row with this method.
+**|
+**|| NextRowCells: read the next row in the table, but only read cells
+**| from the table which are already present in the row (so no new cells
+**| are added to the row, even if they are present in the table). All the
+**| cells will have content specified, even it is the empty string. No
+**| columns will be removed, even if missing from the row (because missing
+**| and empty are semantically equivalent).
+**|
+**|| NextRowAllCells: read the next row in the table, and access all the
+**| cells for this row in the table, adding any missing columns to the row
+**| as needed until all cells are represented. All the
+**| cells will have content specified, even it is the empty string. No
+**| columns will be removed, even if missing from the row (because missing
+**| and empty are semantically equivalent).
+**|
+|*/
+
+#define NS_IMDBTABLEROWCURSOR_IID_STR = "4f325dad-0385-4b62-a992-c914ab93587e"
+
+#define NS_IMDBTABLEROWCURSOR_IID \
+{0x4f325dad, 0x0385, 0x4b62, \
+{0xa9, 0x92, 0xc9, 0x14, 0xab, 0x93, 0x58, 0x7e}}
+
+
+
+class nsIMdbTableRowCursor : public nsISupports { // table row iterator
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID)
+
+// { ===== begin nsIMdbTableRowCursor methods =====
+
+ // { ----- begin attribute methods -----
+ // NS_IMETHOD SetTable(nsIMdbEnv* ev, nsIMdbTable* ioTable) = 0; // sets pos to -1
+ // Method SetTable() cut and made obsolete in keeping with new sorting methods.
+
+ NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin duplicate row removal methods -----
+ NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups?
+ mdb_bool* outCanHaveDups) = 0;
+
+ NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows
+ nsIMdbEnv* ev, // context
+ nsIMdbTableRowCursor** acqCursor) = 0; // acquire clone with no dups
+ // Note that MakeUniqueCursor() is never necessary for a cursor which was
+ // created by table method nsIMdbTable::GetTableRowCursor(), because a table
+ // never contains the same row as a member more than once. However, a cursor
+ // created by table method nsIMdbTable::FindRowMatches() might contain the
+ // same row more than once, because the same row can generate a hit by more
+ // than one column with a matching string prefix. Note this method can
+ // return the very same cursor instance with just an incremented refcount,
+ // when the original cursor could not contain any duplicate rows (calling
+ // CanHaveDupRowMembers() shows this case on a false return). Otherwise
+ // this method returns a different cursor instance. Callers should not use
+ // this MakeUniqueCursor() method lightly, because it tends to defeat the
+ // purpose of lazy programming techniques, since it can force creation of
+ // an explicit row collection in a new cursor's representation, in order to
+ // inspect the row membership and remove any duplicates; this can have big
+ // impact if a collection holds tens of thousands of rows or more, when
+ // the original cursor with dups simply referenced rows indirectly by row
+ // position ranges, without using an explicit row set representation.
+ // Callers are encouraged to use nsIMdbCursor::GetCount() to determine
+ // whether the row collection is very large (tens of thousands), and to
+ // delay calling MakeUniqueCursor() when possible, until a user interface
+ // element actually demands the creation of an explicit set representation.
+ // } ----- end duplicate row removal methods -----
+
+ // { ----- begin oid iteration methods -----
+ NS_IMETHOD NextRowOid( // get row id of next row in the table
+ nsIMdbEnv* ev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+ // } ----- end oid iteration methods -----
+
+ // { ----- begin row iteration methods -----
+ NS_IMETHOD NextRow( // get row cells from table for cells already in row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // acquire next row in table
+ mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+
+ NS_IMETHOD PrevRowOid( // get row id of previous row in the table
+ nsIMdbEnv* ev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+ // } ----- end oid iteration methods -----
+
+ // { ----- begin row iteration methods -----
+ NS_IMETHOD PrevRow( // get row cells from table for cells already in row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // acquire previous row in table
+ mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+
+ // } ----- end row iteration methods -----
+
+ // { ----- begin copy iteration methods -----
+ // NS_IMETHOD NextRowCopy( // put row cells into sink only when already in sink
+ // nsIMdbEnv* ev, // context
+ // nsIMdbRow* ioSinkRow, // sink for row cells read from next row
+ // mdbOid* outOid, // out row oid
+ // mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+ //
+ // NS_IMETHOD NextRowCopyAll( // put all row cells into sink, adding to sink
+ // nsIMdbEnv* ev, // context
+ // nsIMdbRow* ioSinkRow, // sink for row cells read from next row
+ // mdbOid* outOid, // out row oid
+ // mdb_pos* outRowPos) = 0; // zero-based position of the row in table
+ // } ----- end copy iteration methods -----
+
+// } ===== end nsIMdbTableRowCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbTableRowCursor, NS_IMDBTABLEROWCURSOR_IID)
+
+/*| nsIMdbRow: a collection of cells
+**|
+|*/
+
+#define NS_IMDBROW_IID_STR "271e8d6e-183a-40e3-9f18-36913b4c7853"
+
+
+#define NS_IMDBROW_IID \
+{0x271e8d6e, 0x183a, 0x40e3, \
+{0x9f, 0x18, 0x36, 0x91, 0x3b, 0x4c, 0x78, 0x53}}
+
+
+class nsIMdbRow : public nsIMdbCollection { // cell tuple
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROW_IID)
+// { ===== begin nsIMdbRow methods =====
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inCellPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inCellPos, // zero-based ordinal position of cell in row
+ nsIMdbRowCellCursor** acqCursor) = 0; // acquire new cursor instance
+ // } ----- end cursor methods -----
+
+ // { ----- begin column methods -----
+ NS_IMETHOD AddColumn( // make sure a particular column is inside row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to add
+ const mdbYarn* inYarn) = 0; // cell value to install
+
+ NS_IMETHOD CutColumn( // make sure a column is absent from the row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) = 0; // column to ensure absent from row
+
+ NS_IMETHOD CutAllColumns( // remove all columns from the row
+ nsIMdbEnv* ev) = 0; // context
+ // } ----- end column methods -----
+
+ // { ----- begin cell methods -----
+ NS_IMETHOD NewCell( // get cell for specified column, or add new one
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to add
+ nsIMdbCell** acqCell) = 0; // cell column and value
+
+ NS_IMETHOD AddCell( // copy a cell from another row to this row
+ nsIMdbEnv* ev, // context
+ const nsIMdbCell* inCell) = 0; // cell column and value
+
+ NS_IMETHOD GetCell( // find a cell in this row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to find
+ nsIMdbCell** acqCell) = 0; // cell for specified column, or null
+
+ NS_IMETHOD EmptyAllCells( // make all cells in row empty of content
+ nsIMdbEnv* ev) = 0; // context
+ // } ----- end cell methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD AddRow( // add all cells in another row to this one
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioSourceRow) = 0; // row to union with
+
+ NS_IMETHOD SetRow( // make exact duplicate of another row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioSourceRow) = 0; // row to duplicate
+ // } ----- end row methods -----
+
+ // { ----- begin blob methods -----
+ NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn()
+ mdb_column inColumn, // column to write
+ const mdbYarn* inYarn) = 0; // reads from yarn slots
+ // make this text object contain content from the yarn's buffer
+
+ NS_IMETHOD GetCellYarn(nsIMdbEnv* ev,
+ mdb_column inColumn, // column to read
+ mdbYarn* outYarn) = 0; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+ NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev,
+ mdb_column inColumn, // column to alias
+ mdbYarn* outYarn) = 0; // writes ALL yarn slots
+
+ NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn()
+ mdb_column* ioColumn, // next column to read
+ mdbYarn* outYarn) = 0; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+ //
+ // The ioColumn argument is an inout parameter which initially contains the
+ // last column accessed and returns the next column corresponding to the
+ // content read into the yarn. Callers should start with a zero column
+ // value to say 'no previous column', which causes the first column to be
+ // read. Then the value returned in ioColumn is perfect for the next call
+ // to NextCellYarn(), since it will then be the previous column accessed.
+ // Callers need only examine the column token returned to see which cell
+ // in the row is being read into the yarn. When no more columns remain,
+ // and the iteration has ended, ioColumn will return a zero token again.
+ // So iterating over cells starts and ends with a zero column token.
+
+ NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+ nsIMdbEnv* ev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ mdbYarn* outYarn) = 0; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+ // Callers can pass nil for outYarn to indicate no interest in content, so
+ // only the outColumn value is returned. NOTE to subclasses: you must be
+ // able to ignore outYarn when the pointer is nil; please do not crash.
+
+ // } ----- end blob methods -----
+
+// } ===== end nsIMdbRow methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRow, NS_IMDBROW_IID)
+
+/*| nsIMdbRowCellCursor: cursor class for iterating row cells
+**|
+**|| row: the cursor is associated with a specific row, which can be
+**| set to a different row (which resets the position to -1 so the
+**| next cell acquired is the first in the row.
+**|
+**|| NextCell: get the next cell in the row and return its position and
+**| a new instance of a nsIMdbCell to represent this next cell.
+|*/
+
+#define NS_IMDBROWCELLCURSOR_IID_STR "b33371a7-5d63-4d10-85a8-e44dffe75c28"
+
+
+#define NS_IMDBROWCELLCURSOR_IID \
+{0x271e8d6e, 0x5d63, 0x4d10 , \
+{0x85, 0xa8, 0xe4, 0x4d, 0xff, 0xe7, 0x5c, 0x28}}
+
+
+class nsIMdbRowCellCursor : public nsISupports{ // cell collection iterator
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBROWCELLCURSOR_IID)
+// { ===== begin nsIMdbRowCellCursor methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) = 0; // sets pos to -1
+ NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin cell seeking methods -----
+ NS_IMETHOD SeekCell(
+ nsIMdbEnv* ev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ nsIMdbCell** acqCell) = 0; // the cell at inPos
+ // } ----- end cell seeking methods -----
+
+ // { ----- begin cell iteration methods -----
+ NS_IMETHOD NextCell( // get next cell in the row
+ nsIMdbEnv* ev, // context
+ nsIMdbCell** acqCell, // changes to the next cell in the iteration
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos) = 0; // position of cell in row sequence
+
+ NS_IMETHOD PickNextCell( // get next cell in row within filter set
+ nsIMdbEnv* ev, // context
+ nsIMdbCell* ioCell, // changes to the next cell in the iteration
+ const mdbColumnSet* inFilterSet, // col set of actual caller interest
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos) = 0; // position of cell in row sequence
+
+ // Note that inFilterSet should not have too many (many more than 10?)
+ // cols, since this might imply a potential excessive consumption of time
+ // over many cursor calls when looking for column and filter intersection.
+ // } ----- end cell iteration methods -----
+
+// } ===== end nsIMdbRowCellCursor methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbRowCellCursor, NS_IMDBROWCELLCURSOR_IID)
+
+/*| nsIMdbBlob: a base class for objects composed mainly of byte sequence state.
+**| (This provides a base class for nsIMdbCell, so that cells themselves can
+**| be used to set state in another cell, without extracting a buffer.)
+|*/
+class nsIMdbBlob : public nsISupports { // a string with associated charset
+public:
+
+// { ===== begin nsIMdbBlob methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetBlob(nsIMdbEnv* ev,
+ nsIMdbBlob* ioBlob) = 0; // reads inBlob slots
+ // when inBlob is in the same suite, this might be fastest cell-to-cell
+
+ NS_IMETHOD ClearBlob( // make empty (so content has zero length)
+ nsIMdbEnv* ev) = 0;
+ // clearing a yarn is like SetYarn() with empty yarn instance content
+
+ NS_IMETHOD GetBlobFill(nsIMdbEnv* ev,
+ mdb_fill* outFill) = 0; // size of blob
+ // Same value that would be put into mYarn_Fill, if one called GetYarn()
+ // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+
+ NS_IMETHOD SetYarn(nsIMdbEnv* ev,
+ const mdbYarn* inYarn) = 0; // reads from yarn slots
+ // make this text object contain content from the yarn's buffer
+
+ NS_IMETHOD GetYarn(nsIMdbEnv* ev,
+ mdbYarn* outYarn) = 0; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+ NS_IMETHOD AliasYarn(nsIMdbEnv* ev,
+ mdbYarn* outYarn) = 0; // writes ALL yarn slots
+ // AliasYarn() reveals sensitive internal text buffer state to the caller
+ // by setting mYarn_Buf to point into the guts of this text implementation.
+ //
+ // The caller must take great care to avoid writing on this space, and to
+ // avoid calling any method that would cause the state of this text object
+ // to change (say by directly or indirectly setting the text to hold more
+ // content that might grow the size of the buffer and free the old buffer).
+ // In particular, callers should scrupulously avoid making calls into the
+ // mdb interface to write any content while using the buffer pointer found
+ // in the returned yarn instance. Best safe usage involves copying content
+ // into some other kind of external content representation beyond mdb.
+ //
+ // (The original design of this method a week earlier included the concept
+ // of very fast and efficient cooperative locking via a pointer to some lock
+ // member slot. But let's ignore that complexity in the current design.)
+ //
+ // AliasYarn() is specifically intended as the first step in transferring
+ // content from nsIMdbBlob to a nsString representation, without forcing extra
+ // allocations and/or memory copies. (A standard nsIMdbBlob_AsString() utility
+ // will use AliasYarn() as the first step in setting a nsString instance.)
+ //
+ // This is an alternative to the GetYarn() method, which has copy semantics
+ // only; AliasYarn() relaxes a robust safety principle only for performance
+ // reasons, to accommodate the need for callers to transform text content to
+ // some other canonical representation that would necessitate an additional
+ // copy and transformation when such is incompatible with the mdbYarn format.
+ //
+ // The implementation of AliasYarn() should have extremely little overhead
+ // besides the virtual dispatch to the method implementation, and the code
+ // necessary to populate all the mdbYarn member slots with internal buffer
+ // address and metainformation that describes the buffer content. Note that
+ // mYarn_Grow must always be set to nil to indicate no resizing is allowed.
+
+ // } ----- end attribute methods -----
+
+// } ===== end nsIMdbBlob methods =====
+};
+
+/*| nsIMdbCell: the text in a single column of a row. The base nsIMdbBlob
+**| class provides all the interface related to accessing cell text.
+**|
+**|| column: each cell in a row appears in a specific column, where this
+**| column is identified by the an integer mdb_scope value (generated by
+**| the StringToScopeToken() method in the containing nsIMdbPort instance).
+**| Because a row cannot have more than one cell with the same column,
+**| something must give if one calls SetColumn() with an existing column
+**| in the same row. When this happens, the other cell is replaced with
+**| this cell (and the old cell is closed if it has outstanding refs).
+**|
+**|| row: every cell instance is a part of some row, and every cell knows
+**| which row is the parent row. (Note this should be represented by a
+**| weak backpointer, so that outstanding cell references cannot keep a
+**| row open that should be closed. Otherwise we'd have ref graph cycles.)
+**|
+**|| text: a cell can either be text, or it can have a child row or table,
+**| but not both at once. If text is read from a cell with a child, the text
+**| content should be empty (for AliasYarn()) or a description of the type
+**| of child (perhaps "mdb:cell:child:row" or "mdb:cell:child:table").
+**|
+**|| child: a cell might reference another row or a table, rather than text.
+**| The interface for putting and getting children rows and tables was first
+**| defined in the nsIMdbTable interface, but then this was moved to this cell
+**| interface as more natural.
+|*/
+
+
+
+#define NS_IMDBCELL_IID \
+{0xa3b62f71, 0xa181, 0x4a91, \
+{0xb6, 0x6b, 0x27, 0x10, 0x9b, 0x88, 0x98, 0x35}}
+
+#define NS_IMDBCELL_IID_STR = "a3b62f71-a181-4a91-b66b-27109b889835"
+
+class nsIMdbCell : public nsIMdbBlob { // text attribute in row with column scope
+public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMDBTABLEROWCURSOR_IID)
+// { ===== begin nsIMdbCell methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) = 0;
+ NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) = 0;
+
+ NS_IMETHOD GetCellInfo( // all cell metainfo except actual content
+ nsIMdbEnv* ev,
+ mdb_column* outColumn, // the column in the containing row
+ mdb_fill* outBlobFill, // the size of text content in bytes
+ mdbOid* outChildOid, // oid of possible row or table child
+ mdb_bool* outIsRowChild) = 0; // nonzero if child, and a row child
+
+ // Checking all cell metainfo is a good way to avoid forcing a large cell
+ // in to memory when you don't actually want to use the content.
+
+ NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell
+ nsIMdbRow** acqRow) = 0;
+ NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell
+ nsIMdbPort** acqPort) = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin children methods -----
+ NS_IMETHOD HasAnyChild( // does cell have a child instead of text?
+ nsIMdbEnv* ev,
+ mdbOid* outOid, // out id of row or table (or unbound if no child)
+ mdb_bool* outIsRow) = 0; // nonzero if child is a row (rather than a table)
+
+ NS_IMETHOD GetAnyChild( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // child row (or null)
+ nsIMdbTable** acqTable) = 0; // child table (or null)
+
+
+ NS_IMETHOD SetChildRow( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) = 0; // inRow must be bound inside this same db port
+
+ NS_IMETHOD GetChildRow( // access row of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow) = 0; // acquire child row (or nil if no child)
+
+
+ NS_IMETHOD SetChildTable( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbTable* inTable) = 0; // table must be bound inside this same db port
+
+ NS_IMETHOD GetChildTable( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbTable** acqTable) = 0; // acquire child table (or nil if no child)
+ // } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIMdbCell, NS_IMDBTABLEROWCURSOR_IID)
+
+// } %%%%% end C++ abstract class interfaces %%%%%
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MDB_ */
+
diff --git a/db/mork/public/moz.build b/db/mork/public/moz.build
new file mode 100644
index 000000000..7212c6eff
--- /dev/null
+++ b/db/mork/public/moz.build
@@ -0,0 +1,9 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'mdb.h',
+]
+
diff --git a/db/mork/src/mork.h b/db/mork/src/mork.h
new file mode 100644
index 000000000..606e8f3d9
--- /dev/null
+++ b/db/mork/src/mork.h
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORK_
+#define _MORK_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#include "nscore.h"
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// { %%%%% begin disable unused param warnings %%%%%
+#define MORK_USED_1(x) (void)(&x)
+#define MORK_USED_2(x,y) (void)(&x);(void)(&y);
+#define MORK_USED_3(x,y,z) (void)(&x);(void)(&y);(void)(&z);
+#define MORK_USED_4(w,x,y,z) (void)(&w);(void)(&x);(void)(&y);(void)(&z);
+
+// } %%%%% end disable unused param warnings %%%%%
+
+// { %%%%% begin macro for finding class member offset %%%%%
+
+/*| OffsetOf: the unsigned integer offset of a class or struct
+**| field from the beginning of that class or struct. This is
+**| the same as the similarly named public domain IronDoc macro,
+**| and is also the same as another macro appearing in stdlib.h.
+**| We want these offsets so we can correctly convert pointers
+**| to member slots back into pointers to enclosing objects, and
+**| have this exactly match what the compiler thinks is true.
+**|
+**|| Bascially we are asking the compiler to determine the offset at
+**| compile time, and we use the definition of address artithmetic
+**| to do this. By casting integer zero to a pointer of type obj*,
+**| we can reference the address of a slot in such an object that
+**| is hypothetically physically placed at address zero, but without
+**| actually dereferencing a memory location. The absolute address
+**| of slot is the same as offset of that slot, when the object is
+**| placed at address zero.
+|*/
+#define mork_OffsetOf(obj,slot) ((unsigned int)&((obj*) 0)->slot)
+
+// } %%%%% end macro for finding class member offset %%%%%
+
+// { %%%%% begin specific-size integer scalar typedefs %%%%%
+typedef unsigned char mork_u1; // make sure this is one byte
+typedef unsigned short mork_u2; // make sure this is two bytes
+typedef short mork_i2; // make sure this is two bytes
+typedef uint32_t mork_u4; // make sure this is four bytes
+typedef int32_t mork_i4; // make sure this is four bytes
+typedef PRWord mork_ip; // make sure sizeof(mork_ip) == sizeof(void*)
+
+typedef mork_u1 mork_ch; // small byte-sized character (never wide)
+typedef mork_u1 mork_flags; // one byte's worth of predicate bit flags
+
+typedef mork_u2 mork_base; // 2-byte magic class signature slot in object
+typedef mork_u2 mork_derived; // 2-byte magic class signature slot in object
+
+typedef mork_u4 mork_token; // unsigned token for atomized string
+typedef mork_token mork_scope; // token used to id scope for rows
+typedef mork_token mork_kind; // token used to id kind for tables
+typedef mork_token mork_cscode; // token used to id charset names
+typedef mork_token mork_aid; // token used to id atomize cell values
+
+typedef mork_token mork_column; // token used to id columns for rows
+typedef mork_column mork_delta; // mork_column plus mork_change
+
+typedef mork_token mork_color; // bead ID
+#define morkColor_kNone ((mork_color) 0)
+
+typedef mork_u4 mork_magic; // unsigned magic signature
+
+typedef mork_u4 mork_seed; // unsigned collection change counter
+typedef mork_u4 mork_count; // unsigned collection member count
+typedef mork_count mork_num; // synonym for count
+typedef mork_u4 mork_size; // unsigned physical media size
+typedef mork_u4 mork_fill; // unsigned logical content size
+typedef mork_u4 mork_more; // more available bytes for larger buffer
+
+typedef mdb_u4 mork_percent; // 0..100, with values >100 same as 100
+
+typedef mork_i4 mork_pos; // negative means "before first" (at zero pos)
+typedef mork_i4 mork_line; // negative means "before first line in file"
+
+typedef mork_u1 mork_usage; // 1-byte magic usage signature slot in object
+typedef mork_u1 mork_access; // 1-byte magic access signature slot in object
+
+typedef mork_u1 mork_change; // add, cut, put, set, nil
+typedef mork_u1 mork_priority; // 0..9, for a total of ten different values
+
+typedef mork_u1 mork_able; // on, off, asleep (clone IronDoc's fe_able)
+typedef mork_u1 mork_load; // dirty or clean (clone IronDoc's fe_load)
+// } %%%%% end specific-size integer scalar typedefs %%%%%
+
+// 'test' is a public domain Mithril for key equality tests in probe maps
+typedef mork_i2 mork_test; /* neg=>kVoid, zero=>kHit, pos=>kMiss */
+
+#define morkTest_kVoid ((mork_test) -1) /* -1: nil key slot, no key order */
+#define morkTest_kHit ((mork_test) 0) /* 0: keys are equal, a map hit */
+#define morkTest_kMiss ((mork_test) 1) /* 1: keys not equal, a map miss */
+
+// { %%%%% begin constants for Mork scalar types %%%%%
+#define morkPriority_kHi ((mork_priority) 0) /* best priority */
+#define morkPriority_kMin ((mork_priority) 0) /* best priority is smallest */
+
+#define morkPriority_kLo ((mork_priority) 9) /* worst priority */
+#define morkPriority_kMax ((mork_priority) 9) /* worst priority is biggest */
+
+#define morkPriority_kCount 10 /* number of distinct priority values */
+
+#define morkAble_kEnabled ((mork_able) 0x55) /* same as IronDoc constant */
+#define morkAble_kDisabled ((mork_able) 0xAA) /* same as IronDoc constant */
+#define morkAble_kAsleep ((mork_able) 0x5A) /* same as IronDoc constant */
+
+#define morkChange_kAdd 'a' /* add member */
+#define morkChange_kCut 'c' /* cut member */
+#define morkChange_kPut 'p' /* put member */
+#define morkChange_kSet 's' /* set all members */
+#define morkChange_kNil 0 /* no change in this member */
+#define morkChange_kDup 'd' /* duplicate changes have no effect */
+// kDup is intended to replace another change constant in an object as a
+// conclusion about change feasibility while staging intended alterations.
+
+#define morkLoad_kDirty ((mork_load) 0xDD) /* same as IronDoc constant */
+#define morkLoad_kClean ((mork_load) 0x22) /* same as IronDoc constant */
+
+#define morkAccess_kOpen 'o'
+#define morkAccess_kClosing 'c'
+#define morkAccess_kShut 's'
+#define morkAccess_kDead 'd'
+// } %%%%% end constants for Mork scalar types %%%%%
+
+// { %%%%% begin non-specific-size integer scalar typedefs %%%%%
+typedef int mork_char; // nominal type for ints used to hold input byte
+#define morkChar_IsWhite(c) \
+ ((c) == 0xA || (c) == 0x9 || (c) == 0xD || (c) == ' ')
+// } %%%%% end non-specific-size integer scalar typedefs %%%%%
+
+// { %%%%% begin mdb-driven scalar typedefs %%%%%
+// easier to define bool exactly the same as mdb:
+typedef mdb_bool mork_bool; // unsigned byte with zero=false, nonzero=true
+
+/* canonical boolean constants provided only for code clarity: */
+#define morkBool_kTrue ((mork_bool) 1) /* actually any nonzero means true */
+#define morkBool_kFalse ((mork_bool) 0) /* only zero means false */
+
+// mdb clients can assign these, so we cannot pick maximum size:
+typedef mdb_id mork_id; // unsigned object identity in a scope
+typedef mork_id mork_rid; // unsigned row identity inside scope
+typedef mork_id mork_tid; // unsigned table identity inside scope
+typedef mork_id mork_gid; // unsigned group identity without any scope
+
+// we only care about neg, zero, pos -- so we don't care about size:
+typedef mdb_order mork_order; // neg:lessthan, zero:equalto, pos:greaterthan
+// } %%%%% end mdb-driven scalar typedefs %%%%%
+
+#define morkId_kMinusOne ((mdb_id) -1)
+
+// { %%%%% begin class forward defines %%%%%
+// try to put these in alphabetical order for easier examination:
+class morkMid;
+class morkAtom;
+class morkAtomSpace;
+class morkBookAtom;
+class morkBuf;
+class morkBuilder;
+class morkCell;
+class morkCellObject;
+class morkCursor;
+class morkEnv;
+class morkFactory;
+class morkFile;
+class morkHandle;
+class morkHandleFace; // just an opaque cookie type
+class morkHandleFrame;
+class morkHashArrays;
+class morkMap;
+class morkNode;
+class morkObject;
+class morkOidAtom;
+class morkParser;
+class morkPool;
+class morkPlace;
+class morkPort;
+class morkPortTableCursor;
+class morkProbeMap;
+class morkRow;
+class morkRowCellCursor;
+class morkRowObject;
+class morkRowSpace;
+class morkSorting;
+class morkSortingRowCursor;
+class morkSpace;
+class morkSpan;
+class morkStore;
+class morkStream;
+class morkTable;
+class morkTableChange;
+class morkTableRowCursor;
+class morkThumb;
+class morkWriter;
+class morkZone;
+// } %%%%% end class forward defines %%%%%
+
+// include this config file last for platform & environment specific stuff:
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORK_ */
diff --git a/db/mork/src/morkArray.cpp b/db/mork/src/morkArray.cpp
new file mode 100644
index 000000000..5b83b85e7
--- /dev/null
+++ b/db/mork/src/morkArray.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nscore.h"
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkArray::CloseMorkNode(morkEnv* ev) // CloseTable() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseArray(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkArray::~morkArray() // assert CloseTable() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+ MORK_ASSERT(mArray_Slots==0);
+}
+
+/*public non-poly*/
+morkArray::morkArray(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mArray_Slots( 0 )
+, mArray_Heap( 0 )
+, mArray_Fill( 0 )
+, mArray_Size( 0 )
+, mArray_Seed( (mork_u4)NS_PTR_TO_INT32(this) ) // "random" integer assignment
+{
+ if ( ev->Good() )
+ {
+ if ( ioSlotHeap )
+ {
+ nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mArray_Heap);
+ if ( ev->Good() )
+ {
+ if ( inSize < 3 )
+ inSize = 3;
+ mdb_size byteSize = inSize * sizeof(void*);
+ void** block = 0;
+ ioSlotHeap->Alloc(ev->AsMdbEnv(), byteSize, (void**) &block);
+ if ( block && ev->Good() )
+ {
+ mArray_Slots = block;
+ mArray_Size = inSize;
+ MORK_MEMSET(mArray_Slots, 0, byteSize);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kArray;
+ }
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+/*public non-poly*/ void
+morkArray::CloseArray(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ if ( mArray_Heap && mArray_Slots )
+ mArray_Heap->Free(ev->AsMdbEnv(), mArray_Slots);
+
+ mArray_Slots = 0;
+ mArray_Size = 0;
+ mArray_Fill = 0;
+ ++mArray_Seed;
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mArray_Heap);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkArray::NonArrayTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkArray");
+}
+
+/*static*/ void
+morkArray::IndexBeyondEndError(morkEnv* ev)
+{
+ ev->NewError("array index beyond end");
+}
+
+/*static*/ void
+morkArray::NilSlotsAddressError(morkEnv* ev)
+{
+ ev->NewError("nil mArray_Slots");
+}
+
+/*static*/ void
+morkArray::FillBeyondSizeError(morkEnv* ev)
+{
+ ev->NewError("mArray_Fill > mArray_Size");
+}
+
+mork_bool
+morkArray::Grow(morkEnv* ev, mork_size inNewSize)
+// Grow() returns true if capacity becomes >= inNewSize and ev->Good()
+{
+ if ( ev->Good() && inNewSize > mArray_Size ) // make array larger?
+ {
+ if ( mArray_Fill <= mArray_Size ) // fill and size fit the invariant?
+ {
+ if (mArray_Size <= 3)
+ inNewSize = mArray_Size + 3;
+ else
+ inNewSize = mArray_Size * 2;// + 3; // try doubling size here - used to grow by 3
+
+ mdb_size newByteSize = inNewSize * sizeof(void*);
+ void** newBlock = 0;
+ mArray_Heap->Alloc(ev->AsMdbEnv(), newByteSize, (void**) &newBlock);
+ if ( newBlock && ev->Good() ) // okay new block?
+ {
+ void** oldSlots = mArray_Slots;
+ void** oldEnd = oldSlots + mArray_Fill;
+
+ void** newSlots = newBlock;
+ void** newEnd = newBlock + inNewSize;
+
+ while ( oldSlots < oldEnd )
+ *newSlots++ = *oldSlots++;
+
+ while ( newSlots < newEnd )
+ *newSlots++ = (void*) 0;
+
+ oldSlots = mArray_Slots;
+ mArray_Size = inNewSize;
+ mArray_Slots = newBlock;
+ mArray_Heap->Free(ev->AsMdbEnv(), oldSlots);
+ }
+ }
+ else
+ this->FillBeyondSizeError(ev);
+ }
+ ++mArray_Seed; // always modify seed, since caller intends to add slots
+ return ( ev->Good() && mArray_Size >= inNewSize );
+}
+
+void*
+morkArray::SafeAt(morkEnv* ev, mork_pos inPos)
+{
+ if ( mArray_Slots )
+ {
+ if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill )
+ return mArray_Slots[ inPos ];
+ else
+ this->IndexBeyondEndError(ev);
+ }
+ else
+ this->NilSlotsAddressError(ev);
+
+ return (void*) 0;
+}
+
+void
+morkArray::SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot)
+{
+ if ( mArray_Slots )
+ {
+ if ( inPos >= 0 && inPos < (mork_pos) mArray_Fill )
+ {
+ mArray_Slots[ inPos ] = ioSlot;
+ ++mArray_Seed;
+ }
+ else
+ this->IndexBeyondEndError(ev);
+ }
+ else
+ this->NilSlotsAddressError(ev);
+}
+
+mork_pos
+morkArray::AppendSlot(morkEnv* ev, void* ioSlot)
+{
+ mork_pos outPos = -1;
+ if ( mArray_Slots )
+ {
+ mork_fill fill = mArray_Fill;
+ if ( this->Grow(ev, fill+1) )
+ {
+ outPos = (mork_pos) fill;
+ mArray_Slots[ fill ] = ioSlot;
+ mArray_Fill = fill + 1;
+ // note Grow() increments mArray_Seed
+ }
+ }
+ else
+ this->NilSlotsAddressError(ev);
+
+ return outPos;
+}
+
+void
+morkArray::AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot)
+{
+ if ( mArray_Slots )
+ {
+ mork_fill fill = mArray_Fill;
+ if ( this->Grow(ev, fill+1) )
+ {
+ void** slot = mArray_Slots; // the slot vector
+ void** end = slot + fill; // one past the last used array slot
+ slot += inPos; // the slot to be added
+
+ while ( --end >= slot ) // another slot to move upward?
+ end[ 1 ] = *end;
+
+ *slot = ioSlot;
+ mArray_Fill = fill + 1;
+ // note Grow() increments mArray_Seed
+ }
+ }
+ else
+ this->NilSlotsAddressError(ev);
+}
+
+void
+morkArray::CutSlot(morkEnv* ev, mork_pos inPos)
+{
+ MORK_USED_1(ev);
+ mork_fill fill = mArray_Fill;
+ if ( inPos >= 0 && inPos < (mork_pos) fill ) // cutting slot in used array portion?
+ {
+ void** slot = mArray_Slots; // the slot vector
+ void** end = slot + fill; // one past the last used array slot
+ slot += inPos; // the slot to be cut
+
+ while ( ++slot < end ) // another slot to move downward?
+ slot[ -1 ] = *slot;
+
+ slot[ -1 ] = 0; // clear the last used slot which is now unused
+
+ // note inPos<fill implies fill>0, so fill-1 must be nonnegative:
+ mArray_Fill = fill - 1;
+ ++mArray_Seed;
+ }
+}
+
+void
+morkArray::CutAllSlots(morkEnv* ev)
+{
+ if ( mArray_Slots )
+ {
+ if ( mArray_Fill <= mArray_Size )
+ {
+ mdb_size oldByteSize = mArray_Fill * sizeof(void*);
+ MORK_MEMSET(mArray_Slots, 0, oldByteSize);
+ }
+ else
+ this->FillBeyondSizeError(ev);
+ }
+ else
+ this->NilSlotsAddressError(ev);
+
+ ++mArray_Seed;
+ mArray_Fill = 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkArray.h b/db/mork/src/morkArray.h
new file mode 100644
index 000000000..d987b5d25
--- /dev/null
+++ b/db/mork/src/morkArray.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKARRAY_
+#define _MORKARRAY_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kArray /*i*/ 0x4179 /* ascii 'Ay' */
+
+class morkArray : public morkNode { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+ void** mArray_Slots; // array of pointers
+ nsIMdbHeap* mArray_Heap; // required heap for allocating mArray_Slots
+ mork_fill mArray_Fill; // logical count of used slots in mArray_Slots
+ mork_size mArray_Size; // physical count of mArray_Slots ( >= Fill)
+ mork_seed mArray_Seed; // change counter for syncing with iterators
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseArray()
+ virtual ~morkArray(); // assert that close executed earlier
+
+public: // morkArray construction & destruction
+ morkArray(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, mork_size inSize, nsIMdbHeap* ioSlotHeap);
+ void CloseArray(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkArray(const morkArray& other);
+ morkArray& operator=(const morkArray& other);
+
+public: // dynamic type identification
+ mork_bool IsArray() const
+ { return IsNode() && mNode_Derived == morkDerived_kArray; }
+// } ===== end morkNode methods =====
+
+public: // typing & errors
+ static void NonArrayTypeError(morkEnv* ev);
+ static void IndexBeyondEndError(morkEnv* ev);
+ static void NilSlotsAddressError(morkEnv* ev);
+ static void FillBeyondSizeError(morkEnv* ev);
+
+public: // other table row cursor methods
+
+ mork_fill Length() const { return mArray_Fill; }
+ mork_size Capacity() const { return mArray_Size; }
+
+ mork_bool Grow(morkEnv* ev, mork_size inNewSize);
+ // Grow() returns true if capacity becomes >= inNewSize and ev->Good()
+
+ void* At(mork_pos inPos) const { return mArray_Slots[ inPos ]; }
+ void AtPut(mork_pos inPos, void* ioSlot)
+ { mArray_Slots[ inPos ] = ioSlot; }
+
+ void* SafeAt(morkEnv* ev, mork_pos inPos);
+ void SafeAtPut(morkEnv* ev, mork_pos inPos, void* ioSlot);
+
+ mork_pos AppendSlot(morkEnv* ev, void* ioSlot);
+ void AddSlot(morkEnv* ev, mork_pos inPos, void* ioSlot);
+ void CutSlot(morkEnv* ev, mork_pos inPos);
+ void CutAllSlots(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakArray(morkArray* me,
+ morkEnv* ev, morkArray** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongArray(morkArray* me,
+ morkEnv* ev, morkArray** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
diff --git a/db/mork/src/morkAtom.cpp b/db/mork/src/morkAtom.cpp
new file mode 100644
index 000000000..b0621c3e9
--- /dev/null
+++ b/db/mork/src/morkAtom.cpp
@@ -0,0 +1,523 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/* static */
+mork_bool morkAtom::GetYarn(const morkAtom* atom, mdbYarn* outYarn)
+{
+ const void* source = 0;
+ mdb_fill fill = 0;
+ mdb_cscode form = 0;
+ outYarn->mYarn_More = 0;
+
+ if (atom) {
+ if (atom->IsWeeBook()) {
+ morkWeeBookAtom* weeBook = (morkWeeBookAtom*)atom;
+ source = weeBook->mWeeBookAtom_Body;
+ fill = weeBook->mAtom_Size;
+ } else if (atom->IsBigBook()) {
+ morkBigBookAtom* bigBook = (morkBigBookAtom*)atom;
+ source = bigBook->mBigBookAtom_Body;
+ fill = bigBook->mBigBookAtom_Size;
+ form = bigBook->mBigBookAtom_Form;
+ } else if (atom->IsWeeAnon()) {
+ morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*)atom;
+ source = weeAnon->mWeeAnonAtom_Body;
+ fill = weeAnon->mAtom_Size;
+ } else if (atom->IsBigAnon()) {
+ morkBigAnonAtom* bigAnon = (morkBigAnonAtom*)atom;
+ source = bigAnon->mBigAnonAtom_Body;
+ fill = bigAnon->mBigAnonAtom_Size;
+ form = bigAnon->mBigAnonAtom_Form;
+ }
+ }
+
+ if ( source && fill ) // have an atom with nonempty content?
+ {
+ // if we have too many bytes, and yarn seems growable:
+ if ( fill > outYarn->mYarn_Size && outYarn->mYarn_Grow ) // try grow?
+ (*outYarn->mYarn_Grow)(outYarn, (mdb_size) fill); // request bigger
+
+ mdb_size size = outYarn->mYarn_Size; // max dest size
+ if ( fill > size ) // too much atom content?
+ {
+ outYarn->mYarn_More = fill - size; // extra atom bytes omitted
+ fill = size; // copy no more bytes than size of yarn buffer
+ }
+ void* dest = outYarn->mYarn_Buf; // where bytes are going
+ if ( !dest ) // nil destination address buffer?
+ fill = 0; // we can't write any content at all
+
+ if ( fill ) // anything to copy?
+ MORK_MEMCPY(dest, source, fill); // copy fill bytes to yarn
+
+ outYarn->mYarn_Fill = fill; // tell yarn size of copied content
+ }
+ else // no content to put into the yarn
+ {
+ outYarn->mYarn_Fill = 0; // tell yarn that atom has no bytes
+ }
+ outYarn->mYarn_Form = form; // always update the form slot
+
+ return ( source != 0 );
+}
+
+/* static */
+mork_bool
+morkAtom::AliasYarn(const morkAtom* atom, mdbYarn* outYarn)
+{
+ outYarn->mYarn_More = 0;
+ outYarn->mYarn_Form = 0;
+
+ if ( atom )
+ {
+ if ( atom->IsWeeBook() )
+ {
+ morkWeeBookAtom* weeBook = (morkWeeBookAtom*) atom;
+ outYarn->mYarn_Buf = weeBook->mWeeBookAtom_Body;
+ outYarn->mYarn_Fill = weeBook->mAtom_Size;
+ outYarn->mYarn_Size = weeBook->mAtom_Size;
+ }
+ else if ( atom->IsBigBook() )
+ {
+ morkBigBookAtom* bigBook = (morkBigBookAtom*) atom;
+ outYarn->mYarn_Buf = bigBook->mBigBookAtom_Body;
+ outYarn->mYarn_Fill = bigBook->mBigBookAtom_Size;
+ outYarn->mYarn_Size = bigBook->mBigBookAtom_Size;
+ outYarn->mYarn_Form = bigBook->mBigBookAtom_Form;
+ }
+ else if ( atom->IsWeeAnon() )
+ {
+ morkWeeAnonAtom* weeAnon = (morkWeeAnonAtom*) atom;
+ outYarn->mYarn_Buf = weeAnon->mWeeAnonAtom_Body;
+ outYarn->mYarn_Fill = weeAnon->mAtom_Size;
+ outYarn->mYarn_Size = weeAnon->mAtom_Size;
+ }
+ else if ( atom->IsBigAnon() )
+ {
+ morkBigAnonAtom* bigAnon = (morkBigAnonAtom*) atom;
+ outYarn->mYarn_Buf = bigAnon->mBigAnonAtom_Body;
+ outYarn->mYarn_Fill = bigAnon->mBigAnonAtom_Size;
+ outYarn->mYarn_Size = bigAnon->mBigAnonAtom_Size;
+ outYarn->mYarn_Form = bigAnon->mBigAnonAtom_Form;
+ }
+ else
+ atom = 0; // show desire to put empty content in yarn
+ }
+
+ if ( !atom ) // empty content for yarn?
+ {
+ outYarn->mYarn_Buf = 0;
+ outYarn->mYarn_Fill = 0;
+ outYarn->mYarn_Size = 0;
+ // outYarn->mYarn_Grow = 0; // please don't modify the Grow slot
+ }
+ return ( atom != 0 );
+}
+
+mork_aid
+morkAtom::GetBookAtomAid() const // zero or book atom's ID
+{
+ return ( this->IsBook() )? ((morkBookAtom*) this)->mBookAtom_Id : 0;
+}
+
+mork_scope
+morkAtom::GetBookAtomSpaceScope(morkEnv* ev) const // zero or book's space's scope
+{
+ mork_scope outScope = 0;
+ if ( this->IsBook() )
+ {
+ const morkBookAtom* bookAtom = (const morkBookAtom*) this;
+ morkAtomSpace* space = bookAtom->mBookAtom_Space;
+ if ( space->IsAtomSpace() )
+ outScope = space->SpaceScope();
+ else
+ space->NonAtomSpaceTypeError(ev);
+ }
+
+ return outScope;
+}
+
+void
+morkAtom::MakeCellUseForever(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mAtom_CellUses = morkAtom_kForeverCellUses;
+}
+
+mork_u1
+morkAtom::AddCellUse(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not already maxed out?
+ ++mAtom_CellUses;
+
+ return mAtom_CellUses;
+}
+
+mork_u1
+morkAtom::CutCellUse(morkEnv* ev)
+{
+ if ( mAtom_CellUses ) // any outstanding uses to cut?
+ {
+ if ( mAtom_CellUses < morkAtom_kMaxCellUses ) // not frozen at max?
+ --mAtom_CellUses;
+ }
+ else
+ this->CellUsesUnderflowWarning(ev);
+
+ return mAtom_CellUses;
+}
+
+/*static*/ void
+morkAtom::CellUsesUnderflowWarning(morkEnv* ev)
+{
+ ev->NewWarning("mAtom_CellUses underflow");
+}
+
+/*static*/ void
+morkAtom::BadAtomKindError(morkEnv* ev)
+{
+ ev->NewError("bad mAtom_Kind");
+}
+
+/*static*/ void
+morkAtom::ZeroAidError(morkEnv* ev)
+{
+ ev->NewError("zero atom ID");
+}
+
+/*static*/ void
+morkAtom::AtomSizeOverflowError(morkEnv* ev)
+{
+ ev->NewError("atom mAtom_Size overflow");
+}
+
+void
+morkOidAtom::InitRowOidAtom(morkEnv* ev, const mdbOid& inOid)
+{
+ MORK_USED_1(ev);
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindRowOid;
+ mAtom_Change = morkChange_kNil;
+ mAtom_Size = 0;
+ mOidAtom_Oid = inOid; // bitwise copy
+}
+
+void
+morkOidAtom::InitTableOidAtom(morkEnv* ev, const mdbOid& inOid)
+{
+ MORK_USED_1(ev);
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindTableOid;
+ mAtom_Change = morkChange_kNil;
+ mAtom_Size = 0;
+ mOidAtom_Oid = inOid; // bitwise copy
+}
+
+void
+morkWeeAnonAtom::InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf)
+{
+ mAtom_Kind = 0;
+ mAtom_Change = morkChange_kNil;
+ if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize )
+ {
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindWeeAnon;
+ mork_size size = inBuf.mBuf_Fill;
+ mAtom_Size = (mork_u1) size;
+ if ( size && inBuf.mBuf_Body )
+ MORK_MEMCPY(mWeeAnonAtom_Body, inBuf.mBuf_Body, size);
+
+ mWeeAnonAtom_Body[ size ] = 0;
+ }
+ else
+ this->AtomSizeOverflowError(ev);
+}
+
+void
+morkBigAnonAtom::InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm)
+{
+ MORK_USED_1(ev);
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindBigAnon;
+ mAtom_Change = morkChange_kNil;
+ mAtom_Size = 0;
+ mBigAnonAtom_Form = inForm;
+ mork_size size = inBuf.mBuf_Fill;
+ mBigAnonAtom_Size = size;
+ if ( size && inBuf.mBuf_Body )
+ MORK_MEMCPY(mBigAnonAtom_Body, inBuf.mBuf_Body, size);
+
+ mBigAnonAtom_Body[ size ] = 0;
+}
+
+/*static*/ void
+morkBookAtom::NonBookAtomTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkBookAtom");
+}
+
+mork_u4
+morkBookAtom::HashFormAndBody(morkEnv* ev) const
+{
+ // This hash is obviously a variation of the dragon book string hash.
+ // (I won't bother to explain or rationalize this usage for you.)
+
+ mork_u4 outHash = 0; // hash value returned
+ unsigned char c; // next character
+ const mork_u1* body; // body of bytes to hash
+ mork_size size = 0; // the number of bytes to hash
+
+ if ( this->IsWeeBook() )
+ {
+ size = mAtom_Size;
+ body = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body;
+ }
+ else if ( this->IsBigBook() )
+ {
+ size = ((const morkBigBookAtom*) this)->mBigBookAtom_Size;
+ body = ((const morkBigBookAtom*) this)->mBigBookAtom_Body;
+ }
+ else if ( this->IsFarBook() )
+ {
+ size = ((const morkFarBookAtom*) this)->mFarBookAtom_Size;
+ body = ((const morkFarBookAtom*) this)->mFarBookAtom_Body;
+ }
+ else
+ {
+ this->NonBookAtomTypeError(ev);
+ return 0;
+ }
+
+ const mork_u1* end = body + size;
+ while ( body < end )
+ {
+ c = *body++;
+ outHash <<= 4;
+ outHash += c;
+ mork_u4 top = outHash & 0xF0000000L; // top four bits
+ if ( top ) // any of high four bits equal to one?
+ {
+ outHash ^= (top >> 24); // fold down high bits
+ outHash ^= top; // zero top four bits
+ }
+ }
+
+ return outHash;
+}
+
+mork_bool
+morkBookAtom::EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const
+{
+ mork_bool outEqual = morkBool_kFalse;
+
+ const mork_u1* body = 0; // body of inAtom bytes to compare
+ mork_size size; // the number of inAtom bytes to compare
+ mork_cscode form; // nominal charset for ioAtom
+
+ if ( inAtom->IsWeeBook() )
+ {
+ size = inAtom->mAtom_Size;
+ body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body;
+ form = 0;
+ }
+ else if ( inAtom->IsBigBook() )
+ {
+ size = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Size;
+ body = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Body;
+ form = ((const morkBigBookAtom*) inAtom)->mBigBookAtom_Form;
+ }
+ else if ( inAtom->IsFarBook() )
+ {
+ size = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Size;
+ body = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Body;
+ form = ((const morkFarBookAtom*) inAtom)->mFarBookAtom_Form;
+ }
+ else
+ {
+ inAtom->NonBookAtomTypeError(ev);
+ return morkBool_kFalse;
+ }
+
+ const mork_u1* thisBody = 0; // body of bytes in this to compare
+ mork_size thisSize; // the number of bytes in this to compare
+ mork_cscode thisForm; // nominal charset for this atom
+
+ if ( this->IsWeeBook() )
+ {
+ thisSize = mAtom_Size;
+ thisBody = ((const morkWeeBookAtom*) this)->mWeeBookAtom_Body;
+ thisForm = 0;
+ }
+ else if ( this->IsBigBook() )
+ {
+ thisSize = ((const morkBigBookAtom*) this)->mBigBookAtom_Size;
+ thisBody = ((const morkBigBookAtom*) this)->mBigBookAtom_Body;
+ thisForm = ((const morkBigBookAtom*) this)->mBigBookAtom_Form;
+ }
+ else if ( this->IsFarBook() )
+ {
+ thisSize = ((const morkFarBookAtom*) this)->mFarBookAtom_Size;
+ thisBody = ((const morkFarBookAtom*) this)->mFarBookAtom_Body;
+ thisForm = ((const morkFarBookAtom*) this)->mFarBookAtom_Form;
+ }
+ else
+ {
+ this->NonBookAtomTypeError(ev);
+ return morkBool_kFalse;
+ }
+
+ // if atoms are empty, form is irrelevant
+ if ( body && thisBody && size == thisSize && (!size || form == thisForm ))
+ outEqual = (MORK_MEMCMP(body, thisBody, size) == 0);
+
+ return outEqual;
+}
+
+
+void
+morkBookAtom::CutBookAtomFromSpace(morkEnv* ev)
+{
+ morkAtomSpace* space = mBookAtom_Space;
+ if ( space )
+ {
+ mBookAtom_Space = 0;
+ space->mAtomSpace_AtomBodies.CutAtom(ev, this);
+ space->mAtomSpace_AtomAids.CutAtom(ev, this);
+ }
+ else
+ ev->NilPointerError();
+}
+
+morkWeeBookAtom::morkWeeBookAtom(mork_aid inAid)
+{
+ mAtom_Kind = morkAtom_kKindWeeBook;
+ mAtom_CellUses = 0;
+ mAtom_Change = morkChange_kNil;
+ mAtom_Size = 0;
+
+ mBookAtom_Space = 0;
+ mBookAtom_Id = inAid;
+
+ mWeeBookAtom_Body[ 0 ] = 0;
+}
+
+void
+morkWeeBookAtom::InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ morkAtomSpace* ioSpace, mork_aid inAid)
+{
+ mAtom_Kind = 0;
+ mAtom_Change = morkChange_kNil;
+ if ( ioSpace )
+ {
+ if ( inAid )
+ {
+ if ( inBuf.mBuf_Fill <= morkAtom_kMaxByteSize )
+ {
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindWeeBook;
+ mBookAtom_Space = ioSpace;
+ mBookAtom_Id = inAid;
+ mork_size size = inBuf.mBuf_Fill;
+ mAtom_Size = (mork_u1) size;
+ if ( size && inBuf.mBuf_Body )
+ MORK_MEMCPY(mWeeBookAtom_Body, inBuf.mBuf_Body, size);
+
+ mWeeBookAtom_Body[ size ] = 0;
+ }
+ else
+ this->AtomSizeOverflowError(ev);
+ }
+ else
+ this->ZeroAidError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+void
+morkBigBookAtom::InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+{
+ mAtom_Kind = 0;
+ mAtom_Change = morkChange_kNil;
+ if ( ioSpace )
+ {
+ if ( inAid )
+ {
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindBigBook;
+ mAtom_Size = 0;
+ mBookAtom_Space = ioSpace;
+ mBookAtom_Id = inAid;
+ mBigBookAtom_Form = inForm;
+ mork_size size = inBuf.mBuf_Fill;
+ mBigBookAtom_Size = size;
+ if ( size && inBuf.mBuf_Body )
+ MORK_MEMCPY(mBigBookAtom_Body, inBuf.mBuf_Body, size);
+
+ mBigBookAtom_Body[ size ] = 0;
+ }
+ else
+ this->ZeroAidError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+void morkFarBookAtom::InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+{
+ mAtom_Kind = 0;
+ mAtom_Change = morkChange_kNil;
+ if ( ioSpace )
+ {
+ if ( inAid )
+ {
+ mAtom_CellUses = 0;
+ mAtom_Kind = morkAtom_kKindFarBook;
+ mAtom_Size = 0;
+ mBookAtom_Space = ioSpace;
+ mBookAtom_Id = inAid;
+ mFarBookAtom_Form = inForm;
+ mFarBookAtom_Size = inBuf.mBuf_Fill;
+ mFarBookAtom_Body = (mork_u1*) inBuf.mBuf_Body;
+ }
+ else
+ this->ZeroAidError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkAtom.h b/db/mork/src/morkAtom.h
new file mode 100644
index 000000000..1afe21cd8
--- /dev/null
+++ b/db/mork/src/morkAtom.h
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKATOM_
+#define _MORKATOM_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkAtom_kMaxByteSize 255 /* max for 8-bit integer */
+#define morkAtom_kForeverCellUses 0x0FF /* max for 8-bit integer */
+#define morkAtom_kMaxCellUses 0x07F /* max for 7-bit integer */
+
+#define morkAtom_kKindWeeAnon 'a' /* means morkWeeAnonAtom subclass */
+#define morkAtom_kKindBigAnon 'A' /* means morkBigAnonAtom subclass */
+#define morkAtom_kKindWeeBook 'b' /* means morkWeeBookAtom subclass */
+#define morkAtom_kKindBigBook 'B' /* means morkBigBookAtom subclass */
+#define morkAtom_kKindFarBook 'f' /* means morkFarBookAtom subclass */
+#define morkAtom_kKindRowOid 'r' /* means morkOidAtom subclass */
+#define morkAtom_kKindTableOid 't' /* means morkOidAtom subclass */
+
+/*| Atom: .
+|*/
+class morkAtom { //
+
+public:
+
+ mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ mork_change mAtom_Change; // how has this atom been changed?
+ mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes
+
+public:
+ morkAtom(mork_aid inAid, mork_u1 inKind);
+
+ mork_bool IsWeeAnon() const { return mAtom_Kind == morkAtom_kKindWeeAnon; }
+ mork_bool IsBigAnon() const { return mAtom_Kind == morkAtom_kKindBigAnon; }
+ mork_bool IsWeeBook() const { return mAtom_Kind == morkAtom_kKindWeeBook; }
+ mork_bool IsBigBook() const { return mAtom_Kind == morkAtom_kKindBigBook; }
+ mork_bool IsFarBook() const { return mAtom_Kind == morkAtom_kKindFarBook; }
+ mork_bool IsRowOid() const { return mAtom_Kind == morkAtom_kKindRowOid; }
+ mork_bool IsTableOid() const { return mAtom_Kind == morkAtom_kKindTableOid; }
+
+ mork_bool IsBook() const { return this->IsWeeBook() || this->IsBigBook(); }
+
+public: // clean vs dirty
+
+ void SetAtomClean() { mAtom_Change = morkChange_kNil; }
+ void SetAtomDirty() { mAtom_Change = morkChange_kAdd; }
+
+ mork_bool IsAtomClean() const { return mAtom_Change == morkChange_kNil; }
+ mork_bool IsAtomDirty() const { return mAtom_Change == morkChange_kAdd; }
+
+public: // atom space scope if IsBook() is true, or else zero:
+
+ mork_scope GetBookAtomSpaceScope(morkEnv* ev) const;
+ // zero or book's space's scope
+
+ mork_aid GetBookAtomAid() const;
+ // zero or book atom's ID
+
+public: // empty construction does nothing
+ morkAtom() { }
+
+public: // one-byte refcounting, freezing at maximum
+ void MakeCellUseForever(morkEnv* ev);
+ mork_u1 AddCellUse(morkEnv* ev);
+ mork_u1 CutCellUse(morkEnv* ev);
+
+ mork_bool IsCellUseForever() const
+ { return mAtom_CellUses == morkAtom_kForeverCellUses; }
+
+private: // warnings
+
+ static void CellUsesUnderflowWarning(morkEnv* ev);
+
+public: // errors
+
+ static void BadAtomKindError(morkEnv* ev);
+ static void ZeroAidError(morkEnv* ev);
+ static void AtomSizeOverflowError(morkEnv* ev);
+
+public: // yarns
+
+ static mork_bool AliasYarn(const morkAtom* atom, mdbYarn* outYarn);
+ static mork_bool GetYarn(const morkAtom* atom, mdbYarn* outYarn);
+
+private: // copying is not allowed
+ morkAtom(const morkAtom& other);
+ morkAtom& operator=(const morkAtom& other);
+};
+
+/*| OidAtom: an atom that references a row or table by identity.
+|*/
+class morkOidAtom : public morkAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms
+
+public:
+ mdbOid mOidAtom_Oid; // identity of referenced object
+
+public: // empty construction does nothing
+ morkOidAtom() { }
+ void InitRowOidAtom(morkEnv* ev, const mdbOid& inOid);
+ void InitTableOidAtom(morkEnv* ev, const mdbOid& inOid);
+
+private: // copying is not allowed
+ morkOidAtom(const morkOidAtom& other);
+ morkOidAtom& operator=(const morkOidAtom& other);
+};
+
+/*| WeeAnonAtom: an atom whose content immediately follows morkAtom slots
+**| in an inline fashion, so that morkWeeAnonAtom contains both leading
+**| atom slots and then the content bytes without further overhead. Note
+**| that charset encoding is not indicated, so zero is implied for Latin1.
+**| (Non-Latin1 content must be stored in a morkBigAnonAtom with a charset.)
+**|
+**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
+**| for lookup needed for sharing like a book atom.
+**|
+**|| A wee anon atom is immediate but not shared with any other users of this
+**| atom, so no bookkeeping for sharing is needed. This means the atom has
+**| no ID, because the atom has no identity other than this immediate content,
+**| and no hash table is needed to look up this particular atom. This also
+**| applies to the larger format morkBigAnonAtom, which has more slots.
+|*/
+class morkWeeAnonAtom : public morkAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes
+
+public:
+ mork_u1 mWeeAnonAtom_Body[ 1 ]; // 1st byte of immediate content vector
+
+public: // empty construction does nothing
+ morkWeeAnonAtom() { }
+ void InitWeeAnonAtom(morkEnv* ev, const morkBuf& inBuf);
+
+ // allow extra trailing byte for a null byte:
+ static mork_size SizeForFill(mork_fill inFill)
+ { return sizeof(morkWeeAnonAtom) + inFill; }
+
+private: // copying is not allowed
+ morkWeeAnonAtom(const morkWeeAnonAtom& other);
+ morkWeeAnonAtom& operator=(const morkWeeAnonAtom& other);
+};
+
+/*| BigAnonAtom: another immediate atom that cannot be encoded as the smaller
+**| morkWeeAnonAtom format because either the size is too great, and/or the
+**| charset is not the default zero for Latin1 and must be explicitly noted.
+**|
+**|| An anon (anonymous) atom has no identity, with no associated bookkeeping
+**| for lookup needed for sharing like a book atom.
+|*/
+class morkBigAnonAtom : public morkAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms
+
+public:
+ mork_cscode mBigAnonAtom_Form; // charset format encoding
+ mork_size mBigAnonAtom_Size; // size of content vector
+ mork_u1 mBigAnonAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+ morkBigAnonAtom() { }
+ void InitBigAnonAtom(morkEnv* ev, const morkBuf& inBuf, mork_cscode inForm);
+
+ // allow extra trailing byte for a null byte:
+ static mork_size SizeForFill(mork_fill inFill)
+ { return sizeof(morkBigAnonAtom) + inFill; }
+
+private: // copying is not allowed
+ morkBigAnonAtom(const morkBigAnonAtom& other);
+ morkBigAnonAtom& operator=(const morkBigAnonAtom& other);
+};
+
+#define morkBookAtom_kMaxBodySize 1024 /* if larger, cannot be shared */
+
+/*| BookAtom: the common subportion of wee book atoms and big book atoms that
+**| includes the atom ID and the pointer to the space referencing this atom
+**| through a hash table.
+|*/
+class morkBookAtom : public morkAtom { //
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes
+
+public:
+ morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is atom scope
+ mork_aid mBookAtom_Id; // identity token for this shared atom
+
+public: // empty construction does nothing
+ morkBookAtom() { }
+
+ static void NonBookAtomTypeError(morkEnv* ev);
+
+public: // Hash() and Equal() for atom ID maps are same for all subclasses:
+
+ mork_u4 HashAid() const { return mBookAtom_Id; }
+ mork_bool EqualAid(const morkBookAtom* inAtom) const
+ { return ( mBookAtom_Id == inAtom->mBookAtom_Id); }
+
+public: // Hash() and Equal() for atom body maps know about subclasses:
+
+ // YOU CANNOT SUBCLASS morkBookAtom WITHOUT FIXING Hash and Equal METHODS:
+
+ mork_u4 HashFormAndBody(morkEnv* ev) const;
+ mork_bool EqualFormAndBody(morkEnv* ev, const morkBookAtom* inAtom) const;
+
+public: // separation from containing space
+
+ void CutBookAtomFromSpace(morkEnv* ev);
+
+private: // copying is not allowed
+ morkBookAtom(const morkBookAtom& other);
+ morkBookAtom& operator=(const morkBookAtom& other);
+};
+
+/*| FarBookAtom: this alternative format for book atoms was introduced
+**| in May 2000 in order to support finding atoms in hash tables without
+**| first copying the strings from original parsing buffers into a new
+**| atom format. This was consuming too much time. However, we can
+**| use morkFarBookAtom to stage a hash table query, as long as we then
+**| fix HashFormAndBody() and EqualFormAndBody() to use morkFarBookAtom
+**| correctly.
+**|
+**|| Note we do NOT intend that instances of morkFarBookAtom will ever
+**| be installed in hash tables, because this is not space efficient.
+**| We only expect to create temp instances for table lookups.
+|*/
+class morkFarBookAtom : public morkBookAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms
+
+ // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope
+ // mork_aid mBookAtom_Id; // identity token for this shared atom
+
+public:
+ mork_cscode mFarBookAtom_Form; // charset format encoding
+ mork_size mFarBookAtom_Size; // size of content vector
+ mork_u1* mFarBookAtom_Body; // bytes are elsewere, out of line
+
+public: // empty construction does nothing
+ morkFarBookAtom() { }
+ void InitFarBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
+
+private: // copying is not allowed
+ morkFarBookAtom(const morkFarBookAtom& other);
+ morkFarBookAtom& operator=(const morkFarBookAtom& other);
+};
+
+/*| WeeBookAtom: .
+|*/
+class morkWeeBookAtom : public morkBookAtom { //
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // only for atoms smaller than 256 bytes
+
+ // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope
+ // mork_aid mBookAtom_Id; // identity token for this shared atom
+
+public:
+ mork_u1 mWeeBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+ morkWeeBookAtom() { }
+ explicit morkWeeBookAtom(mork_aid inAid);
+
+ void InitWeeBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ morkAtomSpace* ioSpace, mork_aid inAid);
+
+ // allow extra trailing byte for a null byte:
+ static mork_size SizeForFill(mork_fill inFill)
+ { return sizeof(morkWeeBookAtom) + inFill; }
+
+private: // copying is not allowed
+ morkWeeBookAtom(const morkWeeBookAtom& other);
+ morkWeeBookAtom& operator=(const morkWeeBookAtom& other);
+};
+
+/*| BigBookAtom: .
+|*/
+class morkBigBookAtom : public morkBookAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms
+
+ // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope
+ // mork_aid mBookAtom_Id; // identity token for this shared atom
+
+public:
+ mork_cscode mBigBookAtom_Form; // charset format encoding
+ mork_size mBigBookAtom_Size; // size of content vector
+ mork_u1 mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public: // empty construction does nothing
+ morkBigBookAtom() { }
+ void InitBigBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid);
+
+ // allow extra trailing byte for a null byte:
+ static mork_size SizeForFill(mork_fill inFill)
+ { return sizeof(morkBigBookAtom) + inFill; }
+
+private: // copying is not allowed
+ morkBigBookAtom(const morkBigBookAtom& other);
+ morkBigBookAtom& operator=(const morkBigBookAtom& other);
+};
+
+/*| MaxBookAtom: .
+|*/
+class morkMaxBookAtom : public morkBigBookAtom { //
+
+ // mork_u1 mAtom_Kind; // identifies a specific atom subclass
+ // mork_u1 mAtom_CellUses; // number of persistent uses in a cell
+ // mork_change mAtom_Change; // how has this atom been changed?
+ // mork_u1 mAtom_Size; // NOT USED IN "BIG" format atoms
+
+ // morkAtomSpace* mBookAtom_Space; // mBookAtom_Space->SpaceScope() is scope
+ // mork_aid mBookAtom_Id; // identity token for this shared atom
+
+ // mork_cscode mBigBookAtom_Form; // charset format encoding
+ // mork_size mBigBookAtom_Size; // size of content vector
+ // mork_u1 mBigBookAtom_Body[ 1 ]; // 1st byte of immed content vector
+
+public:
+ mork_u1 mMaxBookAtom_Body[ morkBookAtom_kMaxBodySize + 3 ]; // max bytes
+
+public: // empty construction does nothing
+ morkMaxBookAtom() { }
+ void InitMaxBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid)
+ { this->InitBigBookAtom(ev, inBuf, inForm, ioSpace, inAid); }
+
+private: // copying is not allowed
+ morkMaxBookAtom(const morkMaxBookAtom& other);
+ morkMaxBookAtom& operator=(const morkMaxBookAtom& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOM_ */
+
diff --git a/db/mork/src/morkAtomMap.cpp b/db/mork/src/morkAtomMap.cpp
new file mode 100644
index 000000000..2d8658402
--- /dev/null
+++ b/db/mork/src/morkAtomMap.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomAidMap::CloseMorkNode(morkEnv* ev) // CloseAtomAidMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseAtomAidMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkAtomAidMap::~morkAtomAidMap() // assert CloseAtomAidMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkAtomAidMap::morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_ENABLE_PROBE_MAPS
+: morkProbeMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+ ioSlotHeap, morkAtomAidMap_kStartSlotCount,
+ /*inZeroIsClearKey*/ morkBool_kTrue)
+#else /*MORK_ENABLE_PROBE_MAPS*/
+: morkMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+ morkAtomAidMap_kStartSlotCount, ioSlotHeap,
+ /*inHoldChanges*/ morkBool_kFalse)
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kAtomAidMap;
+}
+
+/*public non-poly*/ void
+morkAtomAidMap::CloseAtomAidMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->CloseProbeMap(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->CloseMap(ev);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+
+ /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+ morkAtomAidMap::MapTest(morkEnv* ev, const void* inMapKey,
+ const void* inAppKey) const
+ {
+ MORK_USED_1(ev);
+ const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+ if ( key )
+ {
+ mork_bool hit = key->EqualAid(*(const morkBookAtom**) inAppKey);
+ return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+ }
+ else
+ return morkTest_kVoid;
+ }
+
+ /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ morkAtomAidMap::MapHash(morkEnv* ev, const void* inAppKey) const
+ {
+ const morkBookAtom* key = *(const morkBookAtom**) inAppKey;
+ if ( key )
+ return key->HashAid();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+ }
+
+ /*virtual*/ mork_u4
+ morkAtomAidMap::ProbeMapHashMapKey(morkEnv* ev,
+ const void* inMapKey) const
+ {
+ const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+ if ( key )
+ return key->HashAid();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+ }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ // { ===== begin morkMap poly interface =====
+ /*virtual*/ mork_bool //
+ morkAtomAidMap::Equal(morkEnv* ev, const void* inKeyA,
+ const void* inKeyB) const
+ {
+ MORK_USED_1(ev);
+ return (*(const morkBookAtom**) inKeyA)->EqualAid(
+ *(const morkBookAtom**) inKeyB);
+ }
+
+ /*virtual*/ mork_u4 //
+ morkAtomAidMap::Hash(morkEnv* ev, const void* inKey) const
+ {
+ MORK_USED_1(ev);
+ return (*(const morkBookAtom**) inKey)->HashAid();
+ }
+ // } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+
+mork_bool
+morkAtomAidMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom)
+{
+ if ( ev->Good() )
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Put(ev, &ioAtom, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ }
+ return ev->Good();
+}
+
+morkBookAtom*
+morkAtomAidMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+ morkBookAtom* oldKey = 0;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ MORK_USED_1(inAtom);
+ morkProbeMap::ProbeMapCutError(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0,
+ (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ return oldKey;
+}
+
+morkBookAtom*
+morkAtomAidMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+ morkBookAtom* key = 0; // old val in the map
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ return key;
+}
+
+morkBookAtom*
+morkAtomAidMap::GetAid(morkEnv* ev, mork_aid inAid)
+{
+ morkWeeBookAtom weeAtom(inAid);
+ morkBookAtom* key = &weeAtom; // we need a pointer
+ morkBookAtom* oldKey = 0; // old key in the map
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ return oldKey;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomBodyMap::CloseMorkNode(morkEnv* ev) // CloseAtomBodyMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseAtomBodyMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkAtomBodyMap::~morkAtomBodyMap() // assert CloseAtomBodyMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkAtomBodyMap::morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_ENABLE_PROBE_MAPS
+: morkProbeMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+ ioSlotHeap, morkAtomBodyMap_kStartSlotCount,
+ /*inZeroIsClearKey*/ morkBool_kTrue)
+#else /*MORK_ENABLE_PROBE_MAPS*/
+: morkMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkBookAtom*), /*inValSize*/ 0,
+ morkAtomBodyMap_kStartSlotCount, ioSlotHeap,
+ /*inHoldChanges*/ morkBool_kFalse)
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kAtomBodyMap;
+}
+
+/*public non-poly*/ void
+morkAtomBodyMap::CloseAtomBodyMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->CloseProbeMap(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->CloseMap(ev);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+#ifdef MORK_ENABLE_PROBE_MAPS
+
+ /*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+ morkAtomBodyMap::MapTest(morkEnv* ev, const void* inMapKey,
+ const void* inAppKey) const
+ {
+ const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+ if ( key )
+ {
+ return ( key->EqualFormAndBody(ev, *(const morkBookAtom**) inAppKey) ) ?
+ morkTest_kHit : morkTest_kMiss;
+ }
+ else
+ return morkTest_kVoid;
+ }
+
+ /*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ morkAtomBodyMap::MapHash(morkEnv* ev, const void* inAppKey) const
+ {
+ const morkBookAtom* key = *(const morkBookAtom**) inAppKey;
+ if ( key )
+ return key->HashFormAndBody(ev);
+ else
+ return 0;
+ }
+
+ /*virtual*/ mork_u4
+ morkAtomBodyMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const
+ {
+ const morkBookAtom* key = *(const morkBookAtom**) inMapKey;
+ if ( key )
+ return key->HashFormAndBody(ev);
+ else
+ return 0;
+ }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ // { ===== begin morkMap poly interface =====
+ /*virtual*/ mork_bool //
+ morkAtomBodyMap::Equal(morkEnv* ev, const void* inKeyA,
+ const void* inKeyB) const
+ {
+ return (*(const morkBookAtom**) inKeyA)->EqualFormAndBody(ev,
+ *(const morkBookAtom**) inKeyB);
+ }
+
+ /*virtual*/ mork_u4 //
+ morkAtomBodyMap::Hash(morkEnv* ev, const void* inKey) const
+ {
+ return (*(const morkBookAtom**) inKey)->HashFormAndBody(ev);
+ }
+ // } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+
+mork_bool
+morkAtomBodyMap::AddAtom(morkEnv* ev, morkBookAtom* ioAtom)
+{
+ if ( ev->Good() )
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->MapAtPut(ev, &ioAtom, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Put(ev, &ioAtom, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ }
+ return ev->Good();
+}
+
+morkBookAtom*
+morkAtomBodyMap::CutAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+ morkBookAtom* oldKey = 0;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ MORK_USED_1(inAtom);
+ morkProbeMap::ProbeMapCutError(ev);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Cut(ev, &inAtom, &oldKey, /*val*/ (void*) 0,
+ (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ return oldKey;
+}
+
+morkBookAtom*
+morkAtomBodyMap::GetAtom(morkEnv* ev, const morkBookAtom* inAtom)
+{
+ morkBookAtom* key = 0; // old val in the map
+#ifdef MORK_ENABLE_PROBE_MAPS
+ this->MapAt(ev, &inAtom, &key, /*val*/ (void*) 0);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ this->Get(ev, &inAtom, &key, /*val*/ (void*) 0, (mork_change**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ return key;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkAtomRowMap::~morkAtomRowMap()
+{
+}
+
+// I changed to sizeof(mork_ip) from sizeof(mork_aid) to fix a crash on
+// 64 bit machines. I am not sure it was the right way to fix the problem,
+// but it does stop the crash. Perhaps we should be using the
+// morkPointerMap instead?
+morkAtomRowMap::morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn)
+ : morkIntMap(ev, inUsage, sizeof(mork_ip), ioHeap, ioSlotHeap,
+ /*inHoldChanges*/ morkBool_kFalse)
+, mAtomRowMap_IndexColumn( inIndexColumn )
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kAtomRowMap;
+}
+
+void morkAtomRowMap::AddRow(morkEnv* ev, morkRow* ioRow)
+// add ioRow only if it contains a cell in mAtomRowMap_IndexColumn.
+{
+ mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn);
+ if ( aid )
+ this->AddAid(ev, aid, ioRow);
+}
+
+void morkAtomRowMap::CutRow(morkEnv* ev, morkRow* ioRow)
+// cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn.
+{
+ mork_aid aid = ioRow->GetCellAtomAid(ev, mAtomRowMap_IndexColumn);
+ if ( aid )
+ this->CutAid(ev, aid);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkAtomMap.h b/db/mork/src/morkAtomMap.h
new file mode 100644
index 000000000..6ddecc5c2
--- /dev/null
+++ b/db/mork/src/morkAtomMap.h
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKATOMMAP_
+#define _MORKATOMMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomAidMap /*i*/ 0x6141 /* ascii 'aA' */
+
+#define morkAtomAidMap_kStartSlotCount 23
+
+/*| morkAtomAidMap: keys of morkBookAtom organized by atom ID
+|*/
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomAidMap : public morkProbeMap { // for mapping tokens to maps
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomAidMap : public morkMap { // for mapping tokens to maps
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomAidMap() only if open
+ virtual ~morkAtomAidMap(); // assert that CloseAtomAidMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkAtomAidMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseAtomAidMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsAtomAidMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kAtomAidMap; }
+// } ===== end morkNode methods =====
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+ // { ===== begin morkProbeMap methods =====
+ virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+ MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override;
+
+ virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ MapHash(morkEnv* ev, const void* inAppKey) const override;
+
+ virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override;
+
+ // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+ // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ // void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+ // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ // const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ // void* outMapKey, void* outMapVal); // (key,val) inside map
+
+ // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ // const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ // void* outAppKey, void* outAppVal) const; // (key,val) outside map
+ // } ===== end morkProbeMap methods =====
+#else /*MORK_ENABLE_PROBE_MAPS*/
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override;
+ // implemented using morkBookAtom::HashAid()
+
+ virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+ Hash(morkEnv* ev, const void* inKey) const override;
+ // implemented using morkBookAtom::EqualAid()
+// } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public: // other map methods
+
+ mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom);
+ // AddAtom() returns ev->Good()
+
+ morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom);
+ // CutAtom() returns the atom removed equal to inAtom, if there was one
+
+ morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom);
+ // GetAtom() returns the atom equal to inAtom, or else nil
+
+ morkBookAtom* GetAid(morkEnv* ev, mork_aid inAid);
+ // GetAid() returns the atom equal to inAid, or else nil
+
+ // note the atoms are owned elsewhere, usuall by morkAtomSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakAtomAidMap(morkAtomAidMap* me,
+ morkEnv* ev, morkAtomAidMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongAtomAidMap(morkAtomAidMap* me,
+ morkEnv* ev, morkAtomAidMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomAidMapIter: public morkProbeMapIter { // typesafe wrapper class
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomAidMapIter: public morkMapIter { // typesafe wrapper class
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+ : morkProbeMapIter(ev, ioMap) { }
+
+ morkAtomAidMapIter( ) : morkProbeMapIter() { }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkAtomAidMapIter( ) : morkMapIter() { }
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ void InitAtomAidMapIter(morkEnv* ev, morkAtomAidMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomBodyMap /*i*/ 0x6142 /* ascii 'aB' */
+
+#define morkAtomBodyMap_kStartSlotCount 23
+
+/*| morkAtomBodyMap: keys of morkBookAtom organized by body bytes
+|*/
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomBodyMap : public morkProbeMap { // for mapping tokens to maps
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomBodyMap : public morkMap { // for mapping tokens to maps
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomBodyMap() only if open
+ virtual ~morkAtomBodyMap(); // assert CloseAtomBodyMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkAtomBodyMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseAtomBodyMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsAtomBodyMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kAtomBodyMap; }
+// } ===== end morkNode methods =====
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+ // { ===== begin morkProbeMap methods =====
+ virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+ MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override;
+
+ virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ MapHash(morkEnv* ev, const void* inAppKey) const override;
+
+ virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override;
+
+ // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+ // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ // void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+ // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ // const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ // void* outMapKey, void* outMapVal); // (key,val) inside map
+
+ // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ // const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ // void* outAppKey, void* outAppVal) const; // (key,val) outside map
+ // } ===== end morkProbeMap methods =====
+#else /*MORK_ENABLE_PROBE_MAPS*/
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override;
+ // implemented using morkBookAtom::EqualFormAndBody()
+
+ virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+ Hash(morkEnv* ev, const void* inKey) const override;
+ // implemented using morkBookAtom::HashFormAndBody()
+// } ===== end morkMap poly interface =====
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public: // other map methods
+
+ mork_bool AddAtom(morkEnv* ev, morkBookAtom* ioAtom);
+ // AddAtom() returns ev->Good()
+
+ morkBookAtom* CutAtom(morkEnv* ev, const morkBookAtom* inAtom);
+ // CutAtom() returns the atom removed equal to inAtom, if there was one
+
+ morkBookAtom* GetAtom(morkEnv* ev, const morkBookAtom* inAtom);
+ // GetAtom() returns the atom equal to inAtom, or else nil
+
+ // note the atoms are owned elsewhere, usuall by morkAtomSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakAtomBodyMap(morkAtomBodyMap* me,
+ morkEnv* ev, morkAtomBodyMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongAtomBodyMap(morkAtomBodyMap* me,
+ morkEnv* ev, morkAtomBodyMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+class morkAtomBodyMapIter: public morkProbeMapIter{ // typesafe wrapper class
+#else /*MORK_ENABLE_PROBE_MAPS*/
+class morkAtomBodyMapIter: public morkMapIter{ // typesafe wrapper class
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+public:
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+ : morkProbeMapIter(ev, ioMap) { }
+
+ morkAtomBodyMapIter( ) : morkProbeMapIter() { }
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkAtomBodyMapIter( ) : morkMapIter() { }
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ void InitAtomBodyMapIter(morkEnv* ev, morkAtomBodyMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change* FirstAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->First(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* NextAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->Next(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* HereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->Here(ev, outAtomPtr, /*val*/ (void*) 0); }
+
+ mork_change* CutHereAtom(morkEnv* ev, morkBookAtom** outAtomPtr)
+ { return this->CutHere(ev, outAtomPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomRowMap /*i*/ 0x6152 /* ascii 'aR' */
+
+/*| morkAtomRowMap: maps morkAtom* -> morkRow*
+|*/
+class morkAtomRowMap : public morkIntMap { // for mapping atoms to rows
+
+public:
+ mork_column mAtomRowMap_IndexColumn; // row column being indexed
+
+public:
+
+ virtual ~morkAtomRowMap();
+ morkAtomRowMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_column inIndexColumn);
+
+public: // adding and cutting from morkRow instance candidate
+
+ void AddRow(morkEnv* ev, morkRow* ioRow);
+ // add ioRow only if it contains a cell in mAtomRowMap_IndexColumn.
+
+ void CutRow(morkEnv* ev, morkRow* ioRow);
+ // cut ioRow only if it contains a cell in mAtomRowMap_IndexColumn.
+
+public: // other map methods
+
+ mork_bool AddAid(morkEnv* ev, mork_aid inAid, morkRow* ioRow)
+ { return this->AddInt(ev, inAid, ioRow); }
+ // the AddAid() boolean return equals ev->Good().
+
+ mork_bool CutAid(morkEnv* ev, mork_aid inAid)
+ { return this->CutInt(ev, inAid); }
+ // The CutAid() boolean return indicates whether removal happened.
+
+ morkRow* GetAid(morkEnv* ev, mork_aid inAid)
+ { return (morkRow*) this->GetInt(ev, inAid); }
+ // Note the returned space does NOT have an increase in refcount for this.
+
+public: // dynamic type identification
+ mork_bool IsAtomRowMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kAtomRowMap; }
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakAtomRowMap(morkAtomRowMap* me,
+ morkEnv* ev, morkAtomRowMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongAtomRowMap(morkAtomRowMap* me,
+ morkEnv* ev, morkAtomRowMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+};
+
+class morkAtomRowMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkAtomRowMapIter( ) : morkMapIter() { }
+ void InitAtomRowMapIter(morkEnv* ev, morkAtomRowMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change*
+ FirstAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+ { return this->First(ev, outAtom, outRow); }
+
+ mork_change*
+ NextAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+ { return this->Next(ev, outAtom, outRow); }
+
+ mork_change*
+ HereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+ { return this->Here(ev, outAtom, outRow); }
+
+ mork_change*
+ CutHereAtomAndRow(morkEnv* ev, morkAtom** outAtom, morkRow** outRow)
+ { return this->CutHere(ev, outAtom, outRow); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOMMAP_ */
diff --git a/db/mork/src/morkAtomSpace.cpp b/db/mork/src/morkAtomSpace.cpp
new file mode 100644
index 000000000..e9c1c16ed
--- /dev/null
+++ b/db/mork/src/morkAtomSpace.cpp
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkAtomSpace::CloseMorkNode(morkEnv* ev) // CloseAtomSpace() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseAtomSpace(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkAtomSpace::~morkAtomSpace() // assert CloseAtomSpace() executed earlier
+{
+ MORK_ASSERT(mAtomSpace_HighUnderId==0);
+ MORK_ASSERT(mAtomSpace_HighOverId==0);
+ MORK_ASSERT(this->IsShutNode());
+ MORK_ASSERT(mAtomSpace_AtomAids.IsShutNode());
+ MORK_ASSERT(mAtomSpace_AtomBodies.IsShutNode());
+}
+
+/*public non-poly*/
+morkAtomSpace::morkAtomSpace(morkEnv* ev, const morkUsage& inUsage,
+ mork_scope inScope, morkStore* ioStore,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap)
+, mAtomSpace_HighUnderId( morkAtomSpace_kMinUnderId )
+, mAtomSpace_HighOverId( morkAtomSpace_kMinOverId )
+, mAtomSpace_AtomAids(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+, mAtomSpace_AtomBodies(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+{
+ // the morkSpace base constructor handles any dirty propagation
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kAtomSpace;
+}
+
+/*public non-poly*/ void
+morkAtomSpace::CloseAtomSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mAtomSpace_AtomBodies.CloseMorkNode(ev);
+ morkStore* store = mSpace_Store;
+ if ( store )
+ this->CutAllAtoms(ev, &store->mStore_Pool);
+
+ mAtomSpace_AtomAids.CloseMorkNode(ev);
+ this->CloseSpace(ev);
+ mAtomSpace_HighUnderId = 0;
+ mAtomSpace_HighOverId = 0;
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkAtomSpace::NonAtomSpaceTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkAtomSpace");
+}
+
+mork_num
+morkAtomSpace::CutAllAtoms(morkEnv* ev, morkPool* ioPool)
+{
+#ifdef MORK_ENABLE_ZONE_ARENAS
+ MORK_USED_2(ev, ioPool);
+ return 0;
+#else /*MORK_ENABLE_ZONE_ARENAS*/
+ if ( this->IsAtomSpaceClean() )
+ this->MaybeDirtyStoreAndSpace();
+
+ mork_num outSlots = mAtomSpace_AtomAids.MapFill();
+ morkBookAtom* a = 0; // old key atom in the map
+
+ morkStore* store = mSpace_Store;
+ mork_change* c = 0;
+ morkAtomAidMapIter i(ev, &mAtomSpace_AtomAids);
+ for ( c = i.FirstAtom(ev, &a); c ; c = i.NextAtom(ev, &a) )
+ {
+ if ( a )
+ ioPool->ZapAtom(ev, a, &store->mStore_Zone);
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ // do not cut anything from the map
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ i.CutHereAtom(ev, /*key*/ (morkBookAtom**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ }
+
+ return outSlots;
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+}
+
+
+morkBookAtom*
+morkAtomSpace::MakeBookAtomCopyWithAid(morkEnv* ev,
+ const morkFarBookAtom& inAtom, mork_aid inAid)
+// Make copy of inAtom and put it in both maps, using specified ID.
+{
+ morkBookAtom* outAtom = 0;
+ morkStore* store = mSpace_Store;
+ if ( ev->Good() && store )
+ {
+ morkPool* pool = this->GetSpaceStorePool();
+ outAtom = pool->NewFarBookAtomCopy(ev, inAtom, &store->mStore_Zone);
+ if ( outAtom )
+ {
+ if ( store->mStore_CanDirty )
+ {
+ outAtom->SetAtomDirty();
+ if ( this->IsAtomSpaceClean() )
+ this->MaybeDirtyStoreAndSpace();
+ }
+
+ outAtom->mBookAtom_Id = inAid;
+ outAtom->mBookAtom_Space = this;
+ mAtomSpace_AtomAids.AddAtom(ev, outAtom);
+ mAtomSpace_AtomBodies.AddAtom(ev, outAtom);
+ if ( this->SpaceScope() == morkAtomSpace_kColumnScope )
+ outAtom->MakeCellUseForever(ev);
+
+ if ( mAtomSpace_HighUnderId <= inAid )
+ mAtomSpace_HighUnderId = inAid + 1;
+ }
+ }
+ return outAtom;
+}
+
+morkBookAtom*
+morkAtomSpace::MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom)
+// make copy of inAtom and put it in both maps, using a new ID as needed.
+{
+ morkBookAtom* outAtom = 0;
+ morkStore* store = mSpace_Store;
+ if ( ev->Good() && store )
+ {
+ if ( store->mStore_CanAutoAssignAtomIdentity )
+ {
+ morkPool* pool = this->GetSpaceStorePool();
+ morkBookAtom* atom = pool->NewFarBookAtomCopy(ev, inAtom, &mSpace_Store->mStore_Zone);
+ if ( atom )
+ {
+ mork_aid id = this->MakeNewAtomId(ev, atom);
+ if ( id )
+ {
+ if ( store->mStore_CanDirty )
+ {
+ atom->SetAtomDirty();
+ if ( this->IsAtomSpaceClean() )
+ this->MaybeDirtyStoreAndSpace();
+ }
+
+ outAtom = atom;
+ atom->mBookAtom_Space = this;
+ mAtomSpace_AtomAids.AddAtom(ev, atom);
+ mAtomSpace_AtomBodies.AddAtom(ev, atom);
+ if ( this->SpaceScope() == morkAtomSpace_kColumnScope )
+ outAtom->MakeCellUseForever(ev);
+ }
+ else
+ pool->ZapAtom(ev, atom, &mSpace_Store->mStore_Zone);
+ }
+ }
+ else
+ mSpace_Store->CannotAutoAssignAtomIdentityError(ev);
+ }
+ return outAtom;
+}
+
+
+mork_aid
+morkAtomSpace::MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom)
+{
+ mork_aid outAid = 0;
+ mork_tid id = mAtomSpace_HighUnderId;
+ mork_num count = 8; // try up to eight times
+
+ while ( !outAid && count ) // still trying to find an unused table ID?
+ {
+ --count;
+ ioAtom->mBookAtom_Id = id;
+ if ( !mAtomSpace_AtomAids.GetAtom(ev, ioAtom) )
+ outAid = id;
+ else
+ {
+ MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+ ++id;
+ }
+ }
+
+ mAtomSpace_HighUnderId = id + 1;
+ return outAid;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkAtomSpaceMap::~morkAtomSpaceMap()
+{
+}
+
+morkAtomSpaceMap::morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+ : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kAtomSpaceMap;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkAtomSpace.h b/db/mork/src/morkAtomSpace.h
new file mode 100644
index 000000000..6048261c5
--- /dev/null
+++ b/db/mork/src/morkAtomSpace.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKATOMSPACE_
+#define _MORKATOMSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| kMinUnderId: the smallest ID we auto-assign to the 'under' namespace
+**| reserved for tokens expected to occur very frequently, such as the names
+**| of columns. We reserve single byte ids in the ASCII range to correspond
+**| one-to-one to those tokens consisting single ASCII characters (so that
+**| this assignment is always known and constant). So we start at 0x80, and
+**| then reserve the upper half of two hex digit ids and all the three hex
+**| digit IDs for the 'under' namespace for common tokens.
+|*/
+#define morkAtomSpace_kMinUnderId 0x80 /* low 7 bits mean byte tokens */
+
+#define morkAtomSpace_kMaxSevenBitAid 0x7F /* low seven bit integer ID */
+
+/*| kMinOverId: the smallest ID we auto-assign to the 'over' namespace that
+**| might include very large numbers of tokens that are used infrequently,
+**| so that we care less whether the shortest hex representation is used.
+**| So we start all IDs for 'over' category tokens at a value range that
+**| needs at least four hex digits, so we can reserve three hex digits and
+**| shorter for more commonly occurring tokens in the 'under' category.
+|*/
+#define morkAtomSpace_kMinOverId 0x1000 /* using at least four hex bytes */
+
+#define morkDerived_kAtomSpace /*i*/ 0x6153 /* ascii 'aS' */
+
+#define morkAtomSpace_kColumnScope ((mork_scope) 'c') /* column scope is forever */
+
+/*| morkAtomSpace:
+|*/
+class morkAtomSpace : public morkSpace { //
+
+// public: // slots inherited from morkSpace (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkStore* mSpace_Store; // weak ref to containing store
+
+ // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs
+ // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+ // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+ mork_aid mAtomSpace_HighUnderId; // high ID in 'under' range
+ mork_aid mAtomSpace_HighOverId; // high ID in 'over' range
+
+ morkAtomAidMap mAtomSpace_AtomAids; // all atoms in space by ID
+ morkAtomBodyMap mAtomSpace_AtomBodies; // all atoms in space by body
+
+public: // more specific dirty methods for atom space:
+ void SetAtomSpaceDirty() { this->SetNodeDirty(); }
+ void SetAtomSpaceClean() { this->SetNodeClean(); }
+
+ mork_bool IsAtomSpaceClean() const { return this->IsNodeClean(); }
+ mork_bool IsAtomSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseAtomSpace() only if open
+ virtual ~morkAtomSpace(); // assert that CloseAtomSpace() executed earlier
+
+public: // morkMap construction & destruction
+ morkAtomSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope,
+ morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseAtomSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsAtomSpace() const
+ { return IsNode() && mNode_Derived == morkDerived_kAtomSpace; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ void NonAtomSpaceTypeError(morkEnv* ev);
+
+public: // setup
+
+ mork_bool MarkAllAtomSpaceContentDirty(morkEnv* ev);
+ // MarkAllAtomSpaceContentDirty() visits every space object and marks
+ // them dirty, including every table, row, cell, and atom. The return
+ // equals ev->Good(), to show whether any error happened. This method is
+ // intended for use in the beginning of a "compress commit" which writes
+ // all store content, whether dirty or not. We dirty everything first so
+ // that later iterations over content can mark things clean as they are
+ // written, and organize the process of serialization so that objects are
+ // written only at need (because of being dirty).
+
+public: // other space methods
+
+ // void ReserveColumnAidCount(mork_count inCount)
+ // {
+ // mAtomSpace_HighUnderId = morkAtomSpace_kMinUnderId + inCount;
+ // mAtomSpace_HighOverId = morkAtomSpace_kMinOverId + inCount;
+ // }
+
+ mork_num CutAllAtoms(morkEnv* ev, morkPool* ioPool);
+ // CutAllAtoms() puts all the atoms back in the pool.
+
+ morkBookAtom* MakeBookAtomCopyWithAid(morkEnv* ev,
+ const morkFarBookAtom& inAtom, mork_aid inAid);
+ // Make copy of inAtom and put it in both maps, using specified ID.
+
+ morkBookAtom* MakeBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom);
+ // Make copy of inAtom and put it in both maps, using a new ID as needed.
+
+ mork_aid MakeNewAtomId(morkEnv* ev, morkBookAtom* ioAtom);
+ // generate an unused atom id.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakAtomSpace(morkAtomSpace* me,
+ morkEnv* ev, morkAtomSpace** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongAtomSpace(morkAtomSpace* me,
+ morkEnv* ev, morkAtomSpace** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kAtomSpaceMap /*i*/ 0x615A /* ascii 'aZ' */
+
+/*| morkAtomSpaceMap: maps mork_scope -> morkAtomSpace
+|*/
+class morkAtomSpaceMap : public morkNodeMap { // for mapping tokens to tables
+
+public:
+
+ virtual ~morkAtomSpaceMap();
+ morkAtomSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+ mork_bool AddAtomSpace(morkEnv* ev, morkAtomSpace* ioAtomSpace)
+ { return this->AddNode(ev, ioAtomSpace->SpaceScope(), ioAtomSpace); }
+ // the AddAtomSpace() boolean return equals ev->Good().
+
+ mork_bool CutAtomSpace(morkEnv* ev, mork_scope inScope)
+ { return this->CutNode(ev, inScope); }
+ // The CutAtomSpace() boolean return indicates whether removal happened.
+
+ morkAtomSpace* GetAtomSpace(morkEnv* ev, mork_scope inScope)
+ { return (morkAtomSpace*) this->GetNode(ev, inScope); }
+ // Note the returned space does NOT have an increase in refcount for this.
+
+ mork_num CutAllAtomSpaces(morkEnv* ev)
+ { return this->CutAllNodes(ev); }
+ // CutAllAtomSpaces() releases all the referenced table values.
+};
+
+class morkAtomSpaceMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkAtomSpaceMapIter( ) : morkMapIter() { }
+ void InitAtomSpaceMapIter(morkEnv* ev, morkAtomSpaceMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change*
+ FirstAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+ { return this->First(ev, outScope, outAtomSpace); }
+
+ mork_change*
+ NextAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+ { return this->Next(ev, outScope, outAtomSpace); }
+
+ mork_change*
+ HereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+ { return this->Here(ev, outScope, outAtomSpace); }
+
+ mork_change*
+ CutHereAtomSpace(morkEnv* ev, mork_scope* outScope, morkAtomSpace** outAtomSpace)
+ { return this->CutHere(ev, outScope, outAtomSpace); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKATOMSPACE_ */
+
diff --git a/db/mork/src/morkBead.cpp b/db/mork/src/morkBead.cpp
new file mode 100644
index 000000000..2b552eff1
--- /dev/null
+++ b/db/mork/src/morkBead.cpp
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBead::CloseMorkNode(morkEnv* ev) // CloseBead() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseBead(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkBead::~morkBead() // assert CloseBead() executed earlier
+{
+ MORK_ASSERT(mBead_Color==0 || mNode_Usage == morkUsage_kStack );
+}
+
+/*public non-poly*/
+morkBead::morkBead(mork_color inBeadColor)
+: morkNode( morkUsage_kStack )
+, mBead_Color( inBeadColor )
+{
+}
+
+/*public non-poly*/
+morkBead::morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor)
+: morkNode( inUsage, ioHeap )
+, mBead_Color( inBeadColor )
+{
+}
+
+/*public non-poly*/
+morkBead::morkBead(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, mork_color inBeadColor)
+: morkNode(ev, inUsage, ioHeap)
+, mBead_Color( inBeadColor )
+{
+ if ( ev->Good() )
+ {
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kBead;
+ }
+}
+
+/*public non-poly*/ void
+morkBead::CloseBead(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ if ( !this->IsShutNode() )
+ {
+ mBead_Color = 0;
+ this->MarkShut();
+ }
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBeadMap::CloseMorkNode(morkEnv* ev) // CloseBeadMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseBeadMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkBeadMap::~morkBeadMap() // assert CloseBeadMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkBeadMap::morkBeadMap(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkMap(ev, inUsage, ioHeap, sizeof(morkBead*), /*inValSize*/ 0,
+ /*slotCount*/ 11, ioSlotHeap, /*holdChanges*/ morkBool_kFalse)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kBeadMap;
+}
+
+/*public non-poly*/ void
+morkBeadMap::CloseBeadMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CutAllBeads(ev);
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+mork_bool
+morkBeadMap::AddBead(morkEnv* ev, morkBead* ioBead)
+ // the AddBead() boolean return equals ev->Good().
+{
+ if ( ioBead && ev->Good() )
+ {
+ morkBead* oldBead = 0; // old key in the map
+
+ mork_bool put = this->Put(ev, &ioBead, /*val*/ (void*) 0,
+ /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+
+ if ( put ) // replaced an existing key?
+ {
+ if ( oldBead != ioBead ) // new bead was not already in table?
+ ioBead->AddStrongRef(ev); // now there's another ref
+
+ if ( oldBead && oldBead != ioBead ) // need to release old node?
+ oldBead->CutStrongRef(ev);
+ }
+ else
+ ioBead->AddStrongRef(ev); // another ref if not already in table
+ }
+ else if ( !ioBead )
+ ev->NilPointerError();
+
+ return ev->Good();
+}
+
+mork_bool
+morkBeadMap::CutBead(morkEnv* ev, mork_color inColor)
+{
+ morkBead* oldBead = 0; // old key in the map
+ morkBead bead(inColor);
+ morkBead* key = &bead;
+
+ mork_bool outCutNode = this->Cut(ev, &key,
+ /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+
+ if ( oldBead )
+ oldBead->CutStrongRef(ev);
+
+ bead.CloseBead(ev);
+ return outCutNode;
+}
+
+morkBead*
+morkBeadMap::GetBead(morkEnv* ev, mork_color inColor)
+ // Note the returned bead does NOT have an increase in refcount for this.
+{
+ morkBead* oldBead = 0; // old key in the map
+ morkBead bead(inColor);
+ morkBead* key = &bead;
+
+ this->Get(ev, &key, /*key*/ &oldBead, /*val*/ (void*) 0, (mork_change**) 0);
+
+ bead.CloseBead(ev);
+ return oldBead;
+}
+
+mork_num
+morkBeadMap::CutAllBeads(morkEnv* ev)
+ // CutAllBeads() releases all the referenced beads.
+{
+ mork_num outSlots = mMap_Slots;
+
+ morkBeadMapIter i(ev, this);
+ morkBead* b = i.FirstBead(ev);
+
+ while ( b )
+ {
+ b->CutStrongRef(ev);
+ i.CutHereBead(ev);
+ b = i.NextBead(ev);
+ }
+
+ return outSlots;
+}
+
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool
+morkBeadMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+ MORK_USED_1(ev);
+ return (*(const morkBead**) inKeyA)->BeadEqual(
+ *(const morkBead**) inKeyB);
+}
+
+/*virtual*/ mork_u4
+morkBeadMap::Hash(morkEnv* ev, const void* inKey) const
+{
+ MORK_USED_1(ev);
+ return (*(const morkBead**) inKey)->BeadHash();
+}
+// } ===== end morkMap poly interface =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkBead* morkBeadMapIter::FirstBead(morkEnv* ev)
+{
+ morkBead* bead = 0;
+ this->First(ev, &bead, /*val*/ (void*) 0);
+ return bead;
+}
+
+morkBead* morkBeadMapIter::NextBead(morkEnv* ev)
+{
+ morkBead* bead = 0;
+ this->Next(ev, &bead, /*val*/ (void*) 0);
+ return bead;
+}
+
+morkBead* morkBeadMapIter::HereBead(morkEnv* ev)
+{
+ morkBead* bead = 0;
+ this->Here(ev, &bead, /*val*/ (void*) 0);
+ return bead;
+}
+
+void morkBeadMapIter::CutHereBead(morkEnv* ev)
+{
+ this->CutHere(ev, /*key*/ (void*) 0, /*val*/ (void*) 0);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBeadProbeMap::CloseMorkNode(morkEnv* ev) // CloseBeadProbeMap() if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseBeadProbeMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkBeadProbeMap::~morkBeadProbeMap() // assert CloseBeadProbeMap() earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+
+/*public non-poly*/
+morkBeadProbeMap::morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkProbeMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkBead*), /*inValSize*/ 0,
+ ioSlotHeap, /*startSlotCount*/ 11,
+ /*inZeroIsClearKey*/ morkBool_kTrue)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kBeadProbeMap;
+}
+
+/*public non-poly*/ void
+morkBeadProbeMap::CloseBeadProbeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CutAllBeads(ev);
+ this->CloseProbeMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkBeadProbeMap::MapTest(morkEnv* ev, const void* inMapKey,
+ const void* inAppKey) const
+{
+ MORK_USED_1(ev);
+ const morkBead* key = *(const morkBead**) inMapKey;
+ if ( key )
+ {
+ mork_bool hit = key->BeadEqual(*(const morkBead**) inAppKey);
+ return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+ }
+ else
+ return morkTest_kVoid;
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkBeadProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+ const morkBead* key = *(const morkBead**) inAppKey;
+ if ( key )
+ return key->BeadHash();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+}
+
+/*virtual*/ mork_u4
+morkBeadProbeMap::ProbeMapHashMapKey(morkEnv* ev,
+ const void* inMapKey) const
+{
+ const morkBead* key = *(const morkBead**) inMapKey;
+ if ( key )
+ return key->BeadHash();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+}
+
+mork_bool
+morkBeadProbeMap::AddBead(morkEnv* ev, morkBead* ioBead)
+{
+ if ( ioBead && ev->Good() )
+ {
+ morkBead* bead = 0; // old key in the map
+
+ mork_bool put = this->MapAtPut(ev, &ioBead, /*val*/ (void*) 0,
+ /*key*/ &bead, /*val*/ (void*) 0);
+
+ if ( put ) // replaced an existing key?
+ {
+ if ( bead != ioBead ) // new bead was not already in table?
+ ioBead->AddStrongRef(ev); // now there's another ref
+
+ if ( bead && bead != ioBead ) // need to release old node?
+ bead->CutStrongRef(ev);
+ }
+ else
+ ioBead->AddStrongRef(ev); // now there's another ref
+ }
+ else if ( !ioBead )
+ ev->NilPointerError();
+
+ return ev->Good();
+}
+
+morkBead*
+morkBeadProbeMap::GetBead(morkEnv* ev, mork_color inColor)
+{
+ morkBead* oldBead = 0; // old key in the map
+ morkBead bead(inColor);
+ morkBead* key = &bead;
+
+ this->MapAt(ev, &key, &oldBead, /*val*/ (void*) 0);
+
+ bead.CloseBead(ev);
+ return oldBead;
+}
+
+mork_num
+morkBeadProbeMap::CutAllBeads(morkEnv* ev)
+ // CutAllBeads() releases all the referenced bead values.
+{
+ mork_num outSlots = sMap_Slots;
+
+ morkBeadProbeMapIter i(ev, this);
+ morkBead* b = i.FirstBead(ev);
+
+ while ( b )
+ {
+ b->CutStrongRef(ev);
+ b = i.NextBead(ev);
+ }
+ this->MapCutAll(ev);
+
+ return outSlots;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
diff --git a/db/mork/src/morkBead.h b/db/mork/src/morkBead.h
new file mode 100644
index 000000000..df86c40ad
--- /dev/null
+++ b/db/mork/src/morkBead.h
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKBEAD_
+#define _MORKBEAD_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBead /*i*/ 0x426F /* ascii 'Bo' */
+
+/*| morkBead: subclass of morkNode that adds knowledge of db suite factory
+**| and containing port to those objects that are exposed as instances of
+**| nsIMdbBead in the public interface.
+|*/
+class morkBead : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+ mork_color mBead_Color; // ID for this bead
+
+public: // Hash() and Equal() for bead maps are same for all subclasses:
+
+ mork_u4 BeadHash() const { return (mork_u4) mBead_Color; }
+ mork_bool BeadEqual(const morkBead* inBead) const
+ { return ( mBead_Color == inBead->mBead_Color); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseBead() only if open
+ virtual ~morkBead(); // assert that CloseBead() executed earlier
+
+public: // special case for stack construction for map usage:
+ explicit morkBead(mork_color inBeadColor); // stack-based bead instance
+
+protected: // special case for morkObject:
+ morkBead(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor);
+
+public: // morkEnv construction & destruction
+ morkBead(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor);
+ void CloseBead(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkBead(const morkBead& other);
+ morkBead& operator=(const morkBead& other);
+
+public: // dynamic type identification
+ mork_bool IsBead() const
+ { return IsNode() && mNode_Derived == morkDerived_kBead; }
+// } ===== end morkNode methods =====
+
+ // void NewNilHandleError(morkEnv* ev); // mBead_Handle is nil
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakBead(morkBead* me,
+ morkEnv* ev, morkBead** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongBead(morkBead* me,
+ morkEnv* ev, morkBead** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBeadMap /*i*/ 0x744D /* ascii 'bM' */
+
+/*| morkBeadMap: maps bead -> bead (key only using mBead_Color)
+|*/
+class morkBeadMap : public morkMap {
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseBeadMap() only if open
+ virtual ~morkBeadMap(); // assert that CloseBeadMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkBeadMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseBeadMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsBeadMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kBeadMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+public:
+ virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override;
+
+ virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+ Hash(morkEnv* ev, const void* inKey) const override;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+ mork_bool AddBead(morkEnv* ev, morkBead* ioBead);
+ // the AddBead() boolean return equals ev->Good().
+
+ mork_bool CutBead(morkEnv* ev, mork_color inColor);
+ // The CutBead() boolean return indicates whether removal happened.
+
+ morkBead* GetBead(morkEnv* ev, mork_color inColor);
+ // Note the returned bead does NOT have an increase in refcount for this.
+
+ mork_num CutAllBeads(morkEnv* ev);
+ // CutAllBeads() releases all the referenced beads.
+};
+
+class morkBeadMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkBeadMapIter(morkEnv* ev, morkBeadMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkBeadMapIter( ) : morkMapIter() { }
+ void InitBeadMapIter(morkEnv* ev, morkBeadMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ morkBead* FirstBead(morkEnv* ev);
+ morkBead* NextBead(morkEnv* ev);
+ morkBead* HereBead(morkEnv* ev);
+ void CutHereBead(morkEnv* ev);
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kBeadProbeMap /*i*/ 0x6D74 /* ascii 'mb' */
+
+/*| morkBeadProbeMap: maps bead -> bead (key only using mBead_Color)
+|*/
+class morkBeadProbeMap : public morkProbeMap {
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseBeadProbeMap() only if open
+ virtual ~morkBeadProbeMap(); // assert that CloseBeadProbeMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkBeadProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseBeadProbeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsBeadProbeMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kBeadProbeMap; }
+// } ===== end morkNode methods =====
+
+ // { ===== begin morkProbeMap methods =====
+public:
+ virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+ MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override;
+
+ virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ MapHash(morkEnv* ev, const void* inAppKey) const override;
+
+ virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override;
+
+ // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+ // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ // void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+ // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ // const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ // void* outMapKey, void* outMapVal); // (key,val) inside map
+
+ // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ // const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ // void* outAppKey, void* outAppVal) const; // (key,val) outside map
+ // } ===== end morkProbeMap methods =====
+
+public: // other map methods
+
+ mork_bool AddBead(morkEnv* ev, morkBead* ioBead);
+ // the AddBead() boolean return equals ev->Good().
+
+ morkBead* GetBead(morkEnv* ev, mork_color inColor);
+ // Note the returned bead does NOT have an increase in refcount for this.
+
+ mork_num CutAllBeads(morkEnv* ev);
+ // CutAllBeads() releases all the referenced bead values.
+};
+
+class morkBeadProbeMapIter: public morkProbeMapIter { // typesafe wrapper class
+
+public:
+ morkBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap)
+ : morkProbeMapIter(ev, ioMap) { }
+
+ morkBeadProbeMapIter( ) : morkProbeMapIter() { }
+ void InitBeadProbeMapIter(morkEnv* ev, morkBeadProbeMap* ioMap)
+ { this->InitProbeMapIter(ev, ioMap); }
+
+ morkBead* FirstBead(morkEnv* ev)
+ { return (morkBead*) this->IterFirstKey(ev); }
+
+ morkBead* NextBead(morkEnv* ev)
+ { return (morkBead*) this->IterNextKey(ev); }
+
+ morkBead* HereBead(morkEnv* ev)
+ { return (morkBead*) this->IterHereKey(ev); }
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBEAD_ */
diff --git a/db/mork/src/morkBlob.cpp b/db/mork/src/morkBlob.cpp
new file mode 100644
index 000000000..0d3f3cccd
--- /dev/null
+++ b/db/mork/src/morkBlob.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void
+morkBuf::NilBufBodyError(morkEnv* ev)
+{
+ ev->NewError("nil mBuf_Body");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void
+morkBlob::BlobFillOverSizeError(morkEnv* ev)
+{
+ ev->NewError("mBuf_Fill > mBlob_Size");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+mork_bool
+morkBlob::GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap, mork_size inNewSize)
+{
+ if ( ioHeap )
+ {
+ if ( !mBuf_Body ) // no body? implies zero sized?
+ mBlob_Size = 0;
+
+ if ( mBuf_Fill > mBlob_Size ) // fill more than size?
+ {
+ ev->NewWarning("mBuf_Fill > mBlob_Size");
+ mBuf_Fill = mBlob_Size;
+ }
+
+ if ( inNewSize > mBlob_Size ) // need to allocate larger blob?
+ {
+ mork_u1* body = 0;
+ ioHeap->Alloc(ev->AsMdbEnv(), inNewSize, (void**) &body);
+ if ( body && ev->Good() )
+ {
+ void* oldBody = mBuf_Body;
+ if ( mBlob_Size ) // any old content to transfer?
+ MORK_MEMCPY(body, oldBody, mBlob_Size);
+
+ mBlob_Size = inNewSize; // install new size
+ mBuf_Body = body; // install new body
+
+ if ( oldBody ) // need to free old buffer body?
+ ioHeap->Free(ev->AsMdbEnv(), oldBody);
+ }
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ if ( ev->Good() && mBlob_Size < inNewSize )
+ ev->NewError("mBlob_Size < inNewSize");
+
+ return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkCoil::morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+ mBuf_Body = 0;
+ mBuf_Fill = 0;
+ mBlob_Size = 0;
+ mText_Form = 0;
+ mCoil_Heap = ioHeap;
+ if ( !ioHeap )
+ ev->NilPointerError();
+}
+
+void
+morkCoil::CloseCoil(morkEnv* ev)
+{
+ void* body = mBuf_Body;
+ nsIMdbHeap* heap = mCoil_Heap;
+
+ mBuf_Body = 0;
+ mCoil_Heap = 0;
+
+ if ( body && heap )
+ {
+ heap->Free(ev->AsMdbEnv(), body);
+ }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkBlob.h b/db/mork/src/morkBlob.h
new file mode 100644
index 000000000..5d07098e1
--- /dev/null
+++ b/db/mork/src/morkBlob.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKBLOB_
+#define _MORKBLOB_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| Buf: the minimum needed to describe location and content length.
+**| This is typically only enough to read from this buffer, since
+**| one cannot write effectively without knowing the size of a buf.
+|*/
+class morkBuf { // subset of nsIMdbYarn slots
+public:
+ void* mBuf_Body; // space for holding any binary content
+ mork_fill mBuf_Fill; // logical content in Buf in bytes
+
+public:
+ morkBuf() { }
+ morkBuf(const void* ioBuf, mork_fill inFill)
+ : mBuf_Body((void*) ioBuf), mBuf_Fill(inFill) { }
+
+ void ClearBufFill() { mBuf_Fill = 0; }
+
+ static void NilBufBodyError(morkEnv* ev);
+
+private: // copying is not allowed
+ morkBuf(const morkBuf& other);
+ morkBuf& operator=(const morkBuf& other);
+};
+
+/*| Blob: a buffer with an associated size, to increase known buf info
+**| to include max capacity in addition to buf location and content.
+**| This form factor allows us to allocate a vector of such blobs,
+**| which can share the same managing heap stored elsewhere, and that
+**| is why we don't include a pointer to a heap in this blob class.
+|*/
+class morkBlob : public morkBuf { // greater subset of nsIMdbYarn slots
+
+ // void* mBuf_Body; // space for holding any binary content
+ // mdb_fill mBuf_Fill; // logical content in Buf in bytes
+public:
+ mork_size mBlob_Size; // physical size of Buf in bytes
+
+public:
+ morkBlob() { }
+ morkBlob(const void* ioBuf, mork_fill inFill, mork_size inSize)
+ : morkBuf(ioBuf, inFill), mBlob_Size(inSize) { }
+
+ static void BlobFillOverSizeError(morkEnv* ev);
+
+public:
+ mork_bool GrowBlob(morkEnv* ev, nsIMdbHeap* ioHeap,
+ mork_size inNewSize);
+
+private: // copying is not allowed
+ morkBlob(const morkBlob& other);
+ morkBlob& operator=(const morkBlob& other);
+
+};
+
+/*| Text: a blob with an associated charset annotation, where the
+**| charset actually includes the general notion of typing, and not
+**| just a specification of character set alone; we want to permit
+**| arbitrary charset annotations for ad hoc binary types as well.
+**| (We avoid including a nsIMdbHeap pointer in morkText for the same
+**| reason morkBlob does: we want minimal size vectors of morkText.)
+|*/
+class morkText : public morkBlob { // greater subset of nsIMdbYarn slots
+
+ // void* mBuf_Body; // space for holding any binary content
+ // mdb_fill mBuf_Fill; // logical content in Buf in bytes
+ // mdb_size mBlob_Size; // physical size of Buf in bytes
+
+public:
+ mork_cscode mText_Form; // charset format encoding
+
+ morkText() { }
+
+private: // copying is not allowed
+ morkText(const morkText& other);
+ morkText& operator=(const morkText& other);
+};
+
+/*| Coil: a text with an associated nsIMdbHeap instance that provides
+**| all memory management for the space pointed to by mBuf_Body. (This
+**| was the hardest type to give a name in this small class hierarchy,
+**| because it's hard to characterize self-management of one's space.)
+**| A coil is a self-contained blob that knows how to grow itself as
+**| necessary to hold more content when necessary. Coil descends from
+**| morkText to include the mText_Form slot, even though this won't be
+**| needed always, because we are not as concerned about the overall
+**| size of this particular Coil object (if we were concerned about
+**| the size of an array of Coil instances, we would not bother with
+**| a separate heap pointer for each of them).
+**|
+**|| A coil makes a good medium in which to stream content as a sink,
+**| so we will have a subclass of morkSink called morkCoil that
+**| will stream bytes into this self-contained coil object. The name
+**| of this morkCoil class derives more from this intended usage than
+**| from anything else. The Mork code to parse db content will use
+**| coils with associated sinks to accumulate parsed strings.
+**|
+**|| Heap: this is the heap used for memory allocation. This instance
+**| is NOT refcounted, since this coil always assumes the heap is held
+**| through a reference elsewhere (for example, through the same object
+**| that contains or holds the coil itself. This lack of refcounting
+**| is consistent with the fact that morkCoil itself is not refcounted,
+**| and is not intended for use as a standalone object.
+|*/
+class morkCoil : public morkText { // self-managing text blob object
+
+ // void* mBuf_Body; // space for holding any binary content
+ // mdb_fill mBuf_Fill; // logical content in Buf in bytes
+ // mdb_size mBlob_Size; // physical size of Buf in bytes
+ // mdb_cscode mText_Form; // charset format encoding
+public:
+ nsIMdbHeap* mCoil_Heap; // storage manager for mBuf_Body pointer
+
+public:
+ morkCoil(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+ void CloseCoil(morkEnv* ev);
+
+ mork_bool GrowCoil(morkEnv* ev, mork_size inNewSize)
+ { return this->GrowBlob(ev, mCoil_Heap, inNewSize); }
+
+private: // copying is not allowed
+ morkCoil(const morkCoil& other);
+ morkCoil& operator=(const morkCoil& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBLOB_ */
diff --git a/db/mork/src/morkBuilder.cpp b/db/mork/src/morkBuilder.cpp
new file mode 100644
index 000000000..27e5bd198
--- /dev/null
+++ b/db/mork/src/morkBuilder.cpp
@@ -0,0 +1,1031 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkBuilder::CloseMorkNode(morkEnv* ev) // CloseBuilder() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseBuilder(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkBuilder::~morkBuilder() // assert CloseBuilder() executed earlier
+{
+ MORK_ASSERT(mBuilder_Store==0);
+ MORK_ASSERT(mBuilder_Row==0);
+ MORK_ASSERT(mBuilder_Table==0);
+ MORK_ASSERT(mBuilder_Cell==0);
+ MORK_ASSERT(mBuilder_RowSpace==0);
+ MORK_ASSERT(mBuilder_AtomSpace==0);
+}
+
+/*public non-poly*/
+morkBuilder::morkBuilder(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkStream* ioStream, mdb_count inBytesPerParseSegment,
+ nsIMdbHeap* ioSlotHeap, morkStore* ioStore)
+
+: morkParser(ev, inUsage, ioHeap, ioStream,
+ inBytesPerParseSegment, ioSlotHeap)
+
+, mBuilder_Store( 0 )
+
+, mBuilder_Table( 0 )
+, mBuilder_Row( 0 )
+, mBuilder_Cell( 0 )
+
+, mBuilder_RowSpace( 0 )
+, mBuilder_AtomSpace( 0 )
+
+, mBuilder_OidAtomSpace( 0 )
+, mBuilder_ScopeAtomSpace( 0 )
+
+, mBuilder_PortForm( 0 )
+, mBuilder_PortRowScope( (mork_scope) 'r' )
+, mBuilder_PortAtomScope( (mork_scope) 'v' )
+
+, mBuilder_TableForm( 0 )
+, mBuilder_TableRowScope( (mork_scope) 'r' )
+, mBuilder_TableAtomScope( (mork_scope) 'v' )
+, mBuilder_TableKind( 0 )
+
+, mBuilder_TablePriority( morkPriority_kLo )
+, mBuilder_TableIsUnique( morkBool_kFalse )
+, mBuilder_TableIsVerbose( morkBool_kFalse )
+, mBuilder_TablePadByte( 0 )
+
+, mBuilder_RowForm( 0 )
+, mBuilder_RowRowScope( (mork_scope) 'r' )
+, mBuilder_RowAtomScope( (mork_scope) 'v' )
+
+, mBuilder_CellForm( 0 )
+, mBuilder_CellAtomScope( (mork_scope) 'v' )
+
+, mBuilder_DictForm( 0 )
+, mBuilder_DictAtomScope( (mork_scope) 'v' )
+
+, mBuilder_MetaTokenSlot( 0 )
+
+, mBuilder_DoCutRow( morkBool_kFalse )
+, mBuilder_DoCutCell( morkBool_kFalse )
+, mBuilder_CellsVecFill( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioStore )
+ {
+ morkStore::SlotWeakStore(ioStore, ev, &mBuilder_Store);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kBuilder;
+ }
+ else
+ ev->NilPointerError();
+ }
+
+}
+
+/*public non-poly*/ void
+morkBuilder::CloseBuilder(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mBuilder_Row = 0;
+ mBuilder_Cell = 0;
+ mBuilder_MetaTokenSlot = 0;
+
+ morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table);
+ morkStore::SlotWeakStore((morkStore*) 0, ev, &mBuilder_Store);
+
+ morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev,
+ &mBuilder_RowSpace);
+
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mBuilder_AtomSpace);
+
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mBuilder_OidAtomSpace);
+
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mBuilder_ScopeAtomSpace);
+ this->CloseParser(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkBuilder::NonBuilderTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkBuilder");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderCellError(morkEnv* ev)
+{
+ ev->NewError("nil mBuilder_Cell");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderRowError(morkEnv* ev)
+{
+ ev->NewError("nil mBuilder_Row");
+}
+
+/*static*/ void
+morkBuilder::NilBuilderTableError(morkEnv* ev)
+{
+ ev->NewError("nil mBuilder_Table");
+}
+
+/*static*/ void
+morkBuilder::NonColumnSpaceScopeError(morkEnv* ev)
+{
+ ev->NewError("column space != 'c'");
+}
+
+void
+morkBuilder::LogGlitch(morkEnv* ev, const morkGlitch& inGlitch,
+ const char* inKind)
+{
+ MORK_USED_2(inGlitch,inKind);
+ ev->NewWarning("parsing glitch");
+}
+
+/*virtual*/ void
+morkBuilder::MidToYarn(morkEnv* ev,
+ const morkMid& inMid, // typically an alias to concat with strings
+ mdbYarn* outYarn)
+// The parser might ask that some aliases be turned into yarns, so they
+// can be concatenated into longer blobs under some circumstances. This
+// is an alternative to using a long and complex callback for many parts
+// for a single cell value.
+{
+ mBuilder_Store->MidToYarn(ev, inMid, outYarn);
+}
+
+/*virtual*/ void
+morkBuilder::OnNewPort(morkEnv* ev, const morkPlace& inPlace)
+// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd
+// mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch
+// mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row
+{
+ MORK_USED_2(ev,inPlace);
+ // mParser_InPort = morkBool_kTrue;
+ mBuilder_PortForm = 0;
+ mBuilder_PortRowScope = (mork_scope) 'r';
+ mBuilder_PortAtomScope = (mork_scope) 'v';
+}
+
+/*virtual*/ void
+morkBuilder::OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "port");
+}
+
+/*virtual*/ void
+morkBuilder::OnPortEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd
+{
+ MORK_USED_2(ev,inSpan);
+ // ev->StubMethodOnlyError();
+ // nothing to do?
+ // mParser_InPort = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid)
+{
+ MORK_USED_1(inPlace);
+ mParser_InGroup = morkBool_kTrue;
+ mork_pos startPos = inPlace.mPlace_Pos;
+
+ morkStore* store = mBuilder_Store;
+ if ( store )
+ {
+ if ( inGid >= store->mStore_CommitGroupIdentity )
+ store->mStore_CommitGroupIdentity = inGid + 1;
+
+ if ( !store->mStore_FirstCommitGroupPos )
+ store->mStore_FirstCommitGroupPos = startPos;
+ else if ( !store->mStore_SecondCommitGroupPos )
+ store->mStore_SecondCommitGroupPos = startPos;
+ }
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "group");
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan)
+{
+ MORK_USED_2(ev,inSpan);
+ // mParser_InGroup = morkBool_kFalse;
+ // ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan)
+{
+ MORK_USED_1(inSpan);
+ // mParser_InGroup = morkBool_kFalse;
+ ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnNewPortRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_change inChange)
+{
+ MORK_USED_3(inMid,inPlace,inChange);
+ // mParser_InPortRow = morkBool_kTrue;
+ ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "port row");
+}
+
+/*virtual*/ void
+morkBuilder::OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan)
+{
+ MORK_USED_1(inSpan);
+ // mParser_InPortRow = morkBool_kFalse;
+ ev->StubMethodOnlyError();
+}
+
+/*virtual*/ void
+morkBuilder::OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllRows)
+// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem ::= mp:Cell | OnMetaGlitch
+{
+ MORK_USED_1(inPlace);
+ // mParser_InTable = morkBool_kTrue;
+ mBuilder_TableForm = mBuilder_PortForm;
+ mBuilder_TableRowScope = mBuilder_PortRowScope;
+ mBuilder_TableAtomScope = mBuilder_PortAtomScope;
+ mBuilder_TableKind = morkStore_kNoneToken;
+
+ mBuilder_TablePriority = morkPriority_kLo;
+ mBuilder_TableIsUnique = morkBool_kFalse;
+ mBuilder_TableIsVerbose = morkBool_kFalse;
+
+ morkTable* table = mBuilder_Store->MidToTable(ev, inMid);
+ morkTable::SlotStrongTable(table, ev, &mBuilder_Table);
+ if ( table )
+ {
+ if ( table->mTable_RowSpace )
+ mBuilder_TableRowScope = table->mTable_RowSpace->SpaceScope();
+
+ if ( inCutAllRows )
+ table->CutAllRows(ev);
+ }
+}
+
+/*virtual*/ void
+morkBuilder::OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "table");
+}
+
+/*virtual*/ void
+morkBuilder::OnTableEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd
+{
+ MORK_USED_1(inSpan);
+ // mParser_InTable = morkBool_kFalse;
+ if ( mBuilder_Table )
+ {
+ mBuilder_Table->mTable_Priority = mBuilder_TablePriority;
+
+ if ( mBuilder_TableIsUnique )
+ mBuilder_Table->SetTableUnique();
+
+ if ( mBuilder_TableIsVerbose )
+ mBuilder_Table->SetTableVerbose();
+
+ morkTable::SlotStrongTable((morkTable*) 0, ev, &mBuilder_Table);
+ }
+ else
+ this->NilBuilderTableError(ev);
+
+ mBuilder_Row = 0;
+ mBuilder_Cell = 0;
+
+
+ mBuilder_TablePriority = morkPriority_kLo;
+ mBuilder_TableIsUnique = morkBool_kFalse;
+ mBuilder_TableIsVerbose = morkBool_kFalse;
+
+ if ( mBuilder_TableKind == morkStore_kNoneToken )
+ ev->NewError("missing table kind");
+
+ mBuilder_CellAtomScope = mBuilder_RowAtomScope =
+ mBuilder_TableAtomScope = mBuilder_PortAtomScope;
+
+ mBuilder_DoCutCell = morkBool_kFalse;
+ mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewMeta(morkEnv* ev, const morkPlace& inPlace)
+// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem ::= mp:Cell | OnMetaGlitch
+// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_2(ev,inPlace);
+ // mParser_InMeta = morkBool_kTrue;
+
+}
+
+/*virtual*/ void
+morkBuilder::OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "meta");
+}
+
+/*virtual*/ void
+morkBuilder::OnMetaEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd
+{
+ MORK_USED_2(ev,inSpan);
+ // mParser_InMeta = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnMinusRow(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mBuilder_DoCutRow = morkBool_kTrue;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllCols)
+// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+// mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch
+// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inPlace);
+ // mParser_InRow = morkBool_kTrue;
+
+ mBuilder_CellForm = mBuilder_RowForm = mBuilder_TableForm;
+ mBuilder_CellAtomScope = mBuilder_RowAtomScope = mBuilder_TableAtomScope;
+ mBuilder_RowRowScope = mBuilder_TableRowScope;
+ morkStore* store = mBuilder_Store;
+
+ if ( !inMid.mMid_Buf && !inMid.mMid_Oid.mOid_Scope )
+ {
+ morkMid mid(inMid);
+ mid.mMid_Oid.mOid_Scope = mBuilder_RowRowScope;
+ mBuilder_Row = store->MidToRow(ev, mid);
+ }
+ else
+ {
+ mBuilder_Row = store->MidToRow(ev, inMid);
+ }
+ morkRow* row = mBuilder_Row;
+ if ( row && inCutAllCols )
+ {
+ row->CutAllColumns(ev);
+ }
+
+ morkTable* table = mBuilder_Table;
+ if ( table )
+ {
+ if ( row )
+ {
+ if ( mParser_InMeta )
+ {
+ morkRow* metaRow = table->mTable_MetaRow;
+ if ( !metaRow )
+ {
+ table->mTable_MetaRow = row;
+ table->mTable_MetaRowOid = row->mRow_Oid;
+ row->AddRowGcUse(ev);
+ }
+ else if ( metaRow != row ) // not identical?
+ ev->NewError("duplicate table meta row");
+ }
+ else
+ {
+ if ( mBuilder_DoCutRow )
+ table->CutRow(ev, row);
+ else
+ table->AddRow(ev, row);
+ }
+ }
+ }
+ // else // it is now okay to have rows outside a table:
+ // this->NilBuilderTableError(ev);
+
+ mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnRowPos(morkEnv* ev, mork_pos inRowPos)
+{
+ if ( mBuilder_Row && mBuilder_Table && !mParser_InMeta )
+ {
+ mork_pos hintFromPos = 0; // best hint when we don't know position
+ mBuilder_Table->MoveRow(ev, mBuilder_Row, hintFromPos, inRowPos);
+ }
+}
+
+/*virtual*/ void
+morkBuilder::OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "row");
+}
+
+void
+morkBuilder::FlushBuilderCells(morkEnv* ev)
+{
+ if ( mBuilder_Row )
+ {
+ morkPool* pool = mBuilder_Store->StorePool();
+ morkCell* cells = mBuilder_CellsVec;
+ mork_fill fill = mBuilder_CellsVecFill;
+ mBuilder_Row->TakeCells(ev, cells, fill, mBuilder_Store);
+
+ morkCell* end = cells + fill;
+ --cells; // prepare for preincrement
+ while ( ++cells < end )
+ {
+ if ( cells->mCell_Atom )
+ cells->SetAtom(ev, (morkAtom*) 0, pool);
+ }
+ mBuilder_CellsVecFill = 0;
+ }
+ else
+ this->NilBuilderRowError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnRowEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+{
+ MORK_USED_1(inSpan);
+ // mParser_InRow = morkBool_kFalse;
+ if ( mBuilder_Row )
+ {
+ this->FlushBuilderCells(ev);
+ }
+ else
+ this->NilBuilderRowError(ev);
+
+ mBuilder_Row = 0;
+ mBuilder_Cell = 0;
+
+ mBuilder_DoCutCell = morkBool_kFalse;
+ mBuilder_DoCutRow = morkBool_kFalse;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewDict(morkEnv* ev, const morkPlace& inPlace)
+// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd
+// mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch
+{
+ MORK_USED_2(ev,inPlace);
+ // mParser_InDict = morkBool_kTrue;
+
+ mBuilder_CellForm = mBuilder_DictForm = mBuilder_PortForm;
+ mBuilder_CellAtomScope = mBuilder_DictAtomScope = mBuilder_PortAtomScope;
+}
+
+/*virtual*/ void
+morkBuilder::OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "dict");
+}
+
+/*virtual*/ void
+morkBuilder::OnDictEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd
+{
+ MORK_USED_2(ev,inSpan);
+ // mParser_InDict = morkBool_kFalse;
+
+ mBuilder_DictForm = 0;
+ mBuilder_DictAtomScope = 0;
+}
+
+/*virtual*/ void
+morkBuilder::OnAlias(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid)
+{
+ MORK_USED_1(inSpan);
+ if ( mParser_InDict )
+ {
+ morkMid mid = inMid; // local copy for modification
+ mid.mMid_Oid.mOid_Scope = mBuilder_DictAtomScope;
+ mBuilder_Store->AddAlias(ev, mid, mBuilder_DictForm);
+ }
+ else
+ ev->NewError("alias not in dict");
+}
+
+/*virtual*/ void
+morkBuilder::OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "alias");
+}
+
+
+morkCell*
+morkBuilder::AddBuilderCell(morkEnv* ev,
+ const morkMid& inMid, mork_change inChange)
+{
+ morkCell* outCell = 0;
+ mork_column column = inMid.mMid_Oid.mOid_Id;
+
+ if ( ev->Good() )
+ {
+ if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize )
+ this->FlushBuilderCells(ev);
+ if ( ev->Good() )
+ {
+ if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize )
+ {
+ mork_fill indx = mBuilder_CellsVecFill++;
+ outCell = mBuilder_CellsVec + indx;
+ outCell->SetColumnAndChange(column, inChange);
+ outCell->mCell_Atom = 0;
+ }
+ else
+ ev->NewError("out of builder cells");
+ }
+ }
+ return outCell;
+}
+
+/*virtual*/ void
+morkBuilder::OnMinusCell(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mBuilder_DoCutCell = morkBool_kTrue;
+}
+
+/*virtual*/ void
+morkBuilder::OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid* inMid, const morkBuf* inBuf)
+// Exactly one of inMid and inBuf is nil, and the other is non-nil.
+// When hex ID syntax is used for a column, then inMid is not nil, and
+// when a naked string names a column, then inBuf is not nil.
+
+ // mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+ // mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+ // mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inPlace);
+ // mParser_InCell = morkBool_kTrue;
+
+ mork_change cellChange = ( mBuilder_DoCutCell )?
+ morkChange_kCut : morkChange_kAdd;
+
+ mBuilder_DoCutCell = morkBool_kFalse;
+
+ mBuilder_CellAtomScope = mBuilder_RowAtomScope;
+
+ mBuilder_Cell = 0; // nil until determined for a row
+ morkStore* store = mBuilder_Store;
+ mork_scope scope = morkStore_kColumnSpaceScope;
+ morkMid tempMid; // space for local and modifiable cell mid
+ morkMid* cellMid = &tempMid; // default to local if inMid==0
+
+ if ( inMid ) // mid parameter is actually provided?
+ {
+ *cellMid = *inMid; // bitwise copy for modifiable local mid
+
+ if ( !cellMid->mMid_Oid.mOid_Scope )
+ {
+ if ( cellMid->mMid_Buf )
+ {
+ scope = store->BufToToken(ev, cellMid->mMid_Buf);
+ cellMid->mMid_Buf = 0; // don't do scope lookup again
+ ev->NewWarning("column mids need column scope");
+ }
+ cellMid->mMid_Oid.mOid_Scope = scope;
+ }
+ }
+ else if ( inBuf ) // buf points to naked column string name?
+ {
+ cellMid->ClearMid();
+ cellMid->mMid_Oid.mOid_Id = store->BufToToken(ev, inBuf);
+ cellMid->mMid_Oid.mOid_Scope = scope; // kColumnSpaceScope
+ }
+ else
+ ev->NilPointerError(); // either inMid or inBuf must be non-nil
+
+ mork_column column = cellMid->mMid_Oid.mOid_Id;
+
+ if ( mBuilder_Row && ev->Good() ) // this cell must be inside a row
+ {
+ // mBuilder_Cell = this->AddBuilderCell(ev, *cellMid, cellChange);
+
+ if ( mBuilder_CellsVecFill >= morkBuilder_kCellsVecSize )
+ this->FlushBuilderCells(ev);
+ if ( ev->Good() )
+ {
+ if ( mBuilder_CellsVecFill < morkBuilder_kCellsVecSize )
+ {
+ mork_fill ix = mBuilder_CellsVecFill++;
+ morkCell* cell = mBuilder_CellsVec + ix;
+ cell->SetColumnAndChange(column, cellChange);
+
+ cell->mCell_Atom = 0;
+ mBuilder_Cell = cell;
+ }
+ else
+ ev->NewError("out of builder cells");
+ }
+ }
+
+ else if ( mParser_InMeta && ev->Good() ) // cell is in metainfo structure?
+ {
+ if ( scope == morkStore_kColumnSpaceScope )
+ {
+ if ( mParser_InTable ) // metainfo for table?
+ {
+ if ( column == morkStore_kKindColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_TableKind;
+ else if ( column == morkStore_kStatusColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_TableStatus;
+ else if ( column == morkStore_kRowScopeColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_TableRowScope;
+ else if ( column == morkStore_kAtomScopeColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_TableAtomScope;
+ else if ( column == morkStore_kFormColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_TableForm;
+ }
+ else if ( mParser_InDict ) // metainfo for dict?
+ {
+ if ( column == morkStore_kAtomScopeColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_DictAtomScope;
+ else if ( column == morkStore_kFormColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_DictForm;
+ }
+ else if ( mParser_InRow ) // metainfo for row?
+ {
+ if ( column == morkStore_kAtomScopeColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_RowAtomScope;
+ else if ( column == morkStore_kRowScopeColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_RowRowScope;
+ else if ( column == morkStore_kFormColumn )
+ mBuilder_MetaTokenSlot = &mBuilder_RowForm;
+ }
+ }
+ else
+ ev->NewWarning("expected column scope");
+ }
+}
+
+/*virtual*/ void
+morkBuilder::OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch)
+{
+ this->LogGlitch(ev, inGlitch, "cell");
+}
+
+/*virtual*/ void
+morkBuilder::OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat)
+{
+ morkCell* cell = mBuilder_Cell;
+ if ( cell )
+ {
+ mBuilder_CellForm = inCharsetFormat;
+ }
+ else
+ this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnCellEnd(morkEnv* ev, const morkSpan& inSpan)
+// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+{
+ MORK_USED_2(ev,inSpan);
+ // mParser_InCell = morkBool_kFalse;
+
+ mBuilder_MetaTokenSlot = 0;
+ mBuilder_CellAtomScope = mBuilder_RowAtomScope;
+}
+
+/*virtual*/ void
+morkBuilder::OnValue(morkEnv* ev, const morkSpan& inSpan,
+ const morkBuf& inBuf)
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inSpan);
+ morkStore* store = mBuilder_Store;
+ morkCell* cell = mBuilder_Cell;
+ if ( cell )
+ {
+ mdbYarn yarn;
+ yarn.mYarn_Buf = inBuf.mBuf_Body;
+ yarn.mYarn_Fill = yarn.mYarn_Size = inBuf.mBuf_Fill;
+ yarn.mYarn_More = 0;
+ yarn.mYarn_Form = mBuilder_CellForm;
+ yarn.mYarn_Grow = 0;
+ morkAtom* atom = store->YarnToAtom(ev, &yarn, true /* create */);
+ cell->SetAtom(ev, atom, store->StorePool());
+ }
+ else if ( mParser_InMeta )
+ {
+ mork_token* metaSlot = mBuilder_MetaTokenSlot;
+ if ( metaSlot )
+ {
+ if ( metaSlot == &mBuilder_TableStatus ) // table status?
+ {
+ if ( mParser_InTable && mBuilder_Table )
+ {
+ const char* body = (const char*) inBuf.mBuf_Body;
+ mork_fill bufFill = inBuf.mBuf_Fill;
+ if ( body && bufFill )
+ {
+ const char* bodyEnd = body + bufFill;
+ while ( body < bodyEnd )
+ {
+ int c = *body++;
+ switch ( c )
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ mBuilder_TablePriority = (mork_priority) ( c - '0' );
+ break;
+
+ case 'u':
+ case 'U':
+ mBuilder_TableIsUnique = morkBool_kTrue;
+ break;
+
+ case 'v':
+ case 'V':
+ mBuilder_TableIsVerbose = morkBool_kTrue;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mork_token token = store->BufToToken(ev, &inBuf);
+ if ( token )
+ {
+ *metaSlot = token;
+ if ( metaSlot == &mBuilder_TableKind ) // table kind?
+ {
+ if ( mParser_InTable && mBuilder_Table )
+ mBuilder_Table->mTable_Kind = token;
+ }
+ }
+ }
+ }
+ }
+ else
+ this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid)
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inSpan);
+ morkStore* store = mBuilder_Store;
+ morkCell* cell = mBuilder_Cell;
+
+ morkMid valMid; // local mid for modifications
+ mdbOid* valOid = &valMid.mMid_Oid; // ref to oid inside mid
+ *valOid = inMid.mMid_Oid; // bitwise copy inMid's oid
+
+ if ( inMid.mMid_Buf )
+ {
+ if ( !valOid->mOid_Scope )
+ store->MidToOid(ev, inMid, valOid);
+ }
+ else if ( !valOid->mOid_Scope )
+ valOid->mOid_Scope = mBuilder_CellAtomScope;
+
+ if ( cell )
+ {
+ morkBookAtom* atom = store->MidToAtom(ev, valMid);
+ if ( atom )
+ cell->SetAtom(ev, atom, store->StorePool());
+ else
+ ev->NewError("undefined cell value alias");
+ }
+ else if ( mParser_InMeta )
+ {
+ mork_token* metaSlot = mBuilder_MetaTokenSlot;
+ if ( metaSlot )
+ {
+ mork_scope valScope = valOid->mOid_Scope;
+ if ( !valScope || valScope == morkStore_kColumnSpaceScope )
+ {
+ if ( ev->Good() && valMid.HasSomeId() )
+ {
+ *metaSlot = valOid->mOid_Id;
+ if ( metaSlot == &mBuilder_TableKind ) // table kind?
+ {
+ if ( mParser_InTable && mBuilder_Table )
+ {
+ mBuilder_Table->mTable_Kind = valOid->mOid_Id;
+ }
+ else
+ ev->NewWarning("mBuilder_TableKind not in table");
+ }
+ else if ( metaSlot == &mBuilder_TableStatus ) // table status?
+ {
+ if ( mParser_InTable && mBuilder_Table )
+ {
+ // $$ what here??
+ }
+ else
+ ev->NewWarning("mBuilder_TableStatus not in table");
+ }
+ }
+ }
+ else
+ this->NonColumnSpaceScopeError(ev);
+ }
+ }
+ else
+ this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid)
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inSpan);
+ morkStore* store = mBuilder_Store;
+ morkCell* cell = mBuilder_Cell;
+ if ( cell )
+ {
+ mdbOid rowOid = inMid.mMid_Oid;
+ if ( inMid.mMid_Buf )
+ {
+ if ( !rowOid.mOid_Scope )
+ store->MidToOid(ev, inMid, &rowOid);
+ }
+ else if ( !rowOid.mOid_Scope )
+ rowOid.mOid_Scope = mBuilder_RowRowScope;
+
+ if ( ev->Good() )
+ {
+ morkPool* pool = store->StorePool();
+ morkAtom* atom = pool->NewRowOidAtom(ev, rowOid, &store->mStore_Zone);
+ if ( atom )
+ {
+ cell->SetAtom(ev, atom, pool);
+ morkRow* row = store->OidToRow(ev, &rowOid);
+ if ( row ) // found or created such a row?
+ row->AddRowGcUse(ev);
+ }
+ }
+ }
+ else
+ this->NilBuilderCellError(ev);
+}
+
+/*virtual*/ void
+morkBuilder::OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid)
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+{
+ MORK_USED_1(inSpan);
+ morkStore* store = mBuilder_Store;
+ morkCell* cell = mBuilder_Cell;
+ if ( cell )
+ {
+ mdbOid tableOid = inMid.mMid_Oid;
+ if ( inMid.mMid_Buf )
+ {
+ if ( !tableOid.mOid_Scope )
+ store->MidToOid(ev, inMid, &tableOid);
+ }
+ else if ( !tableOid.mOid_Scope )
+ tableOid.mOid_Scope = mBuilder_RowRowScope;
+
+ if ( ev->Good() )
+ {
+ morkPool* pool = store->StorePool();
+ morkAtom* atom = pool->NewTableOidAtom(ev, tableOid, &store->mStore_Zone);
+ if ( atom )
+ {
+ cell->SetAtom(ev, atom, pool);
+ morkTable* table = store->OidToTable(ev, &tableOid,
+ /*optionalMetaRowOid*/ (mdbOid*) 0);
+ if ( table ) // found or created such a table?
+ table->AddTableGcUse(ev);
+ }
+ }
+ }
+ else
+ this->NilBuilderCellError(ev);
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkBuilder.h b/db/mork/src/morkBuilder.h
new file mode 100644
index 000000000..cd8b9527a
--- /dev/null
+++ b/db/mork/src/morkBuilder.h
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKBUILDER_
+#define _MORKBUILDER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| kCellsVecSize: length of cell vector buffer inside morkBuilder
+|*/
+#define morkBuilder_kCellsVecSize 64
+
+#define morkBuilder_kDefaultBytesPerParseSegment 512 /* plausible to big */
+
+#define morkDerived_kBuilder /*i*/ 0x4275 /* ascii 'Bu' */
+
+class morkBuilder /*d*/ : public morkParser {
+
+// public: // slots inherited from morkParser (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+
+ // nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation
+ // morkStream* mParser_Stream; // refcounted input stream
+
+ // mork_u4 mParser_Tag; // must equal morkParser_kTag
+ // mork_count mParser_MoreGranularity; // constructor inBytesPerParseSegment
+
+ // mork_u4 mParser_State; // state where parser should resume
+
+ // after finding ends of group transactions, we can re-seek the start:
+ // mork_pos mParser_GroupContentStartPos; // start of this group
+
+ // mdbOid mParser_TableOid; // table oid if inside a table
+ // mdbOid mParser_RowOid; // row oid if inside a row
+ // mork_gid mParser_GroupId; // group ID if inside a group
+
+ // mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd?
+ // mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd?
+ // mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd?
+ // mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd?
+
+ // morkMid mParser_Mid; // current alias being parsed
+ // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below:
+
+ // blob coils allocated in mParser_Heap
+ // morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs
+ // morkCoil mParser_ValueCoil; // place to accumulate value blobs
+ // morkCoil mParser_ColumnCoil; // place to accumulate column blobs
+ // morkCoil mParser_StringCoil; // place to accumulate string blobs
+
+ // morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil
+ // morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil
+ // morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil
+ // morkSpool mParser_StringSpool; // writes to mParser_StringCoil
+
+ // yarns allocated in mParser_Heap
+ // morkYarn mParser_MidYarn; // place to receive from MidToYarn()
+
+ // span showing current ongoing file position status:
+ // morkSpan mParser_PortSpan; // span of current db port file
+
+ // various spans denoting nested subspaces inside the file's port span:
+ // morkSpan mParser_GroupSpan; // span of current transaction group
+ // morkSpan mParser_DictSpan;
+ // morkSpan mParser_AliasSpan;
+ // morkSpan mParser_MetaDictSpan;
+ // morkSpan mParser_TableSpan;
+ // morkSpan mParser_MetaTableSpan;
+ // morkSpan mParser_RowSpan;
+ // morkSpan mParser_MetaRowSpan;
+ // morkSpan mParser_CellSpan;
+ // morkSpan mParser_ColumnSpan;
+ // morkSpan mParser_SlotSpan;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected morkBuilder members
+
+ // weak refs that do not prevent closure of referenced nodes:
+ morkStore* mBuilder_Store; // weak ref to builder's store
+
+ // strong refs that do indeed prevent closure of referenced nodes:
+ morkTable* mBuilder_Table; // current table being built (or nil)
+ morkRow* mBuilder_Row; // current row being built (or nil)
+ morkCell* mBuilder_Cell; // current cell within CellsVec (or nil)
+
+ morkRowSpace* mBuilder_RowSpace; // space for mBuilder_CellRowScope
+ morkAtomSpace* mBuilder_AtomSpace; // space for mBuilder_CellAtomScope
+
+ morkAtomSpace* mBuilder_OidAtomSpace; // ground atom space for oids
+ morkAtomSpace* mBuilder_ScopeAtomSpace; // ground atom space for scopes
+
+ // scoped object ids for current objects under construction:
+ mdbOid mBuilder_TableOid; // full oid for current table
+ mdbOid mBuilder_RowOid; // full oid for current row
+
+ // tokens that become set as the result of meta cells in port rows:
+ mork_cscode mBuilder_PortForm; // default port charset format
+ mork_scope mBuilder_PortRowScope; // port row scope
+ mork_scope mBuilder_PortAtomScope; // port atom scope
+
+ // tokens that become set as the result of meta cells in meta tables:
+ mork_cscode mBuilder_TableForm; // default table charset format
+ mork_scope mBuilder_TableRowScope; // table row scope
+ mork_scope mBuilder_TableAtomScope; // table atom scope
+ mork_kind mBuilder_TableKind; // table kind
+
+ mork_token mBuilder_TableStatus; // dummy: priority/unique/verbose
+
+ mork_priority mBuilder_TablePriority; // table priority
+ mork_bool mBuilder_TableIsUnique; // table uniqueness
+ mork_bool mBuilder_TableIsVerbose; // table verboseness
+ mork_u1 mBuilder_TablePadByte; // for u4 alignment
+
+ // tokens that become set as the result of meta cells in meta rows:
+ mork_cscode mBuilder_RowForm; // default row charset format
+ mork_scope mBuilder_RowRowScope; // row scope per row metainfo
+ mork_scope mBuilder_RowAtomScope; // row atom scope
+
+ // meta tokens currently in force, driven by meta info slots above:
+ mork_cscode mBuilder_CellForm; // cell charset format
+ mork_scope mBuilder_CellAtomScope; // cell atom scope
+
+ mork_cscode mBuilder_DictForm; // dict charset format
+ mork_scope mBuilder_DictAtomScope; // dict atom scope
+
+ mork_token* mBuilder_MetaTokenSlot; // pointer to some slot above
+
+ // If any of these 'cut' bools are true, it means a minus was seen in the
+ // Mork source text to indicate removal of content from some container.
+ // (Note there is no corresponding 'add' bool, since add is the default.)
+ // CutRow implies the current row should be cut from the table.
+ // CutCell implies the current column should be cut from the row.
+ mork_bool mBuilder_DoCutRow; // row with kCut change
+ mork_bool mBuilder_DoCutCell; // cell with kCut change
+ mork_u1 mBuilder_row_pad; // pad to u4 alignment
+ mork_u1 mBuilder_cell_pad; // pad to u4 alignment
+
+ morkCell mBuilder_CellsVec[ morkBuilder_kCellsVecSize + 1 ];
+ mork_fill mBuilder_CellsVecFill; // count used in CellsVec
+ // Note when mBuilder_CellsVecFill equals morkBuilder_kCellsVecSize, and
+ // another cell is added, this means all the cells in the vector above
+ // must be flushed to the current row being built to create more room.
+
+protected: // protected inlines
+
+ mork_bool CellVectorIsFull() const
+ { return ( mBuilder_CellsVecFill == morkBuilder_kCellsVecSize ); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseBuilder() only if open
+ virtual ~morkBuilder(); // assert that CloseBuilder() executed earlier
+
+public: // morkYarn construction & destruction
+ morkBuilder(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkStream* ioStream, // the readonly stream for input bytes
+ mdb_count inBytesPerParseSegment, // target for ParseMore()
+ nsIMdbHeap* ioSlotHeap, morkStore* ioStore
+ );
+
+ void CloseBuilder(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkBuilder(const morkBuilder& other);
+ morkBuilder& operator=(const morkBuilder& other);
+
+public: // dynamic type identification
+ mork_bool IsBuilder() const
+ { return IsNode() && mNode_Derived == morkDerived_kBuilder; }
+// } ===== end morkNode methods =====
+
+public: // errors
+ static void NonBuilderTypeError(morkEnv* ev);
+ static void NilBuilderCellError(morkEnv* ev);
+ static void NilBuilderRowError(morkEnv* ev);
+ static void NilBuilderTableError(morkEnv* ev);
+ static void NonColumnSpaceScopeError(morkEnv* ev);
+
+ void LogGlitch(morkEnv* ev, const morkGlitch& inGlitch,
+ const char* inKind);
+
+public: // other builder methods
+
+ morkCell* AddBuilderCell(morkEnv* ev,
+ const morkMid& inMid, mork_change inChange);
+
+ void FlushBuilderCells(morkEnv* ev);
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // in virtual morkParser methods, data flow subclass to parser
+
+ virtual void MidToYarn(morkEnv* ev,
+ const morkMid& inMid, // typically an alias to concat with strings
+ mdbYarn* outYarn) override;
+ // The parser might ask that some aliases be turned into yarns, so they
+ // can be concatenated into longer blobs under some circumstances. This
+ // is an alternative to using a long and complex callback for many parts
+ // for a single cell value.
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // out virtual morkParser methods, data flow parser to subclass
+
+ virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) override;
+ virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) override;
+ virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) override;
+ virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_change inChange) override;
+ virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllRows) override;
+ virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) override;
+ virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnMinusRow(morkEnv* ev) override;
+ virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllCols) override;
+ virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) override;
+ virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) override;
+ virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) override;
+
+ virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+
+ virtual void OnMinusCell(morkEnv* ev) override;
+ virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid* inMid, const morkBuf* inBuf) override;
+ // Exactly one of inMid and inBuf is nil, and the other is non-nil.
+ // When hex ID syntax is used for a column, then inMid is not nil, and
+ // when a naked string names a column, then inBuf is not nil.
+
+ virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) override;
+ virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) override;
+ virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) override;
+
+ virtual void OnValue(morkEnv* ev, const morkSpan& inSpan,
+ const morkBuf& inBuf) override;
+
+ virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) override;
+
+ virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) override;
+
+ virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) override;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkBuilder methods
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakBuilder(morkBuilder* me,
+ morkEnv* ev, morkBuilder** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongBuilder(morkBuilder* me,
+ morkEnv* ev, morkBuilder** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKBUILDER_ */
diff --git a/db/mork/src/morkCell.cpp b/db/mork/src/morkCell.cpp
new file mode 100644
index 000000000..80f9e04d0
--- /dev/null
+++ b/db/mork/src/morkCell.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+void
+morkCell::SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore)
+{
+ morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */);
+ if ( atom )
+ this->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom
+}
+
+void
+morkCell::GetYarn(morkEnv* ev, mdbYarn* outYarn) const
+{
+ MORK_USED_1(ev);
+ morkAtom::GetYarn(mCell_Atom, outYarn);
+}
+
+void
+morkCell::AliasYarn(morkEnv* ev, mdbYarn* outYarn) const
+{
+ MORK_USED_1(ev);
+ morkAtom::AliasYarn(mCell_Atom, outYarn);
+}
+
+void
+morkCell::SetCellClean()
+{
+ mork_column col = this->GetColumn();
+ this->SetColumnAndChange(col, morkChange_kNil);
+}
+
+void
+morkCell::SetCellDirty()
+{
+ mork_column col = this->GetColumn();
+ this->SetColumnAndChange(col, morkChange_kAdd);
+}
+
+void
+morkCell::SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool)
+ // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse()
+ // to increase the refcount, and puts ioAtom into mCell_Atom. If the old
+ // atom in mCell_Atom is non-nil, then it is "released" first by a call to
+ // CutCellUse(), and if the use count then becomes zero, then the old atom
+ // is deallocated by returning it to the pool ioPool. (And this is
+ // why ioPool is a parameter to this method.) Note that ioAtom can be nil
+ // to cause the cell to refer to nothing, and the old atom in mCell_Atom
+ // can also be nil, and all the atom refcounting is handled correctly.
+ //
+ // Note that if ioAtom was just created, it typically has a zero use count
+ // before calling SetAtom(). But use count is one higher after SetAtom().
+{
+ morkAtom* oldAtom = mCell_Atom;
+ if ( oldAtom != ioAtom ) // ioAtom is not already installed in this cell?
+ {
+ if ( oldAtom )
+ {
+ mCell_Atom = 0;
+ if ( oldAtom->CutCellUse(ev) == 0 )
+ {
+ // this was zapping atoms still in use - comment out until davidmc
+ // can figure out a better fix.
+// if ( ioPool )
+// {
+// if ( oldAtom->IsBook() )
+// ((morkBookAtom*) oldAtom)->CutBookAtomFromSpace(ev);
+
+// ioPool->ZapAtom(ev, oldAtom);
+// }
+// else
+// ev->NilPointerError();
+ }
+ }
+ if ( ioAtom )
+ ioAtom->AddCellUse(ev);
+
+ mCell_Atom = ioAtom;
+ }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkCell.h b/db/mork/src/morkCell.h
new file mode 100644
index 000000000..50642d916
--- /dev/null
+++ b/db/mork/src/morkCell.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKCELL_
+#define _MORKCELL_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDelta_kShift 8 /* 8 bit shift */
+#define morkDelta_kChangeMask 0x0FF /* low 8 bit mask */
+#define morkDelta_kColumnMask (~ (mork_column) morkDelta_kChangeMask)
+#define morkDelta_Init(self,cl,ch) ((self) = ((cl)<<morkDelta_kShift) | (ch))
+#define morkDelta_Change(self) ((mork_change) ((self) & morkDelta_kChangeMask))
+#define morkDelta_Column(self) ((self) >> morkDelta_kShift)
+
+class morkCell { // minimal cell format
+
+public:
+ mork_delta mCell_Delta; // encoding of both column and change
+ morkAtom* mCell_Atom; // content in this cell
+
+public:
+ morkCell() : mCell_Delta( 0 ), mCell_Atom( 0 ) { }
+
+ morkCell(const morkCell& c)
+ : mCell_Delta( c.mCell_Delta ), mCell_Atom( c.mCell_Atom ) { }
+
+ // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse():
+ morkCell(mork_column inCol, mork_change inChange, morkAtom* ioAtom)
+ {
+ morkDelta_Init(mCell_Delta, inCol,inChange);
+ mCell_Atom = ioAtom;
+ }
+
+ // note if ioAtom is non-nil, caller needs to call ioAtom->AddCellUse():
+ void Init(mork_column inCol, mork_change inChange, morkAtom* ioAtom)
+ {
+ morkDelta_Init(mCell_Delta,inCol,inChange);
+ mCell_Atom = ioAtom;
+ }
+
+ mork_column GetColumn() const { return morkDelta_Column(mCell_Delta); }
+ mork_change GetChange() const { return morkDelta_Change(mCell_Delta); }
+
+ mork_bool IsCellClean() const { return GetChange() == morkChange_kNil; }
+ mork_bool IsCellDirty() const { return GetChange() != morkChange_kNil; }
+
+ void SetCellClean(); // set change to kNil
+ void SetCellDirty(); // set change to kAdd
+
+ void SetCellColumnDirty(mork_column inCol)
+ { this->SetColumnAndChange(inCol, morkChange_kAdd); }
+
+ void SetCellColumnClean(mork_column inCol)
+ { this->SetColumnAndChange(inCol, morkChange_kNil); }
+
+ void SetColumnAndChange(mork_column inCol, mork_change inChange)
+ { morkDelta_Init(mCell_Delta, inCol, inChange); }
+
+ morkAtom* GetAtom() { return mCell_Atom; }
+
+ void SetAtom(morkEnv* ev, morkAtom* ioAtom, morkPool* ioPool);
+ // SetAtom() "acquires" the new ioAtom if non-nil, by calling AddCellUse()
+ // to increase the refcount, and puts ioAtom into mCell_Atom. If the old
+ // atom in mCell_Atom is non-nil, then it is "released" first by a call to
+ // CutCellUse(), and if the use count then becomes zero, then the old atom
+ // is deallocated by returning it to the pool ioPool. (And this is
+ // why ioPool is a parameter to this method.) Note that ioAtom can be nil
+ // to cause the cell to refer to nothing, and the old atom in mCell_Atom
+ // can also be nil, and all the atom refcounting is handled correctly.
+ //
+ // Note that if ioAtom was just created, it typically has a zero use count
+ // before calling SetAtom(). But use count is one higher after SetAtom().
+
+ void SetYarn(morkEnv* ev, const mdbYarn* inYarn, morkStore* ioStore);
+
+ void AliasYarn(morkEnv* ev, mdbYarn* outYarn) const;
+ void GetYarn(morkEnv* ev, mdbYarn* outYarn) const;
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCELL_ */
diff --git a/db/mork/src/morkCellObject.cpp b/db/mork/src/morkCellObject.cpp
new file mode 100644
index 000000000..210b6d6e2
--- /dev/null
+++ b/db/mork/src/morkCellObject.cpp
@@ -0,0 +1,530 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkCellObject::CloseMorkNode(morkEnv* ev) // CloseCellObject() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseCellObject(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkCellObject::~morkCellObject() // assert CloseCellObject() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(mCellObject_Row==0);
+}
+
+/*public non-poly*/
+morkCellObject::morkCellObject(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell,
+ mork_column inCol, mork_pos inPos)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mCellObject_RowObject( 0 )
+, mCellObject_Row( 0 )
+, mCellObject_Cell( 0 )
+, mCellObject_Col( inCol )
+, mCellObject_RowSeed( 0 )
+, mCellObject_Pos( (mork_u2) inPos )
+{
+ if ( ev->Good() )
+ {
+ if ( ioRow && ioCell )
+ {
+ if ( ioRow->IsRow() )
+ {
+ morkStore* store = ioRow->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ morkRowObject* rowObj = ioRow->AcquireRowObject(ev, store);
+ if ( rowObj )
+ {
+ mCellObject_Row = ioRow;
+ mCellObject_Cell = ioCell;
+ mCellObject_RowSeed = ioRow->mRow_Seed;
+
+ // morkRowObject::SlotStrongRowObject(rowObj, ev,
+ // &mCellObject_RowObject);
+
+ mCellObject_RowObject = rowObj; // assume control of strong ref
+ }
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kCellObject;
+ }
+ }
+ else
+ ioRow->NonRowTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkCellObject, morkObject, nsIMdbCell)
+
+/*public non-poly*/ void
+morkCellObject::CloseCellObject(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ NS_RELEASE(mCellObject_RowObject);
+ mCellObject_Row = 0;
+ mCellObject_Cell = 0;
+ mCellObject_RowSeed = 0;
+ this->CloseObject(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+mork_bool
+morkCellObject::ResyncWithRow(morkEnv* ev)
+{
+ morkRow* row = mCellObject_Row;
+ mork_pos pos = 0;
+ morkCell* cell = row->GetCell(ev, mCellObject_Col, &pos);
+ if ( cell )
+ {
+ mCellObject_Pos = (mork_u2) pos;
+ mCellObject_Cell = cell;
+ mCellObject_RowSeed = row->mRow_Seed;
+ }
+ else
+ {
+ mCellObject_Cell = 0;
+ this->MissingRowColumnError(ev);
+ }
+ return ev->Good();
+}
+
+morkAtom*
+morkCellObject::GetCellAtom(morkEnv* ev) const
+{
+ morkCell* cell = mCellObject_Cell;
+ if ( cell )
+ return cell->GetAtom();
+ else
+ this->NilCellError(ev);
+
+ return (morkAtom*) 0;
+}
+
+/*static*/ void
+morkCellObject::WrongRowObjectRowError(morkEnv* ev)
+{
+ ev->NewError("mCellObject_Row != mCellObject_RowObject->mRowObject_Row");
+}
+
+/*static*/ void
+morkCellObject::NilRowError(morkEnv* ev)
+{
+ ev->NewError("nil mCellObject_Row");
+}
+
+/*static*/ void
+morkCellObject::NilRowObjectError(morkEnv* ev)
+{
+ ev->NewError("nil mCellObject_RowObject");
+}
+
+/*static*/ void
+morkCellObject::NilCellError(morkEnv* ev)
+{
+ ev->NewError("nil mCellObject_Cell");
+}
+
+/*static*/ void
+morkCellObject::NonCellObjectTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkCellObject");
+}
+
+/*static*/ void
+morkCellObject::MissingRowColumnError(morkEnv* ev)
+{
+ ev->NewError("mCellObject_Col not in mCellObject_Row");
+}
+
+nsIMdbCell*
+morkCellObject::AcquireCellHandle(morkEnv* ev)
+{
+ nsIMdbCell* outCell = this;
+ NS_ADDREF(outCell);
+ return outCell;
+}
+
+
+morkEnv*
+morkCellObject::CanUseCell(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr, morkCell** outCell)
+{
+ morkEnv* outEnv = 0;
+ morkCell* cell = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( IsCellObject() )
+ {
+ if ( IsMutable() || !inMutable )
+ {
+ morkRowObject* rowObj = mCellObject_RowObject;
+ if ( rowObj )
+ {
+ morkRow* row = mCellObject_Row;
+ if ( row )
+ {
+ if ( rowObj->mRowObject_Row == row )
+ {
+ mork_u2 oldSeed = mCellObject_RowSeed;
+ if ( row->mRow_Seed == oldSeed || ResyncWithRow(ev) )
+ {
+ cell = mCellObject_Cell;
+ if ( cell )
+ {
+ outEnv = ev;
+ }
+ else
+ NilCellError(ev);
+ }
+ }
+ else
+ WrongRowObjectRowError(ev);
+ }
+ else
+ NilRowError(ev);
+ }
+ else
+ NilRowObjectError(ev);
+ }
+ else
+ NonMutableNodeError(ev);
+ }
+ else
+ NonCellObjectTypeError(ev);
+ }
+ *outErr = ev->AsErr();
+ MORK_ASSERT(outEnv);
+ *outCell = cell;
+
+ return outEnv;
+}
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP morkCellObject::SetBlob(nsIMdbEnv* /* mev */,
+ nsIMdbBlob* /* ioBlob */)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+} // reads inBlob slots
+
+// when inBlob is in the same suite, this might be fastest cell-to-cell
+
+NS_IMETHODIMP morkCellObject::ClearBlob( // make empty (so content has zero length)
+ nsIMdbEnv* /* mev */)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ // remember row->MaybeDirtySpaceStoreAndRow();
+}
+// clearing a yarn is like SetYarn() with empty yarn instance content
+
+NS_IMETHODIMP morkCellObject::GetBlobFill(nsIMdbEnv* mev,
+ mdb_fill* outFill)
+// Same value that would be put into mYarn_Fill, if one called GetYarn()
+// with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+} // size of blob
+
+NS_IMETHODIMP morkCellObject::SetYarn(nsIMdbEnv* mev,
+ const mdbYarn* inYarn)
+{
+ nsresult outErr = NS_OK;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ morkRow* row = mCellObject_Row;
+ if ( row )
+ {
+ morkStore* store = row->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ cell->SetYarn(ev, inYarn, store);
+ if ( row->IsRowClean() && store->mStore_CanDirty )
+ row->MaybeDirtySpaceStoreAndRow();
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+} // reads from yarn slots
+// make this text object contain content from the yarn's buffer
+
+NS_IMETHODIMP morkCellObject::GetYarn(nsIMdbEnv* mev,
+ mdbYarn* outYarn)
+{
+ nsresult outErr = NS_OK;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ morkAtom* atom = cell->GetAtom();
+ morkAtom::GetYarn(atom, outYarn);
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+} // writes some yarn slots
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+NS_IMETHODIMP morkCellObject::AliasYarn(nsIMdbEnv* mev,
+ mdbYarn* outYarn)
+{
+ nsresult outErr = NS_OK;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ morkAtom* atom = cell->GetAtom();
+ morkAtom::AliasYarn(atom, outYarn);
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+} // writes ALL yarn slots
+
+// } ----- end attribute methods -----
+
+// } ===== end nsIMdbBlob methods =====
+
+// { ===== begin nsIMdbCell methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP morkCellObject::SetColumn(nsIMdbEnv* mev, mdb_column inColumn)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ // remember row->MaybeDirtySpaceStoreAndRow();
+}
+
+NS_IMETHODIMP morkCellObject::GetColumn(nsIMdbEnv* mev, mdb_column* outColumn)
+{
+ nsresult outErr = NS_OK;
+ mdb_column col = 0;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ col = mCellObject_Col;
+ outErr = ev->AsErr();
+ }
+ if ( outColumn )
+ *outColumn = col;
+ return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetCellInfo( // all cell metainfo except actual content
+ nsIMdbEnv* mev,
+ mdb_column* outColumn, // the column in the containing row
+ mdb_fill* outBlobFill, // the size of text content in bytes
+ mdbOid* outChildOid, // oid of possible row or table child
+ mdb_bool* outIsRowChild) // nonzero if child, and a row child
+// Checking all cell metainfo is a good way to avoid forcing a large cell
+// in to memory when you don't actually want to use the content.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::GetRow(nsIMdbEnv* mev, // parent row for this cell
+ nsIMdbRow** acqRow)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ outRow = mCellObject_RowObject->AcquireRowHandle(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetPort(nsIMdbEnv* mev, // port containing cell
+ nsIMdbPort** acqPort)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbPort* outPort = 0;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ if ( mCellObject_Row )
+ {
+ morkStore* store = mCellObject_Row->GetRowSpaceStore(ev);
+ if ( store )
+ outPort = store->AcquireStoreHandle(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqPort )
+ *acqPort = outPort;
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin children methods -----
+NS_IMETHODIMP morkCellObject::HasAnyChild( // does cell have a child instead of text?
+ nsIMdbEnv* mev,
+ mdbOid* outOid, // out id of row or table (or unbound if no child)
+ mdb_bool* outIsRow) // nonzero if child is a row (rather than a table)
+{
+ nsresult outErr = NS_OK;
+ mdb_bool isRow = morkBool_kFalse;
+ outOid->mOid_Scope = 0;
+ outOid->mOid_Id = morkId_kMinusOne;
+ morkCell* cell = 0;
+ morkEnv* ev = this->CanUseCell(mev, /*inMutable*/ morkBool_kTrue,
+ &outErr, &cell);
+ if ( ev )
+ {
+ morkAtom* atom = GetCellAtom(ev);
+ if ( atom )
+ {
+ isRow = atom->IsRowOid();
+ if ( isRow || atom->IsTableOid() )
+ *outOid = ((morkOidAtom*) atom)->mOidAtom_Oid;
+ }
+
+ outErr = ev->AsErr();
+ }
+ if ( outIsRow )
+ *outIsRow = isRow;
+
+ return outErr;
+}
+
+NS_IMETHODIMP morkCellObject::GetAnyChild( // access table of specific attribute
+ nsIMdbEnv* mev, // context
+ nsIMdbRow** acqRow, // child row (or null)
+ nsIMdbTable** acqTable) // child table (or null)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::SetChildRow( // access table of specific attribute
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+} // inRow must be bound inside this same db port
+
+NS_IMETHODIMP morkCellObject::GetChildRow( // access row of specific attribute
+ nsIMdbEnv* mev, // context
+ nsIMdbRow** acqRow) // acquire child row (or nil if no child)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP morkCellObject::SetChildTable( // access table of specific attribute
+ nsIMdbEnv* mev, // context
+ nsIMdbTable* inTable) // table must be bound inside this same db port
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ // remember row->MaybeDirtySpaceStoreAndRow();
+}
+
+NS_IMETHODIMP morkCellObject::GetChildTable( // access table of specific attribute
+ nsIMdbEnv* mev, // context
+ nsIMdbTable** acqTable) // acquire child tabdle (or nil if no chil)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkCellObject.h b/db/mork/src/morkCellObject.h
new file mode 100644
index 000000000..fced0accd
--- /dev/null
+++ b/db/mork/src/morkCellObject.h
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKCELLOBJECT_
+#define _MORKCELLOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kCellObject /*i*/ 0x634F /* ascii 'cO' */
+
+class morkCellObject : public morkObject, public nsIMdbCell { // blob attribute in column scope
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+ NS_DECL_ISUPPORTS_INHERITED
+
+ morkRowObject* mCellObject_RowObject; // strong ref to row's object
+ morkRow* mCellObject_Row; // cell's row if still in row object
+ morkCell* mCellObject_Cell; // cell in row if rowseed matches
+ mork_column mCellObject_Col; // col of cell last living in pos
+ mork_u2 mCellObject_RowSeed; // copy of row's seed
+ mork_u2 mCellObject_Pos; // position of cell in row
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseCellObject() only if open
+
+public: // morkCellObject construction & destruction
+ morkCellObject(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkRow* ioRow, morkCell* ioCell,
+ mork_column inCol, mork_pos inPos);
+ void CloseCellObject(morkEnv* ev); // called by CloseMorkNode();
+
+ NS_IMETHOD SetBlob(nsIMdbEnv* ev,
+ nsIMdbBlob* ioBlob) override; // reads inBlob slots
+ // when inBlob is in the same suite, this might be fastest cell-to-cell
+
+ NS_IMETHOD ClearBlob( // make empty (so content has zero length)
+ nsIMdbEnv* ev) override;
+ // clearing a yarn is like SetYarn() with empty yarn instance content
+
+ NS_IMETHOD GetBlobFill(nsIMdbEnv* ev,
+ mdb_fill* outFill) override; // size of blob
+ // Same value that would be put into mYarn_Fill, if one called GetYarn()
+ // with a yarn instance that had mYarn_Buf==nil and mYarn_Size==0.
+
+ NS_IMETHOD SetYarn(nsIMdbEnv* ev,
+ const mdbYarn* inYarn) override; // reads from yarn slots
+ // make this text object contain content from the yarn's buffer
+
+ NS_IMETHOD GetYarn(nsIMdbEnv* ev,
+ mdbYarn* outYarn) override; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+ NS_IMETHOD AliasYarn(nsIMdbEnv* ev,
+ mdbYarn* outYarn) override; // writes ALL yarn slots
+ NS_IMETHOD SetColumn(nsIMdbEnv* ev, mdb_column inColumn) override;
+ NS_IMETHOD GetColumn(nsIMdbEnv* ev, mdb_column* outColumn) override;
+
+ NS_IMETHOD GetCellInfo( // all cell metainfo except actual content
+ nsIMdbEnv* ev,
+ mdb_column* outColumn, // the column in the containing row
+ mdb_fill* outBlobFill, // the size of text content in bytes
+ mdbOid* outChildOid, // oid of possible row or table child
+ mdb_bool* outIsRowChild) override; // nonzero if child, and a row child
+
+ // Checking all cell metainfo is a good way to avoid forcing a large cell
+ // in to memory when you don't actually want to use the content.
+
+ NS_IMETHOD GetRow(nsIMdbEnv* ev, // parent row for this cell
+ nsIMdbRow** acqRow) override;
+ NS_IMETHOD GetPort(nsIMdbEnv* ev, // port containing cell
+ nsIMdbPort** acqPort) override;
+ // } ----- end attribute methods -----
+
+ // { ----- begin children methods -----
+ NS_IMETHOD HasAnyChild( // does cell have a child instead of text?
+ nsIMdbEnv* ev,
+ mdbOid* outOid, // out id of row or table (or unbound if no child)
+ mdb_bool* outIsRow) override; // nonzero if child is a row (rather than a table)
+
+ NS_IMETHOD GetAnyChild( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // child row (or null)
+ nsIMdbTable** acqTable) override; // child table (or null)
+
+
+ NS_IMETHOD SetChildRow( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) override; // inRow must be bound inside this same db port
+
+ NS_IMETHOD GetChildRow( // access row of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow) override; // acquire child row (or nil if no child)
+
+
+ NS_IMETHOD SetChildTable( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbTable* inTable) override; // table must be bound inside this same db port
+
+ NS_IMETHOD GetChildTable( // access table of specific attribute
+ nsIMdbEnv* ev, // context
+ nsIMdbTable** acqTable) override; // acquire child table (or nil if no child)
+ // } ----- end children methods -----
+
+// } ===== end nsIMdbCell methods =====
+private: // copying is not allowed
+ virtual ~morkCellObject(); // assert that CloseCellObject() executed earlier
+ morkCellObject(const morkCellObject& other);
+ morkCellObject& operator=(const morkCellObject& other);
+
+public: // dynamic type identification
+ mork_bool IsCellObject() const
+ { return IsNode() && mNode_Derived == morkDerived_kCellObject; }
+// } ===== end morkNode methods =====
+
+public: // other cell node methods
+
+ morkEnv* CanUseCell(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr, morkCell** outCell);
+
+ mork_bool ResyncWithRow(morkEnv* ev); // return ev->Good()
+ morkAtom* GetCellAtom(morkEnv* ev) const;
+
+ static void MissingRowColumnError(morkEnv* ev);
+ static void NilRowError(morkEnv* ev);
+ static void NilCellError(morkEnv* ev);
+ static void NilRowObjectError(morkEnv* ev);
+ static void WrongRowObjectRowError(morkEnv* ev);
+ static void NonCellObjectTypeError(morkEnv* ev);
+
+ nsIMdbCell* AcquireCellHandle(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakCellObject(morkCellObject* me,
+ morkEnv* ev, morkCellObject** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongCellObject(morkCellObject* me,
+ morkEnv* ev, morkCellObject** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCELLOBJECT_ */
diff --git a/db/mork/src/morkCh.cpp b/db/mork/src/morkCh.cpp
new file mode 100644
index 000000000..07162a315
--- /dev/null
+++ b/db/mork/src/morkCh.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/* this byte char predicate source file derives from public domain Mithril */
+/* (that means much of this has a copyright dedicated to the public domain) */
+
+/*============================================================================*/
+/* morkCh_Type */
+
+const mork_flags morkCh_Type[] = /* derives from public domain Mithril table */
+{
+ 0, /* 0x0 */
+ 0, /* 0x1 */
+ 0, /* 0x2 */
+ 0, /* 0x3 */
+ 0, /* 0x4 */
+ 0, /* 0x5 */
+ 0, /* 0x6 */
+ 0, /* 0x7 */
+ morkCh_kW, /* 0x8 backspace */
+ morkCh_kW, /* 0x9 tab */
+ morkCh_kW, /* 0xA linefeed */
+ 0, /* 0xB */
+ morkCh_kW, /* 0xC page */
+ morkCh_kW, /* 0xD return */
+ 0, /* 0xE */
+ 0, /* 0xF */
+ 0, /* 0x10 */
+ 0, /* 0x11 */
+ 0, /* 0x12 */
+ 0, /* 0x13 */
+ 0, /* 0x14 */
+ 0, /* 0x15 */
+ 0, /* 0x16 */
+ 0, /* 0x17 */
+ 0, /* 0x18 */
+ 0, /* 0x19 */
+ 0, /* 0x1A */
+ 0, /* 0x1B */
+ 0, /* 0x1C */
+ 0, /* 0x1D */
+ 0, /* 0x1E */
+ 0, /* 0x1F */
+
+ morkCh_kV|morkCh_kW, /* 0x20 space */
+ morkCh_kV|morkCh_kM, /* 0x21 ! */
+ morkCh_kV, /* 0x22 " */
+ morkCh_kV, /* 0x23 # */
+ 0, /* 0x24 $ cannot be kV because needs escape */
+ morkCh_kV, /* 0x25 % */
+ morkCh_kV, /* 0x26 & */
+ morkCh_kV, /* 0x27 ' */
+ morkCh_kV, /* 0x28 ( */
+ 0, /* 0x29 ) cannot be kV because needs escape */
+ morkCh_kV, /* 0x2A * */
+ morkCh_kV|morkCh_kM, /* 0x2B + */
+ morkCh_kV, /* 0x2C , */
+ morkCh_kV|morkCh_kM, /* 0x2D - */
+ morkCh_kV, /* 0x2E . */
+ morkCh_kV, /* 0x2F / */
+
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x30 0 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x31 1 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x32 2 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x33 3 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x34 4 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x35 5 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x36 6 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x37 7 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x38 8 */
+ morkCh_kV|morkCh_kD|morkCh_kX, /* 0x39 9 */
+ morkCh_kV|morkCh_kN|morkCh_kM, /* 0x3A : */
+ morkCh_kV, /* 0x3B ; */
+ morkCh_kV, /* 0x3C < */
+ morkCh_kV, /* 0x3D = */
+ morkCh_kV, /* 0x3E > */
+ morkCh_kV|morkCh_kM, /* 0x3F ? */
+
+ morkCh_kV, /* 0x40 @ */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x41 A */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x42 B */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x43 C */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x44 D */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x45 E */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU|morkCh_kX, /* 0x46 F */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x47 G */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x48 H */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x49 I */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4A J */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4B K */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4C L */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4D M */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4E N */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x4F O */
+
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x50 P */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x51 Q */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x52 R */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x53 S */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x54 T */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x55 U */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x56 V */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x57 W */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x58 X */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x59 Y */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kU, /* 0x5A Z */
+ morkCh_kV, /* 0x5B [ */
+ 0, /* 0x5C \ cannot be kV because needs escape */
+ morkCh_kV, /* 0x5D ] */
+ morkCh_kV, /* 0x5E ^ */
+ morkCh_kV|morkCh_kN|morkCh_kM, /* 0x5F _ */
+
+ morkCh_kV, /* 0x60 ` */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x61 a */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x62 b */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x63 c */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x64 d */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x65 e */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL|morkCh_kX, /* 0x66 f */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x67 g */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x68 h */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x69 i */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6A j */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6B k */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6C l */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6D m */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6E n */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x6F o */
+
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x70 p */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x71 q */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x72 r */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x73 s */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x74 t */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x75 u */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x76 v */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x77 w */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x78 x */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x79 y */
+ morkCh_kV|morkCh_kN|morkCh_kM|morkCh_kL, /* 0x7A z */
+ morkCh_kV, /* 0x7B { */
+ morkCh_kV, /* 0x7C | */
+ morkCh_kV, /* 0x7D } */
+ morkCh_kV, /* 0x7E ~ */
+ morkCh_kW, /* 0x7F rubout */
+
+/* $"80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* $"F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF" */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkCh.h b/db/mork/src/morkCh.h
new file mode 100644
index 000000000..1d77ec64e
--- /dev/null
+++ b/db/mork/src/morkCh.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKCH_
+#define _MORKCH_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+/* this byte char predicate header file derives from public domain Mithril */
+/* (that means much of this has a copyright dedicated to the public domain) */
+
+/* Use all 8 pred bits; lose some pred bits only if we need to reuse them. */
+
+/* ch pred bits: W:white D:digit V:value U:upper L:lower N:name M:more */
+#define morkCh_kW (1 << 0)
+#define morkCh_kD (1 << 1)
+#define morkCh_kV (1 << 2)
+#define morkCh_kU (1 << 3)
+#define morkCh_kL (1 << 4)
+#define morkCh_kX (1 << 5)
+#define morkCh_kN (1 << 6)
+#define morkCh_kM (1 << 7)
+
+extern const mork_flags morkCh_Type[]; /* 256 byte predicate bits ch map */
+
+/* is a numeric decimal digit: (note memory access might be slower) */
+/* define morkCh_IsDigit(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kD ) */
+#define morkCh_IsDigit(c) ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '9' )
+
+/* is a numeric octal digit: */
+#define morkCh_IsOctal(c) ( ((mork_ch) c) >= '0' && ((mork_ch) c) <= '7' )
+
+/* is a numeric hexadecimal digit: */
+#define morkCh_IsHex(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kX )
+
+/* is value (can be printed in Mork value without needing hex or escape): */
+#define morkCh_IsValue(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kV )
+
+/* is white space : */
+#define morkCh_IsWhite(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kW )
+
+/* is name (can start a Mork name): */
+#define morkCh_IsName(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kN )
+
+/* is name (can continue a Mork name): */
+#define morkCh_IsMore(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kM )
+
+/* is alphabetic upper or lower case */
+#define morkCh_IsAlpha(c) \
+ ( morkCh_Type[ (mork_ch)(c) ] & (morkCh_kL|morkCh_kU) )
+
+/* is alphanumeric, including lower case, upper case, and digits */
+#define morkCh_IsAlphaNum(c) \
+ (morkCh_Type[ (mork_ch)(c) ]&(morkCh_kL|morkCh_kU|morkCh_kD))
+
+/* ````` repeated testing of predicate bits in single flag byte ````` */
+
+#define morkCh_GetFlags(c) ( morkCh_Type[ (mork_ch)(c) ] )
+
+#define morkFlags_IsDigit(f) ( (f) & morkCh_kD )
+#define morkFlags_IsHex(f) ( (f) & morkCh_kX )
+#define morkFlags_IsValue(f) ( (f) & morkCh_kV )
+#define morkFlags_IsWhite(f) ( (f) & morkCh_kW )
+#define morkFlags_IsName(f) ( (f) & morkCh_kN )
+#define morkFlags_IsMore(f) ( (f) & morkCh_kM )
+#define morkFlags_IsAlpha(f) ( (f) & (morkCh_kL|morkCh_kU) )
+#define morkFlags_IsAlphaNum(f) ( (f) & (morkCh_kL|morkCh_kU|morkCh_kD) )
+
+#define morkFlags_IsUpper(f) ( (f) & morkCh_kU )
+#define morkFlags_IsLower(f) ( (f) & morkCh_kL )
+
+/* ````` character case (e.g. for case insensitive operations) ````` */
+
+
+#define morkCh_IsAscii(c) ( ((mork_u1) c) <= 0x7F )
+#define morkCh_IsSevenBitChar(c) ( ((mork_u1) c) <= 0x7F )
+
+/* ````` character case (e.g. for case insensitive operations) ````` */
+
+#define morkCh_ToLower(c) ((c)-'A'+'a')
+#define morkCh_ToUpper(c) ((c)-'a'+'A')
+
+/* extern int morkCh_IsUpper (int c); */
+#define morkCh_IsUpper(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kU )
+
+/* extern int morkCh_IsLower (int c); */
+#define morkCh_IsLower(c) ( morkCh_Type[ (mork_ch)(c) ] & morkCh_kL )
+
+#endif
+/* _MORKCH_ */
diff --git a/db/mork/src/morkConfig.cpp b/db/mork/src/morkConfig.cpp
new file mode 100644
index 000000000..b58cd03c8
--- /dev/null
+++ b/db/mork/src/morkConfig.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+void mork_assertion_signal(const char* inMessage)
+{
+#if defined(MORK_WIN) || defined(MORK_MAC)
+ // asm { int 3 }
+ NS_ERROR(inMessage);
+#endif /*MORK_WIN*/
+}
+
+#ifdef MORK_PROVIDE_STDLIB
+
+MORK_LIB_IMPL(mork_i4)
+mork_memcmp(const void* inOne, const void* inTwo, mork_size inSize)
+{
+ const mork_u1* t = (const mork_u1*) inTwo;
+ const mork_u1* s = (const mork_u1*) inOne;
+ const mork_u1* end = s + inSize;
+ mork_i4 delta;
+
+ while ( s < end )
+ {
+ delta = ((mork_i4) *s) - ((mork_i4) *t);
+ if ( delta )
+ return delta;
+ else
+ {
+ ++t;
+ ++s;
+ }
+ }
+ return 0;
+}
+
+MORK_LIB_IMPL(void)
+mork_memcpy(void* outDst, const void* inSrc, mork_size inSize)
+{
+ mork_u1* d = (mork_u1*) outDst;
+ mork_u1* end = d + inSize;
+ const mork_u1* s = ((const mork_u1*) inSrc);
+
+ while ( inSize >= 8 )
+ {
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+
+ inSize -= 8;
+ }
+
+ while ( d < end )
+ *d++ = *s++;
+}
+
+MORK_LIB_IMPL(void)
+mork_memmove(void* outDst, const void* inSrc, mork_size inSize)
+{
+ mork_u1* d = (mork_u1*) outDst;
+ const mork_u1* s = (const mork_u1*) inSrc;
+ if ( d != s && inSize ) // copy is necessary?
+ {
+ const mork_u1* srcEnd = s + inSize; // one past last source byte
+
+ if ( d > s && d < srcEnd ) // overlap? need to copy backwards?
+ {
+ s = srcEnd; // start one past last source byte
+ d += inSize; // start one past last dest byte
+ mork_u1* dstBegin = d; // last byte to write is first in dest range
+ while ( d - dstBegin >= 8 )
+ {
+ *--d = *--s;
+ *--d = *--s;
+ *--d = *--s;
+ *--d = *--s;
+
+ *--d = *--s;
+ *--d = *--s;
+ *--d = *--s;
+ *--d = *--s;
+ }
+ while ( d > dstBegin )
+ *--d = *--s;
+ }
+ else // can copy forwards without any overlap
+ {
+ mork_u1* dstEnd = d + inSize;
+ while ( dstEnd - d >= 8 )
+ {
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ }
+ while ( d < dstEnd )
+ *d++ = *s++;
+ }
+ }
+}
+
+MORK_LIB_IMPL(void)
+mork_memset(void* outDst, int inByte, mork_size inSize)
+{
+ mork_u1* d = (mork_u1*) outDst;
+ mork_u1* end = d + inSize;
+ while ( d < end )
+ *d++ = (mork_u1) inByte;
+}
+
+MORK_LIB_IMPL(void)
+mork_strcpy(void* outDst, const void* inSrc)
+{
+ // back up one first to support preincrement
+ mork_u1* d = ((mork_u1*) outDst) - 1;
+ const mork_u1* s = ((const mork_u1*) inSrc) - 1;
+ while ( ( *++d = *++s ) != 0 )
+ /* empty */;
+}
+
+MORK_LIB_IMPL(mork_i4)
+mork_strcmp(const void* inOne, const void* inTwo)
+{
+ const mork_u1* t = (const mork_u1*) inTwo;
+ const mork_u1* s = ((const mork_u1*) inOne);
+ mork_i4 a;
+ mork_i4 b;
+ mork_i4 delta;
+
+ do
+ {
+ a = (mork_i4) *s++;
+ b = (mork_i4) *t++;
+ delta = a - b;
+ }
+ while ( !delta && a && b );
+
+ return delta;
+}
+
+MORK_LIB_IMPL(mork_i4)
+mork_strncmp(const void* inOne, const void* inTwo, mork_size inSize)
+{
+ const mork_u1* t = (const mork_u1*) inTwo;
+ const mork_u1* s = (const mork_u1*) inOne;
+ const mork_u1* end = s + inSize;
+ mork_i4 delta;
+ mork_i4 a;
+ mork_i4 b;
+
+ while ( s < end )
+ {
+ a = (mork_i4) *s++;
+ b = (mork_i4) *t++;
+ delta = a - b;
+ if ( delta || !a || !b )
+ return delta;
+ }
+ return 0;
+}
+
+MORK_LIB_IMPL(mork_size)
+mork_strlen(const void* inString)
+{
+ // back up one first to support preincrement
+ const mork_u1* s = ((const mork_u1*) inString) - 1;
+ while ( *++s ) // preincrement is cheapest
+ /* empty */;
+
+ return s - ((const mork_u1*) inString); // distance from original address
+}
+
+#endif /*MORK_PROVIDE_STDLIB*/
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkConfig.h b/db/mork/src/morkConfig.h
new file mode 100644
index 000000000..370cf1713
--- /dev/null
+++ b/db/mork/src/morkConfig.h
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKCONFIG_
+#define _MORKCONFIG_ 1
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { %%%%% begin debug mode options in Mork %%%%%
+#define MORK_DEBUG 1
+// } %%%%% end debug mode options in Mork %%%%%
+
+#ifdef MORK_DEBUG
+#define MORK_MAX_CODE_COMPILE 1
+#endif
+
+// { %%%%% begin platform defs peculiar to Mork %%%%%
+
+#ifdef XP_MACOSX
+#define MORK_MAC 1
+#endif
+
+#ifdef XP_WIN
+#define MORK_WIN 1
+#endif
+
+#ifdef XP_UNIX
+#define MORK_UNIX 1
+#endif
+
+// } %%%%% end platform defs peculiar to Mork %%%%%
+
+#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC)
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for SEEK_SET, SEEK_END */
+#endif
+
+#include "nsDebug.h"
+
+#define MORK_ISPRINT(c) isprint(c)
+
+#define MORK_FILETELL(file) ftell(file)
+#define MORK_FILESEEK(file, where, how) fseek(file, where, how)
+#define MORK_FILEREAD(outbuf, insize, file) fread(outbuf, 1, insize, file)
+#if defined(MORK_WIN)
+void mork_fileflush(FILE * file);
+#define MORK_FILEFLUSH(file) mork_fileflush(file)
+#else
+#define MORK_FILEFLUSH(file) fflush(file)
+#endif /*MORK_WIN*/
+
+#define MORK_FILEOPEN(file, how) fopen(file, how)
+#define MORK_FILECLOSE(file) fclose(file)
+#endif /*MORK_WIN*/
+
+/* ===== separating switchable features ===== */
+
+#define MORK_ENABLE_ZONE_ARENAS 1 /* using morkZone for pooling */
+
+//#define MORK_ENABLE_PROBE_MAPS 1 /* use smaller hash tables */
+
+#define MORK_BEAD_OVER_NODE_MAPS 1 /* use bead not node maps */
+
+/* ===== pooling ===== */
+
+#if defined(HAVE_64BIT_BUILD)
+#define MORK_CONFIG_ALIGN_8 1 /* must have 8 byte alignment */
+#else
+#define MORK_CONFIG_PTR_SIZE_4 1 /* sizeof(void*) == 4 */
+#endif
+
+// #define MORK_DEBUG_HEAP_STATS 1 /* analyze per-block heap usage */
+
+/* ===== ===== ===== ===== line characters ===== ===== ===== ===== */
+#define mork_kCR 0x0D
+#define mork_kLF 0x0A
+#define mork_kVTAB '\013'
+#define mork_kFF '\014'
+#define mork_kTAB '\011'
+#define mork_kCRLF "\015\012" /* A CR LF equivalent string */
+
+#if defined(MORK_MAC)
+# define mork_kNewline "\015"
+# define mork_kNewlineSize 1
+#else
+# if defined(MORK_WIN)
+# define mork_kNewline "\015\012"
+# define mork_kNewlineSize 2
+# else
+# if defined(MORK_UNIX)
+# define mork_kNewline "\012"
+# define mork_kNewlineSize 1
+# endif /* MORK_UNIX */
+# endif /* MORK_WIN */
+#endif /* MORK_MAC */
+
+// { %%%%% begin assertion macro %%%%%
+extern void mork_assertion_signal(const char* inMessage);
+#define MORK_ASSERTION_SIGNAL(Y) mork_assertion_signal(Y)
+#define MORK_ASSERT(X) if (!(X)) MORK_ASSERTION_SIGNAL(#X)
+// } %%%%% end assertion macro %%%%%
+
+#define MORK_LIB(return) return /*API return declaration*/
+#define MORK_LIB_IMPL(return) return /*implementation return declaration*/
+
+// { %%%%% begin standard c utility methods %%%%%
+
+#if defined(MORK_WIN) || defined(MORK_UNIX) || defined(MORK_MAC)
+#define MORK_USE_C_STDLIB 1
+#endif /*MORK_WIN*/
+
+#ifdef MORK_USE_C_STDLIB
+#define MORK_MEMCMP(src1,src2,size) memcmp(src1,src2,size)
+#define MORK_MEMCPY(dest,src,size) memcpy(dest,src,size)
+#define MORK_MEMMOVE(dest,src,size) memmove(dest,src,size)
+#define MORK_MEMSET(dest,byte,size) memset(dest,byte,size)
+#define MORK_STRCPY(dest,src) strcpy(dest,src)
+#define MORK_STRCMP(one,two) strcmp(one,two)
+#define MORK_STRNCMP(one,two,length) strncmp(one,two,length)
+#define MORK_STRLEN(string) strlen(string)
+#endif /*MORK_USE_C_STDLIB*/
+
+#ifdef MORK_PROVIDE_STDLIB
+MORK_LIB(mork_i4) mork_memcmp(const void* a, const void* b, mork_size inSize);
+MORK_LIB(void) mork_memcpy(void* dst, const void* src, mork_size inSize);
+MORK_LIB(void) mork_memmove(void* dst, const void* src, mork_size inSize);
+MORK_LIB(void) mork_memset(void* dst, int inByte, mork_size inSize);
+MORK_LIB(void) mork_strcpy(void* dst, const void* src);
+MORK_LIB(mork_i4) mork_strcmp(const void* a, const void* b);
+MORK_LIB(mork_i4) mork_strncmp(const void* a, const void* b, mork_size inSize);
+MORK_LIB(mork_size) mork_strlen(const void* inString);
+
+#define MORK_MEMCMP(src1,src2,size) mork_memcmp(src1,src2,size)
+#define MORK_MEMCPY(dest,src,size) mork_memcpy(dest,src,size)
+#define MORK_MEMMOVE(dest,src,size) mork_memmove(dest,src,size)
+#define MORK_MEMSET(dest,byte,size) mork_memset(dest,byte,size)
+#define MORK_STRCPY(dest,src) mork_strcpy(dest,src)
+#define MORK_STRCMP(one,two) mork_strcmp(one,two)
+#define MORK_STRNCMP(one,two,length) mork_strncmp(one,two,length)
+#define MORK_STRLEN(string) mork_strlen(string)
+#endif /*MORK_PROVIDE_STDLIB*/
+
+// } %%%%% end standard c utility methods %%%%%
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCONFIG_ */
+
diff --git a/db/mork/src/morkCursor.cpp b/db/mork/src/morkCursor.cpp
new file mode 100644
index 000000000..f6aa59297
--- /dev/null
+++ b/db/mork/src/morkCursor.cpp
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkCursor::CloseMorkNode(morkEnv* ev) // CloseCursor() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseCursor(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkCursor::~morkCursor() // assert CloseCursor() executed earlier
+{
+}
+
+/*public non-poly*/
+morkCursor::morkCursor(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mCursor_Seed( 0 )
+, mCursor_Pos( -1 )
+, mCursor_DoFailOnSeedOutOfSync( morkBool_kFalse )
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kCursor;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkCursor, morkObject, nsIMdbCursor)
+
+/*public non-poly*/ void
+morkCursor::CloseCursor(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mCursor_Seed = 0;
+ mCursor_Pos = -1;
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkCursor::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+ mdb_count* outCount)
+{
+ *outCount = WeakRefsOnly();
+ return NS_OK;
+}
+NS_IMETHODIMP
+morkCursor::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+ mdb_count* outCount)
+{
+ *outCount = StrongRefsOnly();
+ return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkCursor::AddWeakRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::AddWeakRef((morkEnv *) mev));
+}
+
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkCursor::AddStrongRef(morkEnv* mev)
+{
+ return morkNode::AddStrongRef(mev);
+}
+#endif
+
+NS_IMETHODIMP_(mork_uses)
+morkCursor::AddStrongRef(nsIMdbEnv* mev)
+{
+ return morkNode::AddStrongRef((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkCursor::CutWeakRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::CutWeakRef((morkEnv *) mev));
+}
+
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkCursor::CutStrongRef(morkEnv* mev)
+{
+ return morkNode::CutStrongRef(mev);
+}
+#endif
+
+NS_IMETHODIMP
+morkCursor::CutStrongRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_uses to nsresult
+ return static_cast<nsresult>(morkNode::CutStrongRef((morkEnv *) mev));
+}
+
+NS_IMETHODIMP
+morkCursor::CloseMdbObject(nsIMdbEnv* mev)
+{
+ return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkCursor::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+ *outOpen = IsOpenNode();
+ return NS_OK;
+}
+NS_IMETHODIMP
+morkCursor::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+ *outIsReadonly = IsFrozen();
+ return NS_OK;
+}
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+NS_IMETHODIMP
+morkCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkCursor.h b/db/mork/src/morkCursor.h
new file mode 100644
index 000000000..5b3518f95
--- /dev/null
+++ b/db/mork/src/morkCursor.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKCURSOR_
+#define _MORKCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kCursor /*i*/ 0x4375 /* ascii 'Cu' */
+
+class morkCursor : public morkObject, public nsIMdbCursor{ // collection iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override;
+ // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+ // } ----- end attribute methods -----
+
+ // { ----- begin ref counting for well-behaved cyclic graphs -----
+ NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+ mdb_count* outCount) override;
+ NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+ mdb_count* outCount) override;
+
+ NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero
+ NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override;
+ // } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbCursor methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly
+
+ NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable
+ NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override;
+
+ NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override;
+ NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) override;
+ // } ----- end attribute methods -----
+
+// } ===== end nsIMdbCursor methods =====
+
+ // } ----- end attribute methods -----
+
+ mork_seed mCursor_Seed;
+ mork_pos mCursor_Pos;
+ mork_bool mCursor_DoFailOnSeedOutOfSync;
+ mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseCursor() only if open
+
+public: // morkCursor construction & destruction
+ morkCursor(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+ void CloseCursor(morkEnv* ev); // called by CloseMorkNode();
+
+protected:
+ virtual ~morkCursor(); // assert that CloseCursor() executed earlier
+
+private: // copying is not allowed
+ morkCursor(const morkCursor& other);
+ morkCursor& operator=(const morkCursor& other);
+
+public: // dynamic type identification
+ mork_bool IsCursor() const
+ { return IsNode() && mNode_Derived == morkDerived_kCursor; }
+// } ===== end morkNode methods =====
+
+public: // other cursor methods
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakCursor(morkCursor* me,
+ morkEnv* ev, morkCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongCursor(morkCursor* me,
+ morkEnv* ev, morkCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKCURSOR_ */
diff --git a/db/mork/src/morkDeque.cpp b/db/mork/src/morkDeque.cpp
new file mode 100644
index 000000000..3a380b5d8
--- /dev/null
+++ b/db/mork/src/morkDeque.cpp
@@ -0,0 +1,288 @@
+/*************************************************************************
+This software is part of a public domain IronDoc source code distribution,
+and is provided on an "AS IS" basis, with all risks borne by the consumers
+or users of the IronDoc software. There are no warranties, guarantees, or
+promises about quality of any kind; and no remedies for failure exist.
+
+Permission is hereby granted to use this IronDoc software for any purpose
+at all, without need for written agreements, without royalty or license
+fees, and without fees or obligations of any other kind. Anyone can use,
+copy, change and distribute this software for any purpose, and nothing is
+required, implicitly or otherwise, in exchange for this usage.
+
+You cannot apply your own copyright to this software, but otherwise you
+are encouraged to enjoy the use of this software in any way you see fit.
+However, it would be rude to remove names of developers from the code.
+(IronDoc is also known by the short name "Fe" and a longer name "Ferrum",
+which are used interchangeably with the name IronDoc in the sources.)
+*************************************************************************/
+/*
+ * File: morkDeque.cpp
+ * Contains: Ferrum deque (double ended queue (linked list))
+ *
+ * Copied directly from public domain IronDoc, with minor naming tweaks:
+ * Designed and written by David McCusker, but all this code is public domain.
+ * There are no warranties, no guarantees, no promises, and no remedies.
+ */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+/*=============================================================================
+ * morkNext: linked list node for very simple, singly-linked list
+ */
+
+morkNext::morkNext() : mNext_Link( 0 )
+{
+}
+
+/*static*/ void*
+morkNext::MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+ void* next = 0;
+ ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &next);
+ if ( !next )
+ ev->OutOfMemoryError();
+
+ return next;
+}
+
+/*static*/
+void morkNext::ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+ if ( ioHeap )
+ {
+ ioHeap->Free(ev->AsMdbEnv(), this);
+ }
+ else
+ ev->NilPointerError();
+}
+
+/*=============================================================================
+ * morkList: simple, singly-linked list
+ */
+
+morkList::morkList() : mList_Head( 0 ), mList_Tail( 0 )
+{
+}
+
+void morkList::CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap)
+// make empty list, zapping every member by calling ZapOldNext()
+{
+ if ( ioHeap )
+ {
+ morkNext* next = 0;
+ while ( (next = this->PopHead()) != 0 )
+ next->ZapOldNext(ev, ioHeap);
+
+ mList_Head = 0;
+ mList_Tail = 0;
+ }
+ else
+ ev->NilPointerError();
+}
+
+void morkList::CutAllListMembers()
+// just make list empty, dropping members without zapping
+{
+ while ( this->PopHead() )
+ /* empty */;
+
+ mList_Head = 0;
+ mList_Tail = 0;
+}
+
+morkNext* morkList::PopHead() // cut head of list
+{
+ morkNext* outHead = mList_Head;
+ if ( outHead ) // anything to cut from list?
+ {
+ morkNext* next = outHead->mNext_Link;
+ mList_Head = next;
+ if ( !next ) // cut the last member, so tail no longer exists?
+ mList_Tail = 0;
+
+ outHead->mNext_Link = 0; // nil outgoing node link; unnecessary, but tidy
+ }
+ return outHead;
+}
+
+
+void morkList::PushHead(morkNext* ioLink) // add to head of list
+{
+ morkNext* head = mList_Head; // old head of list
+ morkNext* tail = mList_Tail; // old tail of list
+
+ MORK_ASSERT( (head && tail) || (!head && !tail));
+
+ ioLink->mNext_Link = head; // make old head follow the new link
+ if ( !head ) // list was previously empty?
+ mList_Tail = ioLink; // head is also tail for first member added
+
+ mList_Head = ioLink; // head of list is the new link
+}
+
+void morkList::PushTail(morkNext* ioLink) // add to tail of list
+{
+ morkNext* head = mList_Head; // old head of list
+ morkNext* tail = mList_Tail; // old tail of list
+
+ MORK_ASSERT( (head && tail) || (!head && !tail));
+
+ ioLink->mNext_Link = 0;
+ if ( tail )
+ {
+ tail->mNext_Link = ioLink;
+ mList_Tail = ioLink;
+ }
+ else // list was previously empty?
+ mList_Head = mList_Tail = ioLink; // tail is also head for first member added
+}
+
+/*=============================================================================
+ * morkLink: linked list node embedded in objs to allow insertion in morkDeques
+ */
+
+morkLink::morkLink() : mLink_Next( 0 ), mLink_Prev( 0 )
+{
+}
+
+/*static*/ void*
+morkLink::MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+ void* alink = 0;
+ ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void**) &alink);
+ if ( !alink )
+ ev->OutOfMemoryError();
+
+ return alink;
+}
+
+/*static*/
+void morkLink::ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+ if ( ioHeap )
+ {
+ ioHeap->Free(ev->AsMdbEnv(), this);
+ }
+ else
+ ev->NilPointerError();
+}
+
+/*=============================================================================
+ * morkDeque: doubly linked list modeled after VAX queue instructions
+ */
+
+morkDeque::morkDeque()
+{
+ mDeque_Head.SelfRefer();
+}
+
+
+/*| RemoveFirst:
+|*/
+morkLink*
+morkDeque::RemoveFirst() /*i*/
+{
+ morkLink* alink = mDeque_Head.mLink_Next;
+ if ( alink != &mDeque_Head )
+ {
+ (mDeque_Head.mLink_Next = alink->mLink_Next)->mLink_Prev =
+ &mDeque_Head;
+ return alink;
+ }
+ return (morkLink*) 0;
+}
+
+/*| RemoveLast:
+|*/
+morkLink*
+morkDeque::RemoveLast() /*i*/
+{
+ morkLink* alink = mDeque_Head.mLink_Prev;
+ if ( alink != &mDeque_Head )
+ {
+ (mDeque_Head.mLink_Prev = alink->mLink_Prev)->mLink_Next =
+ &mDeque_Head;
+ return alink;
+ }
+ return (morkLink*) 0;
+}
+
+/*| At:
+|*/
+morkLink*
+morkDeque::At(mork_pos index) const /*i*/
+ /* indexes are one based (and not zero based) */
+{
+ mork_num count = 0;
+ morkLink* alink;
+ for ( alink = this->First(); alink; alink = this->After(alink) )
+ {
+ if ( ++count == (mork_num) index )
+ break;
+ }
+ return alink;
+}
+
+/*| IndexOf:
+|*/
+mork_pos
+morkDeque::IndexOf(const morkLink* member) const /*i*/
+ /* indexes are one based (and not zero based) */
+ /* zero means member is not in deque */
+{
+ mork_num count = 0;
+ const morkLink* alink;
+ for ( alink = this->First(); alink; alink = this->After(alink) )
+ {
+ ++count;
+ if ( member == alink )
+ return (mork_pos) count;
+ }
+ return 0;
+}
+
+/*| Length:
+|*/
+mork_num
+morkDeque::Length() const /*i*/
+{
+ mork_num count = 0;
+ morkLink* alink;
+ for ( alink = this->First(); alink; alink = this->After(alink) )
+ ++count;
+ return count;
+}
+
+/*| LengthCompare:
+|*/
+int
+morkDeque::LengthCompare(mork_num c) const /*i*/
+{
+ mork_num count = 0;
+ const morkLink* alink;
+ for ( alink = this->First(); alink; alink = this->After(alink) )
+ {
+ if ( ++count > c )
+ return 1;
+ }
+ return ( count == c )? 0 : -1;
+}
diff --git a/db/mork/src/morkDeque.h b/db/mork/src/morkDeque.h
new file mode 100644
index 000000000..ad38cdbe2
--- /dev/null
+++ b/db/mork/src/morkDeque.h
@@ -0,0 +1,239 @@
+/*************************************************************************
+This software is part of a public domain IronDoc source code distribution,
+and is provided on an "AS IS" basis, with all risks borne by the consumers
+or users of the IronDoc software. There are no warranties, guarantees, or
+promises about quality of any kind; and no remedies for failure exist.
+
+Permission is hereby granted to use this IronDoc software for any purpose
+at all, without need for written agreements, without royalty or license
+fees, and without fees or obligations of any other kind. Anyone can use,
+copy, change and distribute this software for any purpose, and nothing is
+required, implicitly or otherwise, in exchange for this usage.
+
+You cannot apply your own copyright to this software, but otherwise you
+are encouraged to enjoy the use of this software in any way you see fit.
+However, it would be rude to remove names of developers from the code.
+(IronDoc is also known by the short name "Fe" and a longer name "Ferrum",
+which are used interchangeably with the name IronDoc in the sources.)
+*************************************************************************/
+/*
+ * File: morkDeque.h
+ * Contains: Ferrum deque (double ended queue (linked list))
+ *
+ * Copied directly from public domain IronDoc, with minor naming tweaks:
+ * Designed and written by David McCusker, but all this code is public domain.
+ * There are no warranties, no guarantees, no promises, and no remedies.
+ */
+
+#ifndef _MORKDEQUE_
+#define _MORKDEQUE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+/*=============================================================================
+ * morkNext: linked list node for very simple, singly-linked list
+ */
+
+class morkNext /*d*/ {
+public:
+ morkNext* mNext_Link;
+
+public:
+ explicit morkNext(int inZero) : mNext_Link( 0 ) { }
+
+ explicit morkNext(morkNext* ioLink) : mNext_Link( ioLink ) { }
+
+ morkNext(); // mNext_Link( 0 ), { }
+
+public:
+ morkNext* GetNextLink() const { return mNext_Link; }
+
+public: // link memory management methods
+ static void* MakeNewNext(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+ void ZapOldNext(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+public: // link memory management operators
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkNext::MakeNewNext(inSize, ioHeap, ev); }
+
+ void operator delete(void* ioAddress) // DO NOT CALL THIS, hope to crash:
+ { ((morkNext*) 0)->ZapOldNext((morkEnv*) 0, (nsIMdbHeap*) 0); } // boom
+};
+
+/*=============================================================================
+ * morkList: simple, singly-linked list
+ */
+
+/*| morkList: a list of singly-linked members (instances of morkNext), where
+**| the number of list members might be so numerous that we must about cost
+**| for two pointer link slots per member (as happens with morkLink).
+**|
+**|| morkList is intended to support lists of changes in morkTable, where we
+**| are worried about the space cost of representing such changes. (Later we
+**| can use an array instead, when we get even more worried, to avoid cost
+**| of link slots at all, per member).
+**|
+**|| Do NOT create cycles in links using this list class, since we do not
+**| deal with them very nicely.
+|*/
+class morkList /*d*/ {
+public:
+ morkNext* mList_Head; // first link in the list
+ morkNext* mList_Tail; // last link in the list
+
+public:
+ morkNext* GetListHead() const { return mList_Head; }
+ morkNext* GetListTail() const { return mList_Tail; }
+
+ mork_bool IsListEmpty() const { return ( mList_Head == 0 ); }
+ mork_bool HasListMembers() const { return ( mList_Head != 0 ); }
+
+public:
+ morkList(); // : mList_Head( 0 ), mList_Tail( 0 ) { }
+
+ void CutAndZapAllListMembers(morkEnv* ev, nsIMdbHeap* ioHeap);
+ // make empty list, zapping every member by calling ZapOldNext()
+
+ void CutAllListMembers();
+ // just make list empty, dropping members without zapping
+
+public:
+ morkNext* PopHead(); // cut head of list
+
+ // Note we don't support PopTail(), so use morkDeque if you need that.
+
+ void PushHead(morkNext* ioLink); // add to head of list
+ void PushTail(morkNext* ioLink); // add to tail of list
+};
+
+/*=============================================================================
+ * morkLink: linked list node embedded in objs to allow insertion in morkDeques
+ */
+
+class morkLink /*d*/ {
+public:
+ morkLink* mLink_Next;
+ morkLink* mLink_Prev;
+
+public:
+ explicit morkLink(int inZero) : mLink_Next( 0 ), mLink_Prev( 0 ) { }
+
+ morkLink(); // mLink_Next( 0 ), mLink_Prev( 0 ) { }
+
+public:
+ morkLink* Next() const { return mLink_Next; }
+ morkLink* Prev() const { return mLink_Prev; }
+
+ void SelfRefer() { mLink_Next = mLink_Prev = this; }
+ void Clear() { mLink_Next = mLink_Prev = 0; }
+
+ void AddBefore(morkLink* old)
+ {
+ ((old)->mLink_Prev->mLink_Next = (this))->mLink_Prev = (old)->mLink_Prev;
+ ((this)->mLink_Next = (old))->mLink_Prev = this;
+ }
+
+ void AddAfter(morkLink* old)
+ {
+ ((old)->mLink_Next->mLink_Prev = (this))->mLink_Next = (old)->mLink_Next;
+ ((this)->mLink_Prev = (old))->mLink_Next = this;
+ }
+
+ void Remove()
+ {
+ (mLink_Prev->mLink_Next = mLink_Next)->mLink_Prev = mLink_Prev;
+ }
+
+public: // link memory management methods
+ static void* MakeNewLink(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+ void ZapOldLink(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+public: // link memory management operators
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkLink::MakeNewLink(inSize, ioHeap, ev); }
+
+};
+
+/*=============================================================================
+ * morkDeque: doubly linked list modeled after VAX queue instructions
+ */
+
+class morkDeque /*d*/ {
+public:
+ morkLink mDeque_Head;
+
+public: // construction
+ morkDeque(); // { mDeque_Head.SelfRefer(); }
+
+public:// methods
+ morkLink* RemoveFirst();
+
+ morkLink* RemoveLast();
+
+ morkLink* At(mork_pos index) const ; /* one-based, not zero-based */
+
+ mork_pos IndexOf(const morkLink* inMember) const;
+ /* one-based index ; zero means member is not in deque */
+
+ mork_num Length() const;
+
+ /* the following method is more efficient for long lists: */
+ int LengthCompare(mork_num inCount) const;
+ /* -1: length < count, 0: length == count, 1: length > count */
+
+public: // inlines
+
+ mork_bool IsEmpty()const
+ { return (mDeque_Head.mLink_Next == (morkLink*) &mDeque_Head); }
+
+ morkLink* After(const morkLink* old) const
+ { return (((old)->mLink_Next != &mDeque_Head)?
+ (old)->mLink_Next : (morkLink*) 0); }
+
+ morkLink* Before(const morkLink* old) const
+ { return (((old)->mLink_Prev != &mDeque_Head)?
+ (old)->mLink_Prev : (morkLink*) 0); }
+
+ morkLink* First() const
+ { return ((mDeque_Head.mLink_Next != &mDeque_Head)?
+ mDeque_Head.mLink_Next : (morkLink*) 0); }
+
+ morkLink* Last() const
+ { return ((mDeque_Head.mLink_Prev != &mDeque_Head)?
+ mDeque_Head.mLink_Prev : (morkLink*) 0); }
+
+/*
+From IronDoc documentation for AddFirst:
++--------+ +--------+ +--------+ +--------+ +--------+
+| h.next |-->| b.next | | h.next |-->| a.next |-->| b.next |
++--------+ +--------+ ==> +--------+ +--------+ +--------+
+| h.prev |<--| b.prev | | h.prev |<--| a.prev |<--| b.prev |
++--------+ +--------+ +--------+ +--------+ +--------+
+*/
+
+ void AddFirst(morkLink* in) /*i*/
+ {
+ ( (mDeque_Head.mLink_Next->mLink_Prev =
+ (in))->mLink_Next = mDeque_Head.mLink_Next,
+ ((in)->mLink_Prev = &mDeque_Head)->mLink_Next = (in) );
+ }
+/*
+From IronDoc documentation for AddLast:
++--------+ +--------+ +--------+ +--------+ +--------+
+| y.next |-->| h.next | | y.next |-->| z.next |-->| h.next |
++--------+ +--------+ ==> +--------+ +--------+ +--------+
+| y.prev |<--| h.prev | | y.prev |<--| z.prev |<--| h.prev |
++--------+ +--------+ +--------+ +--------+ +--------+
+*/
+
+ void AddLast(morkLink* in)
+ {
+ ( (mDeque_Head.mLink_Prev->mLink_Next =
+ (in))->mLink_Prev = mDeque_Head.mLink_Prev,
+ ((in)->mLink_Next = &mDeque_Head)->mLink_Prev = (in) );
+ }
+};
+
+#endif /* _MORKDEQUE_ */
diff --git a/db/mork/src/morkEnv.cpp b/db/mork/src/morkEnv.cpp
new file mode 100644
index 000000000..083942f67
--- /dev/null
+++ b/db/mork/src/morkEnv.cpp
@@ -0,0 +1,615 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkEnv::CloseMorkNode(morkEnv* ev) /*i*/ // CloseEnv() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseEnv(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkEnv::~morkEnv() /*i*/ // assert CloseEnv() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ if (mEnv_Heap)
+ {
+ mork_bool ownsHeap = mEnv_OwnsHeap;
+ nsIMdbHeap*saveHeap = mEnv_Heap;
+
+ if (ownsHeap)
+ {
+#ifdef MORK_DEBUG_HEAP_STATS
+ printf("%d blocks remaining \n", ((orkinHeap *) saveHeap)->HeapBlockCount());
+ mork_u4* array = (mork_u4*) this;
+ array -= 3;
+ // null out heap ptr in mem block so we won't crash trying to use it to
+ // delete the env.
+ *array = nullptr;
+#endif // MORK_DEBUG_HEAP_STATS
+ // whoops, this is our heap - hmm. Can't delete it, or not allocate env's from
+ // an orkinHeap.
+ delete saveHeap;
+ }
+
+ }
+// MORK_ASSERT(mEnv_SelfAsMdbEnv==0);
+ MORK_ASSERT(mEnv_ErrorHook==0);
+}
+
+/* choose morkBool_kTrue or morkBool_kFalse for kBeVerbose: */
+#define morkEnv_kBeVerbose morkBool_kFalse
+
+/*public non-poly*/
+morkEnv::morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap)
+: morkObject(inUsage, ioHeap, morkColor_kNone)
+, mEnv_Factory( ioFactory )
+, mEnv_Heap( ioSlotHeap )
+
+, mEnv_SelfAsMdbEnv( 0 )
+, mEnv_ErrorHook( 0 )
+, mEnv_HandlePool( 0 )
+
+, mEnv_ErrorCount( 0 )
+, mEnv_WarningCount( 0 )
+
+, mEnv_ErrorCode(NS_OK)
+
+, mEnv_DoTrace( morkBool_kFalse )
+, mEnv_AutoClear( morkAble_kDisabled )
+, mEnv_ShouldAbort( morkBool_kFalse )
+, mEnv_BeVerbose( morkEnv_kBeVerbose )
+, mEnv_OwnsHeap ( morkBool_kFalse )
+{
+ MORK_ASSERT(ioSlotHeap && ioFactory );
+ if ( ioSlotHeap )
+ {
+ // mEnv_Heap is NOT refcounted:
+ // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, this, &mEnv_Heap);
+
+ mEnv_HandlePool = new morkPool(morkUsage::kGlobal,
+ (nsIMdbHeap*) 0, ioSlotHeap);
+
+ MORK_ASSERT(mEnv_HandlePool);
+ if ( mEnv_HandlePool && this->Good() )
+ {
+ mNode_Derived = morkDerived_kEnv;
+ mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+ }
+ }
+}
+
+/*public non-poly*/
+morkEnv::morkEnv(morkEnv* ev, /*i*/
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbEnv* inSelfAsMdbEnv,
+ morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mEnv_Factory( ioFactory )
+, mEnv_Heap( ioSlotHeap )
+
+, mEnv_SelfAsMdbEnv( inSelfAsMdbEnv )
+, mEnv_ErrorHook( 0 )
+, mEnv_HandlePool( 0 )
+
+, mEnv_ErrorCount( 0 )
+, mEnv_WarningCount( 0 )
+
+, mEnv_ErrorCode(NS_OK)
+
+, mEnv_DoTrace( morkBool_kFalse )
+, mEnv_AutoClear( morkAble_kDisabled )
+, mEnv_ShouldAbort( morkBool_kFalse )
+, mEnv_BeVerbose( morkEnv_kBeVerbose )
+, mEnv_OwnsHeap ( morkBool_kFalse )
+{
+ // $$$ do we need to refcount the inSelfAsMdbEnv nsIMdbEnv??
+
+ if ( ioFactory && inSelfAsMdbEnv && ioSlotHeap)
+ {
+ // mEnv_Heap is NOT refcounted:
+ // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mEnv_Heap);
+
+ mEnv_HandlePool = new(*ioSlotHeap, ev) morkPool(ev,
+ morkUsage::kHeap, ioSlotHeap, ioSlotHeap);
+
+ MORK_ASSERT(mEnv_HandlePool);
+ if ( mEnv_HandlePool && ev->Good() )
+ {
+ mNode_Derived = morkDerived_kEnv;
+ mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+ }
+ }
+ else
+ ev->NilPointerError();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkEnv, morkObject, nsIMdbEnv)
+/*public non-poly*/ void
+morkEnv::CloseEnv(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ // $$$ release mEnv_SelfAsMdbEnv??
+ // $$$ release mEnv_ErrorHook??
+
+ mEnv_SelfAsMdbEnv = 0;
+ mEnv_ErrorHook = 0;
+
+ morkPool* savePool = mEnv_HandlePool;
+ morkPool::SlotStrongPool((morkPool*) 0, ev, &mEnv_HandlePool);
+ // free the pool
+ if (mEnv_SelfAsMdbEnv)
+ {
+ if (savePool && mEnv_Heap)
+ mEnv_Heap->Free(this->AsMdbEnv(), savePool);
+ }
+ else
+ {
+ if (savePool)
+ {
+ if (savePool->IsOpenNode())
+ savePool->CloseMorkNode(ev);
+ delete savePool;
+ }
+ // how do we free this? might need to get rid of asserts.
+ }
+ // mEnv_Factory is NOT refcounted
+
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+mork_size
+morkEnv::OidAsHex(void* outBuf, const mdbOid& inOid)
+// sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope);
+{
+ mork_u1* p = (mork_u1*) outBuf;
+ mork_size outSize = this->TokenAsHex(p, inOid.mOid_Id);
+ p += outSize;
+ *p++ = ':';
+
+ mork_scope scope = inOid.mOid_Scope;
+ if ( scope < 0x80 && morkCh_IsName((mork_ch) scope) )
+ {
+ *p++ = (mork_u1) scope;
+ *p = 0; // null termination
+ outSize += 2;
+ }
+ else
+ {
+ *p++ = '^';
+ mork_size scopeSize = this->TokenAsHex(p, scope);
+ outSize += scopeSize + 2;
+ }
+ return outSize;
+}
+
+
+mork_u1
+morkEnv::HexToByte(mork_ch inFirstHex, mork_ch inSecondHex)
+{
+ mork_u1 hi = 0; // high four hex bits
+ mork_flags f = morkCh_GetFlags(inFirstHex);
+ if ( morkFlags_IsDigit(f) )
+ hi = (mork_u1) (inFirstHex - (mork_ch) '0');
+ else if ( morkFlags_IsUpper(f) )
+ hi = (mork_u1) ((inFirstHex - (mork_ch) 'A') + 10);
+ else if ( morkFlags_IsLower(f) )
+ hi = (mork_u1) ((inFirstHex - (mork_ch) 'a') + 10);
+
+ mork_u1 lo = 0; // low four hex bits
+ f = morkCh_GetFlags(inSecondHex);
+ if ( morkFlags_IsDigit(f) )
+ lo = (mork_u1) (inSecondHex - (mork_ch) '0');
+ else if ( morkFlags_IsUpper(f) )
+ lo = (mork_u1) ((inSecondHex - (mork_ch) 'A') + 10);
+ else if ( morkFlags_IsLower(f) )
+ lo = (mork_u1) ((inSecondHex - (mork_ch) 'a') + 10);
+
+ return (mork_u1) ((hi << 4) | lo);
+}
+
+mork_size
+morkEnv::TokenAsHex(void* outBuf, mork_token inToken)
+ // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken);
+{
+ static const char morkEnv_kHexDigits[] = "0123456789ABCDEF";
+ char* p = (char*) outBuf;
+ char* end = p + 32; // write no more than 32 digits for safety
+ if ( inToken )
+ {
+ // first write all the hex digits in backwards order:
+ while ( p < end && inToken ) // more digits to write?
+ {
+ *p++ = morkEnv_kHexDigits[ inToken & 0x0F ]; // low four bits
+ inToken >>= 4; // we fervently hope this does not sign extend
+ }
+ *p = 0; // end the string with a null byte
+ char* s = (char*) outBuf; // first byte in string
+ mork_size size = (mork_size) (p - s); // distance from start
+
+ // now reverse the string in place:
+ // note that p starts on the null byte, so we need predecrement:
+ while ( --p > s ) // need to swap another byte in the string?
+ {
+ char c = *p; // temp for swap
+ *p = *s;
+ *s++ = c; // move s forward here, and p backward in the test
+ }
+ return size;
+ }
+ else // special case for zero integer
+ {
+ *p++ = '0'; // write a zero digit
+ *p = 0; // end with a null byte
+ return 1; // one digit in hex representation
+ }
+}
+
+void
+morkEnv::StringToYarn(const char* inString, mdbYarn* outYarn)
+{
+ if ( outYarn )
+ {
+ mdb_fill fill = ( inString )? (mdb_fill) MORK_STRLEN(inString) : 0;
+
+ if ( fill ) // have nonempty content?
+ {
+ mdb_size size = outYarn->mYarn_Size; // max dest size
+ if ( fill > size ) // too much string content?
+ {
+ outYarn->mYarn_More = fill - size; // extra string bytes omitted
+ fill = size; // copy no more bytes than size of yarn buffer
+ }
+ void* dest = outYarn->mYarn_Buf; // where bytes are going
+ if ( !dest ) // nil destination address buffer?
+ fill = 0; // we can't write any content at all
+
+ if ( fill ) // anything to copy?
+ MORK_MEMCPY(dest, inString, fill); // copy fill bytes to yarn
+
+ outYarn->mYarn_Fill = fill; // tell yarn size of copied content
+ }
+ else // no content to put into the yarn
+ {
+ outYarn->mYarn_Fill = 0; // tell yarn that string has no bytes
+ }
+ outYarn->mYarn_Form = 0; // always update the form slot
+ }
+ else
+ this->NilPointerError();
+}
+
+char*
+morkEnv::CopyString(nsIMdbHeap* ioHeap, const char* inString)
+{
+ char* outString = 0;
+ if ( ioHeap && inString )
+ {
+ mork_size size = MORK_STRLEN(inString) + 1;
+ ioHeap->Alloc(this->AsMdbEnv(), size, (void**) &outString);
+ if ( outString )
+ MORK_STRCPY(outString, inString);
+ }
+ else
+ this->NilPointerError();
+ return outString;
+}
+
+void
+morkEnv::FreeString(nsIMdbHeap* ioHeap, char* ioString)
+{
+ if ( ioHeap )
+ {
+ if ( ioString )
+ ioHeap->Free(this->AsMdbEnv(), ioString);
+ }
+ else
+ this->NilPointerError();
+}
+
+void
+morkEnv::NewError(const char* inString)
+{
+ MORK_ASSERT(morkBool_kFalse); // get developer's attention
+
+ ++mEnv_ErrorCount;
+ mEnv_ErrorCode = NS_ERROR_FAILURE;
+
+ if ( mEnv_ErrorHook )
+ mEnv_ErrorHook->OnErrorString(this->AsMdbEnv(), inString);
+}
+
+void
+morkEnv::NewWarning(const char* inString)
+{
+ MORK_ASSERT(morkBool_kFalse); // get developer's attention
+
+ ++mEnv_WarningCount;
+ if ( mEnv_ErrorHook )
+ mEnv_ErrorHook->OnWarningString(this->AsMdbEnv(), inString);
+}
+
+void
+morkEnv::StubMethodOnlyError()
+{
+ this->NewError("method is stub only");
+}
+
+void
+morkEnv::OutOfMemoryError()
+{
+ this->NewError("out of memory");
+}
+
+void
+morkEnv::CantMakeWhenBadError()
+{
+ this->NewError("can't make an object when ev->Bad()");
+}
+
+static const char morkEnv_kNilPointer[] = "nil pointer";
+
+void
+morkEnv::NilPointerError()
+{
+ this->NewError(morkEnv_kNilPointer);
+}
+
+void
+morkEnv::NilPointerWarning()
+{
+ this->NewWarning(morkEnv_kNilPointer);
+}
+
+void
+morkEnv::NewNonEnvError()
+{
+ this->NewError("non-env instance");
+}
+
+void
+morkEnv::NilEnvSlotError()
+{
+ if ( !mEnv_HandlePool || !mEnv_Factory )
+ {
+ if ( !mEnv_HandlePool )
+ this->NewError("nil mEnv_HandlePool");
+ if ( !mEnv_Factory )
+ this->NewError("nil mEnv_Factory");
+ }
+ else
+ this->NewError("unknown nil env slot");
+}
+
+
+void morkEnv::NonEnvTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkEnv");
+}
+
+void
+morkEnv::ClearMorkErrorsAndWarnings()
+{
+ mEnv_ErrorCount = 0;
+ mEnv_WarningCount = 0;
+ mEnv_ErrorCode = NS_OK;
+ mEnv_ShouldAbort = morkBool_kFalse;
+}
+
+void
+morkEnv::AutoClearMorkErrorsAndWarnings()
+{
+ if ( this->DoAutoClear() )
+ {
+ mEnv_ErrorCount = 0;
+ mEnv_WarningCount = 0;
+ mEnv_ErrorCode = NS_OK;
+ mEnv_ShouldAbort = morkBool_kFalse;
+ }
+}
+
+/*static*/ morkEnv*
+morkEnv::FromMdbEnv(nsIMdbEnv* ioEnv) // dynamic type checking
+{
+ morkEnv* outEnv = 0;
+ if ( ioEnv )
+ {
+ // Note this cast is expected to perform some address adjustment of the
+ // pointer, so oenv likely does not equal ioEnv. Do not cast to void*
+ // first to force an exactly equal pointer (we tried it and it's wrong).
+ morkEnv* ev = (morkEnv*) ioEnv;
+ if ( ev && ev->IsEnv() )
+ {
+ if ( ev->DoAutoClear() )
+ {
+ ev->mEnv_ErrorCount = 0;
+ ev->mEnv_WarningCount = 0;
+ ev->mEnv_ErrorCode = NS_OK;
+ }
+ outEnv = ev;
+ }
+ else
+ MORK_ASSERT(outEnv);
+ }
+ else
+ MORK_ASSERT(outEnv);
+ return outEnv;
+}
+
+
+NS_IMETHODIMP
+morkEnv::GetErrorCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort)
+{
+ if ( outCount )
+ *outCount = mEnv_ErrorCount;
+ if ( outShouldAbort )
+ *outShouldAbort = mEnv_ShouldAbort;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetWarningCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort)
+{
+ if ( outCount )
+ *outCount = mEnv_WarningCount;
+ if ( outShouldAbort )
+ *outShouldAbort = mEnv_ShouldAbort;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetEnvBeVerbose(mdb_bool* outBeVerbose)
+{
+ NS_ENSURE_ARG_POINTER(outBeVerbose);
+ *outBeVerbose = mEnv_BeVerbose;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetEnvBeVerbose(mdb_bool inBeVerbose)
+{
+ mEnv_BeVerbose = inBeVerbose;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetDoTrace(mdb_bool* outDoTrace)
+{
+ NS_ENSURE_ARG_POINTER(outDoTrace);
+ *outDoTrace = mEnv_DoTrace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetDoTrace(mdb_bool inDoTrace)
+{
+ mEnv_DoTrace = inDoTrace;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetAutoClear(mdb_bool* outAutoClear)
+{
+ NS_ENSURE_ARG_POINTER(outAutoClear);
+ *outAutoClear = DoAutoClear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetAutoClear(mdb_bool inAutoClear)
+{
+ if ( inAutoClear )
+ EnableAutoClear();
+ else
+ DisableAutoClear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetErrorHook(nsIMdbErrorHook** acqErrorHook)
+{
+ NS_ENSURE_ARG_POINTER(acqErrorHook);
+ *acqErrorHook = mEnv_ErrorHook;
+ NS_IF_ADDREF(mEnv_ErrorHook);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetErrorHook(
+ nsIMdbErrorHook* ioErrorHook) // becomes referenced
+{
+ mEnv_ErrorHook = ioErrorHook;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::GetHeap(nsIMdbHeap** acqHeap)
+{
+ NS_ENSURE_ARG_POINTER(acqHeap);
+ nsIMdbHeap* outHeap = mEnv_Heap;
+
+ if ( acqHeap )
+ *acqHeap = outHeap;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::SetHeap(
+ nsIMdbHeap* ioHeap) // becomes referenced
+{
+ nsIMdbHeap_SlotStrongHeap(ioHeap, this, &mEnv_Heap);
+ return NS_OK;
+}
+// } ----- end attribute methods -----
+
+NS_IMETHODIMP
+morkEnv::ClearErrors() // clear errors beore re-entering db API
+{
+ mEnv_ErrorCount = 0;
+ mEnv_ErrorCode = NS_OK;
+ mEnv_ShouldAbort = morkBool_kFalse;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::ClearWarnings() // clear warning
+{
+ mEnv_WarningCount = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkEnv::ClearErrorsAndWarnings() // clear both errors & warnings
+{
+ ClearMorkErrorsAndWarnings();
+ return NS_OK;
+}
+// } ===== end nsIMdbEnv methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkEnv.h b/db/mork/src/morkEnv.h
new file mode 100644
index 000000000..827a56d72
--- /dev/null
+++ b/db/mork/src/morkEnv.h
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKENV_
+#define _MORKENV_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+// sean was here
+#include "nsError.h"
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kEnv /*i*/ 0x4576 /* ascii 'Ev' */
+
+// use NS error codes to make Mork easier to use with the rest of mozilla
+#define morkEnv_kNoError NS_SUCCEEDED /* no error has happened */
+#define morkEnv_kNonEnvTypeError NS_ERROR_FAILURE /* morkEnv::IsEnv() is false */
+
+#define morkEnv_kStubMethodOnlyError NS_ERROR_NO_INTERFACE
+#define morkEnv_kOutOfMemoryError NS_ERROR_OUT_OF_MEMORY
+#define morkEnv_kNilPointerError NS_ERROR_NULL_POINTER
+#define morkEnv_kNewNonEnvError NS_ERROR_FAILURE
+#define morkEnv_kNilEnvSlotError NS_ERROR_FAILURE
+
+#define morkEnv_kBadFactoryError NS_ERROR_FACTORY_NOT_LOADED
+#define morkEnv_kBadFactoryEnvError NS_ERROR_FACTORY_NOT_LOADED
+#define morkEnv_kBadEnvError NS_ERROR_FAILURE
+
+#define morkEnv_kNonHandleTypeError NS_ERROR_FAILURE
+#define morkEnv_kNonOpenNodeError NS_ERROR_FAILURE
+
+
+#define morkEnv_kWeakRefCountEnvBonus 0 /* try NOT to leak all env instances */
+
+/*| morkEnv:
+|*/
+class morkEnv : public morkObject, public nsIMdbEnv {
+ NS_DECL_ISUPPORTS_INHERITED
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+
+ morkFactory* mEnv_Factory; // NON-refcounted factory
+ nsIMdbHeap* mEnv_Heap; // NON-refcounted heap
+
+ nsIMdbEnv* mEnv_SelfAsMdbEnv;
+ nsIMdbErrorHook* mEnv_ErrorHook;
+
+ morkPool* mEnv_HandlePool; // pool for re-using handles
+
+ mork_u2 mEnv_ErrorCount;
+ mork_u2 mEnv_WarningCount;
+
+ nsresult mEnv_ErrorCode;
+
+ mork_bool mEnv_DoTrace;
+ mork_able mEnv_AutoClear;
+ mork_bool mEnv_ShouldAbort;
+ mork_bool mEnv_BeVerbose;
+ mork_bool mEnv_OwnsHeap;
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseEnv() only if open
+ virtual ~morkEnv(); // assert that CloseEnv() executed earlier
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetErrorCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort) override;
+ NS_IMETHOD GetWarningCount(mdb_count* outCount,
+ mdb_bool* outShouldAbort) override;
+
+ NS_IMETHOD GetEnvBeVerbose(mdb_bool* outBeVerbose) override;
+ NS_IMETHOD SetEnvBeVerbose(mdb_bool inBeVerbose) override;
+
+ NS_IMETHOD GetDoTrace(mdb_bool* outDoTrace) override;
+ NS_IMETHOD SetDoTrace(mdb_bool inDoTrace) override;
+
+ NS_IMETHOD GetAutoClear(mdb_bool* outAutoClear) override;
+ NS_IMETHOD SetAutoClear(mdb_bool inAutoClear) override;
+
+ NS_IMETHOD GetErrorHook(nsIMdbErrorHook** acqErrorHook) override;
+ NS_IMETHOD SetErrorHook(
+ nsIMdbErrorHook* ioErrorHook) override; // becomes referenced
+
+ NS_IMETHOD GetHeap(nsIMdbHeap** acqHeap) override;
+ NS_IMETHOD SetHeap(nsIMdbHeap* ioHeap) override; // becomes referenced
+ // } ----- end attribute methods -----
+
+ NS_IMETHOD ClearErrors() override; // clear errors beore re-entering db API
+ NS_IMETHOD ClearWarnings() override; // clear warnings
+ NS_IMETHOD ClearErrorsAndWarnings() override; // clear both errors & warnings
+// } ===== end nsIMdbEnv methods =====
+public: // morkEnv construction & destruction
+ morkEnv(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkFactory* ioFactory, nsIMdbHeap* ioSlotHeap);
+ morkEnv(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbEnv* inSelfAsMdbEnv, morkFactory* ioFactory,
+ nsIMdbHeap* ioSlotHeap);
+ void CloseEnv(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkEnv(const morkEnv& other);
+ morkEnv& operator=(const morkEnv& other);
+
+public: // dynamic type identification
+ mork_bool IsEnv() const
+ { return IsNode() && mNode_Derived == morkDerived_kEnv; }
+// } ===== end morkNode methods =====
+
+public: // utility env methods
+
+ mork_u1 HexToByte(mork_ch inFirstHex, mork_ch inSecondHex);
+
+ mork_size TokenAsHex(void* outBuf, mork_token inToken);
+ // TokenAsHex() is the same as sprintf(outBuf, "%lX", (long) inToken);
+
+ mork_size OidAsHex(void* outBuf, const mdbOid& inOid);
+ // sprintf(buf, "%lX:^%lX", (long) inOid.mOid_Id, (long) inOid.mOid_Scope);
+
+ char* CopyString(nsIMdbHeap* ioHeap, const char* inString);
+ void FreeString(nsIMdbHeap* ioHeap, char* ioString);
+ void StringToYarn(const char* inString, mdbYarn* outYarn);
+
+public: // other env methods
+
+ morkHandleFace* NewHandle(mork_size inSize)
+ { return mEnv_HandlePool->NewHandle(this, inSize, (morkZone*) 0); }
+
+ void ZapHandle(morkHandleFace* ioHandle)
+ { mEnv_HandlePool->ZapHandle(this, ioHandle); }
+
+ void EnableAutoClear() { mEnv_AutoClear = morkAble_kEnabled; }
+ void DisableAutoClear() { mEnv_AutoClear = morkAble_kDisabled; }
+
+ mork_bool DoAutoClear() const
+ { return mEnv_AutoClear == morkAble_kEnabled; }
+
+ void NewError(const char* inString);
+ void NewWarning(const char* inString);
+
+ void ClearMorkErrorsAndWarnings(); // clear both errors & warnings
+ void AutoClearMorkErrorsAndWarnings(); // clear if auto is enabled
+
+ void StubMethodOnlyError();
+ void OutOfMemoryError();
+ void NilPointerError();
+ void NilPointerWarning();
+ void CantMakeWhenBadError();
+ void NewNonEnvError();
+ void NilEnvSlotError();
+
+ void NonEnvTypeError(morkEnv* ev);
+
+ // canonical env convenience methods to check for presence of errors:
+ mork_bool Good() const { return ( mEnv_ErrorCount == 0 ); }
+ mork_bool Bad() const { return ( mEnv_ErrorCount != 0 ); }
+
+ nsIMdbEnv* AsMdbEnv() { return (nsIMdbEnv *) this; }
+ static morkEnv* FromMdbEnv(nsIMdbEnv* ioEnv); // dynamic type checking
+
+ nsresult AsErr() const { return mEnv_ErrorCode; }
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakEnv(morkEnv* me,
+ morkEnv* ev, morkEnv** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongEnv(morkEnv* me,
+ morkEnv* ev, morkEnv** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#undef MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING
+#ifdef MOZ_IS_DESTRUCTIBLE
+#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X) \
+ static_assert(!MOZ_IS_DESTRUCTIBLE(X) || \
+ mozilla::IsSame<X, morkEnv>::value, \
+ "Reference-counted class " #X " should not have a public destructor. " \
+ "Try to make this class's destructor non-public. If that is really " \
+ "not possible, you can whitelist this class by providing a " \
+ "HasDangerousPublicDestructor specialization for it.");
+#else
+#define MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(X)
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKENV_ */
diff --git a/db/mork/src/morkFactory.cpp b/db/mork/src/morkFactory.cpp
new file mode 100644
index 000000000..a3aeadfd4
--- /dev/null
+++ b/db/mork/src/morkFactory.cpp
@@ -0,0 +1,610 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+
+#ifndef _MORKWRITER_
+#include "morkWriter.h"
+#endif
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkFactory::CloseMorkNode(morkEnv* ev) /*i*/ // CloseFactory() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseFactory(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkFactory::~morkFactory() /*i*/ // assert CloseFactory() executed earlier
+{
+ CloseFactory(&mFactory_Env);
+ MORK_ASSERT(mFactory_Env.IsShutNode());
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkFactory::morkFactory() // uses orkinHeap
+: morkObject(morkUsage::kGlobal, (nsIMdbHeap*) 0, morkColor_kNone)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this,
+ new orkinHeap())
+, mFactory_Heap()
+{
+ if ( mFactory_Env.Good() )
+ {
+ mNode_Derived = morkDerived_kFactory;
+ mNode_Refs += morkFactory_kWeakRefCountBonus;
+ }
+}
+
+/*public non-poly*/
+morkFactory::morkFactory(nsIMdbHeap* ioHeap)
+: morkObject(morkUsage::kHeap, ioHeap, morkColor_kNone)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap)
+, mFactory_Heap()
+{
+ if ( mFactory_Env.Good() )
+ {
+ mNode_Derived = morkDerived_kFactory;
+ mNode_Refs += morkFactory_kWeakRefCountBonus;
+ }
+}
+
+/*public non-poly*/
+morkFactory::morkFactory(morkEnv* ev, /*i*/
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mFactory_Env(morkUsage::kMember, (nsIMdbHeap*) 0, this, ioHeap)
+, mFactory_Heap()
+{
+ if ( ev->Good() )
+ {
+ mNode_Derived = morkDerived_kFactory;
+ mNode_Refs += morkFactory_kWeakRefCountBonus;
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkFactory, morkObject, nsIMdbFactory)
+
+extern "C" nsIMdbFactory* MakeMdbFactory()
+{
+ return new morkFactory(new orkinHeap());
+}
+
+
+/*public non-poly*/ void
+morkFactory::CloseFactory(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mFactory_Env.CloseMorkNode(ev);
+ this->CloseObject(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+morkEnv* morkFactory::GetInternalFactoryEnv(nsresult* outErr)
+{
+ morkEnv* outEnv = 0;
+ if (IsNode() && IsOpenNode() && IsFactory() )
+ {
+ morkEnv* fenv = &mFactory_Env;
+ if ( fenv && fenv->IsNode() && fenv->IsOpenNode() && fenv->IsEnv() )
+ {
+ fenv->ClearMorkErrorsAndWarnings(); // drop any earlier errors
+ outEnv = fenv;
+ }
+ else
+ *outErr = morkEnv_kBadFactoryEnvError;
+ }
+ else
+ *outErr = morkEnv_kBadFactoryError;
+
+ return outEnv;
+}
+
+
+void
+morkFactory::NonFactoryTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkFactory");
+}
+
+
+NS_IMETHODIMP
+morkFactory::OpenOldFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+ const char* inFilePath,
+ mork_bool inFrozen, nsIMdbFile** acqFile)
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be open and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ morkFile* file = nullptr;
+ if ( ev )
+ {
+ if ( !ioHeap )
+ ioHeap = &mFactory_Heap;
+
+ file = morkFile::OpenOldFile(ev, ioHeap, inFilePath, inFrozen);
+ NS_IF_ADDREF( file );
+
+ outErr = ev->AsErr();
+ }
+ if ( acqFile )
+ *acqFile = file;
+
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::CreateNewFile(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+ const char* inFilePath, nsIMdbFile** acqFile)
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be created and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ morkFile* file = nullptr;
+ if ( ev )
+ {
+ if ( !ioHeap )
+ ioHeap = &mFactory_Heap;
+
+ file = morkFile::CreateNewFile(ev, ioHeap, inFilePath);
+ if ( file )
+ NS_ADDREF(file);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqFile )
+ *acqFile = file;
+
+ return outErr;
+}
+// } ----- end file methods -----
+
+// { ----- begin env methods -----
+NS_IMETHODIMP
+morkFactory::MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv)
+// ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+{
+ nsresult outErr = NS_OK;
+ nsIMdbEnv* outEnv = 0;
+ mork_bool ownsHeap = (ioHeap == 0);
+ if ( !ioHeap )
+ ioHeap = new orkinHeap();
+
+ if ( acqEnv && ioHeap )
+ {
+ morkEnv* fenv = this->GetInternalFactoryEnv(&outErr);
+ if ( fenv )
+ {
+ morkEnv* newEnv = new(*ioHeap, fenv)
+ morkEnv(morkUsage::kHeap, ioHeap, this, ioHeap);
+
+ if ( newEnv )
+ {
+ newEnv->mEnv_OwnsHeap = ownsHeap;
+ newEnv->mNode_Refs += morkEnv_kWeakRefCountEnvBonus;
+ NS_ADDREF(newEnv);
+ newEnv->mEnv_SelfAsMdbEnv = newEnv;
+ outEnv = newEnv;
+ }
+ else
+ outErr = morkEnv_kOutOfMemoryError;
+ }
+
+ *acqEnv = outEnv;
+ }
+ else
+ outErr = morkEnv_kNilPointerError;
+
+ return outErr;
+}
+// } ----- end env methods -----
+
+// { ----- begin heap methods -----
+NS_IMETHODIMP
+morkFactory::MakeHeap(nsIMdbEnv* mev, nsIMdbHeap** acqHeap)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbHeap* outHeap = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ outHeap = new orkinHeap();
+ if ( !outHeap )
+ ev->OutOfMemoryError();
+ }
+ MORK_ASSERT(acqHeap);
+ if ( acqHeap )
+ *acqHeap = outHeap;
+ return outErr;
+}
+// } ----- end heap methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkFactory::MakeRow(nsIMdbEnv* mev, nsIMdbHeap* ioHeap,
+ nsIMdbRow** acqRow)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// ioHeap can be nil, causing the heap associated with ev to be used
+// } ----- end row methods -----
+
+// { ----- begin port methods -----
+NS_IMETHODIMP
+morkFactory::CanOpenFilePort(
+ nsIMdbEnv* mev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion)
+{
+ nsresult outErr = NS_OK;
+ if ( outFormatVersion )
+ {
+ outFormatVersion->mYarn_Fill = 0;
+ }
+ mdb_bool canOpenAsPort = morkBool_kFalse;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioFile && outCanOpen )
+ {
+ canOpenAsPort = this->CanOpenMorkTextFile(ev, ioFile);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+
+ if ( outCanOpen )
+ *outCanOpen = canOpenAsPort;
+
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::OpenFilePort(
+ nsIMdbEnv* mev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for readonly import
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb)
+{
+ NS_ASSERTION(false, "this doesn't look implemented");
+ MORK_USED_1(ioHeap);
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioFile && inOpenPolicy && acqThumb )
+ {
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+NS_IMETHODIMP
+morkFactory::ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+ nsIMdbEnv* mev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+ nsIMdbPort** acqPort)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbPort* outPort = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioThumb && acqPort )
+ {
+ morkThumb* thumb = (morkThumb*) ioThumb;
+ morkStore* store = thumb->ThumbToOpenStore(ev);
+ if ( store )
+ {
+ store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+ store->mStore_CanDirty = morkBool_kTrue;
+ store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+
+ NS_ADDREF(store);
+ outPort = store;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqPort )
+ *acqPort = outPort;
+ return outErr;
+}
+// } ----- end port methods -----
+
+mork_bool
+morkFactory::CanOpenMorkTextFile(morkEnv* ev,
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile)
+{
+ MORK_USED_1(ev);
+ mork_bool outBool = morkBool_kFalse;
+ mork_size headSize = MORK_STRLEN(morkWriter_kFileHeader);
+
+ char localBuf[ 256 + 4 ]; // for extra for sloppy safety
+ mdbYarn localYarn;
+ mdbYarn* y = &localYarn;
+ y->mYarn_Buf = localBuf; // space to hold content
+ y->mYarn_Fill = 0; // no logical content yet
+ y->mYarn_Size = 256; // physical capacity is 256 bytes
+ y->mYarn_More = 0;
+ y->mYarn_Form = 0;
+ y->mYarn_Grow = 0;
+
+ if ( ioFile )
+ {
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ mdb_size actualSize = 0;
+ ioFile->Get(menv, y->mYarn_Buf, y->mYarn_Size, /*pos*/ 0, &actualSize);
+ y->mYarn_Fill = actualSize;
+
+ if ( y->mYarn_Buf && actualSize >= headSize && ev->Good() )
+ {
+ mork_u1* buf = (mork_u1*) y->mYarn_Buf;
+ outBool = ( MORK_MEMCMP(morkWriter_kFileHeader, buf, headSize) == 0 );
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return outBool;
+}
+
+// { ----- begin store methods -----
+NS_IMETHODIMP
+morkFactory::CanOpenFileStore(
+ nsIMdbEnv* mev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+ mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion)
+{
+ mdb_bool canOpenAsStore = morkBool_kFalse;
+ mdb_bool canOpenAsPort = morkBool_kFalse;
+ if ( outFormatVersion )
+ {
+ outFormatVersion->mYarn_Fill = 0;
+ }
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioFile && outCanOpenAsStore )
+ {
+ // right now always say true; later we should look for magic patterns
+ canOpenAsStore = this->CanOpenMorkTextFile(ev, ioFile);
+ canOpenAsPort = canOpenAsStore;
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( outCanOpenAsStore )
+ *outCanOpenAsStore = canOpenAsStore;
+
+ if ( outCanOpenAsPort )
+ *outCanOpenAsPort = canOpenAsPort;
+
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::OpenFileStore( // open an existing database
+ nsIMdbEnv* mev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for general db usage
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( !ioHeap ) // need to use heap from env?
+ ioHeap = ev->mEnv_Heap;
+
+ if ( ioFile && inOpenPolicy && acqThumb )
+ {
+ morkStore* store = new(*ioHeap, ev)
+ morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap);
+
+ if ( store )
+ {
+ mork_bool frozen = morkBool_kFalse; // open store mutable access
+ if ( store->OpenStoreFile(ev, frozen, ioFile, inOpenPolicy) )
+ {
+ morkThumb* thumb = morkThumb::Make_OpenFileStore(ev, ioHeap, store);
+ if ( thumb )
+ {
+ outThumb = thumb;
+ thumb->AddRef();
+ }
+ }
+// store->CutStrongRef(mev); // always cut ref (handle has its own ref)
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+
+NS_IMETHODIMP
+morkFactory::ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+ nsIMdbEnv* mev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+ nsIMdbStore** acqStore)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbStore* outStore = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioThumb && acqStore )
+ {
+ morkThumb* thumb = (morkThumb*) ioThumb;
+ morkStore* store = thumb->ThumbToOpenStore(ev);
+ if ( store )
+ {
+ store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+ store->mStore_CanDirty = morkBool_kTrue;
+ store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+
+ outStore = store;
+ NS_ADDREF(store);
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqStore )
+ *acqStore = outStore;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkFactory::CreateNewFileStore( // create a new db with minimal content
+ nsIMdbEnv* mev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // name of file which should not yet exist
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbStore** acqStore)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbStore* outStore = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( !ioHeap ) // need to use heap from env?
+ ioHeap = ev->mEnv_Heap;
+
+ if ( ioFile && inOpenPolicy && acqStore && ioHeap )
+ {
+ morkStore* store = new(*ioHeap, ev)
+ morkStore(ev, morkUsage::kHeap, ioHeap, this, ioHeap);
+
+ if ( store )
+ {
+ store->mStore_CanAutoAssignAtomIdentity = morkBool_kTrue;
+ store->mStore_CanDirty = morkBool_kTrue;
+ store->SetStoreAndAllSpacesCanDirty(ev, morkBool_kTrue);
+
+ if ( store->CreateStoreFile(ev, ioFile, inOpenPolicy) )
+ outStore = store;
+ NS_ADDREF(store);
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqStore )
+ *acqStore = outStore;
+ return outErr;
+}
+// } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkFactory.h b/db/mork/src/morkFactory.h
new file mode 100644
index 000000000..f6f045ae0
--- /dev/null
+++ b/db/mork/src/morkFactory.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKFACTORY_
+#define _MORKFACTORY_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbFactory;
+
+#define morkDerived_kFactory /*i*/ 0x4663 /* ascii 'Fc' */
+#define morkFactory_kWeakRefCountBonus 0 /* try NOT to leak all factories */
+
+/*| morkFactory:
+|*/
+class morkFactory : public morkObject, public nsIMdbFactory { // nsIMdbObject
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+
+ morkEnv mFactory_Env; // private env instance used internally
+ orkinHeap mFactory_Heap;
+
+ NS_DECL_ISUPPORTS_INHERITED
+// { ===== begin morkNode interface =====
+public: // morkFactory virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseFactory() only if open
+
+
+// { ===== begin nsIMdbFactory methods =====
+
+ // { ----- begin file methods -----
+ NS_IMETHOD OpenOldFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath,
+ mdb_bool inFrozen, nsIMdbFile** acqFile) override;
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be open and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+
+ NS_IMETHOD CreateNewFile(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath,
+ nsIMdbFile** acqFile) override;
+ // Choose some subclass of nsIMdbFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be created and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+ // } ----- end file methods -----
+
+ // { ----- begin env methods -----
+ NS_IMETHOD MakeEnv(nsIMdbHeap* ioHeap, nsIMdbEnv** acqEnv) override; // new env
+ // ioHeap can be nil, causing a MakeHeap() style heap instance to be used
+ // } ----- end env methods -----
+
+ // { ----- begin heap methods -----
+ NS_IMETHOD MakeHeap(nsIMdbEnv* ev, nsIMdbHeap** acqHeap) override; // new heap
+ // } ----- end heap methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD MakeRow(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbRow** acqRow) override; // new row
+ // ioHeap can be nil, causing the heap associated with ev to be used
+ // } ----- end row methods -----
+
+ // { ----- begin port methods -----
+ NS_IMETHOD CanOpenFilePort(
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpen, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion) override; // informal file format description
+
+ NS_IMETHOD OpenFilePort(
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for readonly import
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental port open
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then call nsIMdbFactory::ThumbToOpenPort() to get the port instance.
+
+ NS_IMETHOD ThumbToOpenPort( // redeeming a completed thumb from OpenFilePort()
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFilePort() with done status
+ nsIMdbPort** acqPort) override; // acquire new port object
+ // } ----- end port methods -----
+
+ // { ----- begin store methods -----
+ NS_IMETHOD CanOpenFileStore(
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to investigate
+ // const mdbYarn* inFirst512Bytes,
+ nsIMdbFile* ioFile, // db abstract file interface
+ mdb_bool* outCanOpenAsStore, // whether OpenFileStore() might succeed
+ mdb_bool* outCanOpenAsPort, // whether OpenFilePort() might succeed
+ mdbYarn* outFormatVersion) override; // informal file format description
+
+ NS_IMETHOD OpenFileStore( // open an existing database
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // the file to open for general db usage
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental store open
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then call nsIMdbFactory::ThumbToOpenStore() to get the store instance.
+
+ NS_IMETHOD
+ ThumbToOpenStore( // redeem completed thumb from OpenFileStore()
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb* ioThumb, // thumb from OpenFileStore() with done status
+ nsIMdbStore** acqStore) override; // acquire new db store object
+
+ NS_IMETHOD CreateNewFileStore( // create a new db with minimal content
+ nsIMdbEnv* ev, // context
+ nsIMdbHeap* ioHeap, // can be nil to cause ev's heap attribute to be used
+ // const char* inFilePath, // name of file which should not yet exist
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy, // runtime policies for using db
+ nsIMdbStore** acqStore) override; // acquire new db store object
+ // } ----- end store methods -----
+
+// } ===== end nsIMdbFactory methods =====
+
+public: // morkYarn construction & destruction
+ morkFactory(); // uses orkinHeap
+ explicit morkFactory(nsIMdbHeap* ioHeap); // caller supplied heap
+ morkFactory(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+ void CloseFactory(morkEnv* ev); // called by CloseMorkNode();
+
+
+public: // morkNode memory management operators
+ void* operator new(size_t inSize) CPP_THROW_NEW
+ { return ::operator new(inSize); }
+
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkNode::MakeNew(inSize, ioHeap, ev); }
+
+private: // copying is not allowed
+ morkFactory(const morkFactory& other);
+ morkFactory& operator=(const morkFactory& other);
+ virtual ~morkFactory(); // assert that CloseFactory() executed earlier
+
+public: // dynamic type identification
+ mork_bool IsFactory() const
+ { return IsNode() && mNode_Derived == morkDerived_kFactory; }
+// } ===== end morkNode methods =====
+
+public: // other factory methods
+
+ void NonFactoryTypeError(morkEnv* ev);
+ morkEnv* GetInternalFactoryEnv(nsresult* outErr);
+ mork_bool CanOpenMorkTextFile(morkEnv* ev, nsIMdbFile* ioFile);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakFactory(morkFactory* me,
+ morkEnv* ev, morkFactory** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongFactory(morkFactory* me,
+ morkEnv* ev, morkFactory** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKFACTORY_ */
diff --git a/db/mork/src/morkFile.cpp b/db/mork/src/morkFile.cpp
new file mode 100644
index 000000000..040d1a8dc
--- /dev/null
+++ b/db/mork/src/morkFile.cpp
@@ -0,0 +1,874 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifdef MORK_WIN
+#include "io.h"
+#include <windows.h>
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkFile::CloseMorkNode(morkEnv* ev) // CloseFile() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseFile(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkFile::~morkFile() // assert CloseFile() executed earlier
+{
+ MORK_ASSERT(mFile_Frozen==0);
+ MORK_ASSERT(mFile_DoTrace==0);
+ MORK_ASSERT(mFile_IoOpen==0);
+ MORK_ASSERT(mFile_Active==0);
+}
+
+/*public non-poly*/
+morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mFile_Frozen( 0 )
+, mFile_DoTrace( 0 )
+, mFile_IoOpen( 0 )
+, mFile_Active( 0 )
+
+, mFile_SlotHeap( 0 )
+, mFile_Name( 0 )
+, mFile_Thief( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioSlotHeap )
+ {
+ nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kFile;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkFile, morkObject, nsIMdbFile)
+/*public non-poly*/ void
+morkFile::CloseFile(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mFile_Frozen = 0;
+ mFile_DoTrace = 0;
+ mFile_IoOpen = 0;
+ mFile_Active = 0;
+
+ if ( mFile_Name )
+ this->SetFileName(ev, (const char*) 0);
+
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mFile_SlotHeap);
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mFile_Thief);
+
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ morkFile*
+morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath, mork_bool inFrozen)
+ // Choose some subclass of morkFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be open and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+{
+ return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen);
+}
+
+/*static*/ morkFile*
+morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath)
+ // Choose some subclass of morkFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be created and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+{
+ return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath);
+}
+
+void
+morkFile::NewMissingIoError(morkEnv* ev) const
+{
+ ev->NewError("file missing io");
+}
+
+/*static*/ void
+morkFile::NonFileTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkFile");
+}
+
+/*static*/ void
+morkFile::NilSlotHeapError(morkEnv* ev)
+{
+ ev->NewError("nil mFile_SlotHeap");
+}
+
+/*static*/ void
+morkFile::NilFileNameError(morkEnv* ev)
+{
+ ev->NewError("nil mFile_Name");
+}
+
+void
+morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief)
+{
+ nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief);
+}
+
+void
+morkFile::SetFileName(morkEnv* ev, const char* inName) // inName can be nil
+{
+ nsIMdbHeap* heap = mFile_SlotHeap;
+ if ( heap )
+ {
+ char* name = mFile_Name;
+ if ( name )
+ {
+ mFile_Name = 0;
+ ev->FreeString(heap, name);
+ }
+ if ( ev->Good() && inName )
+ mFile_Name = ev->CopyString(heap, inName);
+ }
+ else
+ this->NilSlotHeapError(ev);
+}
+
+void
+morkFile::NewFileDownError(morkEnv* ev) const
+// call NewFileDownError() when either IsOpenAndActiveFile()
+// is false, or when IsOpenActiveAndMutableFile() is false.
+{
+ if ( this->IsOpenNode() )
+ {
+ if ( this->FileActive() )
+ {
+ if ( this->FileFrozen() )
+ {
+ ev->NewError("file frozen");
+ }
+ else
+ ev->NewError("unknown file problem");
+ }
+ else
+ ev->NewError("file not active");
+ }
+ else
+ ev->NewError("file not open");
+}
+
+void
+morkFile::NewFileErrnoError(morkEnv* ev) const
+// call NewFileErrnoError() to convert std C errno into AB fault
+{
+ const char* errnoString = strerror(errno);
+ ev->NewError(errnoString); // maybe pass value of strerror() instead
+}
+
+// ````` ````` ````` ````` newlines ````` ````` ````` `````
+
+#if defined(MORK_MAC)
+ static const char morkFile_kNewlines[] =
+ "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015";
+# define morkFile_kNewlinesCount 16
+#else
+# if defined(MORK_WIN)
+ static const char morkFile_kNewlines[] =
+ "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012";
+# define morkFile_kNewlinesCount 8
+# else
+# ifdef MORK_UNIX
+ static const char morkFile_kNewlines[] =
+ "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012";
+# define morkFile_kNewlinesCount 16
+# endif /* MORK_UNIX */
+# endif /* MORK_WIN */
+#endif /* MORK_MAC */
+
+mork_size
+morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines)
+ // WriteNewlines() returns the number of bytes written.
+{
+ mork_size outSize = 0;
+ while ( inNewlines && ev->Good() ) // more newlines to write?
+ {
+ mork_u4 quantum = inNewlines;
+ if ( quantum > morkFile_kNewlinesCount )
+ quantum = morkFile_kNewlinesCount;
+
+ mork_size quantumSize = quantum * mork_kNewlineSize;
+ mdb_size bytesWritten;
+ this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten);
+ outSize += quantumSize;
+ inNewlines -= quantum;
+ }
+ return outSize;
+}
+
+NS_IMETHODIMP
+morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+ nsresult outErr = NS_OK;
+ mdb_pos pos = -1;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ pos = Length(ev);
+ outErr = ev->AsErr();
+ if ( outPos )
+ *outPos = pos;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize)
+{
+ nsresult rv = NS_OK;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mdb_pos outPos;
+ Seek(mev, inPos, &outPos);
+ if ( ev->Good() )
+ rv = Read(mev, outBuf, inSize, outActualSize);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize)
+{
+ nsresult outErr = NS_OK;
+ *outActualSize = 0;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mdb_pos outPos;
+
+ Seek(mev, inPos, &outPos);
+ if ( ev->Good() )
+ Write(mev, inBuf, inSize, outActualSize);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+// { ----- begin path methods -----
+NS_IMETHODIMP
+morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath)
+{
+ nsresult outErr = NS_OK;
+ if ( outFilePath )
+ outFilePath->mYarn_Fill = 0;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ ev->StringToYarn(GetFileNameString(), outFilePath);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+// } ----- end path methods -----
+
+// { ----- begin replacement methods -----
+
+
+NS_IMETHODIMP
+morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbFile* outThief = 0;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ outThief = GetThief();
+ NS_IF_ADDREF(outThief);
+ outErr = ev->AsErr();
+ }
+ if ( acqThief )
+ *acqThief = outThief;
+ return outErr;
+}
+
+// } ----- end replacement methods -----
+
+// { ----- begin versioning methods -----
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStdioFile::CloseMorkNode(morkEnv* ev) // CloseStdioFile() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseStdioFile(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkStdioFile::~morkStdioFile() // assert CloseStdioFile() executed earlier
+{
+ if (mStdioFile_File)
+ CloseStdioFile(mMorkEnv);
+ MORK_ASSERT(mStdioFile_File==0);
+}
+
+/*public non-poly*/ void
+morkStdioFile::CloseStdioFile(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() )
+ {
+ this->CloseStdio(ev);
+ }
+
+ mStdioFile_File = 0;
+
+ this->CloseFile(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// compatible with the morkFile::MakeFile() entry point
+
+/*static*/ morkStdioFile*
+morkStdioFile::OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath, mork_bool inFrozen)
+{
+ morkStdioFile* outFile = 0;
+ if ( ioHeap && inFilePath )
+ {
+ const char* mode = (inFrozen)? "rb" : "rb+";
+ outFile = new(*ioHeap, ev)
+ morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode);
+
+ if ( outFile )
+ {
+ outFile->SetFileFrozen(inFrozen);
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return outFile;
+}
+
+/*static*/ morkStdioFile*
+morkStdioFile::CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath)
+{
+ morkStdioFile* outFile = 0;
+ if ( ioHeap && inFilePath )
+ {
+ const char* mode = "wb+";
+ outFile = new(*ioHeap, ev)
+ morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode);
+ }
+ else
+ ev->NilPointerError();
+
+ return outFile;
+}
+
+
+
+NS_IMETHODIMP
+morkStdioFile::BecomeTrunk(nsIMdbEnv* ev)
+ // If this file is a file version branch created by calling AcquireBud(),
+ // BecomeTrunk() causes this file's content to replace the original
+ // file's content, typically by assuming the original file's identity.
+{
+ return Flush(ev);
+}
+
+NS_IMETHODIMP
+morkStdioFile::AcquireBud(nsIMdbEnv * mdbev, nsIMdbHeap* ioHeap, nsIMdbFile **acquiredFile)
+ // AcquireBud() starts a new "branch" version of the file, empty of content,
+ // so that a new version of the file can be written. This new file
+ // can later be told to BecomeTrunk() the original file, so the branch
+ // created by budding the file will replace the original file. Some
+ // file subclasses might initially take the unsafe but expedient
+ // approach of simply truncating this file down to zero length, and
+ // then returning the same morkFile pointer as this, with an extra
+ // reference count increment. Note that the caller of AcquireBud() is
+ // expected to eventually call CutStrongRef() on the returned file
+ // in order to release the strong reference. High quality versions
+ // of morkFile subclasses will create entirely new files which later
+ // are renamed to become the old file, so that better transactional
+ // behavior is exhibited by the file, so crashes protect old files.
+ // Note that AcquireBud() is an illegal operation on readonly files.
+{
+ NS_ENSURE_ARG(acquiredFile);
+ MORK_USED_1(ioHeap);
+ nsresult rv = NS_OK;
+ morkFile* outFile = 0;
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+ if ( this->IsOpenAndActiveFile() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+//#ifdef MORK_WIN
+// truncate(file, /*eof*/ 0);
+//#else /*MORK_WIN*/
+ char* name = mFile_Name;
+ if ( name )
+ {
+ if ( MORK_FILECLOSE(file) >= 0 )
+ {
+ this->SetFileActive(morkBool_kFalse);
+ this->SetFileIoOpen(morkBool_kFalse);
+ mStdioFile_File = 0;
+
+ file = MORK_FILEOPEN(name, "wb+"); // open for write, discarding old content
+ if ( file )
+ {
+ mStdioFile_File = file;
+ this->SetFileActive(morkBool_kTrue);
+ this->SetFileIoOpen(morkBool_kTrue);
+ this->SetFileFrozen(morkBool_kFalse);
+ }
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ else
+ this->NilFileNameError(ev);
+
+//#endif /*MORK_WIN*/
+
+ if ( ev->Good() && this->AddStrongRef(ev->AsMdbEnv()) )
+ {
+ outFile = this;
+ AddRef();
+ }
+ }
+ else if ( mFile_Thief )
+ {
+ rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile);
+ }
+ else
+ this->NewMissingIoError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ *acquiredFile = outFile;
+ return rv;
+}
+
+mork_pos
+morkStdioFile::Length(morkEnv * ev) const
+{
+ mork_pos outPos = 0;
+
+ if ( this->IsOpenAndActiveFile() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ long start = MORK_FILETELL(file);
+ if ( start >= 0 )
+ {
+ long fore = MORK_FILESEEK(file, 0, SEEK_END);
+ if ( fore >= 0 )
+ {
+ long eof = MORK_FILETELL(file);
+ if ( eof >= 0 )
+ {
+ long back = MORK_FILESEEK(file, start, SEEK_SET);
+ if ( back >= 0 )
+ outPos = eof;
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ else this->new_stdio_file_fault(ev);
+ }
+ else this->new_stdio_file_fault(ev);
+ }
+ else this->new_stdio_file_fault(ev);
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Eof(ev->AsMdbEnv(), &outPos);
+ else
+ this->NewMissingIoError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ return outPos;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos *outPos) const
+{
+ nsresult rv = NS_OK;
+ NS_ENSURE_ARG(outPos);
+ morkEnv* mev = morkEnv::FromMdbEnv(ev);
+ if ( this->IsOpenAndActiveFile() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ long where = MORK_FILETELL(file);
+ if ( where >= 0 )
+ *outPos = where;
+ else
+ this->new_stdio_file_fault(mev);
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Tell(ev, outPos);
+ else
+ this->NewMissingIoError(mev);
+ }
+ else this->NewFileDownError(mev);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_num *outCount)
+{
+ nsresult rv = NS_OK;
+ morkEnv* mev = morkEnv::FromMdbEnv(ev);
+ if ( this->IsOpenAndActiveFile() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ long count = (long) MORK_FILEREAD(outBuf, inSize, file);
+ if ( count >= 0 )
+ {
+ *outCount = (mork_num) count;
+ }
+ else this->new_stdio_file_fault(mev);
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Read(ev, outBuf, inSize, outCount);
+ else
+ this->NewMissingIoError(mev);
+ }
+ else this->NewFileDownError(mev);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos *aOutPos)
+{
+ mork_pos outPos = 0;
+ nsresult rv = NS_OK;
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+ if ( this->IsOpenOrClosingNode() && this->FileActive() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ long where = MORK_FILESEEK(file, inPos, SEEK_SET);
+ if ( where >= 0 )
+ outPos = inPos;
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Seek(mdbev, inPos, aOutPos);
+ else
+ this->NewMissingIoError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ *aOutPos = outPos;
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize, mork_size *aOutSize)
+{
+ mork_num outCount = 0;
+ nsresult rv = NS_OK;
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+ if ( this->IsOpenActiveAndMutableFile() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ fwrite(inBuf, 1, inSize, file);
+ if ( !ferror(file) )
+ outCount = inSize;
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Write(mdbev, inBuf, inSize, &outCount);
+ else
+ this->NewMissingIoError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ *aOutSize = outCount;
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStdioFile::Flush(nsIMdbEnv* mdbev)
+{
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+ if ( this->IsOpenOrClosingNode() && this->FileActive() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( file )
+ {
+ MORK_FILEFLUSH(file);
+
+ }
+ else if ( mFile_Thief )
+ mFile_Thief->Flush(mdbev);
+ else
+ this->NewMissingIoError(ev);
+ }
+ else this->NewFileDownError(ev);
+ return NS_OK;
+}
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+//protected: // protected non-poly morkStdioFile methods
+
+void
+morkStdioFile::new_stdio_file_fault(morkEnv* ev) const
+{
+ FILE* file = (FILE*) mStdioFile_File;
+
+ int copyErrno = errno; // facilitate seeing error in debugger
+
+ // bunch of stuff not ported here
+ if ( !copyErrno && file )
+ {
+ copyErrno = ferror(file);
+ errno = copyErrno;
+ }
+
+ this->NewFileErrnoError(ev);
+}
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+//public: // public non-poly morkStdioFile methods
+
+
+/*public non-poly*/
+morkStdioFile::morkStdioFile(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kStdioFile;
+}
+
+morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+ const char* inName, const char* inMode)
+ // calls OpenStdio() after construction
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+ if ( ev->Good() )
+ this->OpenStdio(ev, inName, inMode);
+}
+
+morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+ void* ioFile, const char* inName, mork_bool inFrozen)
+ // calls UseStdio() after construction
+: morkFile(ev, inUsage, ioHeap, ioSlotHeap)
+, mStdioFile_File( 0 )
+{
+ if ( ev->Good() )
+ this->UseStdio(ev, ioFile, inName, inFrozen);
+}
+
+void
+morkStdioFile::OpenStdio(morkEnv* ev, const char* inName, const char* inMode)
+ // Open a new FILE with name inName, using mode flags from inMode.
+{
+ if ( ev->Good() )
+ {
+ if ( !inMode )
+ inMode = "";
+
+ mork_bool frozen = (*inMode == 'r'); // cursory attempt to note readonly
+
+ if ( this->IsOpenNode() )
+ {
+ if ( !this->FileActive() )
+ {
+ this->SetFileIoOpen(morkBool_kFalse);
+ if ( inName && *inName )
+ {
+ this->SetFileName(ev, inName);
+ if ( ev->Good() )
+ {
+ FILE* file = MORK_FILEOPEN(inName, inMode);
+ if ( file )
+ {
+ mStdioFile_File = file;
+ this->SetFileActive(morkBool_kTrue);
+ this->SetFileIoOpen(morkBool_kTrue);
+ this->SetFileFrozen(frozen);
+ }
+ else
+ this->new_stdio_file_fault(ev);
+ }
+ }
+ else ev->NewError("no file name");
+ }
+ else ev->NewError("file already active");
+ }
+ else this->NewFileDownError(ev);
+ }
+}
+
+void
+morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const char* inName,
+ mork_bool inFrozen)
+ // Use an existing file, like stdin/stdout/stderr, which should not
+ // have the io stream closed when the file is closed. The ioFile
+ // parameter must actually be of type FILE (but we don't want to make
+ // this header file include the stdio.h header file).
+{
+ if ( ev->Good() )
+ {
+ if ( this->IsOpenNode() )
+ {
+ if ( !this->FileActive() )
+ {
+ if ( ioFile )
+ {
+ this->SetFileIoOpen(morkBool_kFalse);
+ this->SetFileName(ev, inName);
+ if ( ev->Good() )
+ {
+ mStdioFile_File = ioFile;
+ this->SetFileActive(morkBool_kTrue);
+ this->SetFileFrozen(inFrozen);
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+ else ev->NewError("file already active");
+ }
+ else this->NewFileDownError(ev);
+ }
+}
+
+void
+morkStdioFile::CloseStdio(morkEnv* ev)
+ // Close the stream io if both and FileActive() and FileIoOpen(), but
+ // this does not close this instances (like CloseStdioFile() does).
+ // If stream io was made active by means of calling UseStdio(),
+ // then this method does little beyond marking the stream inactive
+ // because FileIoOpen() is false.
+{
+ if ( mStdioFile_File && this->FileActive() && this->FileIoOpen() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( MORK_FILECLOSE(file) < 0 )
+ this->new_stdio_file_fault(ev);
+
+ mStdioFile_File = 0;
+ this->SetFileActive(morkBool_kFalse);
+ this->SetFileIoOpen(morkBool_kFalse);
+ }
+}
+
+
+NS_IMETHODIMP
+morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief)
+ // If this file is a file version branch created by calling AcquireBud(),
+ // BecomeTrunk() causes this file's content to replace the original
+ // file's content, typically by assuming the original file's identity.
+{
+ morkEnv *mev = morkEnv::FromMdbEnv(ev);
+ if ( mStdioFile_File && FileActive() && FileIoOpen() )
+ {
+ FILE* file = (FILE*) mStdioFile_File;
+ if ( MORK_FILECLOSE(file) < 0 )
+ new_stdio_file_fault(mev);
+
+ mStdioFile_File = 0;
+ }
+ SetThief(mev, ioThief);
+ return NS_OK;
+}
+
+
+#if defined(MORK_WIN)
+
+void mork_fileflush(FILE * file)
+{
+ fflush(file);
+}
+
+#endif /*MORK_WIN*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkFile.h b/db/mork/src/morkFile.h
new file mode 100644
index 000000000..39242ce73
--- /dev/null
+++ b/db/mork/src/morkFile.h
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKFILE_
+#define _MORKFILE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*=============================================================================
+ * morkFile: abstract file interface
+ */
+
+#define morkDerived_kFile /*i*/ 0x4669 /* ascii 'Fi' */
+
+class morkFile /*d*/ : public morkObject, public nsIMdbFile { /* ````` simple file API ````` */
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+// public: // slots inherited from morkObject (meant to inform only)
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected morkFile members (similar to public domain IronDoc)
+ virtual ~morkFile(); // assert that CloseFile() executed earlier
+
+ mork_u1 mFile_Frozen; // 'F' => file allows only read access
+ mork_u1 mFile_DoTrace; // 'T' trace if ev->DoTrace()
+ mork_u1 mFile_IoOpen; // 'O' => io stream is open (& needs a close)
+ mork_u1 mFile_Active; // 'A' => file is active and usable
+
+ nsIMdbHeap* mFile_SlotHeap; // heap for Name and other allocated slots
+ char* mFile_Name; // can be nil if SetFileName() is never called
+ // mFile_Name convention: managed with morkEnv::CopyString()/FreeString()
+
+ nsIMdbFile* mFile_Thief; // from a call to orkinFile::Steal()
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ NS_DECL_ISUPPORTS_INHERITED
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseFile() only if open
+
+public: // morkFile construction & destruction
+ morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbHeap* ioSlotHeap);
+ void CloseFile(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkFile(const morkFile& other);
+ morkFile& operator=(const morkFile& other);
+
+public: // dynamic type identification
+ mork_bool IsFile() const
+ { return IsNode() && mNode_Derived == morkDerived_kFile; }
+// } ===== end morkNode methods =====
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public static standard file creation entry point
+
+ static morkFile* OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath, mork_bool inFrozen);
+ // Choose some subclass of morkFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be open and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+
+ static morkFile* CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath);
+ // Choose some subclass of morkFile to instantiate, in order to read
+ // (and write if not frozen) the file known by inFilePath. The file
+ // returned should be created and ready for use, and presumably positioned
+ // at the first byte position of the file. The exact manner in which
+ // files must be opened is considered a subclass specific detail, and
+ // other portions or Mork source code don't want to know how it's done.
+
+public: // non-poly morkFile methods
+
+ mork_bool FileFrozen() const { return mFile_Frozen == 'F'; }
+ mork_bool FileDoTrace() const { return mFile_DoTrace == 'T'; }
+ mork_bool FileIoOpen() const { return mFile_IoOpen == 'O'; }
+ mork_bool FileActive() const { return mFile_Active == 'A'; }
+
+ void SetFileFrozen(mork_bool b) { mFile_Frozen = (mork_u1) ((b)? 'F' : 0); }
+ void SetFileDoTrace(mork_bool b) { mFile_DoTrace = (mork_u1) ((b)? 'T' : 0); }
+ void SetFileIoOpen(mork_bool b) { mFile_IoOpen = (mork_u1) ((b)? 'O' : 0); }
+ void SetFileActive(mork_bool b) { mFile_Active = (mork_u1) ((b)? 'A' : 0); }
+
+
+ mork_bool IsOpenActiveAndMutableFile() const
+ { return ( IsOpenNode() && FileActive() && !FileFrozen() ); }
+ // call IsOpenActiveAndMutableFile() before writing a file
+
+ mork_bool IsOpenAndActiveFile() const
+ { return ( this->IsOpenNode() && this->FileActive() ); }
+ // call IsOpenAndActiveFile() before using a file
+
+
+ nsIMdbFile* GetThief() const { return mFile_Thief; }
+ void SetThief(morkEnv* ev, nsIMdbFile* ioThief); // ioThief can be nil
+
+ const char* GetFileNameString() const { return mFile_Name; }
+ void SetFileName(morkEnv* ev, const char* inName); // inName can be nil
+ static void NilSlotHeapError(morkEnv* ev);
+ static void NilFileNameError(morkEnv* ev);
+ static void NonFileTypeError(morkEnv* ev);
+
+ void NewMissingIoError(morkEnv* ev) const;
+
+ void NewFileDownError(morkEnv* ev) const;
+ // call NewFileDownError() when either IsOpenAndActiveFile()
+ // is false, or when IsOpenActiveAndMutableFile() is false.
+
+ void NewFileErrnoError(morkEnv* ev) const;
+ // call NewFileErrnoError() to convert std C errno into AB fault
+
+ mork_size WriteNewlines(morkEnv* ev, mork_count inNewlines);
+ // WriteNewlines() returns the number of bytes written.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakFile(morkFile* me,
+ morkEnv* ev, morkFile** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongFile(morkFile* me,
+ morkEnv* ev, morkFile** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+public:
+ virtual mork_pos Length(morkEnv* ev) const = 0; // eof
+ // nsIMdbFile methods
+ NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override = 0;
+ NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) override = 0;
+ NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos) override;
+ // } ----- end pos methods -----
+
+ // { ----- begin read methods -----
+ NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+ mdb_size* outActualSize) override = 0;
+ NS_IMETHOD Get(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize) override;
+ // } ----- end read methods -----
+
+ // { ----- begin write methods -----
+ NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+ mdb_size* outActualSize) override = 0;
+ NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+ mdb_pos inPos, mdb_size* outActualSize) override;
+ NS_IMETHOD Flush(nsIMdbEnv* ev) override = 0;
+ // } ----- end attribute methods -----
+
+ // { ----- begin path methods -----
+ NS_IMETHOD Path(nsIMdbEnv* ev, mdbYarn* outFilePath) override ;
+ // } ----- end path methods -----
+
+ // { ----- begin replacement methods -----
+ NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override = 0;
+ NS_IMETHOD Thief(nsIMdbEnv* ev, nsIMdbFile** acqThief) override;
+ // } ----- end replacement methods -----
+
+ // { ----- begin versioning methods -----
+ NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override = 0;
+
+ NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ nsIMdbFile** acqBud) override = 0;
+ // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+
+};
+
+/*=============================================================================
+ * morkStdioFile: concrete file using standard C file io
+ */
+
+#define morkDerived_kStdioFile /*i*/ 0x7346 /* ascii 'sF' */
+
+class morkStdioFile /*d*/ : public morkFile { /* `` copied from IronDoc `` */
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected morkStdioFile members
+
+ void* mStdioFile_File;
+ // actually type FILE*, but using opaque void* type
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseStdioFile() only if open
+ virtual ~morkStdioFile(); // assert that CloseStdioFile() executed earlier
+
+public: // morkStdioFile construction & destruction
+ morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseStdioFile(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkStdioFile(const morkStdioFile& other);
+ morkStdioFile& operator=(const morkStdioFile& other);
+
+public: // dynamic type identification
+ mork_bool IsStdioFile() const
+ { return IsNode() && mNode_Derived == morkDerived_kStdioFile; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonStdioFileTypeError(morkEnv* ev);
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // compatible with the morkFile::OpenOldFile() entry point
+
+ static morkStdioFile* OpenOldStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath, mork_bool inFrozen);
+
+ static morkStdioFile* CreateNewStdioFile(morkEnv* ev, nsIMdbHeap* ioHeap,
+ const char* inFilePath);
+
+ virtual mork_pos Length(morkEnv* ev) const override; // eof
+
+ NS_IMETHOD Tell(nsIMdbEnv* ev, mdb_pos* outPos) const override;
+ NS_IMETHOD Seek(nsIMdbEnv* ev, mdb_pos inPos, mdb_pos *outPos) override;
+// NS_IMETHOD Eof(nsIMdbEnv* ev, mdb_pos* outPos);
+ // } ----- end pos methods -----
+
+ // { ----- begin read methods -----
+ NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mdb_size inSize,
+ mdb_size* outActualSize) override;
+
+ // { ----- begin write methods -----
+ NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+ mdb_size* outActualSize) override;
+// NS_IMETHOD Put(nsIMdbEnv* ev, const void* inBuf, mdb_size inSize,
+// mdb_pos inPos, mdb_size* outActualSize);
+ NS_IMETHOD Flush(nsIMdbEnv* ev) override;
+ // } ----- end attribute methods -----
+
+ NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override;
+
+ // { ----- begin versioning methods -----
+ NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap,
+ nsIMdbFile** acqBud) override;
+ // } ----- end versioning methods -----
+
+// } ===== end nsIMdbFile methods =====
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected non-poly morkStdioFile methods
+
+ void new_stdio_file_fault(morkEnv* ev) const;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkStdioFile methods
+
+ morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+ const char* inName, const char* inMode);
+ // calls OpenStdio() after construction
+
+ morkStdioFile(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap,
+ void* ioFile, const char* inName, mork_bool inFrozen);
+ // calls UseStdio() after construction
+
+ void OpenStdio(morkEnv* ev, const char* inName, const char* inMode);
+ // Open a new FILE with name inName, using mode flags from inMode.
+
+ void UseStdio(morkEnv* ev, void* ioFile, const char* inName,
+ mork_bool inFrozen);
+ // Use an existing file, like stdin/stdout/stderr, which should not
+ // have the io stream closed when the file is closed. The ioFile
+ // parameter must actually be of type FILE (but we don't want to make
+ // this header file include the stdio.h header file).
+
+ void CloseStdio(morkEnv* ev);
+ // Close the stream io if both and FileActive() and FileIoOpen(), but
+ // this does not close this instances (like CloseStdioFile() does).
+ // If stream io was made active by means of calling UseStdio(),
+ // then this method does little beyond marking the stream inactive
+ // because FileIoOpen() is false.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakStdioFile(morkStdioFile* me,
+ morkEnv* ev, morkStdioFile** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongStdioFile(morkStdioFile* me,
+ morkEnv* ev, morkStdioFile** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKFILE_ */
diff --git a/db/mork/src/morkHandle.cpp b/db/mork/src/morkHandle.cpp
new file mode 100644
index 000000000..f6d426887
--- /dev/null
+++ b/db/mork/src/morkHandle.cpp
@@ -0,0 +1,423 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkHandle::CloseMorkNode(morkEnv* ev) // CloseHandle() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseHandle(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkHandle::~morkHandle() // assert CloseHandle() executed earlier
+{
+ MORK_ASSERT(mHandle_Env==0);
+ MORK_ASSERT(mHandle_Face==0);
+ MORK_ASSERT(mHandle_Object==0);
+ MORK_ASSERT(mHandle_Magic==0);
+ MORK_ASSERT(mHandle_Tag==morkHandle_kTag); // should still have correct tag
+}
+
+/*public non-poly*/
+morkHandle::morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool
+ morkHandleFace* ioFace, // must not be nil, cookie for this handle
+ morkObject* ioObject, // must not be nil, the object for this handle
+ mork_magic inMagic) // magic sig to denote specific subclass
+: morkNode(ev, morkUsage::kPool, (nsIMdbHeap*) 0L)
+, mHandle_Tag( 0 )
+, mHandle_Env( ev )
+, mHandle_Face( ioFace )
+, mHandle_Object( 0 )
+, mHandle_Magic( 0 )
+{
+ if ( ioFace && ioObject )
+ {
+ if ( ev->Good() )
+ {
+ mHandle_Tag = morkHandle_kTag;
+ morkObject::SlotStrongObject(ioObject, ev, &mHandle_Object);
+ morkHandle::SlotWeakHandle(this, ev, &ioObject->mObject_Handle);
+ if ( ev->Good() )
+ {
+ mHandle_Magic = inMagic;
+ mNode_Derived = morkDerived_kHandle;
+ }
+ }
+ else
+ ev->CantMakeWhenBadError();
+ }
+ else
+ ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkHandle::CloseHandle(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkObject* obj = mHandle_Object;
+ mork_bool objDidRefSelf = ( obj && obj->mObject_Handle == this );
+ if ( objDidRefSelf )
+ obj->mObject_Handle = 0; // drop the reference
+
+ morkObject::SlotStrongObject((morkObject*) 0, ev, &mHandle_Object);
+ mHandle_Magic = 0;
+ // note mHandle_Tag MUST stay morkHandle_kTag for morkNode::ZapOld()
+ this->MarkShut();
+
+ if ( objDidRefSelf )
+ this->CutWeakRef(ev); // do last, because it might self destroy
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+void morkHandle::NilFactoryError(morkEnv* ev) const
+{
+ ev->NewError("nil mHandle_Factory");
+}
+
+void morkHandle::NilHandleObjectError(morkEnv* ev) const
+{
+ ev->NewError("nil mHandle_Object");
+}
+
+void morkHandle::NonNodeObjectError(morkEnv* ev) const
+{
+ ev->NewError("non-node mHandle_Object");
+}
+
+void morkHandle::NonOpenObjectError(morkEnv* ev) const
+{
+ ev->NewError("non-open mHandle_Object");
+}
+
+void morkHandle::NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const
+{
+ MORK_USED_1(inMagic);
+ ev->NewError("wrong mHandle_Magic");
+}
+
+void morkHandle::NewDownHandleError(morkEnv* ev) const
+{
+ if ( this->IsHandle() )
+ {
+ if ( this->GoodHandleTag() )
+ {
+ if ( this->IsOpenNode() )
+ ev->NewError("unknown down morkHandle error");
+ else
+ this->NonOpenNodeError(ev);
+ }
+ else
+ ev->NewError("wrong morkHandle tag");
+ }
+ else
+ ev->NewError("non morkHandle");
+}
+
+morkObject* morkHandle::GetGoodHandleObject(morkEnv* ev,
+ mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const
+{
+ morkObject* outObject = 0;
+ if ( this->IsHandle() && this->GoodHandleTag() &&
+ ( inClosedOkay || this->IsOpenNode() ) )
+ {
+ if ( !inMagicType || mHandle_Magic == inMagicType )
+ {
+ morkObject* obj = this->mHandle_Object;
+ if ( obj )
+ {
+ if ( obj->IsNode() )
+ {
+ if ( inClosedOkay || obj->IsOpenNode() )
+ {
+ if ( this->IsMutable() || !inMutable )
+ outObject = obj;
+ else
+ this->NonMutableNodeError(ev);
+ }
+ else
+ this->NonOpenObjectError(ev);
+ }
+ else
+ this->NonNodeObjectError(ev);
+ }
+ else if ( !inClosedOkay )
+ this->NilHandleObjectError(ev);
+ }
+ else
+ this->NewBadMagicHandleError(ev, inMagicType);
+ }
+ else
+ this->NewDownHandleError(ev);
+
+ MORK_ASSERT(outObject || inClosedOkay);
+ return outObject;
+}
+
+
+morkEnv*
+morkHandle::CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable,
+ mork_bool inClosedOkay, nsresult* outErr) const
+{
+ morkEnv* outEnv = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkObject* obj = this->GetGoodHandleObject(ev, inMutable,
+ /*magic*/ 0, inClosedOkay);
+ if ( obj )
+ {
+ outEnv = ev;
+ }
+ *outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outEnv || inClosedOkay);
+ return outEnv;
+}
+
+// { ===== begin nsIMdbObject methods =====
+
+// { ----- begin attribute methods -----
+/*virtual*/ nsresult
+morkHandle::Handle_IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+ nsresult outErr = NS_OK;
+ mdb_bool readOnly = mdbBool_kTrue;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ readOnly = mHandle_Object->IsFrozen();
+
+ outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outIsReadonly);
+ if ( outIsReadonly )
+ *outIsReadonly = readOnly;
+
+ return outErr;
+}
+// same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+// } ----- end attribute methods -----
+
+// { ----- begin factory methods -----
+/*virtual*/ nsresult
+morkHandle::Handle_GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbFactory* handle = 0;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ morkFactory* factory = ev->mEnv_Factory;
+ if ( factory )
+ {
+ handle = factory;
+ NS_ADDREF(handle);
+ }
+ else
+ this->NilFactoryError(ev);
+
+ outErr = ev->AsErr();
+ }
+
+ MORK_ASSERT(acqFactory);
+ if ( acqFactory )
+ *acqFactory = handle;
+
+ return outErr;
+}
+// } ----- end factory methods -----
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+/*virtual*/ nsresult
+morkHandle::Handle_GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+ mdb_count* outCount)
+{
+ nsresult outErr = NS_OK;
+ mdb_count count = 0;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ count = this->WeakRefsOnly();
+
+ outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outCount);
+ if ( outCount )
+ *outCount = count;
+
+ return outErr;
+}
+/*virtual*/ nsresult
+morkHandle::Handle_GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+ mdb_count* outCount)
+{
+ nsresult outErr = NS_OK;
+ mdb_count count = 0;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ count = this->StrongRefsOnly();
+
+ outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outCount);
+ if ( outCount )
+ *outCount = count;
+
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkHandle::Handle_AddWeakRef(nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ this->AddWeakRef(ev);
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+}
+/*virtual*/ nsresult
+morkHandle::Handle_AddStrongRef(nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ this->AddStrongRef(ev);
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkHandle::Handle_CutWeakRef(nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ this->CutWeakRef(ev);
+ outErr = ev->AsErr();
+ }
+
+ return outErr;
+}
+/*virtual*/ nsresult
+morkHandle::Handle_CutStrongRef(nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ this->CutStrongRef(ev);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkHandle::Handle_CloseMdbObject(nsIMdbEnv* mev)
+// called at strong refs zero
+{
+ // if only one ref, Handle_CutStrongRef will clean up better.
+ if (mNode_Uses == 1)
+ return Handle_CutStrongRef(mev);
+
+ nsresult outErr = NS_OK;
+
+ if ( this->IsNode() && this->IsOpenNode() )
+ {
+ morkEnv* ev = CanUseHandle(mev, /*inMutable*/ morkBool_kFalse,
+ /*inClosedOkay*/ morkBool_kTrue, &outErr);
+ if ( ev )
+ {
+ morkObject* object = mHandle_Object;
+ if ( object && object->IsNode() && object->IsOpenNode() )
+ object->CloseMorkNode(ev);
+
+ this->CloseMorkNode(ev);
+ outErr = ev->AsErr();
+ }
+ }
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkHandle::Handle_IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+ MORK_USED_1(mev);
+ nsresult outErr = NS_OK;
+
+ MORK_ASSERT(outOpen);
+ if ( outOpen )
+ *outOpen = this->IsOpenNode();
+
+ return outErr;
+}
+// } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkHandle.h b/db/mork/src/morkHandle.h
new file mode 100644
index 000000000..8ef7e9057
--- /dev/null
+++ b/db/mork/src/morkHandle.h
@@ -0,0 +1,176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKHANDLE_
+#define _MORKHANDLE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkPool;
+class morkObject;
+class morkFactory;
+
+#define morkDerived_kHandle /*i*/ 0x486E /* ascii 'Hn' */
+#define morkHandle_kTag 0x68416E44 /* ascii 'hAnD' */
+
+/*| morkHandle:
+|*/
+class morkHandle : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+ mork_u4 mHandle_Tag; // must equal morkHandle_kTag
+ morkEnv* mHandle_Env; // pool that allocated this handle
+ morkHandleFace* mHandle_Face; // cookie from pool containing this
+ morkObject* mHandle_Object; // object this handle wraps for MDB API
+ mork_magic mHandle_Magic; // magic sig different in each subclass
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseHandle() only if open
+ virtual ~morkHandle(); // assert that CloseHandle() executed earlier
+
+public: // morkHandle construction & destruction
+ morkHandle(morkEnv* ev, // note morkUsage is always morkUsage_kPool
+ morkHandleFace* ioFace, // must not be nil, cookie for this handle
+ morkObject* ioObject, // must not be nil, the object for this handle
+ mork_magic inMagic); // magic sig to denote specific subclass
+ void CloseHandle(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkHandle(const morkHandle& other);
+ morkHandle& operator=(const morkHandle& other);
+
+protected: // special case empty construction for morkHandleFrame
+ friend class morkHandleFrame;
+ morkHandle() { }
+
+public: // dynamic type identification
+ mork_bool IsHandle() const
+ { return IsNode() && mNode_Derived == morkDerived_kHandle; }
+// } ===== end morkNode methods =====
+
+public: // morkHandle memory management operators
+ void* operator new(size_t inSize, morkPool& ioPool, morkZone& ioZone, morkEnv* ev) CPP_THROW_NEW
+ { return ioPool.NewHandle(ev, inSize, &ioZone); }
+
+ void* operator new(size_t inSize, morkPool& ioPool, morkEnv* ev) CPP_THROW_NEW
+ { return ioPool.NewHandle(ev, inSize, (morkZone*) 0); }
+
+ void* operator new(size_t inSize, morkHandleFace* ioFace) CPP_THROW_NEW
+ { MORK_USED_1(inSize); return ioFace; }
+
+
+public: // other handle methods
+ mork_bool GoodHandleTag() const
+ { return mHandle_Tag == morkHandle_kTag; }
+
+ void NewBadMagicHandleError(morkEnv* ev, mork_magic inMagic) const;
+ void NewDownHandleError(morkEnv* ev) const;
+ void NilFactoryError(morkEnv* ev) const;
+ void NilHandleObjectError(morkEnv* ev) const;
+ void NonNodeObjectError(morkEnv* ev) const;
+ void NonOpenObjectError(morkEnv* ev) const;
+
+ morkObject* GetGoodHandleObject(morkEnv* ev,
+ mork_bool inMutable, mork_magic inMagicType, mork_bool inClosedOkay) const;
+
+public: // interface supporting mdbObject methods
+
+ morkEnv* CanUseHandle(nsIMdbEnv* mev, mork_bool inMutable,
+ mork_bool inClosedOkay, nsresult* outErr) const;
+
+ // { ----- begin mdbObject style methods -----
+ nsresult Handle_IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+
+ nsresult Handle_GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory);
+ nsresult Handle_GetWeakRefCount(nsIMdbEnv* ev, mdb_count* outCount);
+ nsresult Handle_GetStrongRefCount(nsIMdbEnv* ev, mdb_count* outCount);
+
+ nsresult Handle_AddWeakRef(nsIMdbEnv* ev);
+ nsresult Handle_AddStrongRef(nsIMdbEnv* ev);
+
+ nsresult Handle_CutWeakRef(nsIMdbEnv* ev);
+ nsresult Handle_CutStrongRef(nsIMdbEnv* ev);
+
+ nsresult Handle_CloseMdbObject(nsIMdbEnv* ev);
+ nsresult Handle_IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen);
+ // } ----- end mdbObject style methods -----
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakHandle(morkHandle* me,
+ morkEnv* ev, morkHandle** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongHandle(morkHandle* me,
+ morkEnv* ev, morkHandle** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+#define morkHandleFrame_kPadSlotCount 4
+
+/*| morkHandleFrame: an object format used for allocating and maintaining
+**| instances of morkHandle, with leading slots used to maintain this in a
+**| linked list, and following slots to provide extra footprint that might
+**| be needed by any morkHandle subclasses that include very little extra
+**| space (by virtue of the fact that each morkHandle subclass is expected
+**| to multiply inherit from another base class that has only abstract methods
+**| for space overhead related only to some vtable representation).
+|*/
+class morkHandleFrame {
+public:
+ morkLink mHandleFrame_Link; // list storage without trampling Handle
+ morkHandle mHandleFrame_Handle;
+ mork_ip mHandleFrame_Padding[ morkHandleFrame_kPadSlotCount ];
+
+public:
+ morkHandle* AsHandle() { return &mHandleFrame_Handle; }
+
+ morkHandleFrame() {} // actually, morkHandleFrame never gets constructed
+
+private: // copying is not allowed
+ morkHandleFrame(const morkHandleFrame& other);
+ morkHandleFrame& operator=(const morkHandleFrame& other);
+};
+
+#define morkHandleFrame_kHandleOffset \
+ mork_OffsetOf(morkHandleFrame,mHandleFrame_Handle)
+
+#define morkHandle_AsHandleFrame(h) ((h)->mHandle_Block , \
+ ((morkHandleFrame*) (((mork_u1*)(h)) - morkHandleFrame_kHandleOffset)))
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKHANDLE_ */
diff --git a/db/mork/src/morkIntMap.cpp b/db/mork/src/morkIntMap.cpp
new file mode 100644
index 000000000..9164b021d
--- /dev/null
+++ b/db/mork/src/morkIntMap.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkIntMap::CloseMorkNode(morkEnv* ev) // CloseIntMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseIntMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkIntMap::~morkIntMap() // assert CloseIntMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkIntMap::morkIntMap(morkEnv* ev,
+ const morkUsage& inUsage, mork_size inValSize,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges)
+: morkMap(ev, inUsage, ioHeap, sizeof(mork_u4), inValSize,
+ morkIntMap_kStartSlotCount, ioSlotHeap, inHoldChanges)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kIntMap;
+}
+
+/*public non-poly*/ void
+morkIntMap::CloseIntMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+morkIntMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+ MORK_USED_1(ev);
+ return *((const mork_u4*) inKeyA) == *((const mork_u4*) inKeyB);
+}
+
+/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey)
+morkIntMap::Hash(morkEnv* ev, const void* inKey) const
+{
+ MORK_USED_1(ev);
+ return *((const mork_u4*) inKey);
+}
+// } ===== end morkMap poly interface =====
+
+mork_bool
+morkIntMap::AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress)
+ // the AddInt() method return value equals ev->Good().
+{
+ if ( ev->Good() )
+ {
+ this->Put(ev, &inKey, &ioAddress,
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+ }
+
+ return ev->Good();
+}
+
+mork_bool
+morkIntMap::CutInt(morkEnv* ev, mork_u4 inKey)
+{
+ return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+ (mork_change**) 0);
+}
+
+void*
+morkIntMap::GetInt(morkEnv* ev, mork_u4 inKey)
+ // Note the returned val does NOT have an increase in refcount for this.
+{
+ void* val = 0; // old val in the map
+ this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0);
+
+ return val;
+}
+
+mork_bool
+morkIntMap::HasInt(morkEnv* ev, mork_u4 inKey)
+ // Note the returned val does NOT have an increase in refcount for this.
+{
+ return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+ (mork_change**) 0);
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#ifdef MORK_POINTER_MAP_IMPL
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPointerMap::CloseMorkNode(morkEnv* ev) // ClosePointerMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->ClosePointerMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkPointerMap::~morkPointerMap() // assert ClosePointerMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkPointerMap::morkPointerMap(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkMap(ev, inUsage, ioHeap, sizeof(void*), sizeof(void*),
+ morkPointerMap_kStartSlotCount, ioSlotHeap,
+ /*inHoldChanges*/ morkBool_kFalse)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kPointerMap;
+}
+
+/*public non-poly*/ void
+morkPointerMap::ClosePointerMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool // *((void**) inKeyA) == *((void**) inKeyB)
+morkPointerMap::Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const
+{
+ MORK_USED_1(ev);
+ return *((const void**) inKeyA) == *((const void**) inKeyB);
+}
+
+/*virtual*/ mork_u4 // some integer function of *((mork_u4*) inKey)
+morkPointerMap::Hash(morkEnv* ev, const void* inKey) const
+{
+ MORK_USED_1(ev);
+ return *((const mork_u4*) inKey);
+}
+// } ===== end morkMap poly interface =====
+
+mork_bool
+morkPointerMap::AddPointer(morkEnv* ev, void* inKey, void* ioAddress)
+ // the AddPointer() method return value equals ev->Good().
+{
+ if ( ev->Good() )
+ {
+ this->Put(ev, &inKey, &ioAddress,
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+ }
+
+ return ev->Good();
+}
+
+mork_bool
+morkPointerMap::CutPointer(morkEnv* ev, void* inKey)
+{
+ return this->Cut(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+ (mork_change**) 0);
+}
+
+void*
+morkPointerMap::GetPointer(morkEnv* ev, void* inKey)
+ // Note the returned val does NOT have an increase in refcount for this.
+{
+ void* val = 0; // old val in the map
+ this->Get(ev, &inKey, /*key*/ (void*) 0, &val, (mork_change**) 0);
+
+ return val;
+}
+
+mork_bool
+morkPointerMap::HasPointer(morkEnv* ev, void* inKey)
+ // Note the returned val does NOT have an increase in refcount for this.
+{
+ return this->Get(ev, &inKey, /*key*/ (void*) 0, /*val*/ (void*) 0,
+ (mork_change**) 0);
+}
+#endif /*MORK_POINTER_MAP_IMPL*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkIntMap.h b/db/mork/src/morkIntMap.h
new file mode 100644
index 000000000..543f7e59a
--- /dev/null
+++ b/db/mork/src/morkIntMap.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKINTMAP_
+#define _MORKINTMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kIntMap /*i*/ 0x694D /* ascii 'iM' */
+
+#define morkIntMap_kStartSlotCount 256
+
+/*| morkIntMap: maps mork_token -> morkNode
+|*/
+class morkIntMap : public morkMap { // for mapping tokens to maps
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseIntMap() only if open
+ virtual ~morkIntMap(); // assert that CloseIntMap() executed earlier
+
+public: // morkMap construction & destruction
+
+ // keySize for morkIntMap equals sizeof(mork_u4)
+ morkIntMap(morkEnv* ev, const morkUsage& inUsage, mork_size inValSize,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges);
+ void CloseIntMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsIntMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kIntMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // *((mork_u4*) inKeyA) == *((mork_u4*) inKeyB)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override;
+
+ virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+ Hash(morkEnv* ev, const void* inKey) const override;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+ mork_bool AddInt(morkEnv* ev, mork_u4 inKey, void* ioAddress);
+ // the AddInt() boolean return equals ev->Good().
+
+ mork_bool CutInt(morkEnv* ev, mork_u4 inKey);
+ // The CutInt() boolean return indicates whether removal happened.
+
+ void* GetInt(morkEnv* ev, mork_u4 inKey);
+ // Note the returned node does NOT have an increase in refcount for this.
+
+ mork_bool HasInt(morkEnv* ev, mork_u4 inKey);
+ // Note the returned node does NOT have an increase in refcount for this.
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#ifdef MORK_POINTER_MAP_IMPL
+
+#define morkDerived_kPointerMap /*i*/ 0x704D /* ascii 'pM' */
+
+#define morkPointerMap_kStartSlotCount 256
+
+/*| morkPointerMap: maps void* -> void*
+**|
+**| This pointer map class is equivalent to morkIntMap when sizeof(mork_u4)
+**| equals sizeof(void*). However, when these two sizes are different,
+**| then we cannot use the same hash table structure very easily without
+**| being very careful about the size and usage assumptions of those
+**| clients using the smaller data type. So we just go ahead and use
+**| morkPointerMap for hash tables using pointer key types.
+|*/
+class morkPointerMap : public morkMap { // for mapping tokens to maps
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // ClosePointerMap() only if open
+ virtual ~morkPointerMap(); // assert that ClosePointerMap() executed earlier
+
+public: // morkMap construction & destruction
+
+ // keySize for morkPointerMap equals sizeof(mork_u4)
+ morkPointerMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void ClosePointerMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsPointerMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kPointerMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // *((void**) inKeyA) == *((void**) inKeyB)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const;
+
+ virtual mork_u4 // some integer function of *((mork_u4*) inKey)
+ Hash(morkEnv* ev, const void* inKey) const;
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+ mork_bool AddPointer(morkEnv* ev, void* inKey, void* ioAddress);
+ // the AddPointer() boolean return equals ev->Good().
+
+ mork_bool CutPointer(morkEnv* ev, void* inKey);
+ // The CutPointer() boolean return indicates whether removal happened.
+
+ void* GetPointer(morkEnv* ev, void* inKey);
+ // Note the returned node does NOT have an increase in refcount for this.
+
+ mork_bool HasPointer(morkEnv* ev, void* inKey);
+ // Note the returned node does NOT have an increase in refcount for this.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakIntMap(morkIntMap* me,
+ morkEnv* ev, morkIntMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongIntMap(morkIntMap* me,
+ morkEnv* ev, morkIntMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+};
+#endif /*MORK_POINTER_MAP_IMPL*/
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKINTMAP_ */
diff --git a/db/mork/src/morkMap.cpp b/db/mork/src/morkMap.cpp
new file mode 100644
index 000000000..ca6b27827
--- /dev/null
+++ b/db/mork/src/morkMap.cpp
@@ -0,0 +1,953 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is mostly a port to C++ from public domain IronDoc C sources.
+// Note many code comments here come verbatim from cut-and-pasted IronDoc.
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+
+class morkHashArrays {
+public:
+ nsIMdbHeap* mHashArrays_Heap; // copy of mMap_Heap
+ mork_count mHashArrays_Slots; // copy of mMap_Slots
+
+ mork_u1* mHashArrays_Keys; // copy of mMap_Keys
+ mork_u1* mHashArrays_Vals; // copy of mMap_Vals
+ morkAssoc* mHashArrays_Assocs; // copy of mMap_Assocs
+ mork_change* mHashArrays_Changes; // copy of mMap_Changes
+ morkAssoc** mHashArrays_Buckets; // copy of mMap_Buckets
+ morkAssoc* mHashArrays_FreeList; // copy of mMap_FreeList
+
+public:
+ void finalize(morkEnv* ev);
+};
+
+void morkHashArrays::finalize(morkEnv* ev)
+{
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ nsIMdbHeap* heap = mHashArrays_Heap;
+
+ if ( heap )
+ {
+ if ( mHashArrays_Keys )
+ heap->Free(menv, mHashArrays_Keys);
+ if ( mHashArrays_Vals )
+ heap->Free(menv, mHashArrays_Vals);
+ if ( mHashArrays_Assocs )
+ heap->Free(menv, mHashArrays_Assocs);
+ if ( mHashArrays_Changes )
+ heap->Free(menv, mHashArrays_Changes);
+ if ( mHashArrays_Buckets )
+ heap->Free(menv, mHashArrays_Buckets);
+ }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkMap::~morkMap() // assert CloseMap() executed earlier
+{
+ MORK_ASSERT(mMap_FreeList==0);
+ MORK_ASSERT(mMap_Buckets==0);
+ MORK_ASSERT(mMap_Keys==0);
+ MORK_ASSERT(mMap_Vals==0);
+ MORK_ASSERT(mMap_Changes==0);
+ MORK_ASSERT(mMap_Assocs==0);
+}
+
+/*public non-poly*/ void
+morkMap::CloseMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ nsIMdbHeap* heap = mMap_Heap;
+ if ( heap ) /* need to free the arrays? */
+ {
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+
+ if ( mMap_Keys )
+ heap->Free(menv, mMap_Keys);
+
+ if ( mMap_Vals )
+ heap->Free(menv, mMap_Vals);
+
+ if ( mMap_Assocs )
+ heap->Free(menv, mMap_Assocs);
+
+ if ( mMap_Buckets )
+ heap->Free(menv, mMap_Buckets);
+
+ if ( mMap_Changes )
+ heap->Free(menv, mMap_Changes);
+ }
+ mMap_Keys = 0;
+ mMap_Vals = 0;
+ mMap_Buckets = 0;
+ mMap_Assocs = 0;
+ mMap_Changes = 0;
+ mMap_FreeList = 0;
+ MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm));
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+void
+morkMap::clear_map(morkEnv* ev, nsIMdbHeap* ioSlotHeap)
+{
+ mMap_Tag = 0;
+ mMap_Seed = 0;
+ mMap_Slots = 0;
+ mMap_Fill = 0;
+ mMap_Keys = 0;
+ mMap_Vals = 0;
+ mMap_Assocs = 0;
+ mMap_Changes = 0;
+ mMap_Buckets = 0;
+ mMap_FreeList = 0;
+ MORK_MEMSET(&mMap_Form, 0, sizeof(morkMapForm));
+
+ mMap_Heap = 0;
+ if ( ioSlotHeap )
+ {
+ nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mMap_Heap);
+ }
+ else
+ ev->NilPointerError();
+}
+
+morkMap::morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_size inKeySize, mork_size inValSize,
+ mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges)
+: morkNode(ev, inUsage, ioHeap)
+, mMap_Heap( 0 )
+{
+ if ( ev->Good() )
+ {
+ this->clear_map(ev, ioSlotHeap);
+ if ( ev->Good() )
+ {
+ mMap_Form.mMapForm_HoldChanges = inHoldChanges;
+
+ mMap_Form.mMapForm_KeySize = inKeySize;
+ mMap_Form.mMapForm_ValSize = inValSize;
+ mMap_Form.mMapForm_KeyIsIP = ( inKeySize == sizeof(mork_ip) );
+ mMap_Form.mMapForm_ValIsIP = ( inValSize == sizeof(mork_ip) );
+
+ this->InitMap(ev, inSlots);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kMap;
+ }
+ }
+}
+
+void
+morkMap::NewIterOutOfSyncError(morkEnv* ev)
+{
+ ev->NewError("map iter out of sync");
+}
+
+void morkMap::NewBadMapError(morkEnv* ev)
+{
+ ev->NewError("bad morkMap tag");
+}
+
+void morkMap::NewSlotsUnderflowWarning(morkEnv* ev)
+{
+ ev->NewWarning("member count underflow");
+}
+
+void morkMap::InitMap(morkEnv* ev, mork_size inSlots)
+{
+ if ( ev->Good() )
+ {
+ morkHashArrays old;
+ // MORK_MEMCPY(&mMap_Form, &inForm, sizeof(morkMapForm));
+ if ( inSlots < 3 ) /* requested capacity absurdly small? */
+ inSlots = 3; /* bump it up to a minimum practical level */
+ else if ( inSlots > (128 * 1024) ) /* requested slots absurdly big? */
+ inSlots = (128 * 1024); /* decrease it to a maximum practical level */
+
+ if ( this->new_arrays(ev, &old, inSlots) )
+ mMap_Tag = morkMap_kTag;
+
+ MORK_MEMSET(&old, 0, sizeof(morkHashArrays)); // do NOT finalize
+ }
+}
+
+morkAssoc**
+morkMap::find(morkEnv* ev, const void* inKey, mork_u4 inHash) const
+{
+ mork_u1* keys = mMap_Keys;
+ mork_num keySize = this->FormKeySize();
+ // morkMap_mEqual equal = this->FormEqual();
+
+ morkAssoc** ref = mMap_Buckets + (inHash % mMap_Slots);
+ morkAssoc* assoc = *ref;
+ while ( assoc ) /* look at another assoc in the bucket? */
+ {
+ mork_pos i = assoc - mMap_Assocs; /* index of this assoc */
+ if ( this->Equal(ev, keys + (i * keySize), inKey) ) /* found? */
+ return ref;
+
+ ref = &assoc->mAssoc_Next; /* consider next assoc slot in bucket */
+ assoc = *ref; /* if this is null, then we are done */
+ }
+ return 0;
+}
+
+/*| get_assoc: read the key and/or value at index inPos
+|*/
+void
+morkMap::get_assoc(void* outKey, void* outVal, mork_pos inPos) const
+{
+ mork_num valSize = this->FormValSize();
+ if ( valSize && outVal ) /* map holds values? caller wants the value? */
+ {
+ const mork_u1* value = mMap_Vals + (valSize * inPos);
+ if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */
+ *((mork_ip*) outVal) = *((const mork_ip*) value);
+ else
+ MORK_MEMCPY(outVal, value, valSize);
+ }
+ if ( outKey ) /* caller wants the key? */
+ {
+ mork_num keySize = this->FormKeySize();
+ const mork_u1* key = mMap_Keys + (keySize * inPos);
+ if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */
+ *((mork_ip*) outKey) = *((const mork_ip*) key);
+ else
+ MORK_MEMCPY(outKey, key, keySize);
+ }
+}
+
+/*| put_assoc: write the key and/or value at index inPos
+|*/
+void
+morkMap::put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const
+{
+ mork_num valSize = this->FormValSize();
+ if ( valSize && inVal ) /* map holds values? caller sends a value? */
+ {
+ mork_u1* value = mMap_Vals + (valSize * inPos);
+ if ( valSize == sizeof(mork_ip) && this->FormValIsIP() ) /* ip case? */
+ *((mork_ip*) value) = *((const mork_ip*) inVal);
+ else
+ MORK_MEMCPY(value, inVal, valSize);
+ }
+ if ( inKey ) /* caller sends a key? */
+ {
+ mork_num keySize = this->FormKeySize();
+ mork_u1* key = mMap_Keys + (keySize * inPos);
+ if ( keySize == sizeof(mork_ip) && this->FormKeyIsIP() ) /* ip case? */
+ *((mork_ip*) key) = *((const mork_ip*) inKey);
+ else
+ MORK_MEMCPY(key, inKey, keySize);
+ }
+}
+
+void*
+morkMap::clear_alloc(morkEnv* ev, mork_size inSize)
+{
+ void* p = 0;
+ nsIMdbHeap* heap = mMap_Heap;
+ if ( heap )
+ {
+ if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p )
+ {
+ MORK_MEMSET(p, 0, inSize);
+ return p;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return (void*) 0;
+}
+
+void*
+morkMap::alloc(morkEnv* ev, mork_size inSize)
+{
+ void* p = 0;
+ nsIMdbHeap* heap = mMap_Heap;
+ if ( heap )
+ {
+ if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p )
+ return p;
+ }
+ else
+ ev->NilPointerError();
+
+ return (void*) 0;
+}
+
+/*| new_keys: allocate an array of inSlots new keys filled with zero.
+|*/
+mork_u1*
+morkMap::new_keys(morkEnv* ev, mork_num inSlots)
+{
+ mork_num size = inSlots * this->FormKeySize();
+ return (mork_u1*) this->clear_alloc(ev, size);
+}
+
+/*| new_values: allocate an array of inSlots new values filled with zero.
+**| When values are zero sized, we just return a null pointer.
+|*/
+mork_u1*
+morkMap::new_values(morkEnv* ev, mork_num inSlots)
+{
+ mork_u1* values = 0;
+ mork_num size = inSlots * this->FormValSize();
+ if ( size )
+ values = (mork_u1*) this->clear_alloc(ev, size);
+ return values;
+}
+
+mork_change*
+morkMap::new_changes(morkEnv* ev, mork_num inSlots)
+{
+ mork_change* changes = 0;
+ mork_num size = inSlots * sizeof(mork_change);
+ if ( size && mMap_Form.mMapForm_HoldChanges )
+ changes = (mork_change*) this->clear_alloc(ev, size);
+ return changes;
+}
+
+/*| new_buckets: allocate an array of inSlots new buckets filled with zero.
+|*/
+morkAssoc**
+morkMap::new_buckets(morkEnv* ev, mork_num inSlots)
+{
+ mork_num size = inSlots * sizeof(morkAssoc*);
+ return (morkAssoc**) this->clear_alloc(ev, size);
+}
+
+/*| new_assocs: allocate an array of inSlots new assocs, with each assoc
+**| linked together in a list with the first array element at the list head
+**| and the last element at the list tail. (morkMap::grow() needs this.)
+|*/
+morkAssoc*
+morkMap::new_assocs(morkEnv* ev, mork_num inSlots)
+{
+ mork_num size = inSlots * sizeof(morkAssoc);
+ morkAssoc* assocs = (morkAssoc*) this->alloc(ev, size);
+ if ( assocs ) /* able to allocate the array? */
+ {
+ morkAssoc* a = assocs + (inSlots - 1); /* the last array element */
+ a->mAssoc_Next = 0; /* terminate tail element of the list with null */
+ while ( --a >= assocs ) /* another assoc to link into the list? */
+ a->mAssoc_Next = a + 1; /* each points to the following assoc */
+ }
+ return assocs;
+}
+
+mork_bool
+morkMap::new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots)
+{
+ mork_bool outNew = morkBool_kFalse;
+
+ /* see if we can allocate all the new arrays before we go any further: */
+ morkAssoc** newBuckets = this->new_buckets(ev, inSlots);
+ morkAssoc* newAssocs = this->new_assocs(ev, inSlots);
+ mork_u1* newKeys = this->new_keys(ev, inSlots);
+ mork_u1* newValues = this->new_values(ev, inSlots);
+ mork_change* newChanges = this->new_changes(ev, inSlots);
+
+ /* it is okay for newChanges to be null when changes are not held: */
+ mork_bool okayChanges = ( newChanges || !this->FormHoldChanges() );
+
+ /* it is okay for newValues to be null when values are zero sized: */
+ mork_bool okayValues = ( newValues || !this->FormValSize() );
+
+ if ( newBuckets && newAssocs && newKeys && okayChanges && okayValues )
+ {
+ outNew = morkBool_kTrue; /* yes, we created all the arrays we need */
+
+ /* init the old hashArrays with slots from this hash table: */
+ old->mHashArrays_Heap = mMap_Heap;
+
+ old->mHashArrays_Slots = mMap_Slots;
+ old->mHashArrays_Keys = mMap_Keys;
+ old->mHashArrays_Vals = mMap_Vals;
+ old->mHashArrays_Assocs = mMap_Assocs;
+ old->mHashArrays_Buckets = mMap_Buckets;
+ old->mHashArrays_Changes = mMap_Changes;
+
+ /* now replace all our array slots with the new structures: */
+ ++mMap_Seed; /* note the map is now changed */
+ mMap_Keys = newKeys;
+ mMap_Vals = newValues;
+ mMap_Buckets = newBuckets;
+ mMap_Assocs = newAssocs;
+ mMap_FreeList = newAssocs; /* all are free to start with */
+ mMap_Changes = newChanges;
+ mMap_Slots = inSlots;
+ }
+ else /* free the partial set of arrays that were actually allocated */
+ {
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ nsIMdbHeap* heap = mMap_Heap;
+ if ( newBuckets )
+ heap->Free(menv, newBuckets);
+ if ( newAssocs )
+ heap->Free(menv, newAssocs);
+ if ( newKeys )
+ heap->Free(menv, newKeys);
+ if ( newValues )
+ heap->Free(menv, newValues);
+ if ( newChanges )
+ heap->Free(menv, newChanges);
+
+ MORK_MEMSET(old, 0, sizeof(morkHashArrays));
+ }
+
+ return outNew;
+}
+
+/*| grow: make the map arrays bigger by 33%. The old map is completely
+**| full, or else we would not have called grow() to get more space. This
+**| means the free list is empty, and also means every old key and value is in
+**| use in the old arrays. So every key and value must be copied to the new
+**| arrays, and since they are contiguous in the old arrays, we can efficiently
+**| bitwise copy them in bulk from the old arrays to the new arrays, without
+**| paying any attention to the structure of the old arrays.
+**|
+**|| The content of the old bucket and assoc arrays need not be copied because
+**| this information is going to be completely rebuilt by rehashing all the
+**| keys into their new buckets, given the new larger map capacity. The new
+**| bucket array from new_arrays() is assumed to contain all zeroes, which is
+**| necessary to ensure the lists in each bucket stay null terminated as we
+**| build the new linked lists. (Note no old bucket ordering is preserved.)
+**|
+**|| If the old capacity is N, then in the new arrays the assocs with indexes
+**| from zero to N-1 are still allocated and must be rehashed into the map.
+**| The new free list contains all the following assocs, starting with the new
+**| assoc link at index N. (We call the links in the link array "assocs"
+**| because allocating a link is the same as allocating the key/value pair
+**| with the same index as the link.)
+**|
+**|| The new free list is initialized simply by pointing at the first new link
+**| in the assoc array after the size of the old assoc array. This assumes
+**| that FeHashTable_new_arrays() has already linked all the new assocs into a
+**| list with the first at the head of the list and the last at the tail of the
+**| list. So by making the free list point to the first of the new uncopied
+**| assocs, the list is already well-formed.
+|*/
+mork_bool morkMap::grow(morkEnv* ev)
+{
+ if ( mMap_Heap ) /* can we grow the map? */
+ {
+ mork_num newSlots = (mMap_Slots * 2); /* +100% */
+ morkHashArrays old; /* a place to temporarily hold all the old arrays */
+ if ( this->new_arrays(ev, &old, newSlots) ) /* have more? */
+ {
+ // morkMap_mHash hash = this->FormHash(); /* for terse loop use */
+
+ /* figure out the bulk volume sizes of old keys and values to move: */
+ mork_num oldSlots = old.mHashArrays_Slots; /* number of old assocs */
+ mork_num keyBulk = oldSlots * this->FormKeySize(); /* key volume */
+ mork_num valBulk = oldSlots * this->FormValSize(); /* values */
+
+ /* convenient variables for new arrays that need to be rehashed: */
+ morkAssoc** newBuckets = mMap_Buckets; /* new all zeroes */
+ morkAssoc* newAssocs = mMap_Assocs; /* hash into buckets */
+ morkAssoc* newFreeList = newAssocs + oldSlots; /* new room is free */
+ mork_u1* key = mMap_Keys; /* the first key to rehash */
+ --newAssocs; /* back up one before preincrement used in while loop */
+
+ /* move all old keys and values to the new arrays: */
+ MORK_MEMCPY(mMap_Keys, old.mHashArrays_Keys, keyBulk);
+ if ( valBulk ) /* are values nonzero sized? */
+ MORK_MEMCPY(mMap_Vals, old.mHashArrays_Vals, valBulk);
+
+ mMap_FreeList = newFreeList; /* remaining assocs are free */
+
+ while ( ++newAssocs < newFreeList ) /* rehash another old assoc? */
+ {
+ morkAssoc** top = newBuckets + (this->Hash(ev, key) % newSlots);
+ key += this->FormKeySize(); /* the next key to rehash */
+ newAssocs->mAssoc_Next = *top; /* link to prev bucket top */
+ *top = newAssocs; /* push assoc on top of bucket */
+ }
+ ++mMap_Seed; /* note the map has changed */
+ old.finalize(ev); /* remember to free the old arrays */
+ }
+ }
+ else ev->OutOfMemoryError();
+
+ return ev->Good();
+}
+
+
+mork_bool
+morkMap::Put(morkEnv* ev, const void* inKey, const void* inVal,
+ void* outKey, void* outVal, mork_change** outChange)
+{
+ mork_bool outPut = morkBool_kFalse;
+
+ if ( this->GoodMap() ) /* looks good? */
+ {
+ mork_u4 hash = this->Hash(ev, inKey);
+ morkAssoc** ref = this->find(ev, inKey, hash);
+ if ( ref ) /* assoc was found? reuse an existing assoc slot? */
+ {
+ outPut = morkBool_kTrue; /* inKey was indeed already inside the map */
+ }
+ else /* assoc not found -- need to allocate a new assoc slot */
+ {
+ morkAssoc* assoc = this->pop_free_assoc();
+ if ( !assoc ) /* no slots remaining in free list? must grow map? */
+ {
+ if ( this->grow(ev) ) /* successfully made map larger? */
+ assoc = this->pop_free_assoc();
+ }
+ if ( assoc ) /* allocated new assoc without error? */
+ {
+ ref = mMap_Buckets + (hash % mMap_Slots);
+ assoc->mAssoc_Next = *ref; /* link to prev bucket top */
+ *ref = assoc; /* push assoc on top of bucket */
+
+ ++mMap_Fill; /* one more member in the collection */
+ ++mMap_Seed; /* note the map has changed */
+ }
+ }
+ if ( ref ) /* did not have an error during possible growth? */
+ {
+ mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */
+ if ( outPut && (outKey || outVal) ) /* copy old before cobbering? */
+ this->get_assoc(outKey, outVal, i);
+
+ this->put_assoc(inKey, inVal, i);
+ ++mMap_Seed; /* note the map has changed */
+
+ if ( outChange )
+ {
+ if ( mMap_Changes )
+ *outChange = mMap_Changes + i;
+ else
+ *outChange = this->FormDummyChange();
+ }
+ }
+ }
+ else this->NewBadMapError(ev);
+
+ return outPut;
+}
+
+mork_num
+morkMap::CutAll(morkEnv* ev)
+{
+ mork_num outCutAll = 0;
+
+ if ( this->GoodMap() ) /* map looks good? */
+ {
+ mork_num slots = mMap_Slots;
+ morkAssoc* before = mMap_Assocs - 1; /* before first member */
+ morkAssoc* assoc = before + slots; /* the very last member */
+
+ ++mMap_Seed; /* note the map is changed */
+
+ /* make the assoc array a linked list headed by first & tailed by last: */
+ assoc->mAssoc_Next = 0; /* null terminate the new free list */
+ while ( --assoc > before ) /* another assoc to link into the list? */
+ assoc->mAssoc_Next = assoc + 1;
+ mMap_FreeList = mMap_Assocs; /* all are free */
+
+ outCutAll = mMap_Fill; /* we'll cut all of them of course */
+
+ mMap_Fill = 0; /* the map is completely empty */
+ }
+ else this->NewBadMapError(ev);
+
+ return outCutAll;
+}
+
+mork_bool
+morkMap::Cut(morkEnv* ev, const void* inKey,
+ void* outKey, void* outVal, mork_change** outChange)
+{
+ mork_bool outCut = morkBool_kFalse;
+
+ if ( this->GoodMap() ) /* looks good? */
+ {
+ mork_u4 hash = this->Hash(ev, inKey);
+ morkAssoc** ref = this->find(ev, inKey, hash);
+ if ( ref ) /* found an assoc for key? */
+ {
+ outCut = morkBool_kTrue;
+ morkAssoc* assoc = *ref;
+ mork_pos i = assoc - mMap_Assocs; /* index of assoc */
+ if ( outKey || outVal )
+ this->get_assoc(outKey, outVal, i);
+
+ *ref = assoc->mAssoc_Next; /* unlink the found assoc */
+ this->push_free_assoc(assoc); /* and put it in free list */
+
+ if ( outChange )
+ {
+ if ( mMap_Changes )
+ *outChange = mMap_Changes + i;
+ else
+ *outChange = this->FormDummyChange();
+ }
+
+ ++mMap_Seed; /* note the map has changed */
+ if ( mMap_Fill ) /* the count shows nonzero members? */
+ --mMap_Fill; /* one less member in the collection */
+ else
+ this->NewSlotsUnderflowWarning(ev);
+ }
+ }
+ else this->NewBadMapError(ev);
+
+ return outCut;
+}
+
+mork_bool
+morkMap::Get(morkEnv* ev, const void* inKey,
+ void* outKey, void* outVal, mork_change** outChange)
+{
+ mork_bool outGet = morkBool_kFalse;
+
+ if ( this->GoodMap() ) /* looks good? */
+ {
+ mork_u4 hash = this->Hash(ev, inKey);
+ morkAssoc** ref = this->find(ev, inKey, hash);
+ if ( ref ) /* found an assoc for inKey? */
+ {
+ mork_pos i = (*ref) - mMap_Assocs; /* index of assoc */
+ outGet = morkBool_kTrue;
+ this->get_assoc(outKey, outVal, i);
+ if ( outChange )
+ {
+ if ( mMap_Changes )
+ *outChange = mMap_Changes + i;
+ else
+ *outChange = this->FormDummyChange();
+ }
+ }
+ }
+ else this->NewBadMapError(ev);
+
+ return outGet;
+}
+
+
+morkMapIter::morkMapIter( )
+: mMapIter_Map( 0 )
+, mMapIter_Seed( 0 )
+
+, mMapIter_Bucket( 0 )
+, mMapIter_AssocRef( 0 )
+, mMapIter_Assoc( 0 )
+, mMapIter_Next( 0 )
+{
+}
+
+void
+morkMapIter::InitMapIter(morkEnv* ev, morkMap* ioMap)
+{
+ mMapIter_Map = 0;
+ mMapIter_Seed = 0;
+
+ mMapIter_Bucket = 0;
+ mMapIter_AssocRef = 0;
+ mMapIter_Assoc = 0;
+ mMapIter_Next = 0;
+
+ if ( ioMap )
+ {
+ if ( ioMap->GoodMap() )
+ {
+ mMapIter_Map = ioMap;
+ mMapIter_Seed = ioMap->mMap_Seed;
+ }
+ else ioMap->NewBadMapError(ev);
+ }
+ else ev->NilPointerError();
+}
+
+morkMapIter::morkMapIter(morkEnv* ev, morkMap* ioMap)
+: mMapIter_Map( 0 )
+, mMapIter_Seed( 0 )
+
+, mMapIter_Bucket( 0 )
+, mMapIter_AssocRef( 0 )
+, mMapIter_Assoc( 0 )
+, mMapIter_Next( 0 )
+{
+ if ( ioMap )
+ {
+ if ( ioMap->GoodMap() )
+ {
+ mMapIter_Map = ioMap;
+ mMapIter_Seed = ioMap->mMap_Seed;
+ }
+ else ioMap->NewBadMapError(ev);
+ }
+ else ev->NilPointerError();
+}
+
+void
+morkMapIter::CloseMapIter(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mMapIter_Map = 0;
+ mMapIter_Seed = 0;
+
+ mMapIter_Bucket = 0;
+ mMapIter_AssocRef = 0;
+ mMapIter_Assoc = 0;
+ mMapIter_Next = 0;
+}
+
+mork_change*
+morkMapIter::First(morkEnv* ev, void* outKey, void* outVal)
+{
+ mork_change* outFirst = 0;
+
+ morkMap* map = mMapIter_Map;
+
+ if ( map && map->GoodMap() ) /* map looks good? */
+ {
+ morkAssoc** bucket = map->mMap_Buckets;
+ morkAssoc** end = bucket + map->mMap_Slots; /* one past last */
+
+ mMapIter_Seed = map->mMap_Seed; /* sync the seeds */
+
+ while ( bucket < end ) /* another bucket in which to look for assocs? */
+ {
+ morkAssoc* assoc = *bucket++;
+ if ( assoc ) /* found the first map assoc in use? */
+ {
+ mork_pos i = assoc - map->mMap_Assocs;
+ mork_change* c = map->mMap_Changes;
+ outFirst = ( c )? (c + i) : map->FormDummyChange();
+
+ mMapIter_Assoc = assoc; /* current assoc in iteration */
+ mMapIter_Next = assoc->mAssoc_Next; /* more in bucket */
+ mMapIter_Bucket = --bucket; /* bucket for this assoc */
+ mMapIter_AssocRef = bucket; /* slot referencing assoc */
+
+ map->get_assoc(outKey, outVal, i);
+
+ break; /* end while loop */
+ }
+ }
+ }
+ else map->NewBadMapError(ev);
+
+ return outFirst;
+}
+
+mork_change*
+morkMapIter::Next(morkEnv* ev, void* outKey, void* outVal)
+{
+ mork_change* outNext = 0;
+
+ morkMap* map = mMapIter_Map;
+
+ if ( map && map->GoodMap() ) /* map looks good? */
+ {
+ if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+ {
+ morkAssoc* here = mMapIter_Assoc; /* current assoc */
+ if ( here ) /* iteration is not yet concluded? */
+ {
+ morkAssoc* next = mMapIter_Next;
+ morkAssoc* assoc = next; /* default new mMapIter_Assoc */
+ if ( next ) /* there are more assocs in the same bucket after Here? */
+ {
+ morkAssoc** ref = mMapIter_AssocRef;
+
+ /* (*HereRef) equals Here, except when Here has been cut, after
+ ** which (*HereRef) always equals Next. So if (*HereRef) is not
+ ** equal to Next, then HereRef still needs to be updated to point
+ ** somewhere else other than Here. Otherwise it is fine.
+ */
+ if ( *ref != next ) /* Here was not cut? must update HereRef? */
+ mMapIter_AssocRef = &here->mAssoc_Next;
+
+ mMapIter_Next = next->mAssoc_Next;
+ }
+ else /* look for the next assoc in the next nonempty bucket */
+ {
+ morkAssoc** bucket = map->mMap_Buckets;
+ morkAssoc** end = bucket + map->mMap_Slots; /* beyond */
+ mMapIter_Assoc = 0; /* default to no more assocs */
+ bucket = mMapIter_Bucket; /* last exhausted bucket */
+ mMapIter_Assoc = 0; /* default to iteration ended */
+
+ while ( ++bucket < end ) /* another bucket to search for assocs? */
+ {
+ assoc = *bucket;
+ if ( assoc ) /* found another map assoc in use? */
+ {
+ mMapIter_Bucket = bucket;
+ mMapIter_AssocRef = bucket; /* ref to assoc */
+ mMapIter_Next = assoc->mAssoc_Next; /* more */
+
+ break; /* end while loop */
+ }
+ }
+ }
+ if ( assoc ) /* did we find another assoc in the iteration? */
+ {
+ mMapIter_Assoc = assoc; /* current assoc */
+ mork_pos i = assoc - map->mMap_Assocs;
+ mork_change* c = map->mMap_Changes;
+ outNext = ( c )? (c + i) : map->FormDummyChange();
+
+ map->get_assoc( outKey, outVal, i);
+ }
+ }
+ }
+ else map->NewIterOutOfSyncError(ev);
+ }
+ else map->NewBadMapError(ev);
+
+ return outNext;
+}
+
+mork_change*
+morkMapIter::Here(morkEnv* ev, void* outKey, void* outVal)
+{
+ mork_change* outHere = 0;
+
+ morkMap* map = mMapIter_Map;
+
+ if ( map && map->GoodMap() ) /* map looks good? */
+ {
+ if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+ {
+ morkAssoc* here = mMapIter_Assoc; /* current assoc */
+ if ( here ) /* iteration is not yet concluded? */
+ {
+ mork_pos i = here - map->mMap_Assocs;
+ mork_change* c = map->mMap_Changes;
+ outHere = ( c )? (c + i) : map->FormDummyChange();
+
+ map->get_assoc(outKey, outVal, i);
+ }
+ }
+ else map->NewIterOutOfSyncError(ev);
+ }
+ else map->NewBadMapError(ev);
+
+ return outHere;
+}
+
+mork_change*
+morkMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal)
+{
+ mork_change* outCutHere = 0;
+ morkMap* map = mMapIter_Map;
+
+ if ( map && map->GoodMap() ) /* map looks good? */
+ {
+ if ( mMapIter_Seed == map->mMap_Seed ) /* in sync? */
+ {
+ morkAssoc* here = mMapIter_Assoc; /* current assoc */
+ if ( here ) /* iteration is not yet concluded? */
+ {
+ morkAssoc** ref = mMapIter_AssocRef;
+ if ( *ref != mMapIter_Next ) /* not already cut? */
+ {
+ mork_pos i = here - map->mMap_Assocs;
+ mork_change* c = map->mMap_Changes;
+ outCutHere = ( c )? (c + i) : map->FormDummyChange();
+ if ( outKey || outVal )
+ map->get_assoc(outKey, outVal, i);
+
+ map->push_free_assoc(here); /* add to free list */
+ *ref = mMapIter_Next; /* unlink here from bucket list */
+
+ /* note the map has changed, but we are still in sync: */
+ mMapIter_Seed = ++map->mMap_Seed; /* sync */
+
+ if ( map->mMap_Fill ) /* still has nonzero value? */
+ --map->mMap_Fill; /* one less member in the collection */
+ else
+ map->NewSlotsUnderflowWarning(ev);
+ }
+ }
+ }
+ else map->NewIterOutOfSyncError(ev);
+ }
+ else map->NewBadMapError(ev);
+
+ return outCutHere;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkMap.h b/db/mork/src/morkMap.h
new file mode 100644
index 000000000..57104df0e
--- /dev/null
+++ b/db/mork/src/morkMap.h
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKMAP_
+#define _MORKMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/* (These hash methods closely resemble those in public domain IronDoc.) */
+
+/*| Equal: equal for hash table. Note equal(a,b) implies hash(a)==hash(b).
+|*/
+typedef mork_bool (* morkMap_mEqual)
+(const morkMap* self, morkEnv* ev, const void* inKeyA, const void* inKeyB);
+
+/*| Hash: hash for hash table. Note equal(a,b) implies hash(a)==hash(b).
+|*/
+typedef mork_u4 (* morkMap_mHash)
+(const morkMap* self, morkEnv* ev, const void* inKey);
+
+/*| IsNil: whether a key slot contains a "null" value denoting "no such key".
+|*/
+typedef mork_bool (* morkMap_mIsNil)
+(const morkMap* self, morkEnv* ev, const void* inKey);
+
+/*| Note: notify regarding a refcounting change for a key or a value.
+|*/
+//typedef void (* morkMap_mNote)
+//(morkMap* self, morkEnv* ev, void* inKeyOrVal);
+
+/*| morkMapForm: slots need to initialize a new dict. (This is very similar
+**| to the config object for public domain IronDoc hash tables.)
+|*/
+class morkMapForm { // a struct of callback method pointers for morkMap
+public:
+
+ // const void* mMapForm_NilKey; // externally defined 'nil' bit pattern
+
+ // void* mMapForm_NilBuf[ 8 ]; // potential place to put NilKey
+ // If keys are no larger than 8*sizeof(void*), NilKey can be put in NilBuf.
+ // Note this should be true for all Mork subclasses, and we plan usage so.
+
+ // These three methods must always be provided, so zero will cause errors:
+
+ // morkMap_mEqual mMapForm_Equal; // for comparing two keys for identity
+ // morkMap_mHash mMapForm_Hash; // deterministic key to hash method
+ // morkMap_mIsNil mMapForm_IsNil; // to query whether a key equals 'nil'
+
+ // If any of these method slots are nonzero, then morkMap will call the
+ // appropriate one to notify dict users when a key or value is added or cut.
+ // Presumably a caller wants to know this in order to perform refcounting or
+ // some other kind of memory management. These methods are definitely only
+ // called when references to keys or values are inserted or removed, and are
+ // never called when the actual number of references does not change (such
+ // as when added keys are already present or cut keys are alreading missing).
+ //
+ // morkMap_mNote mMapForm_AddKey; // if nonzero, notify about add key
+ // morkMap_mNote mMapForm_CutKey; // if nonzero, notify about cut key
+ // morkMap_mNote mMapForm_AddVal; // if nonzero, notify about add val
+ // morkMap_mNote mMapForm_CutVal; // if nonzero, notify about cut val
+ //
+ // These note methods have been removed because it seems difficult to
+ // guarantee suitable alignment of objects passed to notification methods.
+
+ // Note dict clients should pick key and val sizes that provide whatever
+ // alignment will be required for an array of such keys and values.
+ mork_size mMapForm_KeySize; // size of every key (cannot be zero)
+ mork_size mMapForm_ValSize; // size of every val (can indeed be zero)
+
+ mork_bool mMapForm_HoldChanges; // support changes array in the map
+ mork_change mMapForm_DummyChange; // change used for false HoldChanges
+ mork_bool mMapForm_KeyIsIP; // key is mork_ip sized
+ mork_bool mMapForm_ValIsIP; // key is mork_ip sized
+};
+
+/*| morkAssoc: a canonical association slot in a morkMap. A single assoc
+**| instance does nothing except point to the next assoc in the same bucket
+**| of a hash table. Each assoc has only two interesting attributes: 1) the
+**| address of the assoc, and 2) the next assoc in a bucket's list. The assoc
+**| address is interesting because in the context of an array of such assocs,
+**| one can determine the index of a particular assoc in the array by address
+**| arithmetic, subtracting the array address from the assoc address. And the
+**| index of each assoc is the same index as the associated key and val slots
+**| in the associated arrays
+**|
+**|| Think of an assoc instance as really also containing a key slot and a val
+**| slot, where each key is mMap_Form.mMapForm_KeySize bytes in size, and
+**| each val is mMap_Form.mMapForm_ValSize in size. But the key and val
+**| slots are stored in separate arrays with indexes that are parallel to the
+**| indexes in the array of morkAssoc instances. We have taken the variable
+**| sized slots out of the morkAssoc structure, and put them into parallel
+**| arrays associated with each morkAssoc by array index. And this leaves us
+**| with only the link field to the next assoc in each assoc instance.
+|*/
+class morkAssoc {
+public:
+ morkAssoc* mAssoc_Next;
+};
+
+
+#define morkDerived_kMap /*i*/ 0x4D70 /* ascii 'Mp' */
+
+#define morkMap_kTag /*i*/ 0x6D4D6150 /* ascii 'mMaP' */
+
+/*| morkMap: a hash table based on the public domain IronDoc hash table
+**| (which is in turn rather like a similar OpenDoc hash table).
+|*/
+class morkMap : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+ nsIMdbHeap* mMap_Heap; // strong ref to heap allocating all space
+ mork_u4 mMap_Tag; // must equal morkMap_kTag
+
+ // When a morkMap instance is constructed, the dict form slots must be
+ // provided in order to properly configure a dict with all runtime needs:
+
+ morkMapForm mMap_Form; // construction time parameterization
+
+ // Whenever the dict changes structure in a way that would affect any
+ // iteration of the dict associations, the seed increments to show this:
+
+ mork_seed mMap_Seed; // counter for member and structural changes
+
+ // The current total assoc capacity of the dict is mMap_Slots, where
+ // mMap_Fill of these slots are actually holding content, so mMap_Fill
+ // is the actual membership count, and mMap_Slots is how larger membership
+ // can become before the hash table must grow the buffers being used.
+
+ mork_count mMap_Slots; // count of slots in the hash table
+ mork_fill mMap_Fill; // number of used slots in the hash table
+
+ // Key and value slots are bound to corresponding mMap_Assocs array slots.
+ // Instead of having a single array like this: {key,val,next}[ mMap_Slots ]
+ // we have instead three parallel arrays with essentially the same meaning:
+ // {key}[ mMap_Slots ], {val}[ mMap_Slots ], {assocs}[ mMap_Slots ]
+
+ mork_u1* mMap_Keys; // mMap_Slots * mMapForm_KeySize buffer
+ mork_u1* mMap_Vals; // mMap_Slots * mMapForm_ValSize buffer
+
+ // An assoc is "used" when it appears in a bucket's linked list of assocs.
+ // Until an assoc is used, it appears in the FreeList linked list. Every
+ // assoc that becomes used goes into the bucket determined by hashing the
+ // key associated with a new assoc. The key associated with a new assoc
+ // goes in to the slot in mMap_Keys which occupies exactly the same array
+ // index as the array index of the used assoc in the mMap_Assocs array.
+
+ morkAssoc* mMap_Assocs; // mMap_Slots * sizeof(morkAssoc) buffer
+
+ // The changes array is only needed when the
+
+ mork_change* mMap_Changes; // mMap_Slots * sizeof(mork_change) buffer
+
+ // The Buckets array need not be the same length as the Assocs array, but we
+ // usually do it that way so the average bucket depth is no more than one.
+ // (We could pick a different policy, or make it parameterizable, but that's
+ // tuning we can do some other time.)
+
+ morkAssoc** mMap_Buckets; // mMap_Slots * sizeof(morkAssoc*) buffer
+
+ // The length of the mMap_FreeList should equal (mMap_Slots - mMap_Fill).
+ // We need a free list instead of a simpler representation because assocs
+ // can be cut and returned to availability in any kind of unknown pattern.
+ // (However, when assocs are first allocated, or when the dict is grown, we
+ // know all new assocs are contiguous and can chain together adjacently.)
+
+ morkAssoc* mMap_FreeList; // list of unused mMap_Assocs array slots
+
+public: // getters (morkProbeMap compatibility)
+ mork_fill MapFill() const { return mMap_Fill; }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseMap() only if open
+ virtual ~morkMap(); // assert that CloseMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkMap(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap,
+ mork_size inKeySize, mork_size inValSize,
+ mork_size inSlots, nsIMdbHeap* ioSlotHeap, mork_bool inHoldChanges);
+
+ void CloseMap(morkEnv* ev); // called by
+
+public: // dynamic type identification
+ mork_bool IsMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kMap; }
+// } ===== end morkNode methods =====
+
+public: // poly map hash table methods
+
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const = 0;
+
+ virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+ Hash(morkEnv* ev, const void* inKey) const = 0;
+// } ===== end morkMap poly interface =====
+
+public: // open utitity methods
+
+ mork_bool GoodMapTag() const { return mMap_Tag == morkMap_kTag; }
+ mork_bool GoodMap() const
+ { return ( IsNode() && GoodMapTag() ); }
+
+ void NewIterOutOfSyncError(morkEnv* ev);
+ void NewBadMapError(morkEnv* ev);
+ void NewSlotsUnderflowWarning(morkEnv* ev);
+ void InitMap(morkEnv* ev, mork_size inSlots);
+
+protected: // internal utitity methods
+
+ friend class morkMapIter;
+ void clear_map(morkEnv* ev, nsIMdbHeap* ioHeap);
+
+ void* alloc(morkEnv* ev, mork_size inSize);
+ void* clear_alloc(morkEnv* ev, mork_size inSize);
+
+ void push_free_assoc(morkAssoc* ioAssoc)
+ {
+ ioAssoc->mAssoc_Next = mMap_FreeList;
+ mMap_FreeList = ioAssoc;
+ }
+
+ morkAssoc* pop_free_assoc()
+ {
+ morkAssoc* assoc = mMap_FreeList;
+ if ( assoc )
+ mMap_FreeList = assoc->mAssoc_Next;
+ return assoc;
+ }
+
+ morkAssoc** find(morkEnv* ev, const void* inKey, mork_u4 inHash) const;
+
+ mork_u1* new_keys(morkEnv* ev, mork_num inSlots);
+ mork_u1* new_values(morkEnv* ev, mork_num inSlots);
+ mork_change* new_changes(morkEnv* ev, mork_num inSlots);
+ morkAssoc** new_buckets(morkEnv* ev, mork_num inSlots);
+ morkAssoc* new_assocs(morkEnv* ev, mork_num inSlots);
+ mork_bool new_arrays(morkEnv* ev, morkHashArrays* old, mork_num inSlots);
+
+ mork_bool grow(morkEnv* ev);
+
+ void get_assoc(void* outKey, void* outVal, mork_pos inPos) const;
+ void put_assoc(const void* inKey, const void* inVal, mork_pos inPos) const;
+
+public: // inlines to form slots
+ // const void* FormNilKey() const { return mMap_Form.mMapForm_NilKey; }
+
+ // morkMap_mEqual FormEqual() const { return mMap_Form.mMapForm_Equal; }
+ // morkMap_mHash FormHash() const { return mMap_Form.mMapForm_Hash; }
+ // orkMap_mIsNil FormIsNil() const { return mMap_Form.mMapForm_IsNil; }
+
+ // morkMap_mNote FormAddKey() const { return mMap_Form.mMapForm_AddKey; }
+ // morkMap_mNote FormCutKey() const { return mMap_Form.mMapForm_CutKey; }
+ // morkMap_mNote FormAddVal() const { return mMap_Form.mMapForm_AddVal; }
+ // morkMap_mNote FormCutVal() const { return mMap_Form.mMapForm_CutVal; }
+
+ mork_size FormKeySize() const { return mMap_Form.mMapForm_KeySize; }
+ mork_size FormValSize() const { return mMap_Form.mMapForm_ValSize; }
+
+ mork_bool FormKeyIsIP() const { return mMap_Form.mMapForm_KeyIsIP; }
+ mork_bool FormValIsIP() const { return mMap_Form.mMapForm_ValIsIP; }
+
+ mork_bool FormHoldChanges() const
+ { return mMap_Form.mMapForm_HoldChanges; }
+
+ mork_change* FormDummyChange()
+ { return &mMap_Form.mMapForm_DummyChange; }
+
+public: // other map methods
+
+ mork_bool Put(morkEnv* ev, const void* inKey, const void* inVal,
+ void* outKey, void* outVal, mork_change** outChange);
+
+ mork_bool Cut(morkEnv* ev, const void* inKey,
+ void* outKey, void* outVal, mork_change** outChange);
+
+ mork_bool Get(morkEnv* ev, const void* inKey,
+ void* outKey, void* outVal, mork_change** outChange);
+
+ mork_num CutAll(morkEnv* ev);
+
+private: // copying is not allowed
+ morkMap(const morkMap& other);
+ morkMap& operator=(const morkMap& other);
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakMap(morkMap* me,
+ morkEnv* ev, morkMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongMap(morkMap* me,
+ morkEnv* ev, morkMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+/*| morkMapIter: an iterator for morkMap and subclasses. This is not a node,
+**| and expected usage is as a member of some other node subclass, such as in
+**| a cursor subclass or a thumb subclass. Also, iters might be as temp stack
+**| objects when scanning the content of a map.
+|*/
+class morkMapIter{ // iterator for hash table map
+
+protected:
+ morkMap* mMapIter_Map; // map to iterate, NOT refcounted
+ mork_seed mMapIter_Seed; // cached copy of map's seed
+
+ morkAssoc** mMapIter_Bucket; // one bucket in mMap_Buckets array
+ morkAssoc** mMapIter_AssocRef; // usually *AtRef equals Here
+ morkAssoc* mMapIter_Assoc; // the current assoc in an iteration
+ morkAssoc* mMapIter_Next; // mMapIter_Assoc->mAssoc_Next */
+
+public:
+ morkMapIter(morkEnv* ev, morkMap* ioMap);
+ void CloseMapIter(morkEnv* ev);
+
+ morkMapIter( ); // everything set to zero -- need to call InitMapIter()
+
+protected: // we want all subclasses to provide typesafe wrappers:
+
+ void InitMapIter(morkEnv* ev, morkMap* ioMap);
+
+ // The morkAssoc returned below is always either mork_change* or
+ // else nil (when there is no such assoc). We return a pointer to
+ // the change rather than a simple bool, because callers might
+ // want to access change info associated with an assoc.
+
+ mork_change* First(morkEnv* ev, void* outKey, void* outVal);
+ mork_change* Next(morkEnv* ev, void* outKey, void* outVal);
+ mork_change* Here(morkEnv* ev, void* outKey, void* outVal);
+
+ mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKMAP_ */
diff --git a/db/mork/src/morkNode.cpp b/db/mork/src/morkNode.cpp
new file mode 100644
index 000000000..4e95eed37
--- /dev/null
+++ b/db/mork/src/morkNode.cpp
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+/*3456789_123456789_123456789_123456789_123456789_123456789_123456789_12345678*/
+
+/* ===== ===== ===== ===== morkUsage ===== ===== ===== ===== */
+
+static morkUsage morkUsage_gHeap; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kHeap = morkUsage_gHeap;
+
+static morkUsage morkUsage_gStack; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kStack = morkUsage_gStack;
+
+static morkUsage morkUsage_gMember; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kMember = morkUsage_gMember;
+
+static morkUsage morkUsage_gGlobal; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kGlobal = morkUsage_gGlobal;
+
+static morkUsage morkUsage_gPool; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kPool = morkUsage_gPool;
+
+static morkUsage morkUsage_gNone; // ensure EnsureReadyStaticUsage()
+const morkUsage& morkUsage::kNone = morkUsage_gNone;
+
+// This must be structured to allow for non-zero values in global variables
+// just before static init time. We can only safely check for whether a
+// global has the address of some other global. Please, do not initialize
+// either of the variables below to zero, because this could break when a zero
+// is assigned at static init time, but after EnsureReadyStaticUsage() runs.
+
+static mork_u4 morkUsage_g_static_init_target; // only address of this matters
+static mork_u4* morkUsage_g_static_init_done; // is address of target above?
+
+#define morkUsage_do_static_init() \
+ ( morkUsage_g_static_init_done = &morkUsage_g_static_init_target )
+
+#define morkUsage_need_static_init() \
+ ( morkUsage_g_static_init_done != &morkUsage_g_static_init_target )
+
+/*static*/
+void morkUsage::EnsureReadyStaticUsage()
+{
+ if ( morkUsage_need_static_init() )
+ {
+ morkUsage_do_static_init();
+
+ morkUsage_gHeap.InitUsage(morkUsage_kHeap);
+ morkUsage_gStack.InitUsage(morkUsage_kStack);
+ morkUsage_gMember.InitUsage(morkUsage_kMember);
+ morkUsage_gGlobal.InitUsage(morkUsage_kGlobal);
+ morkUsage_gPool.InitUsage(morkUsage_kPool);
+ morkUsage_gNone.InitUsage(morkUsage_kNone);
+ }
+}
+
+/*static*/
+const morkUsage& morkUsage::GetHeap() // kHeap safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gHeap;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetStack() // kStack safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gStack;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetMember() // kMember safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gMember;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetGlobal() // kGlobal safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gGlobal;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetPool() // kPool safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gPool;
+}
+
+/*static*/
+const morkUsage& morkUsage::GetNone() // kNone safe at static init time
+{
+ EnsureReadyStaticUsage();
+ return morkUsage_gNone;
+}
+
+morkUsage::morkUsage()
+{
+ if ( morkUsage_need_static_init() )
+ {
+ morkUsage::EnsureReadyStaticUsage();
+ }
+}
+
+morkUsage::morkUsage(mork_usage code)
+ : mUsage_Code(code)
+{
+ if ( morkUsage_need_static_init() )
+ {
+ morkUsage::EnsureReadyStaticUsage();
+ }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*static*/ void*
+morkNode::MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev)
+{
+ void* node = 0;
+ ioHeap.Alloc(ev->AsMdbEnv(), inSize, (void **) &node);
+ if ( !node )
+ ev->OutOfMemoryError();
+
+ return node;
+}
+
+/*public non-poly*/ void
+morkNode::ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap)
+{
+ if ( this->IsNode() )
+ {
+ mork_usage usage = mNode_Usage; // mNode_Usage before ~morkNode
+ this->morkNode::~morkNode(); // first call polymorphic destructor
+ if ( ioHeap ) // was this node heap allocated?
+ ioHeap->Free(ev->AsMdbEnv(), this);
+ else if ( usage == morkUsage_kPool ) // mNode_Usage before ~morkNode
+ {
+ morkHandle* h = (morkHandle*) this;
+ if ( h->IsHandle() && h->GoodHandleTag() )
+ {
+ if ( h->mHandle_Face )
+ {
+ if (ev->mEnv_HandlePool)
+ ev->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
+ else if (h->mHandle_Env && h->mHandle_Env->mEnv_HandlePool)
+ h->mHandle_Env->mEnv_HandlePool->ZapHandle(ev, h->mHandle_Face);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+/*public virtual*/ void
+morkNode::CloseMorkNode(morkEnv* ev) // CloseNode() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseNode(ev);
+ this->MarkShut();
+ }
+}
+NS_IMETHODIMP
+morkNode::CloseMdbObject(nsIMdbEnv* mev)
+{
+ return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+nsresult morkNode::CloseMdbObject(morkEnv *ev)
+{
+ // if only one ref, Handle_CutStrongRef will clean up better.
+ if (mNode_Uses == 1)
+ // XXX Casting mork_uses to nsresult
+ return static_cast<nsresult>(CutStrongRef(ev));
+
+ nsresult outErr = NS_OK;
+
+ if ( IsNode() && IsOpenNode() )
+ {
+ if ( ev )
+ {
+ CloseMorkNode(ev);
+ outErr = ev->AsErr();
+ }
+ }
+ return outErr;
+}
+
+/*public virtual*/
+morkNode::~morkNode() // assert that CloseNode() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode() || IsDeadNode()); // sometimes we call destructor explicitly w/o freeing object.
+ mNode_Access = morkAccess_kDead;
+ mNode_Usage = morkUsage_kNone;
+}
+
+/*public virtual*/
+// void CloseMorkNode(morkEnv* ev) = 0; // CloseNode() only if open
+ // CloseMorkNode() is the polymorphic close method called when uses==0,
+ // which must do NOTHING at all when IsOpenNode() is not true. Otherwise,
+ // CloseMorkNode() should call a static close method specific to an object.
+ // Each such static close method should either call inherited static close
+ // methods, or else perform the consolidated effect of calling them, where
+ // subclasses should closely track any changes in base classes with care.
+
+
+/*public non-poly*/
+morkNode::morkNode( mork_usage inCode )
+: mNode_Heap( 0 )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inCode )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+}
+
+/*public non-poly*/
+morkNode::morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: mNode_Heap( ioHeap )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inUsage.Code() )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+ if ( !ioHeap && mNode_Usage == morkUsage_kHeap )
+ MORK_ASSERT(ioHeap);
+}
+
+/*public non-poly*/
+morkNode::morkNode(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+: mNode_Heap( ioHeap )
+, mNode_Base( morkBase_kNode )
+, mNode_Derived ( 0 ) // until subclass sets appropriately
+, mNode_Access( morkAccess_kOpen )
+, mNode_Usage( inUsage.Code() )
+, mNode_Mutable( morkAble_kEnabled )
+, mNode_Load( morkLoad_kClean )
+, mNode_Uses( 1 )
+, mNode_Refs( 1 )
+{
+ if ( !ioHeap && mNode_Usage == morkUsage_kHeap )
+ {
+ this->NilHeapError(ev);
+ }
+}
+
+/*protected non-poly*/ void
+morkNode::RefsUnderUsesWarning(morkEnv* ev) const
+{
+ ev->NewError("mNode_Refs < mNode_Uses");
+}
+
+/*protected non-poly*/ void
+morkNode::NonNodeError(morkEnv* ev) const // called when IsNode() is false
+{
+ ev->NewError("non-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NonOpenNodeError(morkEnv* ev) const // when IsOpenNode() is false
+{
+ ev->NewError("non-open-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NonMutableNodeError(morkEnv* ev) const // when IsMutable() is false
+{
+ ev->NewError("non-mutable-morkNode");
+}
+
+/*protected non-poly*/ void
+morkNode::NilHeapError(morkEnv* ev) const // zero mNode_Heap w/ kHeap usage
+{
+ ev->NewError("nil mNode_Heap");
+}
+
+/*protected non-poly*/ void
+morkNode::RefsOverflowWarning(morkEnv* ev) const // mNode_Refs overflow
+{
+ ev->NewWarning("mNode_Refs overflow");
+}
+
+/*protected non-poly*/ void
+morkNode::UsesOverflowWarning(morkEnv* ev) const // mNode_Uses overflow
+{
+ ev->NewWarning("mNode_Uses overflow");
+}
+
+/*protected non-poly*/ void
+morkNode::RefsUnderflowWarning(morkEnv* ev) const // mNode_Refs underflow
+{
+ ev->NewWarning("mNode_Refs underflow");
+}
+
+/*protected non-poly*/ void
+morkNode::UsesUnderflowWarning(morkEnv* ev) const // mNode_Uses underflow
+{
+ ev->NewWarning("mNode_Uses underflow");
+}
+
+/*public non-poly*/ void
+morkNode::CloseNode(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ this->MarkShut();
+ else
+ this->NonNodeError(ev);
+}
+
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot)
+ // If *ioSlot is non-nil, that file is released by CutStrongRef() and
+ // then zeroed out. Then if self is non-nil, this is acquired by
+ // calling AddStrongRef(), and if the return value shows success,
+ // then self is put into slot *ioSlot. Note self can be nil, so we take
+ // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
+{
+ nsIMdbFile* file = *ioSlot;
+ if ( self != file )
+ {
+ if ( file )
+ {
+ *ioSlot = 0;
+ NS_RELEASE(file);
+ }
+ if (self && ev->Good())
+ NS_ADDREF(*ioSlot = self);
+ }
+}
+
+void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot)
+ // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
+ // then zeroed out. Then if self is non-nil, self is acquired by
+ // calling AddStrongRef(), and if the return value shows success,
+ // then self is put into slot *ioSlot. Note self can be nil, so we
+ // permit expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
+{
+ nsIMdbHeap* heap = *ioSlot;
+ if ( self != heap )
+ {
+ if ( heap )
+ *ioSlot = 0;
+
+ if (self && ev->Good())
+ *ioSlot = self;
+ }
+}
+
+/*public static*/ void
+morkNode::SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot)
+ // If *ioSlot is non-nil, that node is released by CutStrongRef() and
+ // then zeroed out. Then if me is non-nil, this is acquired by
+ // calling AddStrongRef(), and if positive is returned to show success,
+ // then me is put into slot *ioSlot. Note me can be nil, so we take
+ // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
+{
+ morkNode* node = *ioSlot;
+ if ( me != node )
+ {
+ if ( node )
+ {
+ // what if this nulls out the ev and causes asserts?
+ // can we move this after the CutStrongRef()?
+ *ioSlot = 0;
+ node->CutStrongRef(ev);
+ }
+ if ( me && me->AddStrongRef(ev) )
+ *ioSlot = me;
+ }
+}
+
+/*public static*/ void
+morkNode::SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot)
+ // If *ioSlot is non-nil, that node is released by CutWeakRef() and
+ // then zeroed out. Then if me is non-nil, this is acquired by
+ // calling AddWeakRef(), and if positive is returned to show success,
+ // then me is put into slot *ioSlot. Note me can be nil, so we
+ // expression 'morkNode::SlotWeakNode((morkNode*) 0, ev, &slot)'.
+{
+ morkNode* node = *ioSlot;
+ if ( me != node )
+ {
+ if ( node )
+ {
+ *ioSlot = 0;
+ node->CutWeakRef(ev);
+ }
+ if ( me && me->AddWeakRef(ev) )
+ *ioSlot = me;
+ }
+}
+
+/*public non-poly*/ mork_uses
+morkNode::AddStrongRef(morkEnv* ev)
+{
+ mork_uses outUses = 0;
+ if ( this->IsNode() )
+ {
+ mork_uses uses = mNode_Uses;
+ mork_refs refs = mNode_Refs;
+ if ( refs < uses ) // need to fix broken refs/uses relation?
+ {
+ this->RefsUnderUsesWarning(ev);
+ mNode_Refs = mNode_Uses = refs = uses;
+ }
+ if ( refs < morkNode_kMaxRefCount ) // not too great?
+ {
+ mNode_Refs = ++refs;
+ mNode_Uses = ++uses;
+ }
+ else
+ this->RefsOverflowWarning(ev);
+
+ outUses = uses;
+ }
+ else
+ this->NonNodeError(ev);
+ return outUses;
+}
+
+/*private non-poly*/ mork_bool
+morkNode::cut_use_count(morkEnv* ev) // just one part of CutStrongRef()
+{
+ mork_bool didCut = morkBool_kFalse;
+ if ( this->IsNode() )
+ {
+ mork_uses uses = mNode_Uses;
+ if ( uses ) // not yet zero?
+ mNode_Uses = --uses;
+ else
+ this->UsesUnderflowWarning(ev);
+
+ didCut = morkBool_kTrue;
+ if ( !mNode_Uses ) // last use gone? time to close node?
+ {
+ if ( this->IsOpenNode() )
+ {
+ if ( !mNode_Refs ) // no outstanding reference?
+ {
+ this->RefsUnderflowWarning(ev);
+ ++mNode_Refs; // prevent potential crash during close
+ }
+ this->CloseMorkNode(ev); // polymorphic self close
+ // (Note CutNode() is not polymorphic -- so don't call that.)
+ }
+ }
+ }
+ else
+ this->NonNodeError(ev);
+ return didCut;
+}
+
+/*public non-poly*/ mork_uses
+morkNode::CutStrongRef(morkEnv* ev)
+{
+ mork_refs outRefs = 0;
+ if ( this->IsNode() )
+ {
+ if ( this->cut_use_count(ev) )
+ outRefs = this->CutWeakRef(ev);
+ }
+ else
+ this->NonNodeError(ev);
+
+ return outRefs;
+}
+
+/*public non-poly*/ mork_refs
+morkNode::AddWeakRef(morkEnv* ev)
+{
+ mork_refs outRefs = 0;
+ if ( this->IsNode() )
+ {
+ mork_refs refs = mNode_Refs;
+ if ( refs < morkNode_kMaxRefCount ) // not too great?
+ mNode_Refs = ++refs;
+ else
+ this->RefsOverflowWarning(ev);
+
+ outRefs = refs;
+ }
+ else
+ this->NonNodeError(ev);
+
+ return outRefs;
+}
+
+/*public non-poly*/ mork_refs
+morkNode::CutWeakRef(morkEnv* ev)
+{
+ mork_refs outRefs = 0;
+ if ( this->IsNode() )
+ {
+ mork_uses uses = mNode_Uses;
+ mork_refs refs = mNode_Refs;
+ if ( refs ) // not yet zero?
+ mNode_Refs = --refs;
+ else
+ this->RefsUnderflowWarning(ev);
+
+ if ( refs < uses ) // need to fix broken refs/uses relation?
+ {
+ this->RefsUnderUsesWarning(ev);
+ mNode_Refs = mNode_Uses = refs = uses;
+ }
+
+ outRefs = refs;
+ if ( !refs ) // last reference gone? time to destroy node?
+ this->ZapOld(ev, mNode_Heap); // self destroy, use this no longer
+ }
+ else
+ this->NonNodeError(ev);
+
+ return outRefs;
+}
+
+static const char morkNode_kBroken[] = "broken";
+
+/*public non-poly*/ const char*
+morkNode::GetNodeAccessAsString() const // e.g. "open", "shut", etc.
+{
+ const char* outString = morkNode_kBroken;
+ switch( mNode_Access )
+ {
+ case morkAccess_kOpen: outString = "open"; break;
+ case morkAccess_kClosing: outString = "closing"; break;
+ case morkAccess_kShut: outString = "shut"; break;
+ case morkAccess_kDead: outString = "dead"; break;
+ }
+ return outString;
+}
+
+/*public non-poly*/ const char*
+morkNode::GetNodeUsageAsString() const // e.g. "heap", "stack", etc.
+{
+ const char* outString = morkNode_kBroken;
+ switch( mNode_Usage )
+ {
+ case morkUsage_kHeap: outString = "heap"; break;
+ case morkUsage_kStack: outString = "stack"; break;
+ case morkUsage_kMember: outString = "member"; break;
+ case morkUsage_kGlobal: outString = "global"; break;
+ case morkUsage_kPool: outString = "pool"; break;
+ case morkUsage_kNone: outString = "none"; break;
+ }
+ return outString;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkNode.h b/db/mork/src/morkNode.h
new file mode 100644
index 000000000..02dbbb229
--- /dev/null
+++ b/db/mork/src/morkNode.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKNODE_
+#define _MORKNODE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkUsage_kHeap 'h'
+#define morkUsage_kStack 's'
+#define morkUsage_kMember 'm'
+#define morkUsage_kGlobal 'g'
+#define morkUsage_kPool 'p'
+#define morkUsage_kNone 'n'
+
+class morkUsage {
+public:
+ mork_usage mUsage_Code; // kHeap, kStack, kMember, or kGhost
+
+public:
+ explicit morkUsage(mork_usage inCode);
+
+ morkUsage(); // does nothing except maybe call EnsureReadyStaticUsage()
+ void InitUsage( mork_usage inCode)
+ { mUsage_Code = inCode; }
+
+ ~morkUsage() { }
+ mork_usage Code() const { return mUsage_Code; }
+
+ static void EnsureReadyStaticUsage();
+
+public:
+ static const morkUsage& kHeap; // morkUsage_kHeap
+ static const morkUsage& kStack; // morkUsage_kStack
+ static const morkUsage& kMember; // morkUsage_kMember
+ static const morkUsage& kGlobal; // morkUsage_kGlobal
+ static const morkUsage& kPool; // morkUsage_kPool
+ static const morkUsage& kNone; // morkUsage_kNone
+
+ static const morkUsage& GetHeap(); // kHeap, safe at static init time
+ static const morkUsage& GetStack(); // kStack, safe at static init time
+ static const morkUsage& GetMember(); // kMember, safe at static init time
+ static const morkUsage& GetGlobal(); // kGlobal, safe at static init time
+ static const morkUsage& GetPool(); // kPool, safe at static init time
+ static const morkUsage& GetNone(); // kNone, safe at static init time
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkNode_kMaxRefCount 0x0FFFF /* count sticks if it hits this */
+
+#define morkBase_kNode /*i*/ 0x4E64 /* ascii 'Nd' */
+
+/*| morkNode: several groups of two-byte integers that track the basic
+**| status of an object that can be used to compose in-memory graphs.
+**| This is the base class for nsIMdbObject (which adds members that fit
+**| the needs of an nsIMdbObject subclass). The morkNode class is also used
+**| as the base class for other Mork db classes with no strong relation to
+**| the MDB class hierarchy.
+**|
+**|| Heap: the heap in which this node was allocated, when the usage equals
+**| morkUsage_kHeap to show dynamic allocation. Note this heap is NOT ref-
+**| counted, because that would be too great and complex a burden for all
+**| the nodes allocated in that heap. So heap users should take care to
+**| understand that nodes allocated in that heap are considered protected
+**| by some inclusive context in which all those nodes are allocated, and
+**| that context must maintain at least one strong refcount for the heap.
+**| Occasionally a node subclass will indeed wish to hold a refcounted
+**| reference to a heap, and possibly the same heap that is in mNode_Heap,
+**| but this is always done in a separate slot that explicitly refcounts,
+**| so we avoid confusing what is meant by the mNode_Heap slot.
+|*/
+class morkNode /*: public nsISupports */ { // base class for constructing Mork object graphs
+
+public: // state is public because the entire Mork system is private
+
+// NS_DECL_ISUPPORTS;
+ nsIMdbHeap* mNode_Heap; // NON-refcounted heap pointer
+
+ mork_base mNode_Base; // must equal morkBase_kNode
+ mork_derived mNode_Derived; // depends on specific node subclass
+
+ mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ mork_able mNode_Mutable; // can this node be modified?
+ mork_load mNode_Load; // is this node clean or dirty?
+
+ mork_uses mNode_Uses; // refcount for strong refs
+ mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+protected: // special case empty construction for morkHandleFrame
+ friend class morkHandleFrame;
+ morkNode() { }
+
+public: // inlines for weird mNode_Mutable and mNode_Load constants
+
+ void SetFrozen() { mNode_Mutable = morkAble_kDisabled; }
+ void SetMutable() { mNode_Mutable = morkAble_kEnabled; }
+ void SetAsleep() { mNode_Mutable = morkAble_kAsleep; }
+
+ mork_bool IsFrozen() const { return mNode_Mutable == morkAble_kDisabled; }
+ mork_bool IsMutable() const { return mNode_Mutable == morkAble_kEnabled; }
+ mork_bool IsAsleep() const { return mNode_Mutable == morkAble_kAsleep; }
+
+ void SetNodeClean() { mNode_Load = morkLoad_kClean; }
+ void SetNodeDirty() { mNode_Load = morkLoad_kDirty; }
+
+ mork_bool IsNodeClean() const { return mNode_Load == morkLoad_kClean; }
+ mork_bool IsNodeDirty() const { return mNode_Load == morkLoad_kDirty; }
+
+public: // morkNode memory management methods
+ static void* MakeNew(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev);
+
+ void ZapOld(morkEnv* ev, nsIMdbHeap* ioHeap); // replaces operator delete()
+ // this->morkNode::~morkNode(); // first call polymorphic destructor
+ // if ( ioHeap ) // was this node heap allocated?
+ // ioHeap->Free(ev->AsMdbEnv(), this);
+
+public: // morkNode memory management operators
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkNode::MakeNew(inSize, ioHeap, ev); }
+
+
+protected: // construction without an anv needed for first env constructed:
+ morkNode(const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+
+ explicit morkNode(mork_usage inCode); // usage == inCode, heap == nil
+
+// { ===== begin basic node interface =====
+public: // morkNode virtual methods
+ // virtual FlushMorkNode(morkEnv* ev, morkStream* ioStream);
+ // virtual WriteMorkNode(morkEnv* ev, morkStream* ioStream);
+
+ virtual ~morkNode(); // assert that CloseNode() executed earlier
+ virtual void CloseMorkNode(morkEnv* ev); // CloseNode() only if open
+
+ // CloseMorkNode() is the polymorphic close method called when uses==0,
+ // which must do NOTHING at all when IsOpenNode() is not true. Otherwise,
+ // CloseMorkNode() should call a static close method specific to an object.
+ // Each such static close method should either call inherited static close
+ // methods, or else perform the consolidated effect of calling them, where
+ // subclasses should closely track any changes in base classes with care.
+
+public: // morkNode construction
+ morkNode(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+ void CloseNode(morkEnv* ev); // called by CloseMorkNode();
+ nsresult CloseMdbObject(morkEnv *ev);
+ NS_IMETHOD CloseMdbObject(nsIMdbEnv *ev);
+private: // copying is not allowed
+ morkNode(const morkNode& other);
+ morkNode& operator=(const morkNode& other);
+
+public: // dynamic type identification
+ mork_bool IsNode() const
+ { return mNode_Base == morkBase_kNode; }
+// } ===== end basic node methods =====
+
+public: // public error & warning methods
+
+ void RefsUnderUsesWarning(morkEnv* ev) const; // call if mNode_Refs < mNode_Uses
+ void NonNodeError(morkEnv* ev) const; // call when IsNode() is false
+ void NilHeapError(morkEnv* ev) const; // zero mNode_Heap when usage is kHeap
+ void NonOpenNodeError(morkEnv* ev) const; // call when IsOpenNode() is false
+
+ void NonMutableNodeError(morkEnv* ev) const; // when IsMutable() is false
+
+ void RefsOverflowWarning(morkEnv* ev) const; // call on mNode_Refs overflow
+ void UsesOverflowWarning(morkEnv* ev) const; // call on mNode_Uses overflow
+ void RefsUnderflowWarning(morkEnv* ev) const; // call on mNode_Refs underflow
+ void UsesUnderflowWarning(morkEnv* ev) const; // call on mNode_Uses underflow
+
+private: // private refcounting methods
+ mork_bool cut_use_count(morkEnv* ev); // just one part of CutStrongRef()
+
+public: // other morkNode methods
+
+ mork_bool GoodRefs() const { return mNode_Refs >= mNode_Uses; }
+ mork_bool BadRefs() const { return mNode_Refs < mNode_Uses; }
+
+ mork_uses StrongRefsOnly() const { return mNode_Uses; }
+ mork_refs WeakRefsOnly() const { return (mork_refs) ( mNode_Refs - mNode_Uses ); }
+
+ // (this refcounting derives from public domain IronDoc node refcounts)
+ virtual mork_uses AddStrongRef(morkEnv* ev);
+ virtual mork_uses CutStrongRef(morkEnv* ev);
+ mork_refs AddWeakRef(morkEnv* ev);
+ mork_refs CutWeakRef(morkEnv* ev);
+
+ const char* GetNodeAccessAsString() const; // e.g. "open", "shut", etc.
+ const char* GetNodeUsageAsString() const; // e.g. "heap", "stack", etc.
+
+ mork_usage NodeUsage() const { return mNode_Usage; }
+
+ mork_bool IsHeapNode() const
+ { return mNode_Usage == morkUsage_kHeap; }
+
+ mork_bool IsOpenNode() const
+ { return mNode_Access == morkAccess_kOpen; }
+
+ mork_bool IsShutNode() const
+ { return mNode_Access == morkAccess_kShut; }
+
+ mork_bool IsDeadNode() const
+ { return mNode_Access == morkAccess_kDead; }
+
+ mork_bool IsClosingNode() const
+ { return mNode_Access == morkAccess_kClosing; }
+
+ mork_bool IsOpenOrClosingNode() const
+ { return IsOpenNode() || IsClosingNode(); }
+
+ mork_bool HasNodeAccess() const
+ { return ( IsOpenNode() || IsShutNode() || IsClosingNode() ); }
+
+ void MarkShut() { mNode_Access = morkAccess_kShut; }
+ void MarkClosing() { mNode_Access = morkAccess_kClosing; }
+ void MarkDead() { mNode_Access = morkAccess_kDead; }
+
+public: // refcounting for typesafe subclass inline methods
+ static void SlotWeakNode(morkNode* me, morkEnv* ev, morkNode** ioSlot);
+ // If *ioSlot is non-nil, that node is released by CutWeakRef() and
+ // then zeroed out. Then if me is non-nil, this is acquired by
+ // calling AddWeakRef(), and if positive is returned to show success,
+ // then this is put into slot *ioSlot. Note me can be nil, so we
+ // permit expression '((morkNode*) 0L)->SlotWeakNode(ev, &slot)'.
+
+ static void SlotStrongNode(morkNode* me, morkEnv* ev, morkNode** ioSlot);
+ // If *ioSlot is non-nil, that node is released by CutStrongRef() and
+ // then zeroed out. Then if me is non-nil, this is acquired by
+ // calling AddStrongRef(), and if positive is returned to show success,
+ // then me is put into slot *ioSlot. Note me can be nil, so we take
+ // expression 'morkNode::SlotStrongNode((morkNode*) 0, ev, &slot)'.
+};
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbHeap_SlotStrongHeap(nsIMdbHeap* self, morkEnv* ev, nsIMdbHeap** ioSlot);
+ // If *ioSlot is non-nil, that heap is released by CutStrongRef() and
+ // then zeroed out. Then if self is non-nil, this is acquired by
+ // calling AddStrongRef(), and if the return value shows success,
+ // then self is put into slot *ioSlot. Note self can be nil, so we take
+ // expression 'nsIMdbHeap_SlotStrongHeap(0, ev, &slot)'.
+
+extern void // utility method very similar to morkNode::SlotStrongNode():
+nsIMdbFile_SlotStrongFile(nsIMdbFile* self, morkEnv* ev, nsIMdbFile** ioSlot);
+ // If *ioSlot is non-nil, that file is released by CutStrongRef() and
+ // then zeroed out. Then if self is non-nil, this is acquired by
+ // calling AddStrongRef(), and if the return value shows success,
+ // then self is put into slot *ioSlot. Note self can be nil, so we take
+ // expression 'nsIMdbFile_SlotStrongFile(0, ev, &slot)'.
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKNODE_ */
diff --git a/db/mork/src/morkNodeMap.cpp b/db/mork/src/morkNodeMap.cpp
new file mode 100644
index 000000000..7178fbafc
--- /dev/null
+++ b/db/mork/src/morkNodeMap.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkNodeMap::CloseMorkNode(morkEnv* ev) // CloseNodeMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseNodeMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkNodeMap::~morkNodeMap() // assert CloseNodeMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkNodeMap::morkNodeMap(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkIntMap(ev, inUsage, /*valsize*/ sizeof(morkNode*), ioHeap, ioSlotHeap,
+ /*inHoldChanges*/ morkBool_kTrue)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kNodeMap;
+}
+
+/*public non-poly*/ void
+morkNodeMap::CloseNodeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CutAllNodes(ev);
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+mork_bool
+morkNodeMap::AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode)
+ // the AddNode() method return value equals ev->Good().
+{
+ if ( ioNode && ev->Good() )
+ {
+ morkNode* node = 0; // old val in the map
+
+ mork_bool put = this->Put(ev, &inToken, &ioNode,
+ /*key*/ (void*) 0, &node, (mork_change**) 0);
+
+ if ( put ) // replaced an existing value for key inToken?
+ {
+ if ( node && node != ioNode ) // need to release old node?
+ node->CutStrongRef(ev);
+ }
+
+ if ( ev->Bad() || !ioNode->AddStrongRef(ev) )
+ {
+ // problems adding node or increasing refcount?
+ this->Cut(ev, &inToken, // make sure not in map
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+ }
+ }
+ else if ( !ioNode )
+ ev->NilPointerError();
+
+ return ev->Good();
+}
+
+mork_bool
+morkNodeMap::CutNode(morkEnv* ev, mork_token inToken)
+{
+ morkNode* node = 0; // old val in the map
+ mork_bool outCutNode = this->Cut(ev, &inToken,
+ /*key*/ (void*) 0, &node, (mork_change**) 0);
+ if ( node )
+ node->CutStrongRef(ev);
+
+ return outCutNode;
+}
+
+morkNode*
+morkNodeMap::GetNode(morkEnv* ev, mork_token inToken)
+ // Note the returned node does NOT have an increase in refcount for this.
+{
+ morkNode* node = 0; // old val in the map
+ this->Get(ev, &inToken, /*key*/ (void*) 0, &node, (mork_change**) 0);
+
+ return node;
+}
+
+mork_num
+morkNodeMap::CutAllNodes(morkEnv* ev)
+ // CutAllNodes() releases all the reference node values.
+{
+ mork_num outSlots = mMap_Slots;
+ mork_token key = 0; // old key token in the map
+ morkNode* val = 0; // old val node in the map
+
+ mork_change* c = 0;
+ morkNodeMapIter i(ev, this);
+ for ( c = i.FirstNode(ev, &key, &val); c ; c = i.NextNode(ev, &key, &val) )
+ {
+ if ( val )
+ val->CutStrongRef(ev);
+ i.CutHereNode(ev, /*key*/ (mork_token*) 0, /*val*/ (morkNode**) 0);
+ }
+
+ return outSlots;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkNodeMap.h b/db/mork/src/morkNodeMap.h
new file mode 100644
index 000000000..9f4f8e519
--- /dev/null
+++ b/db/mork/src/morkNodeMap.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKNODEMAP_
+#define _MORKNODEMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKINTMAP_
+#include "morkIntMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kNodeMap /*i*/ 0x6E4D /* ascii 'nM' */
+
+#define morkNodeMap_kStartSlotCount 512
+
+/*| morkNodeMap: maps mork_token -> morkNode
+|*/
+class morkNodeMap : public morkIntMap { // for mapping tokens to nodes
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseNodeMap() only if open
+ virtual ~morkNodeMap(); // assert that CloseNodeMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkNodeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseNodeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsNodeMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kNodeMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+ // use the Equal() and Hash() for mork_u4 inherited from morkIntMap
+// } ===== end morkMap poly interface =====
+
+protected: // we want all subclasses to provide typesafe wrappers:
+
+ mork_bool AddNode(morkEnv* ev, mork_token inToken, morkNode* ioNode);
+ // the AddNode() boolean return equals ev->Good().
+
+ mork_bool CutNode(morkEnv* ev, mork_token inToken);
+ // The CutNode() boolean return indicates whether removal happened.
+
+ morkNode* GetNode(morkEnv* ev, mork_token inToken);
+ // Note the returned node does NOT have an increase in refcount for this.
+
+ mork_num CutAllNodes(morkEnv* ev);
+ // CutAllNodes() releases all the reference node values.
+};
+
+class morkNodeMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkNodeMapIter(morkEnv* ev, morkNodeMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkNodeMapIter( ) : morkMapIter() { }
+ void InitNodeMapIter(morkEnv* ev, morkNodeMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change*
+ FirstNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+ { return this->First(ev, outToken, outNode); }
+
+ mork_change*
+ NextNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+ { return this->Next(ev, outToken, outNode); }
+
+ mork_change*
+ HereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+ { return this->Here(ev, outToken, outNode); }
+
+ mork_change*
+ CutHereNode(morkEnv* ev, mork_token* outToken, morkNode** outNode)
+ { return this->CutHere(ev, outToken, outNode); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKNODEMAP_ */
diff --git a/db/mork/src/morkObject.cpp b/db/mork/src/morkObject.cpp
new file mode 100644
index 000000000..f04aff4d3
--- /dev/null
+++ b/db/mork/src/morkObject.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+#include "nsCOMPtr.h"
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+NS_IMPL_ISUPPORTS(morkObject, nsIMdbObject)
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkObject::CloseMorkNode(morkEnv* ev) // CloseObject() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseObject(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkObject::~morkObject() // assert CloseObject() executed earlier
+{
+ if (!IsShutNode())
+ CloseMorkNode(this->mMorkEnv);
+ MORK_ASSERT(mObject_Handle==0);
+}
+
+/*public non-poly*/
+morkObject::morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor)
+: morkBead(inUsage, ioHeap, inBeadColor)
+, mObject_Handle( 0 )
+{
+ mMorkEnv = nullptr;
+}
+
+/*public non-poly*/
+morkObject::morkObject(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor, morkHandle* ioHandle)
+: morkBead(ev, inUsage, ioHeap, inBeadColor)
+, mObject_Handle( 0 )
+{
+ mMorkEnv = ev;
+ if ( ev->Good() )
+ {
+ if ( ioHandle )
+ morkHandle::SlotWeakHandle(ioHandle, ev, &mObject_Handle);
+
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kObject;
+ }
+}
+
+/*public non-poly*/ void
+morkObject::CloseObject(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ if ( !this->IsShutNode() )
+ {
+ if ( mObject_Handle )
+ morkHandle::SlotWeakHandle((morkHandle*) 0L, ev, &mObject_Handle);
+
+ mBead_Color = 0; // this->CloseBead(ev);
+ this->MarkShut();
+ }
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// { ----- begin factory methods -----
+NS_IMETHODIMP
+morkObject::GetMdbFactory(nsIMdbEnv* mev, nsIMdbFactory** acqFactory)
+{
+ nsresult rv;
+ nsCOMPtr <nsIMdbObject> obj = do_QueryInterface(mev);
+ if (obj)
+ rv = obj->GetMdbFactory(mev, acqFactory);
+ else
+ return NS_ERROR_NO_INTERFACE;
+
+ return rv;
+}
+// } ----- end factory methods -----
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkObject::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+ mdb_count* outCount)
+{
+ *outCount = WeakRefsOnly();
+ return NS_OK;
+}
+NS_IMETHODIMP
+morkObject::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+ mdb_count* outCount)
+{
+ *outCount = StrongRefsOnly();
+ return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkObject::AddWeakRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::AddWeakRef((morkEnv *) mev));
+}
+
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkObject::AddStrongRef(morkEnv* mev)
+{
+ return morkNode::AddStrongRef(mev);
+}
+#endif
+
+NS_IMETHODIMP_(mork_uses)
+morkObject::AddStrongRef(nsIMdbEnv* mev)
+{
+ return morkNode::AddStrongRef((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkObject::CutWeakRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::CutWeakRef((morkEnv *) mev));
+}
+
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkObject::CutStrongRef(morkEnv* mev)
+{
+ return morkNode::CutStrongRef(mev);
+}
+#endif
+
+NS_IMETHODIMP
+morkObject::CutStrongRef(nsIMdbEnv* mev)
+{
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::CutStrongRef((morkEnv *) mev));
+}
+
+NS_IMETHODIMP
+morkObject::CloseMdbObject(nsIMdbEnv* mev)
+{
+ return morkNode::CloseMdbObject((morkEnv *) mev);
+}
+
+NS_IMETHODIMP
+morkObject::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+ *outOpen = IsOpenNode();
+ return NS_OK;
+}
+NS_IMETHODIMP
+morkObject::IsFrozenMdbObject(nsIMdbEnv* mev, mdb_bool* outIsReadonly)
+{
+ *outIsReadonly = IsFrozen();
+ return NS_OK;
+}
+
+//void morkObject::NewNilHandleError(morkEnv* ev) // mObject_Handle is nil
+//{
+// ev->NewError("nil mObject_Handle");
+//}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkObject.h b/db/mork/src/morkObject.h
new file mode 100644
index 000000000..eac478d35
--- /dev/null
+++ b/db/mork/src/morkObject.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKOBJECT_
+#define _MORKOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+#ifndef _MORKCONFIG_
+#include "morkConfig.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kObject /*i*/ 0x6F42 /* ascii 'oB' */
+
+/*| morkObject: subclass of morkNode that adds knowledge of db suite factory
+**| and containing port to those objects that are exposed as instances of
+**| nsIMdbObject in the public interface.
+|*/
+class morkObject : public morkBead, public nsIMdbObject {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+
+public: // state is public because the entire Mork system is private
+
+ morkHandle* mObject_Handle; // weak ref to handle for this object
+
+ morkEnv * mMorkEnv; // weak ref to environment this object created in.
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseObject() only if open
+#ifdef MORK_DEBUG_HEAP_STATS
+ void operator delete(void* ioAddress, size_t size)
+ {
+ mork_u4* array = (mork_u4*) ioAddress;
+ array -= 3;
+ orkinHeap *heap = (orkinHeap *) *array;
+ if (heap)
+ heap->Free(nullptr, ioAddress);
+ }
+#endif
+
+ NS_DECL_ISUPPORTS
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly) override;
+ // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+ // } ----- end attribute methods -----
+
+ // { ----- begin factory methods -----
+ NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory) override;
+ // } ----- end factory methods -----
+
+ // { ----- begin ref counting for well-behaved cyclic graphs -----
+ NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+ mdb_count* outCount) override;
+ NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+ mdb_count* outCount) override;
+
+ NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero
+ NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override;
+ // } ----- end ref counting -----
+
+
+protected: // special case construction of first env without preceding env
+ morkObject(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor);
+ virtual ~morkObject(); // assert that CloseObject() executed earlier
+
+public: // morkEnv construction & destruction
+ morkObject(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ mork_color inBeadColor, morkHandle* ioHandle); // ioHandle can be nil
+ void CloseObject(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkObject(const morkObject& other);
+ morkObject& operator=(const morkObject& other);
+
+public: // dynamic type identification
+ mork_bool IsObject() const
+ { return IsNode() && mNode_Derived == morkDerived_kObject; }
+// } ===== end morkNode methods =====
+
+ // void NewNilHandleError(morkEnv* ev); // mObject_Handle is nil
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakObject(morkObject* me,
+ morkEnv* ev, morkObject** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongObject(morkObject* me,
+ morkEnv* ev, morkObject** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKOBJECT_ */
diff --git a/db/mork/src/morkParser.cpp b/db/mork/src/morkParser.cpp
new file mode 100644
index 000000000..667a597fd
--- /dev/null
+++ b/db/mork/src/morkParser.cpp
@@ -0,0 +1,1568 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkParser::CloseMorkNode(morkEnv* ev) // CloseParser() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseParser(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkParser::~morkParser() // assert CloseParser() executed earlier
+{
+ MORK_ASSERT(mParser_Heap==0);
+ MORK_ASSERT(mParser_Stream==0);
+}
+
+/*public non-poly*/
+morkParser::morkParser(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkStream* ioStream, mdb_count inBytesPerParseSegment,
+ nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mParser_Heap( 0 )
+, mParser_Stream( 0 )
+, mParser_MoreGranularity( inBytesPerParseSegment )
+, mParser_State( morkParser_kStartState )
+
+, mParser_GroupContentStartPos( 0 )
+
+, mParser_TableMid( )
+, mParser_RowMid( )
+, mParser_CellMid( )
+
+, mParser_InPort( morkBool_kFalse )
+, mParser_InDict( morkBool_kFalse )
+, mParser_InCell( morkBool_kFalse )
+, mParser_InMeta( morkBool_kFalse )
+
+, mParser_InPortRow( morkBool_kFalse )
+, mParser_InRow( morkBool_kFalse )
+, mParser_InTable( morkBool_kFalse )
+, mParser_InGroup( morkBool_kFalse )
+
+, mParser_AtomChange( morkChange_kNil )
+, mParser_CellChange( morkChange_kNil )
+, mParser_RowChange( morkChange_kNil )
+, mParser_TableChange( morkChange_kNil )
+
+, mParser_Change( morkChange_kNil )
+, mParser_IsBroken( morkBool_kFalse )
+, mParser_IsDone( morkBool_kFalse )
+, mParser_DoMore( morkBool_kTrue )
+
+, mParser_Mid()
+
+, mParser_ScopeCoil(ev, ioSlotHeap)
+, mParser_ValueCoil(ev, ioSlotHeap)
+, mParser_ColumnCoil(ev, ioSlotHeap)
+, mParser_StringCoil(ev, ioSlotHeap)
+
+, mParser_ScopeSpool(ev, &mParser_ScopeCoil)
+, mParser_ValueSpool(ev, &mParser_ValueCoil)
+, mParser_ColumnSpool(ev, &mParser_ColumnCoil)
+, mParser_StringSpool(ev, &mParser_StringCoil)
+
+, mParser_MidYarn(ev, morkUsage(morkUsage_kMember), ioSlotHeap)
+{
+ if ( inBytesPerParseSegment < morkParser_kMinGranularity )
+ inBytesPerParseSegment = morkParser_kMinGranularity;
+ else if ( inBytesPerParseSegment > morkParser_kMaxGranularity )
+ inBytesPerParseSegment = morkParser_kMaxGranularity;
+
+ mParser_MoreGranularity = inBytesPerParseSegment;
+
+ if ( ioSlotHeap && ioStream )
+ {
+ nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mParser_Heap);
+ morkStream::SlotStrongStream(ioStream, ev, &mParser_Stream);
+
+ if ( ev->Good() )
+ {
+ mParser_Tag = morkParser_kTag;
+ mNode_Derived = morkDerived_kParser;
+ }
+ }
+ else
+ ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkParser::CloseParser(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ if ( !this->IsShutNode() )
+ {
+ mParser_ScopeCoil.CloseCoil(ev);
+ mParser_ValueCoil.CloseCoil(ev);
+ mParser_ColumnCoil.CloseCoil(ev);
+ mParser_StringCoil.CloseCoil(ev);
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mParser_Heap);
+ morkStream::SlotStrongStream((morkStream*) 0, ev, &mParser_Stream);
+ this->MarkShut();
+ }
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*protected non-poly*/ void
+morkParser::NonGoodParserError(morkEnv* ev) // when GoodParserTag() is false
+{
+ ev->NewError("non-morkNode");
+}
+
+/*protected non-poly*/ void
+morkParser::NonUsableParserError(morkEnv* ev) //
+{
+ if ( this->IsNode() )
+ {
+ if ( this->IsOpenNode() )
+ {
+ if ( this->GoodParserTag() )
+ {
+ // okay
+ }
+ else
+ this->NonGoodParserError(ev);
+ }
+ else
+ this->NonOpenNodeError(ev);
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+
+/*protected non-poly*/ void
+morkParser::StartParse(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mParser_InCell = morkBool_kFalse;
+ mParser_InMeta = morkBool_kFalse;
+ mParser_InDict = morkBool_kFalse;
+ mParser_InPortRow = morkBool_kFalse;
+
+ mParser_RowMid.ClearMid();
+ mParser_TableMid.ClearMid();
+ mParser_CellMid.ClearMid();
+
+ mParser_GroupId = 0;
+ mParser_InPort = morkBool_kTrue;
+
+ mParser_GroupSpan.ClearSpan();
+ mParser_DictSpan.ClearSpan();
+ mParser_AliasSpan.ClearSpan();
+ mParser_MetaSpan.ClearSpan();
+ mParser_TableSpan.ClearSpan();
+ mParser_RowSpan.ClearSpan();
+ mParser_CellSpan.ClearSpan();
+ mParser_ColumnSpan.ClearSpan();
+ mParser_SlotSpan.ClearSpan();
+
+ mParser_PortSpan.ClearSpan();
+}
+
+/*protected non-poly*/ void
+morkParser::StopParse(morkEnv* ev)
+{
+ if ( mParser_InCell )
+ {
+ mParser_InCell = morkBool_kFalse;
+ mParser_CellSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnCellEnd(ev, mParser_CellSpan);
+ }
+ if ( mParser_InMeta )
+ {
+ mParser_InMeta = morkBool_kFalse;
+ mParser_MetaSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnMetaEnd(ev, mParser_MetaSpan);
+ }
+ if ( mParser_InDict )
+ {
+ mParser_InDict = morkBool_kFalse;
+ mParser_DictSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnDictEnd(ev, mParser_DictSpan);
+ }
+ if ( mParser_InPortRow )
+ {
+ mParser_InPortRow = morkBool_kFalse;
+ mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnPortRowEnd(ev, mParser_RowSpan);
+ }
+ if ( mParser_InRow )
+ {
+ mParser_InRow = morkBool_kFalse;
+ mParser_RowMid.ClearMid();
+ mParser_RowSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnRowEnd(ev, mParser_RowSpan);
+ }
+ if ( mParser_InTable )
+ {
+ mParser_InTable = morkBool_kFalse;
+ mParser_TableMid.ClearMid();
+ mParser_TableSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnTableEnd(ev, mParser_TableSpan);
+ }
+ if ( mParser_GroupId )
+ {
+ mParser_GroupId = 0;
+ mParser_GroupSpan.SetEndWithEnd(mParser_PortSpan);
+ this->OnGroupAbortEnd(ev, mParser_GroupSpan);
+ }
+ if ( mParser_InPort )
+ {
+ mParser_InPort = morkBool_kFalse;
+ this->OnPortEnd(ev, mParser_PortSpan);
+ }
+}
+
+int morkParser::eat_comment(morkEnv* ev) // last char was '/'
+{
+ morkStream* s = mParser_Stream;
+ // Note morkStream::Getc() returns EOF when an error occurs, so
+ // we don't need to check for both c != EOF and ev->Good() below.
+
+ int c = s->Getc(ev);
+ if ( c == '/' ) // C++ style comment?
+ {
+ while ( (c = s->Getc(ev)) != EOF && c != 0xA && c != 0xD )
+ /* empty */;
+
+ if ( c == 0xA || c == 0xD )
+ c = this->eat_line_break(ev, c);
+ }
+ else if ( c == '*' ) /* C style comment? */
+ {
+ int depth = 1; // count depth of comments until depth reaches zero
+
+ while ( depth > 0 && c != EOF ) // still looking for comment end(s)?
+ {
+ while ( (c = s->Getc(ev)) != EOF && c != '/' && c != '*' )
+ {
+ if ( c == 0xA || c == 0xD ) // need to count a line break?
+ {
+ c = this->eat_line_break(ev, c);
+ if ( c == '/' || c == '*' )
+ break; // end while loop
+ }
+ }
+
+ if ( c == '*' ) // maybe end of a comment, if next char is '/'?
+ {
+ if ( (c = s->Getc(ev)) == '/' ) // end of comment?
+ {
+ --depth; // depth of comments has decreased by one
+ if ( !depth ) // comments all done?
+ c = s->Getc(ev); // return the byte after end of comment
+ }
+ else if ( c != EOF ) // need to put the char back?
+ s->Ungetc(c); // especially need to put back '*', 0xA, or 0xD
+ }
+ else if ( c == '/' ) // maybe nested comemnt, if next char is '*'?
+ {
+ if ( (c = s->Getc(ev)) == '*' ) // nested comment?
+ ++depth; // depth of comments has increased by one
+ else if ( c != EOF ) // need to put the char back?
+ s->Ungetc(c); // especially need to put back '/', 0xA, or 0xD
+ }
+
+ if ( ev->Bad() )
+ c = EOF;
+ }
+ if ( c == EOF && depth > 0 )
+ ev->NewWarning("EOF before end of comment");
+ }
+ else
+ ev->NewWarning("expected / or *");
+
+ return c;
+}
+
+int morkParser::eat_line_break(morkEnv* ev, int inLast)
+{
+ morkStream* s = mParser_Stream;
+ int c = s->Getc(ev); // get next char after 0xA or 0xD
+ this->CountLineBreak();
+ if ( c == 0xA || c == 0xD ) // another line break character?
+ {
+ if ( c != inLast ) // not the same as the last one?
+ c = s->Getc(ev); // get next char after two-byte linebreak
+ }
+ return c;
+}
+
+int morkParser::eat_line_continue(morkEnv* ev) // last char was '\'
+{
+ morkStream* s = mParser_Stream;
+ int c = s->Getc(ev);
+ if ( c == 0xA || c == 0xD ) // linebreak follows \ as expected?
+ {
+ c = this->eat_line_break(ev, c);
+ }
+ else
+ ev->NewWarning("expected linebreak");
+
+ return c;
+}
+
+int morkParser::NextChar(morkEnv* ev) // next non-white content
+{
+ morkStream* s = mParser_Stream;
+ int c = s->Getc(ev);
+ while ( c > 0 && ev->Good() )
+ {
+ if ( c == '/' )
+ c = this->eat_comment(ev);
+ else if ( c == 0xA || c == 0xD )
+ c = this->eat_line_break(ev, c);
+ else if ( c == '\\' )
+ c = this->eat_line_continue(ev);
+ else if ( morkCh_IsWhite(c) )
+ c = s->Getc(ev);
+ else
+ break; // end while loop when return c is acceptable
+ }
+ if ( ev->Bad() )
+ {
+ mParser_State = morkParser_kBrokenState;
+ mParser_DoMore = morkBool_kFalse;
+ mParser_IsDone = morkBool_kTrue;
+ mParser_IsBroken = morkBool_kTrue;
+ c = EOF;
+ }
+ else if ( c == EOF )
+ {
+ mParser_DoMore = morkBool_kFalse;
+ mParser_IsDone = morkBool_kTrue;
+ }
+ return c;
+}
+
+void
+morkParser::OnCellState(morkEnv* ev)
+{
+ ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnMetaState(morkEnv* ev)
+{
+ ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnRowState(morkEnv* ev)
+{
+ ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnTableState(morkEnv* ev)
+{
+ ev->StubMethodOnlyError();
+}
+
+void
+morkParser::OnDictState(morkEnv* ev)
+{
+ ev->StubMethodOnlyError();
+}
+
+morkBuf* morkParser::ReadName(morkEnv* ev, int c)
+{
+ morkBuf* outBuf = 0;
+
+ if ( !morkCh_IsName(c) )
+ ev->NewError("not a name char");
+
+ morkCoil* coil = &mParser_ColumnCoil;
+ coil->ClearBufFill();
+
+ morkSpool* spool = &mParser_ColumnSpool;
+ spool->Seek(ev, /*pos*/ 0);
+
+ if ( ev->Good() )
+ {
+ spool->Putc(ev, c);
+
+ morkStream* s = mParser_Stream;
+ while ( (c = s->Getc(ev)) != EOF && morkCh_IsMore(c) && ev->Good() )
+ spool->Putc(ev, c);
+
+ if ( ev->Good() )
+ {
+ if ( c != EOF )
+ {
+ s->Ungetc(c);
+ spool->FlushSink(ev); // update coil->mBuf_Fill
+ }
+ else
+ this->UnexpectedEofError(ev);
+
+ if ( ev->Good() )
+ outBuf = coil;
+ }
+ }
+ return outBuf;
+}
+
+mork_bool
+morkParser::ReadMid(morkEnv* ev, morkMid* outMid)
+{
+ outMid->ClearMid();
+
+ morkStream* s = mParser_Stream;
+ int next;
+ outMid->mMid_Oid.mOid_Id = this->ReadHex(ev, &next);
+ int c = next;
+ if ( c == ':' )
+ {
+ if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+ {
+ if ( c == '^' )
+ {
+ outMid->mMid_Oid.mOid_Scope = this->ReadHex(ev, &next);
+ if ( ev->Good() )
+ s->Ungetc(next);
+ }
+ else if ( morkCh_IsName(c) )
+ {
+ outMid->mMid_Buf = this->ReadName(ev, c);
+ }
+ else
+ ev->NewError("expected name or hex after ':' following ID");
+ }
+
+ if ( c == EOF && ev->Good() )
+ this->UnexpectedEofError(ev);
+ }
+ else
+ s->Ungetc(c);
+
+ return ev->Good();
+}
+
+void
+morkParser::ReadCell(morkEnv* ev)
+{
+ mParser_CellMid.ClearMid();
+ // this->StartSpanOnLastByte(ev, &mParser_CellSpan);
+ morkMid* cellMid = 0; // if mid syntax is used for column
+ morkBuf* cellBuf = 0; // if naked string is used for column
+
+ morkStream* s = mParser_Stream;
+ int c;
+ if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+ {
+ // this->StartSpanOnLastByte(ev, &mParser_ColumnSpan);
+ if ( c == '^' )
+ {
+ cellMid = &mParser_CellMid;
+ this->ReadMid(ev, cellMid);
+ // if ( !mParser_CellMid.mMid_Oid.mOid_Scope )
+ // mParser_CellMid.mMid_Oid.mOid_Scope = (mork_scope) 'c';
+ }
+ else
+ {
+ if (mParser_InMeta && c == morkStore_kFormColumn)
+ {
+ ReadCellForm(ev, c);
+ return;
+ }
+ else
+ cellBuf = this->ReadName(ev, c);
+ }
+ if ( ev->Good() )
+ {
+ // this->EndSpanOnThisByte(ev, &mParser_ColumnSpan);
+
+ mParser_InCell = morkBool_kTrue;
+ this->OnNewCell(ev, *mParser_CellSpan.AsPlace(),
+ cellMid, cellBuf); // , mParser_CellChange
+
+ mParser_CellChange = morkChange_kNil;
+ if ( (c = this->NextChar(ev)) != EOF && ev->Good() )
+ {
+ // this->StartSpanOnLastByte(ev, &mParser_SlotSpan);
+ if ( c == '=' )
+ {
+ morkBuf* buf = this->ReadValue(ev);
+ if ( buf )
+ {
+ // this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
+ this->OnValue(ev, mParser_SlotSpan, *buf);
+ }
+ }
+ else if ( c == '^' )
+ {
+ if ( this->ReadMid(ev, &mParser_Mid) )
+ {
+ // this->EndSpanOnThisByte(ev, &mParser_SlotSpan);
+ if ( (c = this->NextChar(ev)) != EOF && ev->Good() )
+ {
+ if ( c != ')' )
+ ev->NewError("expected ')' after cell ^ID value");
+ }
+ else if ( c == EOF )
+ this->UnexpectedEofError(ev);
+
+ if ( ev->Good() )
+ this->OnValueMid(ev, mParser_SlotSpan, mParser_Mid);
+ }
+ }
+ else if ( c == 'r' || c == 't' || c == '"' || c == '\'' )
+ {
+ ev->NewError("cell syntax not yet supported");
+ }
+ else
+ {
+ ev->NewError("unknown cell syntax");
+ }
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_CellSpan);
+ mParser_InCell = morkBool_kFalse;
+ this->OnCellEnd(ev, mParser_CellSpan);
+ }
+ }
+ mParser_CellChange = morkChange_kNil;
+
+ if ( c == EOF && ev->Good() )
+ this->UnexpectedEofError(ev);
+}
+
+void morkParser::ReadRowPos(morkEnv* ev)
+{
+ int c; // next character
+ mork_pos rowPos = this->ReadHex(ev, &c);
+
+ if ( ev->Good() && c != EOF ) // should put back byte after hex?
+ mParser_Stream->Ungetc(c);
+
+ this->OnRowPos(ev, rowPos);
+}
+
+void morkParser::ReadRow(morkEnv* ev, int c)
+// zm:Row ::= zm:S? '[' zm:S? zm:Id zm:RowItem* zm:S? ']'
+// zm:RowItem ::= zm:MetaRow | zm:Cell
+// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
+// zm:Cell ::= zm:S? '(' zm:Column zm:S? zm:Slot? ')'
+{
+ if ( ev->Good() )
+ {
+ // this->StartSpanOnLastByte(ev, &mParser_RowSpan);
+ if ( mParser_Change )
+ mParser_RowChange = mParser_Change;
+
+ mork_bool cutAllRowCols = morkBool_kFalse;
+
+ if ( c == '[' )
+ {
+ if ( ( c = this->NextChar(ev) ) == '-' )
+ cutAllRowCols = morkBool_kTrue;
+ else if ( ev->Good() && c != EOF )
+ mParser_Stream->Ungetc(c);
+
+ if ( this->ReadMid(ev, &mParser_RowMid) )
+ {
+ mParser_InRow = morkBool_kTrue;
+ this->OnNewRow(ev, *mParser_RowSpan.AsPlace(),
+ mParser_RowMid, cutAllRowCols);
+
+ mParser_Change = mParser_RowChange = morkChange_kNil;
+
+ while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != ']' )
+ {
+ switch ( c )
+ {
+ case '(': // cell
+ this->ReadCell(ev);
+ break;
+
+ case '[': // meta
+ this->ReadMeta(ev, ']');
+ break;
+
+ // case '+': // plus
+ // mParser_CellChange = morkChange_kAdd;
+ // break;
+
+ case '-': // minus
+ // mParser_CellChange = morkChange_kCut;
+ this->OnMinusCell(ev);
+ break;
+
+ // case '!': // bang
+ // mParser_CellChange = morkChange_kSet;
+ // break;
+
+ default:
+ ev->NewWarning("unexpected byte in row");
+ break;
+ } // switch
+ } // while
+
+ if ( ev->Good() )
+ {
+ if ( (c = this->NextChar(ev)) == '!' )
+ this->ReadRowPos(ev);
+ else if ( c != EOF && ev->Good() )
+ mParser_Stream->Ungetc(c);
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_RowSpan);
+ mParser_InRow = morkBool_kFalse;
+ this->OnRowEnd(ev, mParser_RowSpan);
+
+ } // if ReadMid
+ } // if '['
+
+ else // c != '['
+ {
+ morkStream* s = mParser_Stream;
+ s->Ungetc(c);
+ if ( this->ReadMid(ev, &mParser_RowMid) )
+ {
+ mParser_InRow = morkBool_kTrue;
+ this->OnNewRow(ev, *mParser_RowSpan.AsPlace(),
+ mParser_RowMid, cutAllRowCols);
+
+ mParser_Change = mParser_RowChange = morkChange_kNil;
+
+ if ( ev->Good() )
+ {
+ if ( (c = this->NextChar(ev)) == '!' )
+ this->ReadRowPos(ev);
+ else if ( c != EOF && ev->Good() )
+ s->Ungetc(c);
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_RowSpan);
+ mParser_InRow = morkBool_kFalse;
+ this->OnRowEnd(ev, mParser_RowSpan);
+ }
+ }
+ }
+
+ if ( ev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+ else if ( c == EOF )
+ mParser_State = morkParser_kDoneState;
+}
+
+void morkParser::ReadTable(morkEnv* ev)
+// zm:Table ::= zm:S? '{' zm:S? zm:Id zm:TableItem* zm:S? '}'
+// zm:TableItem ::= zm:MetaTable | zm:RowRef | zm:Row
+// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
+{
+ // this->StartSpanOnLastByte(ev, &mParser_TableSpan);
+
+ if ( mParser_Change )
+ mParser_TableChange = mParser_Change;
+
+ mork_bool cutAllTableRows = morkBool_kFalse;
+
+ int c = this->NextChar(ev);
+ if ( c == '-' )
+ cutAllTableRows = morkBool_kTrue;
+ else if ( ev->Good() && c != EOF )
+ mParser_Stream->Ungetc(c);
+
+ if ( ev->Good() && this->ReadMid(ev, &mParser_TableMid) )
+ {
+ mParser_InTable = morkBool_kTrue;
+ this->OnNewTable(ev, *mParser_TableSpan.AsPlace(),
+ mParser_TableMid, cutAllTableRows);
+
+ mParser_Change = mParser_TableChange = morkChange_kNil;
+
+ while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '}' )
+ {
+ if ( morkCh_IsHex(c) )
+ {
+ this->ReadRow(ev, c);
+ }
+ else
+ {
+ switch ( c )
+ {
+ case '[': // row
+ this->ReadRow(ev, '[');
+ break;
+
+ case '{': // meta
+ this->ReadMeta(ev, '}');
+ break;
+
+ // case '+': // plus
+ // mParser_RowChange = morkChange_kAdd;
+ // break;
+
+ case '-': // minus
+ // mParser_RowChange = morkChange_kCut;
+ this->OnMinusRow(ev);
+ break;
+
+ // case '!': // bang
+ // mParser_RowChange = morkChange_kSet;
+ // break;
+
+ default:
+ ev->NewWarning("unexpected byte in table");
+ break;
+ }
+ }
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_TableSpan);
+ mParser_InTable = morkBool_kFalse;
+ this->OnTableEnd(ev, mParser_TableSpan);
+
+ if ( ev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+ else if ( c == EOF )
+ mParser_State = morkParser_kDoneState;
+ }
+}
+
+mork_id morkParser::ReadHex(morkEnv* ev, int* outNextChar)
+// zm:Hex ::= [0-9a-fA-F] /* a single hex digit */
+// zm:Hex+ ::= zm:Hex | zm:Hex zm:Hex+
+{
+ mork_id hex = 0;
+
+ morkStream* s = mParser_Stream;
+ int c = this->NextChar(ev);
+
+ if ( ev->Good() )
+ {
+ if ( c != EOF )
+ {
+ if ( morkCh_IsHex(c) )
+ {
+ do
+ {
+ if ( morkCh_IsDigit(c) ) // '0' through '9'?
+ c -= '0';
+ else if ( morkCh_IsUpper(c) ) // 'A' through 'F'?
+ c -= ('A' - 10) ; // c = (c - 'A') + 10;
+ else // 'a' through 'f'?
+ c -= ('a' - 10) ; // c = (c - 'a') + 10;
+
+ hex = (hex << 4) + c;
+ }
+ while ( (c = s->Getc(ev)) != EOF && ev->Good() && morkCh_IsHex(c) );
+ }
+ else
+ this->ExpectedHexDigitError(ev, c);
+ }
+ }
+ if ( c == EOF )
+ this->EofInsteadOfHexError(ev);
+
+ *outNextChar = c;
+ return hex;
+}
+
+/*static*/ void
+morkParser::EofInsteadOfHexError(morkEnv* ev)
+{
+ ev->NewWarning("eof instead of hex");
+}
+
+/*static*/ void
+morkParser::ExpectedHexDigitError(morkEnv* ev, int c)
+{
+ MORK_USED_1(c);
+ ev->NewWarning("expected hex digit");
+}
+
+/*static*/ void
+morkParser::ExpectedEqualError(morkEnv* ev)
+{
+ ev->NewWarning("expected '='");
+}
+
+/*static*/ void
+morkParser::UnexpectedEofError(morkEnv* ev)
+{
+ ev->NewWarning("unexpected eof");
+}
+
+
+morkBuf* morkParser::ReadValue(morkEnv* ev)
+{
+ morkBuf* outBuf = 0;
+
+ morkCoil* coil = &mParser_ValueCoil;
+ coil->ClearBufFill();
+
+ morkSpool* spool = &mParser_ValueSpool;
+ spool->Seek(ev, /*pos*/ 0);
+
+ if ( ev->Good() )
+ {
+ morkStream* s = mParser_Stream;
+ int c;
+ while ( (c = s->Getc(ev)) != EOF && c != ')' && ev->Good() )
+ {
+ if ( c == '\\' ) // next char is escaped by '\'?
+ {
+ if ( (c = s->Getc(ev)) == 0xA || c == 0xD ) // linebreak after \?
+ {
+ c = this->eat_line_break(ev, c);
+ if ( c == ')' || c == '\\' || c == '$' )
+ {
+ s->Ungetc(c); // just let while loop test read this again
+ continue; // goto next iteration of while loop
+ }
+ }
+ if ( c == EOF || ev->Bad() )
+ break; // end while loop
+ }
+ else if ( c == '$' ) // "$" escapes next two hex digits?
+ {
+ if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+ {
+ mork_ch first = (mork_ch) c; // first hex digit
+ if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+ {
+ mork_ch second = (mork_ch) c; // second hex digit
+ c = ev->HexToByte(first, second);
+ }
+ else
+ break; // end while loop
+ }
+ else
+ break; // end while loop
+ }
+ spool->Putc(ev, c);
+ }
+
+ if ( ev->Good() )
+ {
+ if ( c != EOF )
+ spool->FlushSink(ev); // update coil->mBuf_Fill
+ else
+ this->UnexpectedEofError(ev);
+
+ if ( ev->Good() )
+ outBuf = coil;
+ }
+ }
+ return outBuf;
+}
+
+void morkParser::ReadDictForm(morkEnv *ev)
+{
+ int nextChar;
+ nextChar = this->NextChar(ev);
+ if (nextChar == '(')
+ {
+ nextChar = this->NextChar(ev);
+ if (nextChar == morkStore_kFormColumn)
+ {
+ int dictForm;
+
+ nextChar = this->NextChar(ev);
+ if (nextChar == '=')
+ {
+ dictForm = this->NextChar(ev);
+ nextChar = this->NextChar(ev);
+ }
+ else if (nextChar == '^')
+ {
+ dictForm = this->ReadHex(ev, &nextChar);
+ }
+ else
+ {
+ ev->NewWarning("unexpected byte in dict form");
+ return;
+ }
+ mParser_ValueCoil.mText_Form = dictForm;
+ if (nextChar == ')')
+ {
+ nextChar = this->NextChar(ev);
+ if (nextChar == '>')
+ return;
+ }
+ }
+ }
+ ev->NewWarning("unexpected byte in dict form");
+}
+
+void morkParser::ReadCellForm(morkEnv *ev, int c)
+{
+ MORK_ASSERT (c == morkStore_kFormColumn);
+ int nextChar;
+ nextChar = this->NextChar(ev);
+ int cellForm;
+
+ if (nextChar == '=')
+ {
+ cellForm = this->NextChar(ev);
+ nextChar = this->NextChar(ev);
+ }
+ else if (nextChar == '^')
+ {
+ cellForm = this->ReadHex(ev, &nextChar);
+ }
+ else
+ {
+ ev->NewWarning("unexpected byte in cell form");
+ return;
+ }
+ // ### not sure about this. Which form should we set?
+ // mBuilder_CellForm = mBuilder_RowForm = cellForm;
+ if (nextChar == ')')
+ {
+ OnCellForm(ev, cellForm);
+ return;
+ }
+ ev->NewWarning("unexpected byte in cell form");
+}
+
+void morkParser::ReadAlias(morkEnv* ev)
+// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
+// zm:Value ::= '=' ([^)$\] | '\' zm:NonCRLF | zm:Continue | zm:Dollar)*
+{
+ // this->StartSpanOnLastByte(ev, &mParser_AliasSpan);
+
+ int nextChar;
+ mork_id hex = this->ReadHex(ev, &nextChar);
+ int c = nextChar;
+
+ mParser_Mid.ClearMid();
+ mParser_Mid.mMid_Oid.mOid_Id = hex;
+
+ if ( morkCh_IsWhite(c) && ev->Good() )
+ c = this->NextChar(ev);
+
+ if ( ev->Good() )
+ {
+ if ( c == '<')
+ {
+ ReadDictForm(ev);
+ if (ev->Good())
+ c = this->NextChar(ev);
+ }
+ if ( c == '=' )
+ {
+ mParser_Mid.mMid_Buf = this->ReadValue(ev);
+ if ( mParser_Mid.mMid_Buf )
+ {
+ // this->EndSpanOnThisByte(ev, &mParser_AliasSpan);
+ this->OnAlias(ev, mParser_AliasSpan, mParser_Mid);
+ // need to reset this somewhere.
+ mParser_ValueCoil.mText_Form = 0;
+
+ }
+ }
+ else
+ this->ExpectedEqualError(ev);
+ }
+}
+
+void morkParser::ReadMeta(morkEnv* ev, int inEndMeta)
+// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
+// zm:MetaTable ::= zm:S? '{' zm:S? zm:Cell* zm:S? '}' /* meta attributes */
+// zm:MetaRow ::= zm:S? '[' zm:S? zm:Cell* zm:S? ']' /* meta attributes */
+{
+ // this->StartSpanOnLastByte(ev, &mParser_MetaSpan);
+ mParser_InMeta = morkBool_kTrue;
+ this->OnNewMeta(ev, *mParser_MetaSpan.AsPlace());
+
+ mork_bool more = morkBool_kTrue; // until end meta
+ int c;
+ while ( more && (c = this->NextChar(ev)) != EOF && ev->Good() )
+ {
+ switch ( c )
+ {
+ case '(': // cell
+ this->ReadCell(ev);
+ break;
+
+ case '>': // maybe end meta?
+ if ( inEndMeta == '>' )
+ more = morkBool_kFalse; // stop reading meta
+ else
+ this->UnexpectedByteInMetaWarning(ev);
+ break;
+
+ case '}': // maybe end meta?
+ if ( inEndMeta == '}' )
+ more = morkBool_kFalse; // stop reading meta
+ else
+ this->UnexpectedByteInMetaWarning(ev);
+ break;
+
+ case ']': // maybe end meta?
+ if ( inEndMeta == ']' )
+ more = morkBool_kFalse; // stop reading meta
+ else
+ this->UnexpectedByteInMetaWarning(ev);
+ break;
+
+ case '[': // maybe table meta row?
+ if ( mParser_InTable )
+ this->ReadRow(ev, '[');
+ else
+ this->UnexpectedByteInMetaWarning(ev);
+ break;
+
+ default:
+ if ( mParser_InTable && morkCh_IsHex(c) )
+ this->ReadRow(ev, c);
+ else
+ this->UnexpectedByteInMetaWarning(ev);
+ break;
+ }
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_MetaSpan);
+ mParser_InMeta = morkBool_kFalse;
+ this->OnMetaEnd(ev, mParser_MetaSpan);
+}
+
+/*static*/ void
+morkParser::UnexpectedByteInMetaWarning(morkEnv* ev)
+{
+ ev->NewWarning("unexpected byte in meta");
+}
+
+/*static*/ void
+morkParser::NonParserTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkParser");
+}
+
+mork_bool morkParser::MatchPattern(morkEnv* ev, const char* inPattern)
+{
+ // if an error occurs, we want original inPattern in the debugger:
+ const char* pattern = inPattern; // mutable copy of pointer
+ morkStream* s = mParser_Stream;
+ int c;
+ while ( *pattern && ev->Good() )
+ {
+ char byte = *pattern++;
+ if ( (c = s->Getc(ev)) != byte )
+ {
+ ev->NewError("byte not in expected pattern");
+ }
+ }
+ return ev->Good();
+}
+
+mork_bool morkParser::FindGroupEnd(morkEnv* ev)
+{
+ mork_bool foundEnd = morkBool_kFalse;
+
+ // char gidBuf[ 64 ]; // to hold hex pattern we want
+ // (void) ev->TokenAsHex(gidBuf, mParser_GroupId);
+
+ morkStream* s = mParser_Stream;
+ int c;
+
+ while ( (c = s->Getc(ev)) != EOF && ev->Good() && !foundEnd )
+ {
+ if ( c == '@' ) // maybe start of group ending?
+ {
+ // this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
+ if ( (c = s->Getc(ev)) == '$' ) // '$' follows '@' ?
+ {
+ if ( (c = s->Getc(ev)) == '$' ) // '$' follows "@$" ?
+ {
+ if ( (c = s->Getc(ev)) == '}' )
+ {
+ foundEnd = this->ReadEndGroupId(ev);
+ // this->EndSpanOnThisByte(ev, &mParser_GroupSpan);
+
+ }
+ else
+ ev->NewError("expected '}' after @$$");
+ }
+ }
+ if ( !foundEnd && c == '@' )
+ s->Ungetc(c);
+ }
+ }
+
+ return foundEnd && ev->Good();
+}
+
+void morkParser::ReadGroup(morkEnv* mev)
+{
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ int next = 0;
+ mParser_GroupId = this->ReadHex(mev, &next);
+ if ( next == '{' )
+ {
+ morkStream* s = mParser_Stream;
+ int c;
+ if ( (c = s->Getc(mev)) == '@' )
+ {
+ // we really need the following span inside morkBuilder::OnNewGroup():
+ this->StartSpanOnThisByte(mev, &mParser_GroupSpan);
+ mork_pos startPos = mParser_GroupSpan.mSpan_Start.mPlace_Pos;
+
+ // if ( !store->mStore_FirstCommitGroupPos )
+ // store->mStore_FirstCommitGroupPos = startPos;
+ // else if ( !store->mStore_SecondCommitGroupPos )
+ // store->mStore_SecondCommitGroupPos = startPos;
+
+ if ( this->FindGroupEnd(mev) )
+ {
+ mork_pos outPos;
+ s->Seek(ev, startPos, &outPos);
+ if ( mev->Good() )
+ {
+ this->OnNewGroup(mev, mParser_GroupSpan.mSpan_Start,
+ mParser_GroupId);
+
+ this->ReadContent(mev, /*inInsideGroup*/ morkBool_kTrue);
+
+ this->OnGroupCommitEnd(mev, mParser_GroupSpan);
+ }
+ }
+ }
+ else
+ mev->NewError("expected '@' after @$${id{");
+ }
+ else
+ mev->NewError("expected '{' after @$$id");
+
+}
+
+mork_bool morkParser::ReadAt(morkEnv* ev, mork_bool inInsideGroup)
+/* groups must be ignored until properly terminated */
+// zm:Group ::= zm:GroupStart zm:Content zm:GroupEnd /* transaction */
+// zm:GroupStart ::= zm:S? '@$${' zm:Hex+ '{@' /* xaction id has own space */
+// zm:GroupEnd ::= zm:GroupCommit | zm:GroupAbort
+// zm:GroupCommit ::= zm:S? '@$$}' zm:Hex+ '}@' /* id matches start id */
+// zm:GroupAbort ::= zm:S? '@$$}~~}@' /* id matches start id */
+/* We must allow started transactions to be aborted in summary files. */
+/* Note '$$' will never occur unescaped in values we will see in Mork. */
+{
+ if ( this->MatchPattern(ev, "$$") )
+ {
+ morkStream* s = mParser_Stream;
+ int c;
+ if ( ((c = s->Getc(ev)) == '{' || c == '}') && ev->Good() )
+ {
+ if ( c == '{' ) // start of new group?
+ {
+ if ( !inInsideGroup )
+ this->ReadGroup(ev);
+ else
+ ev->NewError("nested @$${ inside another group");
+ }
+ else // c == '}' // end of old group?
+ {
+ if ( inInsideGroup )
+ {
+ this->ReadEndGroupId(ev);
+ mParser_GroupId = 0;
+ }
+ else
+ ev->NewError("unmatched @$$} outside any group");
+ }
+ }
+ else
+ ev->NewError("expected '{' or '}' after @$$");
+ }
+ return ev->Good();
+}
+
+mork_bool morkParser::ReadEndGroupId(morkEnv* ev)
+{
+ mork_bool outSawGroupId = morkBool_kFalse;
+ morkStream* s = mParser_Stream;
+ int c;
+ if ( (c = s->Getc(ev)) != EOF && ev->Good() )
+ {
+ if ( c == '~' ) // transaction is aborted?
+ {
+ this->MatchPattern(ev, "~}@"); // finish rest of pattern
+ }
+ else // push back byte and read expected trailing hex id
+ {
+ s->Ungetc(c);
+ int next = 0;
+ mork_gid endGroupId = this->ReadHex(ev, &next);
+ if ( ev->Good() )
+ {
+ if ( endGroupId == mParser_GroupId ) // matches start?
+ {
+ if ( next == '}' ) // '}' after @$$}id ?
+ {
+ if ( (c = s->Getc(ev)) == '@' ) // '@' after @$$}id} ?
+ {
+ // looks good, so return with no error
+ outSawGroupId = morkBool_kTrue;
+ mParser_InGroup = false;
+ }
+ else
+ ev->NewError("expected '@' after @$$}id}");
+ }
+ else
+ ev->NewError("expected '}' after @$$}id");
+ }
+ else
+ ev->NewError("end group id mismatch");
+ }
+ }
+ }
+ return ( outSawGroupId && ev->Good() );
+}
+
+
+void morkParser::ReadDict(morkEnv* ev)
+// zm:Dict ::= zm:S? '<' zm:DictItem* zm:S? '>'
+// zm:DictItem ::= zm:MetaDict | zm:Alias
+// zm:MetaDict ::= zm:S? '<' zm:S? zm:Cell* zm:S? '>' /* meta attributes */
+// zm:Alias ::= zm:S? '(' ('#')? zm:Hex+ zm:S? zm:Value ')'
+{
+ mParser_Change = morkChange_kNil;
+ mParser_AtomChange = morkChange_kNil;
+
+ // this->StartSpanOnLastByte(ev, &mParser_DictSpan);
+ mParser_InDict = morkBool_kTrue;
+ this->OnNewDict(ev, *mParser_DictSpan.AsPlace());
+
+ int c;
+ while ( (c = this->NextChar(ev)) != EOF && ev->Good() && c != '>' )
+ {
+ switch ( c )
+ {
+ case '(': // alias
+ this->ReadAlias(ev);
+ break;
+
+ case '<': // meta
+ this->ReadMeta(ev, '>');
+ break;
+
+ default:
+ ev->NewWarning("unexpected byte in dict");
+ break;
+ }
+ }
+
+ // this->EndSpanOnThisByte(ev, &mParser_DictSpan);
+ mParser_InDict = morkBool_kFalse;
+ this->OnDictEnd(ev, mParser_DictSpan);
+
+ if ( ev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+ else if ( c == EOF )
+ mParser_State = morkParser_kDoneState;
+}
+
+void morkParser::EndSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan)
+{
+ mork_pos here;
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ nsresult rv = mParser_Stream->Tell(ev, &here);
+ if (NS_SUCCEEDED(rv) && mev->Good() )
+ {
+ this->SetHerePos(here);
+ ioSpan->SetEndWithEnd(mParser_PortSpan);
+ }
+}
+
+void morkParser::EndSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan)
+{
+ mork_pos here;
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ nsresult rv= mParser_Stream->Tell(ev, &here);
+ if ( NS_SUCCEEDED(rv) && mev->Good() )
+ {
+ if ( here > 0 )
+ --here;
+ else
+ here = 0;
+
+ this->SetHerePos(here);
+ ioSpan->SetEndWithEnd(mParser_PortSpan);
+ }
+}
+
+void morkParser::StartSpanOnLastByte(morkEnv* mev, morkSpan* ioSpan)
+{
+ mork_pos here;
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ nsresult rv = mParser_Stream->Tell(ev, &here);
+ if ( NS_SUCCEEDED(rv) && mev->Good() )
+ {
+ if ( here > 0 )
+ --here;
+ else
+ here = 0;
+
+ this->SetHerePos(here);
+ ioSpan->SetStartWithEnd(mParser_PortSpan);
+ ioSpan->SetEndWithEnd(mParser_PortSpan);
+ }
+}
+
+void morkParser::StartSpanOnThisByte(morkEnv* mev, morkSpan* ioSpan)
+{
+ mork_pos here;
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ nsresult rv = mParser_Stream->Tell(ev, &here);
+ if ( NS_SUCCEEDED(rv) && mev->Good() )
+ {
+ this->SetHerePos(here);
+ ioSpan->SetStartWithEnd(mParser_PortSpan);
+ ioSpan->SetEndWithEnd(mParser_PortSpan);
+ }
+}
+
+mork_bool
+morkParser::ReadContent(morkEnv* ev, mork_bool inInsideGroup)
+{
+ int c;
+ mork_bool keep_going = true;
+ while ( keep_going && (c = this->NextChar(ev)) != EOF && ev->Good())
+ {
+ switch ( c )
+ {
+ case '[': // row
+ this->ReadRow(ev, '[');
+ keep_going = false;
+ break;
+
+ case '{': // table
+ this->ReadTable(ev);
+ keep_going = false;
+ break;
+
+ case '<': // dict
+ this->ReadDict(ev);
+ keep_going = false;
+ break;
+
+ case '@': // group
+ return this->ReadAt(ev, inInsideGroup);
+ // break;
+
+ // case '+': // plus
+ // mParser_Change = morkChange_kAdd;
+ // break;
+
+ // case '-': // minus
+ // mParser_Change = morkChange_kCut;
+ // break;
+
+ // case '!': // bang
+ // mParser_Change = morkChange_kSet;
+ // break;
+
+ default:
+ ev->NewWarning("unexpected byte in ReadContent()");
+ break;
+ }
+ }
+ if ( ev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+ else if ( c == EOF )
+ mParser_State = morkParser_kDoneState;
+
+ return ( ev->Good() && c != EOF );
+}
+
+void
+morkParser::OnPortState(morkEnv* ev)
+{
+ mork_bool firstTime = !mParser_InPort;
+ mParser_InPort = morkBool_kTrue;
+ if (firstTime)
+ this->OnNewPort(ev, *mParser_PortSpan.AsPlace());
+
+ mork_bool done = !this->ReadContent(ev, mParser_InGroup/*inInsideGroup*/);
+
+ if (done)
+ {
+ mParser_InPort = morkBool_kFalse;
+ this->OnPortEnd(ev, mParser_PortSpan);
+ }
+
+ if ( ev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+}
+
+void
+morkParser::OnStartState(morkEnv* mev)
+{
+ morkStream* s = mParser_Stream;
+ nsIMdbEnv *ev = mev->AsMdbEnv();
+ if ( s && s->IsNode() && s->IsOpenNode() )
+ {
+ mork_pos outPos;
+ nsresult rv = s->Seek(ev, 0, &outPos);
+ if (NS_SUCCEEDED(rv) && mev->Good() )
+ {
+ this->StartParse(mev);
+ mParser_State = morkParser_kPortState;
+ }
+ }
+ else
+ mev->NilPointerError();
+
+ if ( mev->Bad() )
+ mParser_State = morkParser_kBrokenState;
+}
+
+/*protected non-poly*/ void
+morkParser::ParseChunk(morkEnv* ev)
+{
+ mParser_Change = morkChange_kNil;
+ mParser_DoMore = morkBool_kTrue;
+
+ switch ( mParser_State )
+ {
+ case morkParser_kCellState: // 0
+ this->OnCellState(ev); break;
+
+ case morkParser_kMetaState: // 1
+ this->OnMetaState(ev); break;
+
+ case morkParser_kRowState: // 2
+ this->OnRowState(ev); break;
+
+ case morkParser_kTableState: // 3
+ this->OnTableState(ev); break;
+
+ case morkParser_kDictState: // 4
+ this->OnDictState(ev); break;
+
+ case morkParser_kPortState: // 5
+ this->OnPortState(ev); break;
+
+ case morkParser_kStartState: // 6
+ this->OnStartState(ev); break;
+
+ case morkParser_kDoneState: // 7
+ mParser_DoMore = morkBool_kFalse;
+ mParser_IsDone = morkBool_kTrue;
+ this->StopParse(ev);
+ break;
+ case morkParser_kBrokenState: // 8
+ mParser_DoMore = morkBool_kFalse;
+ mParser_IsBroken = morkBool_kTrue;
+ this->StopParse(ev);
+ break;
+ default: // ?
+ MORK_ASSERT(morkBool_kFalse);
+ mParser_State = morkParser_kBrokenState;
+ break;
+ }
+}
+
+/*public non-poly*/ mdb_count
+morkParser::ParseMore( // return count of bytes consumed now
+ morkEnv* ev, // context
+ mork_pos* outPos, // current byte pos in the stream afterwards
+ mork_bool* outDone, // is parsing finished?
+ mork_bool* outBroken // is parsing irreparably dead and broken?
+ )
+{
+ mdb_count outCount = 0;
+ if ( this->IsNode() && this->GoodParserTag() && this->IsOpenNode() )
+ {
+ mork_pos startPos = this->HerePos();
+
+ if ( !mParser_IsDone && !mParser_IsBroken )
+ this->ParseChunk(ev);
+
+ // HerePos is only updated for groups. I'd like it to be more accurate.
+
+ mork_pos here;
+ mParser_Stream->Tell(ev, &here);
+
+ if ( outDone )
+ *outDone = mParser_IsDone;
+ if ( outBroken )
+ *outBroken = mParser_IsBroken;
+ if ( outPos )
+ *outPos = here;
+
+ if ( here > startPos )
+ outCount = (mdb_count) (here - startPos);
+ }
+ else
+ {
+ this->NonUsableParserError(ev);
+ if ( outDone )
+ *outDone = morkBool_kTrue;
+ if ( outBroken )
+ *outBroken = morkBool_kTrue;
+ if ( outPos )
+ *outPos = 0;
+ }
+ return outCount;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkParser.h b/db/mork/src/morkParser.h
new file mode 100644
index 000000000..ce1fd8943
--- /dev/null
+++ b/db/mork/src/morkParser.h
@@ -0,0 +1,533 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKPARSER_
+#define _MORKPARSER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKYARN_
+#include "morkYarn.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*=============================================================================
+ * morkPlace: stream byte position and stream line count
+ */
+
+class morkPlace {
+public:
+ mork_pos mPlace_Pos; // byte offset in an input stream
+ mork_line mPlace_Line; // line count in an input stream
+
+ void ClearPlace()
+ {
+ mPlace_Pos = 0; mPlace_Line = 0;
+ }
+
+ void SetPlace(mork_pos inPos, mork_line inLine)
+ {
+ mPlace_Pos = inPos; mPlace_Line = inLine;
+ }
+
+ morkPlace() { mPlace_Pos = 0; mPlace_Line = 0; }
+
+ morkPlace(mork_pos inPos, mork_line inLine)
+ { mPlace_Pos = inPos; mPlace_Line = inLine; }
+
+ morkPlace(const morkPlace& inPlace)
+ : mPlace_Pos(inPlace.mPlace_Pos), mPlace_Line(inPlace.mPlace_Line) { }
+};
+
+/*=============================================================================
+ * morkGlitch: stream place and error comment describing a parsing error
+ */
+
+class morkGlitch {
+public:
+ morkPlace mGlitch_Place; // place in stream where problem happened
+ const char* mGlitch_Comment; // null-terminated ASCII C string
+
+ morkGlitch() { mGlitch_Comment = 0; }
+
+ morkGlitch(const morkPlace& inPlace, const char* inComment)
+ : mGlitch_Place(inPlace), mGlitch_Comment(inComment) { }
+};
+
+/*=============================================================================
+ * morkMid: all possible ways needed to express an alias ID in Mork syntax
+ */
+
+/*| morkMid: an abstraction of all the variations we might need to support
+**| in order to present an ID through the parser interface most cheaply and
+**| with minimum transformation away from the original text format.
+**|
+**|| An ID can have one of four forms:
+**| 1) idHex (mMid_Oid.mOid_Id <- idHex)
+**| 2) idHex:^scopeHex (mMid_Oid.mOid_Id <- idHex, mOid_Scope <- scopeHex)
+**| 3) idHex:scopeName (mMid_Oid.mOid_Id <- idHex, mMid_Buf <- scopeName)
+**| 4) columnName (mMid_Buf <- columnName, for columns in cells only)
+**|
+**|| Typically, mMid_Oid.mOid_Id will hold a nonzero integer value for
+**| an ID, but we might have an optional scope specified by either an integer
+**| in hex format, or a string name. (Note that while the first ID can be
+**| scoped variably, any integer ID for a scope is assumed always located in
+**| the same scope, so the second ID need not be disambiguated.)
+**|
+**|| The only time mMid_Oid.mOid_Id is ever zero is when mMid_Buf alone
+**| is nonzero, to indicate an explicit string instead of an alias appeared.
+**| This case happens to make the representation of columns in cells somewhat
+**| easier to represent, since columns can just appear as a string name; and
+**| this unifies those interfaces with row and table APIs expecting IDs.
+**|
+**|| So when the parser passes an instance of morkMid to a subclass, the
+**| mMid_Oid.mOid_Id slot should usually be nonzero. And the other two
+**| slots, mMid_Oid.mOid_Scope and mMid_Buf, might both be zero, or at
+**| most one of them will be nonzero to indicate an explicit scope; the
+**| parser is responsible for ensuring at most one of these is nonzero.
+|*/
+class morkMid {
+public:
+ mdbOid mMid_Oid; // mOid_Scope is zero when not specified
+ const morkBuf* mMid_Buf; // points to some specific buf subclass
+
+ morkMid()
+ { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+ mMid_Buf = 0; }
+
+ void InitMidWithCoil(morkCoil* ioCoil)
+ { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+ mMid_Buf = ioCoil; }
+
+ void ClearMid()
+ { mMid_Oid.mOid_Scope = 0; mMid_Oid.mOid_Id = morkId_kMinusOne;
+ mMid_Buf = 0; }
+
+ morkMid(const morkMid& other)
+ : mMid_Oid(other.mMid_Oid), mMid_Buf(other.mMid_Buf) { }
+
+ mork_bool HasNoId() const // ID is unspecified?
+ { return ( mMid_Oid.mOid_Id == morkId_kMinusOne ); }
+
+ mork_bool HasSomeId() const // ID is specified?
+ { return ( mMid_Oid.mOid_Id != morkId_kMinusOne ); }
+};
+
+/*=============================================================================
+ * morkSpan: start and end stream byte position and stream line count
+ */
+
+class morkSpan {
+public:
+ morkPlace mSpan_Start;
+ morkPlace mSpan_End;
+
+public: // methods
+
+public: // inlines
+ morkSpan() { } // use inline empty constructor for each place
+
+ morkPlace* AsPlace() { return &mSpan_Start; }
+ const morkPlace* AsConstPlace() const { return &mSpan_Start; }
+
+ void SetSpan(mork_pos inFromPos, mork_line inFromLine,
+ mork_pos inToPos, mork_line inToLine)
+ {
+ mSpan_Start.SetPlace(inFromPos, inFromLine);
+ mSpan_End.SetPlace(inToPos,inToLine);
+ }
+
+ // setting end, useful to terminate a span using current port span end:
+ void SetEndWithEnd(const morkSpan& inSpan) // end <- span.end
+ { mSpan_End = inSpan.mSpan_End; }
+
+ // setting start, useful to initiate a span using current port span end:
+ void SetStartWithEnd(const morkSpan& inSpan) // start <- span.end
+ { mSpan_Start = inSpan.mSpan_End; }
+
+ void ClearSpan()
+ {
+ mSpan_Start.mPlace_Pos = 0; mSpan_Start.mPlace_Line = 0;
+ mSpan_End.mPlace_Pos = 0; mSpan_End.mPlace_Line = 0;
+ }
+
+ morkSpan(mork_pos inFromPos, mork_line inFromLine,
+ mork_pos inToPos, mork_line inToLine)
+ : mSpan_Start(inFromPos, inFromLine), mSpan_End(inToPos, inToLine)
+ { /* empty implementation */ }
+};
+
+/*=============================================================================
+ * morkParser: for parsing Mork text syntax
+ */
+
+#define morkParser_kMinGranularity 512 /* parse at least half 0.5K at once */
+#define morkParser_kMaxGranularity (64 * 1024) /* parse at most 64 K at once */
+
+#define morkDerived_kParser /*i*/ 0x5073 /* ascii 'Ps' */
+#define morkParser_kTag /*i*/ 0x70417253 /* ascii 'pArS' */
+
+// These are states for the simple parsing virtual machine. Needless to say,
+// these must be distinct, and preferably in a contiguous integer range.
+// Don't change these constants without looking at switch statements in code.
+#define morkParser_kCellState 0 /* cell is tightest scope */
+#define morkParser_kMetaState 1 /* meta is tightest scope */
+#define morkParser_kRowState 2 /* row is tightest scope */
+#define morkParser_kTableState 3 /* table is tightest scope */
+#define morkParser_kDictState 4 /* dict is tightest scope */
+#define morkParser_kPortState 5 /* port is tightest scope */
+
+#define morkParser_kStartState 6 /* parsing has not yet begun */
+#define morkParser_kDoneState 7 /* parsing is complete */
+#define morkParser_kBrokenState 8 /* parsing is to broken to work */
+
+class morkParser /*d*/ : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected morkParser members
+
+ nsIMdbHeap* mParser_Heap; // refcounted heap used for allocation
+ morkStream* mParser_Stream; // refcounted input stream
+
+ mork_u4 mParser_Tag; // must equal morkParser_kTag
+ mork_count mParser_MoreGranularity; // constructor inBytesPerParseSegment
+
+ mork_u4 mParser_State; // state where parser should resume
+
+ // after finding ends of group transactions, we can re-seek the start:
+ mork_pos mParser_GroupContentStartPos; // start of this group
+
+ morkMid mParser_TableMid; // table mid if inside a table
+ morkMid mParser_RowMid; // row mid if inside a row
+ morkMid mParser_CellMid; // cell mid if inside a row
+ mork_gid mParser_GroupId; // group ID if inside a group
+
+ mork_bool mParser_InPort; // called OnNewPort but not OnPortEnd?
+ mork_bool mParser_InDict; // called OnNewDict but not OnDictEnd?
+ mork_bool mParser_InCell; // called OnNewCell but not OnCellEnd?
+ mork_bool mParser_InMeta; // called OnNewMeta but not OnMetaEnd?
+
+ mork_bool mParser_InPortRow; // called OnNewPortRow but not OnPortRowEnd?
+ mork_bool mParser_InRow; // called OnNewRow but not OnNewRowEnd?
+ mork_bool mParser_InTable; // called OnNewMeta but not OnMetaEnd?
+ mork_bool mParser_InGroup; // called OnNewGroup but not OnGroupEnd?
+
+ mork_change mParser_AtomChange; // driven by mParser_Change
+ mork_change mParser_CellChange; // driven by mParser_Change
+ mork_change mParser_RowChange; // driven by mParser_Change
+ mork_change mParser_TableChange; // driven by mParser_Change
+
+ mork_change mParser_Change; // driven by modifier in text
+ mork_bool mParser_IsBroken; // has the parse become broken?
+ mork_bool mParser_IsDone; // has the parse finished?
+ mork_bool mParser_DoMore; // mParser_MoreGranularity not exhausted?
+
+ morkMid mParser_Mid; // current alias being parsed
+ // note that mParser_Mid.mMid_Buf points at mParser_ScopeCoil below:
+
+ // blob coils allocated in mParser_Heap
+ morkCoil mParser_ScopeCoil; // place to accumulate ID scope blobs
+ morkCoil mParser_ValueCoil; // place to accumulate value blobs
+ morkCoil mParser_ColumnCoil; // place to accumulate column blobs
+ morkCoil mParser_StringCoil; // place to accumulate string blobs
+
+ morkSpool mParser_ScopeSpool; // writes to mParser_ScopeCoil
+ morkSpool mParser_ValueSpool; // writes to mParser_ValueCoil
+ morkSpool mParser_ColumnSpool; // writes to mParser_ColumnCoil
+ morkSpool mParser_StringSpool; // writes to mParser_StringCoil
+
+ // yarns allocated in mParser_Heap
+ morkYarn mParser_MidYarn; // place to receive from MidToYarn()
+
+ // span showing current ongoing file position status:
+ morkSpan mParser_PortSpan; // span of current db port file
+
+ // various spans denoting nested subspaces inside the file's port span:
+ morkSpan mParser_GroupSpan; // span of current transaction group
+ morkSpan mParser_DictSpan;
+ morkSpan mParser_AliasSpan;
+ morkSpan mParser_MetaSpan;
+ morkSpan mParser_TableSpan;
+ morkSpan mParser_RowSpan;
+ morkSpan mParser_CellSpan;
+ morkSpan mParser_ColumnSpan;
+ morkSpan mParser_SlotSpan;
+
+private: // convenience inlines
+
+ mork_pos HerePos() const
+ { return mParser_PortSpan.mSpan_End.mPlace_Pos; }
+
+ void SetHerePos(mork_pos inPos)
+ { mParser_PortSpan.mSpan_End.mPlace_Pos = inPos; }
+
+ void CountLineBreak()
+ { ++mParser_PortSpan.mSpan_End.mPlace_Line; }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseParser() only if open
+ virtual ~morkParser(); // assert that CloseParser() executed earlier
+
+public: // morkYarn construction & destruction
+ morkParser(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkStream* ioStream, // the readonly stream for input bytes
+ mdb_count inBytesPerParseSegment, // target for ParseMore()
+ nsIMdbHeap* ioSlotHeap);
+
+ void CloseParser(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkParser(const morkParser& other);
+ morkParser& operator=(const morkParser& other);
+
+public: // dynamic type identification
+ mork_bool IsParser() const
+ { return IsNode() && mNode_Derived == morkDerived_kParser; }
+
+// } ===== end morkNode methods =====
+
+public: // errors and warnings
+ static void UnexpectedEofError(morkEnv* ev);
+ static void EofInsteadOfHexError(morkEnv* ev);
+ static void ExpectedEqualError(morkEnv* ev);
+ static void ExpectedHexDigitError(morkEnv* ev, int c);
+ static void NonParserTypeError(morkEnv* ev);
+ static void UnexpectedByteInMetaWarning(morkEnv* ev);
+
+public: // other type methods
+ mork_bool GoodParserTag() const { return mParser_Tag == morkParser_kTag; }
+ void NonGoodParserError(morkEnv* ev);
+ void NonUsableParserError(morkEnv* ev);
+ // call when IsNode() or GoodParserTag() is false
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // in virtual morkParser methods, data flow subclass to parser
+
+ virtual void MidToYarn(morkEnv* ev,
+ const morkMid& inMid, // typically an alias to concat with strings
+ mdbYarn* outYarn) = 0;
+ // The parser might ask that some aliases be turned into yarns, so they
+ // can be concatenated into longer blobs under some circumstances. This
+ // is an alternative to using a long and complex callback for many parts
+ // for a single cell value.
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // out virtual morkParser methods, data flow parser to subclass
+
+// The virtual methods below will be called in a pattern corresponding
+// to the following grammar isomorphic to the Mork grammar. There should
+// be no exceptions, so subclasses can rely on seeing an appropriate "end"
+// method whenever some "new" method has been seen earlier. In the event
+// that some error occurs that causes content to be flushed, or sudden early
+// termination of a larger containing entity, we will always call a more
+// enclosed "end" method before we call an "end" method with greater scope.
+
+// Note the "mp" prefix stands for "Mork Parser":
+
+// mp:Start ::= OnNewPort mp:PortItem* OnPortEnd
+// mp:PortItem ::= mp:Content | mp:Group | OnPortGlitch
+// mp:Group ::= OnNewGroup mp:GroupItem* mp:GroupEnd
+// mp:GroupItem ::= mp:Content | OnGroupGlitch
+// mp:GroupEnd ::= OnGroupCommitEnd | OnGroupAbortEnd
+// mp:Content ::= mp:PortRow | mp:Dict | mp:Table | mp:Row
+// mp:PortRow ::= OnNewPortRow mp:RowItem* OnPortRowEnd
+// mp:Dict ::= OnNewDict mp:DictItem* OnDictEnd
+// mp:DictItem ::= OnAlias | OnAliasGlitch | mp:Meta | OnDictGlitch
+// mp:Table ::= OnNewTable mp:TableItem* OnTableEnd
+// mp:TableItem ::= mp:Row | mp:MetaTable | OnTableGlitch
+// mp:MetaTable ::= OnNewMeta mp:MetaItem* mp:Row OnMetaEnd
+// mp:Meta ::= OnNewMeta mp:MetaItem* OnMetaEnd
+// mp:MetaItem ::= mp:Cell | OnMetaGlitch
+// mp:Row ::= OnMinusRow? OnNewRow mp:RowItem* OnRowEnd
+// mp:RowItem ::= mp:Cell | mp:Meta | OnRowGlitch
+// mp:Cell ::= OnMinusCell? OnNewCell mp:CellItem? OnCellEnd
+// mp:CellItem ::= mp:Slot | OnCellForm | OnCellGlitch
+// mp:Slot ::= OnValue | OnValueMid | OnRowMid | OnTableMid
+
+
+ // Note that in interfaces below, mork_change parameters kAdd and kNil
+ // both mean about the same thing by default. Only kCut is interesting,
+ // because this usually means to remove members instead of adding them.
+
+ virtual void OnNewPort(morkEnv* ev, const morkPlace& inPlace) = 0;
+ virtual void OnPortGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnPortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnNewGroup(morkEnv* ev, const morkPlace& inPlace, mork_gid inGid) = 0;
+ virtual void OnGroupGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnGroupCommitEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+ virtual void OnGroupAbortEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnNewPortRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_change inChange) = 0;
+ virtual void OnPortRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnPortRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnNewTable(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllRows) = 0;
+ virtual void OnTableGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnTableEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnNewMeta(morkEnv* ev, const morkPlace& inPlace) = 0;
+ virtual void OnMetaGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnMetaEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnMinusRow(morkEnv* ev) = 0;
+ virtual void OnNewRow(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid& inMid, mork_bool inCutAllCols) = 0;
+ virtual void OnRowPos(morkEnv* ev, mork_pos inRowPos) = 0;
+ virtual void OnRowGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnRowEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnNewDict(morkEnv* ev, const morkPlace& inPlace) = 0;
+ virtual void OnDictGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnDictEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnAlias(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) = 0;
+
+ virtual void OnAliasGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+
+ virtual void OnMinusCell(morkEnv* ev) = 0;
+ virtual void OnNewCell(morkEnv* ev, const morkPlace& inPlace,
+ const morkMid* inMid, const morkBuf* inBuf) = 0;
+ // Exactly one of inMid and inBuf is nil, and the other is non-nil.
+ // When hex ID syntax is used for a column, then inMid is not nil, and
+ // when a naked string names a column, then inBuf is not nil.
+
+ virtual void OnCellGlitch(morkEnv* ev, const morkGlitch& inGlitch) = 0;
+ virtual void OnCellForm(morkEnv* ev, mork_cscode inCharsetFormat) = 0;
+ virtual void OnCellEnd(morkEnv* ev, const morkSpan& inSpan) = 0;
+
+ virtual void OnValue(morkEnv* ev, const morkSpan& inSpan,
+ const morkBuf& inBuf) = 0;
+
+ virtual void OnValueMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) = 0;
+
+ virtual void OnRowMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) = 0;
+
+ virtual void OnTableMid(morkEnv* ev, const morkSpan& inSpan,
+ const morkMid& inMid) = 0;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected parser helper methods
+
+ void ParseChunk(morkEnv* ev); // find parse continuation and resume
+
+ void StartParse(morkEnv* ev); // prepare for parsing
+ void StopParse(morkEnv* ev); // terminate parsing & call needed methods
+
+ int NextChar(morkEnv* ev); // next non-white content
+
+ void OnCellState(morkEnv* ev);
+ void OnMetaState(morkEnv* ev);
+ void OnRowState(morkEnv* ev);
+ void OnTableState(morkEnv* ev);
+ void OnDictState(morkEnv* ev);
+ void OnPortState(morkEnv* ev);
+ void OnStartState(morkEnv* ev);
+
+ void ReadCell(morkEnv* ev);
+ void ReadRow(morkEnv* ev, int c);
+ void ReadRowPos(morkEnv* ev);
+ void ReadTable(morkEnv* ev);
+ void ReadTableMeta(morkEnv* ev);
+ void ReadDict(morkEnv* ev);
+ mork_bool ReadContent(morkEnv* ev, mork_bool inInsideGroup);
+ void ReadGroup(morkEnv* ev);
+ mork_bool ReadEndGroupId(morkEnv* ev);
+ mork_bool ReadAt(morkEnv* ev, mork_bool inInsideGroup);
+ mork_bool FindGroupEnd(morkEnv* ev);
+ void ReadMeta(morkEnv* ev, int inEndMeta);
+ void ReadAlias(morkEnv* ev);
+ mork_id ReadHex(morkEnv* ev, int* outNextChar);
+ morkBuf* ReadValue(morkEnv* ev);
+ morkBuf* ReadName(morkEnv* ev, int c);
+ mork_bool ReadMid(morkEnv* ev, morkMid* outMid);
+ void ReadDictForm(morkEnv *ev);
+ void ReadCellForm(morkEnv *ev, int c);
+
+ mork_bool MatchPattern(morkEnv* ev, const char* inPattern);
+
+ void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
+ void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
+ void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan);
+
+ void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan);
+
+
+ // void EndSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
+ // { MORK_USED_2(ev,ioSpan); }
+
+ // void EndSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
+ // { MORK_USED_2(ev,ioSpan); }
+
+ // void StartSpanOnLastByte(morkEnv* ev, morkSpan* ioSpan)
+ // { MORK_USED_2(ev,ioSpan); }
+
+ // void StartSpanOnThisByte(morkEnv* ev, morkSpan* ioSpan)
+ // { MORK_USED_2(ev,ioSpan); }
+
+ int eat_line_break(morkEnv* ev, int inLast);
+ int eat_line_continue(morkEnv* ev); // last char was '\\'
+ int eat_comment(morkEnv* ev); // last char was '/'
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkParser methods
+
+ mdb_count ParseMore( // return count of bytes consumed now
+ morkEnv* ev, // context
+ mork_pos* outPos, // current byte pos in the stream afterwards
+ mork_bool* outDone, // is parsing finished?
+ mork_bool* outBroken // is parsing irreparably dead and broken?
+ );
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakParser(morkParser* me,
+ morkEnv* ev, morkParser** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongParser(morkParser* me,
+ morkEnv* ev, morkParser** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPARSER_ */
+
diff --git a/db/mork/src/morkPool.cpp b/db/mork/src/morkPool.cpp
new file mode 100644
index 000000000..6819b8bd0
--- /dev/null
+++ b/db/mork/src/morkPool.cpp
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKHANDLE_
+#include "morkHandle.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPool::CloseMorkNode(morkEnv* ev) // ClosePool() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->ClosePool(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkPool::~morkPool() // assert ClosePool() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkPool::morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbHeap* ioSlotHeap)
+: morkNode(inUsage, ioHeap)
+, mPool_Heap( ioSlotHeap )
+, mPool_UsedFramesCount( 0 )
+, mPool_FreeFramesCount( 0 )
+{
+ // mPool_Heap is NOT refcounted
+ MORK_ASSERT(ioSlotHeap);
+ if ( ioSlotHeap )
+ mNode_Derived = morkDerived_kPool;
+}
+
+/*public non-poly*/
+morkPool::morkPool(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mPool_Heap( ioSlotHeap )
+, mPool_UsedFramesCount( 0 )
+, mPool_FreeFramesCount( 0 )
+{
+ if ( ioSlotHeap )
+ {
+ // mPool_Heap is NOT refcounted:
+ // nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mPool_Heap);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kPool;
+ }
+ else
+ ev->NilPointerError();
+}
+
+/*public non-poly*/ void
+morkPool::ClosePool(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+ //MORK_USED_1(ioZone);
+#endif /*morkZone_CONFIG_ARENA*/
+
+ nsIMdbHeap* heap = mPool_Heap;
+ nsIMdbEnv* mev = ev->AsMdbEnv();
+ morkLink* aLink;
+ morkDeque* d = &mPool_FreeHandleFrames;
+ while ( (aLink = d->RemoveFirst()) != 0 )
+ heap->Free(mev, aLink);
+
+ // if the pool's closed, get rid of the frames in use too.
+ d = &mPool_UsedHandleFrames;
+ while ( (aLink = d->RemoveFirst()) != 0 )
+ heap->Free(mev, aLink);
+
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+
+// alloc and free individual instances of handles (inside hand frames):
+morkHandleFace*
+morkPool::NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone)
+{
+ void* newBlock = 0;
+ if ( inSize <= sizeof(morkHandleFrame) )
+ {
+ morkLink* firstLink = mPool_FreeHandleFrames.RemoveFirst();
+ if ( firstLink )
+ {
+ newBlock = firstLink;
+ if ( mPool_FreeFramesCount )
+ --mPool_FreeFramesCount;
+ else
+ ev->NewWarning("mPool_FreeFramesCount underflow");
+ }
+ else
+ mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkHandleFrame),
+ (void**) &newBlock);
+ }
+ else
+ {
+ ev->NewWarning("inSize > sizeof(morkHandleFrame)");
+ mPool_Heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &newBlock);
+ }
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+#endif /*morkZone_CONFIG_ARENA*/
+
+ return (morkHandleFace*) newBlock;
+}
+
+void
+morkPool::ZapHandle(morkEnv* ev, morkHandleFace* ioHandle)
+{
+ if ( ioHandle )
+ {
+ morkLink* handleLink = (morkLink*) ioHandle;
+ mPool_FreeHandleFrames.AddLast(handleLink);
+ ++mPool_FreeFramesCount;
+ // lets free all handles to track down leaks
+ // - uncomment out next 3 lines, comment out above 2
+// nsIMdbHeap* heap = mPool_Heap;
+// nsIMdbEnv* mev = ev->AsMdbEnv();
+// heap->Free(mev, handleLink);
+
+ }
+}
+
+
+// alloc and free individual instances of rows:
+morkRow*
+morkPool::NewRow(morkEnv* ev, morkZone* ioZone) // allocate a new row instance
+{
+ morkRow* newRow = 0;
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newRow = (morkRow*) ioZone->ZoneNewChip(ev, sizeof(morkRow));
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkRow), (void**) &newRow);
+#endif /*morkZone_CONFIG_ARENA*/
+
+ if ( newRow )
+ MORK_MEMSET(newRow, 0, sizeof(morkRow));
+
+ return newRow;
+}
+
+void
+morkPool::ZapRow(morkEnv* ev, morkRow* ioRow,
+ morkZone* ioZone) // free old row instance
+{
+#ifdef morkZone_CONFIG_ARENA
+ if ( !ioRow )
+ ev->NilPointerWarning(); // a zone 'chip' cannot be freed
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ if ( ioRow )
+ mPool_Heap->Free(ev->AsMdbEnv(), ioRow);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+// alloc and free entire vectors of cells (not just one cell at a time)
+morkCell*
+morkPool::NewCells(morkEnv* ev, mork_size inSize,
+ morkZone* ioZone)
+{
+ morkCell* newCells = 0;
+
+ mork_size size = inSize * sizeof(morkCell);
+ if ( size )
+ {
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'run' knows its size, and can indeed be deallocated:
+ newCells = (morkCell*) ioZone->ZoneNewRun(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newCells);
+#endif /*morkZone_CONFIG_ARENA*/
+ }
+
+ // note morkAtom depends on having nil stored in all new mCell_Atom slots:
+ if ( newCells )
+ MORK_MEMSET(newCells, 0, size);
+ return newCells;
+}
+
+void
+morkPool::ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize,
+ morkZone* ioZone)
+{
+ MORK_USED_1(inSize);
+
+ if ( ioVector )
+ {
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'run' knows its size, and can indeed be deallocated:
+ ioZone->ZoneZapRun(ev, ioVector);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Free(ev->AsMdbEnv(), ioVector);
+#endif /*morkZone_CONFIG_ARENA*/
+ }
+}
+
+// resize (grow or trim) cell vectors inside a containing row instance
+mork_bool
+morkPool::AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize,
+ morkZone* ioZone)
+{
+ // note strong implementation similarity to morkArray::Grow()
+
+ MORK_USED_1(ioZone);
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+#endif /*morkZone_CONFIG_ARENA*/
+
+ mork_fill fill = ioRow->mRow_Length;
+ if ( ev->Good() && fill < inNewSize ) // need more cells?
+ {
+ morkCell* newCells = this->NewCells(ev, inNewSize, ioZone);
+ if ( newCells )
+ {
+ morkCell* c = newCells; // for iterating during copy
+ morkCell* oldCells = ioRow->mRow_Cells;
+ morkCell* end = oldCells + fill; // copy all the old cells
+ while ( oldCells < end )
+ {
+ *c++ = *oldCells++; // bitwise copy each old cell struct
+ }
+ oldCells = ioRow->mRow_Cells;
+ ioRow->mRow_Cells = newCells;
+ ioRow->mRow_Length = (mork_u2) inNewSize;
+ ++ioRow->mRow_Seed;
+
+ if ( oldCells )
+ this->ZapCells(ev, oldCells, fill, ioZone);
+ }
+ }
+ return ( ev->Good() && ioRow->mRow_Length >= inNewSize );
+}
+
+mork_bool
+morkPool::CutRowCells(morkEnv* ev, morkRow* ioRow,
+ mork_size inNewSize,
+ morkZone* ioZone)
+{
+ MORK_USED_1(ioZone);
+#ifdef morkZone_CONFIG_ARENA
+#else /*morkZone_CONFIG_ARENA*/
+#endif /*morkZone_CONFIG_ARENA*/
+
+ mork_fill fill = ioRow->mRow_Length;
+ if ( ev->Good() && fill > inNewSize ) // need fewer cells?
+ {
+ if ( inNewSize ) // want any row cells at all?
+ {
+ morkCell* newCells = this->NewCells(ev, inNewSize, ioZone);
+ if ( newCells )
+ {
+ morkCell* saveNewCells = newCells; // Keep newcell pos
+ morkCell* oldCells = ioRow->mRow_Cells;
+ morkCell* oldEnd = oldCells + fill; // one past all old cells
+ morkCell* newEnd = oldCells + inNewSize; // copy only kept old cells
+ while ( oldCells < newEnd )
+ {
+ *newCells++ = *oldCells++; // bitwise copy each old cell struct
+ }
+ while ( oldCells < oldEnd )
+ {
+ if ( oldCells->mCell_Atom ) // need to unref old cell atom?
+ oldCells->SetAtom(ev, (morkAtom*) 0, this); // unref cell atom
+ ++oldCells;
+ }
+ oldCells = ioRow->mRow_Cells;
+ ioRow->mRow_Cells = saveNewCells;
+ ioRow->mRow_Length = (mork_u2) inNewSize;
+ ++ioRow->mRow_Seed;
+
+ if ( oldCells )
+ this->ZapCells(ev, oldCells, fill, ioZone);
+ }
+ }
+ else // get rid of all row cells
+ {
+ morkCell* oldCells = ioRow->mRow_Cells;
+ ioRow->mRow_Cells = 0;
+ ioRow->mRow_Length = 0;
+ ++ioRow->mRow_Seed;
+
+ if ( oldCells )
+ this->ZapCells(ev, oldCells, fill, ioZone);
+ }
+ }
+ return ( ev->Good() && ioRow->mRow_Length <= inNewSize );
+}
+
+// alloc & free individual instances of atoms (lots of atom subclasses):
+void
+morkPool::ZapAtom(morkEnv* ev, morkAtom* ioAtom,
+ morkZone* ioZone) // any subclass (by kind)
+{
+#ifdef morkZone_CONFIG_ARENA
+ if ( !ioAtom )
+ ev->NilPointerWarning(); // a zone 'chip' cannot be freed
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ if ( ioAtom )
+ mPool_Heap->Free(ev->AsMdbEnv(), ioAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+morkOidAtom*
+morkPool::NewRowOidAtom(morkEnv* ev, const mdbOid& inOid,
+ morkZone* ioZone)
+{
+ morkOidAtom* newAtom = 0;
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom));
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom),(void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+
+ if ( newAtom )
+ newAtom->InitRowOidAtom(ev, inOid);
+ return newAtom;
+}
+
+morkOidAtom*
+morkPool::NewTableOidAtom(morkEnv* ev, const mdbOid& inOid,
+ morkZone* ioZone)
+{
+ morkOidAtom* newAtom = 0;
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkOidAtom*) ioZone->ZoneNewChip(ev, sizeof(morkOidAtom));
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), sizeof(morkOidAtom), (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+ if ( newAtom )
+ newAtom->InitTableOidAtom(ev, inOid);
+ return newAtom;
+}
+
+morkAtom*
+morkPool::NewAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm,
+ morkZone* ioZone)
+// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+// anon atom will be created, and otherwise a 'big' anon atom.
+{
+ morkAtom* newAtom = 0;
+
+ mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 );
+ mork_size size = ( needBig )?
+ morkBigAnonAtom::SizeForFill(inBuf.mBuf_Fill) :
+ morkWeeAnonAtom::SizeForFill(inBuf.mBuf_Fill);
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+ if ( newAtom )
+ {
+ if ( needBig )
+ ((morkBigAnonAtom*) newAtom)->InitBigAnonAtom(ev, inBuf, inForm);
+ else
+ ((morkWeeAnonAtom*) newAtom)->InitWeeAnonAtom(ev, inBuf);
+ }
+ return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid,
+ morkZone* ioZone)
+// if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+// book atom will be created, and otherwise a 'big' book atom.
+{
+ morkBookAtom* newAtom = 0;
+
+ mork_bool needBig = ( inForm || inBuf.mBuf_Fill > 255 );
+ mork_size size = ( needBig )?
+ morkBigBookAtom::SizeForFill(inBuf.mBuf_Fill) :
+ morkWeeBookAtom::SizeForFill(inBuf.mBuf_Fill);
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+ if ( newAtom )
+ {
+ if ( needBig )
+ ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+ inBuf, inForm, ioSpace, inAid);
+ else
+ ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+ inBuf, ioSpace, inAid);
+ }
+ return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom,
+ morkZone* ioZone)
+ // make the smallest kind of book atom that can hold content in inAtom.
+ // The inAtom parameter is often expected to be a staged book atom in
+ // the store, which was used to search an atom space for existing atoms.
+{
+ morkBookAtom* newAtom = 0;
+
+ mork_cscode form = inAtom.mBigBookAtom_Form;
+ mork_fill fill = inAtom.mBigBookAtom_Size;
+ mork_bool needBig = ( form || fill > 255 );
+ mork_size size = ( needBig )?
+ morkBigBookAtom::SizeForFill(fill) :
+ morkWeeBookAtom::SizeForFill(fill);
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+ if ( newAtom )
+ {
+ morkBuf buf(inAtom.mBigBookAtom_Body, fill);
+ if ( needBig )
+ ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+ buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+ else
+ ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+ buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+ }
+ return newAtom;
+}
+
+morkBookAtom*
+morkPool::NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom,
+ morkZone* ioZone)
+ // make the smallest kind of book atom that can hold content in inAtom.
+ // The inAtom parameter is often expected to be a staged book atom in
+ // the store, which was used to search an atom space for existing atoms.
+{
+ morkBookAtom* newAtom = 0;
+
+ mork_cscode form = inAtom.mFarBookAtom_Form;
+ mork_fill fill = inAtom.mFarBookAtom_Size;
+ mork_bool needBig = ( form || fill > 255 );
+ mork_size size = ( needBig )?
+ morkBigBookAtom::SizeForFill(fill) :
+ morkWeeBookAtom::SizeForFill(fill);
+
+#ifdef morkZone_CONFIG_ARENA
+ // a zone 'chip' remembers no size, and so cannot be deallocated:
+ newAtom = (morkBookAtom*) ioZone->ZoneNewChip(ev, size);
+#else /*morkZone_CONFIG_ARENA*/
+ MORK_USED_1(ioZone);
+ mPool_Heap->Alloc(ev->AsMdbEnv(), size, (void**) &newAtom);
+#endif /*morkZone_CONFIG_ARENA*/
+ if ( newAtom )
+ {
+ morkBuf buf(inAtom.mFarBookAtom_Body, fill);
+ if ( needBig )
+ ((morkBigBookAtom*) newAtom)->InitBigBookAtom(ev,
+ buf, form, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+ else
+ ((morkWeeBookAtom*) newAtom)->InitWeeBookAtom(ev,
+ buf, inAtom.mBookAtom_Space, inAtom.mBookAtom_Id);
+ }
+ return newAtom;
+}
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkPool.h b/db/mork/src/morkPool.h
new file mode 100644
index 000000000..b2b349347
--- /dev/null
+++ b/db/mork/src/morkPool.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKPOOL_
+#define _MORKPOOL_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkHandle;
+class morkHandleFrame;
+class morkHandleFace; // just an opaque cookie type
+class morkBigBookAtom;
+class morkFarBookAtom;
+
+#define morkDerived_kPool /*i*/ 0x706C /* ascii 'pl' */
+
+/*| morkPool: a place to manage pools of non-node objects that are memory
+**| managed out of large chunks of space, so that per-object management
+**| space overhead has no significant cost.
+|*/
+class morkPool : public morkNode {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+ nsIMdbHeap* mPool_Heap; // NON-refcounted heap instance
+
+ morkDeque mPool_Blocks; // linked list of large blocks from heap
+
+ // These two lists contain instances of morkHandleFrame:
+ morkDeque mPool_UsedHandleFrames; // handle frames currently being used
+ morkDeque mPool_FreeHandleFrames; // handle frames currently in free list
+
+ mork_count mPool_UsedFramesCount; // length of mPool_UsedHandleFrames
+ mork_count mPool_FreeFramesCount; // length of mPool_UsedHandleFrames
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev); // ClosePool() only if open
+ virtual ~morkPool(); // assert that ClosePool() executed earlier
+
+public: // morkPool construction & destruction
+ morkPool(const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbHeap* ioSlotHeap);
+ morkPool(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbHeap* ioSlotHeap);
+ void ClosePool(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkPool(const morkPool& other);
+ morkPool& operator=(const morkPool& other);
+
+public: // dynamic type identification
+ mork_bool IsPool() const
+ { return IsNode() && mNode_Derived == morkDerived_kPool; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ void NonPoolTypeError(morkEnv* ev);
+
+public: // morkNode memory management operators
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkNode::MakeNew(inSize, ioHeap, ev); }
+
+ void* operator new(size_t inSize) CPP_THROW_NEW
+ { return ::operator new(inSize); }
+
+
+public: // other pool methods
+
+ // alloc and free individual instances of handles (inside hand frames):
+ morkHandleFace* NewHandle(morkEnv* ev, mork_size inSize, morkZone* ioZone);
+ void ZapHandle(morkEnv* ev, morkHandleFace* ioHandle);
+
+ // alloc and free individual instances of rows:
+ morkRow* NewRow(morkEnv* ev, morkZone* ioZone); // alloc new row instance
+ void ZapRow(morkEnv* ev, morkRow* ioRow, morkZone* ioZone); // free old row instance
+
+ // alloc and free entire vectors of cells (not just one cell at a time)
+ morkCell* NewCells(morkEnv* ev, mork_size inSize, morkZone* ioZone);
+ void ZapCells(morkEnv* ev, morkCell* ioVector, mork_size inSize, morkZone* ioZone);
+
+ // resize (grow or trim) cell vectors inside a containing row instance
+ mork_bool AddRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone);
+ mork_bool CutRowCells(morkEnv* ev, morkRow* ioRow, mork_size inNewSize, morkZone* ioZone);
+
+ // alloc & free individual instances of atoms (lots of atom subclasses):
+ void ZapAtom(morkEnv* ev, morkAtom* ioAtom, morkZone* ioZone); // any subclass (by kind)
+
+ morkOidAtom* NewRowOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone);
+ morkOidAtom* NewTableOidAtom(morkEnv* ev, const mdbOid& inOid, morkZone* ioZone);
+
+ morkAtom* NewAnonAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkZone* ioZone);
+ // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+ // anon atom will be created, and otherwise a 'big' anon atom.
+
+ morkBookAtom* NewBookAtom(morkEnv* ev, const morkBuf& inBuf,
+ mork_cscode inForm, morkAtomSpace* ioSpace, mork_aid inAid, morkZone* ioZone);
+ // if inForm is zero, and inBuf.mBuf_Fill is less than 256, then a 'wee'
+ // book atom will be created, and otherwise a 'big' book atom.
+
+ morkBookAtom* NewBookAtomCopy(morkEnv* ev, const morkBigBookAtom& inAtom, morkZone* ioZone);
+ // make the smallest kind of book atom that can hold content in inAtom.
+ // The inAtom parameter is often expected to be a staged book atom in
+ // the store, which was used to search an atom space for existing atoms.
+
+ morkBookAtom* NewFarBookAtomCopy(morkEnv* ev, const morkFarBookAtom& inAtom, morkZone* ioZone);
+ // make the smallest kind of book atom that can hold content in inAtom.
+ // The inAtom parameter is often expected to be a staged book atom in
+ // the store, which was used to search an atom space for existing atoms.
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakPool(morkPool* me,
+ morkEnv* ev, morkPool** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongPool(morkPool* me,
+ morkEnv* ev, morkPool** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPOOL_ */
+
diff --git a/db/mork/src/morkPortTableCursor.cpp b/db/mork/src/morkPortTableCursor.cpp
new file mode 100644
index 000000000..2542fcc49
--- /dev/null
+++ b/db/mork/src/morkPortTableCursor.cpp
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKPORTTABLECURSOR_
+#include "morkPortTableCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkPortTableCursor::CloseMorkNode(morkEnv* ev) // ClosePortTableCursor() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->ClosePortTableCursor(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkPortTableCursor::~morkPortTableCursor() // ClosePortTableCursor() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+}
+
+/*public non-poly*/
+morkPortTableCursor::morkPortTableCursor(morkEnv* ev,
+ const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope,
+ mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap)
+: morkCursor(ev, inUsage, ioHeap)
+, mPortTableCursor_Store( 0 )
+, mPortTableCursor_RowScope( (mdb_scope) -1 ) // we want != inRowScope
+, mPortTableCursor_TableKind( (mdb_kind) -1 ) // we want != inTableKind
+, mPortTableCursor_LastTable ( 0 ) // not refcounted
+, mPortTableCursor_RowSpace( 0 ) // strong ref to row space
+, mPortTableCursor_TablesDidEnd( morkBool_kFalse )
+, mPortTableCursor_SpacesDidEnd( morkBool_kFalse )
+{
+ if ( ev->Good() )
+ {
+ if ( ioStore && ioSlotHeap )
+ {
+ mCursor_Pos = -1;
+ mCursor_Seed = 0; // let the iterator do its own seed handling
+ morkStore::SlotWeakStore(ioStore, ev, &mPortTableCursor_Store);
+
+ if ( this->SetRowScope(ev, inRowScope) )
+ this->SetTableKind(ev, inTableKind);
+
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kPortTableCursor;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkPortTableCursor, morkCursor, nsIMdbPortTableCursor)
+
+morkEnv*
+morkPortTableCursor::CanUsePortTableCursor(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr) const
+{
+ morkEnv* outEnv = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( IsPortTableCursor() )
+ outEnv = ev;
+ else
+ NonPortTableCursorTypeError(ev);
+ *outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outEnv);
+ return outEnv;
+}
+
+
+/*public non-poly*/ void
+morkPortTableCursor::ClosePortTableCursor(morkEnv* ev)
+{
+ if ( this->IsNode() )
+ {
+ mCursor_Pos = -1;
+ mCursor_Seed = 0;
+ mPortTableCursor_LastTable = 0;
+ morkStore::SlotWeakStore((morkStore*) 0, ev, &mPortTableCursor_Store);
+ morkRowSpace::SlotStrongRowSpace((morkRowSpace*) 0, ev,
+ &mPortTableCursor_RowSpace);
+ this->CloseCursor(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkPortTableCursor::NilCursorStoreError(morkEnv* ev)
+{
+ ev->NewError("nil mPortTableCursor_Store");
+}
+
+/*static*/ void
+morkPortTableCursor::NonPortTableCursorTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkPortTableCursor");
+}
+
+mork_bool
+morkPortTableCursor::SetRowScope(morkEnv* ev, mork_scope inRowScope)
+{
+ mPortTableCursor_RowScope = inRowScope;
+ mPortTableCursor_LastTable = 0; // restart iteration of space
+
+ mPortTableCursor_TableIter.CloseMapIter(ev);
+ mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+ mPortTableCursor_SpacesDidEnd = morkBool_kTrue;
+
+ morkStore* store = mPortTableCursor_Store;
+ if ( store )
+ {
+ morkRowSpace* space = mPortTableCursor_RowSpace;
+
+ if ( inRowScope ) // intend to cover a specific scope only?
+ {
+ space = store->LazyGetRowSpace(ev, inRowScope);
+ morkRowSpace::SlotStrongRowSpace(space, ev,
+ &mPortTableCursor_RowSpace);
+
+ // We want mPortTableCursor_SpacesDidEnd == morkBool_kTrue
+ // to show this is the only space to be covered.
+ }
+ else // prepare space map iter to cover all space scopes
+ {
+ morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;
+ rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+
+ space = 0;
+ (void) rsi->FirstRowSpace(ev, (mork_scope*) 0, &space);
+ morkRowSpace::SlotStrongRowSpace(space, ev,
+ &mPortTableCursor_RowSpace);
+
+ if ( space ) // found first space in store
+ mPortTableCursor_SpacesDidEnd = morkBool_kFalse;
+ }
+
+ this->init_space_tables_map(ev);
+ }
+ else
+ this->NilCursorStoreError(ev);
+
+ return ev->Good();
+}
+
+void
+morkPortTableCursor::init_space_tables_map(morkEnv* ev)
+{
+ morkRowSpace* space = mPortTableCursor_RowSpace;
+ if ( space && ev->Good() )
+ {
+ morkTableMapIter* ti = &mPortTableCursor_TableIter;
+ ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+ if ( ev->Good() )
+ mPortTableCursor_TablesDidEnd = morkBool_kFalse;
+ }
+}
+
+
+mork_bool
+morkPortTableCursor::SetTableKind(morkEnv* ev, mork_kind inTableKind)
+{
+ mPortTableCursor_TableKind = inTableKind;
+ mPortTableCursor_LastTable = 0; // restart iteration of space
+
+ mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+
+ morkRowSpace* space = mPortTableCursor_RowSpace;
+ if ( !space && mPortTableCursor_RowScope == 0 )
+ {
+ this->SetRowScope(ev, 0);
+ }
+ this->init_space_tables_map(ev);
+
+ return ev->Good();
+}
+
+morkRowSpace*
+morkPortTableCursor::NextSpace(morkEnv* ev)
+{
+ morkRowSpace* outSpace = 0;
+ mPortTableCursor_LastTable = 0;
+ mPortTableCursor_SpacesDidEnd = morkBool_kTrue;
+ mPortTableCursor_TablesDidEnd = morkBool_kTrue;
+
+ if ( !mPortTableCursor_RowScope ) // not just one scope?
+ {
+ morkStore* store = mPortTableCursor_Store;
+ if ( store )
+ {
+ morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;
+
+ (void) rsi->NextRowSpace(ev, (mork_scope*) 0, &outSpace);
+ morkRowSpace::SlotStrongRowSpace(outSpace, ev,
+ &mPortTableCursor_RowSpace);
+
+ if ( outSpace ) // found next space in store
+ {
+ mPortTableCursor_SpacesDidEnd = morkBool_kFalse;
+
+ this->init_space_tables_map(ev);
+
+ if ( ev->Bad() )
+ outSpace = 0;
+ }
+ }
+ else
+ this->NilCursorStoreError(ev);
+ }
+
+ return outSpace;
+}
+
+morkTable *
+morkPortTableCursor::NextTable(morkEnv* ev)
+{
+ mork_kind kind = mPortTableCursor_TableKind;
+
+ do // until spaces end, or until we find a table in a space
+ {
+ morkRowSpace* space = mPortTableCursor_RowSpace;
+ if ( mPortTableCursor_TablesDidEnd ) // current space exhausted?
+ space = this->NextSpace(ev); // go on to the next space
+
+ if ( space ) // have a space remaining that might hold tables?
+ {
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ morkTableMapIter* ti = &mPortTableCursor_TableIter;
+ morkTable* table = ( mPortTableCursor_LastTable )?
+ ti->NextTable(ev) : ti->FirstTable(ev);
+
+ for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ mork_tid* key = 0; // ignore keys in table map
+ morkTable* table = 0; // old value table in the map
+ morkTableMapIter* ti = &mPortTableCursor_TableIter;
+ mork_change* c = ( mPortTableCursor_LastTable )?
+ ti->NextTable(ev, key, &table) : ti->FirstTable(ev, key, &table);
+
+ for ( ; c && ev->Good(); c = ti->NextTable(ev, key, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+ {
+ if ( table && table->IsTable() )
+ {
+ if ( !kind || kind == table->mTable_Kind )
+ {
+ mPortTableCursor_LastTable = table; // ti->NextTable() hence
+ return table;
+ }
+ }
+ else
+ table->NonTableTypeWarning(ev);
+ }
+ mPortTableCursor_TablesDidEnd = morkBool_kTrue; // space is done
+ mPortTableCursor_LastTable = 0; // make sure next space starts fresh
+ }
+
+ } while ( ev->Good() && !mPortTableCursor_SpacesDidEnd );
+
+ return (morkTable*) 0;
+}
+
+
+// { ----- begin table iteration methods -----
+
+// { ===== begin nsIMdbPortTableCursor methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkPortTableCursor::SetPort(nsIMdbEnv* mev, nsIMdbPort* ioPort)
+{
+ NS_ASSERTION(false,"not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbPort* outPort = 0;
+ morkEnv* ev =
+ this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ if ( mPortTableCursor_Store )
+ outPort = mPortTableCursor_Store->AcquireStoreHandle(ev);
+ outErr = ev->AsErr();
+ }
+ if ( acqPort )
+ *acqPort = outPort;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::SetRowScope(nsIMdbEnv* mev, // sets pos to -1
+ mdb_scope inRowScope)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev =
+ this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ mCursor_Pos = -1;
+
+ SetRowScope(ev, inRowScope);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
+{
+ nsresult outErr = NS_OK;
+ mdb_scope rowScope = 0;
+ morkEnv* ev =
+ this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ rowScope = mPortTableCursor_RowScope;
+ outErr = ev->AsErr();
+ }
+ *outRowScope = rowScope;
+ return outErr;
+}
+// setting row scope to zero iterates over all row scopes in port
+
+NS_IMETHODIMP
+morkPortTableCursor::SetTableKind(nsIMdbEnv* mev, // sets pos to -1
+ mdb_kind inTableKind)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev =
+ this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ mCursor_Pos = -1;
+
+ SetTableKind(ev, inTableKind);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkPortTableCursor::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
+// setting table kind to zero iterates over all table kinds in row scope
+{
+ nsresult outErr = NS_OK;
+ mdb_kind tableKind = 0;
+ morkEnv* ev =
+ this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ tableKind = mPortTableCursor_TableKind;
+ outErr = ev->AsErr();
+ }
+ *outTableKind = tableKind;
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin table iteration methods -----
+NS_IMETHODIMP
+morkPortTableCursor::NextTable( // get table at next position in the db
+ nsIMdbEnv* mev, // context
+ nsIMdbTable** acqTable)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev =
+ CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = NextTable(ev);
+ if ( table && ev->Good() )
+ outTable = table->AcquireTableHandle(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkPortTableCursor.h b/db/mork/src/morkPortTableCursor.h
new file mode 100644
index 000000000..cc9edaa67
--- /dev/null
+++ b/db/mork/src/morkPortTableCursor.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKPORTTABLECURSOR_
+#define _MORKPORTTABLECURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinPortTableCursor;
+#define morkDerived_kPortTableCursor /*i*/ 0x7443 /* ascii 'tC' */
+
+class morkPortTableCursor : public morkCursor, public nsIMdbPortTableCursor { // row iterator
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkFactory* mObject_Factory; // weak ref to suite factory
+
+ // mork_seed mCursor_Seed;
+ // mork_pos mCursor_Pos;
+ // mork_bool mCursor_DoFailOnSeedOutOfSync;
+ // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetPort(nsIMdbEnv* ev, nsIMdbPort* ioPort) override; // sets pos to -1
+ NS_IMETHOD GetPort(nsIMdbEnv* ev, nsIMdbPort** acqPort) override;
+
+ NS_IMETHOD SetRowScope(nsIMdbEnv* ev, // sets pos to -1
+ mdb_scope inRowScope) override;
+ NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override;
+ // setting row scope to zero iterates over all row scopes in port
+
+ NS_IMETHOD SetTableKind(nsIMdbEnv* ev, // sets pos to -1
+ mdb_kind inTableKind) override;
+ NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override;
+ // setting table kind to zero iterates over all table kinds in row scope
+ // } ----- end attribute methods -----
+
+ // { ----- begin table iteration methods -----
+ NS_IMETHOD NextTable( // get table at next position in the db
+ nsIMdbEnv* ev, // context
+ nsIMdbTable** acqTable) override; // the next table in the iteration
+ // } ----- end table iteration methods -----
+ morkStore* mPortTableCursor_Store; // weak ref to store
+
+ mdb_scope mPortTableCursor_RowScope;
+ mdb_kind mPortTableCursor_TableKind;
+
+ // We only care if LastTable is non-nil, so it is not refcounted;
+ // so you must never access table state or methods using LastTable:
+
+ morkTable* mPortTableCursor_LastTable; // nil or last table (no refcount)
+ morkRowSpace* mPortTableCursor_RowSpace; // current space (strong ref)
+
+ morkRowSpaceMapIter mPortTableCursor_SpaceIter; // iter over spaces
+ morkTableMapIter mPortTableCursor_TableIter; // iter over tables
+
+ // these booleans indicate when the table or space iterator is exhausted:
+
+ mork_bool mPortTableCursor_TablesDidEnd; // no more tables?
+ mork_bool mPortTableCursor_SpacesDidEnd; // no more spaces?
+ mork_u1 mPortTableCursor_Pad[ 2 ]; // for u4 alignment
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // ClosePortTableCursor()
+
+public: // morkPortTableCursor construction & destruction
+ morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, mdb_scope inRowScope,
+ mdb_kind inTableKind, nsIMdbHeap* ioSlotHeap);
+ void ClosePortTableCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkPortTableCursor(const morkPortTableCursor& other);
+ morkPortTableCursor& operator=(const morkPortTableCursor& other);
+
+public: // dynamic type identification
+ mork_bool IsPortTableCursor() const
+ { return IsNode() && mNode_Derived == morkDerived_kPortTableCursor; }
+// } ===== end morkNode methods =====
+
+protected: // utilities
+ virtual ~morkPortTableCursor(); // assert that close executed earlier
+
+ void init_space_tables_map(morkEnv* ev);
+
+public: // other cursor methods
+
+ static void NilCursorStoreError(morkEnv* ev);
+ static void NonPortTableCursorTypeError(morkEnv* ev);
+
+ morkEnv* CanUsePortTableCursor(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr) const;
+
+
+ morkRowSpace* NextSpace(morkEnv* ev);
+ morkTable* NextTable(morkEnv* ev);
+
+ mork_bool SetRowScope(morkEnv* ev, mork_scope inRowScope);
+ mork_bool SetTableKind(morkEnv* ev, mork_kind inTableKind);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakPortTableCursor(morkPortTableCursor* me,
+ morkEnv* ev, morkPortTableCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongPortTableCursor(morkPortTableCursor* me,
+ morkEnv* ev, morkPortTableCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPORTTABLECURSOR_ */
diff --git a/db/mork/src/morkProbeMap.cpp b/db/mork/src/morkProbeMap.cpp
new file mode 100644
index 000000000..c2a9d4645
--- /dev/null
+++ b/db/mork/src/morkProbeMap.cpp
@@ -0,0 +1,1234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is a port to NS Mork from public domain Mithril C++ sources.
+// Note many code comments here come verbatim from cut-and-pasted Mithril.
+// In many places, code is identical; Mithril versions stay public domain.
+// Changes in porting are mainly class type and scalar type name changes.
+
+#include "nscore.h"
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+/*============================================================================*/
+/* morkMapScratch */
+
+void morkMapScratch::halt_map_scratch(morkEnv* ev)
+{
+ nsIMdbHeap* heap = sMapScratch_Heap;
+
+ if ( heap )
+ {
+ if ( sMapScratch_Keys )
+ heap->Free(ev->AsMdbEnv(), sMapScratch_Keys);
+ if ( sMapScratch_Vals )
+ heap->Free(ev->AsMdbEnv(), sMapScratch_Vals);
+ }
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+/*============================================================================*/
+/* morkProbeMap */
+
+void morkProbeMap::ProbeMapBadTagError(morkEnv* ev) const
+{
+ ev->NewError("bad sProbeMap_Tag");
+}
+
+void morkProbeMap::WrapWithNoVoidSlotError(morkEnv* ev) const
+{
+ ev->NewError("wrap without void morkProbeMap slot");
+}
+
+void morkProbeMap::GrowFailsMaxFillError(morkEnv* ev) const
+{
+ ev->NewError("grow fails morkEnv > sMap_Fill");
+}
+
+void morkProbeMap::MapKeyIsNotIPError(morkEnv* ev) const
+{
+ ev->NewError("not sMap_KeyIsIP");
+}
+
+void morkProbeMap::MapValIsNotIPError(morkEnv* ev) const
+{
+ ev->NewError("not sMap_ValIsIP");
+}
+
+void morkProbeMap::rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch)
+{
+ mork_size keySize = sMap_KeySize; // size of every key bucket
+ mork_size valSize = sMap_ValSize; // size of every associated value
+
+ mork_count slots = sMap_Slots; // number of new buckets
+ mork_u1* keys = sMap_Keys; // destination for rehashed keys
+ mork_u1* vals = sMap_Vals; // destination for any copied values
+
+ mork_bool keyIsIP = ( keys && keySize == sizeof(mork_ip) && sMap_KeyIsIP );
+ mork_bool valIsIP = ( vals && valSize == sizeof(mork_ip) && sMap_ValIsIP );
+
+ mork_count oldSlots = ioScratch->sMapScratch_Slots; // sMap_Slots
+ mork_u1* oldKeys = ioScratch->sMapScratch_Keys; // sMap_Keys
+ mork_u1* oldVals = ioScratch->sMapScratch_Vals; // sMap_Vals
+ mork_u1* end = oldKeys + (keySize * oldSlots); // one byte past last key
+
+ mork_fill fill = 0; // let's count the actual fill for a double check
+
+ while ( oldKeys < end ) // another old key bucket to rehash if non-nil?
+ {
+ if ( !this->ProbeMapIsKeyNil(ev, oldKeys) ) // need to rehash?
+ {
+ ++fill; // this had better match sMap_Fill when we are all done
+ mork_u4 hash = this->ProbeMapHashMapKey(ev, oldKeys);
+
+ mork_pos i = hash % slots; // target hash bucket
+ mork_pos startPos = i; // remember start to detect
+
+ mork_u1* k = keys + (i * keySize);
+ while ( !this->ProbeMapIsKeyNil(ev, k) )
+ {
+ if ( ++i >= (mork_pos)slots ) // advanced past end? need to wrap around now?
+ i = 0; // wrap around to first slot in map's hash table
+
+ if ( i == startPos ) // no void slots were found anywhere in map?
+ {
+ this->WrapWithNoVoidSlotError(ev); // should never happen
+ return; // this is bad, and we can't go on with the rehash
+ }
+ k = keys + (i * keySize);
+ }
+ if ( keyIsIP ) // int special case?
+ *((mork_ip*) k) = *((const mork_ip*) oldKeys); // fast bitwise copy
+ else
+ MORK_MEMCPY(k, oldKeys, keySize); // slow bitwise copy
+
+ if ( oldVals ) // need to copy values as well?
+ {
+ mork_size valOffset = (i * valSize);
+ mork_u1* v = vals + valOffset;
+ mork_u1* ov = oldVals + valOffset;
+ if ( valIsIP ) // int special case?
+ *((mork_ip*) v) = *((const mork_ip*) ov); // fast bitwise copy
+ else
+ MORK_MEMCPY(v, ov, valSize); // slow bitwise copy
+ }
+ }
+ oldKeys += keySize; // advance to next key bucket in old map
+ }
+ if ( fill != sMap_Fill ) // is the recorded value of sMap_Fill wrong?
+ {
+ ev->NewWarning("fill != sMap_Fill");
+ sMap_Fill = fill;
+ }
+}
+
+mork_bool morkProbeMap::grow_probe_map(morkEnv* ev)
+{
+ if ( sMap_Heap ) // can we grow the map?
+ {
+ mork_num newSlots = ((sMap_Slots * 4) / 3) + 1; // +25%
+ morkMapScratch old; // a place to temporarily hold all the old arrays
+ if ( this->new_slots(ev, &old, newSlots) ) // have more?
+ {
+ ++sMap_Seed; // note the map has changed
+ this->rehash_old_map(ev, &old);
+
+ if ( ev->Good() )
+ {
+ mork_count slots = sMap_Slots;
+ mork_num emptyReserve = (slots / 7) + 1; // keep this many empty
+ mork_fill maxFill = slots - emptyReserve; // new max occupancy
+ if ( maxFill > sMap_Fill ) // new max is bigger than old occupancy?
+ sProbeMap_MaxFill = maxFill; // we can install new max for fill
+ else
+ this->GrowFailsMaxFillError(ev); // we have invariant failure
+ }
+
+ if ( ev->Bad() ) // rehash failed? need to revert map to last state?
+ this->revert_map(ev, &old); // swap the vectors back again
+
+ old.halt_map_scratch(ev); // remember to free the old arrays
+ }
+ }
+ else ev->OutOfMemoryError();
+
+ return ev->Good();
+}
+
+void morkProbeMap::revert_map(morkEnv* ev, morkMapScratch* ioScratch)
+{
+ mork_count tempSlots = ioScratch->sMapScratch_Slots; // sMap_Slots
+ mork_u1* tempKeys = ioScratch->sMapScratch_Keys; // sMap_Keys
+ mork_u1* tempVals = ioScratch->sMapScratch_Vals; // sMap_Vals
+
+ ioScratch->sMapScratch_Slots = sMap_Slots;
+ ioScratch->sMapScratch_Keys = sMap_Keys;
+ ioScratch->sMapScratch_Vals = sMap_Vals;
+
+ sMap_Slots = tempSlots;
+ sMap_Keys = tempKeys;
+ sMap_Vals = tempVals;
+}
+
+void morkProbeMap::put_probe_kv(morkEnv* ev,
+ const void* inAppKey, const void* inAppVal, mork_pos inPos)
+{
+ mork_u1* mapVal = 0;
+ mork_u1* mapKey = 0;
+
+ mork_num valSize = sMap_ValSize;
+ if ( valSize && inAppVal ) // map holds values? caller sends value?
+ {
+ mork_u1* val = sMap_Vals + (valSize * inPos);
+ if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case?
+ *((mork_ip*) val) = *((const mork_ip*) inAppVal);
+ else
+ mapVal = val; // show possible need to call ProbeMapPushIn()
+ }
+ if ( inAppKey ) // caller sends the key?
+ {
+ mork_num keySize = sMap_KeySize;
+ mork_u1* key = sMap_Keys + (keySize * inPos);
+ if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case?
+ *((mork_ip*) key) = *((const mork_ip*) inAppKey);
+ else
+ mapKey = key; // show possible need to call ProbeMapPushIn()
+ }
+ else
+ ev->NilPointerError();
+
+ if ( ( inAppVal && mapVal ) || ( inAppKey && mapKey ) )
+ this->ProbeMapPushIn(ev, inAppKey, inAppVal, mapKey, mapVal);
+
+ if ( sMap_Fill > sProbeMap_MaxFill )
+ this->grow_probe_map(ev);
+}
+
+void morkProbeMap::get_probe_kv(morkEnv* ev,
+ void* outAppKey, void* outAppVal, mork_pos inPos) const
+{
+ const mork_u1* mapVal = 0;
+ const mork_u1* mapKey = 0;
+
+ mork_num valSize = sMap_ValSize;
+ if ( valSize && outAppVal ) // map holds values? caller wants value?
+ {
+ const mork_u1* val = sMap_Vals + (valSize * inPos);
+ if ( valSize == sizeof(mork_ip) && sMap_ValIsIP ) // int special case?
+ *((mork_ip*) outAppVal) = *((const mork_ip*) val);
+ else
+ mapVal = val; // show possible need to call ProbeMapPullOut()
+ }
+ if ( outAppKey ) // caller wants the key?
+ {
+ mork_num keySize = sMap_KeySize;
+ const mork_u1* key = sMap_Keys + (keySize * inPos);
+ if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP ) // int special case?
+ *((mork_ip*) outAppKey) = *((const mork_ip*) key);
+ else
+ mapKey = key; // show possible need to call ProbeMapPullOut()
+ }
+ if ( ( outAppVal && mapVal ) || ( outAppKey && mapKey ) )
+ this->ProbeMapPullOut(ev, mapKey, mapVal, outAppKey, outAppVal);
+}
+
+mork_test
+morkProbeMap::find_key_pos(morkEnv* ev, const void* inAppKey,
+ mork_u4 inHash, mork_pos* outPos) const
+{
+ mork_u1* k = sMap_Keys; // array of keys, each of size sMap_KeySize
+ mork_num size = sMap_KeySize; // number of bytes in each key
+ mork_count slots = sMap_Slots; // total number of key buckets
+ mork_pos i = inHash % slots; // target hash bucket
+ mork_pos startPos = i; // remember start to detect
+
+ mork_test outTest = this->MapTest(ev, k + (i * size), inAppKey);
+ while ( outTest == morkTest_kMiss )
+ {
+ if ( ++i >= (mork_pos)slots ) // advancing goes beyond end? need to wrap around now?
+ i = 0; // wrap around to first slot in map's hash table
+
+ if ( i == startPos ) // no void slots were found anywhere in map?
+ {
+ this->WrapWithNoVoidSlotError(ev); // should never happen
+ break; // end loop on kMiss; note caller expects either kVoid or kHit
+ }
+ outTest = this->MapTest(ev, k + (i * size), inAppKey);
+ }
+ *outPos = i;
+
+ return outTest;
+}
+
+void morkProbeMap::probe_map_lazy_init(morkEnv* ev)
+{
+ if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+ {
+ // The constructor cannot successfully call virtual ProbeMapClearKey(),
+ // so we lazily do so now, when we add the first member to the map.
+
+ mork_u1* keys = sMap_Keys;
+ if ( keys ) // okay to call lazy virtual clear method on new map keys?
+ {
+ if ( sProbeMap_ZeroIsClearKey ) // zero is good enough to clear keys?
+ {
+ mork_num keyVolume = sMap_Slots * sMap_KeySize;
+ if ( keyVolume )
+ MORK_MEMSET(keys, 0, keyVolume);
+ }
+ else
+ this->ProbeMapClearKey(ev, keys, sMap_Slots);
+ }
+ else
+ this->MapNilKeysError(ev);
+ }
+ sProbeMap_LazyClearOnAdd = 0; // don't do this ever again
+}
+
+mork_bool
+morkProbeMap::MapAtPut(morkEnv* ev,
+ const void* inAppKey, const void* inAppVal,
+ void* outAppKey, void* outAppVal)
+{
+ mork_bool outPut = morkBool_kFalse;
+
+ if ( this->GoodProbeMap() ) /* looks good? */
+ {
+ if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+ this->probe_map_lazy_init(ev);
+
+ if ( ev->Good() )
+ {
+ mork_pos slotPos = 0;
+ mork_u4 hash = this->MapHash(ev, inAppKey);
+ mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos);
+ outPut = ( test == morkTest_kHit );
+
+ if ( outPut ) // replacing an old assoc? no change in member count?
+ {
+ if ( outAppKey || outAppVal ) /* copy old before cobber? */
+ this->get_probe_kv(ev, outAppKey, outAppVal, slotPos);
+ }
+ else // adding a new assoc increases membership by one
+ {
+ ++sMap_Fill; /* one more member in the collection */
+ }
+
+ if ( test != morkTest_kMiss ) /* found slot to hold new assoc? */
+ {
+ ++sMap_Seed; /* note the map has changed */
+ this->put_probe_kv(ev, inAppKey, inAppVal, slotPos);
+ }
+ }
+ }
+ else this->ProbeMapBadTagError(ev);
+
+ return outPut;
+}
+
+mork_bool
+morkProbeMap::MapAt(morkEnv* ev, const void* inAppKey,
+ void* outAppKey, void* outAppVal)
+{
+ if ( this->GoodProbeMap() ) /* looks good? */
+ {
+ if ( this->need_lazy_init() && sMap_Fill == 0 ) // pending lazy action?
+ this->probe_map_lazy_init(ev);
+
+ mork_pos slotPos = 0;
+ mork_u4 hash = this->MapHash(ev, inAppKey);
+ mork_test test = this->find_key_pos(ev, inAppKey, hash, &slotPos);
+ if ( test == morkTest_kHit ) /* found an assoc pair for inAppKey? */
+ {
+ this->get_probe_kv(ev, outAppKey, outAppVal, slotPos);
+ return morkBool_kTrue;
+ }
+ }
+ else this->ProbeMapBadTagError(ev);
+
+ return morkBool_kFalse;
+}
+
+mork_num
+morkProbeMap::MapCutAll(morkEnv* ev)
+{
+ mork_num outCutAll = 0;
+
+ if ( this->GoodProbeMap() ) /* looks good? */
+ {
+ outCutAll = sMap_Fill; /* number of members cut, which is all of them */
+
+ if ( sMap_Keys && !sProbeMap_ZeroIsClearKey )
+ this->ProbeMapClearKey(ev, sMap_Keys, sMap_Slots);
+
+ sMap_Fill = 0; /* map now has no members */
+ }
+ else this->ProbeMapBadTagError(ev);
+
+ return outCutAll;
+}
+
+// { ===== node interface =====
+
+/*virtual*/
+morkProbeMap::~morkProbeMap() // assert NodeStop() finished earlier
+{
+ MORK_ASSERT(sMap_Keys==0);
+ MORK_ASSERT(sProbeMap_Tag==0);
+}
+
+/*public virtual*/ void
+morkProbeMap::CloseMorkNode(morkEnv* ev) // CloseMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseProbeMap(ev);
+ this->MarkShut();
+ }
+}
+
+void morkProbeMap::CloseProbeMap(morkEnv* ev)
+{
+ if ( this->IsNode() )
+ {
+ nsIMdbHeap* heap = sMap_Heap;
+ if ( heap ) // able to free map arrays?
+ {
+ void* block = sMap_Keys;
+ if ( block )
+ {
+ heap->Free(ev->AsMdbEnv(), block);
+ sMap_Keys = 0;
+ }
+
+ block = sMap_Vals;
+ if ( block )
+ {
+ heap->Free(ev->AsMdbEnv(), block);
+ sMap_Vals = 0;
+ }
+ }
+ sMap_Keys = 0;
+ sMap_Vals = 0;
+
+ this->CloseNode(ev);
+ sProbeMap_Tag = 0;
+ sProbeMap_MaxFill = 0;
+
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+void*
+morkProbeMap::clear_alloc(morkEnv* ev, mork_size inSize)
+{
+ void* p = 0;
+ nsIMdbHeap* heap = sMap_Heap;
+ if ( heap )
+ {
+ if (NS_SUCCEEDED(heap->Alloc(ev->AsMdbEnv(), inSize, (void**) &p)) && p )
+ {
+ MORK_MEMSET(p, 0, inSize);
+ return p;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return (void*) 0;
+}
+
+/*| map_new_keys: allocate an array of inSlots new keys filled with zero.
+**| (cf IronDoc's FeHashTable_new_keys())
+|*/
+mork_u1*
+morkProbeMap::map_new_keys(morkEnv* ev, mork_num inSlots)
+{
+ mork_num size = inSlots * sMap_KeySize;
+ return (mork_u1*) this->clear_alloc(ev, size);
+}
+
+/*| map_new_vals: allocate an array of inSlots new values filled with zero.
+**| When values are zero sized, we just return a null pointer.
+**|
+**| (cf IronDoc's FeHashTable_new_values())
+|*/
+mork_u1*
+morkProbeMap::map_new_vals(morkEnv* ev, mork_num inSlots)
+{
+ mork_u1* values = 0;
+ mork_num size = inSlots * sMap_ValSize;
+ if ( size )
+ values = (mork_u1*) this->clear_alloc(ev, size);
+ return values;
+}
+
+
+void morkProbeMap::MapSeedOutOfSyncError(morkEnv* ev)
+{
+ ev->NewError("sMap_Seed out of sync");
+}
+
+void morkProbeMap::MapFillUnderflowWarning(morkEnv* ev)
+{
+ ev->NewWarning("sMap_Fill underflow");
+}
+
+void morkProbeMap::MapNilKeysError(morkEnv* ev)
+{
+ ev->NewError("nil sMap_Keys");
+}
+
+void morkProbeMap::MapZeroKeySizeError(morkEnv* ev)
+{
+ ev->NewError("zero sMap_KeySize");
+}
+
+/*static*/
+void morkProbeMap::ProbeMapCutError(morkEnv* ev)
+{
+ ev->NewError("morkProbeMap cannot cut");
+}
+
+
+void morkProbeMap::init_probe_map(morkEnv* ev, mork_size inSlots)
+{
+ // Note we cannot successfully call virtual ProbeMapClearKey() when we
+ // call init_probe_map() inside the constructor; so we leave this problem
+ // to the caller. (The constructor will call ProbeMapClearKey() later
+ // after setting a suitable lazy flag to show this action is pending.)
+
+ if ( ev->Good() )
+ {
+ morkMapScratch old;
+
+ if ( inSlots < 7 ) // capacity too small?
+ inSlots = 7; // increase to reasonable minimum
+ else if ( inSlots > (128 * 1024) ) // requested capacity too big?
+ inSlots = (128 * 1024); // decrease to reasonable maximum
+
+ if ( this->new_slots(ev, &old, inSlots) )
+ sProbeMap_Tag = morkProbeMap_kTag;
+
+ mork_count slots = sMap_Slots;
+ mork_num emptyReserve = (slots / 7) + 1; // keep this many empty
+ sProbeMap_MaxFill = slots - emptyReserve;
+
+ MORK_MEMSET(&old, 0, sizeof(morkMapScratch)); // don't bother halting
+ }
+}
+
+mork_bool
+morkProbeMap::new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots)
+{
+ mork_bool outNew = morkBool_kFalse;
+
+ // Note we cannot successfully call virtual ProbeMapClearKey() when we
+ // call new_slots() inside the constructor; so we leave this problem
+ // to the caller. (The constructor will call ProbeMapClearKey() later
+ // after setting a suitable lazy flag to show this action is pending.)
+
+ // allocate every new array before we continue:
+ mork_u1* newKeys = this->map_new_keys(ev, inSlots);
+ mork_u1* newVals = this->map_new_vals(ev, inSlots);
+
+ // okay for newVals to be null when values are zero sized?
+ mork_bool okayValues = ( newVals || !sMap_ValSize );
+
+ if ( newKeys && okayValues )
+ {
+ outNew = morkBool_kTrue; // we created every array needed
+
+ // init mapScratch using slots from current map:
+ old->sMapScratch_Heap = sMap_Heap;
+
+ old->sMapScratch_Slots = sMap_Slots;
+ old->sMapScratch_Keys = sMap_Keys;
+ old->sMapScratch_Vals = sMap_Vals;
+
+ // replace all map array slots using the newly allocated members:
+ ++sMap_Seed; // the map has changed
+ sMap_Keys = newKeys;
+ sMap_Vals = newVals;
+ sMap_Slots = inSlots;
+ }
+ else // free any allocations if only partially successful
+ {
+ nsIMdbHeap* heap = sMap_Heap;
+ if ( newKeys )
+ heap->Free(ev->AsMdbEnv(), newKeys);
+ if ( newVals )
+ heap->Free(ev->AsMdbEnv(), newVals);
+
+ MORK_MEMSET(old, 0, sizeof(morkMapScratch)); // zap scratch space
+ }
+
+ return outNew;
+}
+
+void
+morkProbeMap::clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap)
+{
+ sProbeMap_Tag = 0;
+ sMap_Seed = 0;
+ sMap_Slots = 0;
+ sMap_Fill = 0;
+ sMap_Keys = 0;
+ sMap_Vals = 0;
+ sProbeMap_MaxFill = 0;
+
+ sMap_Heap = ioMapHeap;
+ if ( !ioMapHeap )
+ ev->NilPointerError();
+}
+
+morkProbeMap::morkProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap,
+ mork_size inKeySize, mork_size inValSize,
+ nsIMdbHeap* ioMapHeap, mork_size inSlots,
+ mork_bool inZeroIsClearKey)
+
+: morkNode(ev, inUsage, ioNodeHeap)
+, sMap_Heap( ioMapHeap )
+
+, sMap_Keys( 0 )
+, sMap_Vals( 0 )
+
+, sMap_Seed( 0 ) // change count of members or structure
+
+, sMap_Slots( 0 ) // count of slots in the hash table
+, sMap_Fill( 0 ) // number of used slots in the hash table
+
+, sMap_KeySize( 0 ) // size of each key (cannot be zero)
+, sMap_ValSize( 0 ) // size of each val (zero allowed)
+
+, sMap_KeyIsIP( morkBool_kFalse ) // sMap_KeySize == sizeof(mork_ip)
+, sMap_ValIsIP( morkBool_kFalse ) // sMap_ValSize == sizeof(mork_ip)
+
+, sProbeMap_MaxFill( 0 )
+, sProbeMap_LazyClearOnAdd( 0 )
+, sProbeMap_ZeroIsClearKey( inZeroIsClearKey )
+, sProbeMap_Tag( 0 )
+{
+ // Note we cannot successfully call virtual ProbeMapClearKey() when we
+ // call init_probe_map() inside the constructor; so we leave this problem
+ // to the caller. (The constructor will call ProbeMapClearKey() later
+ // after setting a suitable lazy flag to show this action is pending.)
+
+ if ( ev->Good() )
+ {
+ this->clear_probe_map(ev, ioMapHeap);
+ if ( ev->Good() )
+ {
+ sMap_KeySize = inKeySize;
+ sMap_ValSize = inValSize;
+ sMap_KeyIsIP = ( inKeySize == sizeof(mork_ip) );
+ sMap_ValIsIP = ( inValSize == sizeof(mork_ip) );
+
+ this->init_probe_map(ev, inSlots);
+ if ( ev->Good() )
+ {
+ if ( !inZeroIsClearKey ) // must lazy clear later with virtual method?
+ sProbeMap_LazyClearOnAdd = morkProbeMap_kLazyClearOnAdd;
+
+ mNode_Derived = morkDerived_kProbeMap;
+ }
+ }
+ }
+}
+
+/*============================================================================*/
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkProbeMap::MapTest(morkEnv* ev,
+ const void* inMapKey, const void* inAppKey) const
+ // Note inMapKey is always a key already stored in the map, while inAppKey
+ // is always a method argument parameter from a client method call.
+ // This matters the most in morkProbeMap subclasses, which have the
+ // responsibility of putting 'app' keys into slots for 'map' keys, and
+ // the bit pattern representation might be different in such cases.
+ // morkTest_kHit means that inMapKey equals inAppKey (and this had better
+ // also imply that hash(inMapKey) == hash(inAppKey)).
+ // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this
+ // implies nothing at all about hash(inMapKey) and hash(inAppKey)).
+ // morkTest_kVoid means that inMapKey is not a valid key bit pattern,
+ // which means that key slot in the map is not being used. Note that
+ // kVoid is only expected as a return value in morkProbeMap subclasses,
+ // because morkProbeMap must ask whether a key slot is used or not.
+ // morkChainMap however, always knows when a key slot is used, so only
+ // key slots expected to have valid bit patterns will be presented to
+ // the MapTest() methods for morkChainMap subclasses.
+ //
+ // NOTE: it is very important that subclasses correctly return the value
+ // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern
+ // that means the slot is not being used, because this is the only way a
+ // probe map can terminate an unsuccessful search for a key in the map.
+{
+ mork_size keySize = sMap_KeySize;
+ if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP )
+ {
+ mork_ip mapKey = *((const mork_ip*) inMapKey);
+ if ( mapKey == *((const mork_ip*) inAppKey) )
+ return morkTest_kHit;
+ else
+ {
+ return ( mapKey )? morkTest_kMiss : morkTest_kVoid;
+ }
+ }
+ else
+ {
+ mork_bool allSame = morkBool_kTrue;
+ mork_bool allZero = morkBool_kTrue;
+ const mork_u1* ak = (const mork_u1*) inAppKey;
+ const mork_u1* mk = (const mork_u1*) inMapKey;
+ const mork_u1* end = mk + keySize;
+ --mk; // prepare for preincrement:
+ while ( ++mk < end )
+ {
+ mork_u1 byte = *mk;
+ if ( byte ) // any nonzero byte in map key means slot is not nil?
+ allZero = morkBool_kFalse;
+ if ( byte != *ak++ ) // bytes differ in map and app keys?
+ allSame = morkBool_kFalse;
+ }
+ if ( allSame )
+ return morkTest_kHit;
+ else
+ return ( allZero )? morkTest_kVoid : morkTest_kMiss;
+ }
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+ mork_size keySize = sMap_KeySize;
+ if ( keySize == sizeof(mork_ip) && sMap_KeyIsIP )
+ {
+ return *((const mork_ip*) inAppKey);
+ }
+ else
+ {
+ const mork_u1* key = (const mork_u1*) inAppKey;
+ const mork_u1* end = key + keySize;
+ --key; // prepare for preincrement:
+ while ( ++key < end )
+ {
+ if ( *key ) // any nonzero byte in map key means slot is not nil?
+ return morkBool_kFalse;
+ }
+ return morkBool_kTrue;
+ }
+ return (mork_u4) NS_PTR_TO_INT32(inAppKey);
+}
+
+
+/*============================================================================*/
+
+/*virtual*/ mork_u4
+morkProbeMap::ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const
+ // ProbeMapHashMapKey() does logically the same thing as MapHash(), and
+ // the default implementation actually calls virtual MapHash(). However,
+ // Subclasses must override this method whenever the formats of keys in
+ // the map differ from app keys outside the map, because MapHash() only
+ // works on keys in 'app' format, while ProbeMapHashMapKey() only works
+ // on keys in 'map' format. This method is called in order to rehash all
+ // map keys when a map is grown, and this causes all old map members to
+ // move into new slot locations.
+ //
+ // Note it is absolutely imperative that a hash for a key in 'map' format
+ // be exactly the same the hash of the same key in 'app' format, or else
+ // maps will seem corrupt later when keys in 'app' format cannot be found.
+{
+ return this->MapHash(ev, inMapKey);
+}
+
+/*virtual*/ mork_bool
+morkProbeMap::ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey)
+ // ProbeMapIsKeyNil() must say whether the representation of logical 'nil'
+ // is currently found inside the key at ioMapKey, for a key found within
+ // the map. The the map iterator uses this method to find map keys that
+ // are actually being used for valid map associations; otherwise the
+ // iterator cannot determine which map slots actually denote used keys.
+ // The default method version returns true if all the bits equal zero.
+{
+ if ( sMap_KeySize == sizeof(mork_ip) && sMap_KeyIsIP )
+ {
+ return !*((const mork_ip*) ioMapKey);
+ }
+ else
+ {
+ const mork_u1* key = (const mork_u1*) ioMapKey;
+ const mork_u1* end = key + sMap_KeySize;
+ --key; // prepare for preincrement:
+ while ( ++key < end )
+ {
+ if ( *key ) // any nonzero byte in map key means slot is not nil?
+ return morkBool_kFalse;
+ }
+ return morkBool_kTrue;
+ }
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ void* ioMapKey, mork_count inKeyCount) // array of keys inside map
+ // ProbeMapClearKey() must put some representation of logical 'nil' into
+ // every key slot in the map, such that MapTest() will later recognize
+ // that this bit pattern shows each key slot is not actually being used.
+ //
+ // This method is typically called whenever the map is either created or
+ // grown into a larger size, where ioMapKey is a pointer to an array of
+ // inKeyCount keys, where each key is this->MapKeySize() bytes in size.
+ // Note that keys are assumed immediately adjacent with no padding, so
+ // if any alignment requirements must be met, then subclasses should have
+ // already accounted for this when specifying a key size in the map.
+ //
+ // Since this method will be called when a map is being grown in size,
+ // nothing should be assumed about the state slots of the map, since the
+ // ioMapKey array might not yet live in sMap_Keys, and the array length
+ // inKeyCount might not yet live in sMap_Slots. However, the value kept
+ // in sMap_KeySize never changes, so this->MapKeySize() is always correct.
+{
+ if ( ioMapKey && inKeyCount )
+ {
+ MORK_MEMSET(ioMapKey, 0, (inKeyCount * sMap_KeySize));
+ }
+ else
+ ev->NilPointerWarning();
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ void* outMapKey, void* outMapVal) // (key,val) inside map
+ // This method actually puts keys and vals in the map in suitable format.
+ //
+ // ProbeMapPushIn() must copy a caller key and value in 'app' format
+ // into the map slots provided, which are in 'map' format. When the
+ // 'app' and 'map' formats are identical, then this is just a bitwise
+ // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+ // and this is exactly what the default implementation performs. However,
+ // if 'app' and 'map' formats are different, and MapTest() depends on this
+ // difference in format, then subclasses must override this method to do
+ // whatever is necessary to store the input app key in output map format.
+ //
+ // Do NOT write more than this->MapKeySize() bytes of a map key, or more
+ // than this->MapValSize() bytes of a map val, or corruption might ensue.
+ //
+ // The inAppKey and inAppVal parameters are the same ones passed into a
+ // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones
+ // determined by how the map currently positions key inAppKey in the map.
+ //
+ // Note any key or val parameter can be a null pointer, in which case
+ // this method must do nothing with those parameters. In particular, do
+ // no key move at all when either inAppKey or outMapKey is nil, and do
+ // no val move at all when either inAppVal or outMapVal is nil. Note that
+ // outMapVal should always be nil when this->MapValSize() is nil.
+{
+}
+
+/*virtual*/ void
+morkProbeMap::ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ void* outAppKey, void* outAppVal) const // (key,val) outside map
+ // This method actually gets keys and vals from the map in suitable format.
+ //
+ // ProbeMapPullOut() must copy a key and val in 'map' format into the
+ // caller key and val slots provided, which are in 'app' format. When the
+ // 'app' and 'map' formats are identical, then this is just a bitwise
+ // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+ // and this is exactly what the default implementation performs. However,
+ // if 'app' and 'map' formats are different, and MapTest() depends on this
+ // difference in format, then subclasses must override this method to do
+ // whatever is necessary to store the input map key in output app format.
+ //
+ // The outAppKey and outAppVal parameters are the same ones passed into a
+ // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are
+ // determined by how the map currently positions the target key in the map.
+ //
+ // Note any key or val parameter can be a null pointer, in which case
+ // this method must do nothing with those parameters. In particular, do
+ // no key move at all when either inMapKey or outAppKey is nil, and do
+ // no val move at all when either inMapVal or outAppVal is nil. Note that
+ // inMapVal should always be nil when this->MapValSize() is nil.
+{
+}
+
+
+/*============================================================================*/
+/* morkProbeMapIter */
+
+morkProbeMapIter::morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap)
+: sProbeMapIter_Map( 0 )
+, sProbeMapIter_Seed( 0 )
+, sProbeMapIter_HereIx( morkProbeMapIter_kBeforeIx )
+{
+ if ( ioMap )
+ {
+ if ( ioMap->GoodProbeMap() )
+ {
+ if ( ioMap->need_lazy_init() ) // pending lazy action?
+ ioMap->probe_map_lazy_init(ev);
+
+ sProbeMapIter_Map = ioMap;
+ sProbeMapIter_Seed = ioMap->sMap_Seed;
+ }
+ else ioMap->ProbeMapBadTagError(ev);
+ }
+ else ev->NilPointerError();
+}
+
+void morkProbeMapIter::CloseMapIter(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ sProbeMapIter_Map = 0;
+ sProbeMapIter_Seed = 0;
+
+ sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx;
+}
+
+morkProbeMapIter::morkProbeMapIter( )
+// zero most slots; caller must call InitProbeMapIter()
+{
+ sProbeMapIter_Map = 0;
+ sProbeMapIter_Seed = 0;
+
+ sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx;
+}
+
+void morkProbeMapIter::InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap)
+{
+ sProbeMapIter_Map = 0;
+ sProbeMapIter_Seed = 0;
+
+ sProbeMapIter_HereIx = morkProbeMapIter_kBeforeIx;
+
+ if ( ioMap )
+ {
+ if ( ioMap->GoodProbeMap() )
+ {
+ if ( ioMap->need_lazy_init() ) // pending lazy action?
+ ioMap->probe_map_lazy_init(ev);
+
+ sProbeMapIter_Map = ioMap;
+ sProbeMapIter_Seed = ioMap->sMap_Seed;
+ }
+ else ioMap->ProbeMapBadTagError(ev);
+ }
+ else ev->NilPointerError();
+}
+
+mork_bool morkProbeMapIter::IterFirst(morkEnv* ev,
+ void* outAppKey, void* outAppVal)
+{
+ sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done
+ morkProbeMap* map = sProbeMapIter_Map;
+
+ if ( map && map->GoodProbeMap() ) /* looks good? */
+ {
+ sProbeMapIter_Seed = map->sMap_Seed; /* sync the seeds */
+
+ mork_u1* k = map->sMap_Keys; // array of keys, each of size sMap_KeySize
+ mork_num size = map->sMap_KeySize; // number of bytes in each key
+ mork_count slots = map->sMap_Slots; // total number of key buckets
+ mork_pos here = 0; // first hash bucket
+
+ while ( here < (mork_pos)slots )
+ {
+ if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+ {
+ map->get_probe_kv(ev, outAppKey, outAppVal, here);
+
+ sProbeMapIter_HereIx = (mork_i4) here;
+ return morkBool_kTrue;
+ }
+ ++here; // next bucket
+ }
+ }
+ else map->ProbeMapBadTagError(ev);
+
+ return morkBool_kFalse;
+}
+
+mork_bool morkProbeMapIter::IterNext(morkEnv* ev,
+ void* outAppKey, void* outAppVal)
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+
+ if ( map && map->GoodProbeMap() ) /* looks good? */
+ {
+ if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */
+ {
+ if ( sProbeMapIter_HereIx != morkProbeMapIter_kAfterIx )
+ {
+ mork_pos here = (mork_pos) sProbeMapIter_HereIx;
+ if ( sProbeMapIter_HereIx < 0 )
+ here = 0;
+ else
+ ++here;
+
+ sProbeMapIter_HereIx = morkProbeMapIter_kAfterIx; // default to done
+
+ mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize
+ mork_num size = map->sMap_KeySize; // number of bytes in each key
+ mork_count slots = map->sMap_Slots; // total number of key buckets
+
+ while ( here < (mork_pos)slots )
+ {
+ if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+ {
+ map->get_probe_kv(ev, outAppKey, outAppVal, here);
+
+ sProbeMapIter_HereIx = (mork_i4) here;
+ return morkBool_kTrue;
+ }
+ ++here; // next bucket
+ }
+ }
+ }
+ else map->MapSeedOutOfSyncError(ev);
+ }
+ else map->ProbeMapBadTagError(ev);
+
+ return morkBool_kFalse;
+}
+
+mork_bool morkProbeMapIter::IterHere(morkEnv* ev,
+ void* outAppKey, void* outAppVal)
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+
+ if ( map && map->GoodProbeMap() ) /* looks good? */
+ {
+ if ( sProbeMapIter_Seed == map->sMap_Seed ) /* in sync? */
+ {
+ mork_pos here = (mork_pos) sProbeMapIter_HereIx;
+ mork_count slots = map->sMap_Slots; // total number of key buckets
+ if ( sProbeMapIter_HereIx >= 0 && (here < (mork_pos)slots))
+ {
+ mork_u1* k = map->sMap_Keys; // key array, each of size sMap_KeySize
+ mork_num size = map->sMap_KeySize; // number of bytes in each key
+
+ if ( !map->ProbeMapIsKeyNil(ev, k + (here * size)) )
+ {
+ map->get_probe_kv(ev, outAppKey, outAppVal, here);
+ return morkBool_kTrue;
+ }
+ }
+ }
+ else map->MapSeedOutOfSyncError(ev);
+ }
+ else map->ProbeMapBadTagError(ev);
+
+ return morkBool_kFalse;
+}
+
+mork_change*
+morkProbeMapIter::First(morkEnv* ev, void* outKey, void* outVal)
+{
+ if ( this->IterFirst(ev, outKey, outVal) )
+ return &sProbeMapIter_Change;
+
+ return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::Next(morkEnv* ev, void* outKey, void* outVal)
+{
+ if ( this->IterNext(ev, outKey, outVal) )
+ return &sProbeMapIter_Change;
+
+ return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::Here(morkEnv* ev, void* outKey, void* outVal)
+{
+ if ( this->IterHere(ev, outKey, outVal) )
+ return &sProbeMapIter_Change;
+
+ return (mork_change*) 0;
+}
+
+mork_change*
+morkProbeMapIter::CutHere(morkEnv* ev, void* outKey, void* outVal)
+{
+ morkProbeMap::ProbeMapCutError(ev);
+
+ return (mork_change*) 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// NOTE: the following methods ONLY work for sMap_ValIsIP pointer values.
+// (Note the implied assumption that zero is never a good value pattern.)
+
+void* morkProbeMapIter::IterFirstVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_ValIsIP )
+ {
+ void* v = 0;
+ this->IterFirst(ev, outKey, &v);
+ return v;
+ }
+ else
+ map->MapValIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+void* morkProbeMapIter::IterNextVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_ValIsIP )
+ {
+ void* v = 0;
+ this->IterNext(ev, outKey, &v);
+ return v;
+ }
+ else
+ map->MapValIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+void* morkProbeMapIter::IterHereVal(morkEnv* ev, void* outKey)
+// equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_ValIsIP )
+ {
+ void* v = 0;
+ this->IterHere(ev, outKey, &v);
+ return v;
+ }
+ else
+ map->MapValIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+// NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values.
+// (Note the implied assumption that zero is never a good key pattern.)
+
+void* morkProbeMapIter::IterFirstKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_KeyIsIP )
+ {
+ void* k = 0;
+ this->IterFirst(ev, &k, (void*) 0);
+ return k;
+ }
+ else
+ map->MapKeyIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+void* morkProbeMapIter::IterNextKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_KeyIsIP )
+ {
+ void* k = 0;
+ this->IterNext(ev, &k, (void*) 0);
+ return k;
+ }
+ else
+ map->MapKeyIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+void* morkProbeMapIter::IterHereKey(morkEnv* ev)
+// equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; }
+{
+ morkProbeMap* map = sProbeMapIter_Map;
+ if ( map )
+ {
+ if ( map->sMap_KeyIsIP )
+ {
+ void* k = 0;
+ this->IterHere(ev, &k, (void*) 0);
+ return k;
+ }
+ else
+ map->MapKeyIsNotIPError(ev);
+ }
+ return (void*) 0;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkProbeMap.h b/db/mork/src/morkProbeMap.h
new file mode 100644
index 000000000..8b9b23d21
--- /dev/null
+++ b/db/mork/src/morkProbeMap.h
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This code is a port to NS Mork from public domain Mithril C++ sources.
+// Note many code comments here come verbatim from cut-and-pasted Mithril.
+// In many places, code is identical; Mithril versions stay public domain.
+// Changes in porting are mainly class type and scalar type name changes.
+
+#ifndef _MORKPROBEMAP_
+#define _MORKPROBEMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkMapScratch { // utility class used by map subclasses
+public:
+ nsIMdbHeap* sMapScratch_Heap; // cached sMap_Heap
+ mork_count sMapScratch_Slots; // cached sMap_Slots
+
+ mork_u1* sMapScratch_Keys; // cached sMap_Keys
+ mork_u1* sMapScratch_Vals; // cached sMap_Vals
+
+public:
+ void halt_map_scratch(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kProbeMap 0x7072 /* ascii 'pr' */
+#define morkProbeMap_kTag 0x70724D50 /* ascii 'prMP' */
+
+#define morkProbeMap_kLazyClearOnAdd ((mork_u1) 'c')
+
+class morkProbeMap: public morkNode {
+
+protected:
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+protected:
+ // { begin morkMap slots
+ nsIMdbHeap* sMap_Heap; // strong ref to heap allocating all space
+
+ mork_u1* sMap_Keys;
+ mork_u1* sMap_Vals;
+
+ mork_count sMap_Seed; // change count of members or structure
+
+ mork_count sMap_Slots; // count of slots in the hash table
+ mork_fill sMap_Fill; // number of used slots in the hash table
+
+ mork_size sMap_KeySize; // size of each key (cannot be zero)
+ mork_size sMap_ValSize; // size of each val (zero allowed)
+
+ mork_bool sMap_KeyIsIP; // sMap_KeySize == sizeof(mork_ip)
+ mork_bool sMap_ValIsIP; // sMap_ValSize == sizeof(mork_ip)
+ mork_u1 sMap_Pad[ 2 ]; // for u4 alignment
+ // } end morkMap slots
+
+ friend class morkProbeMapIter; // for access to protected slots
+
+public: // getters
+ mork_count MapSeed() const { return sMap_Seed; }
+
+ mork_count MapSlots() const { return sMap_Slots; }
+ mork_fill MapFill() const { return sMap_Fill; }
+
+ mork_size MapKeySize() const { return sMap_KeySize; }
+ mork_size MapValSize() const { return sMap_ValSize; }
+
+ mork_bool MapKeyIsIP() const { return sMap_KeyIsIP; }
+ mork_bool MapValIsIP() const { return sMap_ValIsIP; }
+
+protected: // slots
+ // { begin morkProbeMap slots
+
+ mork_fill sProbeMap_MaxFill; // max sMap_Fill before map must grow
+
+ mork_u1 sProbeMap_LazyClearOnAdd; // true if kLazyClearOnAdd
+ mork_bool sProbeMap_ZeroIsClearKey; // zero is adequate to clear keys
+ mork_u1 sProbeMap_Pad[ 2 ]; // for u4 alignment
+
+ mork_u4 sProbeMap_Tag;
+
+ // } end morkProbeMap slots
+
+public: // lazy clear on add
+
+ mork_bool need_lazy_init() const
+ { return sProbeMap_LazyClearOnAdd == morkProbeMap_kLazyClearOnAdd; }
+
+public: // typing
+ mork_bool GoodProbeMap() const
+ { return sProbeMap_Tag == morkProbeMap_kTag; }
+
+protected: // utilities
+
+ void* clear_alloc(morkEnv* ev, mork_size inSize);
+
+ mork_u1* map_new_vals(morkEnv* ev, mork_num inSlots);
+ mork_u1* map_new_keys(morkEnv* ev, mork_num inSlots);
+
+ void clear_probe_map(morkEnv* ev, nsIMdbHeap* ioMapHeap);
+ void init_probe_map(morkEnv* ev, mork_size inSlots);
+ void probe_map_lazy_init(morkEnv* ev);
+
+ mork_bool new_slots(morkEnv* ev, morkMapScratch* old, mork_num inSlots);
+
+ mork_test find_key_pos(morkEnv* ev, const void* inAppKey,
+ mork_u4 inHash, mork_pos* outPos) const;
+
+ void put_probe_kv(morkEnv* ev,
+ const void* inAppKey, const void* inAppVal, mork_pos inPos);
+ void get_probe_kv(morkEnv* ev,
+ void* outAppKey, void* outAppVal, mork_pos inPos) const;
+
+ mork_bool grow_probe_map(morkEnv* ev);
+ void rehash_old_map(morkEnv* ev, morkMapScratch* ioScratch);
+ void revert_map(morkEnv* ev, morkMapScratch* ioScratch);
+
+public: // errors
+ void ProbeMapBadTagError(morkEnv* ev) const;
+ void WrapWithNoVoidSlotError(morkEnv* ev) const;
+ void GrowFailsMaxFillError(morkEnv* ev) const;
+ void MapKeyIsNotIPError(morkEnv* ev) const;
+ void MapValIsNotIPError(morkEnv* ev) const;
+
+ void MapNilKeysError(morkEnv* ev);
+ void MapZeroKeySizeError(morkEnv* ev);
+
+ void MapSeedOutOfSyncError(morkEnv* ev);
+ void MapFillUnderflowWarning(morkEnv* ev);
+
+ static void ProbeMapCutError(morkEnv* ev);
+
+ // { ===== begin morkMap methods =====
+public:
+
+ virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+ MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const;
+ // Note inMapKey is always a key already stored in the map, while inAppKey
+ // is always a method argument parameter from a client method call.
+ // This matters the most in morkProbeMap subclasses, which have the
+ // responsibility of putting 'app' keys into slots for 'map' keys, and
+ // the bit pattern representation might be different in such cases.
+ // morkTest_kHit means that inMapKey equals inAppKey (and this had better
+ // also imply that hash(inMapKey) == hash(inAppKey)).
+ // morkTest_kMiss means that inMapKey does NOT equal inAppKey (but this
+ // implies nothing at all about hash(inMapKey) and hash(inAppKey)).
+ // morkTest_kVoid means that inMapKey is not a valid key bit pattern,
+ // which means that key slot in the map is not being used. Note that
+ // kVoid is only expected as a return value in morkProbeMap subclasses,
+ // because morkProbeMap must ask whether a key slot is used or not.
+ // morkChainMap however, always knows when a key slot is used, so only
+ // key slots expected to have valid bit patterns will be presented to
+ // the MapTest() methods for morkChainMap subclasses.
+ //
+ // NOTE: it is very important that subclasses correctly return the value
+ // morkTest_kVoid whenever the slot for inMapKey contains a bit pattern
+ // that means the slot is not being used, because this is the only way a
+ // probe map can terminate an unsuccessful search for a key in the map.
+
+ virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ MapHash(morkEnv* ev, const void* inAppKey) const;
+
+ virtual mork_bool
+ MapAtPut(morkEnv* ev, const void* inAppKey, const void* inAppVal,
+ void* outAppKey, void* outAppVal);
+
+ virtual mork_bool
+ MapAt(morkEnv* ev, const void* inAppKey, void* outAppKey, void* outAppVal);
+
+ virtual mork_num
+ MapCutAll(morkEnv* ev);
+ // } ===== end morkMap methods =====
+
+
+ // { ===== begin morkProbeMap methods =====
+public:
+
+ virtual mork_u4
+ ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const;
+ // ProbeMapHashMapKey() does logically the same thing as MapHash(), and
+ // the default implementation actually calls virtual MapHash(). However,
+ // Subclasses must override this method whenever the formats of keys in
+ // the map differ from app keys outside the map, because MapHash() only
+ // works on keys in 'app' format, while ProbeMapHashMapKey() only works
+ // on keys in 'map' format. This method is called in order to rehash all
+ // map keys when a map is grown, and this causes all old map members to
+ // move into new slot locations.
+ //
+ // Note it is absolutely imperative that a hash for a key in 'map' format
+ // be exactly the same the hash of the same key in 'app' format, or else
+ // maps will seem corrupt later when keys in 'app' format cannot be found.
+
+ virtual mork_bool
+ ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+ // ProbeMapIsKeyNil() must say whether the representation of logical 'nil'
+ // is currently found inside the key at ioMapKey, for a key found within
+ // the map. The the map iterator uses this method to find map keys that
+ // are actually being used for valid map associations; otherwise the
+ // iterator cannot determine which map slots actually denote used keys.
+ // The default method version returns true if all the bits equal zero.
+
+ virtual void
+ ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+ // ProbeMapClearKey() must put some representation of logical 'nil' into
+ // every key slot in the map, such that MapTest() will later recognize
+ // that this bit pattern shows each key slot is not actually being used.
+ //
+ // This method is typically called whenever the map is either created or
+ // grown into a larger size, where ioMapKey is a pointer to an array of
+ // inKeyCount keys, where each key is this->MapKeySize() bytes in size.
+ // Note that keys are assumed immediately adjacent with no padding, so
+ // if any alignment requirements must be met, then subclasses should have
+ // already accounted for this when specifying a key size in the map.
+ //
+ // Since this method will be called when a map is being grown in size,
+ // nothing should be assumed about the state slots of the map, since the
+ // ioMapKey array might not yet live in sMap_Keys, and the array length
+ // inKeyCount might not yet live in sMap_Slots. However, the value kept
+ // in sMap_KeySize never changes, so this->MapKeySize() is always correct.
+
+ virtual void
+ ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ void* outMapKey, void* outMapVal); // (key,val) inside map
+ // This method actually puts keys and vals in the map in suitable format.
+ //
+ // ProbeMapPushIn() must copy a caller key and value in 'app' format
+ // into the map slots provided, which are in 'map' format. When the
+ // 'app' and 'map' formats are identical, then this is just a bitwise
+ // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+ // and this is exactly what the default implementation performs. However,
+ // if 'app' and 'map' formats are different, and MapTest() depends on this
+ // difference in format, then subclasses must override this method to do
+ // whatever is necessary to store the input app key in output map format.
+ //
+ // Do NOT write more than this->MapKeySize() bytes of a map key, or more
+ // than this->MapValSize() bytes of a map val, or corruption might ensue.
+ //
+ // The inAppKey and inAppVal parameters are the same ones passed into a
+ // call to MapAtPut(), and the outMapKey and outMapVal parameters are ones
+ // determined by how the map currently positions key inAppKey in the map.
+ //
+ // Note any key or val parameter can be a null pointer, in which case
+ // this method must do nothing with those parameters. In particular, do
+ // no key move at all when either inAppKey or outMapKey is nil, and do
+ // no val move at all when either inAppVal or outMapVal is nil. Note that
+ // outMapVal should always be nil when this->MapValSize() is nil.
+
+ virtual void
+ ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ void* outAppKey, void* outAppVal) const; // (key,val) outside map
+ // This method actually gets keys and vals from the map in suitable format.
+ //
+ // ProbeMapPullOut() must copy a key and val in 'map' format into the
+ // caller key and val slots provided, which are in 'app' format. When the
+ // 'app' and 'map' formats are identical, then this is just a bitwise
+ // copy of this->MapKeySize() key bytes and this->MapValSize() val bytes,
+ // and this is exactly what the default implementation performs. However,
+ // if 'app' and 'map' formats are different, and MapTest() depends on this
+ // difference in format, then subclasses must override this method to do
+ // whatever is necessary to store the input map key in output app format.
+ //
+ // The outAppKey and outAppVal parameters are the same ones passed into a
+ // call to either MapAtPut() or MapAt(), while inMapKey and inMapVal are
+ // determined by how the map currently positions the target key in the map.
+ //
+ // Note any key or val parameter can be a null pointer, in which case
+ // this method must do nothing with those parameters. In particular, do
+ // no key move at all when either inMapKey or outAppKey is nil, and do
+ // no val move at all when either inMapVal or outAppVal is nil. Note that
+ // inMapVal should always be nil when this->MapValSize() is nil.
+
+ // } ===== end morkProbeMap methods =====
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseProbeMap() only if open
+ virtual ~morkProbeMap(); // assert that CloseProbeMap() executed earlier
+
+public: // morkProbeMap construction & destruction
+ morkProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap,
+ mork_size inKeySize, mork_size inValSize,
+ nsIMdbHeap* ioMapHeap, mork_size inSlots,
+ mork_bool inZeroIsClearKey);
+
+ void CloseProbeMap(morkEnv* ev); // called by
+
+public: // dynamic type identification
+ mork_bool IsProbeMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kProbeMap; }
+// } ===== end morkNode methods =====
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakMap(morkMap* me,
+ morkEnv* ev, morkMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongMap(morkMap* me,
+ morkEnv* ev, morkMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+/*============================================================================*/
+/* morkProbeMapIter */
+
+#define morkProbeMapIter_kBeforeIx ((mork_i4) -1) /* before first member */
+#define morkProbeMapIter_kAfterIx ((mork_i4) -2) /* after last member */
+
+class morkProbeMapIter {
+
+protected:
+ morkProbeMap* sProbeMapIter_Map; // nonref
+ mork_num sProbeMapIter_Seed; // iter's cached copy of map's seed
+
+ mork_i4 sProbeMapIter_HereIx;
+
+ mork_change sProbeMapIter_Change; // morkMapIter API simulation dummy
+ mork_u1 sProbeMapIter_Pad[ 3 ]; // for u4 alignment
+
+public:
+ morkProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
+ void CloseMapIter(morkEnv* ev);
+
+ morkProbeMapIter( ); // zero most slots; caller must call InitProbeMapIter()
+
+protected: // protected so subclasses must provide suitable typesafe inlines:
+
+ void InitProbeMapIter(morkEnv* ev, morkProbeMap* ioMap);
+
+ void InitMapIter(morkEnv* ev, morkProbeMap* ioMap) // morkMapIter compatibility
+ { this->InitProbeMapIter(ev, ioMap); }
+
+ mork_bool IterFirst(morkEnv* ev, void* outKey, void* outVal);
+ mork_bool IterNext(morkEnv* ev, void* outKey, void* outVal);
+ mork_bool IterHere(morkEnv* ev, void* outKey, void* outVal);
+
+ // NOTE: the following methods ONLY work for sMap_ValIsIP pointer values.
+ // (Note the implied assumption that zero is never a good value pattern.)
+
+ void* IterFirstVal(morkEnv* ev, void* outKey);
+ // equivalent to { void* v=0; this->IterFirst(ev, outKey, &v); return v; }
+
+ void* IterNextVal(morkEnv* ev, void* outKey);
+ // equivalent to { void* v=0; this->IterNext(ev, outKey, &v); return v; }
+
+ void* IterHereVal(morkEnv* ev, void* outKey);
+ // equivalent to { void* v=0; this->IterHere(ev, outKey, &v); return v; }
+
+ // NOTE: the following methods ONLY work for sMap_KeyIsIP pointer values.
+ // (Note the implied assumption that zero is never a good key pattern.)
+
+ void* IterFirstKey(morkEnv* ev);
+ // equivalent to { void* k=0; this->IterFirst(ev, &k, 0); return k; }
+
+ void* IterNextKey(morkEnv* ev);
+ // equivalent to { void* k=0; this->IterNext(ev, &k, 0); return k; }
+
+ void* IterHereKey(morkEnv* ev);
+ // equivalent to { void* k=0; this->IterHere(ev, &k, 0); return k; }
+
+public: // simulation of the morkMapIter API for morkMap compatibility:
+ mork_change* First(morkEnv* ev, void* outKey, void* outVal);
+ mork_change* Next(morkEnv* ev, void* outKey, void* outVal);
+ mork_change* Here(morkEnv* ev, void* outKey, void* outVal);
+
+ mork_change* CutHere(morkEnv* ev, void* outKey, void* outVal);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKPROBEMAP_ */
diff --git a/db/mork/src/morkQuickSort.cpp b/db/mork/src/morkQuickSort.cpp
new file mode 100644
index 000000000..c0a4c11d0
--- /dev/null
+++ b/db/mork/src/morkQuickSort.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* We need this because Solaris' version of qsort is broken and
+ * causes array bounds reads.
+ */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKQUICKSORT_
+#include "morkQuickSort.h"
+#endif
+
+#if !defined(DEBUG) && (defined(__cplusplus) || defined(__gcc))
+# ifndef INLINE
+# define INLINE inline
+# endif
+#else
+# define INLINE
+#endif
+
+static INLINE mork_u1*
+morkQS_med3(mork_u1 *, mork_u1 *, mork_u1 *, mdbAny_Order, void *);
+
+static INLINE void
+morkQS_swapfunc(mork_u1 *, mork_u1 *, int, int);
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define morkQS_swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ TYPE *pi = (TYPE *) (parmi); \
+ TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define morkQS_SwapInit(a, es) swaptype = (a - (mork_u1 *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static INLINE void
+morkQS_swapfunc(mork_u1* a, mork_u1* b, int n, int swaptype)
+{
+ if(swaptype <= 1)
+ morkQS_swapcode(long, a, b, n)
+ else
+ morkQS_swapcode(mork_u1, a, b, n)
+}
+
+#define morkQS_swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ morkQS_swapfunc(a, b, (int)inSize, swaptype)
+
+#define morkQS_vecswap(a, b, n) if ((n) > 0) morkQS_swapfunc(a, b, (int)n, swaptype)
+
+static INLINE mork_u1 *
+morkQS_med3(mork_u1* a, mork_u1* b, mork_u1* c, mdbAny_Order cmp, void* closure)
+{
+ return (*cmp)(a, b, closure) < 0 ?
+ ((*cmp)(b, c, closure) < 0 ? b : ((*cmp)(a, c, closure) < 0 ? c : a ))
+ :((*cmp)(b, c, closure) > 0 ? b : ((*cmp)(a, c, closure) < 0 ? a : c ));
+}
+
+#define morkQS_MIN(x,y) ((x)<(y)?(x):(y))
+
+void
+morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize,
+ mdbAny_Order inOrder, void* ioClosure)
+{
+ mork_u1* pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype, swap_cnt;
+
+tailCall: morkQS_SwapInit(ioVec, inSize);
+ swap_cnt = 0;
+ if (inCount < 7) {
+ for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize)
+ for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0;
+ pl -= inSize)
+ morkQS_swap(pl, pl - inSize);
+ return;
+ }
+ pm = ioVec + (inCount / 2) * inSize;
+ if (inCount > 7) {
+ pl = ioVec;
+ pn = ioVec + (inCount - 1) * inSize;
+ if (inCount > 40) {
+ d = (inCount / 8) * inSize;
+ pl = morkQS_med3(pl, pl + d, pl + 2 * d, inOrder, ioClosure);
+ pm = morkQS_med3(pm - d, pm, pm + d, inOrder, ioClosure);
+ pn = morkQS_med3(pn - 2 * d, pn - d, pn, inOrder, ioClosure);
+ }
+ pm = morkQS_med3(pl, pm, pn, inOrder, ioClosure);
+ }
+ morkQS_swap(ioVec, pm);
+ pa = pb = ioVec + inSize;
+
+ pc = pd = ioVec + (inCount - 1) * inSize;
+ for (;;) {
+ while (pb <= pc && (r = (*inOrder)(pb, ioVec, ioClosure)) <= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ morkQS_swap(pa, pb);
+ pa += inSize;
+ }
+ pb += inSize;
+ }
+ while (pb <= pc && (r = (*inOrder)(pc, ioVec, ioClosure)) >= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ morkQS_swap(pc, pd);
+ pd -= inSize;
+ }
+ pc -= inSize;
+ }
+ if (pb > pc)
+ break;
+ morkQS_swap(pb, pc);
+ swap_cnt = 1;
+ pb += inSize;
+ pc -= inSize;
+ }
+ if (swap_cnt == 0) { /* Switch to insertion sort */
+ for (pm = ioVec + inSize; pm < ioVec + inCount * inSize; pm += inSize)
+ for (pl = pm; pl > ioVec && (*inOrder)(pl - inSize, pl, ioClosure) > 0;
+ pl -= inSize)
+ morkQS_swap(pl, pl - inSize);
+ return;
+ }
+
+ pn = ioVec + inCount * inSize;
+ r = morkQS_MIN(pa - ioVec, pb - pa);
+ morkQS_vecswap(ioVec, pb - r, r);
+ r = morkQS_MIN(pd - pc, (int)(pn - pd - inSize));
+ morkQS_vecswap(pb, pn - r, r);
+ if ((r = pb - pa) > (int)inSize)
+ morkQuickSort(ioVec, r / inSize, inSize, inOrder, ioClosure);
+ if ((r = pd - pc) > (int)inSize) {
+ /* Iterate rather than recurse to save stack space */
+ ioVec = pn - r;
+ inCount = r / inSize;
+ goto tailCall;
+ }
+/* morkQuickSort(pn - r, r / inSize, inSize, inOrder, ioClosure);*/
+}
+
diff --git a/db/mork/src/morkQuickSort.h b/db/mork/src/morkQuickSort.h
new file mode 100644
index 000000000..a9c2e5546
--- /dev/null
+++ b/db/mork/src/morkQuickSort.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKQUICKSORT_
+#define _MORKQUICKSORT_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+extern void
+morkQuickSort(mork_u1* ioVec, mork_u4 inCount, mork_u4 inSize,
+ mdbAny_Order inOrder, void* ioClosure);
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKQUICKSORT_ */
diff --git a/db/mork/src/morkRow.cpp b/db/mork/src/morkRow.cpp
new file mode 100644
index 000000000..e68148d06
--- /dev/null
+++ b/db/mork/src/morkRow.cpp
@@ -0,0 +1,939 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+// notifications regarding row changes:
+
+void morkRow::NoteRowAddCol(morkEnv* ev, mork_column inColumn)
+{
+ if ( !this->IsRowRewrite() )
+ {
+ mork_delta newDelta;
+ morkDelta_Init(newDelta, inColumn, morkChange_kAdd);
+
+ if ( newDelta != mRow_Delta ) // not repeating existing data?
+ {
+ if ( this->HasRowDelta() ) // already have one change recorded?
+ this->SetRowRewrite(); // just plan to write all row cells
+ else
+ this->SetRowDelta(inColumn, morkChange_kAdd);
+ }
+ }
+ else
+ this->ClearRowDelta();
+}
+
+void morkRow::NoteRowCutCol(morkEnv* ev, mork_column inColumn)
+{
+ if ( !this->IsRowRewrite() )
+ {
+ mork_delta newDelta;
+ morkDelta_Init(newDelta, inColumn, morkChange_kCut);
+
+ if ( newDelta != mRow_Delta ) // not repeating existing data?
+ {
+ if ( this->HasRowDelta() ) // already have one change recorded?
+ this->SetRowRewrite(); // just plan to write all row cells
+ else
+ this->SetRowDelta(inColumn, morkChange_kCut);
+ }
+ }
+ else
+ this->ClearRowDelta();
+}
+
+void morkRow::NoteRowSetCol(morkEnv* ev, mork_column inColumn)
+{
+ if ( !this->IsRowRewrite() )
+ {
+ if ( this->HasRowDelta() ) // already have one change recorded?
+ this->SetRowRewrite(); // just plan to write all row cells
+ else
+ this->SetRowDelta(inColumn, morkChange_kSet);
+ }
+ else
+ this->ClearRowDelta();
+}
+
+void morkRow::NoteRowSetAll(morkEnv* ev)
+{
+ this->SetRowRewrite(); // just plan to write all row cells
+ this->ClearRowDelta();
+}
+
+mork_u2
+morkRow::AddRowGcUse(morkEnv* ev)
+{
+ if ( this->IsRow() )
+ {
+ if ( mRow_GcUses < morkRow_kMaxGcUses ) // not already maxed out?
+ ++mRow_GcUses;
+ }
+ else
+ this->NonRowTypeError(ev);
+
+ return mRow_GcUses;
+}
+
+mork_u2
+morkRow::CutRowGcUse(morkEnv* ev)
+{
+ if ( this->IsRow() )
+ {
+ if ( mRow_GcUses ) // any outstanding uses to cut?
+ {
+ if ( mRow_GcUses < morkRow_kMaxGcUses ) // not frozen at max?
+ --mRow_GcUses;
+ }
+ else
+ this->GcUsesUnderflowWarning(ev);
+ }
+ else
+ this->NonRowTypeError(ev);
+
+ return mRow_GcUses;
+}
+
+/*static*/ void
+morkRow::GcUsesUnderflowWarning(morkEnv* ev)
+{
+ ev->NewWarning("mRow_GcUses underflow");
+}
+
+
+/*static*/ void
+morkRow::NonRowTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkRow");
+}
+
+/*static*/ void
+morkRow::NonRowTypeWarning(morkEnv* ev)
+{
+ ev->NewWarning("non morkRow");
+}
+
+/*static*/ void
+morkRow::LengthBeyondMaxError(morkEnv* ev)
+{
+ ev->NewError("mRow_Length over max");
+}
+
+/*static*/ void
+morkRow::ZeroColumnError(morkEnv* ev)
+{
+ ev->NewError(" zero mork_column");
+}
+
+/*static*/ void
+morkRow::NilCellsError(morkEnv* ev)
+{
+ ev->NewError("nil mRow_Cells");
+}
+
+void
+morkRow::InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace,
+ mork_size inLength, morkPool* ioPool)
+ // if inLength is nonzero, cells will be allocated from ioPool
+{
+ if ( ioSpace && ioPool && inOid )
+ {
+ if ( inLength <= morkRow_kMaxLength )
+ {
+ if ( inOid->mOid_Id != morkRow_kMinusOneRid )
+ {
+ mRow_Space = ioSpace;
+ mRow_Object = 0;
+ mRow_Cells = 0;
+ mRow_Oid = *inOid;
+
+ mRow_Length = (mork_u2) inLength;
+ mRow_Seed = (mork_u2) (mork_ip) this; // "random" assignment
+
+ mRow_GcUses = 0;
+ mRow_Pad = 0;
+ mRow_Flags = 0;
+ mRow_Tag = morkRow_kTag;
+
+ morkZone* zone = &ioSpace->mSpace_Store->mStore_Zone;
+
+ if ( inLength )
+ mRow_Cells = ioPool->NewCells(ev, inLength, zone);
+
+ if ( this->MaybeDirtySpaceStoreAndRow() ) // new row might dirty store
+ {
+ this->SetRowRewrite();
+ this->NoteRowSetAll(ev);
+ }
+ }
+ else
+ ioSpace->MinusOneRidError(ev);
+ }
+ else
+ this->LengthBeyondMaxError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+morkRowObject*
+morkRow::AcquireRowObject(morkEnv* ev, morkStore* ioStore)
+{
+ morkRowObject* ro = mRow_Object;
+ if ( ro ) // need new row object?
+ ro->AddRef();
+ else
+ {
+ nsIMdbHeap* heap = ioStore->mPort_Heap;
+ ro = new(*heap, ev)
+ morkRowObject(ev, morkUsage::kHeap, heap, this, ioStore);
+ if ( !ro )
+ return (morkRowObject*) 0;
+
+ morkRowObject::SlotWeakRowObject(ro, ev, &mRow_Object);
+ ro->AddRef();
+ }
+ return ro;
+}
+
+nsIMdbRow*
+morkRow::AcquireRowHandle(morkEnv* ev, morkStore* ioStore)
+{
+ return AcquireRowObject(ev, ioStore);
+}
+
+nsIMdbCell*
+morkRow::AcquireCellHandle(morkEnv* ev, morkCell* ioCell,
+ mdb_column inCol, mork_pos inPos)
+{
+ nsIMdbHeap* heap = ev->mEnv_Heap;
+ morkCellObject* cellObj = new(*heap, ev)
+ morkCellObject(ev, morkUsage::kHeap, heap, this, ioCell, inCol, inPos);
+ if ( cellObj )
+ {
+ nsIMdbCell* cellHandle = cellObj->AcquireCellHandle(ev);
+// cellObj->CutStrongRef(ev->AsMdbEnv());
+ return cellHandle;
+ }
+ return (nsIMdbCell*) 0;
+}
+
+mork_count
+morkRow::CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill)
+ // Count cells in ioVector that change existing cells in this row when
+ // ioVector is added to the row (as in TakeCells()). This is the set
+ // of cells with the same columns in ioVector and mRow_Cells, which do
+ // not have exactly the same value in mCell_Atom, and which do not both
+ // have change status equal to morkChange_kCut (because cutting a cut
+ // cell still yields a cell that has been cut). CountOverlap() also
+ // modifies the change attribute of any cell in ioVector to kDup when
+ // the change was previously kCut and the same column cell was found
+ // in this row with change also equal to kCut; this tells callers later
+ // they need not look for that cell in the row again on a second pass.
+{
+ mork_count outCount = 0;
+ mork_pos pos = 0; // needed by GetCell()
+ morkCell* cells = ioVector;
+ morkCell* end = cells + inFill;
+ --cells; // prepare for preincrement
+ while ( ++cells < end && ev->Good() )
+ {
+ mork_column col = cells->GetColumn();
+
+ morkCell* old = this->GetCell(ev, col, &pos);
+ if ( old ) // same column?
+ {
+ mork_change newChg = cells->GetChange();
+ mork_change oldChg = old->GetChange();
+ if ( newChg != morkChange_kCut || oldChg != newChg ) // not cut+cut?
+ {
+ if ( cells->mCell_Atom != old->mCell_Atom ) // not same atom?
+ ++outCount; // cells will replace old significantly when added
+ }
+ else
+ cells->SetColumnAndChange(col, morkChange_kDup); // note dup status
+ }
+ }
+ return outCount;
+}
+
+void
+morkRow::MergeCells(morkEnv* ev, morkCell* ioVector,
+ mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap)
+ // MergeCells() is the part of TakeCells() that does the insertion.
+ // inOldRowFill is the old value of mRow_Length, and inOverlap is the
+ // number of cells in the intersection that must be updated.
+{
+ morkCell* newCells = mRow_Cells + inOldRowFill; // 1st new cell in row
+ morkCell* newEnd = newCells + mRow_Length; // one past last cell
+
+ morkCell* srcCells = ioVector;
+ morkCell* srcEnd = srcCells + inVecLength;
+
+ --srcCells; // prepare for preincrement
+ while ( ++srcCells < srcEnd && ev->Good() )
+ {
+ mork_change srcChg = srcCells->GetChange();
+ if ( srcChg != morkChange_kDup ) // anything to be done?
+ {
+ morkCell* dstCell = 0;
+ if ( inOverlap )
+ {
+ mork_pos pos = 0; // needed by GetCell()
+ dstCell = this->GetCell(ev, srcCells->GetColumn(), &pos);
+ }
+ if ( dstCell )
+ {
+ --inOverlap; // one fewer intersections to resolve
+ // swap the atoms in the cells to avoid ref counting here:
+ morkAtom* dstAtom = dstCell->mCell_Atom;
+ *dstCell = *srcCells; // bitwise copy, taking src atom
+ srcCells->mCell_Atom = dstAtom; // forget cell ref, if any
+ }
+ else if ( newCells < newEnd ) // another new cell exists?
+ {
+ dstCell = newCells++; // alloc another new cell
+ // take atom from source cell, transferring ref to this row:
+ *dstCell = *srcCells; // bitwise copy, taking src atom
+ srcCells->mCell_Atom = 0; // forget cell ref, if any
+ }
+ else // oops, we ran out...
+ ev->NewError("out of new cells");
+ }
+ }
+}
+
+void
+morkRow::TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
+ morkStore* ioStore)
+{
+ if ( ioVector && inVecLength && ev->Good() )
+ {
+ ++mRow_Seed; // intend to change structure of mRow_Cells
+ mork_size length = (mork_size) mRow_Length;
+
+ mork_count overlap = this->CountOverlap(ev, ioVector, inVecLength);
+
+ mork_size growth = inVecLength - overlap; // cells to add
+ mork_size newLength = length + growth;
+
+ if ( growth && ev->Good() ) // need to add any cells?
+ {
+ morkZone* zone = &ioStore->mStore_Zone;
+ morkPool* pool = ioStore->StorePool();
+ if ( !pool->AddRowCells(ev, this, length + growth, zone) )
+ ev->NewError("cannot take cells");
+ }
+ if ( ev->Good() )
+ {
+ if ( mRow_Length >= newLength )
+ this->MergeCells(ev, ioVector, inVecLength, length, overlap);
+ else
+ ev->NewError("not enough new cells");
+ }
+ }
+}
+
+mork_bool morkRow::MaybeDirtySpaceStoreAndRow()
+{
+ morkRowSpace* rowSpace = mRow_Space;
+ if ( rowSpace )
+ {
+ morkStore* store = rowSpace->mSpace_Store;
+ if ( store && store->mStore_CanDirty )
+ {
+ store->SetStoreDirty();
+ rowSpace->mSpace_CanDirty = morkBool_kTrue;
+ }
+
+ if ( rowSpace->mSpace_CanDirty )
+ {
+ this->SetRowDirty();
+ rowSpace->SetRowSpaceDirty();
+ return morkBool_kTrue;
+ }
+ }
+ return morkBool_kFalse;
+}
+
+morkCell*
+morkRow::NewCell(morkEnv* ev, mdb_column inColumn,
+ mork_pos* outPos, morkStore* ioStore)
+{
+ ++mRow_Seed; // intend to change structure of mRow_Cells
+ mork_size length = (mork_size) mRow_Length;
+ *outPos = (mork_pos) length;
+ morkPool* pool = ioStore->StorePool();
+ morkZone* zone = &ioStore->mStore_Zone;
+
+ mork_bool canDirty = this->MaybeDirtySpaceStoreAndRow();
+
+ if ( pool->AddRowCells(ev, this, length + 1, zone) )
+ {
+ morkCell* cell = mRow_Cells + length;
+ // next line equivalent to inline morkCell::SetCellDirty():
+ if ( canDirty )
+ cell->SetCellColumnDirty(inColumn);
+ else
+ cell->SetCellColumnClean(inColumn);
+
+ if ( canDirty && !this->IsRowRewrite() )
+ this->NoteRowAddCol(ev, inColumn);
+
+ return cell;
+ }
+
+ return (morkCell*) 0;
+}
+
+
+
+void morkRow::SeekColumn(morkEnv* ev, mdb_pos inPos,
+ mdb_column* outColumn, mdbYarn* outYarn)
+{
+ morkCell* cells = mRow_Cells;
+ if ( cells && inPos < mRow_Length && inPos >= 0 )
+ {
+ morkCell* c = cells + inPos;
+ if ( outColumn ) {
+ *outColumn = c->GetColumn();
+ }
+ if ( outYarn ) {
+ morkAtom::GetYarn(c->mCell_Atom, outYarn);
+ }
+ }
+ else
+ {
+ if ( outColumn ) {
+ *outColumn = 0;
+ }
+ if ( outYarn ) {
+ morkAtom::GetYarn((morkAtom*)0, outYarn);
+ }
+ }
+}
+
+void
+morkRow::NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn)
+{
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ mork_column last = 0;
+ mork_column inCol = *ioColumn;
+ morkCell* end = cells + mRow_Length;
+ while ( cells < end )
+ {
+ if ( inCol == last ) // found column?
+ {
+ if ( outYarn ) {
+ if (outYarn) {
+ morkAtom::GetYarn(cells->mCell_Atom, outYarn);
+ }
+ *ioColumn = cells->GetColumn();
+ return; // stop, we are done
+ }
+ } else {
+ last = cells->GetColumn();
+ ++cells;
+ }
+ }
+ }
+ *ioColumn = 0;
+ if ( outYarn ) {
+ morkAtom::GetYarn((morkAtom*)0, outYarn);
+ }
+}
+
+morkCell*
+morkRow::CellAt(morkEnv* ev, mork_pos inPos) const
+{
+ MORK_USED_1(ev);
+ morkCell* cells = mRow_Cells;
+ if ( cells && inPos < mRow_Length && inPos >= 0 )
+ {
+ return cells + inPos;
+ }
+ return (morkCell*) 0;
+}
+
+morkCell*
+morkRow::GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const
+{
+ MORK_USED_1(ev);
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ morkCell* end = cells + mRow_Length;
+ while ( cells < end )
+ {
+ mork_column col = cells->GetColumn();
+ if ( col == inColumn ) // found the desired column?
+ {
+ *outPos = cells - mRow_Cells;
+ return cells;
+ }
+ else
+ ++cells;
+ }
+ }
+ *outPos = -1;
+ return (morkCell*) 0;
+}
+
+mork_aid
+morkRow::GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const
+ // GetCellAtomAid() finds the cell with column inColumn, and sees if the
+ // atom has a token ID, and returns the atom's ID if there is one. Or
+ // else zero is returned if there is no such column, or no atom, or if
+ // the atom has no ID to return. This method is intended to support
+ // efficient updating of column indexes for rows in a row space.
+{
+ if (this->IsRow())
+ {
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ morkCell* end = cells + mRow_Length;
+ while ( cells < end )
+ {
+ mork_column col = cells->GetColumn();
+ if ( col == inColumn ) // found desired column?
+ {
+ morkAtom* atom = cells->mCell_Atom;
+ if ( atom && atom->IsBook() )
+ return ((morkBookAtom*) atom)->mBookAtom_Id;
+ else
+ return 0;
+ }
+ else
+ ++cells;
+ }
+ }
+ }
+ else
+ this->NonRowTypeError(ev);
+
+ return 0;
+}
+
+void
+morkRow::EmptyAllCells(morkEnv* ev)
+{
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ morkStore* store = this->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ if ( this->MaybeDirtySpaceStoreAndRow() )
+ {
+ this->SetRowRewrite();
+ this->NoteRowSetAll(ev);
+ }
+ morkPool* pool = store->StorePool();
+ morkCell* end = cells + mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end )
+ {
+ if ( cells->mCell_Atom )
+ cells->SetAtom(ev, (morkAtom*) 0, pool);
+ }
+ }
+ }
+}
+
+void
+morkRow::cut_all_index_entries(morkEnv* ev)
+{
+ morkRowSpace* rowSpace = mRow_Space;
+ if ( rowSpace->mRowSpace_IndexCount ) // any indexes?
+ {
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ morkCell* end = cells + mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end )
+ {
+ morkAtom* atom = cells->mCell_Atom;
+ if ( atom )
+ {
+ mork_aid atomAid = atom->GetBookAtomAid();
+ if ( atomAid )
+ {
+ mork_column col = cells->GetColumn();
+ morkAtomRowMap* map = rowSpace->FindMap(ev, col);
+ if ( map ) // cut row from index for this column?
+ map->CutAid(ev, atomAid);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+morkRow::CutAllColumns(morkEnv* ev)
+{
+ morkStore* store = this->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ if ( this->MaybeDirtySpaceStoreAndRow() )
+ {
+ this->SetRowRewrite();
+ this->NoteRowSetAll(ev);
+ }
+ morkRowSpace* rowSpace = mRow_Space;
+ if ( rowSpace->mRowSpace_IndexCount ) // any indexes?
+ this->cut_all_index_entries(ev);
+
+ morkPool* pool = store->StorePool();
+ pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone);
+ }
+}
+
+void
+morkRow::SetRow(morkEnv* ev, const morkRow* inSourceRow)
+{
+ // note inSourceRow might be in another DB, with a different store...
+ morkStore* store = this->GetRowSpaceStore(ev);
+ morkStore* srcStore = inSourceRow->GetRowSpaceStore(ev);
+ if ( store && srcStore )
+ {
+ if ( this->MaybeDirtySpaceStoreAndRow() )
+ {
+ this->SetRowRewrite();
+ this->NoteRowSetAll(ev);
+ }
+ morkRowSpace* rowSpace = mRow_Space;
+ mork_count indexes = rowSpace->mRowSpace_IndexCount; // any indexes?
+
+ mork_bool sameStore = ( store == srcStore ); // identical stores?
+ morkPool* pool = store->StorePool();
+ if ( pool->CutRowCells(ev, this, /*newSize*/ 0, &store->mStore_Zone) )
+ {
+ mork_fill fill = inSourceRow->mRow_Length;
+ if ( pool->AddRowCells(ev, this, fill, &store->mStore_Zone) )
+ {
+ morkCell* dst = mRow_Cells;
+ morkCell* dstEnd = dst + mRow_Length;
+
+ const morkCell* src = inSourceRow->mRow_Cells;
+ const morkCell* srcEnd = src + fill;
+ --dst; --src; // prepare both for preincrement:
+
+ while ( ++dst < dstEnd && ++src < srcEnd && ev->Good() )
+ {
+ morkAtom* atom = src->mCell_Atom;
+ mork_column dstCol = src->GetColumn();
+ // Note we modify the mCell_Atom slot directly instead of using
+ // morkCell::SetAtom(), because we know it starts equal to nil.
+
+ if ( sameStore ) // source and dest in same store?
+ {
+ // next line equivalent to inline morkCell::SetCellDirty():
+ dst->SetCellColumnDirty(dstCol);
+ dst->mCell_Atom = atom;
+ if ( atom ) // another ref to non-nil atom?
+ atom->AddCellUse(ev);
+ }
+ else // need to dup items from src store in a dest store
+ {
+ dstCol = store->CopyToken(ev, dstCol, srcStore);
+ if ( dstCol )
+ {
+ // next line equivalent to inline morkCell::SetCellDirty():
+ dst->SetCellColumnDirty(dstCol);
+ atom = store->CopyAtom(ev, atom);
+ dst->mCell_Atom = atom;
+ if ( atom ) // another ref?
+ atom->AddCellUse(ev);
+ }
+ }
+ if ( indexes && atom )
+ {
+ mork_aid atomAid = atom->GetBookAtomAid();
+ if ( atomAid )
+ {
+ morkAtomRowMap* map = rowSpace->FindMap(ev, dstCol);
+ if ( map )
+ map->AddAid(ev, atomAid, this);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+morkRow::AddRow(morkEnv* ev, const morkRow* inSourceRow)
+{
+ if ( mRow_Length ) // any existing cells we might need to keep?
+ {
+ ev->StubMethodOnlyError();
+ }
+ else
+ this->SetRow(ev, inSourceRow); // just exactly duplicate inSourceRow
+}
+
+void
+morkRow::OnZeroRowGcUse(morkEnv* ev)
+// OnZeroRowGcUse() is called when CutRowGcUse() returns zero.
+{
+ MORK_USED_1(ev);
+ // ev->NewWarning("need to implement OnZeroRowGcUse");
+}
+
+void
+morkRow::DirtyAllRowContent(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+
+ if ( this->MaybeDirtySpaceStoreAndRow() )
+ {
+ this->SetRowRewrite();
+ this->NoteRowSetAll(ev);
+ }
+ morkCell* cells = mRow_Cells;
+ if ( cells )
+ {
+ morkCell* end = cells + mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end )
+ {
+ cells->SetCellDirty();
+ }
+ }
+}
+
+morkStore*
+morkRow::GetRowSpaceStore(morkEnv* ev) const
+{
+ morkRowSpace* rowSpace = mRow_Space;
+ if ( rowSpace )
+ {
+ morkStore* store = rowSpace->mSpace_Store;
+ if ( store )
+ {
+ if ( store->IsStore() )
+ {
+ return store;
+ }
+ else
+ store->NonStoreTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ else
+ ev->NilPointerError();
+
+ return (morkStore*) 0;
+}
+
+void morkRow::CutColumn(morkEnv* ev, mdb_column inColumn)
+{
+ mork_pos pos = -1;
+ morkCell* cell = this->GetCell(ev, inColumn, &pos);
+ if ( cell )
+ {
+ morkStore* store = this->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() )
+ this->NoteRowCutCol(ev, inColumn);
+
+ morkRowSpace* rowSpace = mRow_Space;
+ morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )?
+ rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0;
+ if ( map ) // this row attribute is indexed by row space?
+ {
+ morkAtom* oldAtom = cell->mCell_Atom;
+ if ( oldAtom ) // need to cut an entry from the index?
+ {
+ mork_aid oldAid = oldAtom->GetBookAtomAid();
+ if ( oldAid ) // cut old row attribute from row index in space?
+ map->CutAid(ev, oldAid);
+ }
+ }
+
+ morkPool* pool = store->StorePool();
+ cell->SetAtom(ev, (morkAtom*) 0, pool);
+
+ mork_fill fill = mRow_Length; // should not be zero
+ MORK_ASSERT(fill);
+ if ( fill ) // index < fill for last cell exists?
+ {
+ mork_fill last = fill - 1; // index of last cell in row
+
+ if ( pos < (mork_pos)last ) // need to move cells following cut cell?
+ {
+ morkCell* lastCell = mRow_Cells + last;
+ mork_count after = last - pos; // cell count after cut cell
+ morkCell* next = cell + 1; // next cell after cut cell
+ MORK_MEMMOVE(cell, next, after * sizeof(morkCell));
+ lastCell->SetColumnAndChange(0, 0);
+ lastCell->mCell_Atom = 0;
+ }
+
+ if ( ev->Good() )
+ pool->CutRowCells(ev, this, fill - 1, &store->mStore_Zone);
+ }
+ }
+ }
+}
+
+morkAtom* morkRow::GetColumnAtom(morkEnv* ev, mdb_column inColumn)
+{
+ if ( ev->Good() )
+ {
+ mork_pos pos = -1;
+ morkCell* cell = this->GetCell(ev, inColumn, &pos);
+ if ( cell )
+ return cell->mCell_Atom;
+ }
+ return (morkAtom*) 0;
+}
+
+void morkRow::AddColumn(morkEnv* ev, mdb_column inColumn,
+ const mdbYarn* inYarn, morkStore* ioStore)
+{
+ if ( ev->Good() )
+ {
+ mork_pos pos = -1;
+ morkCell* cell = this->GetCell(ev, inColumn, &pos);
+ morkCell* oldCell = cell; // need to know later whether new
+ if ( !cell ) // column does not yet exist?
+ cell = this->NewCell(ev, inColumn, &pos, ioStore);
+
+ if ( cell )
+ {
+ morkAtom* oldAtom = cell->mCell_Atom;
+
+ morkAtom* atom = ioStore->YarnToAtom(ev, inYarn, true /* create */);
+ if ( atom && atom != oldAtom )
+ {
+ morkRowSpace* rowSpace = mRow_Space;
+ morkAtomRowMap* map = ( rowSpace->mRowSpace_IndexCount )?
+ rowSpace->FindMap(ev, inColumn) : (morkAtomRowMap*) 0;
+
+ if ( map ) // inColumn is indexed by row space?
+ {
+ if ( oldAtom && oldAtom != atom ) // cut old cell from index?
+ {
+ mork_aid oldAid = oldAtom->GetBookAtomAid();
+ if ( oldAid ) // cut old row attribute from row index in space?
+ map->CutAid(ev, oldAid);
+ }
+ }
+
+ cell->SetAtom(ev, atom, ioStore->StorePool()); // refcounts atom
+
+ if ( oldCell ) // we changed a pre-existing cell in the row?
+ {
+ ++mRow_Seed;
+ if ( this->MaybeDirtySpaceStoreAndRow() && !this->IsRowRewrite() )
+ this->NoteRowAddCol(ev, inColumn);
+ }
+
+ if ( map ) // inColumn is indexed by row space?
+ {
+ mork_aid newAid = atom->GetBookAtomAid();
+ if ( newAid ) // add new row attribute to row index in space?
+ map->AddAid(ev, newAid, this);
+ }
+ }
+ }
+ }
+}
+
+morkRowCellCursor*
+morkRow::NewRowCellCursor(morkEnv* ev, mdb_pos inPos)
+{
+ morkRowCellCursor* outCursor = 0;
+ if ( ev->Good() )
+ {
+ morkStore* store = this->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ morkRowObject* rowObj = this->AcquireRowObject(ev, store);
+ if ( rowObj )
+ {
+ nsIMdbHeap* heap = store->mPort_Heap;
+ morkRowCellCursor* cursor = new(*heap, ev)
+ morkRowCellCursor(ev, morkUsage::kHeap, heap, rowObj);
+
+ if ( cursor )
+ {
+ if ( ev->Good() )
+ {
+ cursor->mRowCellCursor_Col = inPos;
+ outCursor = cursor;
+ }
+ else
+ cursor->CutStrongRef(ev->mEnv_SelfAsMdbEnv);
+ }
+ rowObj->Release(); // always cut ref (cursor has its own)
+ }
+ }
+ }
+ return outCursor;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkRow.h b/db/mork/src/morkRow.h
new file mode 100644
index 000000000..d33c707e5
--- /dev/null
+++ b/db/mork/src/morkRow.h
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKROW_
+#define _MORKROW_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbRow;
+class nsIMdbCell;
+#define morkDerived_kRow /*i*/ 0x5277 /* ascii 'Rw' */
+
+#define morkRow_kMaxGcUses 0x0FF /* max for 8-bit unsigned int */
+#define morkRow_kMaxLength 0x0FFFF /* max for 16-bit unsigned int */
+#define morkRow_kMinusOneRid ((mork_rid) -1)
+
+#define morkRow_kTag 'r' /* magic signature for mRow_Tag */
+
+#define morkRow_kNotedBit ((mork_u1) (1 << 0)) /* space has change notes */
+#define morkRow_kRewriteBit ((mork_u1) (1 << 1)) /* must rewrite all cells */
+#define morkRow_kDirtyBit ((mork_u1) (1 << 2)) /* row has been changed */
+
+class morkRow{ // row of cells
+
+public: // state is public because the entire Mork system is private
+
+ morkRowSpace* mRow_Space; // mRow_Space->SpaceScope() is the row scope
+ morkRowObject* mRow_Object; // refcount & other state for object sharing
+ morkCell* mRow_Cells;
+ mdbOid mRow_Oid;
+
+ mork_delta mRow_Delta; // space to note a single column change
+
+ mork_u2 mRow_Length; // physical count of cells in mRow_Cells
+ mork_u2 mRow_Seed; // count changes in mRow_Cells structure
+
+ mork_u1 mRow_GcUses; // persistent references from tables
+ mork_u1 mRow_Pad; // for u1 alignment
+ mork_u1 mRow_Flags; // one-byte flags slot
+ mork_u1 mRow_Tag; // one-byte tag (need u4 alignment pad)
+
+public: // interpreting mRow_Delta
+
+ mork_bool HasRowDelta() const { return ( mRow_Delta != 0 ); }
+
+ void ClearRowDelta() { mRow_Delta = 0; }
+
+ void SetRowDelta(mork_column inCol, mork_change inChange)
+ { morkDelta_Init(mRow_Delta, inCol, inChange); }
+
+ mork_column GetDeltaColumn() const { return morkDelta_Column(mRow_Delta); }
+ mork_change GetDeltaChange() const { return morkDelta_Change(mRow_Delta); }
+
+public: // noting row changes
+
+ void NoteRowSetAll(morkEnv* ev);
+ void NoteRowSetCol(morkEnv* ev, mork_column inCol);
+ void NoteRowAddCol(morkEnv* ev, mork_column inCol);
+ void NoteRowCutCol(morkEnv* ev, mork_column inCol);
+
+public: // flags bit twiddling
+
+ void SetRowNoted() { mRow_Flags |= morkRow_kNotedBit; }
+ void SetRowRewrite() { mRow_Flags |= morkRow_kRewriteBit; }
+ void SetRowDirty() { mRow_Flags |= morkRow_kDirtyBit; }
+
+ void ClearRowNoted() { mRow_Flags &= (mork_u1) ~morkRow_kNotedBit; }
+ void ClearRowRewrite() { mRow_Flags &= (mork_u1) ~morkRow_kRewriteBit; }
+ void SetRowClean() { mRow_Flags = 0; mRow_Delta = 0; }
+
+ mork_bool IsRowNoted() const
+ { return ( mRow_Flags & morkRow_kNotedBit ) != 0; }
+
+ mork_bool IsRowRewrite() const
+ { return ( mRow_Flags & morkRow_kRewriteBit ) != 0; }
+
+ mork_bool IsRowClean() const
+ { return ( mRow_Flags & morkRow_kDirtyBit ) == 0; }
+
+ mork_bool IsRowDirty() const
+ { return ( mRow_Flags & morkRow_kDirtyBit ) != 0; }
+
+ mork_bool IsRowUsed() const
+ { return mRow_GcUses != 0; }
+
+public: // other row methods
+ morkRow( ) { }
+ explicit morkRow(const mdbOid* inOid) :mRow_Oid(*inOid) { }
+ void InitRow(morkEnv* ev, const mdbOid* inOid, morkRowSpace* ioSpace,
+ mork_size inLength, morkPool* ioPool);
+ // if inLength is nonzero, cells will be allocated from ioPool
+
+ morkRowObject* AcquireRowObject(morkEnv* ev, morkStore* ioStore);
+ nsIMdbRow* AcquireRowHandle(morkEnv* ev, morkStore* ioStore);
+ nsIMdbCell* AcquireCellHandle(morkEnv* ev, morkCell* ioCell,
+ mdb_column inColumn, mork_pos inPos);
+
+ mork_u2 AddRowGcUse(morkEnv* ev);
+ mork_u2 CutRowGcUse(morkEnv* ev);
+
+
+ mork_bool MaybeDirtySpaceStoreAndRow();
+
+public: // internal row methods
+
+ void cut_all_index_entries(morkEnv* ev);
+
+ // void cut_cell_from_space_index(morkEnv* ev, morkCell* ioCell);
+
+ mork_count CountOverlap(morkEnv* ev, morkCell* ioVector, mork_fill inFill);
+ // Count cells in ioVector that change existing cells in this row when
+ // ioVector is added to the row (as in TakeCells()). This is the set
+ // of cells with the same columns in ioVector and mRow_Cells, which do
+ // not have exactly the same value in mCell_Atom, and which do not both
+ // have change status equal to morkChange_kCut (because cutting a cut
+ // cell still yields a cell that has been cut). CountOverlap() also
+ // modifies the change attribute of any cell in ioVector to kDup when
+ // the change was previously kCut and the same column cell was found
+ // in this row with change also equal to kCut; this tells callers later
+ // they need not look for that cell in the row again on a second pass.
+
+ void MergeCells(morkEnv* ev, morkCell* ioVector,
+ mork_fill inVecLength, mork_fill inOldRowFill, mork_fill inOverlap);
+ // MergeCells() is the part of TakeCells() that does the insertion.
+ // inOldRowFill is the old value of mRow_Length, and inOverlap is the
+ // number of cells in the intersection that must be updated.
+
+ void TakeCells(morkEnv* ev, morkCell* ioVector, mork_fill inVecLength,
+ morkStore* ioStore);
+
+ morkCell* NewCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos,
+ morkStore* ioStore);
+ morkCell* GetCell(morkEnv* ev, mdb_column inColumn, mork_pos* outPos) const;
+ morkCell* CellAt(morkEnv* ev, mork_pos inPos) const;
+
+ mork_aid GetCellAtomAid(morkEnv* ev, mdb_column inColumn) const;
+ // GetCellAtomAid() finds the cell with column inColumn, and sees if the
+ // atom has a token ID, and returns the atom's ID if there is one. Or
+ // else zero is returned if there is no such column, or no atom, or if
+ // the atom has no ID to return. This method is intended to support
+ // efficient updating of column indexes for rows in a row space.
+
+public: // external row methods
+
+ void DirtyAllRowContent(morkEnv* ev);
+
+ morkStore* GetRowSpaceStore(morkEnv* ev) const;
+
+ void AddColumn(morkEnv* ev, mdb_column inColumn,
+ const mdbYarn* inYarn, morkStore* ioStore);
+
+ morkAtom* GetColumnAtom(morkEnv* ev, mdb_column inColumn);
+
+ void NextColumn(morkEnv* ev, mdb_column* ioColumn, mdbYarn* outYarn);
+
+ void SeekColumn(morkEnv* ev, mdb_pos inPos,
+ mdb_column* outColumn, mdbYarn* outYarn);
+
+ void CutColumn(morkEnv* ev, mdb_column inColumn);
+
+ morkRowCellCursor* NewRowCellCursor(morkEnv* ev, mdb_pos inPos);
+
+ void EmptyAllCells(morkEnv* ev);
+ void AddRow(morkEnv* ev, const morkRow* inSourceRow);
+ void SetRow(morkEnv* ev, const morkRow* inSourceRow);
+ void CutAllColumns(morkEnv* ev);
+
+ void OnZeroRowGcUse(morkEnv* ev);
+ // OnZeroRowGcUse() is called when CutRowGcUse() returns zero.
+
+public: // dynamic typing
+
+ mork_bool IsRow() const { return mRow_Tag == morkRow_kTag; }
+
+public: // hash and equal
+
+ mork_u4 HashRow() const
+ {
+ return (mRow_Oid.mOid_Scope << 16) ^ mRow_Oid.mOid_Id;
+ }
+
+ mork_bool EqualRow(const morkRow* ioRow) const
+ {
+ return
+ (
+ ( mRow_Oid.mOid_Scope == ioRow->mRow_Oid.mOid_Scope )
+ && ( mRow_Oid.mOid_Id == ioRow->mRow_Oid.mOid_Id )
+ );
+ }
+
+ mork_bool EqualOid(const mdbOid* ioOid) const
+ {
+ return
+ (
+ ( mRow_Oid.mOid_Scope == ioOid->mOid_Scope )
+ && ( mRow_Oid.mOid_Id == ioOid->mOid_Id )
+ );
+ }
+
+public: // errors
+ static void ZeroColumnError(morkEnv* ev);
+ static void LengthBeyondMaxError(morkEnv* ev);
+ static void NilCellsError(morkEnv* ev);
+ static void NonRowTypeError(morkEnv* ev);
+ static void NonRowTypeWarning(morkEnv* ev);
+ static void GcUsesUnderflowWarning(morkEnv* ev);
+
+private: // copying is not allowed
+ morkRow(const morkRow& other);
+ morkRow& operator=(const morkRow& other);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROW_ */
+
diff --git a/db/mork/src/morkRowCellCursor.cpp b/db/mork/src/morkRowCellCursor.cpp
new file mode 100644
index 000000000..684fd115a
--- /dev/null
+++ b/db/mork/src/morkRowCellCursor.cpp
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowCellCursor::CloseMorkNode(morkEnv* ev) // CloseRowCellCursor() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseRowCellCursor(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkRowCellCursor::~morkRowCellCursor() // CloseRowCellCursor() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowCellCursor::morkRowCellCursor(morkEnv* ev,
+ const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkRowObject* ioRowObject)
+: morkCursor(ev, inUsage, ioHeap)
+, mRowCellCursor_RowObject( 0 )
+, mRowCellCursor_Col( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioRowObject )
+ {
+ morkRow* row = ioRowObject->mRowObject_Row;
+ if ( row )
+ {
+ if ( row->IsRow() )
+ {
+ mCursor_Pos = -1;
+ mCursor_Seed = row->mRow_Seed;
+
+ morkRowObject::SlotStrongRowObject(ioRowObject, ev,
+ &mRowCellCursor_RowObject);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kRowCellCursor;
+ }
+ else
+ row->NonRowTypeError(ev);
+ }
+ else
+ ioRowObject->NilRowError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkRowCellCursor, morkCursor, nsIMdbRowCellCursor)
+
+/*public non-poly*/ void
+morkRowCellCursor::CloseRowCellCursor(morkEnv* ev)
+{
+ if ( this->IsNode() )
+ {
+ mCursor_Pos = -1;
+ mCursor_Seed = 0;
+ morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev,
+ &mRowCellCursor_RowObject);
+ this->CloseCursor(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkRowCellCursor::NilRowObjectError(morkEnv* ev)
+{
+ ev->NewError("nil mRowCellCursor_RowObject");
+}
+
+/*static*/ void
+morkRowCellCursor::NonRowCellCursorTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkRowCellCursor");
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkRowCellCursor::SetRow(nsIMdbEnv* mev, nsIMdbRow* ioRow)
+{
+ nsresult outErr = NS_OK;
+ morkRow* row = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ row = (morkRow *) ioRow;
+ morkStore* store = row->GetRowSpaceStore(ev);
+ if ( store )
+ {
+ morkRowObject* rowObj = row->AcquireRowObject(ev, store);
+ if ( rowObj )
+ {
+ morkRowObject::SlotStrongRowObject((morkRowObject*) 0, ev,
+ &mRowCellCursor_RowObject);
+
+ mRowCellCursor_RowObject = rowObj; // take this strong ref
+ mCursor_Seed = row->mRow_Seed;
+
+ row->GetCell(ev, mRowCellCursor_Col, &mCursor_Pos);
+ }
+ }
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowCellCursor::GetRow(nsIMdbEnv* mev, nsIMdbRow** acqRow)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject* rowObj = mRowCellCursor_RowObject;
+ if ( rowObj )
+ outRow = rowObj->AcquireRowHandle(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cell seeking methods -----
+NS_IMETHODIMP
+morkRowCellCursor::SeekCell(
+ nsIMdbEnv* mev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ nsIMdbCell** acqCell)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end cell seeking methods -----
+
+// { ----- begin cell iteration methods -----
+NS_IMETHODIMP
+morkRowCellCursor::NextCell( // get next cell in the row
+ nsIMdbEnv* mev, // context
+ nsIMdbCell** acqCell, // changes to the next cell in the iteration
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos)
+{
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ mdb_column col = 0;
+ mdb_pos pos = mRowCellCursor_Col;
+ if ( pos < 0 )
+ pos = 0;
+ else
+ ++pos;
+
+ morkCell* cell = mRowCellCursor_RowObject->mRowObject_Row->CellAt(ev, pos);
+ if ( cell )
+ {
+ col = cell->GetColumn();
+ *acqCell = mRowCellCursor_RowObject->mRowObject_Row->AcquireCellHandle(ev, cell, col, pos);
+ }
+ else
+ {
+ *acqCell = nullptr;
+ pos = -1;
+ }
+ if ( outPos )
+ *outPos = pos;
+ if ( outColumn )
+ *outColumn = col;
+
+ mRowCellCursor_Col = pos;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkRowCellCursor::PickNextCell( // get next cell in row within filter set
+ nsIMdbEnv* mev, // context
+ nsIMdbCell* ioCell, // changes to the next cell in the iteration
+ const mdbColumnSet* inFilterSet, // col set of actual caller interest
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos)
+// Note that inFilterSet should not have too many (many more than 10?)
+// cols, since this might imply a potential excessive consumption of time
+// over many cursor calls when looking for column and filter intersection.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end cell iteration methods -----
+
+// } ===== end nsIMdbRowCellCursor methods =====
+
diff --git a/db/mork/src/morkRowCellCursor.h b/db/mork/src/morkRowCellCursor.h
new file mode 100644
index 000000000..ff5c8c67d
--- /dev/null
+++ b/db/mork/src/morkRowCellCursor.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKROWCELLCURSOR_
+#define _MORKROWCELLCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinRowCellCursor;
+#define morkDerived_kRowCellCursor /*i*/ 0x6343 /* ascii 'cC' */
+
+class morkRowCellCursor : public morkCursor, public nsIMdbRowCellCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkFactory* mObject_Factory; // weak ref to suite factory
+
+ // mork_seed mCursor_Seed;
+ // mork_pos mCursor_Pos;
+ // mork_bool mCursor_DoFailOnSeedOutOfSync;
+ // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+ NS_DECL_ISUPPORTS_INHERITED
+ morkRowObject* mRowCellCursor_RowObject; // strong ref to row
+ mork_column mRowCellCursor_Col; // col of cell last at mCursor_Pos
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowCellCursor()
+
+public: // morkRowCellCursor construction & destruction
+ morkRowCellCursor(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkRowObject* ioRowObject);
+ void CloseRowCellCursor(morkEnv* ev); // called by CloseMorkNode();
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD SetRow(nsIMdbEnv* ev, nsIMdbRow* ioRow) override; // sets pos to -1
+ NS_IMETHOD GetRow(nsIMdbEnv* ev, nsIMdbRow** acqRow) override;
+ // } ----- end attribute methods -----
+
+ // { ----- begin cell seeking methods -----
+ NS_IMETHOD SeekCell(
+ nsIMdbEnv* ev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ nsIMdbCell** acqCell) override; // the cell at inPos
+ // } ----- end cell seeking methods -----
+
+ // { ----- begin cell iteration methods -----
+ NS_IMETHOD NextCell( // get next cell in the row
+ nsIMdbEnv* ev, // context
+ nsIMdbCell** acqCell, // changes to the next cell in the iteration
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos) override; // position of cell in row sequence
+
+ NS_IMETHOD PickNextCell( // get next cell in row within filter set
+ nsIMdbEnv* ev, // context
+ nsIMdbCell* ioCell, // changes to the next cell in the iteration
+ const mdbColumnSet* inFilterSet, // col set of actual caller interest
+ mdb_column* outColumn, // column for this particular cell
+ mdb_pos* outPos) override; // position of cell in row sequence
+
+ // Note that inFilterSet should not have too many (many more than 10?)
+ // cols, since this might imply a potential excessive consumption of time
+ // over many cursor calls when looking for column and filter intersection.
+ // } ----- end cell iteration methods -----
+
+
+private: // copying is not allowed
+ morkRowCellCursor(const morkRowCellCursor& other);
+ morkRowCellCursor& operator=(const morkRowCellCursor& other);
+ virtual ~morkRowCellCursor(); // assert that close executed earlier
+
+public: // dynamic type identification
+ mork_bool IsRowCellCursor() const
+ { return IsNode() && mNode_Derived == morkDerived_kRowCellCursor; }
+// } ===== end morkNode methods =====
+
+public: // errors
+ static void NilRowObjectError(morkEnv* ev);
+ static void NonRowCellCursorTypeError(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakRowCellCursor(morkRowCellCursor* me,
+ morkEnv* ev, morkRowCellCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongRowCellCursor(morkRowCellCursor* me,
+ morkEnv* ev, morkRowCellCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWCELLCURSOR_ */
diff --git a/db/mork/src/morkRowMap.cpp b/db/mork/src/morkRowMap.cpp
new file mode 100644
index 000000000..35c8b9202
--- /dev/null
+++ b/db/mork/src/morkRowMap.cpp
@@ -0,0 +1,297 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowMap::CloseMorkNode(morkEnv* ev) // CloseRowMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseRowMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkRowMap::~morkRowMap() // assert CloseRowMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowMap::morkRowMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots)
+: morkMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0,
+ inSlots, ioSlotHeap, /*inHoldChanges*/ morkBool_kFalse)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kRowMap;
+}
+
+/*public non-poly*/ void
+morkRowMap::CloseRowMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CloseMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+
+// { ===== begin morkMap poly interface =====
+/*virtual*/ mork_bool //
+morkRowMap::Equal(morkEnv* ev, const void* inKeyA,
+ const void* inKeyB) const
+{
+ MORK_USED_1(ev);
+ return (*(const morkRow**) inKeyA)->EqualRow(*(const morkRow**) inKeyB);
+}
+
+/*virtual*/ mork_u4 //
+morkRowMap::Hash(morkEnv* ev, const void* inKey) const
+{
+ MORK_USED_1(ev);
+ return (*(const morkRow**) inKey)->HashRow();
+}
+// } ===== end morkMap poly interface =====
+
+
+mork_bool
+morkRowMap::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+ if ( ev->Good() )
+ {
+ this->Put(ev, &ioRow, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0, (mork_change**) 0);
+ }
+ return ev->Good();
+}
+
+morkRow*
+morkRowMap::CutOid(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow row(inOid);
+ morkRow* key = &row;
+ morkRow* oldKey = 0;
+ this->Cut(ev, &key, &oldKey, /*val*/ (void*) 0,
+ (mork_change**) 0);
+
+ return oldKey;
+}
+
+morkRow*
+morkRowMap::CutRow(morkEnv* ev, const morkRow* ioRow)
+{
+ morkRow* oldKey = 0;
+ this->Cut(ev, &ioRow, &oldKey, /*val*/ (void*) 0,
+ (mork_change**) 0);
+
+ return oldKey;
+}
+
+morkRow*
+morkRowMap::GetOid(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow row(inOid);
+ morkRow* key = &row;
+ morkRow* oldKey = 0;
+ this->Get(ev, &key, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+
+ return oldKey;
+}
+
+morkRow*
+morkRowMap::GetRow(morkEnv* ev, const morkRow* ioRow)
+{
+ morkRow* oldKey = 0;
+ this->Get(ev, &ioRow, &oldKey, /*val*/ (void*) 0, (mork_change**) 0);
+
+ return oldKey;
+}
+
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowProbeMap::CloseMorkNode(morkEnv* ev) // CloseRowProbeMap() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseRowProbeMap(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkRowProbeMap::~morkRowProbeMap() // assert CloseRowProbeMap() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowProbeMap::morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots)
+: morkProbeMap(ev, inUsage, ioHeap,
+ /*inKeySize*/ sizeof(morkRow*), /*inValSize*/ 0,
+ ioSlotHeap, inSlots,
+ /*inHoldChanges*/ morkBool_kTrue)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kRowProbeMap;
+}
+
+/*public non-poly*/ void
+morkRowProbeMap::CloseRowProbeMap(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ this->CloseProbeMap(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*virtual*/ mork_test // hit(a,b) implies hash(a) == hash(b)
+morkRowProbeMap::MapTest(morkEnv* ev, const void* inMapKey,
+ const void* inAppKey) const
+{
+ MORK_USED_1(ev);
+ const morkRow* key = *(const morkRow**) inMapKey;
+ if ( key )
+ {
+ mork_bool hit = key->EqualRow(*(const morkRow**) inAppKey);
+ return ( hit ) ? morkTest_kHit : morkTest_kMiss;
+ }
+ else
+ return morkTest_kVoid;
+}
+
+/*virtual*/ mork_u4 // hit(a,b) implies hash(a) == hash(b)
+morkRowProbeMap::MapHash(morkEnv* ev, const void* inAppKey) const
+{
+ const morkRow* key = *(const morkRow**) inAppKey;
+ if ( key )
+ return key->HashRow();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+}
+
+/*virtual*/ mork_u4
+morkRowProbeMap::ProbeMapHashMapKey(morkEnv* ev,
+ const void* inMapKey) const
+{
+ const morkRow* key = *(const morkRow**) inMapKey;
+ if ( key )
+ return key->HashRow();
+ else
+ {
+ ev->NilPointerWarning();
+ return 0;
+ }
+}
+
+mork_bool
+morkRowProbeMap::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+ if ( ev->Good() )
+ {
+ this->MapAtPut(ev, &ioRow, /*val*/ (void*) 0,
+ /*key*/ (void*) 0, /*val*/ (void*) 0);
+ }
+ return ev->Good();
+}
+
+morkRow*
+morkRowProbeMap::CutOid(morkEnv* ev, const mdbOid* inOid)
+{
+ MORK_USED_1(inOid);
+ morkProbeMap::ProbeMapCutError(ev);
+
+ return 0;
+}
+
+morkRow*
+morkRowProbeMap::CutRow(morkEnv* ev, const morkRow* ioRow)
+{
+ MORK_USED_1(ioRow);
+ morkProbeMap::ProbeMapCutError(ev);
+
+ return 0;
+}
+
+morkRow*
+morkRowProbeMap::GetOid(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow row(inOid);
+ morkRow* key = &row;
+ morkRow* oldKey = 0;
+ this->MapAt(ev, &key, &oldKey, /*val*/ (void*) 0);
+
+ return oldKey;
+}
+
+morkRow*
+morkRowProbeMap::GetRow(morkEnv* ev, const morkRow* ioRow)
+{
+ morkRow* oldKey = 0;
+ this->MapAt(ev, &ioRow, &oldKey, /*val*/ (void*) 0);
+
+ return oldKey;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkRowMap.h b/db/mork/src/morkRowMap.h
new file mode 100644
index 000000000..5bf220829
--- /dev/null
+++ b/db/mork/src/morkRowMap.h
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKROWMAP_
+#define _MORKROWMAP_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowMap /*i*/ 0x724D /* ascii 'rM' */
+
+/*| morkRowMap: maps a set of morkRow by contained Oid
+|*/
+class morkRowMap : public morkMap { // for mapping row IDs to rows
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowMap() only if open
+ virtual ~morkRowMap(); // assert that CloseRowMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkRowMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots);
+ void CloseRowMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsRowMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kRowMap; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkMap poly interface =====
+ virtual mork_bool // note: equal(a,b) implies hash(a) == hash(b)
+ Equal(morkEnv* ev, const void* inKeyA, const void* inKeyB) const override;
+ // implemented using morkRow::EqualRow()
+
+ virtual mork_u4 // note: equal(a,b) implies hash(a) == hash(b)
+ Hash(morkEnv* ev, const void* inKey) const override;
+ // implemented using morkRow::HashRow()
+// } ===== end morkMap poly interface =====
+
+public: // other map methods
+
+ mork_bool AddRow(morkEnv* ev, morkRow* ioRow);
+ // AddRow() returns ev->Good()
+
+ morkRow* CutOid(morkEnv* ev, const mdbOid* inOid);
+ // CutRid() returns the row removed equal to inRid, if there was one
+
+ morkRow* CutRow(morkEnv* ev, const morkRow* ioRow);
+ // CutRow() returns the row removed equal to ioRow, if there was one
+
+ morkRow* GetOid(morkEnv* ev, const mdbOid* inOid);
+ // GetOid() returns the row equal to inRid, or else nil
+
+ morkRow* GetRow(morkEnv* ev, const morkRow* ioRow);
+ // GetRow() returns the row equal to ioRow, or else nil
+
+ // note the rows are owned elsewhere, usuall by morkRowSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakRowMap(morkRowMap* me,
+ morkEnv* ev, morkRowMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongRowMap(morkRowMap* me,
+ morkEnv* ev, morkRowMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+class morkRowMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkRowMapIter(morkEnv* ev, morkRowMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkRowMapIter( ) : morkMapIter() { }
+ void InitRowMapIter(morkEnv* ev, morkRowMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->First(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowProbeMap /*i*/ 0x726D /* ascii 'rm' */
+
+/*| morkRowProbeMap: maps a set of morkRow by contained Oid
+|*/
+class morkRowProbeMap : public morkProbeMap { // for mapping row IDs to rows
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowProbeMap() only if open
+ virtual ~morkRowProbeMap(); // assert CloseRowProbeMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkRowProbeMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_size inSlots);
+ void CloseRowProbeMap(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsRowMap() const
+ { return IsNode() && mNode_Derived == morkDerived_kRowMap; }
+// } ===== end morkNode methods =====
+
+ // { ===== begin morkProbeMap methods =====
+ virtual mork_test // hit(a,b) implies hash(a) == hash(b)
+ MapTest(morkEnv* ev, const void* inMapKey, const void* inAppKey) const override;
+
+ virtual mork_u4 // hit(a,b) implies hash(a) == hash(b)
+ MapHash(morkEnv* ev, const void* inAppKey) const override;
+
+ virtual mork_u4 ProbeMapHashMapKey(morkEnv* ev, const void* inMapKey) const override;
+
+ // virtual mork_bool ProbeMapIsKeyNil(morkEnv* ev, void* ioMapKey);
+
+ // virtual void ProbeMapClearKey(morkEnv* ev, // put 'nil' alls keys inside map
+ // void* ioMapKey, mork_count inKeyCount); // array of keys inside map
+
+ // virtual void ProbeMapPushIn(morkEnv* ev, // move (key,val) into the map
+ // const void* inAppKey, const void* inAppVal, // (key,val) outside map
+ // void* outMapKey, void* outMapVal); // (key,val) inside map
+
+ // virtual void ProbeMapPullOut(morkEnv* ev, // move (key,val) out from the map
+ // const void* inMapKey, const void* inMapVal, // (key,val) inside map
+ // void* outAppKey, void* outAppVal) const; // (key,val) outside map
+ // } ===== end morkProbeMap methods =====
+
+public: // other map methods
+
+ mork_bool AddRow(morkEnv* ev, morkRow* ioRow);
+ // AddRow() returns ev->Good()
+
+ morkRow* CutOid(morkEnv* ev, const mdbOid* inOid);
+ // CutRid() returns the row removed equal to inRid, if there was one
+
+ morkRow* CutRow(morkEnv* ev, const morkRow* ioRow);
+ // CutRow() returns the row removed equal to ioRow, if there was one
+
+ morkRow* GetOid(morkEnv* ev, const mdbOid* inOid);
+ // GetOid() returns the row equal to inRid, or else nil
+
+ morkRow* GetRow(morkEnv* ev, const morkRow* ioRow);
+ // GetRow() returns the row equal to ioRow, or else nil
+
+ // note the rows are owned elsewhere, usuall by morkRowSpace
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakRowProbeMap(morkRowProbeMap* me,
+ morkEnv* ev, morkRowProbeMap** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongRowProbeMap(morkRowProbeMap* me,
+ morkEnv* ev, morkRowProbeMap** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+class morkRowProbeMapIter: public morkProbeMapIter{ // typesafe wrapper class
+
+public:
+ morkRowProbeMapIter(morkEnv* ev, morkRowProbeMap* ioMap)
+ : morkProbeMapIter(ev, ioMap) { }
+
+ morkRowProbeMapIter( ) : morkProbeMapIter() { }
+ void InitRowMapIter(morkEnv* ev, morkRowProbeMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change* FirstRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->First(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* NextRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->Next(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* HereRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->Here(ev, outRowPtr, /*val*/ (void*) 0); }
+
+ mork_change* CutHereRow(morkEnv* ev, morkRow** outRowPtr)
+ { return this->CutHere(ev, outRowPtr, /*val*/ (void*) 0); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWMAP_ */
diff --git a/db/mork/src/morkRowObject.cpp b/db/mork/src/morkRowObject.cpp
new file mode 100644
index 000000000..33909c36d
--- /dev/null
+++ b/db/mork/src/morkRowObject.cpp
@@ -0,0 +1,622 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWCELLCURSOR_
+#include "morkRowCellCursor.h"
+#endif
+
+#ifndef _MORKCELLOBJECT_
+#include "morkCellObject.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowObject::CloseMorkNode(morkEnv* ev) // CloseRowObject() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseRowObject(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkRowObject::~morkRowObject() // assert CloseRowObject() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowObject::morkRowObject(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkRow* ioRow, morkStore* ioStore)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mRowObject_Row( 0 )
+, mRowObject_Store( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioRow && ioStore )
+ {
+ mRowObject_Row = ioRow;
+ mRowObject_Store = ioStore; // morkRowObjects don't ref-cnt the owning store.
+
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kRowObject;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkRowObject, morkObject, nsIMdbRow)
+// { ===== begin nsIMdbCollection methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkRowObject::GetSeed(nsIMdbEnv* mev,
+ mdb_seed* outSeed)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ *outSeed = (mdb_seed) mRowObject_Row->mRow_Seed;
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+NS_IMETHODIMP
+morkRowObject::GetCount(nsIMdbEnv* mev,
+ mdb_count* outCount)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ *outCount = (mdb_count) mRowObject_Row->mRow_Length;
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::GetPort(nsIMdbEnv* mev,
+ nsIMdbPort** acqPort)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbPort* outPort = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowSpace* rowSpace = mRowObject_Row->mRow_Space;
+ if ( rowSpace && rowSpace->mSpace_Store )
+ {
+ morkStore* store = mRowObject_Row->GetRowSpaceStore(ev);
+ if ( store )
+ outPort = store->AcquireStoreHandle(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqPort )
+ *acqPort = outPort;
+
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkRowObject::GetCursor( // make a cursor starting iter at inMemberPos
+ nsIMdbEnv* mev, // context
+ mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+ nsIMdbCursor** acqCursor)
+{
+ return this->GetRowCellCursor(mev, inMemberPos,
+ (nsIMdbRowCellCursor**) acqCursor);
+}
+// } ----- end cursor methods -----
+
+// { ----- begin ID methods -----
+NS_IMETHODIMP
+morkRowObject::GetOid(nsIMdbEnv* mev,
+ mdbOid* outOid)
+{
+ *outOid = mRowObject_Row->mRow_Oid;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ return (ev) ? ev->AsErr() : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+morkRowObject::BecomeContent(nsIMdbEnv* mev,
+ const mdbOid* inOid)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ // remember row->MaybeDirtySpaceStoreAndRow();
+}
+// } ----- end ID methods -----
+
+// { ----- begin activity dropping methods -----
+NS_IMETHODIMP
+morkRowObject::DropActivity( // tell collection usage no longer expected
+ nsIMdbEnv* mev)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+
+// { ===== begin nsIMdbRow methods =====
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkRowObject::GetRowCellCursor( // make a cursor starting iteration at inCellPos
+ nsIMdbEnv* mev, // context
+ mdb_pos inPos, // zero-based ordinal position of cell in row
+ nsIMdbRowCellCursor** acqCursor)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ nsIMdbRowCellCursor* outCursor = 0;
+ if ( ev )
+ {
+ morkRowCellCursor* cursor = mRowObject_Row->NewRowCellCursor(ev, inPos);
+ if ( cursor )
+ {
+ if ( ev->Good() )
+ {
+ cursor->mCursor_Seed = (mork_seed) inPos;
+ outCursor = cursor;
+ NS_ADDREF(cursor);
+ }
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqCursor )
+ *acqCursor = outCursor;
+ return outErr;
+}
+// } ----- end cursor methods -----
+
+// { ----- begin column methods -----
+NS_IMETHODIMP
+morkRowObject::AddColumn( // make sure a particular column is inside row
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to add
+ const mdbYarn* inYarn)
+{
+ nsresult outErr = NS_ERROR_FAILURE;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store && mRowObject_Row)
+ mRowObject_Row->AddColumn(ev, inColumn, inYarn, mRowObject_Store);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::CutColumn( // make sure a column is absent from the row
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn)
+{
+ nsresult outErr = NS_ERROR_FAILURE;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mRowObject_Row->CutColumn(ev, inColumn);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::CutAllColumns( // remove all columns from the row
+ nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mRowObject_Row->CutAllColumns(ev);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end column methods -----
+
+// { ----- begin cell methods -----
+NS_IMETHODIMP
+morkRowObject::NewCell( // get cell for specified column, or add new one
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to add
+ nsIMdbCell** acqCell)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ GetCell(mev, inColumn, acqCell);
+ if ( !*acqCell )
+ {
+ if ( mRowObject_Store )
+ {
+ mdbYarn yarn; // to pass empty yarn into morkRowObject::AddColumn()
+ yarn.mYarn_Buf = 0;
+ yarn.mYarn_Fill = 0;
+ yarn.mYarn_Size = 0;
+ yarn.mYarn_More = 0;
+ yarn.mYarn_Form = 0;
+ yarn.mYarn_Grow = 0;
+ AddColumn(ev, inColumn, &yarn);
+ GetCell(mev, inColumn, acqCell);
+ }
+ }
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::AddCell( // copy a cell from another row to this row
+ nsIMdbEnv* mev, // context
+ const nsIMdbCell* inCell)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkCell* cell = 0;
+ morkCellObject* cellObj = (morkCellObject*) inCell;
+ if ( cellObj->CanUseCell(mev, morkBool_kFalse, &outErr, &cell) )
+ {
+
+ morkRow* cellRow = cellObj->mCellObject_Row;
+ if ( cellRow )
+ {
+ if ( mRowObject_Row != cellRow )
+ {
+ morkStore* store = mRowObject_Row->GetRowSpaceStore(ev);
+ morkStore* cellStore = cellRow->GetRowSpaceStore(ev);
+ if ( store && cellStore )
+ {
+ mork_column col = cell->GetColumn();
+ morkAtom* atom = cell->mCell_Atom;
+ mdbYarn yarn;
+ morkAtom::AliasYarn(atom, &yarn); // works even when atom is nil
+
+ if ( store != cellStore )
+ col = store->CopyToken(ev, col, cellStore);
+ if ( ev->Good() )
+ AddColumn(ev, col, &yarn);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::GetCell( // find a cell in this row
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to find
+ nsIMdbCell** acqCell)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbCell* outCell = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+
+ if ( ev )
+ {
+ if ( inColumn )
+ {
+ mork_pos pos = 0;
+ morkCell* cell = mRowObject_Row->GetCell(ev, inColumn, &pos);
+ if ( cell )
+ {
+ outCell = mRowObject_Row->AcquireCellHandle(ev, cell, inColumn, pos);
+ }
+ }
+ else
+ mRowObject_Row->ZeroColumnError(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqCell )
+ *acqCell = outCell;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::EmptyAllCells( // make all cells in row empty of content
+ nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ EmptyAllCells(ev);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end cell methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkRowObject::AddRow( // add all cells in another row to this one
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioSourceRow)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRow* unsafeSource = (morkRow*) ioSourceRow; // unsafe cast
+// if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) )
+ {
+ mRowObject_Row->AddRow(ev, unsafeSource);
+ }
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::SetRow( // make exact duplicate of another row
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioSourceRow)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject *sourceObject = (morkRowObject *) ioSourceRow; // unsafe cast
+ morkRow* unsafeSource = sourceObject->mRowObject_Row;
+// if ( unsafeSource->CanUseRow(mev, morkBool_kFalse, &outErr, &source) )
+ {
+ mRowObject_Row->SetRow(ev, unsafeSource);
+ }
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end row methods -----
+
+// { ----- begin blob methods -----
+NS_IMETHODIMP
+morkRowObject::SetCellYarn( // synonym for AddColumn()
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to add
+ const mdbYarn* inYarn)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store )
+ AddColumn(ev, inColumn, inYarn);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+NS_IMETHODIMP
+morkRowObject::GetCellYarn(
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to read
+ mdbYarn* outYarn) // writes some yarn slots
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store && mRowObject_Row)
+ {
+ morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn);
+ morkAtom::GetYarn(atom, outYarn);
+ }
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::AliasCellYarn(
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to alias
+ mdbYarn* outYarn) // writes ALL yarn slots
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store && mRowObject_Row)
+ {
+ morkAtom* atom = mRowObject_Row->GetColumnAtom(ev, inColumn);
+ morkAtom::AliasYarn(atom, outYarn);
+ // note nil atom works and sets yarn correctly
+ }
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::NextCellYarn(nsIMdbEnv* mev, // iterative version of GetCellYarn()
+ mdb_column* ioColumn, // next column to read
+ mdbYarn* outYarn) // writes some yarn slots
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+//
+// The ioColumn argument is an inout parameter which initially contains the
+// last column accessed and returns the next column corresponding to the
+// content read into the yarn. Callers should start with a zero column
+// value to say 'no previous column', which causes the first column to be
+// read. Then the value returned in ioColumn is perfect for the next call
+// to NextCellYarn(), since it will then be the previous column accessed.
+// Callers need only examine the column token returned to see which cell
+// in the row is being read into the yarn. When no more columns remain,
+// and the iteration has ended, ioColumn will return a zero token again.
+// So iterating over cells starts and ends with a zero column token.
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store && mRowObject_Row)
+ mRowObject_Row->NextColumn(ev, ioColumn, outYarn);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkRowObject::SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+ nsIMdbEnv* mev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ mdbYarn* outYarn) // writes some yarn slots
+// copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+// Callers can pass nil for outYarn to indicate no interest in content, so
+// only the outColumn value is returned. NOTE to subclasses: you must be
+// able to ignore outYarn when the pointer is nil; please do not crash.
+
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mRowObject_Store && mRowObject_Row)
+ mRowObject_Row->SeekColumn(ev, inPos, outColumn, outYarn);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+// } ----- end blob methods -----
+
+
+// } ===== end nsIMdbRow methods =====
+
+
+
+/*public non-poly*/ void
+morkRowObject::CloseRowObject(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkRow* row = mRowObject_Row;
+ mRowObject_Row = 0;
+ this->CloseObject(ev);
+ this->MarkShut();
+
+ if ( row )
+ {
+ MORK_ASSERT(row->mRow_Object == this);
+ if ( row->mRow_Object == this )
+ {
+ row->mRow_Object = 0; // just nil this slot -- cut ref down below
+
+ mRowObject_Store = 0; // morkRowObjects don't ref-cnt the owning store.
+
+ this->CutWeakRef(ev->AsMdbEnv()); // do last, because it might self destroy
+ }
+ }
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkRowObject::NonRowObjectTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkRowObject");
+}
+
+/*static*/ void
+morkRowObject::NilRowError(morkEnv* ev)
+{
+ ev->NewError("nil mRowObject_Row");
+}
+
+/*static*/ void
+morkRowObject::NilStoreError(morkEnv* ev)
+{
+ ev->NewError("nil mRowObject_Store");
+}
+
+/*static*/ void
+morkRowObject::RowObjectRowNotSelfError(morkEnv* ev)
+{
+ ev->NewError("mRowObject_Row->mRow_Object != self");
+}
+
+
+nsIMdbRow*
+morkRowObject::AcquireRowHandle(morkEnv* ev) // mObject_Handle
+{
+ AddRef();
+ return this;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkRowObject.h b/db/mork/src/morkRowObject.h
new file mode 100644
index 000000000..c30494a45
--- /dev/null
+++ b/db/mork/src/morkRowObject.h
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKROWOBJECT_
+#define _MORKROWOBJECT_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbRow;
+#define morkDerived_kRowObject /*i*/ 0x724F /* ascii 'rO' */
+
+class morkRowObject : public morkObject, public nsIMdbRow { //
+
+public: // state is public because the entire Mork system is private
+ NS_DECL_ISUPPORTS_INHERITED
+
+ morkRow* mRowObject_Row; // non-refcounted alias to morkRow
+ morkStore* mRowObject_Store; // non-refcounted ptr to store containing row
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowObject() only if open
+
+public: // morkRowObject construction & destruction
+ morkRowObject(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkRow* ioRow, morkStore* ioStore);
+ void CloseRowObject(morkEnv* ev); // called by CloseMorkNode();
+
+// { ===== begin nsIMdbCollection methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+ mdb_seed* outSeed) override; // member change count
+ NS_IMETHOD GetCount(nsIMdbEnv* ev,
+ mdb_count* outCount) override; // member count
+
+ NS_IMETHOD GetPort(nsIMdbEnv* ev,
+ nsIMdbPort** acqPort) override; // collection container
+ // } ----- end attribute methods -----
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+ nsIMdbCursor** acqCursor) override; // acquire new cursor instance
+ // } ----- end cursor methods -----
+
+ // { ----- begin ID methods -----
+ NS_IMETHOD GetOid(nsIMdbEnv* ev,
+ mdbOid* outOid) override; // read object identity
+ NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+ const mdbOid* inOid) override; // exchange content
+ // } ----- end ID methods -----
+
+ // { ----- begin activity dropping methods -----
+ NS_IMETHOD DropActivity( // tell collection usage no longer expected
+ nsIMdbEnv* ev) override;
+ // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+// { ===== begin nsIMdbRow methods =====
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetRowCellCursor( // make a cursor starting iteration at inRowPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbRowCellCursor** acqCursor) override; // acquire new cursor instance
+ // } ----- end cursor methods -----
+
+ // { ----- begin column methods -----
+ NS_IMETHOD AddColumn( // make sure a particular column is inside row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to add
+ const mdbYarn* inYarn) override; // cell value to install
+
+ NS_IMETHOD CutColumn( // make sure a column is absent from the row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) override; // column to ensure absent from row
+
+ NS_IMETHOD CutAllColumns( // remove all columns from the row
+ nsIMdbEnv* ev) override; // context
+ // } ----- end column methods -----
+
+ // { ----- begin cell methods -----
+ NS_IMETHOD NewCell( // get cell for specified column, or add new one
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to add
+ nsIMdbCell** acqCell) override; // cell column and value
+
+ NS_IMETHOD AddCell( // copy a cell from another row to this row
+ nsIMdbEnv* ev, // context
+ const nsIMdbCell* inCell) override; // cell column and value
+
+ NS_IMETHOD GetCell( // find a cell in this row
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to find
+ nsIMdbCell** acqCell) override; // cell for specified column, or null
+
+ NS_IMETHOD EmptyAllCells( // make all cells in row empty of content
+ nsIMdbEnv* ev) override; // context
+ // } ----- end cell methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD AddRow( // add all cells in another row to this one
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioSourceRow) override; // row to union with
+
+ NS_IMETHOD SetRow( // make exact duplicate of another row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioSourceRow) override; // row to duplicate
+ // } ----- end row methods -----
+
+ // { ----- begin blob methods -----
+ NS_IMETHOD SetCellYarn(nsIMdbEnv* ev, // synonym for AddColumn()
+ mdb_column inColumn, // column to write
+ const mdbYarn* inYarn) override; // reads from yarn slots
+ // make this text object contain content from the yarn's buffer
+
+ NS_IMETHOD GetCellYarn(nsIMdbEnv* ev,
+ mdb_column inColumn, // column to read
+ mdbYarn* outYarn) override; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+
+ NS_IMETHOD AliasCellYarn(nsIMdbEnv* ev,
+ mdb_column inColumn, // column to alias
+ mdbYarn* outYarn) override; // writes ALL yarn slots
+
+ NS_IMETHOD NextCellYarn(nsIMdbEnv* ev, // iterative version of GetCellYarn()
+ mdb_column* ioColumn, // next column to read
+ mdbYarn* outYarn) override; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+ //
+ // The ioColumn argument is an inout parameter which initially contains the
+ // last column accessed and returns the next column corresponding to the
+ // content read into the yarn. Callers should start with a zero column
+ // value to say 'no previous column', which causes the first column to be
+ // read. Then the value returned in ioColumn is perfect for the next call
+ // to NextCellYarn(), since it will then be the previous column accessed.
+ // Callers need only examine the column token returned to see which cell
+ // in the row is being read into the yarn. When no more columns remain,
+ // and the iteration has ended, ioColumn will return a zero token again.
+ // So iterating over cells starts and ends with a zero column token.
+
+ NS_IMETHOD SeekCellYarn( // resembles nsIMdbRowCellCursor::SeekCell()
+ nsIMdbEnv* ev, // context
+ mdb_pos inPos, // position of cell in row sequence
+ mdb_column* outColumn, // column for this particular cell
+ mdbYarn* outYarn) override; // writes some yarn slots
+ // copy content into the yarn buffer, and update mYarn_Fill and mYarn_Form
+ // Callers can pass nil for outYarn to indicate no interest in content, so
+ // only the outColumn value is returned. NOTE to subclasses: you must be
+ // able to ignore outYarn when the pointer is nil; please do not crash.
+
+ // } ----- end blob methods -----
+
+// } ===== end nsIMdbRow methods =====
+
+private: // copying is not allowed
+ morkRowObject(const morkRowObject& other);
+ morkRowObject& operator=(const morkRowObject& other);
+ virtual ~morkRowObject(); // assert that CloseRowObject() executed earlier
+
+public: // dynamic type identification
+ mork_bool IsRowObject() const
+ { return IsNode() && mNode_Derived == morkDerived_kRowObject; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonRowObjectTypeError(morkEnv* ev);
+ static void NilRowError(morkEnv* ev);
+ static void NilStoreError(morkEnv* ev);
+ static void RowObjectRowNotSelfError(morkEnv* ev);
+
+public: // other row node methods
+
+ nsIMdbRow* AcquireRowHandle(morkEnv* ev); // mObject_Handle
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakRowObject(morkRowObject* me,
+ morkEnv* ev, morkRowObject** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongRowObject(morkRowObject* me,
+ morkEnv* ev, morkRowObject** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWOBJECT_ */
diff --git a/db/mork/src/morkRowSpace.cpp b/db/mork/src/morkRowSpace.cpp
new file mode 100644
index 000000000..10f2416f5
--- /dev/null
+++ b/db/mork/src/morkRowSpace.cpp
@@ -0,0 +1,632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkRowSpace::CloseMorkNode(morkEnv* ev) // CloseRowSpace() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseRowSpace(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkRowSpace::~morkRowSpace() // assert CloseRowSpace() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkRowSpace::morkRowSpace(morkEnv* ev,
+ const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkSpace(ev, inUsage, inScope, ioStore, ioHeap, ioSlotHeap)
+, mRowSpace_SlotHeap( ioSlotHeap )
+, mRowSpace_Rows(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
+ morkRowSpace_kStartRowMapSlotCount)
+, mRowSpace_Tables(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+, mRowSpace_NextTableId( 1 )
+, mRowSpace_NextRowId( 1 )
+
+, mRowSpace_IndexCount( 0 )
+{
+ morkAtomRowMap** cache = mRowSpace_IndexCache;
+ morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
+ while ( cache < cacheEnd )
+ *cache++ = 0; // put nil into every slot of cache table
+
+ if ( ev->Good() )
+ {
+ if ( ioSlotHeap )
+ {
+ mNode_Derived = morkDerived_kRowSpace;
+
+ // the morkSpace base constructor handles any dirty propagation
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+/*public non-poly*/ void
+morkRowSpace::CloseRowSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkAtomRowMap** cache = mRowSpace_IndexCache;
+ morkAtomRowMap** cacheEnd = cache + morkRowSpace_kPrimeCacheSize;
+ --cache; // prepare for preincrement:
+ while ( ++cache < cacheEnd )
+ {
+ if ( *cache )
+ morkAtomRowMap::SlotStrongAtomRowMap(0, ev, cache);
+ }
+
+ mRowSpace_Tables.CloseMorkNode(ev);
+
+ morkStore* store = mSpace_Store;
+ if ( store )
+ this->CutAllRows(ev, &store->mStore_Pool);
+
+ mRowSpace_Rows.CloseMorkNode(ev);
+ this->CloseSpace(ev);
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkRowSpace::NonRowSpaceTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkRowSpace");
+}
+
+/*static*/ void
+morkRowSpace::ZeroKindError(morkEnv* ev)
+{
+ ev->NewError("zero table kind");
+}
+
+/*static*/ void
+morkRowSpace::ZeroScopeError(morkEnv* ev)
+{
+ ev->NewError("zero row scope");
+}
+
+/*static*/ void
+morkRowSpace::ZeroTidError(morkEnv* ev)
+{
+ ev->NewError("zero table ID");
+}
+
+/*static*/ void
+morkRowSpace::MinusOneRidError(morkEnv* ev)
+{
+ ev->NewError("row ID is -1");
+}
+
+///*static*/ void
+//morkRowSpace::ExpectAutoIdOnlyError(morkEnv* ev)
+//{
+// ev->NewError("zero row ID");
+//}
+
+///*static*/ void
+//morkRowSpace::ExpectAutoIdNeverError(morkEnv* ev)
+//{
+//}
+
+mork_num
+morkRowSpace::CutAllRows(morkEnv* ev, morkPool* ioPool)
+{
+ if ( this->IsRowSpaceClean() )
+ this->MaybeDirtyStoreAndSpace();
+
+ mork_num outSlots = mRowSpace_Rows.MapFill();
+
+#ifdef MORK_ENABLE_ZONE_ARENAS
+ MORK_USED_2(ev, ioPool);
+ return 0;
+#else /*MORK_ENABLE_ZONE_ARENAS*/
+ morkZone* zone = &mSpace_Store->mStore_Zone;
+ morkRow* r = 0; // old key row in the map
+ mork_change* c = 0;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMapIter i(ev, &mRowSpace_Rows);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMapIter i(ev, &mRowSpace_Rows);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+ for ( c = i.FirstRow(ev, &r); c && ev->Good();
+ c = i.NextRow(ev, &r) )
+ {
+ if ( r )
+ {
+ if ( r->IsRow() )
+ {
+ if ( r->mRow_Object )
+ {
+ morkRowObject::SlotWeakRowObject((morkRowObject*) 0, ev,
+ &r->mRow_Object);
+ }
+ ioPool->ZapRow(ev, r, zone);
+ }
+ else
+ r->NonRowTypeWarning(ev);
+ }
+ else
+ ev->NilPointerError();
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ // cut nothing from the map
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ i.CutHereRow(ev, /*key*/ (morkRow**) 0);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ }
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+
+
+ return outSlots;
+}
+
+morkTable*
+morkRowSpace::FindTableByKind(morkEnv* ev, mork_kind inTableKind)
+{
+ if ( inTableKind )
+ {
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+
+ morkTableMapIter i(ev, &mRowSpace_Tables);
+ morkTable* table = i.FirstTable(ev);
+ for ( ; table && ev->Good(); table = i.NextTable(ev) )
+
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ mork_tid* key = 0; // nil pointer to suppress key access
+ morkTable* table = 0; // old table in the map
+
+ mork_change* c = 0;
+ morkTableMapIter i(ev, &mRowSpace_Tables);
+ for ( c = i.FirstTable(ev, key, &table); c && ev->Good();
+ c = i.NextTable(ev, key, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+ {
+ if ( table->mTable_Kind == inTableKind )
+ return table;
+ }
+ }
+ else
+ this->ZeroKindError(ev);
+
+ return (morkTable*) 0;
+}
+
+morkTable*
+morkRowSpace::NewTableWithTid(morkEnv* ev, mork_tid inTid,
+ mork_kind inTableKind,
+ const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying
+{
+ morkTable* outTable = 0;
+ morkStore* store = mSpace_Store;
+
+ if ( inTableKind && store )
+ {
+ mdb_bool mustBeUnique = morkBool_kFalse;
+ nsIMdbHeap* heap = store->mPort_Heap;
+ morkTable* table = new(*heap, ev)
+ morkTable(ev, morkUsage::kHeap, heap, store, heap, this,
+ inOptionalMetaRowOid, inTid, inTableKind, mustBeUnique);
+ if ( table )
+ {
+ if ( mRowSpace_Tables.AddTable(ev, table) )
+ {
+ outTable = table;
+ if ( mRowSpace_NextTableId <= inTid )
+ mRowSpace_NextTableId = inTid + 1;
+ }
+
+ if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+ this->MaybeDirtyStoreAndSpace(); // morkTable does already
+
+ }
+ }
+ else if ( store )
+ this->ZeroKindError(ev);
+ else
+ this->NilSpaceStoreError(ev);
+
+ return outTable;
+}
+
+morkTable*
+morkRowSpace::NewTable(morkEnv* ev, mork_kind inTableKind,
+ mdb_bool inMustBeUnique,
+ const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying
+{
+ morkTable* outTable = 0;
+ morkStore* store = mSpace_Store;
+
+ if ( inTableKind && store )
+ {
+ if ( inMustBeUnique ) // need to look for existing table first?
+ outTable = this->FindTableByKind(ev, inTableKind);
+
+ if ( !outTable && ev->Good() )
+ {
+ mork_tid id = this->MakeNewTableId(ev);
+ if ( id )
+ {
+ nsIMdbHeap* heap = mSpace_Store->mPort_Heap;
+ morkTable* table = new(*heap, ev)
+ morkTable(ev, morkUsage::kHeap, heap, mSpace_Store, heap, this,
+ inOptionalMetaRowOid, id, inTableKind, inMustBeUnique);
+ if ( table )
+ {
+ if ( mRowSpace_Tables.AddTable(ev, table) )
+ outTable = table;
+ else
+ table->Release();
+
+ if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+ this->MaybeDirtyStoreAndSpace(); // morkTable does already
+ }
+ }
+ }
+ }
+ else if ( store )
+ this->ZeroKindError(ev);
+ else
+ this->NilSpaceStoreError(ev);
+
+ return outTable;
+}
+
+mork_tid
+morkRowSpace::MakeNewTableId(morkEnv* ev)
+{
+ mork_tid outTid = 0;
+ mork_tid id = mRowSpace_NextTableId;
+ mork_num count = 9; // try up to eight times
+
+ while ( !outTid && --count ) // still trying to find an unused table ID?
+ {
+ if ( !mRowSpace_Tables.GetTable(ev, id) )
+ outTid = id;
+ else
+ {
+ MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+ ++id;
+ }
+ }
+
+ mRowSpace_NextTableId = id + 1;
+ return outTid;
+}
+
+#define MAX_AUTO_ID (morkRow_kMinusOneRid - 2)
+mork_rid
+morkRowSpace::MakeNewRowId(morkEnv* ev)
+{
+ mork_rid outRid = 0;
+ mork_rid id = mRowSpace_NextRowId;
+ mork_num count = 9; // try up to eight times
+ mdbOid oid;
+ oid.mOid_Scope = this->SpaceScope();
+
+ // still trying to find an unused row ID?
+ while (!outRid && --count && id <= MAX_AUTO_ID)
+ {
+ oid.mOid_Id = id;
+ if ( !mRowSpace_Rows.GetOid(ev, &oid) )
+ outRid = id;
+ else
+ {
+ MORK_ASSERT(morkBool_kFalse); // alert developer about ID problems
+ ++id;
+ }
+ }
+
+ if (id < MAX_AUTO_ID)
+ mRowSpace_NextRowId = id + 1;
+ return outRid;
+}
+
+morkAtomRowMap*
+morkRowSpace::make_index(morkEnv* ev, mork_column inCol)
+{
+ morkAtomRowMap* outMap = 0;
+ nsIMdbHeap* heap = mRowSpace_SlotHeap;
+ if ( heap ) // have expected heap for allocations?
+ {
+ morkAtomRowMap* map = new(*heap, ev)
+ morkAtomRowMap(ev, morkUsage::kHeap, heap, heap, inCol);
+
+ if ( map ) // able to create new map index?
+ {
+ if ( ev->Good() ) // no errors during construction?
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMapIter i(ev, &mRowSpace_Rows);
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMapIter i(ev, &mRowSpace_Rows);
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ mork_change* c = 0;
+ morkRow* row = 0;
+ mork_aid aidKey = 0;
+
+ for ( c = i.FirstRow(ev, &row); c && ev->Good();
+ c = i.NextRow(ev, &row) ) // another row in space?
+ {
+ aidKey = row->GetCellAtomAid(ev, inCol);
+ if ( aidKey ) // row has indexed attribute?
+ map->AddAid(ev, aidKey, row); // include in map
+ }
+ }
+ if ( ev->Good() ) // no errors constructing index?
+ outMap = map; // return from function
+ else
+ map->CutStrongRef(ev); // discard map on error
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return outMap;
+}
+
+morkAtomRowMap*
+morkRowSpace::ForceMap(morkEnv* ev, mork_column inCol)
+{
+ morkAtomRowMap* outMap = this->FindMap(ev, inCol);
+
+ if ( !outMap && ev->Good() ) // no such existing index?
+ {
+ if ( mRowSpace_IndexCount < morkRowSpace_kMaxIndexCount )
+ {
+ morkAtomRowMap* map = this->make_index(ev, inCol);
+ if ( map ) // created a new index for col?
+ {
+ mork_count wrap = 0; // count times wrap-around occurs
+ morkAtomRowMap** slot = mRowSpace_IndexCache; // table
+ morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
+ slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
+ while ( *slot ) // empty slot not yet found?
+ {
+ if ( ++slot >= end ) // wrap around?
+ {
+ slot = mRowSpace_IndexCache; // back to table start
+ if ( ++wrap > 1 ) // wrapped more than once?
+ {
+ ev->NewError("no free cache slots"); // disaster
+ break; // end while loop
+ }
+ }
+ }
+ if ( ev->Good() ) // everything went just fine?
+ {
+ ++mRowSpace_IndexCount; // note another new map
+ *slot = map; // install map in the hash table
+ outMap = map; // return the new map from function
+ }
+ else
+ map->CutStrongRef(ev); // discard map on error
+ }
+ }
+ else
+ ev->NewError("too many indexes"); // why so many indexes?
+ }
+ return outMap;
+}
+
+morkAtomRowMap*
+morkRowSpace::FindMap(morkEnv* ev, mork_column inCol)
+{
+ if ( mRowSpace_IndexCount && ev->Good() )
+ {
+ mork_count wrap = 0; // count times wrap-around occurs
+ morkAtomRowMap** slot = mRowSpace_IndexCache; // table
+ morkAtomRowMap** end = slot + morkRowSpace_kPrimeCacheSize;
+ slot += ( inCol % morkRowSpace_kPrimeCacheSize ); // hash
+ morkAtomRowMap* map = *slot;
+ while ( map ) // another used slot to examine?
+ {
+ if ( inCol == map->mAtomRowMap_IndexColumn ) // found col?
+ return map;
+ if ( ++slot >= end ) // wrap around?
+ {
+ slot = mRowSpace_IndexCache;
+ if ( ++wrap > 1 ) // wrapped more than once?
+ return (morkAtomRowMap*) 0; // stop searching
+ }
+ map = *slot;
+ }
+ }
+ return (morkAtomRowMap*) 0;
+}
+
+morkRow*
+morkRowSpace::FindRow(morkEnv* ev, mork_column inCol, const mdbYarn* inYarn)
+{
+ morkRow* outRow = 0;
+
+ // if yarn hasn't been atomized, there can't be a corresponding row,
+ // so pass in false to not create the row - should help history bloat
+ morkAtom* atom = mSpace_Store->YarnToAtom(ev, inYarn, false);
+ if ( atom ) // have or created an atom corresponding to input yarn?
+ {
+ mork_aid atomAid = atom->GetBookAtomAid();
+ if ( atomAid ) // atom has an identity for use in hash table?
+ {
+ morkAtomRowMap* map = this->ForceMap(ev, inCol);
+ if ( map ) // able to find or create index for col?
+ {
+ outRow = map->GetAid(ev, atomAid); // search for row
+ }
+ }
+ }
+
+ return outRow;
+}
+
+morkRow*
+morkRowSpace::NewRowWithOid(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow* outRow = mRowSpace_Rows.GetOid(ev, inOid);
+ MORK_ASSERT(outRow==0);
+ if ( !outRow && ev->Good() )
+ {
+ morkStore* store = mSpace_Store;
+ if ( store )
+ {
+ morkPool* pool = this->GetSpaceStorePool();
+ morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
+ if ( row )
+ {
+ row->InitRow(ev, inOid, this, /*length*/ 0, pool);
+
+ if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
+ {
+ outRow = row;
+ mork_rid rid = inOid->mOid_Id;
+ if ( mRowSpace_NextRowId <= rid )
+ mRowSpace_NextRowId = rid + 1;
+ }
+ else
+ pool->ZapRow(ev, row, &store->mStore_Zone);
+
+ if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+ this->MaybeDirtyStoreAndSpace(); // InitRow() does already
+ }
+ }
+ else
+ this->NilSpaceStoreError(ev);
+ }
+ return outRow;
+}
+
+morkRow*
+morkRowSpace::NewRow(morkEnv* ev)
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ mork_rid id = this->MakeNewRowId(ev);
+ if ( id )
+ {
+ morkStore* store = mSpace_Store;
+ if ( store )
+ {
+ mdbOid oid;
+ oid.mOid_Scope = this->SpaceScope();
+ oid.mOid_Id = id;
+ morkPool* pool = this->GetSpaceStorePool();
+ morkRow* row = pool->NewRow(ev, &store->mStore_Zone);
+ if ( row )
+ {
+ row->InitRow(ev, &oid, this, /*length*/ 0, pool);
+
+ if ( ev->Good() && mRowSpace_Rows.AddRow(ev, row) )
+ outRow = row;
+ else
+ pool->ZapRow(ev, row, &store->mStore_Zone);
+
+ if ( this->IsRowSpaceClean() && store->mStore_CanDirty )
+ this->MaybeDirtyStoreAndSpace(); // InitRow() does already
+ }
+ }
+ else
+ this->NilSpaceStoreError(ev);
+ }
+ }
+ return outRow;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkRowSpaceMap::~morkRowSpaceMap()
+{
+}
+
+morkRowSpaceMap::morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+ : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kRowSpaceMap;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkRowSpace.h b/db/mork/src/morkRowSpace.h
new file mode 100644
index 000000000..0ef331bb8
--- /dev/null
+++ b/db/mork/src/morkRowSpace.h
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKROWSPACE_
+#define _MORKROWSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowSpace /*i*/ 0x7253 /* ascii 'rS' */
+
+#define morkRowSpace_kStartRowMapSlotCount 11
+
+#define morkRowSpace_kMaxIndexCount 8 /* no more indexes than this */
+#define morkRowSpace_kPrimeCacheSize 17 /* should be prime number */
+
+class morkAtomRowMap;
+
+/*| morkRowSpace:
+|*/
+class morkRowSpace : public morkSpace { //
+
+// public: // slots inherited from morkSpace (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkStore* mSpace_Store; // weak ref to containing store
+
+ // mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs
+ // mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+ // mork_u1 mSpace_Pad[ 2 ]; // pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+
+ nsIMdbHeap* mRowSpace_SlotHeap;
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMap mRowSpace_Rows; // hash table of morkRow instances
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMap mRowSpace_Rows; // hash table of morkRow instances
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ morkTableMap mRowSpace_Tables; // all the tables in this row scope
+
+ mork_tid mRowSpace_NextTableId; // for auto-assigning table IDs
+ mork_rid mRowSpace_NextRowId; // for auto-assigning row IDs
+
+ mork_count mRowSpace_IndexCount; // if nonzero, row indexes exist
+
+ // every nonzero slot in IndexCache is a strong ref to a morkAtomRowMap:
+ morkAtomRowMap* mRowSpace_IndexCache[ morkRowSpace_kPrimeCacheSize ];
+
+ morkDeque mRowSpace_TablesByPriority[ morkPriority_kCount ];
+
+public: // more specific dirty methods for row space:
+ void SetRowSpaceDirty() { this->SetNodeDirty(); }
+ void SetRowSpaceClean() { this->SetNodeClean(); }
+
+ mork_bool IsRowSpaceClean() const { return this->IsNodeClean(); }
+ mork_bool IsRowSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseRowSpace() only if open
+ virtual ~morkRowSpace(); // assert that CloseRowSpace() executed earlier
+
+public: // morkMap construction & destruction
+ morkRowSpace(morkEnv* ev, const morkUsage& inUsage, mork_scope inScope,
+ morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseRowSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsRowSpace() const
+ { return IsNode() && mNode_Derived == morkDerived_kRowSpace; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonRowSpaceTypeError(morkEnv* ev);
+ static void ZeroScopeError(morkEnv* ev);
+ static void ZeroKindError(morkEnv* ev);
+ static void ZeroTidError(morkEnv* ev);
+ static void MinusOneRidError(morkEnv* ev);
+
+ //static void ExpectAutoIdOnlyError(morkEnv* ev);
+ //static void ExpectAutoIdNeverError(morkEnv* ev);
+
+public: // other space methods
+
+ mork_num CutAllRows(morkEnv* ev, morkPool* ioPool);
+ // CutAllRows() puts all rows and cells back into the pool.
+
+ morkTable* NewTable(morkEnv* ev, mork_kind inTableKind,
+ mdb_bool inMustBeUnique, const mdbOid* inOptionalMetaRowOid);
+
+ morkTable* NewTableWithTid(morkEnv* ev, mork_tid inTid,
+ mork_kind inTableKind, const mdbOid* inOptionalMetaRowOid);
+
+ morkTable* FindTableByKind(morkEnv* ev, mork_kind inTableKind);
+ morkTable* FindTableByTid(morkEnv* ev, mork_tid inTid)
+ { return mRowSpace_Tables.GetTable(ev, inTid); }
+
+ mork_tid MakeNewTableId(morkEnv* ev);
+ mork_rid MakeNewRowId(morkEnv* ev);
+
+ // morkRow* FindRowByRid(morkEnv* ev, mork_rid inRid)
+ // { return (morkRow*) mRowSpace_Rows.GetRow(ev, inRid); }
+
+ morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
+ morkRow* NewRow(morkEnv* ev);
+
+ morkRow* FindRow(morkEnv* ev, mork_column inColumn, const mdbYarn* inYarn);
+
+ morkAtomRowMap* ForceMap(morkEnv* ev, mork_column inColumn);
+ morkAtomRowMap* FindMap(morkEnv* ev, mork_column inColumn);
+
+protected: // internal utilities
+ morkAtomRowMap* make_index(morkEnv* ev, mork_column inColumn);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakRowSpace(morkRowSpace* me,
+ morkEnv* ev, morkRowSpace** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongRowSpace(morkRowSpace* me,
+ morkEnv* ev, morkRowSpace** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kRowSpaceMap /*i*/ 0x725A /* ascii 'rZ' */
+
+/*| morkRowSpaceMap: maps mork_scope -> morkRowSpace
+|*/
+class morkRowSpaceMap : public morkNodeMap { // for mapping tokens to tables
+
+public:
+
+ virtual ~morkRowSpaceMap();
+ morkRowSpaceMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+ mork_bool AddRowSpace(morkEnv* ev, morkRowSpace* ioRowSpace)
+ { return this->AddNode(ev, ioRowSpace->SpaceScope(), ioRowSpace); }
+ // the AddRowSpace() boolean return equals ev->Good().
+
+ mork_bool CutRowSpace(morkEnv* ev, mork_scope inScope)
+ { return this->CutNode(ev, inScope); }
+ // The CutRowSpace() boolean return indicates whether removal happened.
+
+ morkRowSpace* GetRowSpace(morkEnv* ev, mork_scope inScope)
+ { return (morkRowSpace*) this->GetNode(ev, inScope); }
+ // Note the returned space does NOT have an increase in refcount for this.
+
+ mork_num CutAllRowSpaces(morkEnv* ev)
+ { return this->CutAllNodes(ev); }
+ // CutAllRowSpaces() releases all the referenced table values.
+};
+
+class morkRowSpaceMapIter: public morkMapIter{ // typesafe wrapper class
+
+public:
+ morkRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkRowSpaceMapIter( ) : morkMapIter() { }
+ void InitRowSpaceMapIter(morkEnv* ev, morkRowSpaceMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change*
+ FirstRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+ { return this->First(ev, outScope, outRowSpace); }
+
+ mork_change*
+ NextRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+ { return this->Next(ev, outScope, outRowSpace); }
+
+ mork_change*
+ HereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+ { return this->Here(ev, outScope, outRowSpace); }
+
+ mork_change*
+ CutHereRowSpace(morkEnv* ev, mork_scope* outScope, morkRowSpace** outRowSpace)
+ { return this->CutHere(ev, outScope, outRowSpace); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKROWSPACE_ */
diff --git a/db/mork/src/morkSearchRowCursor.cpp b/db/mork/src/morkSearchRowCursor.cpp
new file mode 100644
index 000000000..5bc0b4372
--- /dev/null
+++ b/db/mork/src/morkSearchRowCursor.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKSEARCHROWCURSOR_
+#include "morkSearchRowCursor.h"
+#endif
+
+#ifndef _MORKUNIQROWCURSOR_
+#include "morkUniqRowCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkSearchRowCursor::CloseMorkNode(morkEnv* ev) // CloseSearchRowCursor() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseSearchRowCursor(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkSearchRowCursor::~morkSearchRowCursor() // CloseSearchRowCursor() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkSearchRowCursor::morkSearchRowCursor(morkEnv* ev,
+ const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos)
+: morkTableRowCursor(ev, inUsage, ioHeap, ioTable, inRowPos)
+// , mSortingRowCursor_Sorting( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioTable )
+ {
+ // morkSorting::SlotWeakSorting(ioSorting, ev, &mSortingRowCursor_Sorting);
+ if ( ev->Good() )
+ {
+ // mNode_Derived = morkDerived_kTableRowCursor;
+ // mNode_Derived must stay equal to kTableRowCursor
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+/*public non-poly*/ void
+morkSearchRowCursor::CloseSearchRowCursor(morkEnv* ev)
+{
+ if ( this->IsNode() )
+ {
+ // morkSorting::SlotWeakSorting((morkSorting*) 0, ev, &mSortingRowCursor_Sorting);
+ this->CloseTableRowCursor(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkSearchRowCursor::NonSearchRowCursorTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkSearchRowCursor");
+}
+
+morkUniqRowCursor*
+morkSearchRowCursor::MakeUniqCursor(morkEnv* ev)
+{
+ morkUniqRowCursor* outCursor = 0;
+
+ return outCursor;
+}
+
+#if 0
+orkinTableRowCursor*
+morkSearchRowCursor::AcquireUniqueRowCursorHandle(morkEnv* ev)
+{
+ orkinTableRowCursor* outCursor = 0;
+
+ morkUniqRowCursor* uniqCursor = this->MakeUniqCursor(ev);
+ if ( uniqCursor )
+ {
+ outCursor = uniqCursor->AcquireTableRowCursorHandle(ev);
+ uniqCursor->CutStrongRef(ev);
+ }
+ return outCursor;
+}
+#endif
+mork_bool
+morkSearchRowCursor::CanHaveDupRowMembers(morkEnv* ev)
+{
+ return morkBool_kTrue; // true is correct
+}
+
+mork_count
+morkSearchRowCursor::GetMemberCount(morkEnv* ev)
+{
+ morkTable* table = mTableRowCursor_Table;
+ if ( table )
+ return table->mTable_RowArray.mArray_Fill;
+ else
+ return 0;
+}
+
+morkRow*
+morkSearchRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+ morkRow* outRow = 0;
+ mork_pos pos = -1;
+
+ morkTable* table = mTableRowCursor_Table;
+ if ( table )
+ {
+ }
+ else
+ ev->NilPointerError();
+
+ *outPos = pos;
+ return outRow;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkSearchRowCursor.h b/db/mork/src/morkSearchRowCursor.h
new file mode 100644
index 000000000..019610643
--- /dev/null
+++ b/db/mork/src/morkSearchRowCursor.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKSEARCHROWCURSOR_
+#define _MORKSEARCHROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class morkUniqRowCursor;
+class orkinTableRowCursor;
+// #define morkDerived_kSearchRowCursor /*i*/ 0x7352 /* ascii 'sR' */
+
+class morkSearchRowCursor : public morkTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkFactory* mObject_Factory; // weak ref to suite factory
+
+ // mork_seed mCursor_Seed;
+ // mork_pos mCursor_Pos;
+ // mork_bool mCursor_DoFailOnSeedOutOfSync;
+ // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+ // morkTable* mTableRowCursor_Table; // weak ref to table
+
+public: // state is public because the entire Mork system is private
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev); // CloseSearchRowCursor()
+ virtual ~morkSearchRowCursor(); // assert that close executed earlier
+
+public: // morkSearchRowCursor construction & destruction
+ morkSearchRowCursor(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+ void CloseSearchRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkSearchRowCursor(const morkSearchRowCursor& other);
+ morkSearchRowCursor& operator=(const morkSearchRowCursor& other);
+
+public: // dynamic type identification
+ // mork_bool IsSearchRowCursor() const
+ // { return IsNode() && mNode_Derived == morkDerived_kSearchRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonSearchRowCursorTypeError(morkEnv* ev);
+
+public: // uniquify
+
+ morkUniqRowCursor* MakeUniqCursor(morkEnv* ev);
+
+public: // other search row cursor methods
+
+ virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+ virtual mork_count GetMemberCount(morkEnv* ev);
+
+#if 0
+ virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev);
+#endif
+
+ // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+ virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakSearchRowCursor(morkSearchRowCursor* me,
+ morkEnv* ev, morkSearchRowCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongSearchRowCursor(morkSearchRowCursor* me,
+ morkEnv* ev, morkSearchRowCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSEARCHROWCURSOR_ */
diff --git a/db/mork/src/morkSink.cpp b/db/mork/src/morkSink.cpp
new file mode 100644
index 000000000..1d4089c0a
--- /dev/null
+++ b/db/mork/src/morkSink.cpp
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKSINK_
+#include "morkSink.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*virtual*/ morkSink::~morkSink()
+{
+ mSink_At = 0;
+ mSink_End = 0;
+}
+
+/*virtual*/ void
+morkSpool::FlushSink(morkEnv* ev) // sync mSpool_Coil->mBuf_Fill
+{
+ morkCoil* coil = mSpool_Coil;
+ if ( coil )
+ {
+ mork_u1* body = (mork_u1*) coil->mBuf_Body;
+ if ( body )
+ {
+ mork_u1* at = mSink_At;
+ mork_u1* end = mSink_End;
+ if ( at >= body && at <= end ) // expected cursor order?
+ {
+ mork_fill fill = (mork_fill) (at - body); // current content size
+ if ( fill <= coil->mBlob_Size )
+ coil->mBuf_Fill = fill;
+ else
+ {
+ coil->BlobFillOverSizeError(ev);
+ coil->mBuf_Fill = coil->mBlob_Size; // make it safe
+ }
+ }
+ else
+ this->BadSpoolCursorOrderError(ev);
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ else
+ this->NilSpoolCoilError(ev);
+}
+
+/*virtual*/ void
+morkSpool::SpillPutc(morkEnv* ev, int c) // grow coil and write byte
+{
+ morkCoil* coil = mSpool_Coil;
+ if ( coil )
+ {
+ mork_u1* body = (mork_u1*) coil->mBuf_Body;
+ if ( body )
+ {
+ mork_u1* at = mSink_At;
+ mork_u1* end = mSink_End;
+ if ( at >= body && at <= end ) // expected cursor order?
+ {
+ mork_size size = coil->mBlob_Size;
+ mork_fill fill = (mork_fill) (at - body); // current content size
+ if ( fill <= size ) // less content than medium size?
+ {
+ coil->mBuf_Fill = fill;
+ if ( at >= end ) // need to grow the coil?
+ {
+ if ( size > 2048 ) // grow slower over 2K?
+ size += 512;
+ else
+ {
+ mork_size growth = ( size * 4 ) / 3; // grow by 33%
+ if ( growth < 64 ) // grow faster under (64 * 3)?
+ growth = 64;
+ size += growth;
+ }
+ if ( coil->GrowCoil(ev, size) ) // made coil bigger?
+ {
+ body = (mork_u1*) coil->mBuf_Body;
+ if ( body ) // have a coil body?
+ {
+ mSink_At = at = body + fill;
+ mSink_End = end = body + coil->mBlob_Size;
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ }
+ if ( ev->Good() ) // seem ready to write byte c?
+ {
+ if ( at < end ) // morkSink::Putc() would succeed?
+ {
+ *at++ = (mork_u1) c;
+ mSink_At = at;
+ coil->mBuf_Fill = fill + 1;
+ }
+ else
+ this->BadSpoolCursorOrderError(ev);
+ }
+ }
+ else // fill exceeds size
+ {
+ coil->BlobFillOverSizeError(ev);
+ coil->mBuf_Fill = coil->mBlob_Size; // make it safe
+ }
+ }
+ else
+ this->BadSpoolCursorOrderError(ev);
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ else
+ this->NilSpoolCoilError(ev);
+}
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+// public: // public non-poly morkSink methods
+
+/*virtual*/
+morkSpool::~morkSpool()
+// Zero all slots to show this sink is disabled, but destroy no memory.
+// Note it is typically unnecessary to flush this coil sink, since all
+// content is written directly to the coil without any buffering.
+{
+ mSink_At = 0;
+ mSink_End = 0;
+ mSpool_Coil = 0;
+}
+
+morkSpool::morkSpool(morkEnv* ev, morkCoil* ioCoil)
+// After installing the coil, calls Seek(ev, 0) to prepare for writing.
+: morkSink()
+, mSpool_Coil( 0 )
+{
+ mSink_At = 0; // set correctly later in Seek()
+ mSink_End = 0; // set correctly later in Seek()
+
+ if ( ev->Good() )
+ {
+ if ( ioCoil )
+ {
+ mSpool_Coil = ioCoil;
+ this->Seek(ev, /*pos*/ 0);
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+// ----- All boolean return values below are equal to ev->Good(): -----
+
+/*static*/ void
+morkSpool::BadSpoolCursorOrderError(morkEnv* ev)
+{
+ ev->NewError("bad morkSpool cursor order");
+}
+
+/*static*/ void
+morkSpool::NilSpoolCoilError(morkEnv* ev)
+{
+ ev->NewError("nil mSpool_Coil");
+}
+
+mork_bool
+morkSpool::Seek(morkEnv* ev, mork_pos inPos)
+// Changed the current write position in coil's buffer to inPos.
+// For example, to start writing the coil from scratch, use inPos==0.
+{
+ morkCoil* coil = mSpool_Coil;
+ if ( coil )
+ {
+ mork_size minSize = (mork_size) (inPos + 64);
+
+ if ( coil->mBlob_Size < minSize )
+ coil->GrowCoil(ev, minSize);
+
+ if ( ev->Good() )
+ {
+ coil->mBuf_Fill = (mork_fill) inPos;
+ mork_u1* body = (mork_u1*) coil->mBuf_Body;
+ if ( body )
+ {
+ mSink_At = body + inPos;
+ mSink_End = body + coil->mBlob_Size;
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ }
+ else
+ this->NilSpoolCoilError(ev);
+
+ return ev->Good();
+}
+
+mork_bool
+morkSpool::Write(morkEnv* ev, const void* inBuf, mork_size inSize)
+// write inSize bytes of inBuf to current position inside coil's buffer
+{
+ // This method is conceptually very similar to morkStream::Write(),
+ // and this code was written while looking at that method for clues.
+
+ morkCoil* coil = mSpool_Coil;
+ if ( coil )
+ {
+ mork_u1* body = (mork_u1*) coil->mBuf_Body;
+ if ( body )
+ {
+ if ( inBuf && inSize ) // anything to write?
+ {
+ mork_u1* at = mSink_At;
+ mork_u1* end = mSink_End;
+ if ( at >= body && at <= end ) // expected cursor order?
+ {
+ // note coil->mBuf_Fill can be stale after morkSink::Putc():
+ mork_pos fill = at - body; // current content size
+ mork_num space = (mork_num) (end - at); // space left in body
+ if ( space < inSize ) // not enough to hold write?
+ {
+ mork_size minGrowth = space + 16;
+ mork_size minSize = coil->mBlob_Size + minGrowth;
+ if ( coil->GrowCoil(ev, minSize) )
+ {
+ body = (mork_u1*) coil->mBuf_Body;
+ if ( body )
+ {
+ mSink_At = at = body + fill;
+ mSink_End = end = body + coil->mBlob_Size;
+ space = (mork_num) (end - at); // space left in body
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ }
+ if ( ev->Good() )
+ {
+ if ( space >= inSize ) // enough room to hold write?
+ {
+ MORK_MEMCPY(at, inBuf, inSize); // into body
+ mSink_At = at + inSize; // advance past written bytes
+ coil->mBuf_Fill = fill + inSize; // "flush" to fix fill
+ }
+ else
+ ev->NewError("insufficient morkSpool space");
+ }
+ }
+ else
+ this->BadSpoolCursorOrderError(ev);
+ }
+ }
+ else
+ coil->NilBufBodyError(ev);
+ }
+ else
+ this->NilSpoolCoilError(ev);
+
+ return ev->Good();
+}
+
+mork_bool
+morkSpool::PutString(morkEnv* ev, const char* inString)
+// call Write() with inBuf=inString and inSize=strlen(inString),
+// unless inString is null, in which case we then do nothing at all.
+{
+ if ( inString )
+ {
+ mork_size size = MORK_STRLEN(inString);
+ this->Write(ev, inString, size);
+ }
+ return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkSink.h b/db/mork/src/morkSink.h
new file mode 100644
index 000000000..4d501e76c
--- /dev/null
+++ b/db/mork/src/morkSink.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKSINK_
+#define _MORKSINK_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| morkSink is intended to be a very cheap buffered i/o sink which
+**| writes to bufs and other strings a single byte at a time. The
+**| basic idea is that writing a single byte has a very cheap average
+**| cost, because a polymophic function call need only occur when the
+**| space between At and End is exhausted. The rest of the time a
+**| very cheap inline method will write a byte, and then bump a pointer.
+**|
+**|| At: the current position in some sequence of bytes at which to
+**| write the next byte put into the sink. Presumably At points into
+**| the private storage of some space which is not yet filled (except
+**| when At reaches End, and the overflow must then spill). Note both
+**| At and End are zeroed in the destructor to help show that a sink
+**| is no longer usable; this is safe because At==End causes the case
+**| where SpillPutc() is called to handled an exhausted buffer space.
+**|
+**|| End: an address one byte past the last byte which can be written
+**| without needing to make a buffer larger than previously. When At
+**| and End are equal, this means there is no space to write a byte,
+**| and that some underlying buffer space must be grown before another
+**| byte can be written. Note At must always be less than or equal to
+**| End, and otherwise an important invariant has failed severely.
+**|
+**|| Buf: this original class slot has been commented out in the new
+**| and more abstract version of this sink class, but the general idea
+**| behind this slot should be explained to help design subclasses.
+**| Each subclass should provide space into which At and End can point,
+**| where End is beyond the last writable byte, and At is less than or
+**| equal to this point inside the same buffer. With some kinds of
+**| medium, such as writing to an instance of morkBlob, it is feasible
+**| to point directly into the final resting place for all the content
+**| written to the medium. Other mediums such as files, which write
+**| only through function calls, will typically need a local buffer
+**| to efficiently accumulate many bytes between such function calls.
+**|
+**|| FlushSink: this flush method should move any buffered content to
+**| its final destination. For example, for buffered writes to a
+**| string medium, where string methods are function calls and not just
+**| inline macros, it is faster to accumulate many bytes in a small
+**| local buffer and then move these en masse later in a single call.
+**|
+**|| SpillPutc: when At is greater than or equal to End, this means an
+**| underlying buffer has become full, so the buffer must be flushed
+**| before a new byte can be written. The intention is that SpillPutc()
+**| will be equivalent to calling FlushSink() followed by another call
+**| to Putc(), where the flush is expected to make At less then End once
+**| again. Except that FlushSink() need not make the underlying buffer
+**| any larger, and SpillPutc() typically must make room for more bytes.
+**| Note subclasses might want to guard against the case that both At
+**| and End are null, which happens when a sink is destroyed, which sets
+**| both these pointers to null as an indication the sink is disabled.
+|*/
+class morkSink {
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public sink virtual methods
+
+ virtual void FlushSink(morkEnv* ev) = 0;
+ virtual void SpillPutc(morkEnv* ev, int c) = 0;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // member variables
+
+ mork_u1* mSink_At; // pointer into mSink_Buf
+ mork_u1* mSink_End; // one byte past last content byte
+
+// define morkSink_kBufSize 256 /* small enough to go on stack */
+
+ // mork_u1 mSink_Buf[ morkSink_kBufSize + 4 ];
+ // want plus one for any needed end null byte; use plus 4 for alignment
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkSink methods
+
+ virtual ~morkSink(); // zero both At and End; virtual for subclasses
+ morkSink() { } // does nothing; subclasses must set At and End suitably
+
+ void Putc(morkEnv* ev, int c)
+ {
+ if ( mSink_At < mSink_End )
+ *mSink_At++ = (mork_u1) c;
+ else
+ this->SpillPutc(ev, c);
+ }
+};
+
+/*| morkSpool: an output sink that efficiently writes individual bytes
+**| or entire byte sequences to a coil instance, which grows as needed by
+**| using the heap instance in the coil to grow the internal buffer.
+**|
+**|| Note we do not "own" the coil referenced by mSpool_Coil, and
+**| the lifetime of the coil is expected to equal or exceed that of this
+**| sink by some external means. Typical usage might involve keeping an
+**| instance of morkCoil and an instance of morkSpool in the same
+**| owning parent object, which uses the spool with the associated coil.
+|*/
+class morkSpool : public morkSink { // for buffered i/o to a morkCoil
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public sink virtual methods
+
+ // when morkSink::Putc() moves mSink_At, mSpool_Coil->mBuf_Fill is wrong:
+
+ virtual void FlushSink(morkEnv* ev); // sync mSpool_Coil->mBuf_Fill
+ virtual void SpillPutc(morkEnv* ev, int c); // grow coil and write byte
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // member variables
+ morkCoil* mSpool_Coil; // destination medium for written bytes
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkSink methods
+
+ static void BadSpoolCursorOrderError(morkEnv* ev);
+ static void NilSpoolCoilError(morkEnv* ev);
+
+ virtual ~morkSpool();
+ // Zero all slots to show this sink is disabled, but destroy no memory.
+ // Note it is typically unnecessary to flush this coil sink, since all
+ // content is written directly to the coil without any buffering.
+
+ morkSpool(morkEnv* ev, morkCoil* ioCoil);
+ // After installing the coil, calls Seek(ev, 0) to prepare for writing.
+
+ // ----- All boolean return values below are equal to ev->Good(): -----
+
+ mork_bool Seek(morkEnv* ev, mork_pos inPos);
+ // Changed the current write position in coil's buffer to inPos.
+ // For example, to start writing the coil from scratch, use inPos==0.
+
+ mork_bool Write(morkEnv* ev, const void* inBuf, mork_size inSize);
+ // write inSize bytes of inBuf to current position inside coil's buffer
+
+ mork_bool PutBuf(morkEnv* ev, const morkBuf& inBuffer)
+ { return this->Write(ev, inBuffer.mBuf_Body, inBuffer.mBuf_Fill); }
+
+ mork_bool PutString(morkEnv* ev, const char* inString);
+ // call Write() with inBuf=inString and inSize=strlen(inString),
+ // unless inString is null, in which case we then do nothing at all.
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSINK_ */
diff --git a/db/mork/src/morkSpace.cpp b/db/mork/src/morkSpace.cpp
new file mode 100644
index 000000000..b19a6e737
--- /dev/null
+++ b/db/mork/src/morkSpace.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKSPACE_
+#include "morkSpace.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkSpace::CloseMorkNode(morkEnv* ev) // CloseSpace() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseSpace(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkSpace::~morkSpace() // assert CloseSpace() executed earlier
+{
+ MORK_ASSERT(SpaceScope()==0);
+ MORK_ASSERT(mSpace_Store==0);
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+//morkSpace::morkSpace(morkEnv* ev, const morkUsage& inUsage,
+// nsIMdbHeap* ioNodeHeap, const morkMapForm& inForm,
+// nsIMdbHeap* ioSlotHeap)
+//: morkNode(ev, inUsage, ioNodeHeap)
+//, mSpace_Map(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap)
+//{
+// ev->StubMethodOnlyError();
+//}
+
+/*public non-poly*/
+morkSpace::morkSpace(morkEnv* ev,
+ const morkUsage& inUsage, mork_scope inScope, morkStore* ioStore,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+: morkBead(ev, inUsage, ioHeap, inScope)
+, mSpace_Store( 0 )
+, mSpace_DoAutoIDs( morkBool_kFalse )
+, mSpace_HaveDoneAutoIDs( morkBool_kFalse )
+, mSpace_CanDirty( morkBool_kFalse ) // only when store can be dirtied
+{
+ if ( ev->Good() )
+ {
+ if ( ioStore && ioSlotHeap )
+ {
+ morkStore::SlotWeakStore(ioStore, ev, &mSpace_Store);
+
+ mSpace_CanDirty = ioStore->mStore_CanDirty;
+ if ( mSpace_CanDirty ) // this new space dirties the store?
+ this->MaybeDirtyStoreAndSpace();
+
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kSpace;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+/*public non-poly*/ void
+morkSpace::CloseSpace(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkStore::SlotWeakStore((morkStore*) 0, ev, &mSpace_Store);
+ mBead_Color = 0; // this->CloseBead();
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkSpace::NonAsciiSpaceScopeName(morkEnv* ev)
+{
+ ev->NewError("SpaceScope() > 0x7F");
+}
+
+/*static*/ void
+morkSpace::NilSpaceStoreError(morkEnv* ev)
+{
+ ev->NewError("nil mSpace_Store");
+}
+
+morkPool* morkSpace::GetSpaceStorePool() const
+{
+ return &mSpace_Store->mStore_Pool;
+}
+
+mork_bool morkSpace::MaybeDirtyStoreAndSpace()
+{
+ morkStore* store = mSpace_Store;
+ if ( store && store->mStore_CanDirty )
+ {
+ store->SetStoreDirty();
+ mSpace_CanDirty = morkBool_kTrue;
+ }
+
+ if ( mSpace_CanDirty )
+ {
+ this->SetSpaceDirty();
+ return morkBool_kTrue;
+ }
+
+ return morkBool_kFalse;
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkSpace.h b/db/mork/src/morkSpace.h
new file mode 100644
index 000000000..a0a12d116
--- /dev/null
+++ b/db/mork/src/morkSpace.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKSPACE_
+#define _MORKSPACE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkSpace_kInitialSpaceSlots /*i*/ 1024 /* default */
+#define morkDerived_kSpace /*i*/ 0x5370 /* ascii 'Sp' */
+
+/*| morkSpace:
+|*/
+class morkSpace : public morkBead { //
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+
+public: // bead color setter & getter replace obsolete member mTable_Id:
+
+ mork_tid SpaceScope() const { return mBead_Color; }
+ void SetSpaceScope(mork_scope inScope) { mBead_Color = inScope; }
+
+public: // state is public because the entire Mork system is private
+
+ morkStore* mSpace_Store; // weak ref to containing store
+
+ mork_bool mSpace_DoAutoIDs; // whether db should assign member IDs
+ mork_bool mSpace_HaveDoneAutoIDs; // whether actually auto assigned IDs
+ mork_bool mSpace_CanDirty; // changes imply the store becomes dirty?
+ mork_u1 mSpace_Pad; // pad to u4 alignment
+
+public: // more specific dirty methods for space:
+ void SetSpaceDirty() { this->SetNodeDirty(); }
+ void SetSpaceClean() { this->SetNodeClean(); }
+
+ mork_bool IsSpaceClean() const { return this->IsNodeClean(); }
+ mork_bool IsSpaceDirty() const { return this->IsNodeDirty(); }
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev); // CloseSpace() only if open
+ virtual ~morkSpace(); // assert that CloseSpace() executed earlier
+
+public: // morkMap construction & destruction
+ //morkSpace(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap,
+ // const morkMapForm& inForm, nsIMdbHeap* ioSlotHeap);
+
+ morkSpace(morkEnv* ev, const morkUsage& inUsage,mork_scope inScope,
+ morkStore* ioStore, nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioSlotHeap);
+ void CloseSpace(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsSpace() const
+ { return IsNode() && mNode_Derived == morkDerived_kSpace; }
+// } ===== end morkNode methods =====
+
+public: // other space methods
+
+ mork_bool MaybeDirtyStoreAndSpace();
+
+ static void NonAsciiSpaceScopeName(morkEnv* ev);
+ static void NilSpaceStoreError(morkEnv* ev);
+
+ morkPool* GetSpaceStorePool() const;
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakSpace(morkSpace* me,
+ morkEnv* ev, morkSpace** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongSpace(morkSpace* me,
+ morkEnv* ev, morkSpace** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSPACE_ */
diff --git a/db/mork/src/morkStore.cpp b/db/mork/src/morkStore.cpp
new file mode 100644
index 000000000..d6e70becd
--- /dev/null
+++ b/db/mork/src/morkStore.cpp
@@ -0,0 +1,2290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKFACTORY_
+#include "morkFactory.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKPORTTABLECURSOR_
+#include "morkPortTableCursor.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#include "nsCOMPtr.h"
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStore::CloseMorkNode(morkEnv* ev) // ClosePort() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseStore(ev);
+ this->MarkShut();
+ }
+}
+
+/*public non-poly*/ void
+morkStore::ClosePort(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory);
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap);
+ this->CloseObject(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+/*public virtual*/
+morkStore::~morkStore() // assert CloseStore() executed earlier
+{
+ MOZ_COUNT_DTOR(morkStore);
+ if (IsOpenNode())
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(this->IsShutNode());
+ MORK_ASSERT(mStore_File==0);
+ MORK_ASSERT(mStore_InStream==0);
+ MORK_ASSERT(mStore_OutStream==0);
+ MORK_ASSERT(mStore_Builder==0);
+ MORK_ASSERT(mStore_OidAtomSpace==0);
+ MORK_ASSERT(mStore_GroundAtomSpace==0);
+ MORK_ASSERT(mStore_GroundColumnSpace==0);
+ MORK_ASSERT(mStore_RowSpaces.IsShutNode());
+ MORK_ASSERT(mStore_AtomSpaces.IsShutNode());
+ MORK_ASSERT(mStore_Pool.IsShutNode());
+}
+
+/*public non-poly*/
+morkStore::morkStore(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance
+ morkFactory* inFactory, // the factory for this
+ nsIMdbHeap* ioPortHeap // the heap to hold all content in the port
+ )
+: morkObject(ev, inUsage, ioNodeHeap, morkColor_kNone, (morkHandle*) 0)
+, mPort_Env( ev )
+, mPort_Factory( 0 )
+, mPort_Heap( 0 )
+, mStore_OidAtomSpace( 0 )
+, mStore_GroundAtomSpace( 0 )
+, mStore_GroundColumnSpace( 0 )
+
+, mStore_File( 0 )
+, mStore_InStream( 0 )
+, mStore_Builder( 0 )
+, mStore_OutStream( 0 )
+
+, mStore_RowSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_AtomSpaces(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_Zone(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+, mStore_Pool(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioPortHeap)
+
+, mStore_CommitGroupIdentity( 0 )
+
+, mStore_FirstCommitGroupPos( 0 )
+, mStore_SecondCommitGroupPos( 0 )
+
+// disable auto-assignment of atom IDs until someone knows it is okay:
+, mStore_CanAutoAssignAtomIdentity( morkBool_kFalse )
+, mStore_CanDirty( morkBool_kFalse ) // not until the store is open
+, mStore_CanWriteIncremental( morkBool_kTrue ) // always with few exceptions
+{
+ MOZ_COUNT_CTOR(morkStore);
+ if ( ev->Good() )
+ {
+ if ( inFactory && ioPortHeap )
+ {
+ morkFactory::SlotWeakFactory(inFactory, ev, &mPort_Factory);
+ nsIMdbHeap_SlotStrongHeap(ioPortHeap, ev, &mPort_Heap);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kPort;
+ }
+ else
+ ev->NilPointerError();
+ }
+ if ( ev->Good() )
+ {
+ mNode_Derived = morkDerived_kStore;
+
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkStore, morkObject, nsIMdbStore)
+
+/*public non-poly*/ void
+morkStore::CloseStore(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+
+ nsIMdbFile* file = mStore_File;
+ file->AddRef();
+
+ morkFactory::SlotWeakFactory((morkFactory*) 0, ev, &mPort_Factory);
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mPort_Heap);
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mStore_OidAtomSpace);
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mStore_GroundAtomSpace);
+ morkAtomSpace::SlotStrongAtomSpace((morkAtomSpace*) 0, ev,
+ &mStore_GroundColumnSpace);
+ mStore_RowSpaces.CloseMorkNode(ev);
+ mStore_AtomSpaces.CloseMorkNode(ev);
+ morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder);
+
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev,
+ &mStore_File);
+
+ file->Release();
+
+ morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream);
+ morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_OutStream);
+
+ mStore_Pool.CloseMorkNode(ev);
+ mStore_Zone.CloseMorkNode(ev);
+ this->ClosePort(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+
+mork_bool morkStore::DoPreferLargeOverCompressCommit(morkEnv* ev)
+ // true when mStore_CanWriteIncremental && store has file large enough
+{
+ nsIMdbFile* file = mStore_File;
+ if ( file && mStore_CanWriteIncremental )
+ {
+ mdb_pos fileEof = 0;
+ file->Eof(ev->AsMdbEnv(), &fileEof);
+ if ( ev->Good() && fileEof > 128 )
+ return morkBool_kTrue;
+ }
+ return morkBool_kFalse;
+}
+
+mork_percent morkStore::PercentOfStoreWasted(morkEnv* ev)
+{
+ mork_percent outPercent = 0;
+ nsIMdbFile* file = mStore_File;
+
+ if ( file )
+ {
+ mork_pos firstPos = mStore_FirstCommitGroupPos;
+ mork_pos secondPos = mStore_SecondCommitGroupPos;
+ if ( firstPos || secondPos )
+ {
+ if ( firstPos < 512 && secondPos > firstPos )
+ firstPos = secondPos; // better approximation of first commit
+
+ mork_pos fileLength = 0;
+ file->Eof(ev->AsMdbEnv(), &fileLength); // end of file
+ if ( ev->Good() && fileLength > firstPos )
+ {
+ mork_size groupContent = fileLength - firstPos;
+ outPercent = ( groupContent * 100 ) / fileLength;
+ }
+ }
+ }
+ else
+ this->NilStoreFileError(ev);
+
+ return outPercent;
+}
+
+void
+morkStore::SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty)
+{
+ mStore_CanDirty = inCanDirty;
+
+ mork_change* c = 0;
+ mork_scope* key = 0; // ignore keys in maps
+
+ if ( ev->Good() )
+ {
+ morkAtomSpaceMapIter asi(ev, &mStore_AtomSpaces);
+
+ morkAtomSpace* atomSpace = 0; // old val node in the map
+
+ for ( c = asi.FirstAtomSpace(ev, key, &atomSpace); c && ev->Good();
+ c = asi.NextAtomSpace(ev, key, &atomSpace) )
+ {
+ if ( atomSpace )
+ {
+ if ( atomSpace->IsAtomSpace() )
+ atomSpace->mSpace_CanDirty = inCanDirty;
+ else
+ atomSpace->NonAtomSpaceTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+
+ if ( ev->Good() )
+ {
+ morkRowSpaceMapIter rsi(ev, &mStore_RowSpaces);
+ morkRowSpace* rowSpace = 0; // old val node in the map
+
+ for ( c = rsi.FirstRowSpace(ev, key, &rowSpace); c && ev->Good();
+ c = rsi.NextRowSpace(ev, key, &rowSpace) )
+ {
+ if ( rowSpace )
+ {
+ if ( rowSpace->IsRowSpace() )
+ rowSpace->mSpace_CanDirty = inCanDirty;
+ else
+ rowSpace->NonRowSpaceTypeError(ev);
+ }
+ }
+ }
+}
+
+void
+morkStore::RenumberAllCollectableContent(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ // do nothing currently
+}
+
+nsIMdbStore*
+morkStore::AcquireStoreHandle(morkEnv* ev)
+{
+ return this;
+}
+
+
+morkFarBookAtom*
+morkStore::StageAliasAsFarBookAtom(morkEnv* ev, const morkMid* inMid,
+ morkAtomSpace* ioSpace, mork_cscode inForm)
+{
+ if ( inMid && inMid->mMid_Buf )
+ {
+ const morkBuf* buf = inMid->mMid_Buf;
+ mork_size length = buf->mBuf_Fill;
+ if ( length <= morkBookAtom_kMaxBodySize )
+ {
+ mork_aid dummyAid = 1;
+ //mStore_BookAtom.InitMaxBookAtom(ev, *buf,
+ // inForm, ioSpace, dummyAid);
+
+ mStore_FarBookAtom.InitFarBookAtom(ev, *buf,
+ inForm, ioSpace, dummyAid);
+ return &mStore_FarBookAtom;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return (morkFarBookAtom*) 0;
+}
+
+morkFarBookAtom*
+morkStore::StageYarnAsFarBookAtom(morkEnv* ev, const mdbYarn* inYarn,
+ morkAtomSpace* ioSpace)
+{
+ if ( inYarn && inYarn->mYarn_Buf )
+ {
+ mork_size length = inYarn->mYarn_Fill;
+ if ( length <= morkBookAtom_kMaxBodySize )
+ {
+ morkBuf buf(inYarn->mYarn_Buf, length);
+ mork_aid dummyAid = 1;
+ //mStore_BookAtom.InitMaxBookAtom(ev, buf,
+ // inYarn->mYarn_Form, ioSpace, dummyAid);
+ mStore_FarBookAtom.InitFarBookAtom(ev, buf,
+ inYarn->mYarn_Form, ioSpace, dummyAid);
+ return &mStore_FarBookAtom;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return (morkFarBookAtom*) 0;
+}
+
+morkFarBookAtom*
+morkStore::StageStringAsFarBookAtom(morkEnv* ev, const char* inString,
+ mork_cscode inForm, morkAtomSpace* ioSpace)
+{
+ if ( inString )
+ {
+ mork_size length = MORK_STRLEN(inString);
+ if ( length <= morkBookAtom_kMaxBodySize )
+ {
+ morkBuf buf(inString, length);
+ mork_aid dummyAid = 1;
+ //mStore_BookAtom.InitMaxBookAtom(ev, buf, inForm, ioSpace, dummyAid);
+ mStore_FarBookAtom.InitFarBookAtom(ev, buf, inForm, ioSpace, dummyAid);
+ return &mStore_FarBookAtom;
+ }
+ }
+ else
+ ev->NilPointerError();
+
+ return (morkFarBookAtom*) 0;
+}
+
+morkAtomSpace* morkStore::LazyGetOidAtomSpace(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ if ( !mStore_OidAtomSpace )
+ {
+ }
+ return mStore_OidAtomSpace;
+}
+
+morkAtomSpace* morkStore::LazyGetGroundAtomSpace(morkEnv* ev)
+{
+ if ( !mStore_GroundAtomSpace )
+ {
+ mork_scope atomScope = morkStore_kValueSpaceScope;
+ nsIMdbHeap* heap = mPort_Heap;
+ morkAtomSpace* space = new(*heap, ev)
+ morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap);
+
+ if ( space ) // successful space creation?
+ {
+ this->MaybeDirtyStore();
+
+ mStore_GroundAtomSpace = space; // transfer strong ref to this slot
+ mStore_AtomSpaces.AddAtomSpace(ev, space);
+ }
+ }
+ return mStore_GroundAtomSpace;
+}
+
+morkAtomSpace* morkStore::LazyGetGroundColumnSpace(morkEnv* ev)
+{
+ if ( !mStore_GroundColumnSpace )
+ {
+ mork_scope atomScope = morkStore_kGroundColumnSpace;
+ nsIMdbHeap* heap = mPort_Heap;
+ morkAtomSpace* space = new(*heap, ev)
+ morkAtomSpace(ev, morkUsage::kHeap, atomScope, this, heap, heap);
+
+ if ( space ) // successful space creation?
+ {
+ this->MaybeDirtyStore();
+
+ mStore_GroundColumnSpace = space; // transfer strong ref to this slot
+ mStore_AtomSpaces.AddAtomSpace(ev, space);
+ }
+ }
+ return mStore_GroundColumnSpace;
+}
+
+morkStream* morkStore::LazyGetInStream(morkEnv* ev)
+{
+ if ( !mStore_InStream )
+ {
+ nsIMdbFile* file = mStore_File;
+ if ( file )
+ {
+ morkStream* stream = new(*mPort_Heap, ev)
+ morkStream(ev, morkUsage::kHeap, mPort_Heap, file,
+ morkStore_kStreamBufSize, /*frozen*/ morkBool_kTrue);
+ if ( stream )
+ {
+ this->MaybeDirtyStore();
+ mStore_InStream = stream; // transfer strong ref to this slot
+ }
+ }
+ else
+ this->NilStoreFileError(ev);
+ }
+ return mStore_InStream;
+}
+
+morkStream* morkStore::LazyGetOutStream(morkEnv* ev)
+{
+ if ( !mStore_OutStream )
+ {
+ nsIMdbFile* file = mStore_File;
+ if ( file )
+ {
+ morkStream* stream = new(*mPort_Heap, ev)
+ morkStream(ev, morkUsage::kHeap, mPort_Heap, file,
+ morkStore_kStreamBufSize, /*frozen*/ morkBool_kFalse);
+ if ( stream )
+ {
+ this->MaybeDirtyStore();
+ mStore_InStream = stream; // transfer strong ref to this slot
+ }
+ }
+ else
+ this->NilStoreFileError(ev);
+ }
+ return mStore_OutStream;
+}
+
+void
+morkStore::ForgetBuilder(morkEnv* ev)
+{
+ if ( mStore_Builder )
+ morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mStore_Builder);
+ if ( mStore_InStream )
+ morkStream::SlotStrongStream((morkStream*) 0, ev, &mStore_InStream);
+}
+
+morkBuilder* morkStore::LazyGetBuilder(morkEnv* ev)
+{
+ if ( !mStore_Builder )
+ {
+ morkStream* stream = this->LazyGetInStream(ev);
+ if ( stream )
+ {
+ nsIMdbHeap* heap = mPort_Heap;
+ morkBuilder* builder = new(*heap, ev)
+ morkBuilder(ev, morkUsage::kHeap, heap, stream,
+ morkBuilder_kDefaultBytesPerParseSegment, heap, this);
+ if ( builder )
+ {
+ mStore_Builder = builder; // transfer strong ref to this slot
+ }
+ }
+ }
+ return mStore_Builder;
+}
+
+morkRowSpace*
+morkStore::LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope)
+{
+ morkRowSpace* outSpace = mStore_RowSpaces.GetRowSpace(ev, inRowScope);
+ if ( !outSpace && ev->Good() ) // try to make new space?
+ {
+ nsIMdbHeap* heap = mPort_Heap;
+ outSpace = new(*heap, ev)
+ morkRowSpace(ev, morkUsage::kHeap, inRowScope, this, heap, heap);
+
+ if ( outSpace ) // successful space creation?
+ {
+ this->MaybeDirtyStore();
+
+ // note adding to node map creates its own strong ref...
+ if ( mStore_RowSpaces.AddRowSpace(ev, outSpace) )
+ outSpace->CutStrongRef(ev); // ...so we can drop our strong ref
+ }
+ }
+ return outSpace;
+}
+
+morkAtomSpace*
+morkStore::LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope)
+{
+ morkAtomSpace* outSpace = mStore_AtomSpaces.GetAtomSpace(ev, inAtomScope);
+ if ( !outSpace && ev->Good() ) // try to make new space?
+ {
+ if ( inAtomScope == morkStore_kValueSpaceScope )
+ outSpace = this->LazyGetGroundAtomSpace(ev);
+
+ else if ( inAtomScope == morkStore_kGroundColumnSpace )
+ outSpace = this->LazyGetGroundColumnSpace(ev);
+ else
+ {
+ nsIMdbHeap* heap = mPort_Heap;
+ outSpace = new(*heap, ev)
+ morkAtomSpace(ev, morkUsage::kHeap, inAtomScope, this, heap, heap);
+
+ if ( outSpace ) // successful space creation?
+ {
+ this->MaybeDirtyStore();
+
+ // note adding to node map creates its own strong ref...
+ if ( mStore_AtomSpaces.AddAtomSpace(ev, outSpace) )
+ outSpace->CutStrongRef(ev); // ...so we can drop our strong ref
+ }
+ }
+ }
+ return outSpace;
+}
+
+/*static*/ void
+morkStore::NonStoreTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkStore");
+}
+
+/*static*/ void
+morkStore::NilStoreFileError(morkEnv* ev)
+{
+ ev->NewError("nil mStore_File");
+}
+
+/*static*/ void
+morkStore::CannotAutoAssignAtomIdentityError(morkEnv* ev)
+{
+ ev->NewError("false mStore_CanAutoAssignAtomIdentity");
+}
+
+
+mork_bool
+morkStore::OpenStoreFile(morkEnv* ev, mork_bool inFrozen,
+ // const char* inFilePath,
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy)
+{
+ MORK_USED_2(inOpenPolicy,inFrozen);
+ nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File);
+
+ // if ( ev->Good() )
+ // {
+ // morkFile* file = morkFile::OpenOldFile(ev, mPort_Heap,
+ // inFilePath, inFrozen);
+ // if ( ioFile )
+ // {
+ // if ( ev->Good() )
+ // morkFile::SlotStrongFile(file, ev, &mStore_File);
+ // else
+ // file->CutStrongRef(ev);
+ //
+ // }
+ // }
+ return ev->Good();
+}
+
+mork_bool
+morkStore::CreateStoreFile(morkEnv* ev,
+ // const char* inFilePath,
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy)
+{
+ MORK_USED_1(inOpenPolicy);
+ nsIMdbFile_SlotStrongFile(ioFile, ev, &mStore_File);
+
+ return ev->Good();
+}
+
+morkAtom*
+morkStore::CopyAtom(morkEnv* ev, const morkAtom* inAtom)
+// copy inAtom (from some other store) over to this store
+{
+ morkAtom* outAtom = 0;
+ if ( inAtom )
+ {
+ mdbYarn yarn;
+ if ( morkAtom::AliasYarn(inAtom, &yarn) )
+ outAtom = this->YarnToAtom(ev, &yarn, true /* create */);
+ }
+ return outAtom;
+}
+
+morkAtom*
+morkStore::YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, bool createIfMissing /* = true */)
+{
+ morkAtom* outAtom = 0;
+ if ( ev->Good() )
+ {
+ morkAtomSpace* groundSpace = this->LazyGetGroundAtomSpace(ev);
+ if ( groundSpace )
+ {
+ morkFarBookAtom* keyAtom =
+ this->StageYarnAsFarBookAtom(ev, inYarn, groundSpace);
+
+ if ( keyAtom )
+ {
+ morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+ outAtom = map->GetAtom(ev, keyAtom);
+ if ( !outAtom && createIfMissing)
+ {
+ this->MaybeDirtyStore();
+ outAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+ }
+ }
+ else if ( ev->Good() )
+ {
+ morkBuf b(inYarn->mYarn_Buf, inYarn->mYarn_Fill);
+ morkZone* z = &mStore_Zone;
+ outAtom = mStore_Pool.NewAnonAtom(ev, b, inYarn->mYarn_Form, z);
+ }
+ }
+ }
+ return outAtom;
+}
+
+mork_bool
+morkStore::MidToOid(morkEnv* ev, const morkMid& inMid, mdbOid* outOid)
+{
+ *outOid = inMid.mMid_Oid;
+ const morkBuf* buf = inMid.mMid_Buf;
+ if ( buf && !outOid->mOid_Scope )
+ {
+ if ( buf->mBuf_Fill <= morkBookAtom_kMaxBodySize )
+ {
+ if ( buf->mBuf_Fill == 1 )
+ {
+ mork_u1* name = (mork_u1*) buf->mBuf_Body;
+ if ( name )
+ {
+ outOid->mOid_Scope = (mork_scope) *name;
+ return ev->Good();
+ }
+ }
+ morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+ if ( groundSpace )
+ {
+ mork_cscode form = 0; // default
+ mork_aid aid = 1; // dummy
+ mStore_FarBookAtom.InitFarBookAtom(ev, *buf, form, groundSpace, aid);
+ morkFarBookAtom* keyAtom = &mStore_FarBookAtom;
+ morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+ morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+ if ( bookAtom )
+ outOid->mOid_Scope = bookAtom->mBookAtom_Id;
+ else
+ {
+ this->MaybeDirtyStore();
+ bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+ if ( bookAtom )
+ {
+ outOid->mOid_Scope = bookAtom->mBookAtom_Id;
+ bookAtom->MakeCellUseForever(ev);
+ }
+ }
+ }
+ }
+ }
+ return ev->Good();
+}
+
+morkRow*
+morkStore::MidToRow(morkEnv* ev, const morkMid& inMid)
+{
+ mdbOid tempOid;
+ this->MidToOid(ev, inMid, &tempOid);
+ return this->OidToRow(ev, &tempOid);
+}
+
+morkTable*
+morkStore::MidToTable(morkEnv* ev, const morkMid& inMid)
+{
+ mdbOid tempOid;
+ this->MidToOid(ev, inMid, &tempOid);
+ return this->OidToTable(ev, &tempOid, /*metarow*/ (mdbOid*) 0);
+}
+
+mork_bool
+morkStore::MidToYarn(morkEnv* ev, const morkMid& inMid, mdbYarn* outYarn)
+{
+ mdbOid tempOid;
+ this->MidToOid(ev, inMid, &tempOid);
+ return this->OidToYarn(ev, tempOid, outYarn);
+}
+
+mork_bool
+morkStore::OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn)
+{
+ morkBookAtom* atom = 0;
+
+ morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, inOid.mOid_Scope);
+ if ( atomSpace )
+ {
+ morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+ atom = map->GetAid(ev, (mork_aid) inOid.mOid_Id);
+ }
+ morkAtom::GetYarn(atom, outYarn);
+
+ return ev->Good();
+}
+
+morkBookAtom*
+morkStore::MidToAtom(morkEnv* ev, const morkMid& inMid)
+{
+ morkBookAtom* outAtom = 0;
+ mdbOid oid;
+ if ( this->MidToOid(ev, inMid, &oid) )
+ {
+ morkAtomSpace* atomSpace = mStore_AtomSpaces.GetAtomSpace(ev, oid.mOid_Scope);
+ if ( atomSpace )
+ {
+ morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+ outAtom = map->GetAid(ev, (mork_aid) oid.mOid_Id);
+ }
+ }
+ return outAtom;
+}
+
+/*static*/ void
+morkStore::SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
+ mdbYarn* outYarn)
+{
+ MORK_USED_1(ev);
+ if ( outYarn->mYarn_Buf && outYarn->mYarn_Size ) // any space in yarn at all?
+ {
+ mork_u1* buf = (mork_u1*) outYarn->mYarn_Buf; // for byte arithmetic
+ buf[ 0 ] = (mork_u1) inToken; // write the single byte
+ outYarn->mYarn_Fill = 1;
+ outYarn->mYarn_More = 0;
+ }
+ else // just record we could not write the single byte
+ {
+ outYarn->mYarn_More = 1;
+ outYarn->mYarn_Fill = 0;
+ }
+}
+
+void
+morkStore::TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName)
+{
+ if ( inToken > morkAtomSpace_kMaxSevenBitAid )
+ {
+ morkBookAtom* atom = 0;
+ morkAtomSpace* space = mStore_GroundColumnSpace;
+ if ( space ) {
+ atom = space->mAtomSpace_AtomAids.GetAid(ev, (mork_aid) inToken);
+ }
+ morkAtom::GetYarn(atom, outTokenName);
+ }
+ else // token is an "immediate" single byte string representation?
+ this->SmallTokenToOneByteYarn(ev, inToken, outTokenName);
+}
+
+// void
+// morkStore::SyncTokenIdChange(morkEnv* ev, const morkBookAtom* inAtom,
+// const mdbOid* inOid)
+// {
+// mork_token mStore_MorkNoneToken; // token for "mork:none" // fill=9
+// mork_column mStore_CharsetToken; // token for "charset" // fill=7
+// mork_column mStore_AtomScopeToken; // token for "atomScope" // fill=9
+// mork_column mStore_RowScopeToken; // token for "rowScope" // fill=8
+// mork_column mStore_TableScopeToken; // token for "tableScope" // fill=10
+// mork_column mStore_ColumnScopeToken; // token for "columnScope" // fill=11
+// mork_kind mStore_TableKindToken; // token for "tableKind" // fill=9
+// ---------------------ruler-for-token-length-above---123456789012
+//
+// if ( inOid->mOid_Scope == morkStore_kColumnSpaceScope && inAtom->IsWeeBook() )
+// {
+// const mork_u1* body = ((const morkWeeBookAtom*) inAtom)->mWeeBookAtom_Body;
+// mork_size size = inAtom->mAtom_Size;
+//
+// if ( size >= 7 && size <= 11 )
+// {
+// if ( size == 9 )
+// {
+// if ( *body == 'm' )
+// {
+// if ( MORK_MEMCMP(body, "mork:none", 9) == 0 )
+// mStore_MorkNoneToken = inAtom->mBookAtom_Id;
+// }
+// else if ( *body == 'a' )
+// {
+// if ( MORK_MEMCMP(body, "atomScope", 9) == 0 )
+// mStore_AtomScopeToken = inAtom->mBookAtom_Id;
+// }
+// else if ( *body == 't' )
+// {
+// if ( MORK_MEMCMP(body, "tableKind", 9) == 0 )
+// mStore_TableKindToken = inAtom->mBookAtom_Id;
+// }
+// }
+// else if ( size == 7 && *body == 'c' )
+// {
+// if ( MORK_MEMCMP(body, "charset", 7) == 0 )
+// mStore_CharsetToken = inAtom->mBookAtom_Id;
+// }
+// else if ( size == 8 && *body == 'r' )
+// {
+// if ( MORK_MEMCMP(body, "rowScope", 8) == 0 )
+// mStore_RowScopeToken = inAtom->mBookAtom_Id;
+// }
+// else if ( size == 10 && *body == 't' )
+// {
+// if ( MORK_MEMCMP(body, "tableScope", 10) == 0 )
+// mStore_TableScopeToken = inAtom->mBookAtom_Id;
+// }
+// else if ( size == 11 && *body == 'c' )
+// {
+// if ( MORK_MEMCMP(body, "columnScope", 11) == 0 )
+// mStore_ColumnScopeToken = inAtom->mBookAtom_Id;
+// }
+// }
+// }
+// }
+
+morkAtom*
+morkStore::AddAlias(morkEnv* ev, const morkMid& inMid, mork_cscode inForm)
+{
+ morkBookAtom* outAtom = 0;
+ if ( ev->Good() )
+ {
+ const mdbOid* oid = &inMid.mMid_Oid;
+ morkAtomSpace* atomSpace = this->LazyGetAtomSpace(ev, oid->mOid_Scope);
+ if ( atomSpace )
+ {
+ morkFarBookAtom* keyAtom =
+ this->StageAliasAsFarBookAtom(ev, &inMid, atomSpace, inForm);
+ if ( keyAtom )
+ {
+ morkAtomAidMap* map = &atomSpace->mAtomSpace_AtomAids;
+ outAtom = map->GetAid(ev, (mork_aid) oid->mOid_Id);
+ if ( outAtom )
+ {
+ if ( !outAtom->EqualFormAndBody(ev, keyAtom) )
+ ev->NewError("duplicate alias ID");
+ }
+ else
+ {
+ this->MaybeDirtyStore();
+ keyAtom->mBookAtom_Id = oid->mOid_Id;
+ outAtom = atomSpace->MakeBookAtomCopyWithAid(ev,
+ *keyAtom, (mork_aid) oid->mOid_Id);
+
+ // if ( outAtom && outAtom->IsWeeBook() )
+ // {
+ // if ( oid->mOid_Scope == morkStore_kColumnSpaceScope )
+ // {
+ // mork_size size = outAtom->mAtom_Size;
+ // if ( size >= 7 && size <= 11 )
+ // this->SyncTokenIdChange(ev, outAtom, oid);
+ // }
+ // }
+ }
+ }
+ }
+ }
+ return outAtom;
+}
+
+#define morkStore_kMaxCopyTokenSize 512 /* if larger, cannot be copied */
+
+mork_token
+morkStore::CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore)
+// copy inToken from inStore over to this store
+{
+ mork_token outToken = 0;
+ if ( inStore == this ) // same store?
+ outToken = inToken; // just return token unchanged
+ else
+ {
+ char yarnBuf[ morkStore_kMaxCopyTokenSize ];
+ mdbYarn yarn;
+ yarn.mYarn_Buf = yarnBuf;
+ yarn.mYarn_Fill = 0;
+ yarn.mYarn_Size = morkStore_kMaxCopyTokenSize;
+ yarn.mYarn_More = 0;
+ yarn.mYarn_Form = 0;
+ yarn.mYarn_Grow = 0;
+
+ inStore->TokenToString(ev, inToken, &yarn);
+ if ( ev->Good() )
+ {
+ morkBuf buf(yarn.mYarn_Buf, yarn.mYarn_Fill);
+ outToken = this->BufToToken(ev, &buf);
+ }
+ }
+ return outToken;
+}
+
+mork_token
+morkStore::BufToToken(morkEnv* ev, const morkBuf* inBuf)
+{
+ mork_token outToken = 0;
+ if ( ev->Good() )
+ {
+ const mork_u1* s = (const mork_u1*) inBuf->mBuf_Body;
+ mork_bool nonAscii = ( *s > 0x7F );
+ mork_size length = inBuf->mBuf_Fill;
+ if ( nonAscii || length > 1 ) // more than one byte?
+ {
+ mork_cscode form = 0; // default charset
+ morkAtomSpace* space = this->LazyGetGroundColumnSpace(ev);
+ if ( space )
+ {
+ morkFarBookAtom* keyAtom = 0;
+ if ( length <= morkBookAtom_kMaxBodySize )
+ {
+ mork_aid aid = 1; // dummy
+ //mStore_BookAtom.InitMaxBookAtom(ev, *inBuf, form, space, aid);
+ mStore_FarBookAtom.InitFarBookAtom(ev, *inBuf, form, space, aid);
+ keyAtom = &mStore_FarBookAtom;
+ }
+ if ( keyAtom )
+ {
+ morkAtomBodyMap* map = &space->mAtomSpace_AtomBodies;
+ morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+ if ( bookAtom )
+ outToken = bookAtom->mBookAtom_Id;
+ else
+ {
+ this->MaybeDirtyStore();
+ bookAtom = space->MakeBookAtomCopy(ev, *keyAtom);
+ if ( bookAtom )
+ {
+ outToken = bookAtom->mBookAtom_Id;
+ bookAtom->MakeCellUseForever(ev);
+ }
+ }
+ }
+ }
+ }
+ else // only a single byte in inTokenName string:
+ outToken = *s;
+ }
+
+ return outToken;
+}
+
+mork_token
+morkStore::StringToToken(morkEnv* ev, const char* inTokenName)
+{
+ mork_token outToken = 0;
+ if ( ev->Good() )
+ {
+ const mork_u1* s = (const mork_u1*) inTokenName;
+ mork_bool nonAscii = ( *s > 0x7F );
+ if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte?
+ {
+ mork_cscode form = 0; // default charset
+ morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+ if ( groundSpace )
+ {
+ morkFarBookAtom* keyAtom =
+ this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace);
+ if ( keyAtom )
+ {
+ morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+ morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+ if ( bookAtom )
+ outToken = bookAtom->mBookAtom_Id;
+ else
+ {
+ this->MaybeDirtyStore();
+ bookAtom = groundSpace->MakeBookAtomCopy(ev, *keyAtom);
+ if ( bookAtom )
+ {
+ outToken = bookAtom->mBookAtom_Id;
+ bookAtom->MakeCellUseForever(ev);
+ }
+ }
+ }
+ }
+ }
+ else // only a single byte in inTokenName string:
+ outToken = *s;
+ }
+
+ return outToken;
+}
+
+mork_token
+morkStore::QueryToken(morkEnv* ev, const char* inTokenName)
+{
+ mork_token outToken = 0;
+ if ( ev->Good() )
+ {
+ const mork_u1* s = (const mork_u1*) inTokenName;
+ mork_bool nonAscii = ( *s > 0x7F );
+ if ( nonAscii || ( *s && s[ 1 ] ) ) // more than one byte?
+ {
+ mork_cscode form = 0; // default charset
+ morkAtomSpace* groundSpace = this->LazyGetGroundColumnSpace(ev);
+ if ( groundSpace )
+ {
+ morkFarBookAtom* keyAtom =
+ this->StageStringAsFarBookAtom(ev, inTokenName, form, groundSpace);
+ if ( keyAtom )
+ {
+ morkAtomBodyMap* map = &groundSpace->mAtomSpace_AtomBodies;
+ morkBookAtom* bookAtom = map->GetAtom(ev, keyAtom);
+ if ( bookAtom )
+ {
+ outToken = bookAtom->mBookAtom_Id;
+ bookAtom->MakeCellUseForever(ev);
+ }
+ }
+ }
+ }
+ else // only a single byte in inTokenName string:
+ outToken = *s;
+ }
+
+ return outToken;
+}
+
+mork_bool
+morkStore::HasTableKind(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_count* outTableCount)
+{
+ MORK_USED_2(inRowScope,inTableKind);
+ mork_bool outBool = morkBool_kFalse;
+ mdb_count tableCount = 0;
+
+ ev->StubMethodOnlyError();
+
+ if ( outTableCount )
+ *outTableCount = tableCount;
+ return outBool;
+}
+
+morkTable*
+morkStore::GetTableKind(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_count* outTableCount,
+ mdb_bool* outMustBeUnique)
+{
+ morkTable* outTable = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+ if ( rowSpace )
+ {
+ outTable = rowSpace->FindTableByKind(ev, inTableKind);
+ if ( outTable )
+ {
+ if ( outTableCount )
+ *outTableCount = outTable->GetRowCount();
+ if ( outMustBeUnique )
+ *outMustBeUnique = outTable->IsTableUnique();
+ }
+ }
+ }
+ return outTable;
+}
+
+morkRow*
+morkStore::FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
+ const mdbYarn* inYarn)
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inScope);
+ if ( rowSpace )
+ {
+ outRow = rowSpace->FindRow(ev, inColumn, inYarn);
+ }
+ }
+ return outRow;
+}
+
+morkRow*
+morkStore::GetRow(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+ if ( rowSpace )
+ {
+ outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid);
+ }
+ }
+ return outRow;
+}
+
+morkTable*
+morkStore::GetTable(morkEnv* ev, const mdbOid* inOid)
+{
+ morkTable* outTable = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+ if ( rowSpace )
+ {
+ outTable = rowSpace->FindTableByTid(ev, inOid->mOid_Id);
+ }
+ }
+ return outTable;
+}
+
+morkTable*
+morkStore::NewTable(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_bool inMustBeUnique,
+ const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying
+{
+ morkTable* outTable = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+ if ( rowSpace )
+ outTable = rowSpace->NewTable(ev, inTableKind, inMustBeUnique,
+ inOptionalMetaRowOid);
+ }
+ return outTable;
+}
+
+morkPortTableCursor*
+morkStore::GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind)
+{
+ morkPortTableCursor* outCursor = 0;
+ if ( ev->Good() )
+ {
+ nsIMdbHeap* heap = mPort_Heap;
+ outCursor = new(*heap, ev)
+ morkPortTableCursor(ev, morkUsage::kHeap, heap, this,
+ inRowScope, inTableKind, heap);
+ }
+ NS_IF_ADDREF(outCursor);
+ return outCursor;
+}
+
+morkRow*
+morkStore::NewRow(morkEnv* ev, mdb_scope inRowScope)
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inRowScope);
+ if ( rowSpace )
+ outRow = rowSpace->NewRow(ev);
+ }
+ return outRow;
+}
+
+morkRow*
+morkStore::NewRowWithOid(morkEnv* ev, const mdbOid* inOid)
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+ if ( rowSpace )
+ outRow = rowSpace->NewRowWithOid(ev, inOid);
+ }
+ return outRow;
+}
+
+morkRow*
+morkStore::OidToRow(morkEnv* ev, const mdbOid* inOid)
+ // OidToRow() finds old row with oid, or makes new one if not found.
+{
+ morkRow* outRow = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+ if ( rowSpace )
+ {
+ outRow = rowSpace->mRowSpace_Rows.GetOid(ev, inOid);
+ if ( !outRow && ev->Good() )
+ outRow = rowSpace->NewRowWithOid(ev, inOid);
+ }
+ }
+ return outRow;
+}
+
+morkTable*
+morkStore::OidToTable(morkEnv* ev, const mdbOid* inOid,
+ const mdbOid* inOptionalMetaRowOid) // can be nil to avoid specifying
+ // OidToTable() finds old table with oid, or makes new one if not found.
+{
+ morkTable* outTable = 0;
+ if ( ev->Good() )
+ {
+ morkRowSpace* rowSpace = this->LazyGetRowSpace(ev, inOid->mOid_Scope);
+ if ( rowSpace )
+ {
+ outTable = rowSpace->mRowSpace_Tables.GetTable(ev, inOid->mOid_Id);
+ if ( !outTable && ev->Good() )
+ {
+ mork_kind tableKind = morkStore_kNoneToken;
+ outTable = rowSpace->NewTableWithTid(ev, inOid->mOid_Id, tableKind,
+ inOptionalMetaRowOid);
+ }
+ }
+ }
+ return outTable;
+}
+
+// { ===== begin nsIMdbObject methods =====
+
+// { ----- begin ref counting for well-behaved cyclic graphs -----
+NS_IMETHODIMP
+morkStore::GetWeakRefCount(nsIMdbEnv* mev, // weak refs
+ mdb_count* outCount)
+{
+ *outCount = WeakRefsOnly();
+ return NS_OK;
+}
+NS_IMETHODIMP
+morkStore::GetStrongRefCount(nsIMdbEnv* mev, // strong refs
+ mdb_count* outCount)
+{
+ *outCount = StrongRefsOnly();
+ return NS_OK;
+}
+// ### TODO - clean up this cast, if required
+NS_IMETHODIMP
+morkStore::AddWeakRef(nsIMdbEnv* mev)
+{
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::AddWeakRef(ev));
+}
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkStore::AddStrongRef(morkEnv* mev)
+{
+ return AddRef();
+}
+#endif
+NS_IMETHODIMP_(mork_uses)
+morkStore::AddStrongRef(nsIMdbEnv* mev)
+{
+ return AddRef();
+}
+NS_IMETHODIMP
+morkStore::CutWeakRef(nsIMdbEnv* mev)
+{
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ // XXX Casting mork_refs to nsresult
+ return static_cast<nsresult>(morkNode::CutWeakRef(ev));
+}
+#ifndef _MSC_VER
+NS_IMETHODIMP_(mork_uses)
+morkStore::CutStrongRef(morkEnv* mev)
+{
+ return Release();
+}
+#endif
+NS_IMETHODIMP
+morkStore::CutStrongRef(nsIMdbEnv* mev)
+{
+ // XXX Casting nsrefcnt to nsresult
+ return static_cast<nsresult>(Release());
+}
+
+NS_IMETHODIMP
+morkStore::CloseMdbObject(nsIMdbEnv* mev)
+{
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ CloseMorkNode(ev);
+ Release();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkStore::IsOpenMdbObject(nsIMdbEnv* mev, mdb_bool* outOpen)
+{
+ *outOpen = IsOpenNode();
+ return NS_OK;
+}
+// } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbPort methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkStore::GetIsPortReadonly(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+ nsresult outErr = NS_OK;
+ mdb_bool isReadOnly = morkBool_kFalse;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ ev->StubMethodOnlyError();
+ outErr = ev->AsErr();
+ }
+ if ( outBool )
+ *outBool = isReadOnly;
+ return outErr;
+}
+
+morkEnv*
+morkStore::CanUseStore(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr) const
+{
+ morkEnv* outEnv = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if (IsStore())
+ outEnv = ev;
+ else
+ NonStoreTypeError(ev);
+ *outErr = ev->AsErr();
+ }
+ MORK_ASSERT(outEnv);
+ return outEnv;
+}
+
+NS_IMETHODIMP
+morkStore::GetIsStore(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+ MORK_USED_1(mev);
+ if ( outBool )
+ *outBool = morkBool_kTrue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkStore::GetIsStoreAndDirty(nsIMdbEnv* mev, mdb_bool* outBool)
+{
+ nsresult outErr = NS_OK;
+ mdb_bool isStoreAndDirty = morkBool_kFalse;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ ev->StubMethodOnlyError();
+ outErr = ev->AsErr();
+ }
+ if ( outBool )
+ *outBool = isStoreAndDirty;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetUsagePolicy(nsIMdbEnv* mev,
+ mdbUsagePolicy* ioUsagePolicy)
+{
+ MORK_USED_1(ioUsagePolicy);
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ ev->StubMethodOnlyError();
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SetUsagePolicy(nsIMdbEnv* mev,
+ const mdbUsagePolicy* inUsagePolicy)
+{
+ MORK_USED_1(inUsagePolicy);
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing?
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin memory policy methods -----
+NS_IMETHODIMP
+morkStore::IdleMemoryPurge( // do memory management already scheduled
+ nsIMdbEnv* mev, // context
+ mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+ nsresult outErr = NS_OK;
+ mdb_size estimatedBytesFreed = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing?
+ outErr = ev->AsErr();
+ }
+ if ( outEstimatedBytesFreed )
+ *outEstimatedBytesFreed = estimatedBytesFreed;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SessionMemoryPurge( // request specific footprint decrease
+ nsIMdbEnv* mev, // context
+ mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+ mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+ MORK_USED_1(inDesiredBytesFreed);
+ nsresult outErr = NS_OK;
+ mdb_size estimate = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing?
+ outErr = ev->AsErr();
+ }
+ if ( outEstimatedBytesFreed )
+ *outEstimatedBytesFreed = estimate;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::PanicMemoryPurge( // desperately free all possible memory
+ nsIMdbEnv* mev, // context
+ mdb_size* outEstimatedBytesFreed) // approximate bytes actually freed
+{
+ nsresult outErr = NS_OK;
+ mdb_size estimate = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing?
+ outErr = ev->AsErr();
+ }
+ if ( outEstimatedBytesFreed )
+ *outEstimatedBytesFreed = estimate;
+ return outErr;
+}
+// } ----- end memory policy methods -----
+
+// { ----- begin filepath methods -----
+NS_IMETHODIMP
+morkStore::GetPortFilePath(
+ nsIMdbEnv* mev, // context
+ mdbYarn* outFilePath, // name of file holding port content
+ mdbYarn* outFormatVersion) // file format description
+{
+ nsresult outErr = NS_OK;
+ if ( outFormatVersion )
+ outFormatVersion->mYarn_Fill = 0;
+ if ( outFilePath )
+ outFilePath->mYarn_Fill = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ if ( mStore_File )
+ mStore_File->Path(mev, outFilePath);
+ else
+ NilStoreFileError(ev);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetPortFile(
+ nsIMdbEnv* mev, // context
+ nsIMdbFile** acqFile) // acquire file used by port or store
+{
+ nsresult outErr = NS_OK;
+ if ( acqFile )
+ *acqFile = 0;
+
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+
+ if ( mStore_File )
+ {
+ if ( acqFile )
+ {
+ mStore_File->AddRef();
+ if ( ev->Good() )
+ *acqFile = mStore_File;
+ }
+ }
+ else
+ NilStoreFileError(ev);
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end filepath methods -----
+
+// { ----- begin export methods -----
+NS_IMETHODIMP
+morkStore::BestExportFormat( // determine preferred export format
+ nsIMdbEnv* mev, // context
+ mdbYarn* outFormatVersion) // file format description
+{
+ nsresult outErr = NS_OK;
+ if ( outFormatVersion )
+ outFormatVersion->mYarn_Fill = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ ev->StubMethodOnlyError();
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::CanExportToFormat( // can export content in given specific format?
+ nsIMdbEnv* mev, // context
+ const char* inFormatVersion, // file format description
+ mdb_bool* outCanExport) // whether ExportSource() might succeed
+{
+ MORK_USED_1(inFormatVersion);
+ mdb_bool canExport = morkBool_kFalse;
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ ev->StubMethodOnlyError();
+ outErr = ev->AsErr();
+ }
+ if ( outCanExport )
+ *outCanExport = canExport;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::ExportToFormat( // export content in given specific format
+ nsIMdbEnv* mev, // context
+ // const char* inFilePath, // the file to receive exported content
+ nsIMdbFile* ioFile, // destination abstract file interface
+ const char* inFormatVersion, // file format description
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental export
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the export will be finished.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ if ( ioFile && inFormatVersion && acqThumb )
+ {
+ ev->StubMethodOnlyError();
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+
+// } ----- end export methods -----
+
+// { ----- begin token methods -----
+NS_IMETHODIMP
+morkStore::TokenToString( // return a string name for an integer token
+ nsIMdbEnv* mev, // context
+ mdb_token inToken, // token for inTokenName inside this port
+ mdbYarn* outTokenName) // the type of table to access
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ TokenToString(ev, inToken, outTokenName);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::StringToToken( // return an integer token for scope name
+ nsIMdbEnv* mev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) // token for inTokenName inside this port
+ // String token zero is never used and never supported. If the port
+ // is a mutable store, then StringToToken() to create a new
+ // association of inTokenName with a new integer token if possible.
+ // But a readonly port will return zero for an unknown scope name.
+{
+ nsresult outErr = NS_OK;
+ mdb_token token = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ token = StringToToken(ev, inTokenName);
+ outErr = ev->AsErr();
+ }
+ if ( outToken )
+ *outToken = token;
+ return outErr;
+}
+
+
+NS_IMETHODIMP
+morkStore::QueryToken( // like StringToToken(), but without adding
+ nsIMdbEnv* mev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) // token for inTokenName inside this port
+ // QueryToken() will return a string token if one already exists,
+ // but unlike StringToToken(), will not assign a new token if not
+ // already in use.
+{
+ nsresult outErr = NS_OK;
+ mdb_token token = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ token = QueryToken(ev, inTokenName);
+ outErr = ev->AsErr();
+ }
+ if ( outToken )
+ *outToken = token;
+ return outErr;
+}
+
+
+// } ----- end token methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkStore::HasRow( // contains a row with the specified oid?
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_bool* outHasRow) // whether GetRow() might succeed
+{
+ nsresult outErr = NS_OK;
+ mdb_bool hasRow = morkBool_kFalse;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = GetRow(ev, inOid);
+ if ( row )
+ hasRow = morkBool_kTrue;
+
+ outErr = ev->AsErr();
+ }
+ if ( outHasRow )
+ *outHasRow = hasRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetRow( // access one row with specific oid
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ nsIMdbRow** acqRow) // acquire specific row (or null)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = GetRow(ev, inOid);
+ if ( row && ev->Good() )
+ outRow = row->AcquireRowHandle(ev, this);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetRowRefCount( // get number of tables that contain a row
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_count* outRefCount) // number of tables containing inRowKey
+{
+ nsresult outErr = NS_OK;
+ mdb_count count = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = GetRow(ev, inOid);
+ if ( row && ev->Good() )
+ count = row->mRow_GcUses;
+
+ outErr = ev->AsErr();
+ }
+ if ( outRefCount )
+ *outRefCount = count;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::FindRow(nsIMdbEnv* mev, // search for row with matching cell
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_column inColumn, // the column to search (and maintain an index)
+ const mdbYarn* inTargetCellValue, // cell value for which to search
+ mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+ nsIMdbRow** acqRow) // acquire matching row (or nil for no match)
+ // FindRow() searches for one row that has a cell in column inColumn with
+ // a contained value with the same form (i.e. charset) and is byte-wise
+ // identical to the blob described by yarn inTargetCellValue. Both content
+ // and form of the yarn must be an exact match to find a matching row.
+ //
+ // (In other words, both a yarn's blob bytes and form are significant. The
+ // form is not expected to vary in columns used for identity anyway. This
+ // is intended to make the cost of FindRow() cheaper for MDB implementors,
+ // since any cell value atomization performed internally must necessarily
+ // make yarn form significant in order to avoid data loss in atomization.)
+ //
+ // FindRow() can lazily create an index on attribute inColumn for all rows
+ // with that attribute in row space scope inRowScope, so that subsequent
+ // calls to FindRow() will perform faster. Such an index might or might
+ // not be persistent (but this seems desirable if it is cheap to do so).
+ // Note that lazy index creation in readonly DBs is not very feasible.
+ //
+ // This FindRow() interface assumes that attribute inColumn is effectively
+ // an alternative means of unique identification for a row in a rowspace,
+ // so correct behavior is only guaranteed when no duplicates for this col
+ // appear in the given set of rows. (If more than one row has the same cell
+ // value in this column, no more than one will be found; and cutting one of
+ // two duplicate rows can cause the index to assume no other such row lives
+ // in the row space, so future calls return nil for negative search results
+ // even though some duplicate row might still live within the rowspace.)
+ //
+ // In other words, the FindRow() implementation is allowed to assume simple
+ // hash tables mapping unqiue column keys to associated row values will be
+ // sufficient, where any duplication is not recorded because only one copy
+ // of a given key need be remembered. Implementors are not required to sort
+ // all rows by the specified column.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = 0;
+ rowOid.mOid_Id = (mdb_id) -1;
+
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = FindRow(ev, inRowScope, inColumn, inTargetCellValue);
+ if ( row && ev->Good() )
+ {
+ rowOid = row->mRow_Oid;
+ if ( acqRow )
+ outRow = row->AcquireRowHandle(ev, this);
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ if ( outRowOid )
+ *outRowOid = rowOid;
+
+ return outErr;
+}
+
+// } ----- end row methods -----
+
+// { ----- begin table methods -----
+NS_IMETHODIMP
+morkStore::HasTable( // supports a table with the specified oid?
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ mdb_bool* outHasTable) // whether GetTable() might succeed
+{
+ nsresult outErr = NS_OK;
+ mork_bool hasTable = morkBool_kFalse;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = GetTable(ev, inOid);
+ if ( table )
+ hasTable = morkBool_kTrue;
+
+ outErr = ev->AsErr();
+ }
+ if ( outHasTable )
+ *outHasTable = hasTable;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetTable( // access one table with specific oid
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ nsIMdbTable** acqTable) // acquire specific table (or null)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = GetTable(ev, inOid);
+ if ( table && ev->Good() )
+ outTable = table->AcquireTableHandle(ev);
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::HasTableKind( // supports a table of the specified type?
+ nsIMdbEnv* mev, // context
+ mdb_scope inRowScope, // rid scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outSupportsTable) // whether GetTableKind() might succeed
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ *outSupportsTable = HasTableKind(ev, inRowScope,
+ inTableKind, outTableCount);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetTableKind( // access one (random) table of specific type
+ nsIMdbEnv* mev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outMustBeUnique, // whether port can hold only one of these
+ nsIMdbTable** acqTable) // acquire scoped collection of rows
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = GetTableKind(ev, inRowScope,
+ inTableKind, outTableCount, outMustBeUnique);
+ if ( table && ev->Good() )
+ outTable = table->AcquireTableHandle(ev);
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::GetPortTableCursor( // get cursor for all tables of specific type
+ nsIMdbEnv* mev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ nsIMdbPortTableCursor** acqCursor) // all such tables in the port
+{
+ nsresult outErr = NS_OK;
+ nsIMdbPortTableCursor* outCursor = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkPortTableCursor* cursor =
+ GetPortTableCursor(ev, inRowScope,
+ inTableKind);
+ if ( cursor && ev->Good() )
+ outCursor = cursor;
+
+ outErr = ev->AsErr();
+ }
+ if ( acqCursor )
+ *acqCursor = outCursor;
+ return outErr;
+}
+// } ----- end table methods -----
+
+// { ----- begin commit methods -----
+
+NS_IMETHODIMP
+morkStore::ShouldCompress( // store wastes at least inPercentWaste?
+ nsIMdbEnv* mev, // context
+ mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+ mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+ mdb_bool* outShould) // true when about inPercentWaste% is wasted
+{
+ mdb_percent actualWaste = 0;
+ mdb_bool shouldCompress = morkBool_kFalse;
+ nsresult outErr = NS_OK;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ actualWaste = PercentOfStoreWasted(ev);
+ if ( inPercentWaste > 100 )
+ inPercentWaste = 100;
+ shouldCompress = ( actualWaste >= inPercentWaste );
+ outErr = ev->AsErr();
+ }
+ if ( outActualWaste )
+ *outActualWaste = actualWaste;
+ if ( outShould )
+ *outShould = shouldCompress;
+ return outErr;
+}
+
+
+// } ===== end nsIMdbPort methods =====
+
+NS_IMETHODIMP
+morkStore::NewTable( // make one new table of specific type
+ nsIMdbEnv* mev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) // acquire scoped collection of rows
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev = this->CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = NewTable(ev, inRowScope,
+ inTableKind, inMustBeUnique, inOptionalMetaRowOid);
+ if ( table && ev->Good() )
+ outTable = table->AcquireTableHandle(ev);
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::NewTableWithOid( // make one new table of specific type
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // caller assigned oid
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) // acquire scoped collection of rows
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkTable* table = OidToTable(ev, inOid,
+ inOptionalMetaRowOid);
+ if ( table && ev->Good() )
+ {
+ table->mTable_Kind = inTableKind;
+ if ( inMustBeUnique )
+ table->SetTableUnique();
+ outTable = table->AcquireTableHandle(ev);
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+// } ----- end table methods -----
+
+// { ----- begin row scope methods -----
+NS_IMETHODIMP
+morkStore::RowScopeHasAssignedIds(nsIMdbEnv* mev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+ NS_ASSERTION(false, " not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::SetCallerAssignedIds(nsIMdbEnv* mev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+ NS_ASSERTION(false, " not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::SetStoreAssignedIds(nsIMdbEnv* mev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) // nonzero if store db assigned specified
+{
+ NS_ASSERTION(false, " not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end row scope methods -----
+
+// { ----- begin row methods -----
+NS_IMETHODIMP
+morkStore::NewRowWithOid(nsIMdbEnv* mev, // new row w/ caller assigned oid
+ const mdbOid* inOid, // caller assigned oid
+ nsIMdbRow** acqRow) // create new row
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = NewRowWithOid(ev, inOid);
+ if ( row && ev->Good() )
+ outRow = row->AcquireRowHandle(ev, this);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::NewRow(nsIMdbEnv* mev, // new row with db assigned oid
+ mdb_scope inRowScope, // row scope for row ids
+ nsIMdbRow** acqRow) // create new row
+// Note this row must be added to some table or cell child before the
+// store is closed in order to make this row persist across sessions.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ morkRow* row = NewRow(ev, inRowScope);
+ if ( row && ev->Good() )
+ outRow = row->AcquireRowHandle(ev, this);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+// } ----- end row methods -----
+
+// { ----- begin inport/export methods -----
+NS_IMETHODIMP
+morkStore::ImportContent( // import content from port
+ nsIMdbEnv* mev, // context
+ mdb_scope inRowScope, // scope for rows (or zero for all?)
+ nsIMdbPort* ioPort, // the port with content to add to store
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental import
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the import will be finished.
+{
+ NS_ASSERTION(false, " not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStore::ImportFile( // import content from port
+ nsIMdbEnv* mev, // context
+ nsIMdbFile* ioFile, // the file with content to add to store
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental import
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the import will be finished.
+{
+ NS_ASSERTION(false, " not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end inport/export methods -----
+
+// { ----- begin hinting methods -----
+NS_IMETHODIMP
+morkStore::ShareAtomColumnsHint( // advise re shared col content atomizing
+ nsIMdbEnv* mev, // context
+ mdb_scope inScopeHint, // zero, or suggested shared namespace
+ const mdbColumnSet* inColumnSet) // cols desired tokenized together
+{
+ MORK_USED_2(inColumnSet,inScopeHint);
+ nsresult outErr = NS_OK;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing for a hint method
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::AvoidAtomColumnsHint( // advise col w/ poor atomizing prospects
+ nsIMdbEnv* mev, // context
+ const mdbColumnSet* inColumnSet) // cols with poor atomizing prospects
+{
+ MORK_USED_1(inColumnSet);
+ nsresult outErr = NS_OK;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ // ev->StubMethodOnlyError(); // okay to do nothing for a hint method
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end hinting methods -----
+
+// { ----- begin commit methods -----
+NS_IMETHODIMP
+morkStore::LargeCommit( // save important changes if at all possible
+ nsIMdbEnv* mev, // context
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished. Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+
+ morkThumb* thumb = 0;
+ // morkFile* file = store->mStore_File;
+ if ( DoPreferLargeOverCompressCommit(ev) )
+ {
+ thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this);
+ }
+ else
+ {
+ mork_bool doCollect = morkBool_kFalse;
+ thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+ }
+
+ if ( thumb )
+ {
+ outThumb = thumb;
+ thumb->AddRef();
+ }
+
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::SessionCommit( // save all changes if large commits delayed
+ nsIMdbEnv* mev, // context
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished. Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+
+ morkThumb* thumb = 0;
+ if ( DoPreferLargeOverCompressCommit(ev) )
+ {
+ thumb = morkThumb::Make_LargeCommit(ev, mPort_Heap, this);
+ }
+ else
+ {
+ mork_bool doCollect = morkBool_kFalse;
+ thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+ }
+
+ if ( thumb )
+ {
+ outThumb = thumb;
+ thumb->AddRef();
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkStore::CompressCommit( // commit and make db smaller if possible
+ nsIMdbEnv* mev, // context
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental commit
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the commit will be finished. Note the store is effectively write
+// locked until commit is finished or canceled through the thumb instance.
+// Until the commit is done, the store will report it has readonly status.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbThumb* outThumb = 0;
+ morkEnv* ev = CanUseStore(mev, /*inMutable*/ morkBool_kFalse, &outErr);
+ if ( ev )
+ {
+ mork_bool doCollect = morkBool_kFalse;
+ morkThumb* thumb = morkThumb::Make_CompressCommit(ev, mPort_Heap, this, doCollect);
+ if ( thumb )
+ {
+ outThumb = thumb;
+ thumb->AddRef();
+ mStore_CanWriteIncremental = morkBool_kTrue;
+ }
+
+ outErr = ev->AsErr();
+ }
+ if ( acqThumb )
+ *acqThumb = outThumb;
+ return outErr;
+}
+
+// } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkStore.h b/db/mork/src/morkStore.h
new file mode 100644
index 000000000..fe548245e
--- /dev/null
+++ b/db/mork/src/morkStore.h
@@ -0,0 +1,768 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKSTORE_
+#define _MORKSTORE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKPOOL_
+#include "morkPool.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kPort /*i*/ 0x7054 /* ascii 'pT' */
+
+#define morkDerived_kStore /*i*/ 0x7354 /* ascii 'sT' */
+
+/*| kGroundColumnSpace: we use the 'column space' as the default scope
+**| for grounding column name IDs, and this is also the default scope for
+**| all other explicitly tokenized strings.
+|*/
+#define morkStore_kGroundColumnSpace 'c' /* for mStore_GroundColumnSpace*/
+#define morkStore_kColumnSpaceScope ((mork_scope) 'c') /*kGroundColumnSpace*/
+#define morkStore_kValueSpaceScope ((mork_scope) 'v')
+#define morkStore_kStreamBufSize (8 * 1024) /* okay buffer size */
+
+#define morkStore_kReservedColumnCount 0x20 /* for well-known columns */
+
+#define morkStore_kNoneToken ((mork_token) 'n')
+#define morkStore_kFormColumn ((mork_column) 'f')
+#define morkStore_kAtomScopeColumn ((mork_column) 'a')
+#define morkStore_kRowScopeColumn ((mork_column) 'r')
+#define morkStore_kMetaScope ((mork_scope) 'm')
+#define morkStore_kKindColumn ((mork_column) 'k')
+#define morkStore_kStatusColumn ((mork_column) 's')
+
+/*| morkStore:
+|*/
+class morkStore : public morkObject, public nsIMdbStore {
+
+public: // state is public because the entire Mork system is private
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ morkEnv* mPort_Env; // non-refcounted env which created port
+ morkFactory* mPort_Factory; // weak ref to suite factory
+ nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+
+ void ClosePort(morkEnv* ev); // called by CloseMorkNode();
+
+public: // dynamic type identification
+ mork_bool IsPort() const
+ { return IsNode() && mNode_Derived == morkDerived_kPort; }
+// } ===== end morkNode methods =====
+
+public: // other port methods
+
+ // { ----- begin attribute methods -----
+// NS_IMETHOD IsFrozenMdbObject(nsIMdbEnv* ev, mdb_bool* outIsReadonly);
+ // same as nsIMdbPort::GetIsPortReadonly() when this object is inside a port.
+ // } ----- end attribute methods -----
+
+ // { ----- begin factory methods -----
+// NS_IMETHOD GetMdbFactory(nsIMdbEnv* ev, nsIMdbFactory** acqFactory);
+ // } ----- end factory methods -----
+
+ // { ----- begin ref counting for well-behaved cyclic graphs -----
+ NS_IMETHOD GetWeakRefCount(nsIMdbEnv* ev, // weak refs
+ mdb_count* outCount) override;
+ NS_IMETHOD GetStrongRefCount(nsIMdbEnv* ev, // strong refs
+ mdb_count* outCount) override;
+
+ NS_IMETHOD AddWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) AddStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD_(mork_uses) AddStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CutWeakRef(nsIMdbEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ NS_IMETHOD_(mork_uses) CutStrongRef(morkEnv* ev) override;
+#endif
+ NS_IMETHOD CutStrongRef(nsIMdbEnv* ev) override;
+
+ NS_IMETHOD CloseMdbObject(nsIMdbEnv* ev) override; // called at strong refs zero
+ NS_IMETHOD IsOpenMdbObject(nsIMdbEnv* ev, mdb_bool* outOpen) override;
+ // } ----- end ref counting -----
+
+// } ===== end nsIMdbObject methods =====
+
+// { ===== begin nsIMdbPort methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetIsPortReadonly(nsIMdbEnv* ev, mdb_bool* outBool) override;
+ NS_IMETHOD GetIsStore(nsIMdbEnv* ev, mdb_bool* outBool) override;
+ NS_IMETHOD GetIsStoreAndDirty(nsIMdbEnv* ev, mdb_bool* outBool) override;
+
+ NS_IMETHOD GetUsagePolicy(nsIMdbEnv* ev,
+ mdbUsagePolicy* ioUsagePolicy) override;
+
+ NS_IMETHOD SetUsagePolicy(nsIMdbEnv* ev,
+ const mdbUsagePolicy* inUsagePolicy) override;
+ // } ----- end attribute methods -----
+
+ // { ----- begin memory policy methods -----
+ NS_IMETHOD IdleMemoryPurge( // do memory management already scheduled
+ nsIMdbEnv* ev, // context
+ mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed
+
+ NS_IMETHOD SessionMemoryPurge( // request specific footprint decrease
+ nsIMdbEnv* ev, // context
+ mdb_size inDesiredBytesFreed, // approximate number of bytes wanted
+ mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed
+
+ NS_IMETHOD PanicMemoryPurge( // desperately free all possible memory
+ nsIMdbEnv* ev, // context
+ mdb_size* outEstimatedBytesFreed) override; // approximate bytes actually freed
+ // } ----- end memory policy methods -----
+
+ // { ----- begin filepath methods -----
+ NS_IMETHOD GetPortFilePath(
+ nsIMdbEnv* ev, // context
+ mdbYarn* outFilePath, // name of file holding port content
+ mdbYarn* outFormatVersion) override; // file format description
+
+ NS_IMETHOD GetPortFile(
+ nsIMdbEnv* ev, // context
+ nsIMdbFile** acqFile) override; // acquire file used by port or store
+ // } ----- end filepath methods -----
+
+ // { ----- begin export methods -----
+ NS_IMETHOD BestExportFormat( // determine preferred export format
+ nsIMdbEnv* ev, // context
+ mdbYarn* outFormatVersion) override; // file format description
+
+ NS_IMETHOD
+ CanExportToFormat( // can export content in given specific format?
+ nsIMdbEnv* ev, // context
+ const char* inFormatVersion, // file format description
+ mdb_bool* outCanExport) override; // whether ExportSource() might succeed
+
+ NS_IMETHOD ExportToFormat( // export content in given specific format
+ nsIMdbEnv* ev, // context
+ // const char* inFilePath, // the file to receive exported content
+ nsIMdbFile* ioFile, // destination abstract file interface
+ const char* inFormatVersion, // file format description
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental export
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the export will be finished.
+
+ // } ----- end export methods -----
+
+ // { ----- begin token methods -----
+ NS_IMETHOD TokenToString( // return a string name for an integer token
+ nsIMdbEnv* ev, // context
+ mdb_token inToken, // token for inTokenName inside this port
+ mdbYarn* outTokenName) override; // the type of table to access
+
+ NS_IMETHOD StringToToken( // return an integer token for scope name
+ nsIMdbEnv* ev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) override; // token for inTokenName inside this port
+
+ // String token zero is never used and never supported. If the port
+ // is a mutable store, then StringToToken() to create a new
+ // association of inTokenName with a new integer token if possible.
+ // But a readonly port will return zero for an unknown scope name.
+
+ NS_IMETHOD QueryToken( // like StringToToken(), but without adding
+ nsIMdbEnv* ev, // context
+ const char* inTokenName, // Latin1 string to tokenize if possible
+ mdb_token* outToken) override; // token for inTokenName inside this port
+
+ // QueryToken() will return a string token if one already exists,
+ // but unlike StringToToken(), will not assign a new token if not
+ // already in use.
+
+ // } ----- end token methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD HasRow( // contains a row with the specified oid?
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_bool* outHasRow) override; // whether GetRow() might succeed
+
+ NS_IMETHOD GetRowRefCount( // get number of tables that contain a row
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ mdb_count* outRefCount) override; // number of tables containing inRowKey
+
+ NS_IMETHOD GetRow( // access one row with specific oid
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical row oid
+ nsIMdbRow** acqRow) override; // acquire specific row (or null)
+
+ NS_IMETHOD FindRow(nsIMdbEnv* ev, // search for row with matching cell
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_column inColumn, // the column to search (and maintain an index)
+ const mdbYarn* inTargetCellValue, // cell value for which to search
+ mdbOid* outRowOid, // out row oid on match (or {0,-1} for no match)
+ nsIMdbRow** acqRow) override; // acquire matching row (or nil for no match)
+ // can be null if you only want the oid
+ // FindRow() searches for one row that has a cell in column inColumn with
+ // a contained value with the same form (i.e. charset) and is byte-wise
+ // identical to the blob described by yarn inTargetCellValue. Both content
+ // and form of the yarn must be an exact match to find a matching row.
+ //
+ // (In other words, both a yarn's blob bytes and form are significant. The
+ // form is not expected to vary in columns used for identity anyway. This
+ // is intended to make the cost of FindRow() cheaper for MDB implementors,
+ // since any cell value atomization performed internally must necessarily
+ // make yarn form significant in order to avoid data loss in atomization.)
+ //
+ // FindRow() can lazily create an index on attribute inColumn for all rows
+ // with that attribute in row space scope inRowScope, so that subsequent
+ // calls to FindRow() will perform faster. Such an index might or might
+ // not be persistent (but this seems desirable if it is cheap to do so).
+ // Note that lazy index creation in readonly DBs is not very feasible.
+ //
+ // This FindRow() interface assumes that attribute inColumn is effectively
+ // an alternative means of unique identification for a row in a rowspace,
+ // so correct behavior is only guaranteed when no duplicates for this col
+ // appear in the given set of rows. (If more than one row has the same cell
+ // value in this column, no more than one will be found; and cutting one of
+ // two duplicate rows can cause the index to assume no other such row lives
+ // in the row space, so future calls return nil for negative search results
+ // even though some duplicate row might still live within the rowspace.)
+ //
+ // In other words, the FindRow() implementation is allowed to assume simple
+ // hash tables mapping unqiue column keys to associated row values will be
+ // sufficient, where any duplication is not recorded because only one copy
+ // of a given key need be remembered. Implementors are not required to sort
+ // all rows by the specified column.
+ // } ----- end row methods -----
+
+ // { ----- begin table methods -----
+ NS_IMETHOD HasTable( // supports a table with the specified oid?
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ mdb_bool* outHasTable) override; // whether GetTable() might succeed
+
+ NS_IMETHOD GetTable( // access one table with specific oid
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // hypothetical table oid
+ nsIMdbTable** acqTable) override; // acquire specific table (or null)
+
+ NS_IMETHOD HasTableKind( // supports a table of the specified type?
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // rid scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outSupportsTable) override; // whether GetTableKind() might succeed
+
+ NS_IMETHOD GetTableKind( // access one (random) table of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_count* outTableCount, // current number of such tables
+ mdb_bool* outMustBeUnique, // whether port can hold only one of these
+ nsIMdbTable** acqTable) override; // acquire scoped collection of rows
+
+ NS_IMETHOD
+ GetPortTableCursor( // get cursor for all tables of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ nsIMdbPortTableCursor** acqCursor) override; // all such tables in the port
+ // } ----- end table methods -----
+
+
+ // { ----- begin commit methods -----
+
+ NS_IMETHOD ShouldCompress( // store wastes at least inPercentWaste?
+ nsIMdbEnv* ev, // context
+ mdb_percent inPercentWaste, // 0..100 percent file size waste threshold
+ mdb_percent* outActualWaste, // 0..100 percent of file actually wasted
+ mdb_bool* outShould) override; // true when about inPercentWaste% is wasted
+ // ShouldCompress() returns true if the store can determine that the file
+ // will shrink by an estimated percentage of inPercentWaste% (or more) if
+ // CompressCommit() is called, because that percentage of the file seems
+ // to be recoverable free space. The granularity is only in terms of
+ // percentage points, and any value over 100 is considered equal to 100.
+ //
+ // If a store only has an approximate idea how much space might be saved
+ // during a compress, then a best guess should be made. For example, the
+ // Mork implementation might keep track of how much file space began with
+ // text content before the first updating transaction, and then consider
+ // all content following the start of the first transaction as potentially
+ // wasted space if it is all updates and not just new content. (This is
+ // a safe assumption in the sense that behavior will stabilize on a low
+ // estimate of wastage after a commit removes all transaction updates.)
+ //
+ // Some db formats might attempt to keep a very accurate reckoning of free
+ // space size, so a very accurate determination can be made. But other db
+ // formats might have difficulty determining size of free space, and might
+ // require some lengthy calculation to answer. This is the reason for
+ // passing in the percentage threshold of interest, so that such lengthy
+ // computations can terminate early as soon as at least inPercentWaste is
+ // found, so that the entire file need not be groveled when unnecessary.
+ // However, we hope implementations will always favor fast but imprecise
+ // heuristic answers instead of extremely slow but very precise answers.
+ //
+ // If the outActualWaste parameter is non-nil, it will be used to return
+ // the actual estimated space wasted as a percentage of file size. (This
+ // parameter is provided so callers need not call repeatedly with altered
+ // inPercentWaste values to isolate the actual wastage figure.) Note the
+ // actual wastage figure returned can exactly equal inPercentWaste even
+ // when this grossly underestimates the real figure involved, if the db
+ // finds it very expensive to determine the extent of wastage after it is
+ // known to at least exceed inPercentWaste. Note we expect that whenever
+ // outShould returns true, that outActualWaste returns >= inPercentWaste.
+ //
+ // The effect of different inPercentWaste values is not very uniform over
+ // the permitted range. For example, 50 represents 50% wastage, or a file
+ // that is about double what it should be ideally. But 99 represents 99%
+ // wastage, or a file that is about ninety-nine times as big as it should
+ // be ideally. In the smaller direction, 25 represents 25% wastage, or
+ // a file that is only 33% larger than it should be ideally.
+ //
+ // Callers can determine what policy they want to use for considering when
+ // a file holds too much wasted space, and express this as a percentage
+ // of total file size to pass as in the inPercentWaste parameter. A zero
+ // likely returns always trivially true, and 100 always trivially false.
+ // The great majority of callers are expected to use values from 25 to 75,
+ // since most plausible thresholds for compressing might fall between the
+ // extremes of 133% of ideal size and 400% of ideal size. (Presumably the
+ // larger a file gets, the more important the percentage waste involved, so
+ // a sliding scale for compress thresholds might use smaller numbers for
+ // much bigger file sizes.)
+
+ // } ----- end commit methods -----
+
+// } ===== end nsIMdbPort methods =====
+
+// { ===== begin nsIMdbStore methods =====
+
+ // { ----- begin table methods -----
+ NS_IMETHOD NewTable( // make one new table of specific type
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) override; // acquire scoped collection of rows
+
+ NS_IMETHOD NewTableWithOid( // make one new table of specific type
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // caller assigned oid
+ mdb_kind inTableKind, // the type of table to access
+ mdb_bool inMustBeUnique, // whether store can hold only one of these
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ nsIMdbTable** acqTable) override; // acquire scoped collection of rows
+ // } ----- end table methods -----
+
+ // { ----- begin row scope methods -----
+ NS_IMETHOD RowScopeHasAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified
+
+ NS_IMETHOD SetCallerAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified
+
+ NS_IMETHOD SetStoreAssignedIds(nsIMdbEnv* ev,
+ mdb_scope inRowScope, // row scope for row ids
+ mdb_bool* outCallerAssigned, // nonzero if caller assigned specified
+ mdb_bool* outStoreAssigned) override; // nonzero if store db assigned specified
+ // } ----- end row scope methods -----
+
+ // { ----- begin row methods -----
+ NS_IMETHOD NewRowWithOid(nsIMdbEnv* ev, // new row w/ caller assigned oid
+ const mdbOid* inOid, // caller assigned oid
+ nsIMdbRow** acqRow) override; // create new row
+
+ NS_IMETHOD NewRow(nsIMdbEnv* ev, // new row with db assigned oid
+ mdb_scope inRowScope, // row scope for row ids
+ nsIMdbRow** acqRow) override; // create new row
+ // Note this row must be added to some table or cell child before the
+ // store is closed in order to make this row persist across sessions.
+
+ // } ----- end row methods -----
+
+ // { ----- begin inport/export methods -----
+ NS_IMETHOD ImportContent( // import content from port
+ nsIMdbEnv* ev, // context
+ mdb_scope inRowScope, // scope for rows (or zero for all?)
+ nsIMdbPort* ioPort, // the port with content to add to store
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the import will be finished.
+
+ NS_IMETHOD ImportFile( // import content from port
+ nsIMdbEnv* ev, // context
+ nsIMdbFile* ioFile, // the file with content to add to store
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental import
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the import will be finished.
+ // } ----- end inport/export methods -----
+
+ // { ----- begin hinting methods -----
+ NS_IMETHOD
+ ShareAtomColumnsHint( // advise re shared column content atomizing
+ nsIMdbEnv* ev, // context
+ mdb_scope inScopeHint, // zero, or suggested shared namespace
+ const mdbColumnSet* inColumnSet) override; // cols desired tokenized together
+
+ NS_IMETHOD
+ AvoidAtomColumnsHint( // advise column with poor atomizing prospects
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) override; // cols with poor atomizing prospects
+ // } ----- end hinting methods -----
+
+ // { ----- begin commit methods -----
+ NS_IMETHOD LargeCommit( // save important changes if at all possible
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ NS_IMETHOD SessionCommit( // save all changes if large commits delayed
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ NS_IMETHOD
+ CompressCommit( // commit and make db physically smaller if possible
+ nsIMdbEnv* ev, // context
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental commit
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the commit will be finished. Note the store is effectively write
+ // locked until commit is finished or canceled through the thumb instance.
+ // Until the commit is done, the store will report it has readonly status.
+
+ // } ----- end commit methods -----
+
+// } ===== end nsIMdbStore methods =====
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakPort(morkPort* me,
+ morkEnv* ev, morkPort** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongPort(morkPort* me,
+ morkEnv* ev, morkPort** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+// public: // slots inherited from morkPort (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkEnv* mPort_Env; // non-refcounted env which created port
+ // morkFactory* mPort_Factory; // weak ref to suite factory
+ // nsIMdbHeap* mPort_Heap; // heap in which this port allocs objects
+
+public: // state is public because the entire Mork system is private
+
+// mStore_OidAtomSpace might be unnecessary; I don't remember why I wanted it.
+ morkAtomSpace* mStore_OidAtomSpace; // ground atom space for oids
+ morkAtomSpace* mStore_GroundAtomSpace; // ground atom space for scopes
+ morkAtomSpace* mStore_GroundColumnSpace; // ground column space for scopes
+
+ nsIMdbFile* mStore_File; // the file containing Mork text
+ morkStream* mStore_InStream; // stream using file used by the builder
+ morkBuilder* mStore_Builder; // to parse Mork text and build structures
+
+ morkStream* mStore_OutStream; // stream using file used by the writer
+
+ morkRowSpaceMap mStore_RowSpaces; // maps mork_scope -> morkSpace
+ morkAtomSpaceMap mStore_AtomSpaces; // maps mork_scope -> morkSpace
+
+ morkZone mStore_Zone;
+
+ morkPool mStore_Pool;
+
+ // we alloc a max size book atom to reuse space for atom map key searches:
+ // morkMaxBookAtom mStore_BookAtom; // staging area for atom map searches
+
+ morkFarBookAtom mStore_FarBookAtom; // staging area for atom map searches
+
+ // GroupIdentity should be one more than largest seen in a parsed db file:
+ mork_gid mStore_CommitGroupIdentity; // transaction ID number
+
+ // group positions are used to help compute PercentOfStoreWasted():
+ mork_pos mStore_FirstCommitGroupPos; // start of first group
+ mork_pos mStore_SecondCommitGroupPos; // start of second group
+ // If the first commit group is very near the start of the file (say less
+ // than 512 bytes), then we might assume the file started nearly empty and
+ // that most of the first group is not wasted. In that case, the pos of
+ // the second commit group might make a better estimate of the start of
+ // transaction space that might represent wasted file space. That's why
+ // we support fields for both first and second commit group positions.
+ //
+ // We assume that a zero in either group pos means that the slot has not
+ // yet been given a valid value, since the file will always start with a
+ // tag, and a commit group cannot actually start at position zero.
+ //
+ // Either or both the first or second commit group positions might be
+ // supplied by either morkWriter (while committing) or morkBuilder (while
+ // parsing), since either reading or writing the file might encounter the
+ // first transaction groups which came into existence either in the past
+ // or in the very recent present.
+
+ mork_bool mStore_CanAutoAssignAtomIdentity;
+ mork_bool mStore_CanDirty; // changes imply the store becomes dirty?
+ mork_u1 mStore_CanWriteIncremental; // compress not required?
+ mork_u1 mStore_Pad; // for u4 alignment
+
+ // mStore_CanDirty should be FALSE when parsing a file while building the
+ // content going into the store, because such data structure modifications
+ // are actuallly in sync with the file. So content read from a file must
+ // be clean with respect to the file. After a file is finished parsing,
+ // the mStore_CanDirty slot should become TRUE, so that any additional
+ // changes at runtime cause structures to be marked dirty with respect to
+ // the file which must later be updated with changes during a commit.
+ //
+ // It might also make sense to set mStore_CanDirty to FALSE while a commit
+ // is in progress, lest some internal transformations make more content
+ // appear dirty when it should not. So anyone modifying content during a
+ // commit should think about the intended significance regarding dirty.
+
+public: // more specific dirty methods for store:
+ void SetStoreDirty() { this->SetNodeDirty(); }
+ void SetStoreClean() { this->SetNodeClean(); }
+
+ mork_bool IsStoreClean() const { return this->IsNodeClean(); }
+ mork_bool IsStoreDirty() const { return this->IsNodeDirty(); }
+
+public: // setting dirty based on CanDirty:
+
+ void MaybeDirtyStore()
+ { if ( mStore_CanDirty ) this->SetStoreDirty(); }
+
+public: // space waste analysis
+
+ mork_percent PercentOfStoreWasted(morkEnv* ev);
+
+public: // setting store and all subspaces canDirty:
+
+ void SetStoreAndAllSpacesCanDirty(morkEnv* ev, mork_bool inCanDirty);
+
+public: // building an atom inside mStore_FarBookAtom from a char* string
+
+ morkFarBookAtom* StageAliasAsFarBookAtom(morkEnv* ev,
+ const morkMid* inMid, morkAtomSpace* ioSpace, mork_cscode inForm);
+
+ morkFarBookAtom* StageYarnAsFarBookAtom(morkEnv* ev,
+ const mdbYarn* inYarn, morkAtomSpace* ioSpace);
+
+ morkFarBookAtom* StageStringAsFarBookAtom(morkEnv* ev,
+ const char* inString, mork_cscode inForm, morkAtomSpace* ioSpace);
+
+public: // determining whether incremental writing is a good use of time:
+
+ mork_bool DoPreferLargeOverCompressCommit(morkEnv* ev);
+ // true when mStore_CanWriteIncremental && store has file large enough
+
+public: // lazy creation of members and nested row or atom spaces
+
+ morkAtomSpace* LazyGetOidAtomSpace(morkEnv* ev);
+ morkAtomSpace* LazyGetGroundAtomSpace(morkEnv* ev);
+ morkAtomSpace* LazyGetGroundColumnSpace(morkEnv* ev);
+
+ morkStream* LazyGetInStream(morkEnv* ev);
+ morkBuilder* LazyGetBuilder(morkEnv* ev);
+ void ForgetBuilder(morkEnv* ev);
+
+ morkStream* LazyGetOutStream(morkEnv* ev);
+
+ morkRowSpace* LazyGetRowSpace(morkEnv* ev, mdb_scope inRowScope);
+ morkAtomSpace* LazyGetAtomSpace(morkEnv* ev, mdb_scope inAtomScope);
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseStore() only if open
+
+public: // morkStore construction & destruction
+ morkStore(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap, // the heap (if any) for this node instance
+ morkFactory* inFactory, // the factory for this
+ nsIMdbHeap* ioPortHeap // the heap to hold all content in the port
+ );
+ void CloseStore(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkStore(const morkStore& other);
+ morkStore& operator=(const morkStore& other);
+ virtual ~morkStore(); // assert that CloseStore() executed earlier
+
+public: // dynamic type identification
+ morkEnv* CanUseStore(nsIMdbEnv* mev, mork_bool inMutable,
+ nsresult* outErr) const;
+ mork_bool IsStore() const
+ { return IsNode() && mNode_Derived == morkDerived_kStore; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonStoreTypeError(morkEnv* ev);
+ static void NilStoreFileError(morkEnv* ev);
+ static void CannotAutoAssignAtomIdentityError(morkEnv* ev);
+
+public: // store utilities
+
+ morkAtom* YarnToAtom(morkEnv* ev, const mdbYarn* inYarn, bool createIfMissing = true);
+ morkAtom* AddAlias(morkEnv* ev, const morkMid& inMid,
+ mork_cscode inForm);
+
+public: // other store methods
+
+ void RenumberAllCollectableContent(morkEnv* ev);
+
+ nsIMdbStore* AcquireStoreHandle(morkEnv* ev); // mObject_Handle
+
+ morkPool* StorePool() { return &mStore_Pool; }
+
+ mork_bool OpenStoreFile(morkEnv* ev, // return value equals ev->Good()
+ mork_bool inFrozen,
+ // const char* inFilePath,
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy);
+
+ mork_bool CreateStoreFile(morkEnv* ev, // return value equals ev->Good()
+ // const char* inFilePath,
+ nsIMdbFile* ioFile, // db abstract file interface
+ const mdbOpenPolicy* inOpenPolicy);
+
+ morkAtom* CopyAtom(morkEnv* ev, const morkAtom* inAtom);
+ // copy inAtom (from some other store) over to this store
+
+ mork_token CopyToken(morkEnv* ev, mdb_token inToken, morkStore* inStore);
+ // copy inToken from inStore over to this store
+
+ mork_token BufToToken(morkEnv* ev, const morkBuf* inBuf);
+ mork_token StringToToken(morkEnv* ev, const char* inTokenName);
+ mork_token QueryToken(morkEnv* ev, const char* inTokenName);
+ void TokenToString(morkEnv* ev, mdb_token inToken, mdbYarn* outTokenName);
+
+ mork_bool MidToOid(morkEnv* ev, const morkMid& inMid,
+ mdbOid* outOid);
+ mork_bool OidToYarn(morkEnv* ev, const mdbOid& inOid, mdbYarn* outYarn);
+ mork_bool MidToYarn(morkEnv* ev, const morkMid& inMid,
+ mdbYarn* outYarn);
+
+ morkBookAtom* MidToAtom(morkEnv* ev, const morkMid& inMid);
+ morkRow* MidToRow(morkEnv* ev, const morkMid& inMid);
+ morkTable* MidToTable(morkEnv* ev, const morkMid& inMid);
+
+ morkRow* OidToRow(morkEnv* ev, const mdbOid* inOid);
+ // OidToRow() finds old row with oid, or makes new one if not found.
+
+ morkTable* OidToTable(morkEnv* ev, const mdbOid* inOid,
+ const mdbOid* inOptionalMetaRowOid);
+ // OidToTable() finds old table with oid, or makes new one if not found.
+
+ static void SmallTokenToOneByteYarn(morkEnv* ev, mdb_token inToken,
+ mdbYarn* outYarn);
+
+ mork_bool HasTableKind(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_count* outTableCount);
+
+ morkTable* GetTableKind(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_count* outTableCount,
+ mdb_bool* outMustBeUnique);
+
+ morkRow* FindRow(morkEnv* ev, mdb_scope inScope, mdb_column inColumn,
+ const mdbYarn* inTargetCellValue);
+
+ morkRow* GetRow(morkEnv* ev, const mdbOid* inOid);
+ morkTable* GetTable(morkEnv* ev, const mdbOid* inOid);
+
+ morkTable* NewTable(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind, mdb_bool inMustBeUnique,
+ const mdbOid* inOptionalMetaRowOid);
+
+ morkPortTableCursor* GetPortTableCursor(morkEnv* ev, mdb_scope inRowScope,
+ mdb_kind inTableKind) ;
+
+ morkRow* NewRowWithOid(morkEnv* ev, const mdbOid* inOid);
+ morkRow* NewRow(morkEnv* ev, mdb_scope inRowScope);
+
+ morkThumb* MakeCompressCommitThumb(morkEnv* ev, mork_bool inDoCollect);
+
+public: // commit related methods
+
+ mork_bool MarkAllStoreContentDirty(morkEnv* ev);
+ // MarkAllStoreContentDirty() visits every object in the store and marks
+ // them dirty, including every table, row, cell, and atom. The return
+ // equals ev->Good(), to show whether any error happened. This method is
+ // intended for use in the beginning of a "compress commit" which writes
+ // all store content, whether dirty or not. We dirty everything first so
+ // that later iterations over content can mark things clean as they are
+ // written, and organize the process of serialization so that objects are
+ // written only at need (because of being dirty).
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakStore(morkStore* me,
+ morkEnv* ev, morkStore** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongStore(morkStore* me,
+ morkEnv* ev, morkStore** ioSlot)
+ {
+ morkStore* store = *ioSlot;
+ if ( me != store )
+ {
+ if ( store )
+ {
+ // what if this nulls out the ev and causes asserts?
+ // can we move this after the CutStrongRef()?
+ *ioSlot = 0;
+ store->Release();
+ }
+ if ( me && me->AddRef() )
+ *ioSlot = me;
+ }
+ }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSTORE_ */
+
diff --git a/db/mork/src/morkStream.cpp b/db/mork/src/morkStream.cpp
new file mode 100644
index 000000000..64cd1011e
--- /dev/null
+++ b/db/mork/src/morkStream.cpp
@@ -0,0 +1,859 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkStream::CloseMorkNode(morkEnv* ev) // CloseStream() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseStream(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkStream::~morkStream() // assert CloseStream() executed earlier
+{
+ MORK_ASSERT(mStream_ContentFile==0);
+ MORK_ASSERT(mStream_Buf==0);
+}
+
+/*public non-poly*/
+morkStream::morkStream(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap,
+ nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen)
+: morkFile(ev, inUsage, ioHeap, ioHeap)
+, mStream_At( 0 )
+, mStream_ReadEnd( 0 )
+, mStream_WriteEnd( 0 )
+
+, mStream_ContentFile( 0 )
+
+, mStream_Buf( 0 )
+, mStream_BufSize( inBufSize )
+, mStream_BufPos( 0 )
+, mStream_Dirty( morkBool_kFalse )
+, mStream_HitEof( morkBool_kFalse )
+{
+ if ( ev->Good() )
+ {
+ if ( inBufSize < morkStream_kMinBufSize )
+ mStream_BufSize = inBufSize = morkStream_kMinBufSize;
+ else if ( inBufSize > morkStream_kMaxBufSize )
+ mStream_BufSize = inBufSize = morkStream_kMaxBufSize;
+
+ if ( ioContentFile && ioHeap )
+ {
+ // if ( ioContentFile->FileFrozen() ) // forced to be readonly?
+ // inFrozen = morkBool_kTrue; // override the input value
+
+ nsIMdbFile_SlotStrongFile(ioContentFile, ev, &mStream_ContentFile);
+ if ( ev->Good() )
+ {
+ mork_u1* buf = 0;
+ ioHeap->Alloc(ev->AsMdbEnv(), inBufSize, (void**) &buf);
+ if ( buf )
+ {
+ mStream_At = mStream_Buf = buf;
+
+ if ( !inFrozen )
+ {
+ // physical buffer end never moves:
+ mStream_WriteEnd = buf + inBufSize;
+ }
+ else
+ mStream_WriteEnd = 0; // no writing is allowed
+
+ if ( inFrozen )
+ {
+ // logical buffer end starts at Buf with no content:
+ mStream_ReadEnd = buf;
+ this->SetFileFrozen(inFrozen);
+ }
+ else
+ mStream_ReadEnd = 0; // no reading is allowed
+
+ this->SetFileActive(morkBool_kTrue);
+ this->SetFileIoOpen(morkBool_kTrue);
+ }
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kStream;
+ }
+ }
+ else ev->NilPointerError();
+ }
+}
+
+/*public non-poly*/ void
+morkStream::CloseStream(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mStream_ContentFile);
+ nsIMdbHeap* heap = mFile_SlotHeap;
+ mork_u1* buf = mStream_Buf;
+ mStream_Buf = 0;
+
+ if ( heap && buf )
+ heap->Free(ev->AsMdbEnv(), buf);
+
+ this->CloseFile(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+#define morkStream_kSpacesPerIndent 1 /* one space per indent */
+#define morkStream_kMaxIndentDepth 70 /* max indent of 70 space bytes */
+static const char morkStream_kSpaces[] // next line to ease length perception
+= " ";
+// 123456789_123456789_123456789_123456789_123456789_123456789_123456789_
+// morkStream_kSpaces above must contain (at least) 70 spaces (ASCII 0x20)
+
+mork_size
+morkStream::PutIndent(morkEnv* ev, mork_count inDepth)
+ // PutIndent() puts a linebreak, and then
+ // "indents" by inDepth, and returns the line length after indentation.
+{
+ mork_size outLength = 0;
+ nsIMdbEnv *mev = ev->AsMdbEnv();
+ if ( ev->Good() )
+ {
+ this->PutLineBreak(ev);
+ if ( ev->Good() )
+ {
+ outLength = inDepth;
+ mdb_size bytesWritten;
+ if ( inDepth )
+ this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+ }
+ }
+ return outLength;
+}
+
+mork_size
+morkStream::PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth)
+ // PutByteThenIndent() puts the byte, then a linebreak, and then
+ // "indents" by inDepth, and returns the line length after indentation.
+{
+ mork_size outLength = 0;
+ nsIMdbEnv *mev = ev->AsMdbEnv();
+
+ if ( inDepth > morkStream_kMaxIndentDepth )
+ inDepth = morkStream_kMaxIndentDepth;
+
+ this->Putc(ev, inByte);
+ if ( ev->Good() )
+ {
+ this->PutLineBreak(ev);
+ if ( ev->Good() )
+ {
+ outLength = inDepth;
+ mdb_size bytesWritten;
+ if ( inDepth )
+ this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+ }
+ }
+ return outLength;
+}
+
+mork_size
+morkStream::PutStringThenIndent(morkEnv* ev,
+ const char* inString, mork_count inDepth)
+// PutStringThenIndent() puts the string, then a linebreak, and then
+// "indents" by inDepth, and returns the line length after indentation.
+{
+ mork_size outLength = 0;
+ mdb_size bytesWritten;
+ nsIMdbEnv *mev = ev->AsMdbEnv();
+
+ if ( inDepth > morkStream_kMaxIndentDepth )
+ inDepth = morkStream_kMaxIndentDepth;
+
+ if ( inString )
+ {
+ mork_size length = MORK_STRLEN(inString);
+ if ( length && ev->Good() ) // any bytes to write?
+ this->Write(mev, inString, length, &bytesWritten);
+ }
+
+ if ( ev->Good() )
+ {
+ this->PutLineBreak(ev);
+ if ( ev->Good() )
+ {
+ outLength = inDepth;
+ if ( inDepth )
+ this->Write(mev, morkStream_kSpaces, inDepth, &bytesWritten);
+ }
+ }
+ return outLength;
+}
+
+mork_size
+morkStream::PutString(morkEnv* ev, const char* inString)
+{
+ nsIMdbEnv *mev = ev->AsMdbEnv();
+ mork_size outSize = 0;
+ mdb_size bytesWritten;
+ if ( inString )
+ {
+ outSize = MORK_STRLEN(inString);
+ if ( outSize && ev->Good() ) // any bytes to write?
+ {
+ this->Write(mev, inString, outSize, &bytesWritten);
+ }
+ }
+ return outSize;
+}
+
+mork_size
+morkStream::PutStringThenNewline(morkEnv* ev, const char* inString)
+ // PutStringThenNewline() returns total number of bytes written.
+{
+ nsIMdbEnv *mev = ev->AsMdbEnv();
+ mork_size outSize = 0;
+ mdb_size bytesWritten;
+ if ( inString )
+ {
+ outSize = MORK_STRLEN(inString);
+ if ( outSize && ev->Good() ) // any bytes to write?
+ {
+ this->Write(mev, inString, outSize, &bytesWritten);
+ if ( ev->Good() )
+ outSize += this->PutLineBreak(ev);
+ }
+ }
+ return outSize;
+}
+
+mork_size
+morkStream::PutByteThenNewline(morkEnv* ev, int inByte)
+ // PutByteThenNewline() returns total number of bytes written.
+{
+ mork_size outSize = 1; // one for the following byte
+ this->Putc(ev, inByte);
+ if ( ev->Good() )
+ outSize += this->PutLineBreak(ev);
+ return outSize;
+}
+
+mork_size
+morkStream::PutLineBreak(morkEnv* ev)
+{
+#if defined(MORK_MAC)
+
+ this->Putc(ev, mork_kCR);
+ return 1;
+
+#else
+# if defined(MORK_WIN)
+
+ this->Putc(ev, mork_kCR);
+ this->Putc(ev, mork_kLF);
+ return 2;
+
+# else
+# ifdef MORK_UNIX
+
+ this->Putc(ev, mork_kLF);
+ return 1;
+
+# endif /* MORK_UNIX */
+# endif /* MORK_WIN */
+#endif /* MORK_MAC */
+}
+// ````` ````` ````` ````` ````` ````` ````` `````
+// public: // virtual morkFile methods
+
+
+NS_IMETHODIMP
+morkStream::Steal(nsIMdbEnv* mev, nsIMdbFile* ioThief)
+ // Steal: tell this file to close any associated i/o stream in the file
+ // system, because the file ioThief intends to reopen the file in order
+ // to provide the MDB implementation with more exotic file access than is
+ // offered by the nsIMdbFile alone. Presumably the thief knows enough
+ // from Path() in order to know which file to reopen. If Steal() is
+ // successful, this file should probably delegate all future calls to
+ // the nsIMdbFile interface down to the thief files, so that even after
+ // the file has been stolen, it can still be read, written, or forcibly
+ // closed (by a call to CloseMdbObject()).
+{
+ MORK_USED_1(ioThief);
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ ev->StubMethodOnlyError();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStream::BecomeTrunk(nsIMdbEnv* mev)
+ // If this file is a file version branch created by calling AcquireBud(),
+ // BecomeTrunk() causes this file's content to replace the original
+ // file's content, typically by assuming the original file's identity.
+{
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ ev->StubMethodOnlyError();
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkStream::AcquireBud(nsIMdbEnv* mev, nsIMdbHeap* ioHeap, nsIMdbFile **acqBud)
+ // AcquireBud() starts a new "branch" version of the file, empty of content,
+ // so that a new version of the file can be written. This new file
+ // can later be told to BecomeTrunk() the original file, so the branch
+ // created by budding the file will replace the original file. Some
+ // file subclasses might initially take the unsafe but expedient
+ // approach of simply truncating this file down to zero length, and
+ // then returning the same morkFile pointer as this, with an extra
+ // reference count increment. Note that the caller of AcquireBud() is
+ // expected to eventually call CutStrongRef() on the returned file
+ // in order to release the strong reference. High quality versions
+ // of morkFile subclasses will create entirely new files which later
+ // are renamed to become the old file, so that better transactional
+ // behavior is exhibited by the file, so crashes protect old files.
+ // Note that AcquireBud() is an illegal operation on readonly files.
+{
+ MORK_USED_1(ioHeap);
+ morkFile* outFile = 0;
+ nsIMdbFile* file = mStream_ContentFile;
+ morkEnv *ev = morkEnv::FromMdbEnv(mev);
+ if ( this->IsOpenAndActiveFile() && file )
+ {
+ // figure out how this interacts with buffering and mStream_WriteEnd:
+ ev->StubMethodOnlyError();
+ }
+ else this->NewFileDownError(ev);
+
+ *acqBud = outFile;
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+mork_pos
+morkStream::Length(morkEnv* ev) const // eof
+{
+ mork_pos outPos = 0;
+
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenAndActiveFile() && file )
+ {
+ mork_pos contentEof = 0;
+ file->Eof(ev->AsMdbEnv(), &contentEof);
+ if ( ev->Good() )
+ {
+ if ( mStream_WriteEnd ) // this stream supports writing?
+ {
+ // the local buffer might have buffered content past content eof
+ if ( ev->Good() ) // no error happened during Length() above?
+ {
+ mork_u1* at = mStream_At;
+ mork_u1* buf = mStream_Buf;
+ if ( at >= buf ) // expected cursor order?
+ {
+ mork_pos localContent = mStream_BufPos + (at - buf);
+ if ( localContent > contentEof ) // buffered past eof?
+ contentEof = localContent; // return new logical eof
+
+ outPos = contentEof;
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ }
+ else
+ outPos = contentEof; // frozen files get length from content file
+ }
+ }
+ else this->NewFileDownError(ev);
+
+ return outPos;
+}
+
+void morkStream::NewBadCursorSlotsError(morkEnv* ev) const
+{ ev->NewError("bad stream cursor slots"); }
+
+void morkStream::NewNullStreamBufferError(morkEnv* ev) const
+{ ev->NewError("null stream buffer"); }
+
+void morkStream::NewCantReadSinkError(morkEnv* ev) const
+{ ev->NewError("can not read stream sink"); }
+
+void morkStream::NewCantWriteSourceError(morkEnv* ev) const
+{ ev->NewError("can not write stream source"); }
+
+void morkStream::NewPosBeyondEofError(morkEnv* ev) const
+{ ev->NewError("stream pos beyond eof"); }
+
+void morkStream::NewBadCursorOrderError(morkEnv* ev) const
+{ ev->NewError("bad stream cursor order"); }
+
+NS_IMETHODIMP
+morkStream::Tell(nsIMdbEnv* mdbev, mork_pos *aOutPos) const
+{
+ nsresult rv = NS_OK;
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+
+ NS_ENSURE_ARG_POINTER(aOutPos);
+
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenAndActiveFile() && file )
+ {
+ mork_u1* buf = mStream_Buf;
+ mork_u1* at = mStream_At;
+
+ mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly
+ mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
+
+ if ( writeEnd )
+ {
+ if ( buf && at >= buf && at <= writeEnd )
+ {
+ *aOutPos = mStream_BufPos + (at - buf);
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ else if ( readEnd )
+ {
+ if ( buf && at >= buf && at <= readEnd )
+ {
+ *aOutPos = mStream_BufPos + (at - buf);
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ }
+ else this->NewFileDownError(ev);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStream::Read(nsIMdbEnv* mdbev, void* outBuf, mork_size inSize, mork_size *aOutSize)
+{
+ NS_ENSURE_ARG_POINTER(aOutSize);
+ // First we satisfy the request from buffered bytes, if any. Then
+ // if additional bytes are needed, we satisfy these by direct reads
+ // from the content file without any local buffering (but we still need
+ // to adjust the buffer position to reflect the current i/o point).
+
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+ nsresult rv = NS_OK;
+
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenAndActiveFile() && file )
+ {
+ mork_u1* end = mStream_ReadEnd; // byte after last buffered byte
+ if ( end ) // file is open for read access?
+ {
+ if ( inSize ) // caller wants any output?
+ {
+ mork_u1* sink = (mork_u1*) outBuf; // where we plan to write bytes
+ if ( sink ) // caller passed good buffer address?
+ {
+ mork_u1* at = mStream_At;
+ mork_u1* buf = mStream_Buf;
+ if ( at >= buf && at <= end ) // expected cursor order?
+ {
+ mork_num remaining = (mork_num) (end - at); // bytes left in buffer
+
+ mork_num quantum = inSize; // number of bytes to copy
+ if ( quantum > remaining ) // more than buffer content?
+ quantum = remaining; // restrict to buffered bytes
+
+ if ( quantum ) // any bytes left in the buffer?
+ {
+ MORK_MEMCPY(sink, at, quantum); // from buffer bytes
+
+ at += quantum; // advance past read bytes
+ mStream_At = at;
+ *aOutSize += quantum; // this much copied so far
+
+ sink += quantum; // in case we need to copy more
+ inSize -= quantum; // filled this much of request
+ mStream_HitEof = morkBool_kFalse;
+ }
+
+ if ( inSize ) // we still need to read more content?
+ {
+ // We need to read more bytes directly from the
+ // content file, without local buffering. We have
+ // exhausted the local buffer, so we need to show
+ // it is now empty, and adjust the current buf pos.
+
+ mork_num posDelta = (mork_num) (at - buf); // old buf content
+ mStream_BufPos += posDelta; // past now empty buf
+
+ mStream_At = mStream_ReadEnd = buf; // empty buffer
+
+ // file->Seek(ev, mStream_BufPos); // set file pos
+ // if ( ev->Good() ) // no seek error?
+ // {
+ // }
+
+ mork_num actual = 0;
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ file->Get(menv, sink, inSize, mStream_BufPos, &actual);
+ if ( ev->Good() ) // no read error?
+ {
+ if ( actual )
+ {
+ *aOutSize += actual;
+ mStream_BufPos += actual;
+ mStream_HitEof = morkBool_kFalse;
+ }
+ else if ( !*aOutSize )
+ mStream_HitEof = morkBool_kTrue;
+ }
+ }
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ else this->NewNullStreamBufferError(ev);
+ }
+ }
+ else this->NewCantReadSinkError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ if ( ev->Bad() )
+ *aOutSize = 0;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStream::Seek(nsIMdbEnv * mdbev, mork_pos inPos, mork_pos *aOutPos)
+{
+ NS_ENSURE_ARG_POINTER(aOutPos);
+ morkEnv *ev = morkEnv::FromMdbEnv(mdbev);
+ *aOutPos = 0;
+ nsresult rv = NS_OK;
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+ {
+ mork_u1* at = mStream_At; // current position in buffer
+ mork_u1* buf = mStream_Buf; // beginning of buffer
+ mork_u1* readEnd = mStream_ReadEnd; // nonzero only if readonly
+ mork_u1* writeEnd = mStream_WriteEnd; // nonzero only if writeonly
+
+ if ( writeEnd ) // file is mutable/writeonly?
+ {
+ if ( mStream_Dirty ) // need to commit buffer changes?
+ this->Flush(mdbev);
+
+ if ( ev->Good() ) // no errors during flush or earlier?
+ {
+ if ( at == buf ) // expected post flush cursor value?
+ {
+ if ( mStream_BufPos != inPos ) // need to change pos?
+ {
+ mork_pos eof = 0;
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ file->Eof(menv, &eof);
+ if ( ev->Good() ) // no errors getting length?
+ {
+ if ( inPos <= eof ) // acceptable new position?
+ {
+ mStream_BufPos = inPos; // new stream position
+ *aOutPos = inPos;
+ }
+ else this->NewPosBeyondEofError(ev);
+ }
+ }
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ }
+ else if ( readEnd ) // file is frozen/readonly?
+ {
+ if ( at >= buf && at <= readEnd ) // expected cursor order?
+ {
+ mork_pos eof = 0;
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ file->Eof(menv, &eof);
+ if ( ev->Good() ) // no errors getting length?
+ {
+ if ( inPos <= eof ) // acceptable new position?
+ {
+ *aOutPos = inPos;
+ mStream_BufPos = inPos; // new stream position
+ mStream_At = mStream_ReadEnd = buf; // empty buffer
+ if ( inPos == eof ) // notice eof reached?
+ mStream_HitEof = morkBool_kTrue;
+ }
+ else this->NewPosBeyondEofError(ev);
+ }
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+
+ }
+ else this->NewFileDownError(ev);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+morkStream::Write(nsIMdbEnv* menv, const void* inBuf, mork_size inSize, mork_size *aOutSize)
+{
+ mork_num outActual = 0;
+ morkEnv *ev = morkEnv::FromMdbEnv(menv);
+
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenActiveAndMutableFile() && file )
+ {
+ mork_u1* end = mStream_WriteEnd; // byte after last buffered byte
+ if ( end ) // file is open for write access?
+ {
+ if ( inSize ) // caller provided any input?
+ {
+ const mork_u1* source = (const mork_u1*) inBuf; // from where
+ if ( source ) // caller passed good buffer address?
+ {
+ mork_u1* at = mStream_At;
+ mork_u1* buf = mStream_Buf;
+ if ( at >= buf && at <= end ) // expected cursor order?
+ {
+ mork_num space = (mork_num) (end - at); // space left in buffer
+
+ mork_num quantum = inSize; // number of bytes to write
+ if ( quantum > space ) // more than buffer size?
+ quantum = space; // restrict to avail space
+
+ if ( quantum ) // any space left in the buffer?
+ {
+ mStream_Dirty = morkBool_kTrue; // to ensure later flush
+ MORK_MEMCPY(at, source, quantum); // into buffer
+
+ mStream_At += quantum; // advance past written bytes
+ outActual += quantum; // this much written so far
+
+ source += quantum; // in case we need to write more
+ inSize -= quantum; // filled this much of request
+ }
+
+ if ( inSize ) // we still need to write more content?
+ {
+ // We need to write more bytes directly to the
+ // content file, without local buffering. We have
+ // exhausted the local buffer, so we need to flush
+ // it and empty it, and adjust the current buf pos.
+ // After flushing, if the rest of the write fits
+ // inside the buffer, we will put bytes into the
+ // buffer rather than write them to content file.
+
+ if ( mStream_Dirty )
+ this->Flush(menv); // will update mStream_BufPos
+
+ at = mStream_At;
+ if ( at < buf || at > end ) // bad cursor?
+ this->NewBadCursorOrderError(ev);
+
+ if ( ev->Good() ) // no errors?
+ {
+ space = (mork_num) (end - at); // space left in buffer
+ if ( space > inSize ) // write to buffer?
+ {
+ mStream_Dirty = morkBool_kTrue; // ensure flush
+ MORK_MEMCPY(at, source, inSize); // copy
+
+ mStream_At += inSize; // past written bytes
+ outActual += inSize; // this much written
+ }
+ else // directly to content file instead
+ {
+ // file->Seek(ev, mStream_BufPos); // set pos
+ // if ( ev->Good() ) // no seek error?
+ // {
+ // }
+
+ mork_num actual = 0;
+ file->Put(menv, source, inSize, mStream_BufPos, &actual);
+ if ( ev->Good() ) // no write error?
+ {
+ outActual += actual;
+ mStream_BufPos += actual;
+ }
+ }
+ }
+ }
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ else this->NewNullStreamBufferError(ev);
+ }
+ }
+ else this->NewCantWriteSourceError(ev);
+ }
+ else this->NewFileDownError(ev);
+
+ if ( ev->Bad() )
+ outActual = 0;
+
+ *aOutSize = outActual;
+ return ev->AsErr();
+}
+
+NS_IMETHODIMP
+morkStream::Flush(nsIMdbEnv* ev)
+{
+ morkEnv *mev = morkEnv::FromMdbEnv(ev);
+ nsresult rv = NS_ERROR_FAILURE;
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+ {
+ if ( mStream_Dirty )
+ this->spill_buf(mev);
+
+ rv = file->Flush(ev);
+ }
+ else this->NewFileDownError(mev);
+ return rv;
+}
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+// protected: // protected non-poly morkStream methods (for char io)
+
+int
+morkStream::fill_getc(morkEnv* ev)
+{
+ int c = EOF;
+
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenAndActiveFile() && file )
+ {
+ mork_u1* buf = mStream_Buf;
+ mork_u1* end = mStream_ReadEnd; // beyond buf after earlier read
+ if ( end > buf ) // any earlier read bytes buffered?
+ {
+ mStream_BufPos += ( end - buf ); // advance past old read
+ }
+
+ if ( ev->Good() ) // no errors yet?
+ {
+ // file->Seek(ev, mStream_BufPos); // set file pos
+ // if ( ev->Good() ) // no seek error?
+ // {
+ // }
+
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ mork_num actual = 0;
+ file->Get(menv, buf, mStream_BufSize, mStream_BufPos, &actual);
+ if ( ev->Good() ) // no read errors?
+ {
+ if ( actual > mStream_BufSize ) // more than asked for??
+ actual = mStream_BufSize;
+
+ mStream_At = buf;
+ mStream_ReadEnd = buf + actual;
+ if ( actual ) // any bytes actually read?
+ {
+ c = *mStream_At++; // return first byte from buffer
+ mStream_HitEof = morkBool_kFalse;
+ }
+ else
+ mStream_HitEof = morkBool_kTrue;
+ }
+ }
+ }
+ else this->NewFileDownError(ev);
+
+ return c;
+}
+
+void
+morkStream::spill_putc(morkEnv* ev, int c)
+{
+ this->spill_buf(ev);
+ if ( ev->Good() && mStream_At < mStream_WriteEnd )
+ this->Putc(ev, c);
+}
+
+void
+morkStream::spill_buf(morkEnv* ev) // spill/flush from buffer to file
+{
+ nsIMdbFile* file = mStream_ContentFile;
+ if ( this->IsOpenOrClosingNode() && this->FileActive() && file )
+ {
+ mork_u1* buf = mStream_Buf;
+ if ( mStream_Dirty )
+ {
+ mork_u1* at = mStream_At;
+ if ( at >= buf && at <= mStream_WriteEnd ) // order?
+ {
+ mork_num count = (mork_num) (at - buf); // bytes buffered
+ if ( count ) // anything to write to the string?
+ {
+ if ( count > mStream_BufSize ) // no more than max?
+ {
+ count = mStream_BufSize;
+ mStream_WriteEnd = buf + mStream_BufSize;
+ this->NewBadCursorSlotsError(ev);
+ }
+ if ( ev->Good() )
+ {
+ // file->Seek(ev, mStream_BufPos);
+ // if ( ev->Good() )
+ // {
+ // }
+ nsIMdbEnv* menv = ev->AsMdbEnv();
+ mork_num actual = 0;
+
+ file->Put(menv, buf, count, mStream_BufPos, &actual);
+ if ( ev->Good() )
+ {
+ mStream_BufPos += actual; // past bytes written
+ mStream_At = buf; // reset buffer cursor
+ mStream_Dirty = morkBool_kFalse;
+ }
+ }
+ }
+ }
+ else this->NewBadCursorOrderError(ev);
+ }
+ else
+ {
+#ifdef MORK_DEBUG
+ ev->NewWarning("stream:spill:not:dirty");
+#endif /*MORK_DEBUG*/
+ }
+ }
+ else this->NewFileDownError(ev);
+
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkStream.h b/db/mork/src/morkStream.h
new file mode 100644
index 000000000..418b68070
--- /dev/null
+++ b/db/mork/src/morkStream.h
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1999
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MORKSTREAM_
+#define _MORKSTREAM_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKFILE_
+#include "morkFile.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*=============================================================================
+ * morkStream: buffered file i/o
+ */
+
+/*| morkStream exists to define an morkFile subclass that provides buffered
+**| i/o for an underlying content file. Naturally this arrangement only makes
+**| sense when the underlying content file is itself not efficiently buffered
+**| (especially for character by character i/o).
+**|
+**|| morkStream is intended for either reading use or writing use, but not
+**| both simultaneously or interleaved. Pick one when the stream is created
+**| and don't change your mind. This restriction is intended to avoid obscure
+**| and complex bugs that might arise from interleaved reads and writes -- so
+**| just don't do it. A stream is either a sink or a source, but not both.
+**|
+**|| (When the underlying content file is intended to support both reading and
+**| writing, a developer might use two instances of morkStream where one is for
+**| reading and the other is for writing. In this case, a developer must take
+**| care to keep the two streams in sync because each will maintain a separate
+**| buffer representing a cache consistency problem for the other. A simple
+**| approach is to invalidate the buffer of one when one uses the other, with
+**| the assumption that closely mixed reading and writing is not expected, so
+**| that little cost is associated with changing read/write streaming modes.)
+**|
+**|| Exactly one of mStream_ReadEnd or mStream_WriteEnd must be a null pointer,
+**| and this will cause the right thing to occur when inlines use them, because
+**| mStream_At < mStream_WriteEnd (for example) will always be false and the
+**| else branch of the statement calls a function that raises an appropriate
+**| error to complain about either reading a sink or writing a source.
+**|
+**|| morkStream is a direct clone of ab_Stream from Communicator 4.5's
+**| address book code, which in turn was based on the stream class in the
+**| public domain Mithril programming language.
+|*/
+
+#define morkStream_kPrintBufSize /*i*/ 512 /* buffer size used by printf() */
+
+#define morkStream_kMinBufSize /*i*/ 512 /* buffer no fewer bytes */
+#define morkStream_kMaxBufSize /*i*/ (32 * 1024) /* buffer no more bytes */
+
+#define morkDerived_kStream /*i*/ 0x7A74 /* ascii 'zt' */
+
+class morkStream /*d*/ : public morkFile { /* from Mithril's AgStream class */
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected morkStream members
+ mork_u1* mStream_At; // pointer into mStream_Buf
+ mork_u1* mStream_ReadEnd; // null or one byte past last readable byte
+ mork_u1* mStream_WriteEnd; // null or mStream_Buf + mStream_BufSize
+
+ nsIMdbFile* mStream_ContentFile; // where content is read and written
+
+ mork_u1* mStream_Buf; // dynamically allocated memory to buffer io
+ mork_size mStream_BufSize; // requested buf size (fixed by min and max)
+ mork_pos mStream_BufPos; // logical position of byte at mStream_Buf
+ mork_bool mStream_Dirty; // does the buffer need to be flushed?
+ mork_bool mStream_HitEof; // has eof been reached? (only frozen streams)
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseStream() only if open
+ virtual ~morkStream(); // assert that CloseStream() executed earlier
+
+public: // morkStream construction & destruction
+ morkStream(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbFile* ioContentFile, mork_size inBufSize, mork_bool inFrozen);
+ void CloseStream(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkStream(const morkStream& other);
+ morkStream& operator=(const morkStream& other);
+
+public: // dynamic type identification
+ mork_bool IsStream() const
+ { return IsNode() && mNode_Derived == morkDerived_kStream; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ void NonStreamTypeError(morkEnv* ev);
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // virtual morkFile methods
+
+ NS_IMETHOD Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) override;
+ // Steal: tell this file to close any associated i/o stream in the file
+ // system, because the file ioThief intends to reopen the file in order
+ // to provide the MDB implementation with more exotic file access than is
+ // offered by the nsIMdbFile alone. Presumably the thief knows enough
+ // from Path() in order to know which file to reopen. If Steal() is
+ // successful, this file should probably delegate all future calls to
+ // the nsIMdbFile interface down to the thief files, so that even after
+ // the file has been stolen, it can still be read, written, or forcibly
+ // closed (by a call to CloseMdbObject()).
+
+ NS_IMETHOD BecomeTrunk(nsIMdbEnv* ev) override;
+ // If this file is a file version branch created by calling AcquireBud(),
+ // BecomeTrunk() causes this file's content to replace the original
+ // file's content, typically by assuming the original file's identity.
+
+ NS_IMETHOD AcquireBud(nsIMdbEnv* ev, nsIMdbHeap* ioHeap, nsIMdbFile** acqBud) override;
+ // AcquireBud() starts a new "branch" version of the file, empty of content,
+ // so that a new version of the file can be written. This new file
+ // can later be told to BecomeTrunk() the original file, so the branch
+ // created by budding the file will replace the original file. Some
+ // file subclasses might initially take the unsafe but expedient
+ // approach of simply truncating this file down to zero length, and
+ // then returning the same morkFile pointer as this, with an extra
+ // reference count increment. Note that the caller of AcquireBud() is
+ // expected to eventually call CutStrongRef() on the returned file
+ // in order to release the strong reference. High quality versions
+ // of morkFile subclasses will create entirely new files which later
+ // are renamed to become the old file, so that better transactional
+ // behavior is exhibited by the file, so crashes protect old files.
+ // Note that AcquireBud() is an illegal operation on readonly files.
+
+ virtual mork_pos Length(morkEnv* ev) const override; // eof
+ NS_IMETHOD Tell(nsIMdbEnv* ev, mork_pos *aOutPos ) const override;
+ NS_IMETHOD Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_size *aOutCount) override;
+ NS_IMETHOD Seek(nsIMdbEnv* ev, mork_pos inPos, mork_pos *aOutPos) override;
+ NS_IMETHOD Write(nsIMdbEnv* ev, const void* inBuf, mork_size inSize, mork_size *aOutCount) override;
+ NS_IMETHOD Flush(nsIMdbEnv* ev) override;
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+protected: // protected non-poly morkStream methods (for char io)
+
+ int fill_getc(morkEnv* ev);
+ void spill_putc(morkEnv* ev, int c);
+ void spill_buf(morkEnv* ev); // spill/flush from buffer to file
+
+// ````` ````` ````` ````` ````` ````` ````` `````
+public: // public non-poly morkStream methods
+
+ void NewBadCursorSlotsError(morkEnv* ev) const;
+ void NewBadCursorOrderError(morkEnv* ev) const;
+ void NewNullStreamBufferError(morkEnv* ev) const;
+ void NewCantReadSinkError(morkEnv* ev) const;
+ void NewCantWriteSourceError(morkEnv* ev) const;
+ void NewPosBeyondEofError(morkEnv* ev) const;
+
+ nsIMdbFile* GetStreamContentFile() const { return mStream_ContentFile; }
+ mork_size GetStreamBufferSize() const { return mStream_BufSize; }
+
+ mork_size PutIndent(morkEnv* ev, mork_count inDepth);
+ // PutIndent() puts a linebreak, and then
+ // "indents" by inDepth, and returns the line length after indentation.
+
+ mork_size PutByteThenIndent(morkEnv* ev, int inByte, mork_count inDepth);
+ // PutByteThenIndent() puts the byte, then a linebreak, and then
+ // "indents" by inDepth, and returns the line length after indentation.
+
+ mork_size PutStringThenIndent(morkEnv* ev,
+ const char* inString, mork_count inDepth);
+ // PutStringThenIndent() puts the string, then a linebreak, and then
+ // "indents" by inDepth, and returns the line length after indentation.
+
+ mork_size PutString(morkEnv* ev, const char* inString);
+ // PutString() returns the length of the string written.
+
+ mork_size PutStringThenNewline(morkEnv* ev, const char* inString);
+ // PutStringThenNewline() returns total number of bytes written.
+
+ mork_size PutByteThenNewline(morkEnv* ev, int inByte);
+ // PutByteThenNewline() returns total number of bytes written.
+
+ // ````` ````` stdio type methods ````` `````
+ void Ungetc(int c) /*i*/
+ { if ( mStream_At > mStream_Buf && c > 0 ) *--mStream_At = (mork_u1) c; }
+
+ // Note Getc() returns EOF consistently after any fill_getc() error occurs.
+ int Getc(morkEnv* ev) /*i*/
+ { return ( mStream_At < mStream_ReadEnd )? *mStream_At++ : fill_getc(ev); }
+
+ void Putc(morkEnv* ev, int c) /*i*/
+ {
+ mStream_Dirty = morkBool_kTrue;
+ if ( mStream_At < mStream_WriteEnd )
+ *mStream_At++ = (mork_u1) c;
+ else
+ spill_putc(ev, c);
+ }
+
+ mork_size PutLineBreak(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakStream(morkStream* me,
+ morkEnv* ev, morkStream** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongStream(morkStream* me,
+ morkEnv* ev, morkStream** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKSTREAM_ */
diff --git a/db/mork/src/morkTable.cpp b/db/mork/src/morkTable.cpp
new file mode 100644
index 000000000..ba92f222c
--- /dev/null
+++ b/db/mork/src/morkTable.cpp
@@ -0,0 +1,1610 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKROWOBJECT_
+#include "morkRowObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkTable::CloseMorkNode(morkEnv* ev) /*i*/ // CloseTable() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ morkObject::CloseMorkNode(ev); // give base class a chance.
+ this->MarkClosing();
+ this->CloseTable(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkTable::~morkTable() /*i*/ // assert CloseTable() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(this->IsShutNode());
+ MORK_ASSERT(mTable_Store==0);
+ MORK_ASSERT(mTable_RowSpace==0);
+}
+
+/*public non-poly*/
+morkTable::morkTable(morkEnv* ev, /*i*/
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ morkStore* ioStore, nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ mork_tid inTid, mork_kind inKind, mork_bool inMustBeUnique)
+: morkObject(ev, inUsage, ioHeap, (mork_color) inTid, (morkHandle*) 0)
+, mTable_Store( 0 )
+, mTable_RowSpace( 0 )
+, mTable_MetaRow( 0 )
+
+, mTable_RowMap( 0 )
+// , mTable_RowMap(ev, morkUsage::kMember, (nsIMdbHeap*) 0, ioSlotHeap,
+// morkTable_kStartRowMapSlotCount)
+, mTable_RowArray(ev, morkUsage::kMember, (nsIMdbHeap*) 0,
+ morkTable_kStartRowArraySize, ioSlotHeap)
+
+, mTable_ChangeList()
+, mTable_ChangesCount( 0 )
+, mTable_ChangesMax( 3 ) // any very small number greater than zero
+
+, mTable_Kind( inKind )
+
+, mTable_Flags( 0 )
+, mTable_Priority( morkPriority_kLo ) // NOT high priority
+, mTable_GcUses( 0 )
+, mTable_Pad( 0 )
+{
+ this->mLink_Next = 0;
+ this->mLink_Prev = 0;
+
+ if ( ev->Good() )
+ {
+ if ( ioStore && ioSlotHeap && ioRowSpace )
+ {
+ if ( inKind )
+ {
+ if ( inMustBeUnique )
+ this->SetTableUnique();
+ mTable_Store = ioStore;
+ mTable_RowSpace = ioRowSpace;
+ if ( inOptionalMetaRowOid )
+ mTable_MetaRowOid = *inOptionalMetaRowOid;
+ else
+ {
+ mTable_MetaRowOid.mOid_Scope = 0;
+ mTable_MetaRowOid.mOid_Id = morkRow_kMinusOneRid;
+ }
+ if ( ev->Good() )
+ {
+ if ( this->MaybeDirtySpaceStoreAndTable() )
+ this->SetTableRewrite(); // everything is dirty
+
+ mNode_Derived = morkDerived_kTable;
+ }
+ this->MaybeDirtySpaceStoreAndTable(); // new table might dirty store
+ }
+ else
+ ioRowSpace->ZeroKindError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkTable, morkObject, nsIMdbTable)
+
+/*public non-poly*/ void
+morkTable::CloseTable(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkRowMap::SlotStrongRowMap((morkRowMap*) 0, ev, &mTable_RowMap);
+ // mTable_RowMap.CloseMorkNode(ev);
+ mTable_RowArray.CloseMorkNode(ev);
+ mTable_Store = 0;
+ mTable_RowSpace = 0;
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// { ===== begin nsIMdbCollection methods =====
+
+// { ----- begin attribute methods -----
+NS_IMETHODIMP
+morkTable::GetSeed(nsIMdbEnv* mev,
+ mdb_seed* outSeed) // member change count
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ *outSeed = mTable_RowArray.mArray_Seed;
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetCount(nsIMdbEnv* mev,
+ mdb_count* outCount) // member count
+{
+ NS_ENSURE_ARG_POINTER(outCount);
+ *outCount = mTable_RowArray.mArray_Fill;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetPort(nsIMdbEnv* mev,
+ nsIMdbPort** acqPort) // collection container
+{
+ (void) morkEnv::FromMdbEnv(mev);
+ NS_ENSURE_ARG_POINTER(acqPort);
+ *acqPort = mTable_Store;
+ return NS_OK;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkTable::GetCursor( // make a cursor starting iter at inMemberPos
+ nsIMdbEnv* mev, // context
+ mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+ nsIMdbCursor** acqCursor) // acquire new cursor instance
+{
+ return this->GetTableRowCursor(mev, inMemberPos,
+ (nsIMdbTableRowCursor**) acqCursor);
+}
+// } ----- end cursor methods -----
+
+// { ----- begin ID methods -----
+NS_IMETHODIMP
+morkTable::GetOid(nsIMdbEnv* mev,
+ mdbOid* outOid) // read object identity
+{
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ GetTableOid(ev, outOid);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::BecomeContent(nsIMdbEnv* mev,
+ const mdbOid* inOid) // exchange content
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ // remember table->MaybeDirtySpaceStoreAndTable();
+}
+
+// } ----- end ID methods -----
+
+// { ----- begin activity dropping methods -----
+NS_IMETHODIMP
+morkTable::DropActivity( // tell collection usage no longer expected
+ nsIMdbEnv* mev)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+
+// { ===== begin nsIMdbTable methods =====
+
+// { ----- begin attribute methods -----
+
+NS_IMETHODIMP
+morkTable::SetTablePriority(nsIMdbEnv* mev, mdb_priority inPrio)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( inPrio > morkPriority_kMax )
+ inPrio = morkPriority_kMax;
+
+ mTable_Priority = inPrio;
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetTablePriority(nsIMdbEnv* mev, mdb_priority* outPrio)
+{
+ nsresult outErr = NS_OK;
+ mork_priority prio = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ prio = mTable_Priority;
+ if ( prio > morkPriority_kMax )
+ {
+ prio = morkPriority_kMax;
+ mTable_Priority = prio;
+ }
+ outErr = ev->AsErr();
+ }
+ if ( outPrio )
+ *outPrio = prio;
+ return outErr;
+}
+
+
+NS_IMETHODIMP
+morkTable:: GetTableBeVerbose(nsIMdbEnv* mev, mdb_bool* outBeVerbose)
+{
+ NS_ENSURE_ARG_POINTER(outBeVerbose);
+ *outBeVerbose = IsTableVerbose();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::SetTableBeVerbose(nsIMdbEnv* mev, mdb_bool inBeVerbose)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( inBeVerbose )
+ SetTableVerbose();
+ else
+ ClearTableVerbose();
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetTableIsUnique(nsIMdbEnv* mev, mdb_bool* outIsUnique)
+{
+ NS_ENSURE_ARG_POINTER(outIsUnique);
+ *outIsUnique = IsTableUnique();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
+{
+ NS_ENSURE_ARG_POINTER(outTableKind);
+ *outTableKind = mTable_Kind;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope)
+{
+ nsresult outErr = NS_OK;
+ mdb_scope rowScope = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mTable_RowSpace )
+ rowScope = mTable_RowSpace->SpaceScope();
+ else
+ NilRowSpaceError(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( outRowScope )
+ *outRowScope = rowScope;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::GetMetaRow( nsIMdbEnv* mev,
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ mdbOid* outOid, // output meta row oid, can be nil to suppress output
+ nsIMdbRow** acqRow) // acquire table's unique singleton meta row
+ // The purpose of a meta row is to support the persistent recording of
+ // meta info about a table as cells put into the distinguished meta row.
+ // Each table has exactly one meta row, which is not considered a member
+ // of the collection of rows inside the table. The only way to tell
+ // whether a row is a meta row is by the fact that it is returned by this
+ // GetMetaRow() method from some table. Otherwise nothing distinguishes
+ // a meta row from any other row. A meta row can be used anyplace that
+ // any other row can be used, and can even be put into other tables (or
+ // the same table) as a table member, if this is useful for some reason.
+ // The first attempt to access a table's meta row using GetMetaRow() will
+ // cause the meta row to be created if it did not already exist. When the
+ // meta row is created, it will have the row oid that was previously
+ // requested for this table's meta row; or if no oid was ever explicitly
+ // specified for this meta row, then a unique oid will be generated in
+ // the row scope named "metaScope" (so obviously MDB clients should not
+ // manually allocate any row IDs from that special meta scope namespace).
+ // The meta row oid can be specified either when the table is created, or
+ // else the first time that GetMetaRow() is called, by passing a non-nil
+ // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's
+ // actual oid is returned in outOid (if this is a non-nil pointer), and
+ // it will be different from inOptionalMetaRowOid when the meta row was
+ // already given a different oid earlier.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRow* row = GetMetaRow(ev, inOptionalMetaRowOid);
+ if ( row && ev->Good() )
+ {
+ if ( outOid )
+ *outOid = row->mRow_Oid;
+
+ outRow = row->AcquireRowHandle(ev, mTable_Store);
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+
+ if ( ev->Bad() && outOid )
+ {
+ outOid->mOid_Scope = 0;
+ outOid->mOid_Id = morkRow_kMinusOneRid;
+ }
+ return outErr;
+}
+
+// } ----- end attribute methods -----
+
+// { ----- begin cursor methods -----
+NS_IMETHODIMP
+morkTable::GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+ nsIMdbEnv* mev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbTableRowCursor** acqCursor) // acquire new cursor instance
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTableRowCursor* outCursor = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkTableRowCursor* cursor = NewTableRowCursor(ev, inRowPos);
+ if ( cursor )
+ {
+ if ( ev->Good() )
+ {
+ // cursor->mCursor_Seed = (mork_seed) inRowPos;
+ outCursor = cursor;
+ outCursor->AddRef();
+ }
+ }
+
+ outErr = ev->AsErr();
+ }
+ if ( acqCursor )
+ *acqCursor = outCursor;
+ return outErr;
+}
+// } ----- end row position methods -----
+
+// { ----- begin row position methods -----
+NS_IMETHODIMP
+morkTable::PosToOid( // get row member for a table position
+ nsIMdbEnv* mev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ mdbOid* outOid) // row oid at the specified position
+{
+ nsresult outErr = NS_OK;
+ mdbOid roid;
+ roid.mOid_Scope = 0;
+ roid.mOid_Id = (mork_id) -1;
+
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRow* row = SafeRowAt(ev, inRowPos);
+ if ( row )
+ roid = row->mRow_Oid;
+
+ outErr = ev->AsErr();
+ }
+ if ( outOid )
+ *outOid = roid;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::OidToPos( // test for the table position of a row member
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_pos* outPos) // zero-based ordinal position of row in table
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mork_pos pos = ArrayHasOid(ev, inOid);
+ if ( outPos )
+ *outPos = pos;
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::PosToRow( // get row member for a table position
+ nsIMdbEnv* mev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbRow** acqRow) // acquire row at table position inRowPos
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRow* row = SafeRowAt(ev, inRowPos);
+ if ( row && mTable_Store )
+ outRow = row->AcquireRowHandle(ev, mTable_Store);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::RowToPos( // test for the table position of a row member
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_pos* outPos) // zero-based ordinal position of row in table
+{
+ nsresult outErr = NS_OK;
+ mork_pos pos = -1;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject* row = (morkRowObject*) ioRow;
+ pos = ArrayHasOid(ev, &row->mRowObject_Row->mRow_Oid);
+ outErr = ev->AsErr();
+ }
+ if ( outPos )
+ *outPos = pos;
+ return outErr;
+}
+
+// Note that HasRow() performs the inverse oid->pos mapping
+// } ----- end row position methods -----
+
+// { ----- begin oid set methods -----
+NS_IMETHODIMP
+morkTable::AddOid( // make sure the row with inOid is a table member
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid) // row to ensure membership in table
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::HasOid( // test for the table position of a row member
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_bool* outHasOid) // whether inOid is a member row
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( outHasOid )
+ *outHasOid = MapHasOid(ev, inOid);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::CutOid( // make sure the row with inOid is not a member
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid) // row to remove from table
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( inOid && mTable_Store )
+ {
+ morkRow* row = mTable_Store->GetRow(ev, inOid);
+ if ( row )
+ CutRow(ev, row);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end oid set methods -----
+
+// { ----- begin row set methods -----
+NS_IMETHODIMP
+morkTable::NewRow( // create a new row instance in table
+ nsIMdbEnv* mev, // context
+ mdbOid* ioOid, // please use zero (unbound) rowId for db-assigned IDs
+ nsIMdbRow** acqRow) // create new row
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( ioOid && mTable_Store )
+ {
+ morkRow* row = 0;
+ if ( ioOid->mOid_Id == morkRow_kMinusOneRid )
+ row = mTable_Store->NewRow(ev, ioOid->mOid_Scope);
+ else
+ row = mTable_Store->NewRowWithOid(ev, ioOid);
+
+ if ( row && AddRow(ev, row) )
+ outRow = row->AcquireRowHandle(ev, mTable_Store);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::AddRow( // make sure the row with inOid is a table member
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow) // row to ensure membership in table
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject *rowObj = (morkRowObject *) ioRow;
+ morkRow* row = rowObj->mRowObject_Row;
+ AddRow(ev, row);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::HasRow( // test for the table position of a row member
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_bool* outBool) // zero-based ordinal position of row in table
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject *rowObj = (morkRowObject *) ioRow;
+ morkRow* row = rowObj->mRowObject_Row;
+ if ( outBool )
+ *outBool = MapHasOid(ev, &row->mRow_Oid);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+
+NS_IMETHODIMP
+morkTable::CutRow( // make sure the row with inOid is not a member
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow) // row to remove from table
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject *rowObj = (morkRowObject *) ioRow;
+ morkRow* row = rowObj->mRowObject_Row;
+ CutRow(ev, row);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::CutAllRows( // remove all rows from the table
+ nsIMdbEnv* mev) // context
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ CutAllRows(ev);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ----- end row set methods -----
+
+// { ----- begin searching methods -----
+NS_IMETHODIMP
+morkTable::FindRowMatches( // search variable number of sorted cols
+ nsIMdbEnv* mev, // context
+ const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+ nsIMdbTableRowCursor** acqCursor) // set of matching rows
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::GetSearchColumns( // query columns used by FindRowMatches()
+ nsIMdbEnv* mev, // context
+ mdb_count* outCount, // context
+ mdbColumnSet* outColSet) // caller supplied space to put columns
+ // GetSearchColumns() returns the columns actually searched when the
+ // FindRowMatches() method is called. No more than mColumnSet_Count
+ // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+ // indicates how many slots are present in the column array. The
+ // actual number of search column used by the table is returned in
+ // the outCount parameter; if this number exceeds mColumnSet_Count,
+ // then a caller needs a bigger array to read the entire column set.
+ // The minimum of mColumnSet_Count and outCount is the number slots
+ // in mColumnSet_Columns that were actually written by this method.
+ //
+ // Callers are expected to change this set of columns by calls to
+ // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end searching methods -----
+
+// { ----- begin hinting methods -----
+NS_IMETHODIMP
+morkTable::SearchColumnsHint( // advise re future expected search cols
+ nsIMdbEnv* mev, // context
+ const mdbColumnSet* inColumnSet) // columns likely to be searched
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::SortColumnsHint( // advise re future expected sort columns
+ nsIMdbEnv* mev, // context
+ const mdbColumnSet* inColumnSet) // columns for likely sort requests
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::StartBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* mev, // context
+ const void* inLabel) // intend unique address to match end call
+ // If batch starts nest by virtue of nesting calls in the stack, then
+ // the address of a local variable makes a good batch start label that
+ // can be used at batch end time, and such addresses remain unique.
+{
+ // we don't do anything here.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+morkTable::EndBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* mev, // context
+ const void* inLabel) // label matching start label
+ // Suppose a table is maintaining one or many sort orders for a table,
+ // so that every row added to the table must be inserted in each sort,
+ // and every row cut must be removed from each sort. If a db client
+ // intends to make many such changes before needing any information
+ // about the order or positions of rows inside a table, then a client
+ // might tell the table to start batch changes in order to disable
+ // sorting of rows for the interim. Presumably a table will then do
+ // a full sort of all rows at need when the batch changes end, or when
+ // a surprise request occurs for row position during batch changes.
+{
+ // we don't do anything here.
+ return NS_OK;
+}
+// } ----- end hinting methods -----
+
+// { ----- begin sorting methods -----
+// sorting: note all rows are assumed sorted by row ID as a secondary
+// sort following the primary column sort, when table rows are sorted.
+
+NS_IMETHODIMP
+morkTable::CanSortColumn( // query which column is currently used for sorting
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // column to query sorting potential
+ mdb_bool* outCanSort) // whether the column can be sorted
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::GetSorting( // view same table in particular sorting
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // requested new column for sorting table
+ nsIMdbSorting** acqSorting) // acquire sorting for column
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::SetSearchSorting( // use this sorting in FindRowMatches()
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+ nsIMdbSorting* ioSorting) // requested sorting for some column
+ // SetSearchSorting() attempts to inform the table that ioSorting
+ // should be used during calls to FindRowMatches() for searching
+ // the column which is actually sorted by ioSorting. This method
+ // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+ // because otherwise a caller would not be able to override the
+ // comparison ordering method used during searches. Note that some
+ // database implementations might be unable to use an arbitrarily
+ // specified sort order, either due to schema or runtime interface
+ // constraints, in which case ioSorting might not actually be used.
+ // Presumably ioSorting is an instance that was returned from some
+ // earlier call to nsIMdbTable::GetSorting(). A caller can also
+ // use nsIMdbTable::SearchColumnsHint() to specify desired change
+ // in which columns are sorted and searched by FindRowMatches().
+ //
+ // A caller can pass a nil pointer for ioSorting to request that
+ // column inColumn no longer be used at all by FindRowMatches().
+ // But when ioSorting is non-nil, then inColumn should match the
+ // column actually sorted by ioSorting; when these do not agree,
+ // implementations are instructed to give precedence to the column
+ // specified by ioSorting (so this means callers might just pass
+ // zero for inColumn when ioSorting is also provided, since then
+ // inColumn is both redundant and ignored).
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// } ----- end sorting methods -----
+
+// { ----- begin moving methods -----
+// moving a row does nothing unless a table is currently unsorted
+
+NS_IMETHODIMP
+morkTable::MoveOid( // change position of row in unsorted table
+ nsIMdbEnv* mev, // context
+ const mdbOid* inOid, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row inOid
+ mdb_pos* outActualPos) // actual new position of row in table
+{
+ nsresult outErr = NS_OK;
+ mdb_pos actualPos = -1; // meaning it was never found in table
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( inOid && mTable_Store )
+ {
+ morkRow* row = mTable_Store->GetRow(ev, inOid);
+ if ( row )
+ actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
+ }
+ else
+ ev->NilPointerError();
+
+ outErr = ev->AsErr();
+ }
+ if ( outActualPos )
+ *outActualPos = actualPos;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTable::MoveRow( // change position of row in unsorted table
+ nsIMdbEnv* mev, // context
+ nsIMdbRow* ioRow, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row ioRow
+ mdb_pos* outActualPos) // actual new position of row in table
+{
+ mdb_pos actualPos = -1; // meaning it was never found in table
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ morkRowObject *rowObj = (morkRowObject *) ioRow;
+ morkRow* row = rowObj->mRowObject_Row;
+ actualPos = MoveRow(ev, row, inHintFromPos, inToPos);
+ outErr = ev->AsErr();
+ }
+ if ( outActualPos )
+ *outActualPos = actualPos;
+ return outErr;
+}
+// } ----- end moving methods -----
+
+// { ----- begin index methods -----
+NS_IMETHODIMP
+morkTable::AddIndex( // create a sorting index for column if possible
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // the column to sort by index
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental index building
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the index addition will be finished.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::CutIndex( // stop supporting a specific column index
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // the column with index to be removed
+ nsIMdbThumb** acqThumb) // acquire thumb for incremental index destroy
+// Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+// then the index removal will be finished.
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::HasIndex( // query for current presence of a column index
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outHasIndex) // whether column has index for this column
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::EnableIndexOnSort( // create an index for col on first sort
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn) // the column to index if ever sorted
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::QueryIndexOnSort( // check whether index on sort is enabled
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outIndexOnSort) // whether column has index-on-sort enabled
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+morkTable::DisableIndexOnSort( // prevent future index creation on sort
+ nsIMdbEnv* mev, // context
+ mdb_column inColumn) // the column to index if ever sorted
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// } ----- end index methods -----
+
+// } ===== end nsIMdbTable methods =====
+
+// we override these so that we'll use the xpcom add and release ref.
+#ifndef _MSC_VER
+mork_refs
+morkTable::AddStrongRef(nsIMdbEnv *ev)
+{
+ return (mork_refs) AddRef();
+}
+#endif
+
+mork_refs
+morkTable::AddStrongRef(morkEnv *ev)
+{
+ return (mork_refs) AddRef();
+}
+
+#ifndef _MSC_VER
+nsresult
+morkTable::CutStrongRef(nsIMdbEnv *ev)
+{
+ return (nsresult) Release();
+}
+#endif
+
+mork_refs
+morkTable::CutStrongRef(morkEnv *ev)
+{
+ return (mork_refs) Release();
+}
+
+mork_u2
+morkTable::AddTableGcUse(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not already maxed out?
+ ++mTable_GcUses;
+
+ return mTable_GcUses;
+}
+
+mork_u2
+morkTable::CutTableGcUse(morkEnv* ev)
+{
+ if ( mTable_GcUses ) // any outstanding uses to cut?
+ {
+ if ( mTable_GcUses < morkTable_kMaxTableGcUses ) // not frozen at max?
+ --mTable_GcUses;
+ }
+ else
+ this->TableGcUsesUnderflowWarning(ev);
+
+ return mTable_GcUses;
+}
+
+// table dirty handling more complex thatn morkNode::SetNodeDirty() etc.
+
+void morkTable::SetTableClean(morkEnv* ev)
+{
+ if ( mTable_ChangeList.HasListMembers() )
+ {
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
+ }
+ mTable_ChangesCount = 0;
+
+ mTable_Flags = 0;
+ this->SetNodeClean();
+}
+
+// notifications regarding table changes:
+
+void morkTable::NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
+{
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+ this->NoteTableSetAll(ev);
+ else
+ {
+ morkTableChange* tableChange = new(*heap, ev)
+ morkTableChange(ev, ioRow, inPos);
+ if ( tableChange )
+ {
+ if ( ev->Good() )
+ {
+ mTable_ChangeList.PushTail(tableChange);
+ ++mTable_ChangesCount;
+ }
+ else
+ {
+ tableChange->ZapOldNext(ev, heap);
+ this->SetTableRewrite(); // just plan to write all table rows
+ }
+ }
+ }
+}
+
+void morkTable::note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos)
+{
+ if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+ this->NoteTableSetAll(ev);
+ else
+ {
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ morkTableChange* tableChange = new(*heap, ev)
+ morkTableChange(ev, ioRow, inNewPos);
+ if ( tableChange )
+ {
+ if ( ev->Good() )
+ {
+ mTable_ChangeList.PushTail(tableChange);
+ ++mTable_ChangesCount;
+ }
+ else
+ {
+ tableChange->ZapOldNext(ev, heap);
+ this->NoteTableSetAll(ev);
+ }
+ }
+ }
+}
+
+void morkTable::note_row_change(morkEnv* ev, mork_change inChange,
+ morkRow* ioRow)
+{
+ if ( this->IsTableRewrite() || this->HasChangeOverflow() )
+ this->NoteTableSetAll(ev);
+ else
+ {
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ morkTableChange* tableChange = new(*heap, ev)
+ morkTableChange(ev, inChange, ioRow);
+ if ( tableChange )
+ {
+ if ( ev->Good() )
+ {
+ mTable_ChangeList.PushTail(tableChange);
+ ++mTable_ChangesCount;
+ }
+ else
+ {
+ tableChange->ZapOldNext(ev, heap);
+ this->NoteTableSetAll(ev);
+ }
+ }
+ }
+}
+
+void morkTable::NoteTableSetAll(morkEnv* ev)
+{
+ if ( mTable_ChangeList.HasListMembers() )
+ {
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ mTable_ChangeList.CutAndZapAllListMembers(ev, heap); // forget changes
+ }
+ mTable_ChangesCount = 0;
+ this->SetTableRewrite();
+}
+
+/*static*/ void
+morkTable::TableGcUsesUnderflowWarning(morkEnv* ev)
+{
+ ev->NewWarning("mTable_GcUses underflow");
+}
+
+/*static*/ void
+morkTable::NonTableTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkTable");
+}
+
+/*static*/ void
+morkTable::NonTableTypeWarning(morkEnv* ev)
+{
+ ev->NewWarning("non morkTable");
+}
+
+/*static*/ void
+morkTable::NilRowSpaceError(morkEnv* ev)
+{
+ ev->NewError("nil mTable_RowSpace");
+}
+
+mork_bool morkTable::MaybeDirtySpaceStoreAndTable()
+{
+ morkRowSpace* rowSpace = mTable_RowSpace;
+ if ( rowSpace )
+ {
+ morkStore* store = rowSpace->mSpace_Store;
+ if ( store && store->mStore_CanDirty )
+ {
+ store->SetStoreDirty();
+ rowSpace->mSpace_CanDirty = morkBool_kTrue;
+ }
+
+ if ( rowSpace->mSpace_CanDirty ) // first time being dirtied?
+ {
+ if ( this->IsTableClean() )
+ {
+ mork_count rowCount = this->GetRowCount();
+ mork_count oneThird = rowCount / 4; // one third of rows
+ if ( oneThird > 0x07FFF ) // more than half max u2?
+ oneThird = 0x07FFF;
+
+ mTable_ChangesMax = (mork_u2) oneThird;
+ }
+ this->SetTableDirty();
+ rowSpace->SetRowSpaceDirty();
+
+ return morkBool_kTrue;
+ }
+ }
+ return morkBool_kFalse;
+}
+
+morkRow*
+morkTable::GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid)
+{
+ morkRow* outRow = mTable_MetaRow;
+ if ( !outRow )
+ {
+ morkStore* store = mTable_Store;
+ mdbOid* oid = &mTable_MetaRowOid;
+ if ( inOptionalMetaRowOid && !oid->mOid_Scope )
+ *oid = *inOptionalMetaRowOid;
+
+ if ( oid->mOid_Scope ) // oid already recorded in table?
+ outRow = store->OidToRow(ev, oid);
+ else
+ {
+ outRow = store->NewRow(ev, morkStore_kMetaScope);
+ if ( outRow ) // need to record new oid in table?
+ *oid = outRow->mRow_Oid;
+ }
+ mTable_MetaRow = outRow;
+ if ( outRow ) // need to note another use of this row?
+ {
+ outRow->AddRowGcUse(ev);
+
+ this->SetTableNewMeta();
+ if ( this->IsTableClean() ) // catch dirty status of meta row?
+ this->MaybeDirtySpaceStoreAndTable();
+ }
+ }
+
+ return outRow;
+}
+
+void
+morkTable::GetTableOid(morkEnv* ev, mdbOid* outOid)
+{
+ morkRowSpace* space = mTable_RowSpace;
+ if ( space )
+ {
+ outOid->mOid_Scope = space->SpaceScope();
+ outOid->mOid_Id = this->TableId();
+ }
+ else
+ this->NilRowSpaceError(ev);
+}
+
+nsIMdbTable*
+morkTable::AcquireTableHandle(morkEnv* ev)
+{
+ AddRef();
+ return this;
+}
+
+mork_pos
+morkTable::ArrayHasOid(morkEnv* ev, const mdbOid* inOid)
+{
+ MORK_USED_1(ev);
+ mork_count count = mTable_RowArray.mArray_Fill;
+ mork_pos pos = -1;
+ while ( ++pos < (mork_pos)count )
+ {
+ morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+ MORK_ASSERT(row);
+ if ( row && row->EqualOid(inOid) )
+ {
+ return pos;
+ }
+ }
+ return -1;
+}
+
+mork_bool
+morkTable::MapHasOid(morkEnv* ev, const mdbOid* inOid)
+{
+ if ( mTable_RowMap )
+ return ( mTable_RowMap->GetOid(ev, inOid) != 0 );
+ else
+ return ( ArrayHasOid(ev, inOid) >= 0 );
+}
+
+void morkTable::build_row_map(morkEnv* ev)
+{
+ morkRowMap* map = mTable_RowMap;
+ if ( !map )
+ {
+ mork_count count = mTable_RowArray.mArray_Fill + 3;
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ map = new(*heap, ev) morkRowMap(ev, morkUsage::kHeap, heap, heap, count);
+ if ( map )
+ {
+ if ( ev->Good() )
+ {
+ mTable_RowMap = map; // put strong ref here
+ count = mTable_RowArray.mArray_Fill;
+ mork_pos pos = -1;
+ while ( ++pos < (mork_pos)count )
+ {
+ morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+ if ( row && row->IsRow() )
+ map->AddRow(ev, row);
+ else
+ row->NonRowTypeError(ev);
+ }
+ }
+ else
+ map->CutStrongRef(ev);
+ }
+ }
+}
+
+morkRow* morkTable::find_member_row(morkEnv* ev, morkRow* ioRow)
+{
+ if ( mTable_RowMap )
+ return mTable_RowMap->GetRow(ev, ioRow);
+ else
+ {
+ mork_count count = mTable_RowArray.mArray_Fill;
+ mork_pos pos = -1;
+ while ( ++pos < (mork_pos)count )
+ {
+ morkRow* row = (morkRow*) mTable_RowArray.At(pos);
+ if ( row == ioRow )
+ return row;
+ }
+ }
+ return (morkRow*) 0;
+}
+
+mork_pos
+morkTable::MoveRow(morkEnv* ev, morkRow* ioRow, // change row position
+ mork_pos inHintFromPos, // suggested hint regarding start position
+ mork_pos inToPos) // desired new position for row ioRow
+ // MoveRow() returns the actual position of ioRow afterwards; this
+ // position is -1 if and only if ioRow was not found as a member.
+{
+ mork_pos outPos = -1; // means ioRow was not a table member
+ mork_bool canDirty = ( this->IsTableClean() )?
+ this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+
+ morkRow** rows = (morkRow**) mTable_RowArray.mArray_Slots;
+ mork_count count = mTable_RowArray.mArray_Fill;
+ if ( count && rows && ev->Good() ) // any members at all? no errors?
+ {
+ mork_pos lastPos = count - 1; // index of last row slot
+
+ if ( inToPos > lastPos ) // beyond last used array slot?
+ inToPos = lastPos; // put row into last available slot
+ else if ( inToPos < 0 ) // before first usable slot?
+ inToPos = 0; // put row in very first slow
+
+ if ( inHintFromPos > lastPos ) // beyond last used array slot?
+ inHintFromPos = lastPos; // seek row in last available slot
+ else if ( inHintFromPos < 0 ) // before first usable slot?
+ inHintFromPos = 0; // seek row in very first slow
+
+ morkRow** fromSlot = 0; // becomes nonzero of ioRow is ever found
+ morkRow** rowsEnd = rows + count; // one past last used array slot
+
+ if ( inHintFromPos <= 0 ) // start of table? just scan for row?
+ {
+ morkRow** cursor = rows - 1; // before first array slot
+ while ( ++cursor < rowsEnd )
+ {
+ if ( *cursor == ioRow )
+ {
+ fromSlot = cursor;
+ break; // end while loop
+ }
+ }
+ }
+ else // search near the start position and work outwards
+ {
+ morkRow** lo = rows + inHintFromPos; // lowest search point
+ morkRow** hi = lo; // highest search point starts at lowest point
+
+ // Seek ioRow in spiral widening search below and above inHintFromPos.
+ // This is faster when inHintFromPos is at all accurate, but is slower
+ // than a straightforward scan when inHintFromPos is nearly random.
+
+ while ( lo >= rows || hi < rowsEnd ) // keep searching?
+ {
+ if ( lo >= rows ) // low direction search still feasible?
+ {
+ if ( *lo == ioRow ) // actually found the row?
+ {
+ fromSlot = lo;
+ break; // end while loop
+ }
+ --lo; // advance further lower
+ }
+ if ( hi < rowsEnd ) // high direction search still feasible?
+ {
+ if ( *hi == ioRow ) // actually found the row?
+ {
+ fromSlot = hi;
+ break; // end while loop
+ }
+ ++hi; // advance further higher
+ }
+ }
+ }
+
+ if ( fromSlot ) // ioRow was found as a table member?
+ {
+ outPos = fromSlot - rows; // actual position where row was found
+ if ( outPos != inToPos ) // actually need to move this row?
+ {
+ morkRow** toSlot = rows + inToPos; // slot where row must go
+
+ ++mTable_RowArray.mArray_Seed; // we modify the array now:
+
+ if ( fromSlot < toSlot ) // row is moving upwards?
+ {
+ morkRow** up = fromSlot; // leading pointer going upward
+ while ( ++up <= toSlot ) // have not gone above destination?
+ {
+ *fromSlot = *up; // shift down one
+ fromSlot = up; // shift trailing pointer up
+ }
+ }
+ else // ( fromSlot > toSlot ) // row is moving downwards
+ {
+ morkRow** down = fromSlot; // leading pointer going downward
+ while ( --down >= toSlot ) // have not gone below destination?
+ {
+ *fromSlot = *down; // shift up one
+ fromSlot = down; // shift trailing pointer
+ }
+ }
+ *toSlot = ioRow;
+ outPos = inToPos; // okay, we actually moved the row here
+
+ if ( canDirty )
+ this->note_row_move(ev, ioRow, inToPos);
+ }
+ }
+ }
+ return outPos;
+}
+
+mork_bool
+morkTable::AddRow(morkEnv* ev, morkRow* ioRow)
+{
+ morkRow* row = this->find_member_row(ev, ioRow);
+ if ( !row && ev->Good() )
+ {
+ mork_bool canDirty = ( this->IsTableClean() )?
+ this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+
+ mork_pos pos = mTable_RowArray.AppendSlot(ev, ioRow);
+ if ( ev->Good() && pos >= 0 )
+ {
+ ioRow->AddRowGcUse(ev);
+ if ( mTable_RowMap )
+ {
+ if ( mTable_RowMap->AddRow(ev, ioRow) )
+ {
+ // okay, anything else?
+ }
+ else
+ mTable_RowArray.CutSlot(ev, pos);
+ }
+ else if ( mTable_RowArray.mArray_Fill >= morkTable_kMakeRowMapThreshold )
+ this->build_row_map(ev);
+
+ if ( canDirty && ev->Good() )
+ this->NoteTableAddRow(ev, ioRow);
+ }
+ }
+ return ev->Good();
+}
+
+mork_bool
+morkTable::CutRow(morkEnv* ev, morkRow* ioRow)
+{
+ morkRow* row = this->find_member_row(ev, ioRow);
+ if ( row )
+ {
+ mork_bool canDirty = ( this->IsTableClean() )?
+ this->MaybeDirtySpaceStoreAndTable() : morkBool_kTrue;
+
+ mork_count count = mTable_RowArray.mArray_Fill;
+ morkRow** rowSlots = (morkRow**) mTable_RowArray.mArray_Slots;
+ if ( rowSlots ) // array has vector as expected?
+ {
+ mork_pos pos = -1;
+ morkRow** end = rowSlots + count;
+ morkRow** slot = rowSlots - 1; // prepare for preincrement:
+ while ( ++slot < end ) // another slot to check?
+ {
+ if ( *slot == row ) // found the slot containing row?
+ {
+ pos = slot - rowSlots; // record absolute position
+ break; // end while loop
+ }
+ }
+ if ( pos >= 0 ) // need to cut if from the array?
+ mTable_RowArray.CutSlot(ev, pos);
+ else
+ ev->NewWarning("row not found in array");
+ }
+ else
+ mTable_RowArray.NilSlotsAddressError(ev);
+
+ if ( mTable_RowMap )
+ mTable_RowMap->CutRow(ev, ioRow);
+
+ if ( canDirty )
+ this->NoteTableCutRow(ev, ioRow);
+
+ if ( ioRow->CutRowGcUse(ev) == 0 )
+ ioRow->OnZeroRowGcUse(ev);
+ }
+ return ev->Good();
+}
+
+
+mork_bool
+morkTable::CutAllRows(morkEnv* ev)
+{
+ if ( this->MaybeDirtySpaceStoreAndTable() )
+ {
+ this->SetTableRewrite(); // everything is dirty
+ this->NoteTableSetAll(ev);
+ }
+
+ if ( ev->Good() )
+ {
+ mTable_RowArray.CutAllSlots(ev);
+ if ( mTable_RowMap )
+ {
+ morkRowMapIter i(ev, mTable_RowMap);
+ mork_change* c = 0;
+ morkRow* r = 0;
+
+ for ( c = i.FirstRow(ev, &r); c; c = i.NextRow(ev, &r) )
+ {
+ if ( r )
+ {
+ if ( r->CutRowGcUse(ev) == 0 )
+ r->OnZeroRowGcUse(ev);
+
+ i.CutHereRow(ev, (morkRow**) 0);
+ }
+ else
+ ev->NewWarning("nil row in table map");
+ }
+ }
+ }
+ return ev->Good();
+}
+
+morkTableRowCursor*
+morkTable::NewTableRowCursor(morkEnv* ev, mork_pos inRowPos)
+{
+ morkTableRowCursor* outCursor = 0;
+ if ( ev->Good() )
+ {
+ nsIMdbHeap* heap = mTable_Store->mPort_Heap;
+ morkTableRowCursor* cursor = new(*heap, ev)
+ morkTableRowCursor(ev, morkUsage::kHeap, heap, this, inRowPos);
+ if ( cursor )
+ {
+ if ( ev->Good() )
+ outCursor = cursor;
+ else
+ cursor->CutStrongRef((nsIMdbEnv *) ev);
+ }
+ }
+ return outCursor;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+morkTableChange::morkTableChange(morkEnv* ev, mork_change inChange,
+ morkRow* ioRow)
+// use this constructor for inChange == morkChange_kAdd or morkChange_kCut
+: morkNext()
+, mTableChange_Row( ioRow )
+, mTableChange_Pos( morkTableChange_kNone )
+{
+ if ( ioRow )
+ {
+ if ( ioRow->IsRow() )
+ {
+ if ( inChange == morkChange_kAdd )
+ mTableChange_Pos = morkTableChange_kAdd;
+ else if ( inChange == morkChange_kCut )
+ mTableChange_Pos = morkTableChange_kCut;
+ else
+ this->UnknownChangeError(ev);
+ }
+ else
+ ioRow->NonRowTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+morkTableChange::morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos)
+// use this constructor when the row is moved
+: morkNext()
+, mTableChange_Row( ioRow )
+, mTableChange_Pos( inPos )
+{
+ if ( ioRow )
+ {
+ if ( ioRow->IsRow() )
+ {
+ if ( inPos < 0 )
+ this->NegativeMovePosError(ev);
+ }
+ else
+ ioRow->NonRowTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+}
+
+void morkTableChange::UnknownChangeError(morkEnv* ev) const
+// morkChange_kAdd or morkChange_kCut
+{
+ ev->NewError("mTableChange_Pos neither kAdd nor kCut");
+}
+
+void morkTableChange::NegativeMovePosError(morkEnv* ev) const
+// move must be non-neg position
+{
+ ev->NewError("negative mTableChange_Pos for row move");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+morkTableMap::~morkTableMap()
+{
+}
+
+morkTableMap::morkTableMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap)
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ : morkBeadMap(ev, inUsage, ioHeap, ioSlotHeap)
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ : morkNodeMap(ev, inUsage, ioHeap, ioSlotHeap)
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kTableMap;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkTable.h b/db/mork/src/morkTable.h
new file mode 100644
index 000000000..196eeb8a6
--- /dev/null
+++ b/db/mork/src/morkTable.h
@@ -0,0 +1,729 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKTABLE_
+#define _MORKTABLE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkArray.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKNODEMAP_
+#include "morkNodeMap.h"
+#endif
+
+#ifndef _MORKPROBEMAP_
+#include "morkProbeMap.h"
+#endif
+
+#ifndef _MORKBEAD_
+#include "morkBead.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class nsIMdbTable;
+#define morkDerived_kTable /*i*/ 0x5462 /* ascii 'Tb' */
+
+/*| kStartRowArraySize: starting physical size of array for mTable_RowArray.
+**| We want this number very small, so that a table containing exactly one
+**| row member will not pay too significantly in space overhead. But we want
+**| a number bigger than one, so there is some space for growth.
+|*/
+#define morkTable_kStartRowArraySize 3 /* modest starting size for array */
+
+/*| kMakeRowMapThreshold: this is the number of rows in a table which causes
+**| a hash table (mTable_RowMap) to be lazily created for faster member row
+**| identification, during such operations as cuts and adds. This number must
+**| be small enough that linear searches are not bad for member counts less
+**| than this; but this number must also be large enough that creating a hash
+**| table does not increase the per-row space overhead by a big percentage.
+**| For speed, numbers on the order of ten to twenty are all fine; for space,
+**| I believe a number as small as ten will have too much space overhead.
+|*/
+#define morkTable_kMakeRowMapThreshold 17 /* when to build mTable_RowMap */
+
+#define morkTable_kStartRowMapSlotCount 13
+#define morkTable_kMaxTableGcUses 0x0FF /* max for 8-bit unsigned int */
+
+#define morkTable_kUniqueBit ((mork_u1) (1 << 0))
+#define morkTable_kVerboseBit ((mork_u1) (1 << 1))
+#define morkTable_kNotedBit ((mork_u1) (1 << 2)) /* space has change notes */
+#define morkTable_kRewriteBit ((mork_u1) (1 << 3)) /* must rewrite all rows */
+#define morkTable_kNewMetaBit ((mork_u1) (1 << 4)) /* new table meta row */
+
+class morkTable : public morkObject, public morkLink, public nsIMdbTable {
+
+ // NOTE the morkLink base is for morkRowSpace::mRowSpace_TablesByPriority
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // bead color setter & getter replace obsolete member mTable_Id:
+
+ NS_DECL_ISUPPORTS_INHERITED
+ mork_tid TableId() const { return mBead_Color; }
+ void SetTableId(mork_tid inTid) { mBead_Color = inTid; }
+
+ // we override these so we use xpcom ref-counting semantics.
+#ifndef _MSC_VER
+ // The first declaration of AddStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ virtual mork_refs AddStrongRef(nsIMdbEnv* ev) override;
+#endif
+ virtual mork_refs AddStrongRef(morkEnv* ev) override;
+#ifndef _MSC_VER
+ // The first declaration of CutStrongRef is to suppress -Werror,-Woverloaded-virtual.
+ virtual nsresult CutStrongRef(nsIMdbEnv* ev) override;
+#endif
+ virtual mork_refs CutStrongRef(morkEnv* ev) override;
+public: // state is public because the entire Mork system is private
+
+// { ===== begin nsIMdbCollection methods =====
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev,
+ mdb_seed* outSeed) override; // member change count
+ NS_IMETHOD GetCount(nsIMdbEnv* ev,
+ mdb_count* outCount) override; // member count
+
+ NS_IMETHOD GetPort(nsIMdbEnv* ev,
+ nsIMdbPort** acqPort) override; // collection container
+ // } ----- end attribute methods -----
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetCursor( // make a cursor starting iter at inMemberPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inMemberPos, // zero-based ordinal pos of member in collection
+ nsIMdbCursor** acqCursor) override; // acquire new cursor instance
+ // } ----- end cursor methods -----
+
+ // { ----- begin ID methods -----
+ NS_IMETHOD GetOid(nsIMdbEnv* ev,
+ mdbOid* outOid) override; // read object identity
+ NS_IMETHOD BecomeContent(nsIMdbEnv* ev,
+ const mdbOid* inOid) override; // exchange content
+ // } ----- end ID methods -----
+
+ // { ----- begin activity dropping methods -----
+ NS_IMETHOD DropActivity( // tell collection usage no longer expected
+ nsIMdbEnv* ev) override;
+ // } ----- end activity dropping methods -----
+
+// } ===== end nsIMdbCollection methods =====
+ NS_IMETHOD SetTablePriority(nsIMdbEnv* ev, mdb_priority inPrio) override;
+ NS_IMETHOD GetTablePriority(nsIMdbEnv* ev, mdb_priority* outPrio) override;
+
+ NS_IMETHOD GetTableBeVerbose(nsIMdbEnv* ev, mdb_bool* outBeVerbose) override;
+ NS_IMETHOD SetTableBeVerbose(nsIMdbEnv* ev, mdb_bool inBeVerbose) override;
+
+ NS_IMETHOD GetTableIsUnique(nsIMdbEnv* ev, mdb_bool* outIsUnique) override;
+
+ NS_IMETHOD GetTableKind(nsIMdbEnv* ev, mdb_kind* outTableKind) override;
+ NS_IMETHOD GetRowScope(nsIMdbEnv* ev, mdb_scope* outRowScope) override;
+
+ NS_IMETHOD GetMetaRow(
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ mdbOid* outOid, // output meta row oid, can be nil to suppress output
+ nsIMdbRow** acqRow) override; // acquire table's unique singleton meta row
+ // The purpose of a meta row is to support the persistent recording of
+ // meta info about a table as cells put into the distinguished meta row.
+ // Each table has exactly one meta row, which is not considered a member
+ // of the collection of rows inside the table. The only way to tell
+ // whether a row is a meta row is by the fact that it is returned by this
+ // GetMetaRow() method from some table. Otherwise nothing distinguishes
+ // a meta row from any other row. A meta row can be used anyplace that
+ // any other row can be used, and can even be put into other tables (or
+ // the same table) as a table member, if this is useful for some reason.
+ // The first attempt to access a table's meta row using GetMetaRow() will
+ // cause the meta row to be created if it did not already exist. When the
+ // meta row is created, it will have the row oid that was previously
+ // requested for this table's meta row; or if no oid was ever explicitly
+ // specified for this meta row, then a unique oid will be generated in
+ // the row scope named "m" (so obviously MDB clients should not
+ // manually allocate any row IDs from that special meta scope namespace).
+ // The meta row oid can be specified either when the table is created, or
+ // else the first time that GetMetaRow() is called, by passing a non-nil
+ // pointer to an oid for parameter inOptionalMetaRowOid. The meta row's
+ // actual oid is returned in outOid (if this is a non-nil pointer), and
+ // it will be different from inOptionalMetaRowOid when the meta row was
+ // already given a different oid earlier.
+ // } ----- end meta attribute methods -----
+
+
+ // { ----- begin cursor methods -----
+ NS_IMETHOD GetTableRowCursor( // make a cursor, starting iteration at inRowPos
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbTableRowCursor** acqCursor) override; // acquire new cursor instance
+ // } ----- end row position methods -----
+
+ // { ----- begin row position methods -----
+ NS_IMETHOD PosToOid( // get row member for a table position
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ mdbOid* outOid) override; // row oid at the specified position
+
+ NS_IMETHOD OidToPos( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_pos* outPos) override; // zero-based ordinal position of row in table
+
+ NS_IMETHOD PosToRow( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ mdb_pos inRowPos, // zero-based ordinal position of row in table
+ nsIMdbRow** acqRow) override; // acquire row at table position inRowPos
+
+ NS_IMETHOD RowToPos( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_pos* outPos) override; // zero-based ordinal position of row in table
+ // } ----- end row position methods -----
+
+ // { ----- begin oid set methods -----
+ NS_IMETHOD AddOid( // make sure the row with inOid is a table member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid) override; // row to ensure membership in table
+
+ NS_IMETHOD HasOid( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row to find in table
+ mdb_bool* outHasOid) override; // whether inOid is a member row
+
+ NS_IMETHOD CutOid( // make sure the row with inOid is not a member
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid) override; // row to remove from table
+ // } ----- end oid set methods -----
+
+ // { ----- begin row set methods -----
+ NS_IMETHOD NewRow( // create a new row instance in table
+ nsIMdbEnv* ev, // context
+ mdbOid* ioOid, // please use minus one (unbound) rowId for db-assigned IDs
+ nsIMdbRow** acqRow) override; // create new row
+
+ NS_IMETHOD AddRow( // make sure the row with inOid is a table member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) override; // row to ensure membership in table
+
+ NS_IMETHOD HasRow( // test for the table position of a row member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row to find in table
+ mdb_bool* outHasRow) override; // whether row is a table member
+
+ NS_IMETHOD CutRow( // make sure the row with inOid is not a member
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow) override; // row to remove from table
+
+ NS_IMETHOD CutAllRows( // remove all rows from the table
+ nsIMdbEnv* ev) override; // context
+ // } ----- end row set methods -----
+
+ // { ----- begin hinting methods -----
+ NS_IMETHOD SearchColumnsHint( // advise re future expected search cols
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) override; // columns likely to be searched
+
+ NS_IMETHOD SortColumnsHint( // advise re future expected sort columns
+ nsIMdbEnv* ev, // context
+ const mdbColumnSet* inColumnSet) override; // columns for likely sort requests
+
+ NS_IMETHOD StartBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* ev, // context
+ const void* inLabel) override; // intend unique address to match end call
+ // If batch starts nest by virtue of nesting calls in the stack, then
+ // the address of a local variable makes a good batch start label that
+ // can be used at batch end time, and such addresses remain unique.
+
+ NS_IMETHOD EndBatchChangeHint( // advise before many adds and cuts
+ nsIMdbEnv* ev, // context
+ const void* inLabel) override; // label matching start label
+ // Suppose a table is maintaining one or many sort orders for a table,
+ // so that every row added to the table must be inserted in each sort,
+ // and every row cut must be removed from each sort. If a db client
+ // intends to make many such changes before needing any information
+ // about the order or positions of rows inside a table, then a client
+ // might tell the table to start batch changes in order to disable
+ // sorting of rows for the interim. Presumably a table will then do
+ // a full sort of all rows at need when the batch changes end, or when
+ // a surprise request occurs for row position during batch changes.
+ // } ----- end hinting methods -----
+
+ // { ----- begin searching methods -----
+ NS_IMETHOD FindRowMatches( // search variable number of sorted cols
+ nsIMdbEnv* ev, // context
+ const mdbYarn* inPrefix, // content to find as prefix in row's column cell
+ nsIMdbTableRowCursor** acqCursor) override; // set of matching rows
+
+ NS_IMETHOD GetSearchColumns( // query columns used by FindRowMatches()
+ nsIMdbEnv* ev, // context
+ mdb_count* outCount, // context
+ mdbColumnSet* outColSet) override; // caller supplied space to put columns
+ // GetSearchColumns() returns the columns actually searched when the
+ // FindRowMatches() method is called. No more than mColumnSet_Count
+ // slots of mColumnSet_Columns will be written, since mColumnSet_Count
+ // indicates how many slots are present in the column array. The
+ // actual number of search column used by the table is returned in
+ // the outCount parameter; if this number exceeds mColumnSet_Count,
+ // then a caller needs a bigger array to read the entire column set.
+ // The minimum of mColumnSet_Count and outCount is the number slots
+ // in mColumnSet_Columns that were actually written by this method.
+ //
+ // Callers are expected to change this set of columns by calls to
+ // nsIMdbTable::SearchColumnsHint() or SetSearchSorting(), or both.
+ // } ----- end searching methods -----
+
+ // { ----- begin sorting methods -----
+ // sorting: note all rows are assumed sorted by row ID as a secondary
+ // sort following the primary column sort, when table rows are sorted.
+
+ NS_IMETHOD
+ CanSortColumn( // query which column is currently used for sorting
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // column to query sorting potential
+ mdb_bool* outCanSort) override; // whether the column can be sorted
+
+ NS_IMETHOD GetSorting( // view same table in particular sorting
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // requested new column for sorting table
+ nsIMdbSorting** acqSorting) override; // acquire sorting for column
+
+ NS_IMETHOD SetSearchSorting( // use this sorting in FindRowMatches()
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // often same as nsIMdbSorting::GetSortColumn()
+ nsIMdbSorting* ioSorting) override; // requested sorting for some column
+ // SetSearchSorting() attempts to inform the table that ioSorting
+ // should be used during calls to FindRowMatches() for searching
+ // the column which is actually sorted by ioSorting. This method
+ // is most useful in conjunction with nsIMdbSorting::SetCompare(),
+ // because otherwise a caller would not be able to override the
+ // comparison ordering method used during searches. Note that some
+ // database implementations might be unable to use an arbitrarily
+ // specified sort order, either due to schema or runtime interface
+ // constraints, in which case ioSorting might not actually be used.
+ // Presumably ioSorting is an instance that was returned from some
+ // earlier call to nsIMdbTable::GetSorting(). A caller can also
+ // use nsIMdbTable::SearchColumnsHint() to specify desired change
+ // in which columns are sorted and searched by FindRowMatches().
+ //
+ // A caller can pass a nil pointer for ioSorting to request that
+ // column inColumn no longer be used at all by FindRowMatches().
+ // But when ioSorting is non-nil, then inColumn should match the
+ // column actually sorted by ioSorting; when these do not agree,
+ // implementations are instructed to give precedence to the column
+ // specified by ioSorting (so this means callers might just pass
+ // zero for inColumn when ioSorting is also provided, since then
+ // inColumn is both redundant and ignored).
+ // } ----- end sorting methods -----
+
+ // { ----- begin moving methods -----
+ // moving a row does nothing unless a table is currently unsorted
+
+ NS_IMETHOD MoveOid( // change position of row in unsorted table
+ nsIMdbEnv* ev, // context
+ const mdbOid* inOid, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row inRowId
+ mdb_pos* outActualPos) override; // actual new position of row in table
+
+ NS_IMETHOD MoveRow( // change position of row in unsorted table
+ nsIMdbEnv* ev, // context
+ nsIMdbRow* ioRow, // row oid to find in table
+ mdb_pos inHintFromPos, // suggested hint regarding start position
+ mdb_pos inToPos, // desired new position for row inRowId
+ mdb_pos* outActualPos) override; // actual new position of row in table
+ // } ----- end moving methods -----
+
+ // { ----- begin index methods -----
+ NS_IMETHOD AddIndex( // create a sorting index for column if possible
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to sort by index
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental index building
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the index addition will be finished.
+
+ NS_IMETHOD CutIndex( // stop supporting a specific column index
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column with index to be removed
+ nsIMdbThumb** acqThumb) override; // acquire thumb for incremental index destroy
+ // Call nsIMdbThumb::DoMore() until done, or until the thumb is broken, and
+ // then the index removal will be finished.
+
+ NS_IMETHOD HasIndex( // query for current presence of a column index
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outHasIndex) override; // whether column has index for this column
+
+
+ NS_IMETHOD EnableIndexOnSort( // create an index for col on first sort
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) override; // the column to index if ever sorted
+
+ NS_IMETHOD QueryIndexOnSort( // check whether index on sort is enabled
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn, // the column to investigate
+ mdb_bool* outIndexOnSort) override; // whether column has index-on-sort enabled
+
+ NS_IMETHOD DisableIndexOnSort( // prevent future index creation on sort
+ nsIMdbEnv* ev, // context
+ mdb_column inColumn) override; // the column to index if ever sorted
+ // } ----- end index methods -----
+
+ morkStore* mTable_Store; // non-refcnted ptr to port
+
+ // mTable_RowSpace->SpaceScope() is row scope
+ morkRowSpace* mTable_RowSpace; // non-refcnted ptr to containing space
+
+ morkRow* mTable_MetaRow; // table's actual meta row
+ mdbOid mTable_MetaRowOid; // oid for meta row
+
+ morkRowMap* mTable_RowMap; // (strong ref) hash table of all members
+ morkArray mTable_RowArray; // array of morkRow pointers
+
+ morkList mTable_ChangeList; // list of table changes
+ mork_u2 mTable_ChangesCount; // length of changes list
+ mork_u2 mTable_ChangesMax; // max list length before rewrite
+
+ // mork_tid mTable_Id;
+ mork_kind mTable_Kind;
+
+ mork_u1 mTable_Flags; // bit flags
+ mork_priority mTable_Priority; // 0..9, any other value equals 9
+ mork_u1 mTable_GcUses; // persistent references from cells
+ mork_u1 mTable_Pad; // for u4 alignment
+
+public: // flags bit twiddling
+
+ void SetTableUnique() { mTable_Flags |= morkTable_kUniqueBit; }
+ void SetTableVerbose() { mTable_Flags |= morkTable_kVerboseBit; }
+ void SetTableNoted() { mTable_Flags |= morkTable_kNotedBit; }
+ void SetTableRewrite() { mTable_Flags |= morkTable_kRewriteBit; }
+ void SetTableNewMeta() { mTable_Flags |= morkTable_kNewMetaBit; }
+
+ void ClearTableUnique() { mTable_Flags &= (mork_u1) ~morkTable_kUniqueBit; }
+ void ClearTableVerbose() { mTable_Flags &= (mork_u1) ~morkTable_kVerboseBit; }
+ void ClearTableNoted() { mTable_Flags &= (mork_u1) ~morkTable_kNotedBit; }
+ void ClearTableRewrite() { mTable_Flags &= (mork_u1) ~morkTable_kRewriteBit; }
+ void ClearTableNewMeta() { mTable_Flags &= (mork_u1) ~morkTable_kNewMetaBit; }
+
+ mork_bool IsTableUnique() const
+ { return ( mTable_Flags & morkTable_kUniqueBit ) != 0; }
+
+ mork_bool IsTableVerbose() const
+ { return ( mTable_Flags & morkTable_kVerboseBit ) != 0; }
+
+ mork_bool IsTableNoted() const
+ { return ( mTable_Flags & morkTable_kNotedBit ) != 0; }
+
+ mork_bool IsTableRewrite() const
+ { return ( mTable_Flags & morkTable_kRewriteBit ) != 0; }
+
+ mork_bool IsTableNewMeta() const
+ { return ( mTable_Flags & morkTable_kNewMetaBit ) != 0; }
+
+public: // table dirty handling more complex than morkNode::SetNodeDirty() etc.
+
+ void SetTableDirty() { this->SetNodeDirty(); }
+ void SetTableClean(morkEnv* ev);
+
+ mork_bool IsTableClean() const { return this->IsNodeClean(); }
+ mork_bool IsTableDirty() const { return this->IsNodeDirty(); }
+
+public: // morkNode memory management operators
+ void* operator new(size_t inSize, nsIMdbHeap& ioHeap, morkEnv* ev) CPP_THROW_NEW
+ { return morkNode::MakeNew(inSize, ioHeap, ev); }
+
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseTable() if open
+
+public: // morkTable construction & destruction
+ morkTable(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap, morkStore* ioStore,
+ nsIMdbHeap* ioSlotHeap, morkRowSpace* ioRowSpace,
+ const mdbOid* inOptionalMetaRowOid, // can be nil to avoid specifying
+ mork_tid inTableId,
+ mork_kind inKind, mork_bool inMustBeUnique);
+ void CloseTable(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkTable(const morkTable& other);
+ morkTable& operator=(const morkTable& other);
+ virtual ~morkTable(); // assert that close executed earlier
+
+public: // dynamic type identification
+ mork_bool IsTable() const
+ { return IsNode() && mNode_Derived == morkDerived_kTable; }
+// } ===== end morkNode methods =====
+
+public: // errors
+ static void NonTableTypeError(morkEnv* ev);
+ static void NonTableTypeWarning(morkEnv* ev);
+ static void NilRowSpaceError(morkEnv* ev);
+
+public: // warnings
+ static void TableGcUsesUnderflowWarning(morkEnv* ev);
+
+public: // noting table changes
+
+ mork_bool HasChangeOverflow() const
+ { return mTable_ChangesCount >= mTable_ChangesMax; }
+
+ void NoteTableSetAll(morkEnv* ev);
+ void NoteTableMoveRow(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
+
+ void note_row_change(morkEnv* ev, mork_change inChange, morkRow* ioRow);
+ void note_row_move(morkEnv* ev, morkRow* ioRow, mork_pos inNewPos);
+
+ void NoteTableAddRow(morkEnv* ev, morkRow* ioRow)
+ { this->note_row_change(ev, morkChange_kAdd, ioRow); }
+
+ void NoteTableCutRow(morkEnv* ev, morkRow* ioRow)
+ { this->note_row_change(ev, morkChange_kCut, ioRow); }
+
+protected: // internal row map methods
+
+ morkRow* find_member_row(morkEnv* ev, morkRow* ioRow);
+ void build_row_map(morkEnv* ev);
+
+public: // other table methods
+
+ mork_bool MaybeDirtySpaceStoreAndTable();
+
+ morkRow* GetMetaRow(morkEnv* ev, const mdbOid* inOptionalMetaRowOid);
+
+ mork_u2 AddTableGcUse(morkEnv* ev);
+ mork_u2 CutTableGcUse(morkEnv* ev);
+
+ // void DirtyAllTableContent(morkEnv* ev);
+
+ mork_seed TableSeed() const { return mTable_RowArray.mArray_Seed; }
+
+ morkRow* SafeRowAt(morkEnv* ev, mork_pos inPos)
+ { return (morkRow*) mTable_RowArray.SafeAt(ev, inPos); }
+
+ nsIMdbTable* AcquireTableHandle(morkEnv* ev); // mObject_Handle
+
+ mork_count GetRowCount() const { return mTable_RowArray.mArray_Fill; }
+
+ mork_bool IsTableUsed() const
+ { return (mTable_GcUses != 0 || this->GetRowCount() != 0); }
+
+ void GetTableOid(morkEnv* ev, mdbOid* outOid);
+ mork_pos ArrayHasOid(morkEnv* ev, const mdbOid* inOid);
+ mork_bool MapHasOid(morkEnv* ev, const mdbOid* inOid);
+ mork_bool AddRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good()
+ mork_bool CutRow(morkEnv* ev, morkRow* ioRow); // returns ev->Good()
+ mork_bool CutAllRows(morkEnv* ev); // returns ev->Good()
+
+ mork_pos MoveRow(morkEnv* ev, morkRow* ioRow, // change row position
+ mork_pos inHintFromPos, // suggested hint regarding start position
+ mork_pos inToPos); // desired new position for row ioRow
+ // MoveRow() returns the actual position of ioRow afterwards; this
+ // position is -1 if and only if ioRow was not found as a member.
+
+
+ morkTableRowCursor* NewTableRowCursor(morkEnv* ev, mork_pos inRowPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakTable(morkTable* me,
+ morkEnv* ev, morkTable** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongTable(morkTable* me,
+ morkEnv* ev, morkTable** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// use negative values for kCut and kAdd, to keep non-neg move pos distinct:
+#define morkTableChange_kCut ((mork_pos) -1) /* shows row was cut */
+#define morkTableChange_kAdd ((mork_pos) -2) /* shows row was added */
+#define morkTableChange_kNone ((mork_pos) -3) /* unknown change */
+
+class morkTableChange : public morkNext {
+public: // state is public because the entire Mork system is private
+
+ morkRow* mTableChange_Row; // the row in the change
+
+ mork_pos mTableChange_Pos; // kAdd, kCut, or non-neg for row move
+
+public:
+ morkTableChange(morkEnv* ev, mork_change inChange, morkRow* ioRow);
+ // use this constructor for inChange == morkChange_kAdd or morkChange_kCut
+
+ morkTableChange(morkEnv* ev, morkRow* ioRow, mork_pos inPos);
+ // use this constructor when the row is moved
+
+public:
+ void UnknownChangeError(morkEnv* ev) const; // morkChange_kAdd or morkChange_kCut
+ void NegativeMovePosError(morkEnv* ev) const; // move must be non-neg position
+
+public:
+
+ mork_bool IsAddRowTableChange() const
+ { return ( mTableChange_Pos == morkTableChange_kAdd ); }
+
+ mork_bool IsCutRowTableChange() const
+ { return ( mTableChange_Pos == morkTableChange_kCut ); }
+
+ mork_bool IsMoveRowTableChange() const
+ { return ( mTableChange_Pos >= 0 ); }
+
+public:
+
+ mork_pos GetMovePos() const { return mTableChange_Pos; }
+ // GetMovePos() assumes that IsMoveRowTableChange() is true.
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkDerived_kTableMap /*i*/ 0x744D /* ascii 'tM' */
+
+/*| morkTableMap: maps mork_token -> morkTable
+|*/
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+class morkTableMap : public morkBeadMap {
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+class morkTableMap : public morkNodeMap { // for mapping tokens to tables
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+public:
+
+ virtual ~morkTableMap();
+ morkTableMap(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap);
+
+public: // other map methods
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ mork_bool AddTable(morkEnv* ev, morkTable* ioTable)
+ { return this->AddBead(ev, ioTable); }
+ // the AddTable() boolean return equals ev->Good().
+
+ mork_bool CutTable(morkEnv* ev, mork_tid inTid)
+ { return this->CutBead(ev, inTid); }
+ // The CutTable() boolean return indicates whether removal happened.
+
+ morkTable* GetTable(morkEnv* ev, mork_tid inTid)
+ { return (morkTable*) this->GetBead(ev, inTid); }
+ // Note the returned table does NOT have an increase in refcount for this.
+
+ mork_num CutAllTables(morkEnv* ev)
+ { return this->CutAllBeads(ev); }
+ // CutAllTables() releases all the referenced table values.
+
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ mork_bool AddTable(morkEnv* ev, morkTable* ioTable)
+ { return this->AddNode(ev, ioTable->TableId(), ioTable); }
+ // the AddTable() boolean return equals ev->Good().
+
+ mork_bool CutTable(morkEnv* ev, mork_tid inTid)
+ { return this->CutNode(ev, inTid); }
+ // The CutTable() boolean return indicates whether removal happened.
+
+ morkTable* GetTable(morkEnv* ev, mork_tid inTid)
+ { return (morkTable*) this->GetNode(ev, inTid); }
+ // Note the returned table does NOT have an increase in refcount for this.
+
+ mork_num CutAllTables(morkEnv* ev)
+ { return this->CutAllNodes(ev); }
+ // CutAllTables() releases all the referenced table values.
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+};
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+class morkTableMapIter: public morkBeadMapIter {
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+class morkTableMapIter: public morkMapIter{ // typesafe wrapper class
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+
+public:
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ morkTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+ : morkBeadMapIter(ev, ioMap) { }
+
+ morkTableMapIter( ) : morkBeadMapIter() { }
+ void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+ { this->InitBeadMapIter(ev, ioMap); }
+
+ morkTable* FirstTable(morkEnv* ev)
+ { return (morkTable*) this->FirstBead(ev); }
+
+ morkTable* NextTable(morkEnv* ev)
+ { return (morkTable*) this->NextBead(ev); }
+
+ morkTable* HereTable(morkEnv* ev)
+ { return (morkTable*) this->HereBead(ev); }
+
+
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ morkTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+ : morkMapIter(ev, ioMap) { }
+
+ morkTableMapIter( ) : morkMapIter() { }
+ void InitTableMapIter(morkEnv* ev, morkTableMap* ioMap)
+ { this->InitMapIter(ev, ioMap); }
+
+ mork_change*
+ FirstTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+ { return this->First(ev, outTid, outTable); }
+
+ mork_change*
+ NextTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+ { return this->Next(ev, outTid, outTable); }
+
+ mork_change*
+ HereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+ { return this->Here(ev, outTid, outTable); }
+
+ // cutting while iterating hash map might dirty the parent table:
+ mork_change*
+ CutHereTable(morkEnv* ev, mork_tid* outTid, morkTable** outTable)
+ { return this->CutHere(ev, outTid, outTable); }
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLE_ */
+
diff --git a/db/mork/src/morkTableRowCursor.cpp b/db/mork/src/morkTableRowCursor.cpp
new file mode 100644
index 000000000..6f3693269
--- /dev/null
+++ b/db/mork/src/morkTableRowCursor.cpp
@@ -0,0 +1,493 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKTABLEROWCURSOR_
+#include "morkTableRowCursor.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkTableRowCursor::CloseMorkNode(morkEnv* ev) // CloseTableRowCursor() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseTableRowCursor(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkTableRowCursor::~morkTableRowCursor() // CloseTableRowCursor() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(this->IsShutNode());
+}
+
+/*public non-poly*/
+morkTableRowCursor::morkTableRowCursor(morkEnv* ev,
+ const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos)
+: morkCursor(ev, inUsage, ioHeap)
+, mTableRowCursor_Table( 0 )
+{
+ if ( ev->Good() )
+ {
+ if ( ioTable )
+ {
+ mCursor_Pos = inRowPos;
+ mCursor_Seed = ioTable->TableSeed();
+ morkTable::SlotWeakTable(ioTable, ev, &mTableRowCursor_Table);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kTableRowCursor;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkTableRowCursor, morkCursor, nsIMdbTableRowCursor)
+/*public non-poly*/ void
+morkTableRowCursor::CloseTableRowCursor(morkEnv* ev)
+{
+ if ( this->IsNode() )
+ {
+ mCursor_Pos = -1;
+ mCursor_Seed = 0;
+ morkTable::SlotWeakTable((morkTable*) 0, ev, &mTableRowCursor_Table);
+ this->CloseCursor(ev);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+// { ----- begin attribute methods -----
+/*virtual*/ nsresult
+morkTableRowCursor::GetCount(nsIMdbEnv* mev, mdb_count* outCount)
+{
+ nsresult outErr = NS_OK;
+ mdb_count count = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ count = GetMemberCount(ev);
+ outErr = ev->AsErr();
+ }
+ if ( outCount )
+ *outCount = count;
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkTableRowCursor::GetSeed(nsIMdbEnv* mev, mdb_seed* outSeed)
+{
+ NS_ASSERTION(false, "not implemented");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/*virtual*/ nsresult
+morkTableRowCursor::SetPos(nsIMdbEnv* mev, mdb_pos inPos)
+{
+ mCursor_Pos = inPos;
+ return NS_OK;
+}
+
+/*virtual*/ nsresult
+morkTableRowCursor::GetPos(nsIMdbEnv* mev, mdb_pos* outPos)
+{
+ *outPos = mCursor_Pos;
+ return NS_OK;
+}
+
+/*virtual*/ nsresult
+morkTableRowCursor::SetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool inFail)
+{
+ mCursor_DoFailOnSeedOutOfSync = inFail;
+ return NS_OK;
+}
+
+/*virtual*/ nsresult
+morkTableRowCursor::GetDoFailOnSeedOutOfSync(nsIMdbEnv* mev, mdb_bool* outFail)
+{
+ NS_ENSURE_ARG_POINTER(outFail);
+ *outFail = mCursor_DoFailOnSeedOutOfSync;
+ return NS_OK;
+}
+// } ----- end attribute methods -----
+
+
+// { ===== begin nsIMdbTableRowCursor methods =====
+
+// { ----- begin attribute methods -----
+
+NS_IMETHODIMP
+morkTableRowCursor::GetTable(nsIMdbEnv* mev, nsIMdbTable** acqTable)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTable* outTable = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( mTableRowCursor_Table )
+ outTable = mTableRowCursor_Table->AcquireTableHandle(ev);
+
+ outErr = ev->AsErr();
+ }
+ if ( acqTable )
+ *acqTable = outTable;
+ return outErr;
+}
+// } ----- end attribute methods -----
+
+// { ----- begin oid iteration methods -----
+NS_IMETHODIMP
+morkTableRowCursor::NextRowOid( // get row id of next row in the table
+ nsIMdbEnv* mev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos)
+{
+ nsresult outErr = NS_OK;
+ mork_pos pos = -1;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( outOid )
+ {
+ pos = NextRowOid(ev, outOid);
+ }
+ else
+ ev->NilPointerError();
+ outErr = ev->AsErr();
+ }
+ if ( outRowPos )
+ *outRowPos = pos;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTableRowCursor::PrevRowOid( // get row id of previous row in the table
+ nsIMdbEnv* mev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos)
+{
+ nsresult outErr = NS_OK;
+ mork_pos pos = -1;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ if ( outOid )
+ {
+ pos = PrevRowOid(ev, outOid);
+ }
+ else
+ ev->NilPointerError();
+ outErr = ev->AsErr();
+ }
+ if ( outRowPos )
+ *outRowPos = pos;
+ return outErr;
+}
+// } ----- end oid iteration methods -----
+
+// { ----- begin row iteration methods -----
+NS_IMETHODIMP
+morkTableRowCursor::NextRow( // get row cells from table for cells already in row
+ nsIMdbEnv* mev, // context
+ nsIMdbRow** acqRow, // acquire next row in table
+ mdb_pos* outRowPos)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+
+ mdbOid oid; // place to put oid we intend to ignore
+ morkRow* row = NextRow(ev, &oid, outRowPos);
+ if ( row )
+ {
+ morkStore* store = row->GetRowSpaceStore(ev);
+ if ( store )
+ outRow = row->AcquireRowHandle(ev, store);
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTableRowCursor::PrevRow( // get row cells from table for cells already in row
+ nsIMdbEnv* mev, // context
+ nsIMdbRow** acqRow, // acquire previous row in table
+ mdb_pos* outRowPos)
+{
+ nsresult outErr = NS_OK;
+ nsIMdbRow* outRow = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+
+ mdbOid oid; // place to put oid we intend to ignore
+ morkRow* row = PrevRow(ev, &oid, outRowPos);
+ if ( row )
+ {
+ morkStore* store = row->GetRowSpaceStore(ev);
+ if ( store )
+ outRow = row->AcquireRowHandle(ev, store);
+ }
+ outErr = ev->AsErr();
+ }
+ if ( acqRow )
+ *acqRow = outRow;
+ return outErr;
+}
+
+// } ----- end row iteration methods -----
+
+
+// { ----- begin duplicate row removal methods -----
+NS_IMETHODIMP
+morkTableRowCursor::CanHaveDupRowMembers(nsIMdbEnv* mev, // cursor might hold dups?
+ mdb_bool* outCanHaveDups)
+{
+ nsresult outErr = NS_OK;
+ mdb_bool canHaveDups = mdbBool_kFalse;
+
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ canHaveDups = CanHaveDupRowMembers(ev);
+ outErr = ev->AsErr();
+ }
+ if ( outCanHaveDups )
+ *outCanHaveDups = canHaveDups;
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkTableRowCursor::MakeUniqueCursor( // clone cursor, removing duplicate rows
+ nsIMdbEnv* mev, // context
+ nsIMdbTableRowCursor** acqCursor) // acquire clone with no dups
+ // Note that MakeUniqueCursor() is never necessary for a cursor which was
+ // created by table method nsIMdbTable::GetTableRowCursor(), because a table
+ // never contains the same row as a member more than once. However, a cursor
+ // created by table method nsIMdbTable::FindRowMatches() might contain the
+ // same row more than once, because the same row can generate a hit by more
+ // than one column with a matching string prefix. Note this method can
+ // return the very same cursor instance with just an incremented refcount,
+ // when the original cursor could not contain any duplicate rows (calling
+ // CanHaveDupRowMembers() shows this case on a false return). Otherwise
+ // this method returns a different cursor instance. Callers should not use
+ // this MakeUniqueCursor() method lightly, because it tends to defeat the
+ // purpose of lazy programming techniques, since it can force creation of
+ // an explicit row collection in a new cursor's representation, in order to
+ // inspect the row membership and remove any duplicates; this can have big
+ // impact if a collection holds tens of thousands of rows or more, when
+ // the original cursor with dups simply referenced rows indirectly by row
+ // position ranges, without using an explicit row set representation.
+ // Callers are encouraged to use nsIMdbCursor::GetCount() to determine
+ // whether the row collection is very large (tens of thousands), and to
+ // delay calling MakeUniqueCursor() when possible, until a user interface
+ // element actually demands the creation of an explicit set representation.
+{
+ nsresult outErr = NS_OK;
+ nsIMdbTableRowCursor* outCursor = 0;
+
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ AddRef();
+ outCursor = this;
+
+ outErr = ev->AsErr();
+ }
+ if ( acqCursor )
+ *acqCursor = outCursor;
+ return outErr;
+}
+// } ----- end duplicate row removal methods -----
+
+// } ===== end nsIMdbTableRowCursor methods =====
+
+
+/*static*/ void
+morkTableRowCursor::NonTableRowCursorTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkTableRowCursor");
+}
+
+
+mdb_pos
+morkTableRowCursor::NextRowOid(morkEnv* ev, mdbOid* outOid)
+{
+ mdb_pos outPos = -1;
+ (void) this->NextRow(ev, outOid, &outPos);
+ return outPos;
+}
+
+mdb_pos
+morkTableRowCursor::PrevRowOid(morkEnv* ev, mdbOid* outOid)
+{
+ mdb_pos outPos = -1;
+ (void) this->PrevRow(ev, outOid, &outPos);
+ return outPos;
+}
+
+mork_bool
+morkTableRowCursor::CanHaveDupRowMembers(morkEnv* ev)
+{
+ return morkBool_kFalse; // false default is correct
+}
+
+mork_count
+morkTableRowCursor::GetMemberCount(morkEnv* ev)
+{
+ morkTable* table = mTableRowCursor_Table;
+ if ( table )
+ return table->mTable_RowArray.mArray_Fill;
+ else
+ return 0;
+}
+
+morkRow*
+morkTableRowCursor::PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+ morkRow* outRow = 0;
+ mork_pos pos = -1;
+
+ morkTable* table = mTableRowCursor_Table;
+ if ( table )
+ {
+ if ( table->IsOpenNode() )
+ {
+ morkArray* array = &table->mTable_RowArray;
+ pos = mCursor_Pos - 1;
+
+ if ( pos >= 0 && pos < (mork_pos)(array->mArray_Fill) )
+ {
+ mCursor_Pos = pos; // update for next time
+ morkRow* row = (morkRow*) array->At(pos);
+ if ( row )
+ {
+ if ( row->IsRow() )
+ {
+ outRow = row;
+ *outOid = row->mRow_Oid;
+ }
+ else
+ row->NonRowTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ else
+ {
+ outOid->mOid_Scope = 0;
+ outOid->mOid_Id = morkId_kMinusOne;
+ }
+ }
+ else
+ table->NonOpenNodeError(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ *outPos = pos;
+ return outRow;
+}
+
+morkRow*
+morkTableRowCursor::NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos)
+{
+ morkRow* outRow = 0;
+ mork_pos pos = -1;
+
+ morkTable* table = mTableRowCursor_Table;
+ if ( table )
+ {
+ if ( table->IsOpenNode() )
+ {
+ morkArray* array = &table->mTable_RowArray;
+ pos = mCursor_Pos;
+ if ( pos < 0 )
+ pos = 0;
+ else
+ ++pos;
+
+ if ( pos < (mork_pos)(array->mArray_Fill) )
+ {
+ mCursor_Pos = pos; // update for next time
+ morkRow* row = (morkRow*) array->At(pos);
+ if ( row )
+ {
+ if ( row->IsRow() )
+ {
+ outRow = row;
+ *outOid = row->mRow_Oid;
+ }
+ else
+ row->NonRowTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ else
+ {
+ outOid->mOid_Scope = 0;
+ outOid->mOid_Id = morkId_kMinusOne;
+ }
+ }
+ else
+ table->NonOpenNodeError(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ *outPos = pos;
+ return outRow;
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkTableRowCursor.h b/db/mork/src/morkTableRowCursor.h
new file mode 100644
index 000000000..edd98947b
--- /dev/null
+++ b/db/mork/src/morkTableRowCursor.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKTABLEROWCURSOR_
+#define _MORKTABLEROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinTableRowCursor;
+#define morkDerived_kTableRowCursor /*i*/ 0x7243 /* ascii 'rC' */
+
+class morkTableRowCursor : public morkCursor, public nsIMdbTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkFactory* mObject_Factory; // weak ref to suite factory
+
+ // mork_seed mCursor_Seed;
+ // mork_pos mCursor_Pos;
+ // mork_bool mCursor_DoFailOnSeedOutOfSync;
+ // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+public: // state is public because the entire Mork system is private
+ morkTable* mTableRowCursor_Table; // weak ref to table
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseTableRowCursor()
+
+protected:
+ virtual ~morkTableRowCursor(); // assert that close executed earlier
+
+public: // morkTableRowCursor construction & destruction
+ morkTableRowCursor(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+ void CloseTableRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkTableRowCursor(const morkTableRowCursor& other);
+ morkTableRowCursor& operator=(const morkTableRowCursor& other);
+
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // { ----- begin attribute methods -----
+ NS_IMETHOD GetCount(nsIMdbEnv* ev, mdb_count* outCount) override; // readonly
+ NS_IMETHOD GetSeed(nsIMdbEnv* ev, mdb_seed* outSeed) override; // readonly
+
+ NS_IMETHOD SetPos(nsIMdbEnv* ev, mdb_pos inPos) override; // mutable
+ NS_IMETHOD GetPos(nsIMdbEnv* ev, mdb_pos* outPos) override;
+
+ NS_IMETHOD SetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool inFail) override;
+ NS_IMETHOD GetDoFailOnSeedOutOfSync(nsIMdbEnv* ev, mdb_bool* outFail) override;
+
+ // } ----- end attribute methods -----
+ NS_IMETHOD GetTable(nsIMdbEnv* ev, nsIMdbTable** acqTable) override;
+ // } ----- end attribute methods -----
+
+ // { ----- begin duplicate row removal methods -----
+ NS_IMETHOD CanHaveDupRowMembers(nsIMdbEnv* ev, // cursor might hold dups?
+ mdb_bool* outCanHaveDups) override;
+
+ NS_IMETHOD MakeUniqueCursor( // clone cursor, removing duplicate rows
+ nsIMdbEnv* ev, // context
+ nsIMdbTableRowCursor** acqCursor) override; // acquire clone with no dups
+ // } ----- end duplicate row removal methods -----
+
+ // { ----- begin oid iteration methods -----
+ NS_IMETHOD NextRowOid( // get row id of next row in the table
+ nsIMdbEnv* ev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos) override; // zero-based position of the row in table
+ NS_IMETHOD PrevRowOid( // get row id of previous row in the table
+ nsIMdbEnv* ev, // context
+ mdbOid* outOid, // out row oid
+ mdb_pos* outRowPos) override; // zero-based position of the row in table
+ // } ----- end oid iteration methods -----
+
+ // { ----- begin row iteration methods -----
+ NS_IMETHOD NextRow( // get row cells from table for cells already in row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // acquire next row in table
+ mdb_pos* outRowPos) override; // zero-based position of the row in table
+ NS_IMETHOD PrevRow( // get row cells from table for cells already in row
+ nsIMdbEnv* ev, // context
+ nsIMdbRow** acqRow, // acquire previous row in table
+ mdb_pos* outRowPos) override; // zero-based position of the row in table
+ // } ----- end row iteration methods -----
+
+
+public: // dynamic type identification
+ mork_bool IsTableRowCursor() const
+ { return IsNode() && mNode_Derived == morkDerived_kTableRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonTableRowCursorTypeError(morkEnv* ev);
+
+public: // oid only iteration
+ mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+ mdb_pos PrevRowOid(morkEnv* ev, mdbOid* outOid);
+
+public: // other table row cursor methods
+
+ virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+ virtual mork_count GetMemberCount(morkEnv* ev);
+
+ virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+ virtual morkRow* PrevRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakTableRowCursor(morkTableRowCursor* me,
+ morkEnv* ev, morkTableRowCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongTableRowCursor(morkTableRowCursor* me,
+ morkEnv* ev, morkTableRowCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
diff --git a/db/mork/src/morkThumb.cpp b/db/mork/src/morkThumb.cpp
new file mode 100644
index 000000000..ed88ea971
--- /dev/null
+++ b/db/mork/src/morkThumb.cpp
@@ -0,0 +1,523 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKTHUMB_
+#include "morkThumb.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKWRITER_
+#include "morkWriter.h"
+#endif
+
+#ifndef _MORKPARSER_
+#include "morkParser.h"
+#endif
+
+#ifndef _MORKBUILDER_
+#include "morkBuilder.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkThumb::CloseMorkNode(morkEnv* ev) // CloseThumb() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseThumb(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkThumb::~morkThumb() // assert CloseThumb() executed earlier
+{
+ CloseMorkNode(mMorkEnv);
+ MORK_ASSERT(mThumb_Magic==0);
+ MORK_ASSERT(mThumb_Store==0);
+ MORK_ASSERT(mThumb_File==0);
+}
+
+/*public non-poly*/
+morkThumb::morkThumb(morkEnv* ev,
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap,
+ nsIMdbHeap* ioSlotHeap, mork_magic inMagic)
+: morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*) 0)
+, mThumb_Magic( 0 )
+, mThumb_Total( 0 )
+, mThumb_Current( 0 )
+
+, mThumb_Done( morkBool_kFalse )
+, mThumb_Broken( morkBool_kFalse )
+, mThumb_Seed( 0 )
+
+, mThumb_Store( 0 )
+, mThumb_File( 0 )
+, mThumb_Writer( 0 )
+, mThumb_Builder( 0 )
+, mThumb_SourcePort( 0 )
+
+, mThumb_DoCollect( morkBool_kFalse )
+{
+ if ( ev->Good() )
+ {
+ if ( ioSlotHeap )
+ {
+ mThumb_Magic = inMagic;
+ mNode_Derived = morkDerived_kThumb;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(morkThumb, morkObject, nsIMdbThumb)
+
+/*public non-poly*/ void
+morkThumb::CloseThumb(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ mThumb_Magic = 0;
+ if ( mThumb_Builder && mThumb_Store )
+ mThumb_Store->ForgetBuilder(ev);
+ morkBuilder::SlotStrongBuilder((morkBuilder*) 0, ev, &mThumb_Builder);
+
+ morkWriter::SlotStrongWriter((morkWriter*) 0, ev, &mThumb_Writer);
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mThumb_File);
+ morkStore::SlotStrongStore((morkStore*) 0, ev, &mThumb_Store);
+ morkStore::SlotStrongPort((morkPort*) 0, ev, &mThumb_SourcePort);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+// { ===== begin nsIMdbThumb methods =====
+NS_IMETHODIMP
+morkThumb::GetProgress(nsIMdbEnv* mev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ GetProgress(ev, outTotal, outCurrent, outDone, outBroken);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkThumb::DoMore(nsIMdbEnv* mev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ DoMore(ev, outTotal, outCurrent, outDone, outBroken);
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+
+NS_IMETHODIMP
+morkThumb::CancelAndBreakThumb(nsIMdbEnv* mev)
+{
+ nsresult outErr = NS_OK;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ mThumb_Done = morkBool_kTrue;
+ mThumb_Broken = morkBool_kTrue;
+ CloseMorkNode(ev); // should I close this here?
+ outErr = ev->AsErr();
+ }
+ return outErr;
+}
+// } ===== end nsIMdbThumb methods =====
+
+/*static*/ void morkThumb::NonThumbTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkThumb");
+}
+
+/*static*/ void morkThumb::UnsupportedThumbMagicError(morkEnv* ev)
+{
+ ev->NewError("unsupported mThumb_Magic");
+}
+
+/*static*/ void morkThumb::NilThumbStoreError(morkEnv* ev)
+{
+ ev->NewError("nil mThumb_Store");
+}
+
+/*static*/ void morkThumb::NilThumbFileError(morkEnv* ev)
+{
+ ev->NewError("nil mThumb_File");
+}
+
+/*static*/ void morkThumb::NilThumbWriterError(morkEnv* ev)
+{
+ ev->NewError("nil mThumb_Writer");
+}
+
+/*static*/ void morkThumb::NilThumbBuilderError(morkEnv* ev)
+{
+ ev->NewError("nil mThumb_Builder");
+}
+
+/*static*/ void morkThumb::NilThumbSourcePortError(morkEnv* ev)
+{
+ ev->NewError("nil mThumb_SourcePort");
+}
+
+/*static*/ morkThumb*
+morkThumb::Make_OpenFileStore(morkEnv* ev, nsIMdbHeap* ioHeap,
+ morkStore* ioStore)
+{
+ morkThumb* outThumb = 0;
+ if ( ioHeap && ioStore )
+ {
+ nsIMdbFile* file = ioStore->mStore_File;
+ if ( file )
+ {
+ mork_pos fileEof = 0;
+ file->Eof(ev->AsMdbEnv(), &fileEof);
+ if ( ev->Good() )
+ {
+ outThumb = new(*ioHeap, ev)
+ morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+ morkThumb_kMagic_OpenFileStore);
+
+ if ( outThumb )
+ {
+ morkBuilder* builder = ioStore->LazyGetBuilder(ev);
+ if ( builder )
+ {
+ outThumb->mThumb_Total = (mork_count) fileEof;
+ morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+ morkBuilder::SlotStrongBuilder(builder, ev,
+ &outThumb->mThumb_Builder);
+ }
+ }
+ }
+ }
+ else
+ ioStore->NilStoreFileError(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ return outThumb;
+}
+
+
+/*static*/ morkThumb*
+morkThumb::Make_LargeCommit(morkEnv* ev,
+ nsIMdbHeap* ioHeap, morkStore* ioStore)
+{
+ morkThumb* outThumb = 0;
+ if ( ioHeap && ioStore )
+ {
+ nsIMdbFile* file = ioStore->mStore_File;
+ if ( file )
+ {
+ outThumb = new(*ioHeap, ev)
+ morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+ morkThumb_kMagic_LargeCommit);
+
+ if ( outThumb )
+ {
+ morkWriter* writer = new(*ioHeap, ev)
+ morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap);
+ if ( writer )
+ {
+ writer->mWriter_CommitGroupIdentity =
+ ++ioStore->mStore_CommitGroupIdentity;
+ writer->mWriter_NeedDirtyAll = morkBool_kFalse;
+ outThumb->mThumb_DoCollect = morkBool_kFalse;
+ morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+
+ nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File);
+
+ outThumb->mThumb_Writer = writer; // pass writer ownership to thumb
+ }
+ }
+ }
+ else
+ ioStore->NilStoreFileError(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ return outThumb;
+}
+
+/*static*/ morkThumb*
+morkThumb::Make_CompressCommit(morkEnv* ev,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect)
+{
+ morkThumb* outThumb = 0;
+ if ( ioHeap && ioStore )
+ {
+ nsIMdbFile* file = ioStore->mStore_File;
+ if ( file )
+ {
+ outThumb = new(*ioHeap, ev)
+ morkThumb(ev, morkUsage::kHeap, ioHeap, ioHeap,
+ morkThumb_kMagic_CompressCommit);
+
+ if ( outThumb )
+ {
+ morkWriter* writer = new(*ioHeap, ev)
+ morkWriter(ev, morkUsage::kHeap, ioHeap, ioStore, file, ioHeap);
+ if ( writer )
+ {
+ writer->mWriter_NeedDirtyAll = morkBool_kTrue;
+ outThumb->mThumb_DoCollect = inDoCollect;
+ morkStore::SlotStrongStore(ioStore, ev, &outThumb->mThumb_Store);
+ nsIMdbFile_SlotStrongFile(file, ev, &outThumb->mThumb_File);
+ outThumb->mThumb_Writer = writer; // pass writer ownership to thumb
+
+ // cope with fact that parsed transaction groups are going away:
+ ioStore->mStore_FirstCommitGroupPos = 0;
+ ioStore->mStore_SecondCommitGroupPos = 0;
+ }
+ }
+ }
+ else
+ ioStore->NilStoreFileError(ev);
+ }
+ else
+ ev->NilPointerError();
+
+ return outThumb;
+}
+
+// { ===== begin non-poly methods imitating nsIMdbThumb =====
+void morkThumb::GetProgress(morkEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+ MORK_USED_1(ev);
+ if ( outTotal )
+ *outTotal = mThumb_Total;
+ if ( outCurrent )
+ *outCurrent = mThumb_Current;
+ if ( outDone )
+ *outDone = mThumb_Done;
+ if ( outBroken )
+ *outBroken = mThumb_Broken;
+}
+
+void morkThumb::DoMore(morkEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken)
+{
+ if ( !mThumb_Done && !mThumb_Broken )
+ {
+ switch ( mThumb_Magic )
+ {
+ case morkThumb_kMagic_OpenFilePort: // 1 /* factory method */
+ this->DoMore_OpenFilePort(ev); break;
+
+ case morkThumb_kMagic_OpenFileStore: // 2 /* factory method */
+ this->DoMore_OpenFileStore(ev); break;
+
+ case morkThumb_kMagic_ExportToFormat: // 3 /* port method */
+ this->DoMore_ExportToFormat(ev); break;
+
+ case morkThumb_kMagic_ImportContent: // 4 /* store method */
+ this->DoMore_ImportContent(ev); break;
+
+ case morkThumb_kMagic_LargeCommit: // 5 /* store method */
+ this->DoMore_LargeCommit(ev); break;
+
+ case morkThumb_kMagic_SessionCommit: // 6 /* store method */
+ this->DoMore_SessionCommit(ev); break;
+
+ case morkThumb_kMagic_CompressCommit: // 7 /* store method */
+ this->DoMore_CompressCommit(ev); break;
+
+ case morkThumb_kMagic_SearchManyColumns: // 8 /* table method */
+ this->DoMore_SearchManyColumns(ev); break;
+
+ case morkThumb_kMagic_NewSortColumn: // 9 /* table metho) */
+ this->DoMore_NewSortColumn(ev); break;
+
+ case morkThumb_kMagic_NewSortColumnWithCompare: // 10 /* table method */
+ this->DoMore_NewSortColumnWithCompare(ev); break;
+
+ case morkThumb_kMagic_CloneSortColumn: // 11 /* table method */
+ this->DoMore_CloneSortColumn(ev); break;
+
+ case morkThumb_kMagic_AddIndex: // 12 /* table method */
+ this->DoMore_AddIndex(ev); break;
+
+ case morkThumb_kMagic_CutIndex: // 13 /* table method */
+ this->DoMore_CutIndex(ev); break;
+
+ default:
+ this->UnsupportedThumbMagicError(ev);
+ break;
+ }
+ }
+ if ( outTotal )
+ *outTotal = mThumb_Total;
+ if ( outCurrent )
+ *outCurrent = mThumb_Current;
+ if ( outDone )
+ *outDone = mThumb_Done;
+ if ( outBroken )
+ *outBroken = mThumb_Broken;
+}
+
+void morkThumb::CancelAndBreakThumb(morkEnv* ev)
+{
+ MORK_USED_1(ev);
+ mThumb_Broken = morkBool_kTrue;
+}
+
+// } ===== end non-poly methods imitating nsIMdbThumb =====
+
+morkStore*
+morkThumb::ThumbToOpenStore(morkEnv* ev)
+// for orkinFactory::ThumbToOpenStore() after OpenFileStore()
+{
+ MORK_USED_1(ev);
+ return mThumb_Store;
+}
+
+void morkThumb::DoMore_OpenFilePort(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_OpenFileStore(morkEnv* ev)
+{
+ morkBuilder* builder = mThumb_Builder;
+ if ( builder )
+ {
+ mork_pos pos = 0;
+ builder->ParseMore(ev, &pos, &mThumb_Done, &mThumb_Broken);
+ // mThumb_Total = builder->mBuilder_TotalCount;
+ // mThumb_Current = builder->mBuilder_DoneCount;
+ mThumb_Current = (mork_count) pos;
+ }
+ else
+ {
+ this->NilThumbBuilderError(ev);
+ mThumb_Broken = morkBool_kTrue;
+ mThumb_Done = morkBool_kTrue;
+ }
+}
+
+void morkThumb::DoMore_ExportToFormat(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_ImportContent(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_LargeCommit(morkEnv* ev)
+{
+ this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_SessionCommit(morkEnv* ev)
+{
+ this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_Commit(morkEnv* ev)
+{
+ morkWriter* writer = mThumb_Writer;
+ if ( writer )
+ {
+ writer->WriteMore(ev);
+ mThumb_Total = writer->mWriter_TotalCount;
+ mThumb_Current = writer->mWriter_DoneCount;
+ mThumb_Done = ( ev->Bad() || writer->IsWritingDone() );
+ mThumb_Broken = ev->Bad();
+ }
+ else
+ {
+ this->NilThumbWriterError(ev);
+ mThumb_Broken = morkBool_kTrue;
+ mThumb_Done = morkBool_kTrue;
+ }
+}
+
+void morkThumb::DoMore_CompressCommit(morkEnv* ev)
+{
+ this->DoMore_Commit(ev);
+}
+
+void morkThumb::DoMore_SearchManyColumns(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_NewSortColumn(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_NewSortColumnWithCompare(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_CloneSortColumn(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_AddIndex(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+void morkThumb::DoMore_CutIndex(morkEnv* ev)
+{
+ this->UnsupportedThumbMagicError(ev);
+}
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkThumb.h b/db/mork/src/morkThumb.h
new file mode 100644
index 000000000..f17aee9a5
--- /dev/null
+++ b/db/mork/src/morkThumb.h
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKTHUMB_
+#define _MORKTHUMB_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKOBJECT_
+#include "morkObject.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkThumb_kMagic_OpenFilePort 1 /* factory method */
+#define morkThumb_kMagic_OpenFileStore 2 /* factory method */
+#define morkThumb_kMagic_ExportToFormat 3 /* port method */
+#define morkThumb_kMagic_ImportContent 4 /* store method */
+#define morkThumb_kMagic_LargeCommit 5 /* store method */
+#define morkThumb_kMagic_SessionCommit 6 /* store method */
+#define morkThumb_kMagic_CompressCommit 7 /* store method */
+#define morkThumb_kMagic_SearchManyColumns 8 /* table method */
+#define morkThumb_kMagic_NewSortColumn 9 /* table metho) */
+#define morkThumb_kMagic_NewSortColumnWithCompare 10 /* table method */
+#define morkThumb_kMagic_CloneSortColumn 11 /* table method */
+#define morkThumb_kMagic_AddIndex 12 /* table method */
+#define morkThumb_kMagic_CutIndex 13 /* table method */
+
+#define morkDerived_kThumb /*i*/ 0x5468 /* ascii 'Th' */
+
+/*| morkThumb:
+|*/
+class morkThumb : public morkObject, public nsIMdbThumb {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // mork_color mBead_Color; // ID for this bead
+ // morkHandle* mObject_Handle; // weak ref to handle for this object
+
+public: // state is public because the entire Mork system is private
+ NS_DECL_ISUPPORTS_INHERITED
+
+// { ===== begin nsIMdbThumb methods =====
+ NS_IMETHOD GetProgress(nsIMdbEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) override;
+
+ NS_IMETHOD DoMore(nsIMdbEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken) override;
+
+ NS_IMETHOD CancelAndBreakThumb(nsIMdbEnv* ev) override;
+// } ===== end nsIMdbThumb methods =====
+
+ // might as well include all the return values here:
+
+ mork_magic mThumb_Magic; // magic sig different in each thumb type
+ mork_count mThumb_Total;
+ mork_count mThumb_Current;
+
+ mork_bool mThumb_Done;
+ mork_bool mThumb_Broken;
+ mork_u2 mThumb_Seed; // optional seed for u4 alignment padding
+
+ morkStore* mThumb_Store; // weak ref to created store
+ nsIMdbFile* mThumb_File; // strong ref to file (store, import, export)
+ morkWriter* mThumb_Writer; // strong ref to writer (for commit)
+ morkBuilder* mThumb_Builder; // strong ref to builder (for store open)
+ morkPort* mThumb_SourcePort; // strong ref to port for import
+
+ mork_bool mThumb_DoCollect; // influence whether a collect happens
+ mork_bool mThumb_Pad[ 3 ]; // padding for u4 alignment
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseThumb() only if open
+
+public: // morkThumb construction & destruction
+ morkThumb(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, mork_magic inMagic);
+ void CloseThumb(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkThumb(const morkThumb& other);
+ morkThumb& operator=(const morkThumb& other);
+ virtual ~morkThumb(); // assert that CloseThumb() executed earlier
+
+public: // dynamic type identification
+ mork_bool IsThumb() const
+ { return IsNode() && mNode_Derived == morkDerived_kThumb; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonThumbTypeError(morkEnv* ev);
+ static void UnsupportedThumbMagicError(morkEnv* ev);
+
+ static void NilThumbStoreError(morkEnv* ev);
+ static void NilThumbFileError(morkEnv* ev);
+ static void NilThumbWriterError(morkEnv* ev);
+ static void NilThumbBuilderError(morkEnv* ev);
+ static void NilThumbSourcePortError(morkEnv* ev);
+
+public: // 'do more' methods
+
+ void DoMore_OpenFilePort(morkEnv* ev);
+ void DoMore_OpenFileStore(morkEnv* ev);
+ void DoMore_ExportToFormat(morkEnv* ev);
+ void DoMore_ImportContent(morkEnv* ev);
+ void DoMore_LargeCommit(morkEnv* ev);
+ void DoMore_SessionCommit(morkEnv* ev);
+ void DoMore_CompressCommit(morkEnv* ev);
+ void DoMore_Commit(morkEnv* ev);
+ void DoMore_SearchManyColumns(morkEnv* ev);
+ void DoMore_NewSortColumn(morkEnv* ev);
+ void DoMore_NewSortColumnWithCompare(morkEnv* ev);
+ void DoMore_CloneSortColumn(morkEnv* ev);
+ void DoMore_AddIndex(morkEnv* ev);
+ void DoMore_CutIndex(morkEnv* ev);
+
+public: // other thumb methods
+
+ morkStore* ThumbToOpenStore(morkEnv* ev);
+ // for orkinFactory::ThumbToOpenStore() after OpenFileStore()
+
+public: // assorted thumb constructors
+
+ static morkThumb* Make_OpenFileStore(morkEnv* ev,
+ nsIMdbHeap* ioHeap, morkStore* ioStore);
+
+ static morkThumb* Make_CompressCommit(morkEnv* ev,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, mork_bool inDoCollect);
+
+ static morkThumb* Make_LargeCommit(morkEnv* ev,
+ nsIMdbHeap* ioHeap, morkStore* ioStore);
+
+// { ===== begin non-poly methods imitating nsIMdbThumb =====
+ void GetProgress(morkEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+
+ void DoMore(morkEnv* ev, mdb_count* outTotal,
+ mdb_count* outCurrent, mdb_bool* outDone, mdb_bool* outBroken);
+
+ void CancelAndBreakThumb(morkEnv* ev);
+// } ===== end non-poly methods imitating nsIMdbThumb =====
+
+
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakThumb(morkThumb* me,
+ morkEnv* ev, morkThumb** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongThumb(morkThumb* me,
+ morkEnv* ev, morkThumb** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTHUMB_ */
diff --git a/db/mork/src/morkUniqRowCursor.h b/db/mork/src/morkUniqRowCursor.h
new file mode 100644
index 000000000..44eac9471
--- /dev/null
+++ b/db/mork/src/morkUniqRowCursor.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKUNIQROWCURSOR_
+#define _MORKUNIQROWCURSOR_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKCURSOR_
+#include "morkCursor.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+class orkinTableRowCursor;
+// #define morkDerived_kUniqRowCursor /*i*/ 0x7352 /* ascii 'sR' */
+
+class morkUniqRowCursor : public morkTableRowCursor { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+ // morkFactory* mObject_Factory; // weak ref to suite factory
+
+ // mork_seed mCursor_Seed;
+ // mork_pos mCursor_Pos;
+ // mork_bool mCursor_DoFailOnSeedOutOfSync;
+ // mork_u1 mCursor_Pad[ 3 ]; // explicitly pad to u4 alignment
+
+ // morkTable* mTableRowCursor_Table; // weak ref to table
+
+public: // state is public because the entire Mork system is private
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseUniqRowCursor()
+ virtual ~morkUniqRowCursor(); // assert that close executed earlier
+
+public: // morkUniqRowCursor construction & destruction
+ morkUniqRowCursor(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkTable* ioTable, mork_pos inRowPos);
+ void CloseUniqRowCursor(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkUniqRowCursor(const morkUniqRowCursor& other);
+ morkUniqRowCursor& operator=(const morkUniqRowCursor& other);
+
+public: // dynamic type identification
+ // mork_bool IsUniqRowCursor() const
+ // { return IsNode() && mNode_Derived == morkDerived_kUniqRowCursor; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonUniqRowCursorTypeError(morkEnv* ev);
+
+public: // other search row cursor methods
+
+ virtual mork_bool CanHaveDupRowMembers(morkEnv* ev);
+ virtual mork_count GetMemberCount(morkEnv* ev);
+
+ virtual orkinTableRowCursor* AcquireUniqueRowCursorHandle(morkEnv* ev);
+
+ // virtual mdb_pos NextRowOid(morkEnv* ev, mdbOid* outOid);
+ virtual morkRow* NextRow(morkEnv* ev, mdbOid* outOid, mdb_pos* outPos);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakUniqRowCursor(morkUniqRowCursor* me,
+ morkEnv* ev, morkUniqRowCursor** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongUniqRowCursor(morkUniqRowCursor* me,
+ morkEnv* ev, morkUniqRowCursor** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKUNIQROWCURSOR_ */
diff --git a/db/mork/src/morkWriter.cpp b/db/mork/src/morkWriter.cpp
new file mode 100644
index 000000000..98ca5457f
--- /dev/null
+++ b/db/mork/src/morkWriter.cpp
@@ -0,0 +1,2206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKBLOB_
+#include "morkBlob.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKARRAY_
+#include "morkWriter.h"
+#endif
+
+// #ifndef _MORKFILE_
+// #include "morkFile.h"
+// #endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+#ifndef _MORKSTORE_
+#include "morkStore.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKROW_
+#include "morkRow.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKCELL_
+#include "morkCell.h"
+#endif
+
+#ifndef _MORKATOM_
+#include "morkAtom.h"
+#endif
+
+#ifndef _MORKCH_
+#include "morkCh.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkWriter::CloseMorkNode(morkEnv* ev) // CloseTable() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseWriter(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkWriter::~morkWriter() // assert CloseTable() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+ MORK_ASSERT(mWriter_Store==0);
+}
+
+/*public non-poly*/
+morkWriter::morkWriter(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile,
+ nsIMdbHeap* ioSlotHeap)
+: morkNode(ev, inUsage, ioHeap)
+, mWriter_Store( 0 )
+, mWriter_File( 0 )
+, mWriter_Bud( 0 )
+, mWriter_Stream( 0 )
+, mWriter_SlotHeap( 0 )
+
+, mWriter_CommitGroupIdentity( 0 ) // see mStore_CommitGroupIdentity
+, mWriter_GroupBufFill( 0 )
+
+, mWriter_TotalCount( morkWriter_kCountNumberOfPhases )
+, mWriter_DoneCount( 0 )
+
+, mWriter_LineSize( 0 )
+, mWriter_MaxIndent( morkWriter_kMaxIndent )
+, mWriter_MaxLine( morkWriter_kMaxLine )
+
+, mWriter_TableForm( 0 )
+, mWriter_TableAtomScope( 'v' )
+, mWriter_TableRowScope( 0 )
+, mWriter_TableKind( 0 )
+
+, mWriter_RowForm( 0 )
+, mWriter_RowAtomScope( 0 )
+, mWriter_RowScope( 0 )
+
+, mWriter_DictForm( 0 )
+, mWriter_DictAtomScope( 'v' )
+
+, mWriter_NeedDirtyAll( morkBool_kFalse )
+, mWriter_Incremental( morkBool_kTrue ) // opposite of mWriter_NeedDirtyAll
+, mWriter_DidStartDict( morkBool_kFalse )
+, mWriter_DidEndDict( morkBool_kTrue )
+
+, mWriter_SuppressDirtyRowNewline( morkBool_kFalse )
+, mWriter_DidStartGroup( morkBool_kFalse )
+, mWriter_DidEndGroup( morkBool_kTrue )
+, mWriter_Phase( morkWriter_kPhaseNothingDone )
+
+, mWriter_BeVerbose( ev->mEnv_BeVerbose )
+
+, mWriter_TableRowArrayPos( 0 )
+
+// empty constructors for map iterators:
+, mWriter_StoreAtomSpacesIter( )
+, mWriter_AtomSpaceAtomAidsIter( )
+
+, mWriter_StoreRowSpacesIter( )
+, mWriter_RowSpaceTablesIter( )
+, mWriter_RowSpaceRowsIter( )
+{
+ mWriter_GroupBuf[ 0 ] = 0;
+
+ mWriter_SafeNameBuf[ 0 ] = 0;
+ mWriter_SafeNameBuf[ morkWriter_kMaxColumnNameSize * 2 ] = 0;
+ mWriter_ColNameBuf[ 0 ] = 0;
+ mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize ] = 0;
+
+ mdbYarn* y = &mWriter_ColYarn;
+ y->mYarn_Buf = mWriter_ColNameBuf; // where to put col bytes
+ y->mYarn_Fill = 0; // set later by writer
+ y->mYarn_Size = morkWriter_kMaxColumnNameSize; // our buf size
+ y->mYarn_More = 0; // set later by writer
+ y->mYarn_Form = 0; // set later by writer
+ y->mYarn_Grow = 0; // do not allow buffer growth
+
+ y = &mWriter_SafeYarn;
+ y->mYarn_Buf = mWriter_SafeNameBuf; // where to put col bytes
+ y->mYarn_Fill = 0; // set later by writer
+ y->mYarn_Size = morkWriter_kMaxColumnNameSize * 2; // our buf size
+ y->mYarn_More = 0; // set later by writer
+ y->mYarn_Form = 0; // set later by writer
+ y->mYarn_Grow = 0; // do not allow buffer growth
+
+ if ( ev->Good() )
+ {
+ if ( ioSlotHeap && ioFile && ioStore )
+ {
+ morkStore::SlotWeakStore(ioStore, ev, &mWriter_Store);
+ nsIMdbFile_SlotStrongFile(ioFile, ev, &mWriter_File);
+ nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mWriter_SlotHeap);
+ if ( ev->Good() )
+ {
+ mNode_Derived = morkDerived_kWriter;
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+
+void
+morkWriter::MakeWriterStream(morkEnv* ev) // give writer a suitable stream
+{
+ mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
+
+ if ( !mWriter_Stream && ev->Good() )
+ {
+ if ( mWriter_File )
+ {
+ morkStream* stream = 0;
+ mork_bool frozen = morkBool_kFalse; // need to modify
+ nsIMdbHeap* heap = mWriter_SlotHeap;
+
+ if ( mWriter_Incremental )
+ {
+ stream = new(*heap, ev)
+ morkStream(ev, morkUsage::kHeap, heap, mWriter_File,
+ morkWriter_kStreamBufSize, frozen);
+ }
+ else // compress commit
+ {
+ nsIMdbFile* bud = 0;
+ mWriter_File->AcquireBud(ev->AsMdbEnv(), heap, &bud);
+ if ( bud )
+ {
+ if ( ev->Good() )
+ {
+ mWriter_Bud = bud;
+ stream = new(*heap, ev)
+ morkStream(ev, morkUsage::kHeap, heap, bud,
+ morkWriter_kStreamBufSize, frozen);
+ }
+ else
+ bud->Release();
+ }
+ }
+
+ if ( stream )
+ {
+ if ( ev->Good() )
+ mWriter_Stream = stream;
+ else
+ stream->CutStrongRef(ev->AsMdbEnv());
+ }
+ }
+ else
+ this->NilWriterFileError(ev);
+ }
+}
+
+/*public non-poly*/ void
+morkWriter::CloseWriter(morkEnv* ev) // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ {
+ morkStore::SlotWeakStore((morkStore*) 0, ev, &mWriter_Store);
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_File);
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
+ morkStream::SlotStrongStream((morkStream*) 0, ev, &mWriter_Stream);
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mWriter_SlotHeap);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkWriter::NonWriterTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkWriter");
+}
+
+/*static*/ void
+morkWriter::NilWriterStoreError(morkEnv* ev)
+{
+ ev->NewError("nil mWriter_Store");
+}
+
+/*static*/ void
+morkWriter::NilWriterBudError(morkEnv* ev)
+{
+ ev->NewError("nil mWriter_Bud");
+}
+
+/*static*/ void
+morkWriter::NilWriterFileError(morkEnv* ev)
+{
+ ev->NewError("nil mWriter_File");
+}
+
+/*static*/ void
+morkWriter::NilWriterStreamError(morkEnv* ev)
+{
+ ev->NewError("nil mWriter_Stream");
+}
+
+/*static*/ void
+morkWriter::UnsupportedPhaseError(morkEnv* ev)
+{
+ ev->NewError("unsupported mWriter_Phase");
+}
+
+mork_bool
+morkWriter::WriteMore(morkEnv* ev) // call until IsWritingDone() is true
+{
+ if ( this->IsOpenNode() )
+ {
+ if ( this->IsWriter() )
+ {
+ if ( !mWriter_Stream )
+ this->MakeWriterStream(ev);
+
+ if ( mWriter_Stream )
+ {
+ if ( ev->Bad() )
+ {
+ ev->NewWarning("writing stops on error");
+ mWriter_Phase = morkWriter_kPhaseWritingDone;
+ }
+ switch( mWriter_Phase )
+ {
+ case morkWriter_kPhaseNothingDone:
+ OnNothingDone(ev); break;
+
+ case morkWriter_kPhaseDirtyAllDone:
+ OnDirtyAllDone(ev); break;
+
+ case morkWriter_kPhasePutHeaderDone:
+ OnPutHeaderDone(ev); break;
+
+ case morkWriter_kPhaseRenumberAllDone:
+ OnRenumberAllDone(ev); break;
+
+ case morkWriter_kPhaseStoreAtomSpaces:
+ OnStoreAtomSpaces(ev); break;
+
+ case morkWriter_kPhaseAtomSpaceAtomAids:
+ OnAtomSpaceAtomAids(ev); break;
+
+ case morkWriter_kPhaseStoreRowSpacesTables:
+ OnStoreRowSpacesTables(ev); break;
+
+ case morkWriter_kPhaseRowSpaceTables:
+ OnRowSpaceTables(ev); break;
+
+ case morkWriter_kPhaseTableRowArray:
+ OnTableRowArray(ev); break;
+
+ case morkWriter_kPhaseStoreRowSpacesRows:
+ OnStoreRowSpacesRows(ev); break;
+
+ case morkWriter_kPhaseRowSpaceRows:
+ OnRowSpaceRows(ev); break;
+
+ case morkWriter_kPhaseContentDone:
+ OnContentDone(ev); break;
+
+ case morkWriter_kPhaseWritingDone:
+ OnWritingDone(ev); break;
+
+ default:
+ this->UnsupportedPhaseError(ev);
+ }
+ }
+ else
+ this->NilWriterStreamError(ev);
+ }
+ else
+ this->NonWriterTypeError(ev);
+ }
+ else
+ this->NonOpenNodeError(ev);
+
+ return ev->Good();
+}
+
+static const char morkWriter_kHexDigits[] = "0123456789ABCDEF";
+
+mork_size
+morkWriter::WriteYarn(morkEnv* ev, const mdbYarn* inYarn)
+ // return number of atom bytes written on the current line (which
+ // implies that escaped line breaks will make the size value smaller
+ // than the entire yarn's size, since only part goes on a last line).
+{
+
+
+ mork_size outSize = 0;
+ mork_size lineSize = mWriter_LineSize;
+ morkStream* stream = mWriter_Stream;
+
+ const mork_u1* b = (const mork_u1*) inYarn->mYarn_Buf;
+ if ( b )
+ {
+ int c;
+ mork_fill fill = inYarn->mYarn_Fill;
+
+ const mork_u1* end = b + fill;
+ while ( b < end && ev->Good() )
+ {
+ if ( lineSize + outSize >= mWriter_MaxLine ) // continue line?
+ {
+ stream->PutByteThenNewline(ev, '\\');
+ mWriter_LineSize = lineSize = outSize = 0;
+ }
+
+ c = *b++; // next byte to print
+ if ( morkCh_IsValue(c) )
+ {
+ stream->Putc(ev, c);
+ ++outSize; // c
+ }
+ else if ( c == ')' || c == '$' || c == '\\' )
+ {
+ stream->Putc(ev, '\\');
+ stream->Putc(ev, c);
+ outSize += 2; // '\' c
+ }
+ else
+ {
+ outSize += 3; // '$' hex hex
+ stream->Putc(ev, '$');
+ stream->Putc(ev, morkWriter_kHexDigits[ (c >> 4) & 0x0F ]);
+ stream->Putc(ev, morkWriter_kHexDigits[ c & 0x0F ]);
+ }
+ }
+ }
+ mWriter_LineSize += outSize;
+
+ return outSize;
+}
+
+mork_size
+morkWriter::WriteAtom(morkEnv* ev, const morkAtom* inAtom)
+ // return number of atom bytes written on the current line (which
+ // implies that escaped line breaks will make the size value smaller
+ // than the entire atom's size, since only part goes on a last line).
+{
+ mork_size outSize = 0;
+ mdbYarn yarn; // to ref content inside atom
+
+ if ( morkAtom::AliasYarn(inAtom, &yarn) )
+ {
+ if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
+ this->ChangeDictForm(ev, yarn.mYarn_Form);
+
+ outSize = this->WriteYarn(ev, &yarn);
+ // mWriter_LineSize += stream->Write(ev, inYarn->mYarn_Buf, outSize);
+ }
+ else
+ inAtom->BadAtomKindError(ev);
+
+ return outSize;
+}
+
+void
+morkWriter::WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace)
+{
+ morkStream* stream = mWriter_Stream;
+ nsIMdbEnv *mdbev = ev->AsMdbEnv();
+ mork_scope scope = ioSpace->SpaceScope();
+ if ( scope < 0x80 )
+ {
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ stream->PutString(ev, "< <(a=");
+ stream->Putc(ev, (int) scope);
+ ++mWriter_LineSize;
+ stream->PutString(ev, ")> // (f=iso-8859-1)");
+ mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
+ }
+ else
+ ioSpace->NonAsciiSpaceScopeName(ev);
+
+ if ( ev->Good() )
+ {
+ mdbYarn yarn; // to ref content inside atom
+ char buf[ 64 ]; // buffer for staging the dict alias hex ID
+ char* idBuf = buf + 1; // where the id always starts
+ buf[ 0 ] = '('; // we always start with open paren
+ morkBookAtom* atom = 0;
+ morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
+ ai->InitAtomAidMapIter(ev, &ioSpace->mAtomSpace_AtomAids);
+ mork_change* c = 0;
+
+ for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
+ c = ai->NextAtom(ev, &atom) )
+ {
+ if ( atom )
+ {
+ if ( atom->IsAtomDirty() )
+ {
+ atom->SetAtomClean(); // neutralize change
+
+ morkAtom::AliasYarn(atom, &yarn);
+ mork_size size = ev->TokenAsHex(idBuf, atom->mBookAtom_Id);
+
+ if ( yarn.mYarn_Form != mWriter_DictForm )
+ this->ChangeDictForm(ev, yarn.mYarn_Form);
+
+ mork_size pending = yarn.mYarn_Fill + size +
+ morkWriter_kYarnEscapeSlop + 4;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+ mork_size bytesWritten;
+ stream->Write(mdbev, buf, size+1, &bytesWritten); // + '('
+ mWriter_LineSize += bytesWritten;
+
+ pending -= ( size + 1 );
+ this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
+ stream->Putc(ev, '='); // start alias
+ ++mWriter_LineSize;
+
+ this->WriteYarn(ev, &yarn);
+ stream->Putc(ev, ')'); // end alias
+ ++mWriter_LineSize;
+
+ ++mWriter_DoneCount;
+ }
+ }
+ else
+ ev->NilPointerError();
+ }
+ ai->CloseMapIter(ev);
+ }
+
+ if ( ev->Good() )
+ {
+ ioSpace->SetAtomSpaceClean();
+ // this->IndentAsNeeded(ev, 0);
+ // stream->PutByteThenNewline(ev, '>'); // end dict
+
+ stream->Putc(ev, '>'); // end dict
+ ++mWriter_LineSize;
+ }
+}
+
+/*
+(I'm putting the text of this message in file morkWriter.cpp.)
+
+I'm making a change which should cause rows and tables to go away
+when a Mork db is compress committed, when the rows and tables
+are no longer needed. Because this is subtle, I'm describing it
+here in case misbehavior is ever observed. Otherwise you'll have
+almost no hope of fixing a related bug.
+
+This is done entirely in morkWriter.cpp: morkWriter::DirtyAll(),
+which currently marks all rows and tables dirty so they will be
+written in a later phase of the commit. My change is to merely
+selectively not mark certain rows and tables dirty, when they seem
+to be superfluous.
+
+A row is no longer needed when the mRow_GcUses slot hits zero, and
+this is used by the following inline morkRow method:
+
+ mork_bool IsRowUsed() const { return mRow_GcUses != 0; }
+
+Naturally disaster ensues if mRow_GcUses is ever smaller than right.
+
+Similarly, we should drop tables when mTable_GcUses hits zero, but
+only when a table contains no row members. We consider tables to
+self reference (and prevent collection) when they contain content.
+Again, disaster ensues if mTable_GcUses is ever smaller than right.
+
+ mork_count GetRowCount() const
+ { return mTable_RowArray.mArray_Fill; }
+
+ mork_bool IsTableUsed() const
+ { return (mTable_GcUses != 0 || this->GetRowCount() != 0); }
+
+Now let's question why the design involves filtering what gets set
+to dirty. Why not apply a filter in the later phase when we write
+content? Because I'm afraid of missing some subtle interaction in
+updating table and row relationships. It seems safer to write a row
+or table when it starts out dirty, before morkWriter::DirtyAll() is
+called. So this design calls for writing out rows and tables when
+they are still clearly used, and additionally, <i>when we have just
+been actively writing to them right before this commit</i>.
+
+Presumably if they are truly useless, they will no longer be dirtied
+in later sessions and will get collected during the next compress
+commit. So we wait to collect them until they become all dead, and
+not just mostly dead. (At which time you can feel free to go through
+their pockets looking for loose change.)
+*/
+
+mork_bool
+morkWriter::DirtyAll(morkEnv* ev)
+ // DirtyAll() visits every store sub-object and marks
+ // them dirty, including every table, row, cell, and atom. The return
+ // equals ev->Good(), to show whether any error happened. This method is
+ // intended for use in the beginning of a "compress commit" which writes
+ // all store content, whether dirty or not. We dirty everything first so
+ // that later iterations over content can mark things clean as they are
+ // written, and organize the process of serialization so that objects are
+ // written only at need (because of being dirty). Note the method can
+ // stop early when any error happens, since this will abort any commit.
+{
+ morkStore* store = mWriter_Store;
+ if ( store )
+ {
+ store->SetStoreDirty();
+ mork_change* c = 0;
+
+ if ( ev->Good() )
+ {
+ morkAtomSpaceMapIter* asi = &mWriter_StoreAtomSpacesIter;
+ asi->InitAtomSpaceMapIter(ev, &store->mStore_AtomSpaces);
+
+ mork_scope* key = 0; // ignore keys in map
+ morkAtomSpace* space = 0; // old val node in the map
+
+ for ( c = asi->FirstAtomSpace(ev, key, &space); c && ev->Good();
+ c = asi->NextAtomSpace(ev, key, &space) )
+ {
+ if ( space )
+ {
+ if ( space->IsAtomSpace() )
+ {
+ space->SetAtomSpaceDirty();
+ morkBookAtom* atom = 0;
+ morkAtomAidMapIter* ai = &mWriter_AtomSpaceAtomAidsIter;
+ ai->InitAtomAidMapIter(ev, &space->mAtomSpace_AtomAids);
+
+ for ( c = ai->FirstAtom(ev, &atom); c && ev->Good();
+ c = ai->NextAtom(ev, &atom) )
+ {
+ if ( atom )
+ {
+ atom->SetAtomDirty();
+ ++mWriter_TotalCount;
+ }
+ else
+ ev->NilPointerError();
+ }
+
+ ai->CloseMapIter(ev);
+ }
+ else
+ space->NonAtomSpaceTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+
+ if ( ev->Good() )
+ {
+ morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
+ rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+
+ mork_scope* key = 0; // ignore keys in map
+ morkRowSpace* space = 0; // old val node in the map
+
+ for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
+ c = rsi->NextRowSpace(ev, key, &space) )
+ {
+ if ( space )
+ {
+ if ( space->IsRowSpace() )
+ {
+ space->SetRowSpaceDirty();
+ if ( ev->Good() )
+ {
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
+
+ morkRow* row = 0; // old key row in the map
+
+ for ( c = ri->FirstRow(ev, &row); c && ev->Good();
+ c = ri->NextRow(ev, &row) )
+ {
+ if ( row && row->IsRow() ) // need to dirty row?
+ {
+ if ( row->IsRowUsed() || row->IsRowDirty() )
+ {
+ row->DirtyAllRowContent(ev);
+ ++mWriter_TotalCount;
+ }
+ }
+ else
+ row->NonRowTypeWarning(ev);
+ }
+ ri->CloseMapIter(ev);
+ }
+
+ if ( ev->Good() )
+ {
+ morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
+ ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ morkTable* table = ti->FirstTable(ev);
+
+ for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ mork_tid* tableKey = 0; // ignore keys in table map
+ morkTable* table = 0; // old key row in the map
+
+ for ( c = ti->FirstTable(ev, tableKey, &table); c && ev->Good();
+ c = ti->NextTable(ev, tableKey, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+ {
+ if ( table && table->IsTable() ) // need to dirty table?
+ {
+ if ( table->IsTableUsed() || table->IsTableDirty() )
+ {
+ // table->DirtyAllTableContent(ev);
+ // only necessary to mark table itself dirty:
+ table->SetTableDirty();
+ table->SetTableRewrite();
+ ++mWriter_TotalCount;
+ }
+ }
+ else
+ table->NonTableTypeWarning(ev);
+ }
+ ti->CloseMapIter(ev);
+ }
+ }
+ else
+ space->NonRowSpaceTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+ }
+ else
+ this->NilWriterStoreError(ev);
+
+ return ev->Good();
+}
+
+
+mork_bool
+morkWriter::OnNothingDone(morkEnv* ev)
+{
+ mWriter_Incremental = !mWriter_NeedDirtyAll; // opposites
+
+ if (!mWriter_Store->IsStoreDirty() && !mWriter_NeedDirtyAll)
+ {
+ mWriter_Phase = morkWriter_kPhaseWritingDone;
+ return morkBool_kTrue;
+ }
+
+ // morkStream* stream = mWriter_Stream;
+ if ( mWriter_NeedDirtyAll )
+ this->DirtyAll(ev);
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseDirtyAllDone;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::StartGroup(morkEnv* ev)
+{
+ nsIMdbEnv *mdbev = ev->AsMdbEnv();
+ morkStream* stream = mWriter_Stream;
+ mWriter_DidStartGroup = morkBool_kTrue;
+ mWriter_DidEndGroup = morkBool_kFalse;
+
+ char buf[ 64 ];
+ char* p = buf;
+ *p++ = '@';
+ *p++ = '$';
+ *p++ = '$';
+ *p++ = '{';
+
+ mork_token groupID = mWriter_CommitGroupIdentity;
+ mork_fill idFill = ev->TokenAsHex(p, groupID);
+ mWriter_GroupBufFill = 0;
+ // ev->TokenAsHex(mWriter_GroupBuf, groupID);
+ if ( idFill < morkWriter_kGroupBufSize )
+ {
+ MORK_MEMCPY(mWriter_GroupBuf, p, idFill + 1);
+ mWriter_GroupBufFill = idFill;
+ }
+ else
+ *mWriter_GroupBuf = 0;
+
+ p += idFill;
+ *p++ = '{';
+ *p++ = '@';
+ *p = 0;
+
+ stream->PutLineBreak(ev);
+
+ morkStore* store = mWriter_Store;
+ if ( store ) // might need to capture commit group position?
+ {
+ mork_pos groupPos;
+ stream->Tell(mdbev, &groupPos);
+ if ( !store->mStore_FirstCommitGroupPos )
+ store->mStore_FirstCommitGroupPos = groupPos;
+ else if ( !store->mStore_SecondCommitGroupPos )
+ store->mStore_SecondCommitGroupPos = groupPos;
+ }
+
+ mork_size bytesWritten;
+ stream->Write(mdbev, buf, idFill + 6, &bytesWritten); // '@$${' + idFill + '{@'
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::CommitGroup(morkEnv* ev)
+{
+ if ( mWriter_DidStartGroup )
+ {
+ nsIMdbEnv *mdbev = ev->AsMdbEnv();
+ mork_size bytesWritten;
+ morkStream* stream = mWriter_Stream;
+
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ stream->Putc(ev, '@');
+ stream->Putc(ev, '$');
+ stream->Putc(ev, '$');
+ stream->Putc(ev, '}');
+
+ mork_fill bufFill = mWriter_GroupBufFill;
+ if ( bufFill )
+ stream->Write(mdbev, mWriter_GroupBuf, bufFill, &bytesWritten);
+
+ stream->Putc(ev, '}');
+ stream->Putc(ev, '@');
+ stream->PutLineBreak(ev);
+
+ mWriter_LineSize = 0;
+ }
+
+ mWriter_DidStartGroup = morkBool_kFalse;
+ mWriter_DidEndGroup = morkBool_kTrue;
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::AbortGroup(morkEnv* ev)
+{
+ if ( mWriter_DidStartGroup )
+ {
+ morkStream* stream = mWriter_Stream;
+ stream->PutLineBreak(ev);
+ stream->PutStringThenNewline(ev, "@$$}~~}@");
+ mWriter_LineSize = 0;
+ }
+
+ mWriter_DidStartGroup = morkBool_kFalse;
+ mWriter_DidEndGroup = morkBool_kTrue;
+
+ return ev->Good();
+}
+
+
+mork_bool
+morkWriter::OnDirtyAllDone(morkEnv* ev)
+{
+ if ( ev->Good() )
+ {
+ nsIMdbEnv *mdbev = ev->AsMdbEnv();
+ morkStream* stream = mWriter_Stream;
+ mork_pos resultPos;
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+
+ stream->Seek(mdbev, 0, &resultPos); // beginning of stream
+ stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
+ mWriter_LineSize = 0;
+ }
+ else // else mWriter_Incremental
+ {
+ mork_pos eos = stream->Length(ev); // length is end of stream
+ if ( ev->Good() )
+ {
+ stream->Seek(mdbev, eos, &resultPos); // goto end of stream
+ if ( eos < 128 ) // maybe need file header?
+ {
+ stream->PutStringThenNewline(ev, morkWriter_kFileHeader);
+ mWriter_LineSize = 0;
+ }
+ this->StartGroup(ev); // begin incremental transaction
+ }
+ }
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhasePutHeaderDone;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnPutHeaderDone(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnPutHeaderDone()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ morkStore* store = mWriter_Store;
+ if ( store )
+ store->RenumberAllCollectableContent(ev);
+ else
+ this->NilWriterStoreError(ev);
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseRenumberAllDone;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRenumberAllDone(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnRenumberAllDone()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreAtomSpaces;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnStoreAtomSpaces(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnStoreAtomSpaces()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ {
+ morkStore* store = mWriter_Store;
+ if ( store )
+ {
+ morkAtomSpace* space = store->LazyGetGroundColumnSpace(ev);
+ if ( space && space->IsAtomSpaceDirty() )
+ {
+ // stream->PutStringThenNewline(ev, "// ground column space dict:");
+
+ if ( mWriter_LineSize )
+ {
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+ }
+ this->WriteAtomSpaceAsDict(ev, space);
+ space->SetAtomSpaceClean();
+ }
+ }
+ else
+ this->NilWriterStoreError(ev);
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnAtomSpaceAtomAids(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnAtomSpaceAtomAids()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreRowSpacesTables;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+void
+morkWriter::WriteAllStoreTables(morkEnv* ev)
+{
+ morkStore* store = mWriter_Store;
+ if ( store && ev->Good() )
+ {
+ morkRowSpaceMapIter* rsi = &mWriter_StoreRowSpacesIter;
+ rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);
+
+ mork_scope* key = 0; // ignore keys in map
+ morkRowSpace* space = 0; // old val node in the map
+ mork_change* c = 0;
+
+ for ( c = rsi->FirstRowSpace(ev, key, &space); c && ev->Good();
+ c = rsi->NextRowSpace(ev, key, &space) )
+ {
+ if ( space )
+ {
+ if ( space->IsRowSpace() )
+ {
+ space->SetRowSpaceClean();
+ if ( ev->Good() )
+ {
+ morkTableMapIter* ti = &mWriter_RowSpaceTablesIter;
+ ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
+
+#ifdef MORK_BEAD_OVER_NODE_MAPS
+ morkTable* table = ti->FirstTable(ev);
+
+ for ( ; table && ev->Good(); table = ti->NextTable(ev) )
+#else /*MORK_BEAD_OVER_NODE_MAPS*/
+ mork_tid* key2 = 0; // ignore keys in table map
+ morkTable* table = 0; // old key row in the map
+
+ for ( c = ti->FirstTable(ev, key2, &table); c && ev->Good();
+ c = ti->NextTable(ev, key2, &table) )
+#endif /*MORK_BEAD_OVER_NODE_MAPS*/
+ {
+ if ( table && table->IsTable() )
+ {
+ if ( table->IsTableDirty() )
+ {
+ mWriter_BeVerbose =
+ ( ev->mEnv_BeVerbose || table->IsTableVerbose() );
+
+ if ( this->PutTableDict(ev, table) )
+ this->PutTable(ev, table);
+
+ table->SetTableClean(ev);
+ mWriter_BeVerbose = ev->mEnv_BeVerbose;
+ }
+ }
+ else
+ table->NonTableTypeWarning(ev);
+ }
+ ti->CloseMapIter(ev);
+ }
+ if ( ev->Good() )
+ {
+ mWriter_TableRowScope = 0; // ensure no table context now
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMapIter* ri = &mWriter_RowSpaceRowsIter;
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMapIter* ri = &mWriter_RowSpaceRowsIter;
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+ ri->InitRowMapIter(ev, &space->mRowSpace_Rows);
+
+ morkRow* row = 0; // old row in the map
+
+ for ( c = ri->FirstRow(ev, &row); c && ev->Good();
+ c = ri->NextRow(ev, &row) )
+ {
+ if ( row && row->IsRow() )
+ {
+ // later we should also check that table use count is nonzero:
+ if ( row->IsRowDirty() ) // && row->IsRowUsed() ??
+ {
+ mWriter_BeVerbose = ev->mEnv_BeVerbose;
+ if ( this->PutRowDict(ev, row) )
+ {
+ if ( ev->Good() && mWriter_DidStartDict )
+ {
+ this->EndDict(ev);
+ if ( mWriter_LineSize < 32 && ev->Good() )
+ mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
+ }
+
+ if ( ev->Good() )
+ this->PutRow(ev, row);
+ }
+ mWriter_BeVerbose = ev->mEnv_BeVerbose;
+ }
+ }
+ else
+ row->NonRowTypeWarning(ev);
+ }
+ ri->CloseMapIter(ev);
+ }
+ }
+ else
+ space->NonRowSpaceTypeError(ev);
+ }
+ else
+ ev->NilPointerError();
+ }
+ }
+}
+
+mork_bool
+morkWriter::OnStoreRowSpacesTables(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesTables()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ // later we'll break this up, but today we'll write all in one shot:
+ this->WriteAllStoreTables(ev);
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRowSpaceTables(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnRowSpaceTables()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnTableRowArray(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnTableRowArray()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseStoreRowSpacesRows;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnStoreRowSpacesRows(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnStoreRowSpacesRows()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseContentDone;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnRowSpaceRows(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnRowSpaceRows()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_NeedDirtyAll ) // compress commit
+ {
+ }
+
+ if ( ev->Good() )
+ mWriter_Phase = morkWriter_kPhaseContentDone;
+ else
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop on error
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnContentDone(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+
+ // if ( mWriter_NeedDirtyAll )
+ // stream->PutStringThenNewline(ev, "// OnContentDone()");
+ mWriter_LineSize = 0;
+
+ if ( mWriter_Incremental )
+ {
+ if ( ev->Good() )
+ this->CommitGroup(ev);
+ else
+ this->AbortGroup(ev);
+ }
+ else if ( mWriter_Store && ev->Good() )
+ {
+ // after rewriting everything, there are no transaction groups:
+ mWriter_Store->mStore_FirstCommitGroupPos = 0;
+ mWriter_Store->mStore_SecondCommitGroupPos = 0;
+ }
+
+ stream->Flush(ev->AsMdbEnv());
+ nsIMdbFile* bud = mWriter_Bud;
+ if ( bud )
+ {
+ bud->Flush(ev->AsMdbEnv());
+ bud->BecomeTrunk(ev->AsMdbEnv());
+ nsIMdbFile_SlotStrongFile((nsIMdbFile*) 0, ev, &mWriter_Bud);
+ }
+ else if ( !mWriter_Incremental ) // should have a bud?
+ this->NilWriterBudError(ev);
+
+ mWriter_Phase = morkWriter_kPhaseWritingDone; // stop always
+ mWriter_DoneCount = mWriter_TotalCount;
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::OnWritingDone(morkEnv* ev)
+{
+ mWriter_DoneCount = mWriter_TotalCount;
+ ev->NewWarning("writing is done");
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTableChange(morkEnv* ev, const morkTableChange* inChange)
+{
+ nsIMdbEnv *mdbev = ev->AsMdbEnv();
+ if ( inChange->IsAddRowTableChange() )
+ {
+ this->PutRow(ev, inChange->mTableChange_Row ); // row alone means add
+ }
+ else if ( inChange->IsCutRowTableChange() )
+ {
+ mWriter_Stream->Putc(ev, '-'); // prefix '-' indicates cut row
+ ++mWriter_LineSize;
+ this->PutRow(ev, inChange->mTableChange_Row );
+ }
+ else if ( inChange->IsMoveRowTableChange() )
+ {
+ this->PutRow(ev, inChange->mTableChange_Row );
+ char buf[ 64 ];
+ char* p = buf;
+ *p++ = '!'; // for moves, position is indicated by prefix '!'
+ mork_size posSize = ev->TokenAsHex(p, inChange->mTableChange_Pos);
+ p += posSize;
+ *p++ = ' ';
+ mork_size bytesWritten;
+ mWriter_Stream->Write(mdbev, buf, posSize + 2, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+ }
+ else
+ inChange->UnknownChangeError(ev);
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTable(morkEnv* ev, morkTable* ioTable)
+{
+ if ( ev->Good() )
+ this->StartTable(ev, ioTable);
+
+ if ( ev->Good() )
+ {
+ if ( ioTable->IsTableRewrite() || mWriter_NeedDirtyAll )
+ {
+ morkArray* array = &ioTable->mTable_RowArray; // vector of rows
+ mork_fill fill = array->mArray_Fill; // count of rows
+ morkRow** rows = (morkRow**) array->mArray_Slots;
+ if ( rows && fill )
+ {
+ morkRow** end = rows + fill;
+ while ( rows < end && ev->Good() )
+ {
+ morkRow* r = *rows++; // next row to consider
+ this->PutRow(ev, r);
+ }
+ }
+ }
+ else // incremental write only table changes
+ {
+ morkList* list = &ioTable->mTable_ChangeList;
+ morkNext* next = list->GetListHead();
+ while ( next && ev->Good() )
+ {
+ this->PutTableChange(ev, (morkTableChange*) next);
+ next = next->GetNextLink();
+ }
+ }
+ }
+
+ if ( ev->Good() )
+ this->EndTable(ev);
+
+ ioTable->SetTableClean(ev); // note this also cleans change list
+ mWriter_TableRowScope = 0;
+
+ ++mWriter_DoneCount;
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutTableDict(morkEnv* ev, morkTable* ioTable)
+{
+ morkRowSpace* space = ioTable->mTable_RowSpace;
+ mWriter_TableRowScope = space->SpaceScope();
+ mWriter_TableForm = 0; // (f=iso-8859-1)
+ mWriter_TableAtomScope = 'v'; // (a=v)
+ mWriter_TableKind = ioTable->mTable_Kind;
+
+ mWriter_RowForm = mWriter_TableForm;
+ mWriter_RowAtomScope = mWriter_TableAtomScope;
+ mWriter_RowScope = mWriter_TableRowScope;
+
+ mWriter_DictForm = mWriter_TableForm;
+ mWriter_DictAtomScope = mWriter_TableAtomScope;
+
+ // if ( ev->Good() )
+ // this->StartDict(ev); // delay as long as possible
+
+ if ( ev->Good() )
+ {
+ morkRow* r = ioTable->mTable_MetaRow;
+ if ( r )
+ {
+ if ( r->IsRow() )
+ this->PutRowDict(ev, r);
+ else
+ r->NonRowTypeError(ev);
+ }
+ morkArray* array = &ioTable->mTable_RowArray; // vector of rows
+ mork_fill fill = array->mArray_Fill; // count of rows
+ morkRow** rows = (morkRow**) array->mArray_Slots;
+ if ( rows && fill )
+ {
+ morkRow** end = rows + fill;
+ while ( rows < end && ev->Good() )
+ {
+ r = *rows++; // next row to consider
+ if ( r && r->IsRow() )
+ this->PutRowDict(ev, r);
+ else
+ r->NonRowTypeError(ev);
+ }
+ }
+ // we may have a change for a row which is no longer in the
+ // table, but contains a cell with something not in the dictionary.
+ // So, loop through the rows in the change log, writing out any
+ // dirty dictionary elements.
+ morkList* list = &ioTable->mTable_ChangeList;
+ morkNext* next = list->GetListHead();
+ while ( next && ev->Good() )
+ {
+ r = ((morkTableChange*) next)->mTableChange_Row;
+ if ( r && r->IsRow() )
+ this->PutRowDict(ev, r);
+ next = next->GetNextLink();
+ }
+ }
+ if ( ev->Good() )
+ this->EndDict(ev);
+
+ return ev->Good();
+}
+
+void
+morkWriter::WriteTokenToTokenMetaCell(morkEnv* ev,
+ mork_token inCol, mork_token inValue)
+{
+ morkStream* stream = mWriter_Stream;
+ mork_bool isKindCol = ( morkStore_kKindColumn == inCol );
+ mork_u1 valSep = (mork_u1) (( isKindCol )? '^' : '=');
+
+ char buf[ 128 ]; // buffer for staging the two hex IDs
+ char* p = buf;
+
+ mork_size bytesWritten;
+ if ( inCol < 0x80 )
+ {
+ stream->Putc(ev, '(');
+ stream->Putc(ev, (char) inCol);
+ stream->Putc(ev, valSep);
+ }
+ else
+ {
+ *p++ = '('; // we always start with open paren
+
+ *p++ = '^'; // indicates col is hex ID
+ mork_size colSize = ev->TokenAsHex(p, inCol);
+ p += colSize;
+ *p++ = (char) valSep;
+ stream->Write(ev->AsMdbEnv(), buf, colSize + 3, &bytesWritten);
+
+ mWriter_LineSize += bytesWritten;
+ }
+
+ if ( isKindCol )
+ {
+ p = buf;
+ mork_size valSize = ev->TokenAsHex(p, inValue);
+ p += valSize;
+ *p++ = ':';
+ *p++ = 'c';
+ *p++ = ')';
+ stream->Write(ev->AsMdbEnv(), buf, valSize + 3, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+ }
+ else
+ {
+ this->IndentAsNeeded(ev, morkWriter_kTableMetaCellValueDepth);
+ mdbYarn* yarn = &mWriter_ColYarn;
+ // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
+ mWriter_Store->TokenToString(ev, inValue, yarn);
+ this->WriteYarn(ev, yarn);
+ stream->Putc(ev, ')');
+ ++mWriter_LineSize;
+ }
+
+ // mork_fill fill = yarn->mYarn_Fill;
+ // yarnBuf[ fill ] = ')'; // append terminator
+ // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
+}
+
+void
+morkWriter::WriteStringToTokenDictCell(morkEnv* ev,
+ const char* inCol, mork_token inValue)
+ // Note inCol should begin with '(' and end with '=', with col in between.
+{
+ morkStream* stream = mWriter_Stream;
+ mWriter_LineSize += stream->PutString(ev, inCol);
+
+ this->IndentAsNeeded(ev, morkWriter_kDictMetaCellValueDepth);
+ mdbYarn* yarn = &mWriter_ColYarn;
+ // mork_u1* yarnBuf = (mork_u1*) yarn->mYarn_Buf;
+ mWriter_Store->TokenToString(ev, inValue, yarn);
+ this->WriteYarn(ev, yarn);
+ stream->Putc(ev, ')');
+ ++mWriter_LineSize;
+
+ // mork_fill fill = yarn->mYarn_Fill;
+ // yarnBuf[ fill ] = ')'; // append terminator
+ // mWriter_LineSize += stream->Write(ev, yarnBuf, fill + 1); // +1 for ')'
+}
+
+void
+morkWriter::ChangeDictAtomScope(morkEnv* ev, mork_scope inScope)
+{
+ if ( inScope != mWriter_DictAtomScope )
+ {
+ ev->NewWarning("unexpected atom scope change");
+
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+
+ char buf[ 128 ]; // buffer for staging the two hex IDs
+ char* p = buf;
+ *p++ = '<'; // we always start with open paren
+ *p++ = '('; // we always start with open paren
+ *p++ = (char) morkStore_kAtomScopeColumn;
+
+ mork_size scopeSize = 1; // default to one byte
+ if ( inScope >= 0x80 )
+ {
+ *p++ = '^'; // indicates col is hex ID
+ scopeSize = ev->TokenAsHex(p, inScope);
+ p += scopeSize;
+ }
+ else
+ {
+ *p++ = '='; // indicates col is imm byte
+ *p++ = (char) (mork_u1) inScope;
+ }
+
+ *p++ = ')';
+ *p++ = '>';
+ *p = 0;
+
+ mork_size pending = scopeSize + 6;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+ mork_size bytesWritten;
+
+ stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ mWriter_DictAtomScope = inScope;
+ }
+}
+
+void
+morkWriter::ChangeRowForm(morkEnv* ev, mork_cscode inNewForm)
+{
+ if ( inNewForm != mWriter_RowForm )
+ {
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+
+ char buf[ 128 ]; // buffer for staging the two hex IDs
+ char* p = buf;
+ *p++ = '['; // we always start with open bracket
+ *p++ = '('; // we always start with open paren
+ *p++ = (char) morkStore_kFormColumn;
+
+ mork_size formSize = 1; // default to one byte
+ if (! morkCh_IsValue(inNewForm))
+ {
+ *p++ = '^'; // indicates col is hex ID
+ formSize = ev->TokenAsHex(p, inNewForm);
+ p += formSize;
+ }
+ else
+ {
+ *p++ = '='; // indicates col is imm byte
+ *p++ = (char) (mork_u1) inNewForm;
+ }
+
+ *p++ = ')';
+ *p++ = ']';
+ *p = 0;
+
+ mork_size pending = formSize + 6;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+ mork_size bytesWritten;
+ stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ mWriter_RowForm = inNewForm;
+ }
+}
+
+void
+morkWriter::ChangeDictForm(morkEnv* ev, mork_cscode inNewForm)
+{
+ if ( inNewForm != mWriter_DictForm )
+ {
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+
+ char buf[ 128 ]; // buffer for staging the two hex IDs
+ char* p = buf;
+ *p++ = '<'; // we always start with open angle
+ *p++ = '('; // we always start with open paren
+ *p++ = (char) morkStore_kFormColumn;
+
+ mork_size formSize = 1; // default to one byte
+ if (! morkCh_IsValue(inNewForm))
+ {
+ *p++ = '^'; // indicates col is hex ID
+ formSize = ev->TokenAsHex(p, inNewForm);
+ p += formSize;
+ }
+ else
+ {
+ *p++ = '='; // indicates col is imm byte
+ *p++ = (char) (mork_u1) inNewForm;
+ }
+
+ *p++ = ')';
+ *p++ = '>';
+ *p = 0;
+
+ mork_size pending = formSize + 6;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasDepth);
+
+ mork_size bytesWritten;
+ stream->Write(ev->AsMdbEnv(), buf, pending, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ mWriter_DictForm = inNewForm;
+ }
+}
+
+void
+morkWriter::StartDict(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_DidStartDict )
+ {
+ stream->Putc(ev, '>'); // end dict
+ ++mWriter_LineSize;
+ }
+ mWriter_DidStartDict = morkBool_kTrue;
+ mWriter_DidEndDict = morkBool_kFalse;
+
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+
+ if ( mWriter_TableRowScope ) // blank line before table's dict?
+ stream->PutLineBreak(ev);
+
+ if ( mWriter_DictForm || mWriter_DictAtomScope != 'v' )
+ {
+ stream->Putc(ev, '<');
+ stream->Putc(ev, ' ');
+ stream->Putc(ev, '<');
+ mWriter_LineSize = 3;
+ if ( mWriter_DictForm )
+ this->WriteStringToTokenDictCell(ev, "(f=", mWriter_DictForm);
+ if ( mWriter_DictAtomScope != 'v' )
+ this->WriteStringToTokenDictCell(ev, "(a=", mWriter_DictAtomScope);
+
+ stream->Putc(ev, '>');
+ ++mWriter_LineSize;
+
+ mWriter_LineSize = stream->PutIndent(ev, morkWriter_kDictAliasDepth);
+ }
+ else
+ {
+ stream->Putc(ev, '<');
+ // stream->Putc(ev, ' ');
+ ++mWriter_LineSize;
+ }
+}
+
+void
+morkWriter::EndDict(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_DidStartDict )
+ {
+ stream->Putc(ev, '>'); // end dict
+ ++mWriter_LineSize;
+ }
+ mWriter_DidStartDict = morkBool_kFalse;
+ mWriter_DidEndDict = morkBool_kTrue;
+}
+
+void
+morkWriter::StartTable(morkEnv* ev, morkTable* ioTable)
+{
+ mdbOid toid; // to receive table oid
+ ioTable->GetTableOid(ev, &toid);
+
+ if ( ev->Good() )
+ {
+ morkStream* stream = mWriter_Stream;
+ if ( mWriter_LineSize )
+ stream->PutLineBreak(ev);
+ mWriter_LineSize = 0;
+ // stream->PutLineBreak(ev);
+
+ char buf[ 64 + 16 ]; // buffer for staging hex
+ char* p = buf;
+ *p++ = '{'; // punct 1
+ mork_size punctSize = (mWriter_BeVerbose) ? 10 : 3; // counting "{ {/*r=*/ "
+
+ if ( ioTable->IsTableRewrite() && mWriter_Incremental )
+ {
+ *p++ = '-';
+ ++punctSize; // counting '-' // punct ++
+ ++mWriter_LineSize;
+ }
+ mork_size oidSize = ev->OidAsHex(p, toid);
+ p += oidSize;
+ *p++ = ' '; // punct 2
+ *p++ = '{'; // punct 3
+ if (mWriter_BeVerbose)
+ {
+
+ *p++ = '/'; // punct=4
+ *p++ = '*'; // punct=5
+ *p++ = 'r'; // punct=6
+ *p++ = '='; // punct=7
+
+ mork_token tableUses = (mork_token) ioTable->mTable_GcUses;
+ mork_size usesSize = ev->TokenAsHex(p, tableUses);
+ punctSize += usesSize;
+ p += usesSize;
+
+ *p++ = '*'; // punct=8
+ *p++ = '/'; // punct=9
+ *p++ = ' '; // punct=10
+ }
+ mork_size bytesWritten;
+
+ stream->Write(ev->AsMdbEnv(), buf, oidSize + punctSize, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ mork_kind tk = mWriter_TableKind;
+ if ( tk )
+ {
+ this->IndentAsNeeded(ev, morkWriter_kTableMetaCellDepth);
+ this->WriteTokenToTokenMetaCell(ev, morkStore_kKindColumn, tk);
+ }
+
+ stream->Putc(ev, '('); // start 's' col cell
+ stream->Putc(ev, 's'); // column
+ stream->Putc(ev, '='); // column
+ mWriter_LineSize += 3;
+
+ int prio = (int) ioTable->mTable_Priority;
+ if ( prio > 9 ) // need to force down to max decimal digit?
+ prio = 9;
+ prio += '0'; // add base digit zero
+ stream->Putc(ev, prio); // priority: (s=0
+ ++mWriter_LineSize;
+
+ if ( ioTable->IsTableUnique() )
+ {
+ stream->Putc(ev, 'u'); // (s=0u
+ ++mWriter_LineSize;
+ }
+ if ( ioTable->IsTableVerbose() )
+ {
+ stream->Putc(ev, 'v'); // (s=0uv
+ ++mWriter_LineSize;
+ }
+
+ // stream->Putc(ev, ':'); // (s=0uv:
+ // stream->Putc(ev, 'c'); // (s=0uv:c
+ stream->Putc(ev, ')'); // end 's' col cell (s=0uv:c)
+ mWriter_LineSize += 1; // maybe 3 if we add ':' and 'c'
+
+ morkRow* r = ioTable->mTable_MetaRow;
+ if ( r )
+ {
+ if ( r->IsRow() )
+ {
+ mWriter_SuppressDirtyRowNewline = morkBool_kTrue;
+ this->PutRow(ev, r);
+ }
+ else
+ r->NonRowTypeError(ev);
+ }
+
+ stream->Putc(ev, '}'); // end meta
+ ++mWriter_LineSize;
+
+ if ( mWriter_LineSize < mWriter_MaxIndent )
+ {
+ stream->Putc(ev, ' '); // nice white space
+ ++mWriter_LineSize;
+ }
+ }
+}
+
+void
+morkWriter::EndTable(morkEnv* ev)
+{
+ morkStream* stream = mWriter_Stream;
+ stream->Putc(ev, '}'); // end table
+ ++mWriter_LineSize;
+
+ mWriter_TableAtomScope = 'v'; // (a=v)
+}
+
+mork_bool
+morkWriter::PutRowDict(morkEnv* ev, morkRow* ioRow)
+{
+ mWriter_RowForm = mWriter_TableForm;
+
+ morkCell* cells = ioRow->mRow_Cells;
+ if ( cells )
+ {
+ morkStream* stream = mWriter_Stream;
+ mdbYarn yarn; // to ref content inside atom
+ char buf[ 64 ]; // buffer for staging the dict alias hex ID
+ char* idBuf = buf + 1; // where the id always starts
+ buf[ 0 ] = '('; // we always start with open paren
+
+ morkCell* end = cells + ioRow->mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end && ev->Good() )
+ {
+ morkAtom* atom = cells->GetAtom();
+ if ( atom && atom->IsAtomDirty() )
+ {
+ if ( atom->IsBook() ) // is it possible to write atom ID?
+ {
+ if ( !this->DidStartDict() )
+ {
+ this->StartDict(ev);
+ if ( ev->Bad() )
+ break;
+ }
+ atom->SetAtomClean(); // neutralize change
+
+ this->IndentAsNeeded(ev, morkWriter_kDictAliasDepth);
+ morkBookAtom* ba = (morkBookAtom*) atom;
+ mork_size size = ev->TokenAsHex(idBuf, ba->mBookAtom_Id);
+ mork_size bytesWritten;
+ stream->Write(ev->AsMdbEnv(), buf, size+1, &bytesWritten); // '('
+ mWriter_LineSize += bytesWritten;
+
+ if ( morkAtom::AliasYarn(atom, &yarn) )
+ {
+ mork_scope atomScope = atom->GetBookAtomSpaceScope(ev);
+ if ( atomScope && atomScope != mWriter_DictAtomScope )
+ this->ChangeDictAtomScope(ev, atomScope);
+
+ if ( mWriter_DidStartDict && yarn.mYarn_Form != mWriter_DictForm )
+ this->ChangeDictForm(ev, yarn.mYarn_Form);
+
+ mork_size pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop + 1;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kDictAliasValueDepth);
+
+ stream->Putc(ev, '='); // start value
+ ++mWriter_LineSize;
+
+ this->WriteYarn(ev, &yarn);
+
+ stream->Putc(ev, ')'); // end value
+ ++mWriter_LineSize;
+ }
+ else
+ atom->BadAtomKindError(ev);
+
+ ++mWriter_DoneCount;
+ }
+ }
+ }
+ }
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::IsYarnAllValue(const mdbYarn* inYarn)
+{
+ mork_fill fill = inYarn->mYarn_Fill;
+ const mork_u1* buf = (const mork_u1*) inYarn->mYarn_Buf;
+ const mork_u1* end = buf + fill;
+ --buf; // prepare for preincrement
+ while ( ++buf < end )
+ {
+ mork_ch c = *buf;
+ if ( !morkCh_IsValue(c) )
+ return morkBool_kFalse;
+ }
+ return morkBool_kTrue;
+}
+
+mork_bool
+morkWriter::PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
+{
+ morkStream* stream = mWriter_Stream;
+ morkStore* store = mWriter_Store;
+
+ mdbYarn* colYarn = &mWriter_ColYarn;
+
+ morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
+
+ mork_column col = ioCell->GetColumn();
+ store->TokenToString(ev, col, colYarn);
+
+ mdbYarn yarn; // to ref content inside atom
+ morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil
+
+ if ( yarn.mYarn_Form != mWriter_RowForm )
+ this->ChangeRowForm(ev, yarn.mYarn_Form);
+
+ mork_size pending = yarn.mYarn_Fill + colYarn->mYarn_Fill +
+ morkWriter_kYarnEscapeSlop + 3;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+
+ stream->Putc(ev, '('); // start cell
+ ++mWriter_LineSize;
+
+ this->WriteYarn(ev, colYarn); // column
+
+ pending = yarn.mYarn_Fill + morkWriter_kYarnEscapeSlop;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellValueDepth);
+ stream->Putc(ev, '=');
+ ++mWriter_LineSize;
+
+ this->WriteYarn(ev, &yarn); // value
+
+ stream->Putc(ev, ')'); // end cell
+ ++mWriter_LineSize;
+
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutVerboseRowCells(morkEnv* ev, morkRow* ioRow)
+{
+ morkCell* cells = ioRow->mRow_Cells;
+ if ( cells )
+ {
+
+ morkCell* end = cells + ioRow->mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end && ev->Good() )
+ {
+ // note we prefer to avoid writing cells here with no value:
+ if ( cells->GetAtom() ) // does cell have any value?
+ this->PutVerboseCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
+ }
+ }
+ return ev->Good();
+}
+
+
+mork_bool
+morkWriter::PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal)
+{
+ morkStream* stream = mWriter_Stream;
+ char buf[ 128 ]; // buffer for staging hex ids
+ char* idBuf = buf + 2; // where the id always starts
+ buf[ 0 ] = '('; // we always start with open paren
+ buf[ 1 ] = '^'; // column is always a hex ID
+
+ mork_size colSize = 0; // the size of col hex ID
+ mork_size bytesWritten;
+
+ morkAtom* atom = (inWithVal)? ioCell->GetAtom() : (morkAtom*) 0;
+
+ mork_column col = ioCell->GetColumn();
+ char* p = idBuf;
+ colSize = ev->TokenAsHex(p, col);
+ p += colSize;
+
+ mdbYarn yarn; // to ref content inside atom
+ morkAtom::AliasYarn(atom, &yarn); // works even when atom==nil
+
+ if ( yarn.mYarn_Form != mWriter_RowForm )
+ this->ChangeRowForm(ev, yarn.mYarn_Form);
+
+ if ( atom && atom->IsBook() ) // is it possible to write atom ID?
+ {
+ this->IndentAsNeeded(ev, morkWriter_kRowCellDepth);
+ *p++ = '^';
+ morkBookAtom* ba = (morkBookAtom*) atom;
+
+ mork_size valSize = ev->TokenAsHex(p, ba->mBookAtom_Id);
+ mork_fill yarnFill = yarn.mYarn_Fill;
+ mork_bool putImmYarn = ( yarnFill <= valSize );
+ if ( putImmYarn )
+ putImmYarn = this->IsYarnAllValue(&yarn);
+
+ if ( putImmYarn ) // value no bigger than id?
+ {
+ p[ -1 ] = '='; // go back and clobber '^' with '=' instead
+ if ( yarnFill )
+ {
+ MORK_MEMCPY(p, yarn.mYarn_Buf, yarnFill);
+ p += yarnFill;
+ }
+ *p++ = ')';
+ mork_size distance = (mork_size) (p - buf);
+ stream->Write(ev->AsMdbEnv(), buf, distance, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+ }
+ else
+ {
+ p += valSize;
+ *p = ')';
+ stream->Write(ev->AsMdbEnv(), buf, colSize + valSize + 4, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+ }
+
+ if ( atom->IsAtomDirty() )
+ {
+ atom->SetAtomClean();
+ ++mWriter_DoneCount;
+ }
+ }
+ else // must write an anonymous atom
+ {
+ mork_size pending = yarn.mYarn_Fill + colSize +
+ morkWriter_kYarnEscapeSlop + 2;
+ this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+
+ mork_size bytesWritten;
+ stream->Write(ev->AsMdbEnv(), buf, colSize + 2, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ pending -= ( colSize + 2 );
+ this->IndentOverMaxLine(ev, pending, morkWriter_kRowCellDepth);
+ stream->Putc(ev, '=');
+ ++mWriter_LineSize;
+
+ this->WriteYarn(ev, &yarn);
+ stream->Putc(ev, ')'); // end cell
+ ++mWriter_LineSize;
+ }
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutRowCells(morkEnv* ev, morkRow* ioRow)
+{
+ morkCell* cells = ioRow->mRow_Cells;
+ if ( cells )
+ {
+ morkCell* end = cells + ioRow->mRow_Length;
+ --cells; // prepare for preincrement:
+ while ( ++cells < end && ev->Good() )
+ {
+ // note we prefer to avoid writing cells here with no value:
+ if ( cells->GetAtom() ) // does cell have any value?
+ this->PutCell(ev, cells, /*inWithVal*/ morkBool_kTrue);
+ }
+ }
+ return ev->Good();
+}
+
+mork_bool
+morkWriter::PutRow(morkEnv* ev, morkRow* ioRow)
+{
+ if ( ioRow && ioRow->IsRow() )
+ {
+ mWriter_RowForm = mWriter_TableForm;
+
+ mork_size bytesWritten;
+ morkStream* stream = mWriter_Stream;
+ char buf[ 128 + 16 ]; // buffer for staging hex
+ char* p = buf;
+ mdbOid* roid = &ioRow->mRow_Oid;
+ mork_size ridSize = 0;
+
+ mork_scope tableScope = mWriter_TableRowScope;
+
+ if ( ioRow->IsRowDirty() )
+ {
+ if ( mWriter_SuppressDirtyRowNewline || !mWriter_LineSize )
+ mWriter_SuppressDirtyRowNewline = morkBool_kFalse;
+ else
+ {
+ if ( tableScope ) // in a table?
+ mWriter_LineSize = stream->PutIndent(ev, morkWriter_kRowDepth);
+ else
+ mWriter_LineSize = stream->PutIndent(ev, 0); // no indent
+ }
+
+// mork_rid rid = roid->mOid_Id;
+ *p++ = '['; // start row punct=1
+ mork_size punctSize = (mWriter_BeVerbose) ? 9 : 1; // counting "[ /*r=*/ "
+
+ mork_bool rowRewrite = ioRow->IsRowRewrite();
+
+ if ( rowRewrite && mWriter_Incremental )
+ {
+ *p++ = '-';
+ ++punctSize; // counting '-'
+ ++mWriter_LineSize;
+ }
+
+ if ( tableScope && roid->mOid_Scope == tableScope )
+ ridSize = ev->TokenAsHex(p, roid->mOid_Id);
+ else
+ ridSize = ev->OidAsHex(p, *roid);
+
+ p += ridSize;
+
+ if (mWriter_BeVerbose)
+ {
+ *p++ = ' '; // punct=2
+ *p++ = '/'; // punct=3
+ *p++ = '*'; // punct=4
+ *p++ = 'r'; // punct=5
+ *p++ = '='; // punct=6
+
+ mork_size usesSize = ev->TokenAsHex(p, (mork_token) ioRow->mRow_GcUses);
+ punctSize += usesSize;
+ p += usesSize;
+
+ *p++ = '*'; // punct=7
+ *p++ = '/'; // punct=8
+ *p++ = ' '; // punct=9
+ }
+ stream->Write(ev->AsMdbEnv(), buf, ridSize + punctSize, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+
+ // special case situation where row puts exactly one column:
+ if ( !rowRewrite && mWriter_Incremental && ioRow->HasRowDelta() )
+ {
+ mork_column col = ioRow->GetDeltaColumn();
+ morkCell dummy(col, morkChange_kNil, (morkAtom*) 0);
+ morkCell* cell = 0;
+
+ mork_bool withVal = ( ioRow->GetDeltaChange() != morkChange_kCut );
+
+ if ( withVal )
+ {
+ mork_pos cellPos = 0; // dummy pos
+ cell = ioRow->GetCell(ev, col, &cellPos);
+ }
+ if ( !cell )
+ cell = &dummy;
+
+ if ( mWriter_BeVerbose )
+ this->PutVerboseCell(ev, cell, withVal);
+ else
+ this->PutCell(ev, cell, withVal);
+ }
+ else // put entire row?
+ {
+ if ( mWriter_BeVerbose )
+ this->PutVerboseRowCells(ev, ioRow); // write all, verbosely
+ else
+ this->PutRowCells(ev, ioRow); // write all, hex notation
+ }
+
+ stream->Putc(ev, ']'); // end row
+ ++mWriter_LineSize;
+ }
+ else
+ {
+ this->IndentAsNeeded(ev, morkWriter_kRowDepth);
+
+ if ( tableScope && roid->mOid_Scope == tableScope )
+ ridSize = ev->TokenAsHex(p, roid->mOid_Id);
+ else
+ ridSize = ev->OidAsHex(p, *roid);
+
+ stream->Write(ev->AsMdbEnv(), buf, ridSize, &bytesWritten);
+ mWriter_LineSize += bytesWritten;
+ stream->Putc(ev, ' ');
+ ++mWriter_LineSize;
+ }
+
+ ++mWriter_DoneCount;
+
+ ioRow->SetRowClean(); // try to do this at the very last
+ }
+ else
+ ioRow->NonRowTypeWarning(ev);
+
+ return ev->Good();
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
diff --git a/db/mork/src/morkWriter.h b/db/mork/src/morkWriter.h
new file mode 100644
index 000000000..1610d47c7
--- /dev/null
+++ b/db/mork/src/morkWriter.h
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKWRITER_
+#define _MORKWRITER_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKMAP_
+#include "morkMap.h"
+#endif
+
+#ifndef _MORKROWMAP_
+#include "morkRowMap.h"
+#endif
+
+#ifndef _MORKTABLE_
+#include "morkTable.h"
+#endif
+
+#ifndef _MORKATOMMAP_
+#include "morkAtomMap.h"
+#endif
+
+#ifndef _MORKATOMSPACE_
+#include "morkAtomSpace.h"
+#endif
+
+#ifndef _MORKROWSPACE_
+#include "morkRowSpace.h"
+#endif
+
+#ifndef _MORKSTREAM_
+#include "morkStream.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkWriter_kStreamBufSize /*i*/ (16 * 1024) /* buffer size for stream */
+
+#define morkDerived_kWriter /*i*/ 0x5772 /* ascii 'Wr' */
+
+#define morkWriter_kPhaseNothingDone 0 /* nothing has yet been done */
+#define morkWriter_kPhaseDirtyAllDone 1 /* DirtyAll() is done */
+#define morkWriter_kPhasePutHeaderDone 2 /* PutHeader() is done */
+
+#define morkWriter_kPhaseRenumberAllDone 3 /* RenumberAll() is done */
+
+#define morkWriter_kPhaseStoreAtomSpaces 4 /*mWriter_StoreAtomSpacesIter*/
+#define morkWriter_kPhaseAtomSpaceAtomAids 5 /*mWriter_AtomSpaceAtomAidsIter*/
+
+#define morkWriter_kPhaseStoreRowSpacesTables 6 /*mWriter_StoreRowSpacesIter*/
+#define morkWriter_kPhaseRowSpaceTables 7 /*mWriter_RowSpaceTablesIter*/
+#define morkWriter_kPhaseTableRowArray 8 /*mWriter_TableRowArrayPos */
+
+#define morkWriter_kPhaseStoreRowSpacesRows 9 /*mWriter_StoreRowSpacesIter*/
+#define morkWriter_kPhaseRowSpaceRows 10 /*mWriter_RowSpaceRowsIter*/
+
+#define morkWriter_kPhaseContentDone 11 /* all content written */
+#define morkWriter_kPhaseWritingDone 12 /* everything has been done */
+
+#define morkWriter_kCountNumberOfPhases 13 /* part of mWrite_TotalCount */
+
+#define morkWriter_kMaxColumnNameSize 128 /* longest writable col name */
+
+#define morkWriter_kMaxIndent 66 /* default value for mWriter_MaxIndent */
+#define morkWriter_kMaxLine 78 /* default value for mWriter_MaxLine */
+
+#define morkWriter_kYarnEscapeSlop 4 /* guess average yarn escape overhead */
+
+#define morkWriter_kTableMetaCellDepth 4 /* */
+#define morkWriter_kTableMetaCellValueDepth 6 /* */
+
+#define morkWriter_kDictMetaCellDepth 4 /* */
+#define morkWriter_kDictMetaCellValueDepth 6 /* */
+
+#define morkWriter_kDictAliasDepth 2 /* */
+#define morkWriter_kDictAliasValueDepth 4 /* */
+
+#define morkWriter_kRowDepth 2 /* */
+#define morkWriter_kRowCellDepth 4 /* */
+#define morkWriter_kRowCellValueDepth 6 /* */
+
+#define morkWriter_kGroupBufSize 64 /* */
+
+// v=1.1 retired on 23-Mar-99 (for metainfo one char column names)
+// v=1.2 retired on 20-Apr-99 (for ":c" suffix on table kind hex refs)
+// v=1.3 retired on 20-Apr-99 (for 1CE:m instead of ill-formed 1CE:^6D)
+#define morkWriter_kFileHeader "// <!-- <mdb:mork:z v=\"1.4\"/> -->"
+
+class morkWriter : public morkNode { // row iterator
+
+// public: // slots inherited from morkObject (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+ morkStore* mWriter_Store; // weak ref to committing store
+ nsIMdbFile* mWriter_File; // strong ref to store's file
+ nsIMdbFile* mWriter_Bud; // strong ref to bud of mWriter_File
+ morkStream* mWriter_Stream; // strong ref to stream on bud file
+ nsIMdbHeap* mWriter_SlotHeap; // strong ref to slot heap
+
+ // GroupIdentity should be based on mStore_CommitGroupIdentity:
+ mork_gid mWriter_CommitGroupIdentity; // transaction ID number
+
+ // GroupBuf holds a hex version of mWriter_CommitGroupIdentity:
+ char mWriter_GroupBuf[ morkWriter_kGroupBufSize ];
+ mork_fill mWriter_GroupBufFill; // actual bytes in GroupBuf
+
+ mork_count mWriter_TotalCount; // count of all things to be written
+ mork_count mWriter_DoneCount; // count of things already written
+
+ mork_size mWriter_LineSize; // length of current line being written
+ mork_size mWriter_MaxIndent; // line size forcing a line break
+ mork_size mWriter_MaxLine; // line size forcing a value continuation
+
+ mork_cscode mWriter_TableForm; // current charset metainfo
+ mork_scope mWriter_TableAtomScope; // current atom scope
+ mork_scope mWriter_TableRowScope; // current row scope
+ mork_kind mWriter_TableKind; // current table kind
+
+ mork_cscode mWriter_RowForm; // current charset metainfo
+ mork_scope mWriter_RowAtomScope; // current atom scope
+ mork_scope mWriter_RowScope; // current row scope
+
+ mork_cscode mWriter_DictForm; // current charset metainfo
+ mork_scope mWriter_DictAtomScope; // current atom scope
+
+ mork_bool mWriter_NeedDirtyAll; // need to call DirtyAll()
+ mork_bool mWriter_Incremental; // opposite of mWriter_NeedDirtyAll
+ mork_bool mWriter_DidStartDict; // true when a dict has been started
+ mork_bool mWriter_DidEndDict; // true when a dict has been ended
+
+ mork_bool mWriter_SuppressDirtyRowNewline; // for table meta rows
+ mork_bool mWriter_DidStartGroup; // true when a group has been started
+ mork_bool mWriter_DidEndGroup; // true when a group has been ended
+ mork_u1 mWriter_Phase; // status of writing process
+
+ mork_bool mWriter_BeVerbose; // driven by env and table verbose settings:
+ // mWriter_BeVerbose equals ( ev->mEnv_BeVerbose || table->IsTableVerbose() )
+
+ mork_u1 mWriter_Pad[ 3 ]; // for u4 alignment
+
+ mork_pos mWriter_TableRowArrayPos; // index into mTable_RowArray
+
+ char mWriter_SafeNameBuf[ (morkWriter_kMaxColumnNameSize * 2) + 4 ];
+ // Note: extra four bytes in ColNameBuf means we can always append to yarn
+
+ char mWriter_ColNameBuf[ morkWriter_kMaxColumnNameSize + 4 ];
+ // Note: extra four bytes in ColNameBuf means we can always append to yarn
+
+ mdbYarn mWriter_ColYarn; // a yarn to describe space in ColNameBuf:
+ // mYarn_Buf == mWriter_ColNameBuf, mYarn_Size == morkWriter_kMaxColumnNameSize
+
+ mdbYarn mWriter_SafeYarn; // a yarn to describe space in ColNameBuf:
+ // mYarn_Buf == mWriter_SafeNameBuf, mYarn_Size == (kMaxColumnNameSize * 2)
+
+ morkAtomSpaceMapIter mWriter_StoreAtomSpacesIter; // for mStore_AtomSpaces
+ morkAtomAidMapIter mWriter_AtomSpaceAtomAidsIter; // for AtomSpace_AtomAids
+
+ morkRowSpaceMapIter mWriter_StoreRowSpacesIter; // for mStore_RowSpaces
+ morkTableMapIter mWriter_RowSpaceTablesIter; // for mRowSpace_Tables
+
+#ifdef MORK_ENABLE_PROBE_MAPS
+ morkRowProbeMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows
+#else /*MORK_ENABLE_PROBE_MAPS*/
+ morkRowMapIter mWriter_RowSpaceRowsIter; // for mRowSpace_Rows
+#endif /*MORK_ENABLE_PROBE_MAPS*/
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseWriter()
+ virtual ~morkWriter(); // assert that close executed earlier
+
+public: // morkWriter construction & destruction
+ morkWriter(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioHeap, morkStore* ioStore, nsIMdbFile* ioFile,
+ nsIMdbHeap* ioSlotHeap);
+ void CloseWriter(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkWriter(const morkWriter& other);
+ morkWriter& operator=(const morkWriter& other);
+
+public: // dynamic type identification
+ mork_bool IsWriter() const
+ { return IsNode() && mNode_Derived == morkDerived_kWriter; }
+// } ===== end morkNode methods =====
+
+public: // typing & errors
+ static void NonWriterTypeError(morkEnv* ev);
+ static void NilWriterStoreError(morkEnv* ev);
+ static void NilWriterBudError(morkEnv* ev);
+ static void NilWriterStreamError(morkEnv* ev);
+ static void NilWriterFileError(morkEnv* ev);
+ static void UnsupportedPhaseError(morkEnv* ev);
+
+public: // utitlities
+ void ChangeRowForm(morkEnv* ev, mork_cscode inNewForm);
+ void ChangeDictForm(morkEnv* ev, mork_cscode inNewForm);
+ void ChangeDictAtomScope(morkEnv* ev, mork_scope inScope);
+
+public: // inlines
+ mork_bool DidStartDict() const { return mWriter_DidStartDict; }
+ mork_bool DidEndDict() const { return mWriter_DidEndDict; }
+
+ void IndentAsNeeded(morkEnv* ev, mork_size inDepth)
+ {
+ if ( mWriter_LineSize > mWriter_MaxIndent )
+ mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth);
+ }
+
+ void IndentOverMaxLine(morkEnv* ev,
+ mork_size inPendingSize, mork_size inDepth)
+ {
+ if ( mWriter_LineSize + inPendingSize > mWriter_MaxLine )
+ mWriter_LineSize = mWriter_Stream->PutIndent(ev, inDepth);
+ }
+
+public: // delayed construction
+
+ void MakeWriterStream(morkEnv* ev); // give writer a suitable stream
+
+public: // iterative/asynchronous writing
+
+ mork_bool WriteMore(morkEnv* ev); // call until IsWritingDone() is true
+
+ mork_bool IsWritingDone() const // don't call WriteMore() any longer?
+ { return mWriter_Phase == morkWriter_kPhaseWritingDone; }
+
+public: // marking all content dirty
+ mork_bool DirtyAll(morkEnv* ev);
+ // DirtyAll() visits every store sub-object and marks
+ // them dirty, including every table, row, cell, and atom. The return
+ // equals ev->Good(), to show whether any error happened. This method is
+ // intended for use in the beginning of a "compress commit" which writes
+ // all store content, whether dirty or not. We dirty everything first so
+ // that later iterations over content can mark things clean as they are
+ // written, and organize the process of serialization so that objects are
+ // written only at need (because of being dirty). Note the method can
+ // stop early when any error happens, since this will abort any commit.
+
+public: // group commit transactions
+
+ mork_bool StartGroup(morkEnv* ev);
+ mork_bool CommitGroup(morkEnv* ev);
+ mork_bool AbortGroup(morkEnv* ev);
+
+public: // phase methods
+ mork_bool OnNothingDone(morkEnv* ev);
+ mork_bool OnDirtyAllDone(morkEnv* ev);
+ mork_bool OnPutHeaderDone(morkEnv* ev);
+
+ mork_bool OnRenumberAllDone(morkEnv* ev);
+
+ mork_bool OnStoreAtomSpaces(morkEnv* ev);
+ mork_bool OnAtomSpaceAtomAids(morkEnv* ev);
+
+ mork_bool OnStoreRowSpacesTables(morkEnv* ev);
+ mork_bool OnRowSpaceTables(morkEnv* ev);
+ mork_bool OnTableRowArray(morkEnv* ev);
+
+ mork_bool OnStoreRowSpacesRows(morkEnv* ev);
+ mork_bool OnRowSpaceRows(morkEnv* ev);
+
+ mork_bool OnContentDone(morkEnv* ev);
+ mork_bool OnWritingDone(morkEnv* ev);
+
+public: // writing dict items first pass
+ mork_bool PutTableDict(morkEnv* ev, morkTable* ioTable);
+ mork_bool PutRowDict(morkEnv* ev, morkRow* ioRow);
+
+public: // writing node content second pass
+ mork_bool PutTable(morkEnv* ev, morkTable* ioTable);
+ mork_bool PutRow(morkEnv* ev, morkRow* ioRow);
+ mork_bool PutRowCells(morkEnv* ev, morkRow* ioRow);
+ mork_bool PutVerboseRowCells(morkEnv* ev, morkRow* ioRow);
+
+ mork_bool PutCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal);
+ mork_bool PutVerboseCell(morkEnv* ev, morkCell* ioCell, mork_bool inWithVal);
+
+ mork_bool PutTableChange(morkEnv* ev, const morkTableChange* inChange);
+
+public: // other writer methods
+
+ mork_bool IsYarnAllValue(const mdbYarn* inYarn);
+
+ mork_size WriteYarn(morkEnv* ev, const mdbYarn* inYarn);
+ // return number of atom bytes written on the current line (which
+ // implies that escaped line breaks will make the size value smaller
+ // than the entire yarn's size, since only part goes on a last line).
+
+ mork_size WriteAtom(morkEnv* ev, const morkAtom* inAtom);
+ // return number of atom bytes written on the current line (which
+ // implies that escaped line breaks will make the size value smaller
+ // than the entire atom's size, since only part goes on a last line).
+
+ void WriteAllStoreTables(morkEnv* ev);
+ void WriteAtomSpaceAsDict(morkEnv* ev, morkAtomSpace* ioSpace);
+
+ void WriteTokenToTokenMetaCell(morkEnv* ev, mork_token inCol,
+ mork_token inValue);
+ void WriteStringToTokenDictCell(morkEnv* ev, const char* inCol,
+ mork_token inValue);
+ // Note inCol should begin with '(' and end with '=', with col in between.
+
+ void StartDict(morkEnv* ev);
+ void EndDict(morkEnv* ev);
+
+ void StartTable(morkEnv* ev, morkTable* ioTable);
+ void EndTable(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakWriter(morkWriter* me,
+ morkEnv* ev, morkWriter** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongWriter(morkWriter* me,
+ morkEnv* ev, morkWriter** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKTABLEROWCURSOR_ */
diff --git a/db/mork/src/morkYarn.cpp b/db/mork/src/morkYarn.cpp
new file mode 100644
index 000000000..ce52a741e
--- /dev/null
+++ b/db/mork/src/morkYarn.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#ifndef _MORKYARN_
+#include "morkYarn.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// ````` ````` ````` ````` `````
+// { ===== begin morkNode interface =====
+
+/*public virtual*/ void
+morkYarn::CloseMorkNode(morkEnv* ev) /*i*/ // CloseYarn() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseYarn(ev);
+ this->MarkShut();
+ }
+}
+
+/*public virtual*/
+morkYarn::~morkYarn() /*i*/ // assert CloseYarn() executed earlier
+{
+ MORK_ASSERT(mYarn_Body.mYarn_Buf==0);
+}
+
+/*public non-poly*/
+morkYarn::morkYarn(morkEnv* ev, /*i*/
+ const morkUsage& inUsage, nsIMdbHeap* ioHeap)
+ : morkNode(ev, inUsage, ioHeap)
+{
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kYarn;
+}
+
+/*public non-poly*/ void
+morkYarn::CloseYarn(morkEnv* ev) /*i*/ // called by CloseMorkNode();
+{
+ if ( this->IsNode() )
+ this->MarkShut();
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+// ````` ````` ````` ````` `````
+
+/*static*/ void
+morkYarn::NonYarnTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkYarn");
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/morkYarn.h b/db/mork/src/morkYarn.h
new file mode 100644
index 000000000..a5b38bbf3
--- /dev/null
+++ b/db/mork/src/morkYarn.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKYARN_
+#define _MORKYARN_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+#define morkDerived_kYarn /*i*/ 0x7952 /* ascii 'yR' */
+
+/*| morkYarn: a reference counted nsIMdbYarn C struct. This is for use in those
+**| few cases where single instances of reference counted buffers are needed
+**| in Mork, and we expect few enough instances that overhead is not a factor
+**| in decided whether to use such a thing.
+|*/
+class morkYarn : public morkNode { // refcounted yarn
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+ mdbYarn mYarn_Body;
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseYarn() only if open
+ virtual ~morkYarn(); // assert that CloseYarn() executed earlier
+
+public: // morkYarn construction & destruction
+ morkYarn(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap);
+ void CloseYarn(morkEnv* ev); // called by CloseMorkNode();
+
+private: // copying is not allowed
+ morkYarn(const morkYarn& other);
+ morkYarn& operator=(const morkYarn& other);
+
+public: // dynamic type identification
+ mork_bool IsYarn() const
+ { return IsNode() && mNode_Derived == morkDerived_kYarn; }
+// } ===== end morkNode methods =====
+
+public: // typing
+ static void NonYarnTypeError(morkEnv* ev);
+
+public: // typesafe refcounting inlines calling inherited morkNode methods
+ static void SlotWeakYarn(morkYarn* me,
+ morkEnv* ev, morkYarn** ioSlot)
+ { morkNode::SlotWeakNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+
+ static void SlotStrongYarn(morkYarn* me,
+ morkEnv* ev, morkYarn** ioSlot)
+ { morkNode::SlotStrongNode((morkNode*) me, ev, (morkNode**) ioSlot); }
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKYARN_ */
diff --git a/db/mork/src/morkZone.cpp b/db/mork/src/morkZone.cpp
new file mode 100644
index 000000000..239e86bba
--- /dev/null
+++ b/db/mork/src/morkZone.cpp
@@ -0,0 +1,527 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKZONE_
+#include "morkZone.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { ===== begin morkNode interface =====
+// public: // morkNode virtual methods
+void morkZone::CloseMorkNode(morkEnv* ev) // CloseZone() only if open
+{
+ if ( this->IsOpenNode() )
+ {
+ this->MarkClosing();
+ this->CloseZone(ev);
+ this->MarkShut();
+ }
+}
+
+morkZone::~morkZone() // assert that CloseZone() executed earlier
+{
+ MORK_ASSERT(this->IsShutNode());
+}
+
+// public: // morkMap construction & destruction
+morkZone::morkZone(morkEnv* ev, const morkUsage& inUsage,
+ nsIMdbHeap* ioNodeHeap, nsIMdbHeap* ioZoneHeap)
+: morkNode(ev, inUsage, ioNodeHeap)
+, mZone_Heap( 0 )
+, mZone_HeapVolume( 0 )
+, mZone_BlockVolume( 0 )
+, mZone_RunVolume( 0 )
+, mZone_ChipVolume( 0 )
+
+, mZone_FreeOldRunVolume( 0 )
+
+, mZone_HunkCount( 0 )
+, mZone_FreeOldRunCount( 0 )
+
+, mZone_HunkList( 0 )
+, mZone_FreeOldRunList( 0 )
+
+, mZone_At( 0 )
+, mZone_AtSize( 0 )
+
+ // morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ];
+{
+
+ morkRun** runs = mZone_FreeRuns;
+ morkRun** end = runs + (morkZone_kBuckets + 1); // one past last slot
+ --runs; // prepare for preincrement
+ while ( ++runs < end ) // another slot in array?
+ *runs = 0; // clear all the slots
+
+ if ( ev->Good() )
+ {
+ if ( ioZoneHeap )
+ {
+ nsIMdbHeap_SlotStrongHeap(ioZoneHeap, ev, &mZone_Heap);
+ if ( ev->Good() )
+ mNode_Derived = morkDerived_kZone;
+ }
+ else
+ ev->NilPointerError();
+ }
+}
+
+void morkZone::CloseZone(morkEnv* ev) // called by CloseMorkNode()
+{
+ if ( this->IsNode() )
+ {
+ nsIMdbHeap* heap = mZone_Heap;
+ if ( heap )
+ {
+ morkHunk* hunk = 0;
+ nsIMdbEnv* mev = ev->AsMdbEnv();
+
+ morkHunk* next = mZone_HunkList;
+ while ( ( hunk = next ) != 0 )
+ {
+#ifdef morkHunk_USE_TAG_SLOT
+ if ( !hunk->HunkGoodTag() )
+ hunk->BadHunkTagWarning(ev);
+#endif /* morkHunk_USE_TAG_SLOT */
+
+ next = hunk->HunkNext();
+ heap->Free(mev, hunk);
+ }
+ }
+ nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*) 0, ev, &mZone_Heap);
+ this->MarkShut();
+ }
+ else
+ this->NonNodeError(ev);
+}
+
+// } ===== end morkNode methods =====
+
+/*static*/ void
+morkZone::NonZoneTypeError(morkEnv* ev)
+{
+ ev->NewError("non morkZone");
+}
+
+/*static*/ void
+morkZone::NilZoneHeapError(morkEnv* ev)
+{
+ ev->NewError("nil mZone_Heap");
+}
+
+/*static*/ void
+morkHunk::BadHunkTagWarning(morkEnv* ev)
+{
+ ev->NewWarning("bad mHunk_Tag");
+}
+
+/*static*/ void
+morkRun::BadRunTagError(morkEnv* ev)
+{
+ ev->NewError("bad mRun_Tag");
+}
+
+/*static*/ void
+morkRun::RunSizeAlignError(morkEnv* ev)
+{
+ ev->NewError("bad RunSize() alignment");
+}
+
+// { ===== begin morkZone methods =====
+
+
+mork_size morkZone::zone_grow_at(morkEnv* ev, mork_size inNeededSize)
+{
+ mZone_At = 0; // remove any ref to current hunk
+ mZone_AtSize = 0; // zero available bytes in current hunk
+
+ mork_size runSize = 0; // actual size of a particular run
+
+ // try to find a run in old run list with at least inNeededSize bytes:
+ morkRun* run = mZone_FreeOldRunList; // cursor in list scan
+ morkRun* prev = 0; // the node before run in the list scan
+
+ while ( run ) // another run in list to check?
+ {
+ morkOldRun* oldRun = (morkOldRun*) run;
+ mork_size oldSize = oldRun->OldSize();
+ if ( oldSize >= inNeededSize ) // found one big enough?
+ {
+ runSize = oldSize;
+ break; // end while loop early
+ }
+ prev = run; // remember last position in singly linked list
+ run = run->RunNext(); // advance cursor to next node in list
+ }
+ if ( runSize && run ) // found a usable old run?
+ {
+ morkRun* next = run->RunNext();
+ if ( prev ) // another node in free list precedes run?
+ prev->RunSetNext(next); // unlink run
+ else
+ mZone_FreeOldRunList = next; // unlink run from head of list
+
+ morkOldRun *oldRun = (morkOldRun *) run;
+ oldRun->OldSetSize(runSize);
+ mZone_At = (mork_u1*) run;
+ mZone_AtSize = runSize;
+
+#ifdef morkZone_CONFIG_DEBUG
+#ifdef morkZone_CONFIG_ALIGN_8
+ mork_ip lowThree = ((mork_ip) mZone_At) & 7;
+ if ( lowThree ) // not 8 byte aligned?
+#else /*morkZone_CONFIG_ALIGN_8*/
+ mork_ip lowTwo = ((mork_ip) mZone_At) & 3;
+ if ( lowTwo ) // not 4 byte aligned?
+#endif /*morkZone_CONFIG_ALIGN_8*/
+ ev->NewWarning("mZone_At not aligned");
+#endif /*morkZone_CONFIG_DEBUG*/
+ }
+ else // need to allocate a brand new run
+ {
+ inNeededSize += 7; // allow for possible alignment padding
+ mork_size newSize = ( inNeededSize > morkZone_kNewHunkSize )?
+ inNeededSize : morkZone_kNewHunkSize;
+
+ morkHunk* hunk = this->zone_new_hunk(ev, newSize);
+ if ( hunk )
+ {
+ morkRun* hunkRun = hunk->HunkRun();
+ mork_u1* at = (mork_u1*) hunkRun->RunAsBlock();
+ mork_ip lowBits = ((mork_ip) at) & 7;
+ if ( lowBits ) // not 8 byte aligned?
+ {
+ mork_ip skip = (8 - lowBits); // skip the complement to align
+ at += skip;
+ newSize -= skip;
+ }
+ mZone_At = at;
+ mZone_AtSize = newSize;
+ }
+ }
+
+ return mZone_AtSize;
+}
+
+morkHunk* morkZone::zone_new_hunk(morkEnv* ev, mdb_size inSize) // alloc
+{
+ mdb_size hunkSize = inSize + sizeof(morkHunk);
+ void* outBlock = 0; // we are going straight to the heap:
+ mZone_Heap->Alloc(ev->AsMdbEnv(), hunkSize, &outBlock);
+ if ( outBlock )
+ {
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_HeapVolume += hunkSize; // track all heap allocations
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+ morkHunk* hunk = (morkHunk*) outBlock;
+#ifdef morkHunk_USE_TAG_SLOT
+ hunk->HunkInitTag();
+#endif /* morkHunk_USE_TAG_SLOT */
+
+ hunk->HunkSetNext(mZone_HunkList);
+ mZone_HunkList = hunk;
+ ++mZone_HunkCount;
+
+ morkRun* run = hunk->HunkRun();
+ run->RunSetSize(inSize);
+#ifdef morkRun_USE_TAG_SLOT
+ run->RunInitTag();
+#endif /* morkRun_USE_TAG_SLOT */
+
+ return hunk;
+ }
+ if ( ev->Good() ) // got this far without any error reported yet?
+ ev->OutOfMemoryError();
+ return (morkHunk*) 0;
+}
+
+void* morkZone::zone_new_chip(morkEnv* ev, mdb_size inSize) // alloc
+{
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_BlockVolume += inSize; // sum sizes of both chips and runs
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+ mork_u1* at = mZone_At;
+ mork_size atSize = mZone_AtSize; // available bytes in current hunk
+ if ( atSize >= inSize ) // current hunk can satisfy request?
+ {
+ mZone_At = at + inSize;
+ mZone_AtSize = atSize - inSize;
+ return at;
+ }
+ else if ( atSize > morkZone_kMaxHunkWaste ) // over max waste allowed?
+ {
+ morkHunk* hunk = this->zone_new_hunk(ev, inSize);
+ if ( hunk )
+ return hunk->HunkRun();
+
+ return (void*) 0; // show allocation has failed
+ }
+ else // get ourselves a new hunk for suballocation:
+ {
+ atSize = this->zone_grow_at(ev, inSize); // get a new hunk
+ }
+
+ if ( atSize >= inSize ) // current hunk can satisfy request?
+ {
+ at = mZone_At;
+ mZone_At = at + inSize;
+ mZone_AtSize = atSize - inSize;
+ return at;
+ }
+
+ if ( ev->Good() ) // got this far without any error reported yet?
+ ev->OutOfMemoryError();
+
+ return (void*) 0; // show allocation has failed
+}
+
+void* morkZone::ZoneNewChip(morkEnv* ev, mdb_size inSize) // alloc
+{
+#ifdef morkZone_CONFIG_ARENA
+
+#ifdef morkZone_CONFIG_DEBUG
+ if ( !this->IsZone() )
+ this->NonZoneTypeError(ev);
+ else if ( !mZone_Heap )
+ this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+#ifdef morkZone_CONFIG_ALIGN_8
+ inSize += 7;
+ inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
+#else /*morkZone_CONFIG_ALIGN_8*/
+ inSize += 3;
+ inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
+#endif /*morkZone_CONFIG_ALIGN_8*/
+
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_ChipVolume += inSize; // sum sizes of chips only
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+ return this->zone_new_chip(ev, inSize);
+
+#else /*morkZone_CONFIG_ARENA*/
+ void* outBlock = 0;
+ mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
+ return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+
+}
+
+// public: // ...but runs do indeed know how big they are
+void* morkZone::ZoneNewRun(morkEnv* ev, mdb_size inSize) // alloc
+{
+#ifdef morkZone_CONFIG_ARENA
+
+#ifdef morkZone_CONFIG_DEBUG
+ if ( !this->IsZone() )
+ this->NonZoneTypeError(ev);
+ else if ( !mZone_Heap )
+ this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+ inSize += morkZone_kRoundAdd;
+ inSize &= morkZone_kRoundMask;
+ if ( inSize <= morkZone_kMaxCachedRun )
+ {
+ morkRun** bucket = mZone_FreeRuns + (inSize >> morkZone_kRoundBits);
+ morkRun* hit = *bucket;
+ if ( hit ) // cache hit?
+ {
+ *bucket = hit->RunNext();
+ hit->RunSetSize(inSize);
+ return hit->RunAsBlock();
+ }
+ }
+ mdb_size blockSize = inSize + sizeof(morkRun); // plus run overhead
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_RunVolume += blockSize; // sum sizes of runs only
+#endif /* morkZone_CONFIG_VOL_STATS */
+ morkRun* run = (morkRun*) this->zone_new_chip(ev, blockSize);
+ if ( run )
+ {
+ run->RunSetSize(inSize);
+#ifdef morkRun_USE_TAG_SLOT
+ run->RunInitTag();
+#endif /* morkRun_USE_TAG_SLOT */
+ return run->RunAsBlock();
+ }
+
+ if ( ev->Good() ) // got this far without any error reported yet?
+ ev->OutOfMemoryError();
+
+ return (void*) 0; // indicate failed allocation
+
+#else /*morkZone_CONFIG_ARENA*/
+ void* outBlock = 0;
+ mZone_Heap->Alloc(ev->AsMdbEnv(), inSize, &outBlock);
+ return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+void morkZone::ZoneZapRun(morkEnv* ev, void* ioRunBlock) // free
+{
+#ifdef morkZone_CONFIG_ARENA
+
+ morkRun* run = morkRun::BlockAsRun(ioRunBlock);
+ mdb_size runSize = run->RunSize();
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_BlockVolume -= runSize; // tracking sizes of both chips and runs
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+#ifdef morkZone_CONFIG_DEBUG
+ if ( !this->IsZone() )
+ this->NonZoneTypeError(ev);
+ else if ( !mZone_Heap )
+ this->NilZoneHeapError(ev);
+ else if ( !ioRunBlock )
+ ev->NilPointerError();
+ else if ( runSize & morkZone_kRoundAdd )
+ run->RunSizeAlignError(ev);
+#ifdef morkRun_USE_TAG_SLOT
+ else if ( !run->RunGoodTag() )
+ run->BadRunTagError(ev);
+#endif /* morkRun_USE_TAG_SLOT */
+#endif /*morkZone_CONFIG_DEBUG*/
+
+ if ( runSize <= morkZone_kMaxCachedRun ) // goes into free run list?
+ {
+ morkRun** bucket = mZone_FreeRuns + (runSize >> morkZone_kRoundBits);
+ run->RunSetNext(*bucket); // push onto free run list
+ *bucket = run;
+ }
+ else // free old run list
+ {
+ run->RunSetNext(mZone_FreeOldRunList); // push onto free old run list
+ mZone_FreeOldRunList = run;
+ ++mZone_FreeOldRunCount;
+#ifdef morkZone_CONFIG_VOL_STATS
+ mZone_FreeOldRunVolume += runSize;
+#endif /* morkZone_CONFIG_VOL_STATS */
+
+ morkOldRun* oldRun = (morkOldRun*) run; // to access extra size slot
+ oldRun->OldSetSize(runSize); // so we know how big this is later
+ }
+
+#else /*morkZone_CONFIG_ARENA*/
+ mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+void* morkZone::ZoneGrowRun(morkEnv* ev, void* ioRunBlock, mdb_size inSize)
+{
+#ifdef morkZone_CONFIG_ARENA
+
+ morkRun* run = morkRun::BlockAsRun(ioRunBlock);
+ mdb_size runSize = run->RunSize();
+
+#ifdef morkZone_CONFIG_DEBUG
+ if ( !this->IsZone() )
+ this->NonZoneTypeError(ev);
+ else if ( !mZone_Heap )
+ this->NilZoneHeapError(ev);
+#endif /*morkZone_CONFIG_DEBUG*/
+
+#ifdef morkZone_CONFIG_ALIGN_8
+ inSize += 7;
+ inSize &= ~((mork_ip) 7); // force to multiple of 8 bytes
+#else /*morkZone_CONFIG_ALIGN_8*/
+ inSize += 3;
+ inSize &= ~((mork_ip) 3); // force to multiple of 4 bytes
+#endif /*morkZone_CONFIG_ALIGN_8*/
+
+ if ( inSize > runSize )
+ {
+ void* newBuf = this->ZoneNewRun(ev, inSize);
+ if ( newBuf )
+ {
+ MORK_MEMCPY(newBuf, ioRunBlock, runSize);
+ this->ZoneZapRun(ev, ioRunBlock);
+
+ return newBuf;
+ }
+ }
+ else
+ return ioRunBlock; // old size is big enough
+
+ if ( ev->Good() ) // got this far without any error reported yet?
+ ev->OutOfMemoryError();
+
+ return (void*) 0; // indicate failed allocation
+
+#else /*morkZone_CONFIG_ARENA*/
+ void* outBlock = 0;
+ mZone_Heap->Free(ev->AsMdbEnv(), ioRunBlock);
+ return outBlock;
+#endif /*morkZone_CONFIG_ARENA*/
+}
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+// { ===== begin nsIMdbHeap methods =====
+/*virtual*/ nsresult
+morkZone::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
+ mdb_size inSize, // requested size of new memory block
+ void** outBlock) // memory block of inSize bytes, or nil
+{
+ nsresult outErr = NS_OK;
+ void* block = 0;
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ block = this->ZoneNewRun(ev, inSize);
+ outErr = ev->AsErr();
+ }
+ else
+ outErr = morkEnv_kOutOfMemoryError;
+
+ if ( outBlock )
+ *outBlock = block;
+
+ return outErr;
+}
+
+/*virtual*/ nsresult
+morkZone::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
+ void* inBlock)
+{
+ nsresult outErr = NS_OK;
+ if ( inBlock )
+ {
+ morkEnv* ev = morkEnv::FromMdbEnv(mev);
+ if ( ev )
+ {
+ this->ZoneZapRun(ev, inBlock);
+ outErr = ev->AsErr();
+ }
+ else
+ // XXX 1 is not a valid nsresult
+ outErr = static_cast<nsresult>(1);
+ }
+
+ return outErr;
+}
+
+// } ===== end nsIMdbHeap methods =====
+
diff --git a/db/mork/src/morkZone.h b/db/mork/src/morkZone.h
new file mode 100644
index 000000000..807424949
--- /dev/null
+++ b/db/mork/src/morkZone.h
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MORKZONE_
+#define _MORKZONE_ 1
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _MORKNODE_
+#include "morkNode.h"
+#endif
+
+#ifndef _MORKDEQUE_
+#include "morkDeque.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| CONFIG_DEBUG: do paranoid debug checks if defined.
+|*/
+#ifdef MORK_DEBUG
+#define morkZone_CONFIG_DEBUG 1 /* debug paranoid if defined */
+#endif /*MORK_DEBUG*/
+
+/*| CONFIG_STATS: keep volume and usage statistics.
+|*/
+#define morkZone_CONFIG_VOL_STATS 1 /* count space used by zone instance */
+
+/*| CONFIG_ARENA: if this is defined, then the morkZone class will alloc big
+**| blocks from the zone's heap, and suballocate from these. If undefined,
+**| then morkZone will just pass all calls through to the zone's heap.
+|*/
+#ifdef MORK_ENABLE_ZONE_ARENAS
+#define morkZone_CONFIG_ARENA 1 /* be arena, if defined; otherwise no-op */
+#endif /*MORK_ENABLE_ZONE_ARENAS*/
+
+/*| CONFIG_ALIGN_8: if this is defined, then the morkZone class will give
+**| blocks 8 byte alignment instead of only 4 byte alignment.
+|*/
+#ifdef MORK_CONFIG_ALIGN_8
+#define morkZone_CONFIG_ALIGN_8 1 /* ifdef: align to 8 bytes, otherwise 4 */
+#endif /*MORK_CONFIG_ALIGN_8*/
+
+/*| CONFIG_PTR_SIZE_4: if this is defined, then the morkZone class will
+**| assume sizeof(void*) == 4, so a tag slot for padding is needed.
+|*/
+#ifdef MORK_CONFIG_PTR_SIZE_4
+#define morkZone_CONFIG_PTR_SIZE_4 1 /* ifdef: sizeof(void*) == 4 */
+#endif /*MORK_CONFIG_PTR_SIZE_4*/
+
+/*| morkZone_USE_TAG_SLOT: if this is defined, then define slot mRun_Tag
+**| in order to achieve eight byte alignment after the mRun_Next slot.
+|*/
+#if defined(morkZone_CONFIG_ALIGN_8) && defined(morkZone_CONFIG_PTR_SIZE_4)
+#define morkRun_USE_TAG_SLOT 1 /* need mRun_Tag slot inside morkRun */
+#define morkHunk_USE_TAG_SLOT 1 /* need mHunk_Tag slot inside morkHunk */
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkRun_kTag ((mork_u4) 0x6D52754E ) /* ascii 'mRuN' */
+
+/*| morkRun: structure used by morkZone for sized blocks
+|*/
+class morkRun {
+
+protected: // member variable slots
+#ifdef morkRun_USE_TAG_SLOT
+ mork_u4 mRun_Tag; // force 8 byte alignment after mRun_Next
+#endif /* morkRun_USE_TAG_SLOT */
+
+ morkRun* mRun_Next;
+
+public: // pointer interpretation of mRun_Next (when inside a list):
+ morkRun* RunNext() const { return mRun_Next; }
+ void RunSetNext(morkRun* ioNext) { mRun_Next = ioNext; }
+
+public: // size interpretation of mRun_Next (when not inside a list):
+ mork_size RunSize() const { return (mork_size) ((mork_ip) mRun_Next); }
+ void RunSetSize(mork_size inSize)
+ { mRun_Next = (morkRun*) ((mork_ip) inSize); }
+
+public: // maintenance and testing of optional tag magic signature slot:
+#ifdef morkRun_USE_TAG_SLOT
+ void RunInitTag() { mRun_Tag = morkRun_kTag; }
+ mork_bool RunGoodTag() { return ( mRun_Tag == morkRun_kTag ); }
+#endif /* morkRun_USE_TAG_SLOT */
+
+public: // conversion back and forth to inline block following run instance:
+ void* RunAsBlock() { return (((mork_u1*) this) + sizeof(morkRun)); }
+
+ static morkRun* BlockAsRun(void* ioBlock)
+ { return (morkRun*) (((mork_u1*) ioBlock) - sizeof(morkRun)); }
+
+public: // typing & errors
+ static void BadRunTagError(morkEnv* ev);
+ static void RunSizeAlignError(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+/*| morkOldRun: more space to record size when run is put into old free list
+|*/
+class morkOldRun : public morkRun {
+
+protected: // need another size field when mRun_Next is used for linkage:
+ mdb_size mOldRun_Size;
+
+public: // size getter/setter
+ mork_size OldSize() const { return mOldRun_Size; }
+ void OldSetSize(mork_size inSize) { mOldRun_Size = inSize; }
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define morkHunk_kTag ((mork_u4) 0x68556E4B ) /* ascii 'hUnK' */
+
+/*| morkHunk: structure used by morkZone for heap allocations.
+|*/
+class morkHunk {
+
+protected: // member variable slots
+
+#ifdef morkHunk_USE_TAG_SLOT
+ mork_u4 mHunk_Tag; // force 8 byte alignment after mHunk_Next
+#endif /* morkHunk_USE_TAG_SLOT */
+
+ morkHunk* mHunk_Next;
+
+ morkRun mHunk_Run;
+
+public: // setters
+ void HunkSetNext(morkHunk* ioNext) { mHunk_Next = ioNext; }
+
+public: // getters
+ morkHunk* HunkNext() const { return mHunk_Next; }
+
+ morkRun* HunkRun() { return &mHunk_Run; }
+
+public: // maintenance and testing of optional tag magic signature slot:
+#ifdef morkHunk_USE_TAG_SLOT
+ void HunkInitTag() { mHunk_Tag = morkHunk_kTag; }
+ mork_bool HunkGoodTag() { return ( mHunk_Tag == morkHunk_kTag ); }
+#endif /* morkHunk_USE_TAG_SLOT */
+
+public: // typing & errors
+ static void BadHunkTagWarning(morkEnv* ev);
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+/*| kNewHunkSize: the default size for a hunk, assuming we must allocate
+**| a new one whenever the free hunk list does not already have. Note this
+**| number should not be changed without also considering suitable changes
+**| in the related kMaxHunkWaste and kMinHunkSize constants.
+|*/
+#define morkZone_kNewHunkSize ((mork_size) (64 * 1024)) /* 64K per hunk */
+
+/*| kMaxFreeVolume: some number of bytes of free space in the free hunk list
+**| over which we no longer want to add more free hunks to the list, for fear
+**| of accumulating too much unused, fragmented free space. This should be a
+**| small multiple of kNewHunkSize, say about two to four times as great, to
+**| allow for no more free hunk space than fits in a handful of new hunks.
+**| This strategy will let us usefully accumulate "some" free space in the
+**| free hunk list, but without accumulating "too much" free space that way.
+|*/
+#define morkZone_kMaxFreeVolume (morkZone_kNewHunkSize * 3)
+
+/*| kMaxHunkWaste: if a current request is larger than this, and we cannot
+**| satisfy the request with the current hunk, then we just allocate the
+**| block from the heap without changing the current hunk. Basically this
+**| number represents the largest amount of memory we are willing to waste,
+**| since a block request barely less than this can cause the current hunk
+**| to be retired (with any unused space wasted) as well get a new hunk.
+|*/
+#define morkZone_kMaxHunkWaste ((mork_size) 4096) /* 1/16 kNewHunkSize */
+
+/*| kRound*: the algorithm for rounding up allocation sizes for caching
+**| in free lists works like the following. We add kRoundAdd to any size
+**| requested, and then bitwise AND with kRoundMask, and this will give us
+**| the smallest multiple of kRoundSize that is at least as large as the
+**| requested size. Then if we rightshift this number by kRoundBits, we
+**| will have the index into the mZone_FreeRuns array which will hold any
+**| cache runs of that size. So 4 bits of shift gives us a granularity
+**| of 16 bytes, so that free lists will hold successive runs that are
+**| 16 bytes greater than the next smaller run size. If we have 256 free
+**| lists of nonzero sized runs altogether, then the largest run that can
+**| be cached is 4096, or 4K (since 4096 == 16 * 256). A larger run that
+**| gets freed will go in to the free hunk list (or back to the heap).
+|*/
+#define morkZone_kRoundBits 4 /* bits to round-up size for free lists */
+#define morkZone_kRoundSize (1 << morkZone_kRoundBits)
+#define morkZone_kRoundAdd ((1 << morkZone_kRoundBits) - 1)
+#define morkZone_kRoundMask (~ ((mork_ip) morkZone_kRoundAdd))
+
+#define morkZone_kBuckets 256 /* number of distinct free lists */
+
+/*| kMaxCachedRun: the largest run that will be stored inside a free
+**| list of old zapped runs. A run larger than this cannot be put in
+**| a free list, and must be allocated from the heap at need, and put
+**| into the free hunk list when discarded.
+|*/
+#define morkZone_kMaxCachedRun (morkZone_kBuckets * morkZone_kRoundSize)
+
+#define morkDerived_kZone /*i*/ 0x5A6E /* ascii 'Zn' */
+
+/*| morkZone: a pooled memory allocator like an NSPR arena. The term 'zone'
+**| is roughly synonymous with 'heap'. I avoid calling this class a "heap"
+**| to avoid any confusion with nsIMdbHeap, and I avoid calling this class
+**| an arean to avoid confusion with NSPR usage.
+|*/
+class morkZone : public morkNode, public nsIMdbHeap {
+
+// public: // slots inherited from morkNode (meant to inform only)
+ // nsIMdbHeap* mNode_Heap;
+
+ // mork_base mNode_Base; // must equal morkBase_kNode
+ // mork_derived mNode_Derived; // depends on specific node subclass
+
+ // mork_access mNode_Access; // kOpen, kClosing, kShut, or kDead
+ // mork_usage mNode_Usage; // kHeap, kStack, kMember, kGlobal, kNone
+ // mork_able mNode_Mutable; // can this node be modified?
+ // mork_load mNode_Load; // is this node clean or dirty?
+
+ // mork_uses mNode_Uses; // refcount for strong refs
+ // mork_refs mNode_Refs; // refcount for strong refs + weak refs
+
+public: // state is public because the entire Mork system is private
+
+ nsIMdbHeap* mZone_Heap; // strong ref to heap allocating all space
+
+ mork_size mZone_HeapVolume; // total bytes allocated from heap
+ mork_size mZone_BlockVolume; // total bytes in all zone blocks
+ mork_size mZone_RunVolume; // total bytes in all zone runs
+ mork_size mZone_ChipVolume; // total bytes in all zone chips
+
+ mork_size mZone_FreeOldRunVolume; // total bytes in all used hunks
+
+ mork_count mZone_HunkCount; // total number of used hunks
+ mork_count mZone_FreeOldRunCount; // total free old runs
+
+ morkHunk* mZone_HunkList; // linked list of all used hunks
+ morkRun* mZone_FreeOldRunList; // linked list of free old runs
+
+ // note mZone_At is a byte pointer for single byte address arithmetic:
+ mork_u1* mZone_At; // current position in most recent hunk
+ mork_size mZone_AtSize; // number of bytes remaining in this hunk
+
+ // kBuckets+1 so indexes zero through kBuckets are all okay to use:
+
+ morkRun* mZone_FreeRuns[ morkZone_kBuckets + 1 ];
+ // Each piece of memory stored in list mZone_FreeRuns[ i ] has an
+ // allocation size equal to sizeof(morkRun) + (i * kRoundSize), so
+ // that callers can be given a piece of memory with (i * kRoundSize)
+ // bytes of writeable space while reserving the first sizeof(morkRun)
+ // bytes to keep track of size information for later re-use. Note
+ // that mZone_FreeRuns[ 0 ] is unused because no run will be zero
+ // bytes in size (and morkZone plans to complain about zero sizes).
+
+protected: // zone utilities
+
+ mork_size zone_grow_at(morkEnv* ev, mork_size inNeededSize);
+
+ void* zone_new_chip(morkEnv* ev, mdb_size inSize); // alloc
+ morkHunk* zone_new_hunk(morkEnv* ev, mdb_size inRunSize); // alloc
+
+// { ===== begin nsIMdbHeap methods =====
+public:
+ NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+ mdb_size inSize, // requested size of new memory block
+ void** outBlock) override; // memory block of inSize bytes, or nil
+
+ NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+ void* inBlock) override;
+
+ virtual size_t GetUsedSize() override { return mZone_Heap->GetUsedSize(); }
+// } ===== end nsIMdbHeap methods =====
+
+// { ===== begin morkNode interface =====
+public: // morkNode virtual methods
+ virtual void CloseMorkNode(morkEnv* ev) override; // CloseZone() only if open
+ virtual ~morkZone(); // assert that CloseMap() executed earlier
+
+public: // morkMap construction & destruction
+ morkZone(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioNodeHeap,
+ nsIMdbHeap* ioZoneHeap);
+
+ void CloseZone(morkEnv* ev); // called by CloseMorkNode()
+
+public: // dynamic type identification
+ mork_bool IsZone() const
+ { return IsNode() && mNode_Derived == morkDerived_kZone; }
+// } ===== end morkNode methods =====
+
+// { ===== begin morkZone methods =====
+public: // chips do not know how big they are...
+ void* ZoneNewChip(morkEnv* ev, mdb_size inSize); // alloc
+
+public: // ...but runs do indeed know how big they are
+ void* ZoneNewRun(morkEnv* ev, mdb_size inSize); // alloc
+ void ZoneZapRun(morkEnv* ev, void* ioRunBody); // free
+ void* ZoneGrowRun(morkEnv* ev, void* ioRunBody, mdb_size inSize); // realloc
+
+// } ===== end morkZone methods =====
+
+public: // typing & errors
+ static void NonZoneTypeError(morkEnv* ev);
+ static void NilZoneHeapError(morkEnv* ev);
+ static void BadZoneTagError(morkEnv* ev);
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _MORKZONE_ */
diff --git a/db/mork/src/moz.build b/db/mork/src/moz.build
new file mode 100644
index 000000000..0fb9645d9
--- /dev/null
+++ b/db/mork/src/moz.build
@@ -0,0 +1,55 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'morkArray.cpp',
+ 'morkAtom.cpp',
+ 'morkAtomMap.cpp',
+ 'morkAtomSpace.cpp',
+ 'morkBead.cpp',
+ 'morkBlob.cpp',
+ 'morkBuilder.cpp',
+ 'morkCell.cpp',
+ 'morkCellObject.cpp',
+ 'morkCh.cpp',
+ 'morkConfig.cpp',
+ 'morkCursor.cpp',
+ 'morkDeque.cpp',
+ 'morkEnv.cpp',
+ 'morkFactory.cpp',
+ 'morkFile.cpp',
+ 'morkHandle.cpp',
+ 'morkIntMap.cpp',
+ 'morkMap.cpp',
+ 'morkNode.cpp',
+ 'morkNodeMap.cpp',
+ 'morkObject.cpp',
+ 'morkParser.cpp',
+ 'morkPool.cpp',
+ 'morkPortTableCursor.cpp',
+ 'morkProbeMap.cpp',
+ 'morkRow.cpp',
+ 'morkRowCellCursor.cpp',
+ 'morkRowMap.cpp',
+ 'morkRowObject.cpp',
+ 'morkRowSpace.cpp',
+ 'morkSink.cpp',
+ 'morkSpace.cpp',
+ 'morkStore.cpp',
+ 'morkStream.cpp',
+ 'morkTable.cpp',
+ 'morkTableRowCursor.cpp',
+ 'morkThumb.cpp',
+ 'morkWriter.cpp',
+ 'morkYarn.cpp',
+ 'morkZone.cpp',
+ 'orkinHeap.cpp',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ SOURCES += ['morkSearchRowCursor.cpp']
+
+FINAL_LIBRARY = 'mork'
+
diff --git a/db/mork/src/orkinHeap.cpp b/db/mork/src/orkinHeap.cpp
new file mode 100644
index 000000000..4a309a154
--- /dev/null
+++ b/db/mork/src/orkinHeap.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+#ifndef _ORKINHEAP_
+#include "orkinHeap.h"
+#endif
+
+#ifndef _MORKENV_
+#include "morkEnv.h"
+#endif
+
+#include "nsIMemoryReporter.h"
+
+#include <stdlib.h>
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+
+orkinHeap::orkinHeap() // does nothing
+ : mUsedSize(0)
+{
+}
+
+/*virtual*/
+orkinHeap::~orkinHeap() // does nothing
+{
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MorkSizeOfOnAlloc)
+MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MorkSizeOfOnFree)
+
+// { ===== begin nsIMdbHeap methods =====
+/*virtual*/ nsresult
+orkinHeap::Alloc(nsIMdbEnv* mev, // allocate a piece of memory
+ mdb_size inSize, // requested size of new memory block
+ void** outBlock) // memory block of inSize bytes, or nil
+{
+
+ MORK_USED_1(mev);
+ nsresult outErr = NS_OK;
+ void* block = malloc(inSize);
+ if ( !block )
+ outErr = morkEnv_kOutOfMemoryError;
+ else
+ mUsedSize += MorkSizeOfOnAlloc(block);
+
+ MORK_ASSERT(outBlock);
+ if ( outBlock )
+ *outBlock = block;
+ return outErr;
+}
+
+/*virtual*/ nsresult
+orkinHeap::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc()
+ void* inBlock)
+{
+ MORK_USED_1(mev);
+ MORK_ASSERT(inBlock);
+ if ( inBlock )
+ {
+ mUsedSize -= MorkSizeOfOnFree(inBlock);
+ free(inBlock);
+ }
+ return NS_OK;
+}
+
+size_t
+orkinHeap::GetUsedSize()
+{
+ return mUsedSize;
+}
+// } ===== end nsIMdbHeap methods =====
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
diff --git a/db/mork/src/orkinHeap.h b/db/mork/src/orkinHeap.h
new file mode 100644
index 000000000..a3c14d295
--- /dev/null
+++ b/db/mork/src/orkinHeap.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _ORKINHEAP_
+#define _ORKINHEAP_ 1
+
+#ifndef _MDB_
+#include "mdb.h"
+#endif
+
+#ifndef _MORK_
+#include "mork.h"
+#endif
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#define orkinHeap_kTag 0x68456150 /* ascii 'hEaP' */
+
+/*| orkinHeap:
+|*/
+class orkinHeap : public nsIMdbHeap { //
+protected:
+ size_t mUsedSize;
+
+public:
+ orkinHeap(); // does nothing
+ virtual ~orkinHeap(); // does nothing
+
+private: // copying is not allowed
+ orkinHeap(const orkinHeap& other);
+ orkinHeap& operator=(const orkinHeap& other);
+
+public:
+
+// { ===== begin nsIMdbHeap methods =====
+ NS_IMETHOD Alloc(nsIMdbEnv* ev, // allocate a piece of memory
+ mdb_size inSize, // requested size of new memory block
+ void** outBlock); // memory block of inSize bytes, or nil
+
+ NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc()
+ void* inBlock);
+
+ virtual size_t GetUsedSize();
+// } ===== end nsIMdbHeap methods =====
+
+};
+
+//3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
+
+#endif /* _ORKINHEAP_ */
diff --git a/db/moz.build b/db/moz.build
new file mode 100644
index 000000000..2f360fad6
--- /dev/null
+++ b/db/moz.build
@@ -0,0 +1,11 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if not CONFIG['NSS_DISABLE_DBM'] and CONFIG['MOZ_MORK']:
+ DIRS += [
+ 'mork/public',
+ 'mork/src',
+ 'mork/build',
+ ]
diff --git a/db/sqlite3/src/sqlite3.c b/db/sqlite3/src/sqlite3.c
index 61bfdeb76..8fd740b30 100644
--- a/db/sqlite3/src/sqlite3.c
+++ b/db/sqlite3/src/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.29.0. By combining all the individual C code files into this
+** version 3.30.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -331,8 +331,6 @@ static const char * const sqlite3azCompileOpt[] = {
#endif
#if defined(SQLITE_ENABLE_STAT4)
"ENABLE_STAT4",
-#elif defined(SQLITE_ENABLE_STAT3)
- "ENABLE_STAT3",
#endif
#if SQLITE_ENABLE_STMTVTAB
"ENABLE_STMTVTAB",
@@ -1167,9 +1165,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.29.0"
-#define SQLITE_VERSION_NUMBER 3029000
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6"
+#define SQLITE_VERSION "3.30.1"
+#define SQLITE_VERSION_NUMBER 3030001
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -3137,6 +3135,17 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back. </dd>
+**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the
@@ -3309,7 +3318,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1014 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -4858,7 +4868,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -5893,6 +5903,12 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
+** flag is recommended for any application-defined SQL function that has
+** side-effects.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -6009,8 +6025,30 @@ SQLITE_API int sqlite3_create_window_function(
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** The SQLITE_DETERMINISTIC flag means that the new function will always
+** maps the same inputs into the same output. The abs() function is
+** deterministic, for example, but randomblob() is not.
+**
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
+** a security feature which is recommended for all
+** [application-defined SQL functions] that have side-effects. This flag
+** prevents an attacker from adding triggers and views to a schema then
+** tricking a high-privilege application into causing unintended side-effects
+** while performing ordinary queries.
+**
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** Specifying this flag makes no difference for scalar or aggregate user
+** functions. However, if it is not specified for a user-defined window
+** function, then any sub-types belonging to arguments passed to the window
+** function may be discarded before the window function is called (i.e.
+** sqlite3_value_subtype() will always return 0).
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
/*
** CAPI3REF: Deprecated Functions
@@ -7656,6 +7694,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is create and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -7672,6 +7716,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -8379,7 +8440,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8402,7 +8463,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
-#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
@@ -13099,15 +13162,15 @@ struct fts5_api {
** So we have to define the macros in different ways depending on the
** compiler.
*/
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+#if defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
+#elif defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
-#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
-# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
@@ -13597,100 +13660,103 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_VIEW 79
#define TK_VIRTUAL 80
#define TK_WITH 81
-#define TK_CURRENT 82
-#define TK_FOLLOWING 83
-#define TK_PARTITION 84
-#define TK_PRECEDING 85
-#define TK_RANGE 86
-#define TK_UNBOUNDED 87
-#define TK_EXCLUDE 88
-#define TK_GROUPS 89
-#define TK_OTHERS 90
-#define TK_TIES 91
-#define TK_REINDEX 92
-#define TK_RENAME 93
-#define TK_CTIME_KW 94
-#define TK_ANY 95
-#define TK_BITAND 96
-#define TK_BITOR 97
-#define TK_LSHIFT 98
-#define TK_RSHIFT 99
-#define TK_PLUS 100
-#define TK_MINUS 101
-#define TK_STAR 102
-#define TK_SLASH 103
-#define TK_REM 104
-#define TK_CONCAT 105
-#define TK_COLLATE 106
-#define TK_BITNOT 107
-#define TK_ON 108
-#define TK_INDEXED 109
-#define TK_STRING 110
-#define TK_JOIN_KW 111
-#define TK_CONSTRAINT 112
-#define TK_DEFAULT 113
-#define TK_NULL 114
-#define TK_PRIMARY 115
-#define TK_UNIQUE 116
-#define TK_CHECK 117
-#define TK_REFERENCES 118
-#define TK_AUTOINCR 119
-#define TK_INSERT 120
-#define TK_DELETE 121
-#define TK_UPDATE 122
-#define TK_SET 123
-#define TK_DEFERRABLE 124
-#define TK_FOREIGN 125
-#define TK_DROP 126
-#define TK_UNION 127
-#define TK_ALL 128
-#define TK_EXCEPT 129
-#define TK_INTERSECT 130
-#define TK_SELECT 131
-#define TK_VALUES 132
-#define TK_DISTINCT 133
-#define TK_DOT 134
-#define TK_FROM 135
-#define TK_JOIN 136
-#define TK_USING 137
-#define TK_ORDER 138
-#define TK_GROUP 139
-#define TK_HAVING 140
-#define TK_LIMIT 141
-#define TK_WHERE 142
-#define TK_INTO 143
-#define TK_NOTHING 144
-#define TK_FLOAT 145
-#define TK_BLOB 146
-#define TK_INTEGER 147
-#define TK_VARIABLE 148
-#define TK_CASE 149
-#define TK_WHEN 150
-#define TK_THEN 151
-#define TK_ELSE 152
-#define TK_INDEX 153
-#define TK_ALTER 154
-#define TK_ADD 155
-#define TK_WINDOW 156
-#define TK_OVER 157
-#define TK_FILTER 158
-#define TK_TRUEFALSE 159
-#define TK_ISNOT 160
-#define TK_FUNCTION 161
+#define TK_NULLS 82
+#define TK_FIRST 83
+#define TK_LAST 84
+#define TK_CURRENT 85
+#define TK_FOLLOWING 86
+#define TK_PARTITION 87
+#define TK_PRECEDING 88
+#define TK_RANGE 89
+#define TK_UNBOUNDED 90
+#define TK_EXCLUDE 91
+#define TK_GROUPS 92
+#define TK_OTHERS 93
+#define TK_TIES 94
+#define TK_REINDEX 95
+#define TK_RENAME 96
+#define TK_CTIME_KW 97
+#define TK_ANY 98
+#define TK_BITAND 99
+#define TK_BITOR 100
+#define TK_LSHIFT 101
+#define TK_RSHIFT 102
+#define TK_PLUS 103
+#define TK_MINUS 104
+#define TK_STAR 105
+#define TK_SLASH 106
+#define TK_REM 107
+#define TK_CONCAT 108
+#define TK_COLLATE 109
+#define TK_BITNOT 110
+#define TK_ON 111
+#define TK_INDEXED 112
+#define TK_STRING 113
+#define TK_JOIN_KW 114
+#define TK_CONSTRAINT 115
+#define TK_DEFAULT 116
+#define TK_NULL 117
+#define TK_PRIMARY 118
+#define TK_UNIQUE 119
+#define TK_CHECK 120
+#define TK_REFERENCES 121
+#define TK_AUTOINCR 122
+#define TK_INSERT 123
+#define TK_DELETE 124
+#define TK_UPDATE 125
+#define TK_SET 126
+#define TK_DEFERRABLE 127
+#define TK_FOREIGN 128
+#define TK_DROP 129
+#define TK_UNION 130
+#define TK_ALL 131
+#define TK_EXCEPT 132
+#define TK_INTERSECT 133
+#define TK_SELECT 134
+#define TK_VALUES 135
+#define TK_DISTINCT 136
+#define TK_DOT 137
+#define TK_FROM 138
+#define TK_JOIN 139
+#define TK_USING 140
+#define TK_ORDER 141
+#define TK_GROUP 142
+#define TK_HAVING 143
+#define TK_LIMIT 144
+#define TK_WHERE 145
+#define TK_INTO 146
+#define TK_NOTHING 147
+#define TK_FLOAT 148
+#define TK_BLOB 149
+#define TK_INTEGER 150
+#define TK_VARIABLE 151
+#define TK_CASE 152
+#define TK_WHEN 153
+#define TK_THEN 154
+#define TK_ELSE 155
+#define TK_INDEX 156
+#define TK_ALTER 157
+#define TK_ADD 158
+#define TK_WINDOW 159
+#define TK_OVER 160
+#define TK_FILTER 161
#define TK_COLUMN 162
#define TK_AGG_FUNCTION 163
#define TK_AGG_COLUMN 164
-#define TK_UMINUS 165
-#define TK_UPLUS 166
-#define TK_TRUTH 167
-#define TK_REGISTER 168
-#define TK_VECTOR 169
-#define TK_SELECT_COLUMN 170
-#define TK_IF_NULL_ROW 171
-#define TK_ASTERISK 172
-#define TK_SPAN 173
-#define TK_SPACE 174
-#define TK_ILLEGAL 175
+#define TK_TRUEFALSE 165
+#define TK_ISNOT 166
+#define TK_FUNCTION 167
+#define TK_UMINUS 168
+#define TK_UPLUS 169
+#define TK_TRUTH 170
+#define TK_REGISTER 171
+#define TK_VECTOR 172
+#define TK_SELECT_COLUMN 173
+#define TK_IF_NULL_ROW 174
+#define TK_ASTERISK 175
+#define TK_SPAN 176
+#define TK_SPACE 177
+#define TK_ILLEGAL 178
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14102,20 +14168,6 @@ typedef INT16_TYPE LogEst;
#endif
/*
-** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
-** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
-** define SQLITE_ENABLE_STAT3_OR_STAT4
-*/
-#ifdef SQLITE_ENABLE_STAT4
-# undef SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3_OR_STAT4
-# undef SQLITE_ENABLE_STAT3_OR_STAT4
-#endif
-
-/*
** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
** the Select query generator tracing logic is turned on.
*/
@@ -14984,24 +15036,24 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Count 93 /* synopsis: r[P2]=count() */
#define OP_ReadCookie 94
#define OP_SetCookie 95
-#define OP_BitAnd 96 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
-#define OP_BitOr 97 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
-#define OP_ShiftLeft 98 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
-#define OP_ShiftRight 99 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
-#define OP_Add 100 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
-#define OP_Subtract 101 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
-#define OP_Multiply 102 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
-#define OP_Divide 103 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
-#define OP_Remainder 104 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
-#define OP_Concat 105 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_ReopenIdx 106 /* synopsis: root=P2 iDb=P3 */
-#define OP_BitNot 107 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
-#define OP_OpenRead 108 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 109 /* synopsis: root=P2 iDb=P3 */
-#define OP_String8 110 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_OpenDup 111
-#define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */
-#define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */
+#define OP_ReopenIdx 96 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenRead 97 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 98 /* synopsis: root=P2 iDb=P3 */
+#define OP_BitAnd 99 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 100 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 101 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 102 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 103 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 104 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 105 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 106 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 107 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 108 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_OpenDup 109
+#define OP_BitNot 110 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
+#define OP_OpenAutoindex 111 /* synopsis: nColumn=P2 */
+#define OP_OpenEphemeral 112 /* synopsis: nColumn=P2 */
+#define OP_String8 113 /* same as TK_STRING, synopsis: r[P2]='P4' */
#define OP_SorterOpen 114
#define OP_SequenceTest 115 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
#define OP_OpenPseudo 116 /* synopsis: P3 columns in r[P2] */
@@ -15033,10 +15085,10 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_LoadAnalysis 142
#define OP_DropTable 143
#define OP_DropIndex 144
-#define OP_Real 145 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_DropTrigger 146
-#define OP_IntegrityCk 147
-#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */
+#define OP_DropTrigger 145
+#define OP_IntegrityCk 146
+#define OP_RowSetAdd 147 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Real 148 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
#define OP_Param 149
#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */
#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */
@@ -15085,13 +15137,13 @@ typedef struct VdbeOpList VdbeOpList;
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
/* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\
-/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
-/* 104 */ 0x26, 0x26, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00,\
-/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 96 */ 0x00, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12, 0x00,\
+/* 112 */ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
-/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 144 */ 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x04,\
/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,}
@@ -15161,10 +15213,10 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*);
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
#endif
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
@@ -16121,6 +16173,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
#define MUTEX_LOGIC(X)
#else
#define MUTEX_LOGIC(X) X
+SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*);
#endif /* defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.h ***********************************************/
@@ -16379,6 +16432,7 @@ struct sqlite3 {
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
unsigned imposterTable : 1; /* Building an imposter table */
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
+ char **azInit; /* "type", "name", and "tbl_name" columns */
} init;
int nVdbeActive; /* Number of VDBEs currently running */
int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -16517,16 +16571,17 @@ struct sqlite3 {
#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */
#define SQLITE_DqsDDL 0x20000000 /* dbl-quoted strings allowed in DDL*/
#define SQLITE_DqsDML 0x40000000 /* dbl-quoted strings allowed in DML*/
+#define SQLITE_EnableView 0x80000000 /* Enable the use of views */
/* Flags used only if debugging */
#define HI(X) ((u64)(X)<<32)
#ifdef SQLITE_DEBUG
-#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */
-#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */
-#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */
-#define SQLITE_ParserTrace HI(0x0020) /* PRAGMA parser_trace=ON */
+#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing HI(0x0200000) /* Debug listings of VDBE progs */
+#define SQLITE_VdbeTrace HI(0x0400000) /* True to trace VDBE execution */
+#define SQLITE_VdbeAddopTrace HI(0x0800000) /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_VdbeEQP HI(0x1000000) /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_ParserTrace HI(0x2000000) /* PRAGMA parser_trace=ON */
#endif
/*
@@ -16554,8 +16609,8 @@ struct sqlite3 {
#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */
#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */
#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
-#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
- /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */
+#define SQLITE_Stat4 0x0800 /* Use STAT4 data */
+ /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */
#define SQLITE_PushDown 0x1000 /* The push-down optimization */
#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan 0x4000 /* Skip-scans */
@@ -16643,6 +16698,7 @@ struct FuncDestructor {
** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
*/
#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
@@ -16663,6 +16719,8 @@ struct FuncDestructor {
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
+#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
+#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -16776,6 +16834,7 @@ struct Savepoint {
struct Module {
const sqlite3_module *pModule; /* Callback pointers */
const char *zName; /* Name passed to create_module() */
+ int nRefModule; /* Number of pointers to this object */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
Table *pEpoTab; /* Eponymous table for this module */
@@ -16841,11 +16900,12 @@ struct CollSeq {
** Note also that the numeric types are grouped together so that testing
** for a numeric type is a single comparison. And the BLOB type is first.
*/
-#define SQLITE_AFF_BLOB 'A'
-#define SQLITE_AFF_TEXT 'B'
-#define SQLITE_AFF_NUMERIC 'C'
-#define SQLITE_AFF_INTEGER 'D'
-#define SQLITE_AFF_REAL 'E'
+#define SQLITE_AFF_NONE 0x40 /* '@' */
+#define SQLITE_AFF_BLOB 0x41 /* 'A' */
+#define SQLITE_AFF_TEXT 0x42 /* 'B' */
+#define SQLITE_AFF_NUMERIC 0x43 /* 'C' */
+#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
+#define SQLITE_AFF_REAL 0x45 /* 'E' */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@@ -17113,11 +17173,17 @@ struct KeyInfo {
u16 nKeyField; /* Number of key columns in the index */
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
- u8 *aSortOrder; /* Sort order for each column. */
+ u8 *aSortFlags; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
+** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
+*/
+#define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
+#define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
+
+/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
**
@@ -17224,7 +17290,7 @@ struct Index {
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
@@ -17256,7 +17322,7 @@ struct Index {
#define XN_EXPR (-2) /* Indexed column is an expression */
/*
-** Each sample stored in the sqlite_stat3 table is represented in memory
+** Each sample stored in the sqlite_stat4 table is represented in memory
** using a structure of this type. See documentation at the top of the
** analyze.c source file for additional information.
*/
@@ -17414,7 +17480,7 @@ typedef int ynVar;
*/
struct Expr {
u8 op; /* Operation performed by this node */
- char affinity; /* The affinity of the column or 0 if not a column */
+ char affExpr; /* affinity, or RAISE type */
u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
@@ -17445,6 +17511,8 @@ struct Expr {
** TK_REGISTER: register number
** TK_TRIGGER: 1 -> new, 0 -> old
** EP_Unlikely: 134217728 times likelihood
+ ** TK_IN: ephemerial table holding RHS
+ ** TK_SELECT_COLUMN: Number of columns on the LHS
** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1).
@@ -17458,7 +17526,7 @@ struct Expr {
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
- Window *pWin; /* TK_FUNCTION: Window definition for the func */
+ Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -17473,36 +17541,37 @@ struct Expr {
** EP_Agg == NC_HasAgg == SF_HasAgg
** EP_Win == NC_HasWin
*/
-#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
-#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
-#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
-#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
-#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
-#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
-#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
-#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
-#define EP_Skip 0x001000 /* Operator does not contribute to affinity */
-#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
-#define EP_Win 0x008000 /* Contains window functions */
-#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
-#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
-#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
-#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
-#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
-#define EP_Alias 0x400000 /* Is an alias for a result set column */
-#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
-#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
-#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
-#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
-#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
-#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
-#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
+#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
+#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
+#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
+#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
+ /* 0x000200 Available for reuse */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* Operator does not contribute to affinity */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Win 0x008000 /* Contains window functions */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
+#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
+#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
+#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
+#define EP_Alias 0x400000 /* Is an alias for a result set column */
+#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
+#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
+#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
+#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
+#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
+#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
+#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_Indirect 0x40000000 /* Contained within a TRIGGER or a VIEW */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@@ -17547,6 +17616,18 @@ struct Expr {
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
/*
+** True if the expression passed as an argument was a function with
+** an OVER() clause (a window function).
+*/
+#ifdef SQLITE_OMIT_WINDOWFUNC
+# define IsWindowFunc(p) 0
+#else
+# define IsWindowFunc(p) ( \
+ ExprHasProperty((p), EP_WinFunc) && p->y.pWin->eFrmType!=TK_FILTER \
+ )
+#endif
+
+/*
** A list of expressions. Each expression may optionally have a
** name. An expr/name combination can be used in several ways, such
** as the list of "expr AS ID" fields following a "SELECT" or in the
@@ -17568,11 +17649,12 @@ struct ExprList {
Expr *pExpr; /* The parse tree for this expression */
char *zName; /* Token associated with this expression */
char *zSpan; /* Original text of the expression */
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
+ u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
unsigned reusable :1; /* Constant expression is reusable */
unsigned bSorterRef :1; /* Defer evaluation until after sorting */
+ unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */
union {
struct {
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@@ -17863,6 +17945,7 @@ struct Select {
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
+#define SF_WhereBegin 0x80000 /* Really a WhereBegin() call. Debug Only */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -18367,11 +18450,12 @@ typedef struct {
*/
struct Sqlite3Config {
int bMemstat; /* True to enable memory status */
- int bCoreMutex; /* True to enable core mutexing */
- int bFullMutex; /* True to enable full mutexing */
- int bOpenUri; /* True to interpret filenames as URIs */
- int bUseCis; /* Use covering indices for full-scans */
- int bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bCoreMutex; /* True to enable core mutexing */
+ u8 bFullMutex; /* True to enable full mutexing */
+ u8 bOpenUri; /* True to interpret filenames as URIs */
+ u8 bUseCis; /* Use covering indices for full-scans */
+ u8 bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -18423,6 +18507,7 @@ struct Sqlite3Config {
int bInternalFunctions; /* Internal SQL functions are visible */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
+ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */
};
/*
@@ -18519,10 +18604,11 @@ struct TreeView {
#endif /* SQLITE_DEBUG */
/*
-** This object is used in various ways, all related to window functions
+** This object is used in various ways, most (but not all) related to window
+** functions.
**
** (1) A single instance of this structure is attached to the
-** the Expr.pWin field for each window function in an expression tree.
+** the Expr.y.pWin field for each window function in an expression tree.
** This object holds the information contained in the OVER clause,
** plus additional fields used during code generation.
**
@@ -18533,6 +18619,10 @@ struct TreeView {
** (3) The terms of the WINDOW clause of a SELECT are instances of this
** object on a linked list attached to Select.pWinDefn.
**
+** (4) For an aggregate function with a FILTER clause, an instance
+** of this object is stored in Expr.y.pWin with eFrmType set to
+** TK_FILTER. In this case the only field used is Window.pFilter.
+**
** The uses (1) and (2) are really the same Window object that just happens
** to be accessible in two different ways. Use case (3) are separate objects.
*/
@@ -18548,12 +18638,13 @@ struct Window {
u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */
Expr *pStart; /* Expression for "<expr> PRECEDING" */
Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
+ Window **ppThis; /* Pointer to this object in Select.pWin list */
Window *pNextWin; /* Next window function belonging to this SELECT */
Expr *pFilter; /* The FILTER expression */
FuncDef *pFunc; /* The function */
int iEphCsr; /* Partition buffer or Peer buffer */
- int regAccum;
- int regResult;
+ int regAccum; /* Accumulator */
+ int regResult; /* Interim result */
int csrApp; /* Function cursor (used by min/max) */
int regApp; /* Function register (also used by min/max) */
int regPart; /* Array of registers for PARTITION BY values */
@@ -18563,14 +18654,18 @@ struct Window {
int regOne; /* Register containing constant value 1 */
int regStartRowid;
int regEndRowid;
+ u8 bExprArgs; /* Defer evaluation of window function arguments
+ ** due to the SQLITE_SUBTYPE flag */
};
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*);
SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*);
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin);
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int);
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*);
SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);
@@ -18842,7 +18937,7 @@ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
@@ -18861,8 +18956,8 @@ SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*);
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char);
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
@@ -19163,7 +19258,7 @@ SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
#endif
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_ENABLE_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
#endif
@@ -19229,9 +19324,10 @@ SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
+SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*);
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64);
@@ -19264,7 +19360,6 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
-SQLITE_PRIVATE const Token sqlite3IntTokens[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
#ifndef SQLITE_OMIT_WSD
@@ -19318,6 +19413,7 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
@@ -19350,8 +19446,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse*, Expr*);
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+#ifdef SQLITE_ENABLE_STAT4
SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
@@ -19398,6 +19493,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3VtabInSync(db) 0
# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
+# define sqlite3VtabModuleUnref(D,X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
# define sqlite3GetVTable(X,Y) ((VTable*)0)
@@ -19409,6 +19505,7 @@ SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
@@ -19876,6 +19973,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0, /* bSmallMalloc */
+ 1, /* bExtraSchemaChecks */
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
@@ -19922,6 +20020,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* bInternalFunctions */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
+ 0, /* iPrngSeed */
};
/*
@@ -19931,14 +20030,6 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
*/
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
-/*
-** Constant tokens for values 0 and 1.
-*/
-SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
- { "0", 1 },
- { "1", 1 }
-};
-
#ifdef VDBE_PROFILE
/*
** The following performance counter can be used in place of
@@ -20491,7 +20582,6 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);
@@ -22497,7 +22587,15 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
}
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
- return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ if( sqlite3Config.iPrngSeed ){
+ memset(zBufOut, 0, nByte);
+ if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int);
+ memcpy(zBufOut, &sqlite3Config.iPrngSeed, nByte);
+ return SQLITE_OK;
+ }else{
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ }
+
}
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
return pVfs->xSleep(pVfs, nMicro);
@@ -28778,13 +28876,17 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
sqlite3TreeViewPush(pView, 1);
}
do{
- sqlite3TreeViewLine(pView,
- "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
- ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
- ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
- p->selId, p, p->selFlags,
- (int)p->nSelectRow
- );
+ if( p->selFlags & SF_WhereBegin ){
+ sqlite3TreeViewLine(pView, "sqlite3WhereBegin()");
+ }else{
+ sqlite3TreeViewLine(pView,
+ "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
+ p->selId, p, p->selFlags,
+ (int)p->nSelectRow
+ );
+ }
if( cnt++ ) sqlite3TreeViewPop(pView);
if( p->pPrior ){
n = 1000;
@@ -28801,7 +28903,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p->pWinDefn ) n++;
#endif
}
- sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
+ if( p->pEList ){
+ sqlite3TreeViewExprList(pView, p->pEList, n>0, "result-set");
+ }
+ n--;
#ifndef SQLITE_OMIT_WINDOWFUNC
if( p->pWin ){
Window *pX;
@@ -28997,12 +29102,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewPop(pView);
return;
}
- if( pExpr->flags ){
+ if( pExpr->flags || pExpr->affExpr ){
if( ExprHasProperty(pExpr, EP_FromJoin) ){
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x iRJT=%d",
- pExpr->flags, pExpr->iRightJoinTable);
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," fg.af=%x.%c iRJT=%d",
+ pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n',
+ pExpr->iRightJoinTable);
}else{
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," fg.af=%x.%c",
+ pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n');
}
}else{
zFlgs[0] = 0;
@@ -29129,7 +29236,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
}
case TK_COLLATE: {
- sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
+ /* COLLATE operators without the EP_Collate flag are intended to
+ ** emulate collation associated with a table column. These show
+ ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE
+ ** operators that appear in the original SQL always have the
+ ** EP_Collate bit set and appear in treeview output as just "COLLATE" */
+ sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s",
+ !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "",
+ pExpr->u.zToken, zFlgs);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
@@ -29150,10 +29264,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#endif
}
if( pExpr->op==TK_AGG_FUNCTION ){
- sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
- pExpr->op2, pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s",
+ pExpr->op2, pExpr->u.zToken, zFlgs);
}else{
- sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
@@ -29230,7 +29344,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
const char *zType = "unk";
- switch( pExpr->affinity ){
+ switch( pExpr->affExpr ){
case OE_Rollback: zType = "rollback"; break;
case OE_Abort: zType = "abort"; break;
case OE_Fail: zType = "fail"; break;
@@ -29271,7 +29385,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
}else if( zUniOp ){
sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
}
sqlite3TreeViewPop(pView);
}
@@ -31779,7 +31893,7 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_ENABLE_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
** Convert a LogEst into an integer.
@@ -31797,7 +31911,7 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
if( x>60 ) return (u64)LARGEST_INT64;
#else
- /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
+ /* If only SQLITE_ENABLE_STAT4 is on, then the largest input
** possible to this routine is 310, resulting in a maximum x of 31 */
assert( x<=60 );
#endif
@@ -32290,24 +32404,24 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 93 */ "Count" OpHelp("r[P2]=count()"),
/* 94 */ "ReadCookie" OpHelp(""),
/* 95 */ "SetCookie" OpHelp(""),
- /* 96 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
- /* 97 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
- /* 98 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
- /* 99 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
- /* 100 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
- /* 101 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
- /* 102 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
- /* 103 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
- /* 104 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
- /* 105 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 106 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 107 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
- /* 108 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 109 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
- /* 110 */ "String8" OpHelp("r[P2]='P4'"),
- /* 111 */ "OpenDup" OpHelp(""),
- /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"),
- /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 96 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 97 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 98 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 99 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 100 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 101 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 102 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 103 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 104 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 105 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 106 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 107 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 108 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 109 */ "OpenDup" OpHelp(""),
+ /* 110 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
+ /* 111 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 112 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 113 */ "String8" OpHelp("r[P2]='P4'"),
/* 114 */ "SorterOpen" OpHelp(""),
/* 115 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
/* 116 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
@@ -32339,10 +32453,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 142 */ "LoadAnalysis" OpHelp(""),
/* 143 */ "DropTable" OpHelp(""),
/* 144 */ "DropIndex" OpHelp(""),
- /* 145 */ "Real" OpHelp("r[P2]=P4"),
- /* 146 */ "DropTrigger" OpHelp(""),
- /* 147 */ "IntegrityCk" OpHelp(""),
- /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 145 */ "DropTrigger" OpHelp(""),
+ /* 146 */ "IntegrityCk" OpHelp(""),
+ /* 147 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 148 */ "Real" OpHelp("r[P2]=P4"),
/* 149 */ "Param" OpHelp(""),
/* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
/* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
@@ -32481,13 +32595,29 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
# include <sys/param.h>
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
- (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
-# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
- && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
-# define HAVE_GETHOSTUUID 1
-# else
-# warning "gethostuuid() is disabled."
+/*
+** Try to determine if gethostuuid() is available based on standard
+** macros. This might sometimes compute the wrong value for some
+** obscure platforms. For those cases, simply compile with one of
+** the following:
+**
+** -DHAVE_GETHOSTUUID=0
+** -DHAVE_GETHOSTUUID=1
+**
+** None if this matters except when building on Apple products with
+** -DSQLITE_ENABLE_LOCKING_STYLE.
+*/
+#ifndef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 0
+# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
+ (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
+# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
+# undef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 1
+# else
+# warning "gethostuuid() is disabled."
+# endif
# endif
#endif
@@ -33095,13 +33225,14 @@ static struct unix_syscall {
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
# ifdef __ANDROID__
{ "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 },
+#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
# else
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
+#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent)
# endif
#else
{ "ioctl", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
}; /* End of the overrideable system calls */
@@ -38343,6 +38474,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
UnixUnusedFd **pp;
assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
sqlite3_mutex_enter(pInode->pLockMutex);
+ flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
pUnused = *pp;
if( pUnused ){
@@ -38396,7 +38528,7 @@ static int getFileMode(
** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
** original filename is unavailable. But 8_3_NAMES is only used for
** FAT filesystems and permissions do not matter there, so just use
-** the default permissions.
+** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero.
*/
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
@@ -38631,11 +38763,19 @@ static int unixOpen(
goto open_finished;
}
- /* If this process is running as root and if creating a new rollback
- ** journal or WAL file, set the ownership of the journal or WAL to be
- ** the same as the original database.
+ /* The owner of the rollback journal or WAL file should always be the
+ ** same as the owner of the database file. Try to ensure that this is
+ ** the case. The chown() system call will be a no-op if the current
+ ** process lacks root privileges, be we should at least try. Without
+ ** this step, if a root process opens a database file, it can leave
+ ** behinds a journal/WAL that is owned by root and hence make the
+ ** database inaccessible to unprivileged processes.
+ **
+ ** If openMode==0, then that means uid and gid are not set correctly
+ ** (probably because SQLite is configured to use 8+3 filename mode) and
+ ** in that case we do not want to attempt the chown().
*/
- if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
+ if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
robustFchown(fd, uid, gid);
}
}
@@ -38646,7 +38786,8 @@ static int unixOpen(
if( p->pPreallocatedUnused ){
p->pPreallocatedUnused->fd = fd;
- p->pPreallocatedUnused->flags = flags;
+ p->pPreallocatedUnused->flags =
+ flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
}
if( isDelete ){
@@ -39492,7 +39633,7 @@ SQLITE_API int sqlite3_hostid_num = 0;
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
/* Not always defined in the headers as it ought to be */
extern int gethostuuid(uuid_t id, const struct timespec *wait);
#endif
@@ -39503,7 +39644,7 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
static int proxyGetHostID(unsigned char *pHostID, int *pError){
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
{
struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
@@ -40177,7 +40318,7 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
assert( 0 ); /* The call assures that only valid opcodes are sent */
}
}
- /*NOTREACHED*/
+ /*NOTREACHED*/ assert(0);
return SQLITE_ERROR;
}
@@ -44862,6 +45003,7 @@ static int winShmMap(
rc = winOpenSharedMemory(pDbFd);
if( rc!=SQLITE_OK ) return rc;
pShm = pDbFd->pShm;
+ assert( pShm!=0 );
}
pShmNode = pShm->pShmNode;
@@ -45164,6 +45306,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
}
}
if( pFd->mmapSize >= iOff+nAmt ){
+ assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -48085,6 +48228,7 @@ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
+ assert( sqlite3GlobalConfig.pcache2.xInit!=0 );
}
return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
}
@@ -49131,6 +49275,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
+ assert( pCache->pFree!=0 );
p = pCache->pFree;
pCache->pFree = p->pNext;
p->pNext = 0;
@@ -61857,6 +62002,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
+ assert( pLast!=0 );
}
}
if( bSync ){
@@ -61889,6 +62035,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
+ assert( pLast!=0 || nExtra==0 );
while( rc==SQLITE_OK && nExtra>0 ){
iFrame++;
nExtra--;
@@ -64900,9 +65047,12 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
if( pSpace ){
- assert( pSpace>=data && (pSpace - data)<65536 );
- *pIdx = (int)(pSpace - data);
- return SQLITE_OK;
+ assert( pSpace+nByte<=data+pPage->pBt->usableSize );
+ if( (*pIdx = (int)(pSpace-data))<=gap ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }else{
+ return SQLITE_OK;
+ }
}else if( rc ){
return rc;
}
@@ -68129,6 +68279,7 @@ static int accessPayload(
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
+ if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
@@ -69917,12 +70068,7 @@ static void insertCell(
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- /* The cell should normally be sized correctly. However, when moving a
- ** malformed cell from a leaf page to an interior page, if the cell size
- ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
- ** might be less than 8 (leaf-size + pointer) on the interior node. Hence
- ** the term after the || in the following assert(). */
- assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
assert( pPage->nFree>=0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
@@ -70154,7 +70300,7 @@ static int rebuildPage(
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
- if( NEVER(j>(u32)usableSize) ){ j = 0; }
+ if( j>(u32)usableSize ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; pCArray->ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
@@ -70246,7 +70392,8 @@ static int pageInsertArray(
while( 1 /*Exit by break*/ ){
int sz, rc;
u8 *pSlot;
- sz = cachedCellSize(pCArray, i);
+ assert( pCArray->szCell[i]!=0 );
+ sz = pCArray->szCell[i];
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
if( (pData - pBegin)<sz ) return 1;
pData -= sz;
@@ -70407,6 +70554,7 @@ static int editPage(
memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
}
nCell++;
+ cachedCellSize(pCArray, iCell+iNew);
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
iCell+iNew, 1, pCArray
@@ -70929,7 +71077,7 @@ static int balance_nonroot(
*/
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
- if( limit<pOld->aiOvfl[0] ){
+ if( NEVER(limit<pOld->aiOvfl[0]) ){
rc = SQLITE_CORRUPT_BKPT;
goto balance_cleanup;
}
@@ -71215,6 +71363,8 @@ static int balance_nonroot(
));
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
+ assert( nNew>=1 && nNew<=ArraySize(apNew) );
+ assert( apNew[nNew-1]!=0 );
put4byte(pRight, apNew[nNew-1]->pgno);
/* If the sibling pages are not leaves, ensure that the right-child pointer
@@ -71560,11 +71710,13 @@ static int balance(BtCursor *pCur){
VVA_ONLY( int balance_deeper_called = 0 );
do {
- int iPage = pCur->iPage;
+ int iPage;
MemPage *pPage = pCur->pPage;
if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
- if( iPage==0 ){
+ if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
+ break;
+ }else if( (iPage = pCur->iPage)==0 ){
if( pPage->nOverflow ){
/* The root page of the b-tree is overfull. In this case call the
** balance_deeper() function to create a new child for the root-page
@@ -71585,8 +71737,6 @@ static int balance(BtCursor *pCur){
}else{
break;
}
- }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
- break;
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -71728,7 +71878,9 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
Pgno ovflPgno; /* Next overflow page to write */
u32 ovflPageSize; /* Size to write on overflow page */
- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
+ || pCur->info.pPayload < pPage->aData + pPage->cellOffset
+ ){
return SQLITE_CORRUPT_BKPT;
}
/* Overwrite the local portion first */
@@ -71969,6 +72121,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
memcpy(newCell, oldCell, 4);
}
rc = clearCell(pPage, oldCell, &info);
+ testcase( pCur->curFlags & BTCF_ValidOvfl );
+ invalidateOverflowCache(pCur);
if( info.nSize==szNew && info.nLocal==info.nPayload
&& (!ISAUTOVACUUM || szNew<pPage->minLocal)
){
@@ -71982,7 +72136,12 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** new entry uses overflow pages, as the insertCell() call below is
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
- if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( oldCell+szNew > pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
}
@@ -74319,8 +74478,10 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
}
if( p->isAttached ){
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
+ assert( pp!=0 );
while( *pp!=p ){
pp = &(*pp)->pNext;
+ assert( pp!=0 );
}
*pp = p->pNext;
}
@@ -74735,7 +74896,13 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
assert( pMem->szMalloc==0
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
- pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ if( pMem->db ){
+ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ }else{
+ pMem->zMalloc = sqlite3Realloc(pMem->z, n);
+ if( pMem->zMalloc==0 ) sqlite3_free(pMem->z);
+ pMem->z = pMem->zMalloc;
+ }
bPreserve = 0;
}else{
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
@@ -75806,7 +75973,7 @@ struct ValueNewStat4Ctx {
** an sqlite3_value within the UnpackedRecord.a[] array.
*/
static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( p ){
UnpackedRecord *pRec = p->ppRec[0];
@@ -75842,7 +76009,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
}
#else
UNUSED_PARAMETER(p);
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
return sqlite3ValueNew(db);
}
@@ -75866,7 +76033,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
** NULL and an SQLite error code returned.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static int valueFromFunction(
sqlite3 *db, /* The database connection */
Expr *p, /* The expression to evaluate */
@@ -75949,7 +76116,7 @@ static int valueFromFunction(
}
#else
# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
/*
** Extract a value from the supplied expression in the manner described
@@ -75978,7 +76145,7 @@ static int valueFromExpr(
assert( pExpr!=0 );
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
-#if defined(SQLITE_ENABLE_STAT3_OR_STAT4)
+#if defined(SQLITE_ENABLE_STAT4)
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
@@ -76071,7 +76238,7 @@ static int valueFromExpr(
0, SQLITE_DYNAMIC);
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( op==TK_FUNCTION && pCtx!=0 ){
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
}
@@ -76088,13 +76255,13 @@ static int valueFromExpr(
return rc;
no_mem:
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx==0 || pCtx->pParse->nErr==0 )
#endif
sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
assert( *ppVal==0 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx==0 ) sqlite3ValueFree(pVal);
#else
assert( pCtx==0 ); sqlite3ValueFree(pVal);
@@ -76122,56 +76289,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-/*
-** The implementation of the sqlite_record() function. This function accepts
-** a single argument of any type. The return value is a formatted database
-** record (a blob) containing the argument value.
-**
-** This is used to convert the value stored in the 'sample' column of the
-** sqlite_stat3 table to the record format SQLite uses internally.
-*/
-static void recordFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- const int file_format = 1;
- u32 iSerial; /* Serial type */
- int nSerial; /* Bytes of space for iSerial as varint */
- u32 nVal; /* Bytes of space required for argv[0] */
- int nRet;
- sqlite3 *db;
- u8 *aRet;
-
- UNUSED_PARAMETER( argc );
- iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
- nSerial = sqlite3VarintLen(iSerial);
- db = sqlite3_context_db_handle(context);
-
- nRet = 1 + nSerial + nVal;
- aRet = sqlite3DbMallocRawNN(db, nRet);
- if( aRet==0 ){
- sqlite3_result_error_nomem(context);
- }else{
- aRet[0] = nSerial+1;
- putVarint32(&aRet[1], iSerial);
- sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
- sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
- sqlite3DbFreeNN(db, aRet);
- }
-}
-
-/*
-** Register built-in functions used to help read ANALYZE data.
-*/
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
- static FuncDef aAnalyzeTableFuncs[] = {
- FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
- };
- sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs));
-}
-
+#ifdef SQLITE_ENABLE_STAT4
/*
** Attempt to extract a value from pExpr and use it to construct *ppVal.
**
@@ -77078,7 +77196,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int opcode = pOp->opcode;
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
|| opcode==OP_VDestroy
- || (opcode==OP_Function0 && pOp->p4.pFunc->funcFlags&SQLITE_FUNC_INTERNAL)
+ || (opcode==OP_ParseSchema && pOp->p4.z==0)
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
){
@@ -77415,16 +77533,16 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
** Change the value of the opcode, or P1, P2, P3, or P5 operands
** for a specific instruction.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, u32 addr, u8 iNewOpcode){
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){
sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p1 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p2 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p3 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
@@ -77931,14 +78049,16 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
case P4_KEYINFO: {
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
- sqlite3_str_appendf(&x, ",%s%s",
- pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
+ sqlite3_str_appendf(&x, ",%s%s%s",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "",
+ zColl);
}
sqlite3_str_append(&x, ")", 1);
break;
@@ -78345,8 +78465,11 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** pick up the appropriate opcode. */
int j;
i -= p->nOp;
+ assert( apSub!=0 );
+ assert( nSub>0 );
for(j=0; i>=apSub[j]->nOp; j++){
i -= apSub[j]->nOp;
+ assert( i<apSub[j]->nOp || j+1<nSub );
}
pOp = &apSub[j]->aOp[i];
}
@@ -79868,10 +79991,17 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
** of SQLite will not understand those serial types.
*/
+#if 0 /* Inlined into the OP_MakeRecord opcode */
/*
** Return the serial-type for the value stored in pMem.
**
** This routine might convert a large MEM_IntReal value into MEM_Real.
+**
+** 2019-07-11: The primary user of this subroutine was the OP_MakeRecord
+** opcode in the byte-code engine. But by moving this routine in-line, we
+** can omit some redundant tests and make that opcode a lot faster. So
+** this routine is now only used by the STAT3 logic and STAT3 support has
+** ended. The code is kept here for historical reference only.
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
@@ -79932,6 +80062,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
*pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
+#endif /* inlined into OP_MakeRecord */
/*
** The sizes for serial types less than 128
@@ -80240,7 +80371,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nKeyField + 1;
return p;
@@ -80339,7 +80470,7 @@ static int vdbeRecordCompareDebug(
if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
assert( pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -80370,7 +80501,12 @@ static int vdbeRecordCompareDebug(
pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
assert( mem1.szMalloc==0 ); /* See comment below */
- if( pKeyInfo->aSortOrder[i] ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null))
+ ){
+ rc = -rc;
+ }
+ if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){
rc = -rc; /* Invert the result for DESC sort order. */
}
goto debugCompareEnd;
@@ -80746,7 +80882,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|| CORRUPT_DB );
- assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -80869,8 +81005,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}
if( rc!=0 ){
- if( pPKey2->pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ int sortFlags = pPKey2->pKeyInfo->aSortFlags[i];
+ if( sortFlags ){
+ if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0
+ || ((sortFlags & KEYINFO_ORDER_DESC)
+ !=(serial_type==0 || (pRhs->flags&MEM_Null)))
+ ){
+ rc = -rc;
+ }
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
assert( mem1.szMalloc==0 ); /* See comment below */
@@ -81038,7 +81180,11 @@ static int vdbeRecordCompareString(
nCmp = MIN( pPKey2->aMem[0].n, nStr );
res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
- if( res==0 ){
+ if( res>0 ){
+ res = pPKey2->r2;
+ }else if( res<0 ){
+ res = pPKey2->r1;
+ }else{
res = nStr - pPKey2->aMem[0].n;
if( res==0 ){
if( pPKey2->nField>1 ){
@@ -81052,10 +81198,6 @@ static int vdbeRecordCompareString(
}else{
res = pPKey2->r1;
}
- }else if( res>0 ){
- res = pPKey2->r2;
- }else{
- res = pPKey2->r1;
}
}
@@ -81087,7 +81229,10 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
** header size is (12*5 + 1 + 1) bytes. */
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
- if( p->pKeyInfo->aSortOrder[0] ){
+ if( p->pKeyInfo->aSortFlags[0] ){
+ if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
+ return sqlite3VdbeRecordCompare;
+ }
p->r1 = 1;
p->r2 = -1;
}else{
@@ -81336,7 +81481,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
** features such as 'now'.
*/
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 1;
#endif
if( pCtx->pVdbe->aOp[pCtx->iOp].opcode==OP_PureFunc ){
@@ -81433,7 +81578,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nKeyField = pTab->nCol;
- preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
+ preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
@@ -82302,7 +82447,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
*/
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
int rc;
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime;
assert( p->pVdbe!=0 );
#else
@@ -82367,7 +82512,7 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#if SQLITE_ENABLE_STAT3_OR_STAT4
+#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
#else
assert( pCtx->pVdbe!=0 );
@@ -82401,7 +82546,7 @@ SQLITE_API void sqlite3_set_auxdata(
Vdbe *pVdbe = pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
#else
assert( pVdbe!=0 );
@@ -84053,6 +84198,7 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** Convert pRec to a text representation.
**
** SQLITE_AFF_BLOB:
+** SQLITE_AFF_NONE:
** No-op. pRec is unchanged.
*/
static void applyAffinity(
@@ -84192,13 +84338,15 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
c = 's';
}
*(zCsr++) = c;
+ *(zCsr++) = 'x';
sqlite3_snprintf(100, zCsr, "%d[", pMem->n);
zCsr += sqlite3Strlen30(zCsr);
- for(i=0; i<16 && i<pMem->n; i++){
+ for(i=0; i<25 && i<pMem->n; i++){
sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
zCsr += sqlite3Strlen30(zCsr);
}
- for(i=0; i<16 && i<pMem->n; i++){
+ *zCsr++ = '|';
+ for(i=0; i<25 && i<pMem->n; i++){
char z = pMem->z[i];
if( z<32 || z>126 ) *zCsr++ = '.';
else *zCsr++ = z;
@@ -84228,7 +84376,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
sqlite3_snprintf(100, &zBuf[k], "%d", pMem->n);
k += sqlite3Strlen30(&zBuf[k]);
zBuf[k++] = '[';
- for(j=0; j<15 && j<pMem->n; j++){
+ for(j=0; j<25 && j<pMem->n; j++){
u8 c = pMem->z[j];
if( c>=0x20 && c<0x7f ){
zBuf[k++] = c;
@@ -84261,7 +84409,7 @@ static void memTracePrint(Mem *p){
printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- printf(" r:%g", p->u.r);
+ printf(" r:%.17g", p->u.r);
#endif
}else if( sqlite3VdbeMemIsRowSet(p) ){
printf(" (rowset)");
@@ -84939,7 +85087,6 @@ case OP_Real: { /* same as TK_FLOAT, out2 */
case OP_String8: { /* same as TK_STRING, out2 */
assert( pOp->p4.z!=0 );
pOut = out2Prerelease(p, pOp);
- pOp->opcode = OP_String;
pOp->p1 = sqlite3Strlen30(pOp->p4.z);
#ifndef SQLITE_OMIT_UTF16
@@ -84963,6 +85110,7 @@ case OP_String8: { /* same as TK_STRING, out2 */
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
+ pOp->opcode = OP_String;
assert( rc==SQLITE_OK );
/* Fall through to the next case, OP_String */
}
@@ -85630,6 +85778,7 @@ case OP_RealAffinity: { /* in1 */
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pIn1);
+ REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@@ -86025,9 +86174,14 @@ case OP_Compare: {
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nKeyField );
pColl = pKeyInfo->aColl[i];
- bRev = pKeyInfo->aSortOrder[i];
+ bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC);
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
if( iCompare ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null))
+ ){
+ iCompare = -iCompare;
+ }
if( bRev ) iCompare = -iCompare;
break;
}
@@ -86318,11 +86472,6 @@ case OP_Offset: { /* out3 */
** if the P4 argument is a P4_MEM use the value of the P4 argument as
** the result.
**
-** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor,
-** then the cache of the cursor is reset prior to extracting the column.
-** The first OP_Column against a pseudo-table after the value of the content
-** register has changed should have this bit set.
-**
** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then
** the result is guaranteed to only be used as the argument of a length()
** or typeof() function, respectively. The loading of large blobs can be
@@ -86611,15 +86760,27 @@ case OP_Affinity: {
assert( pOp->p2>0 );
assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
- while( 1 /*edit-by-break*/ ){
+ while( 1 /*exit-by-break*/ ){
assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
applyAffinity(pIn1, zAffinity[0], encoding);
if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){
- /* When applying REAL affinity, if the result is still MEM_Int,
- ** indicate that REAL is actually desired */
- pIn1->flags |= MEM_IntReal;
- pIn1->flags &= ~MEM_Int;
+ /* When applying REAL affinity, if the result is still an MEM_Int
+ ** that will fit in 6 bytes, then change the type to MEM_IntReal
+ ** so that we keep the high-resolution integer value but know that
+ ** the type really wants to be REAL. */
+ testcase( pIn1->u.i==140737488355328LL );
+ testcase( pIn1->u.i==140737488355327LL );
+ testcase( pIn1->u.i==-140737488355328LL );
+ testcase( pIn1->u.i==-140737488355329LL );
+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL ){
+ pIn1->flags |= MEM_IntReal;
+ pIn1->flags &= ~MEM_Int;
+ }else{
+ pIn1->u.r = (double)pIn1->u.i;
+ pIn1->flags |= MEM_Real;
+ pIn1->flags &= ~MEM_Int;
+ }
}
REGISTER_TRACE((int)(pIn1-aMem), pIn1);
zAffinity++;
@@ -86726,14 +86887,36 @@ case OP_MakeRecord: {
#endif
/* Loop through the elements that will make up the record to figure
- ** out how much space is required for the new record.
+ ** out how much space is required for the new record. After this loop,
+ ** the Mem.uTemp field of each term should hold the serial-type that will
+ ** be used for that term in the generated record:
+ **
+ ** Mem.uTemp value type
+ ** --------------- ---------------
+ ** 0 NULL
+ ** 1 1-byte signed integer
+ ** 2 2-byte signed integer
+ ** 3 3-byte signed integer
+ ** 4 4-byte signed integer
+ ** 5 6-byte signed integer
+ ** 6 8-byte signed integer
+ ** 7 IEEE float
+ ** 8 Integer constant 0
+ ** 9 Integer constant 1
+ ** 10,11 reserved for expansion
+ ** N>=12 and even BLOB
+ ** N>=13 and odd text
+ **
+ ** The following additional values are computed:
+ ** nHdr Number of bytes needed for the record header
+ ** nData Number of bytes of data space needed for the record
+ ** nZero Zero bytes at the end of the record
*/
pRec = pLast;
do{
assert( memIsValid(pRec) );
- serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
- if( pRec->flags & MEM_Zero ){
- if( serial_type==0 ){
+ if( pRec->flags & MEM_Null ){
+ if( pRec->flags & MEM_Zero ){
/* Values with MEM_Null and MEM_Zero are created by xColumn virtual
** table methods that never invoke sqlite3_result_xxxxx() while
** computing an unchanging column value in an UPDATE statement.
@@ -86741,19 +86924,83 @@ case OP_MakeRecord: {
** so that they can be passed through to xUpdate and have
** a true sqlite3_value_nochange(). */
assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB );
- serial_type = 10;
- }else if( nData ){
- if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ pRec->uTemp = 10;
+ }else{
+ pRec->uTemp = 0;
+ }
+ nHdr++;
+ }else if( pRec->flags & (MEM_Int|MEM_IntReal) ){
+ /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
+ i64 i = pRec->u.i;
+ u64 uu;
+ testcase( pRec->flags & MEM_Int );
+ testcase( pRec->flags & MEM_IntReal );
+ if( i<0 ){
+ uu = ~i;
}else{
- nZero += pRec->u.nZero;
- len -= pRec->u.nZero;
+ uu = i;
+ }
+ nHdr++;
+ testcase( uu==127 ); testcase( uu==128 );
+ testcase( uu==32767 ); testcase( uu==32768 );
+ testcase( uu==8388607 ); testcase( uu==8388608 );
+ testcase( uu==2147483647 ); testcase( uu==2147483648 );
+ testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL );
+ if( uu<=127 ){
+ if( (i&1)==i && file_format>=4 ){
+ pRec->uTemp = 8+(u32)uu;
+ }else{
+ nData++;
+ pRec->uTemp = 1;
+ }
+ }else if( uu<=32767 ){
+ nData += 2;
+ pRec->uTemp = 2;
+ }else if( uu<=8388607 ){
+ nData += 3;
+ pRec->uTemp = 3;
+ }else if( uu<=2147483647 ){
+ nData += 4;
+ pRec->uTemp = 4;
+ }else if( uu<=140737488355327LL ){
+ nData += 6;
+ pRec->uTemp = 5;
+ }else{
+ nData += 8;
+ if( pRec->flags & MEM_IntReal ){
+ /* If the value is IntReal and is going to take up 8 bytes to store
+ ** as an integer, then we might as well make it an 8-byte floating
+ ** point value */
+ pRec->u.r = (double)pRec->u.i;
+ pRec->flags &= ~MEM_IntReal;
+ pRec->flags |= MEM_Real;
+ pRec->uTemp = 7;
+ }else{
+ pRec->uTemp = 6;
+ }
+ }
+ }else if( pRec->flags & MEM_Real ){
+ nHdr++;
+ nData += 8;
+ pRec->uTemp = 7;
+ }else{
+ assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
+ assert( pRec->n>=0 );
+ len = (u32)pRec->n;
+ serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
+ if( pRec->flags & MEM_Zero ){
+ serial_type += pRec->u.nZero*2;
+ if( nData ){
+ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ len += pRec->u.nZero;
+ }else{
+ nZero += pRec->u.nZero;
+ }
}
+ nData += len;
+ nHdr += sqlite3VarintLen(serial_type);
+ pRec->uTemp = serial_type;
}
- nData += len;
- testcase( serial_type==127 );
- testcase( serial_type==128 );
- nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
- pRec->uTemp = serial_type;
if( pRec==pData0 ) break;
pRec--;
}while(1);
@@ -87089,7 +87336,7 @@ case OP_AutoCommit: {
rc = SQLITE_ERROR;
goto abort_due_to_error;
}
- break;
+ /*NOTREACHED*/ assert(0);
}
/* Opcode: Transaction P1 P2 P3 P4 P5
@@ -87827,6 +88074,7 @@ case OP_SeekGT: { /* jump, in3, group */
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
if( pC->isTable ){
+ u16 flags3, newType;
/* The BTREE_SEEK_EQ flag is only set on index cursors */
assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0
|| CORRUPT_DB );
@@ -87835,18 +88083,21 @@ case OP_SeekGT: { /* jump, in3, group */
** blob, or NULL. But it needs to be an integer before we can do
** the seek, so convert it. */
pIn3 = &aMem[pOp->p3];
- if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
+ flags3 = pIn3->flags;
+ if( (flags3 & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3, 0);
}
- iKey = sqlite3VdbeIntValue(pIn3);
+ iKey = sqlite3VdbeIntValue(pIn3); /* Get the integer key value */
+ newType = pIn3->flags; /* Record the type after applying numeric affinity */
+ pIn3->flags = flags3; /* But convert the type back to its original */
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
- if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
- if( (pIn3->flags & MEM_Real)==0 ){
- if( (pIn3->flags & MEM_Null) || oc>=OP_SeekGE ){
- VdbeBranchTaken(1,2); goto jump_to_p2;
- break;
+ if( (newType & (MEM_Int|MEM_IntReal))==0 ){
+ if( (newType & MEM_Real)==0 ){
+ if( (newType & MEM_Null) || oc>=OP_SeekGE ){
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2;
}else{
rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
@@ -88231,23 +88482,27 @@ case OP_SeekRowid: { /* jump, in3 */
pIn3 = &aMem[pOp->p3];
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_IntReal );
+ testcase( pIn3->flags & MEM_Real );
+ testcase( (pIn3->flags & (MEM_Str|MEM_Int))==MEM_Str );
if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
- /* Make sure pIn3->u.i contains a valid integer representation of
- ** the key value, but do not change the datatype of the register, as
- ** other parts of the perpared statement might be depending on the
- ** current datatype. */
- u16 origFlags = pIn3->flags;
- int isNotInt;
- applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
- isNotInt = (pIn3->flags & MEM_Int)==0;
- pIn3->flags = origFlags;
- if( isNotInt ) goto jump_to_p2;
+ /* If pIn3->u.i does not contain an integer, compute iKey as the
+ ** integer value of pIn3. Jump to P2 if pIn3 cannot be converted
+ ** into an integer without loss of information. Take care to avoid
+ ** changing the datatype of pIn3, however, as it is used by other
+ ** parts of the prepared statement. */
+ Mem x = pIn3[0];
+ applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding);
+ if( (x.flags & MEM_Int)==0 ) goto jump_to_p2;
+ iKey = x.u.i;
+ goto notExistsWithKey;
}
/* Fall through into OP_NotExists */
case OP_NotExists: /* jump, in3 */
pIn3 = &aMem[pOp->p3];
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ iKey = pIn3->u.i;
+notExistsWithKey:
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
#ifdef SQLITE_DEBUG
@@ -88258,7 +88513,6 @@ case OP_NotExists: /* jump, in3 */
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
res = 0;
- iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
assert( rc==SQLITE_OK || res==0 );
pC->movetoTarget = iKey; /* Used by OP_Delete */
@@ -89140,11 +89394,12 @@ case OP_Next: /* jump */
** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
assert( pOp->opcode!=OP_Next
|| pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
- || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
- || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid);
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
+ || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid
+ || pC->seekOp==OP_IfNoHope);
assert( pOp->opcode!=OP_Prev
|| pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
- || pC->seekOp==OP_Last
+ || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope
|| pC->seekOp==OP_NullRow);
rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3);
@@ -89663,7 +89918,7 @@ case OP_ParseSchema: {
initData.pzErrMsg = &p->zErrMsg;
initData.mInitFlags = 0;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
+ "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid",
db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -91870,11 +92125,12 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){
sqlite3 *db;
if( p ){
+ sqlite3_stmt *pStmt = p->pStmt;
db = p->db;
sqlite3_mutex_enter(db->mutex);
- rc = sqlite3_finalize(p->pStmt);
sqlite3DbFree(db, p);
sqlite3_mutex_leave(db->mutex);
+ rc = sqlite3_finalize(pStmt);
}else{
rc = SQLITE_OK;
}
@@ -92854,7 +93110,8 @@ static int vdbeSorterCompareText(
);
}
}else{
- if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
+ if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
res = res * -1;
}
}
@@ -92922,7 +93179,8 @@ static int vdbeSorterCompareInt(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
- }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
res = res * -1;
}
@@ -93037,6 +93295,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
if( pKeyInfo->nAllField<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
+ && (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
}
@@ -93753,13 +94012,16 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list);
}else{
/* Launch a background thread for this operation */
- u8 *aMem = pTask->list.aMemory;
- void *pCtx = (void*)pTask;
+ u8 *aMem;
+ void *pCtx;
+ assert( pTask!=0 );
assert( pTask->pThread==0 && pTask->bDone==0 );
assert( pTask->list.pList==0 );
assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 );
+ aMem = pTask->list.aMemory;
+ pCtx = (void*)pTask;
pSorter->iPrev = (u8)(pTask - pSorter->aTask);
pTask->list = pSorter->list;
pSorter->list.pList = 0;
@@ -94883,14 +95145,9 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
-#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
if( (iAmt+iOfst)>p->endpoint.iOffset ){
return SQLITE_IOERR_SHORT_READ;
}
-#endif
-
- assert( (iAmt+iOfst)<=p->endpoint.iOffset );
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
@@ -95249,9 +95506,22 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
static int walkWindowList(Walker *pWalker, Window *pList){
Window *pWin;
for(pWin=pList; pWin; pWin=pWin->pNextWin){
- if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
- if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
- if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
+ int rc;
+ rc = sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExprList(pWalker, pWin->pPartition);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pFilter);
+ if( rc ) return WRC_Abort;
+
+ /* The next two are purely for calls to sqlite3RenameExprUnmap()
+ ** within sqlite3WindowOffsetExpr(). Because of constraints imposed
+ ** by sqlite3WindowOffsetExpr(), they can never fail. The results do
+ ** not matter anyhow. */
+ rc = sqlite3WalkExpr(pWalker, pWin->pStart);
+ if( NEVER(rc) ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
+ if( NEVER(rc) ) return WRC_Abort;
}
return WRC_Continue;
}
@@ -95287,18 +95557,22 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
assert( pExpr->x.pList==0 || pExpr->pRight==0 );
if( pExpr->pRight ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
pExpr = pExpr->pRight;
continue;
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else if( pExpr->x.pList ){
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ }else{
+ if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pExpr, EP_WinFunc) ){
- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
- }
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
+ }
#endif
+ }
}
break;
}
@@ -95340,8 +95614,9 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
{
Parse *pParse = pWalker->pParse;
if( pParse && IN_RENAME_OBJECT ){
+ /* The following may return WRC_Abort if there are unresolvable
+ ** symbols (e.g. a table that does not exist) in a window definition. */
int rc = walkWindowList(pWalker, p->pWinDefn);
- assert( rc==WRC_Continue );
return rc;
}
}
@@ -95513,6 +95788,13 @@ static void resolveAlias(
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
pExpr->flags |= EP_MemToken;
}
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( pExpr->y.pWin!=0 ){
+ pExpr->y.pWin->pOwner = pExpr;
+ }else{
+ assert( db->mallocFailed );
+ }
+ }
sqlite3DbFree(db, pDup);
}
ExprSetProperty(pExpr, EP_Alias);
@@ -95798,7 +96080,7 @@ static int lookupName(
{
#ifndef SQLITE_OMIT_TRIGGER
if( iCol<0 ){
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}else if( pExpr->iTable==0 ){
testcase( iCol==31 );
testcase( iCol==32 );
@@ -95830,7 +96112,7 @@ static int lookupName(
){
cnt = 1;
pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
/*
@@ -96106,7 +96388,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->y.pTab = pItem->pTab;
pExpr->iTable = pItem->iCursor;
pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
break;
}
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
@@ -96166,7 +96448,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
-
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
+#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
@@ -96238,6 +96522,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** SQL is being compiled using sqlite3NestedParse() */
no_such_func = 1;
pDef = 0;
+ }else
+ if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
+ && ExprHasProperty(pExpr, EP_Indirect)
+ && !IN_RENAME_OBJECT
+ ){
+ /* Functions tagged with SQLITE_DIRECTONLY may not be used
+ ** inside of triggers and views */
+ sqlite3ErrorMsg(pParse, "%s() prohibited in triggers and views",
+ pDef->zName);
}
}
@@ -96247,18 +96540,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
- if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( pDef && pDef->xValue==0 && pWin ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
- || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
- || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin)
+ || (is_agg && pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
- if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pWin ){
zType = "window";
}else{
zType = "aggregate";
@@ -96286,34 +96579,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
nId, zId);
pNC->nErr++;
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ErrorMsg(pParse,
+ "FILTER may not be used with non-aggregate %.*s()",
+ nId, zId
+ );
+ pNC->nErr++;
+ }
+#endif
if( is_agg ){
/* Window functions may not be arguments of aggregate functions.
** Or arguments of other window functions. But aggregate functions
** may be arguments for window functions. */
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0));
+ pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0));
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
}
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ is_agg = 1;
+ }
+#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pExpr->y.pWin ){
+ if( pWin ){
Select *pSel = pNC->pWinSelect;
+ assert( pWin==pExpr->y.pWin );
if( IN_RENAME_OBJECT==0 ){
- sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
- }
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
- sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
- if( 0==pSel->pWin
- || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
- ){
- pExpr->y.pWin->pNextWin = pSel->pWin;
- pSel->pWin = pExpr->y.pWin;
+ sqlite3WindowUpdate(pParse, pSel->pWinDefn, pWin, pDef);
}
+ sqlite3WalkExprList(pWalker, pWin->pPartition);
+ sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ sqlite3WalkExpr(pWalker, pWin->pFilter);
+ sqlite3WindowLink(pSel, pWin);
pNC->ncFlags |= NC_HasWin;
}else
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -96321,12 +96624,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
pExpr->op2 = 0;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
+ }
+#endif
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
pExpr->op2++;
pNC2 = pNC2->pNext;
}
- assert( pDef!=0 );
- if( pNC2 ){
+ assert( pDef!=0 || IN_RENAME_OBJECT );
+ if( pNC2 && pDef ){
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
@@ -96364,7 +96672,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
case TK_IS:
case TK_ISNOT: {
- Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
+ Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
assert( !ExprHasProperty(pExpr, EP_Reduced) );
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
** and "x IS NOT FALSE". */
@@ -96575,7 +96883,7 @@ static int resolveCompoundOrderBy(
int iCol = -1;
Expr *pE, *pDup;
if( pItem->done ) continue;
- pE = sqlite3ExprSkipCollate(pItem->pExpr);
+ pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
if( sqlite3ExprIsInteger(pE, &iCol) ){
if( iCol<=0 || iCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
@@ -96669,7 +96977,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
ExprList *pEList;
struct ExprList_item *pItem;
- if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
+ if( pOrderBy==0 || pParse->db->mallocFailed || IN_RENAME_OBJECT ) return 0;
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
return 1;
@@ -96691,17 +96999,13 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
#ifndef SQLITE_OMIT_WINDOWFUNC
/*
-** Walker callback for resolveRemoveWindows().
+** Walker callback for windowRemoveExprFromSelect().
*/
static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
+ UNUSED_PARAMETER(pWalker);
if( ExprHasProperty(pExpr, EP_WinFunc) ){
- Window **pp;
- for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
- if( *pp==pExpr->y.pWin ){
- *pp = (*pp)->pNextWin;
- break;
- }
- }
+ Window *pWin = pExpr->y.pWin;
+ sqlite3WindowUnlinkFromSelect(pWin);
}
return WRC_Continue;
}
@@ -96710,16 +97014,18 @@ static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
** Remove any Window objects owned by the expression pExpr from the
** Select.pWin list of Select object pSelect.
*/
-static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){
- Walker sWalker;
- memset(&sWalker, 0, sizeof(Walker));
- sWalker.xExprCallback = resolveRemoveWindowsCb;
- sWalker.u.pSelect = pSelect;
- sqlite3WalkExpr(&sWalker, pExpr);
+static void windowRemoveExprFromSelect(Select *pSelect, Expr *pExpr){
+ if( pSelect->pWin ){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.xExprCallback = resolveRemoveWindowsCb;
+ sWalker.u.pSelect = pSelect;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ }
}
#else
-# define resolveRemoveWindows(x,y)
-#endif
+# define windowRemoveExprFromSelect(a, b)
+#endif /* SQLITE_OMIT_WINDOWFUNC */
/*
** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
@@ -96756,7 +97062,7 @@ static int resolveOrderGroupBy(
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
if( zType[0]!='G' ){
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
if( iCol>0 ){
@@ -96790,7 +97096,7 @@ static int resolveOrderGroupBy(
/* Since this expresion is being changed into a reference
** to an identical expression in the result set, remove all Window
** objects belonging to the expression from the Select.pWin list. */
- resolveRemoveWindows(pSelect, pE);
+ windowRemoveExprFromSelect(pSelect, pE);
pItem->u.x.iOrderByCol = j+1;
}
}
@@ -97258,7 +97564,6 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
*/
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
int op;
- if( pExpr->flags & EP_Generic ) return 0;
while( ExprHasProperty(pExpr, EP_Skip) ){
assert( pExpr->op==TK_COLLATE );
pExpr = pExpr->pLeft;
@@ -97285,7 +97590,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
);
}
- return pExpr->affinity;
+ return pExpr->affExpr;
}
/*
@@ -97320,10 +97625,22 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con
}
/*
-** Skip over any TK_COLLATE operators and any unlikely()
-** or likelihood() function at the root of an expression.
+** Skip over any TK_COLLATE operators.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ assert( pExpr->op==TK_COLLATE );
+ pExpr = pExpr->pLeft;
+ }
+ return pExpr;
+}
+
+/*
+** Skip over any TK_COLLATE operators and/or any unlikely()
+** or likelihood() or likely() functions at the root of an
+** expression.
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){
if( ExprHasProperty(pExpr, EP_Unlikely) ){
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
@@ -97358,7 +97675,6 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
Expr *p = pExpr;
while( p ){
int op = p->op;
- if( p->flags & EP_Generic ) break;
if( op==TK_REGISTER ) op = p->op2;
if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER)
&& p->y.pTab!=0
@@ -97444,7 +97760,7 @@ SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
*/
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
char aff1 = sqlite3ExprAffinity(pExpr);
- if( aff1 && aff2 ){
+ if( aff1>SQLITE_AFF_NONE && aff2>SQLITE_AFF_NONE ){
/* Both sides of the comparison are columns. If one has numeric
** affinity, use that. Otherwise use no affinity.
*/
@@ -97453,15 +97769,10 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
}else{
return SQLITE_AFF_BLOB;
}
- }else if( !aff1 && !aff2 ){
- /* Neither side of the comparison is a column. Compare the
- ** results directly.
- */
- return SQLITE_AFF_BLOB;
}else{
/* One side is a column, the other is not. Use the columns affinity. */
- assert( aff1==0 || aff2==0 );
- return (aff1 + aff2);
+ assert( aff1<=SQLITE_AFF_NONE || aff2<=SQLITE_AFF_NONE );
+ return (aff1<=SQLITE_AFF_NONE ? aff2 : aff1) | SQLITE_AFF_NONE;
}
}
@@ -97494,14 +97805,13 @@ static char comparisonAffinity(Expr *pExpr){
*/
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
char aff = comparisonAffinity(pExpr);
- switch( aff ){
- case SQLITE_AFF_BLOB:
- return 1;
- case SQLITE_AFF_TEXT:
- return idx_affinity==SQLITE_AFF_TEXT;
- default:
- return sqlite3IsNumericAffinity(idx_affinity);
+ if( aff<SQLITE_AFF_TEXT ){
+ return 1;
+ }
+ if( aff==SQLITE_AFF_TEXT ){
+ return idx_affinity==SQLITE_AFF_TEXT;
}
+ return sqlite3IsNumericAffinity(idx_affinity);
}
/*
@@ -98115,7 +98425,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){
}else if( ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight) ){
sqlite3ExprUnmapAndDelete(pParse, pLeft);
sqlite3ExprUnmapAndDelete(pParse, pRight);
- return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
+ return sqlite3Expr(db, TK_INTEGER, "0");
}else{
return sqlite3PExpr(pParse, TK_AND, pLeft, pRight);
}
@@ -98254,15 +98564,18 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p->x.pList==0 || p->pRight==0 );
if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3ExprDeleteNN(db, p->pRight);
}else if( ExprHasProperty(p, EP_xIsSelect) ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
- }
- if( ExprHasProperty(p, EP_WinFunc) ){
- assert( p->op==TK_FUNCTION );
- sqlite3WindowDelete(db, p->y.pWin);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ sqlite3WindowDelete(db, p->y.pWin);
+ }
+#endif
}
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
@@ -98298,16 +98611,6 @@ static int exprStructSize(Expr *p){
}
/*
-** Copy the complete content of an Expr node, taking care not to read
-** past the end of the structure for a reduced-size version of the source
-** Expr.
-*/
-static void exprNodeCopy(Expr *pDest, Expr *pSrc){
- memset(pDest, 0, sizeof(Expr));
- memcpy(pDest, pSrc, exprStructSize(pSrc));
-}
-
-/*
** The dupedExpr*Size() routines each return the number of bytes required
** to store a copy of an expression or expression tree. They differ in
** how much of the tree is measured.
@@ -98546,10 +98849,13 @@ static With *withDup(sqlite3 *db, With *p){
** objects found there, assembling them onto the linked list at Select->pWin.
*/
static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){
- if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){
- assert( ExprHasProperty(pExpr, EP_WinFunc) );
- pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin;
- pWalker->u.pSelect->pWin = pExpr->y.pWin;
+ if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_WinFunc) ){
+ Select *pSelect = pWalker->u.pSelect;
+ Window *pWin = pExpr->y.pWin;
+ assert( pWin );
+ assert( IsWindowFunc(pExpr) );
+ assert( pWin->ppThis==0 );
+ sqlite3WindowLink(pSelect, pWin);
}
return WRC_Continue;
}
@@ -98623,8 +98929,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags)
}
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
- pItem->sortOrder = pOldItem->sortOrder;
+ pItem->sortFlags = pOldItem->sortFlags;
pItem->done = 0;
+ pItem->bNulls = pOldItem->bNulls;
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
@@ -98735,7 +99042,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
#ifndef SQLITE_OMIT_WINDOWFUNC
pNew->pWin = 0;
pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);
- if( p->pWin ) gatherSelectWindows(pNew);
+ if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew);
#endif
pNew->selId = p->selId;
*pp = pNew;
@@ -98844,6 +99151,10 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
for(i=0; i<pColumns->nId; i++){
Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
+ assert( pSubExpr!=0 || db->mallocFailed );
+ assert( pSubExpr==0 || pSubExpr->iTable==0 );
+ if( pSubExpr==0 ) continue;
+ pSubExpr->iTable = pColumns->nId;
pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
if( pList ){
assert( pList->nExpr==iFirst+i+1 );
@@ -98876,15 +99187,34 @@ vector_append_error:
/*
** Set the sort order for the last element on the given ExprList.
*/
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int eNulls){
+ struct ExprList_item *pItem;
if( p==0 ) return;
- assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
assert( p->nExpr>0 );
- if( iSortOrder<0 ){
- assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
- return;
+
+ assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC==0 && SQLITE_SO_DESC>0 );
+ assert( iSortOrder==SQLITE_SO_UNDEFINED
+ || iSortOrder==SQLITE_SO_ASC
+ || iSortOrder==SQLITE_SO_DESC
+ );
+ assert( eNulls==SQLITE_SO_UNDEFINED
+ || eNulls==SQLITE_SO_ASC
+ || eNulls==SQLITE_SO_DESC
+ );
+
+ pItem = &p->a[p->nExpr-1];
+ assert( pItem->bNulls==0 );
+ if( iSortOrder==SQLITE_SO_UNDEFINED ){
+ iSortOrder = SQLITE_SO_ASC;
+ }
+ pItem->sortFlags = (u8)iSortOrder;
+
+ if( eNulls!=SQLITE_SO_UNDEFINED ){
+ pItem->bNulls = 1;
+ if( iSortOrder!=eNulls ){
+ pItem->sortFlags |= KEYINFO_ORDER_BIGNULL;
+ }
}
- p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
}
/*
@@ -99383,27 +99713,30 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
*/
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){
u8 op;
+ int unaryMinus = 0;
if( aff==SQLITE_AFF_BLOB ) return 1;
- while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
+ while( p->op==TK_UPLUS || p->op==TK_UMINUS ){
+ if( p->op==TK_UMINUS ) unaryMinus = 1;
+ p = p->pLeft;
+ }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
switch( op ){
case TK_INTEGER: {
- return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_FLOAT: {
- return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_STRING: {
- return aff==SQLITE_AFF_TEXT;
+ return !unaryMinus && aff==SQLITE_AFF_TEXT;
}
case TK_BLOB: {
- return 1;
+ return !unaryMinus;
}
case TK_COLUMN: {
assert( p->iTable>=0 ); /* p cannot be part of a CHECK constraint */
- return p->iColumn<0
- && (aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC);
+ return aff>=SQLITE_AFF_NUMERIC && p->iColumn<0;
}
default: {
return 0;
@@ -99586,7 +99919,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
#ifndef SQLITE_OMIT_SUBQUERY
SQLITE_PRIVATE int sqlite3FindInIndex(
Parse *pParse, /* Parsing context */
- Expr *pX, /* The right-hand side (RHS) of the IN operator */
+ Expr *pX, /* The IN expression */
u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */
int *prRhsHasNull, /* Register holding NULL status. See notes */
int *aiMap, /* Mapping from Index fields to RHS fields */
@@ -100011,9 +100344,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
- int r1, r2, r3;
+ int r1, r2;
affinity = sqlite3ExprAffinity(pLeft);
- if( !affinity ){
+ if( affinity<=SQLITE_AFF_NONE ){
affinity = SQLITE_AFF_BLOB;
}
if( pKeyInfo ){
@@ -100039,9 +100372,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
}
/* Evaluate the expression and insert it into the temp table */
- r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
+ sqlite3ExprCode(pParse, pE2, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r1, 1);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -100054,6 +100387,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
/* Subroutine return */
sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
+ sqlite3ClearTempRegCache(pParse);
}
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -100067,7 +100401,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
**
** The pExpr parameter is the SELECT or EXISTS operator to be coded.
**
-** The register that holds the result. For a multi-column SELECT,
+** Return the register that holds the result. For a multi-column SELECT,
** the result is stored in a contiguous array of registers and the
** return value is the register of the left-most result column.
** Return 0 if an error occurs.
@@ -100145,11 +100479,21 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
VdbeComment((v, "Init EXISTS result"));
}
- pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
if( pSel->pLimit ){
- sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
+ /* The subquery already has a limit. If the pre-existing limit is X
+ ** then make the new limit X<>0 so that the new limit is either 1 or 0 */
+ sqlite3 *db = pParse->db;
+ pLimit = sqlite3Expr(db, TK_INTEGER, "0");
+ if( pLimit ){
+ pLimit->affExpr = SQLITE_AFF_NUMERIC;
+ pLimit = sqlite3PExpr(pParse, TK_NE,
+ sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
+ }
+ sqlite3ExprDelete(db, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
+ /* If there is no pre-existing limit add a limit of 1 */
+ pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
}
pSel->iLimit = 0;
@@ -100164,6 +100508,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
/* Subroutine return */
sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
+ sqlite3ClearTempRegCache(pParse);
}
return rReg;
@@ -100311,13 +100656,21 @@ static void sqlite3ExprCodeIN(
int r2, regToFree;
int regCkNull = 0;
int ii;
+ int bLhsReal; /* True if the LHS of the IN has REAL affinity */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
if( destIfNull!=destIfFalse ){
regCkNull = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
}
+ bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL;
for(ii=0; ii<pList->nExpr; ii++){
- r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ if( bLhsReal ){
+ r2 = regToFree = sqlite3GetTempReg(pParse);
+ sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2);
+ sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC);
+ }else{
+ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ }
if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
}
@@ -100602,7 +100955,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n
** the correct value for the expression.
*/
static void exprToRegister(Expr *pExpr, int iReg){
- Expr *p = sqlite3ExprSkipCollate(pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
p->op2 = p->op;
p->op = TK_REGISTER;
p->iTable = iReg;
@@ -100703,7 +101056,7 @@ expr_code_doover:
*/
int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
- if( aff!=SQLITE_AFF_BLOB ){
+ if( aff>SQLITE_AFF_BLOB ){
static const char zAff[] = "B\000C\000D\000E";
assert( SQLITE_AFF_BLOB=='A' );
assert( SQLITE_AFF_TEXT=='B' );
@@ -100719,7 +101072,19 @@ expr_code_doover:
if( iTab<0 ){
if( pParse->iSelfTab<0 ){
/* Generating CHECK constraints or inserting into partial index */
- return pExpr->iColumn - pParse->iSelfTab;
+ assert( pExpr->y.pTab!=0 );
+ assert( pExpr->iColumn>=XN_ROWID );
+ assert( pExpr->iColumn<pExpr->y.pTab->nCol );
+ if( pExpr->iColumn>=0
+ && pExpr->y.pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
+ ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, pExpr->iColumn - pParse->iSelfTab,
+ target);
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
+ return target;
+ }else{
+ return pExpr->iColumn - pParse->iSelfTab;
+ }
}else{
/* Coding an expression that is part of an index where column names
** in the index refer to the table to which the index belongs */
@@ -101006,7 +101371,7 @@ expr_code_doover:
assert( nFarg==1 );
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
sqlite3VdbeLoadString(v, target,
- aff ? azAff[aff-SQLITE_AFF_BLOB] : "none");
+ (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
return target;
}
#endif
@@ -101114,8 +101479,8 @@ expr_code_doover:
pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft);
}
assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT );
- if( pExpr->iTable
- && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft))
+ if( pExpr->iTable!=0
+ && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft))
){
sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
pExpr->iTable, n);
@@ -101218,10 +101583,23 @@ expr_code_doover:
break;
}
+ /* TK_IF_NULL_ROW Expr nodes are inserted ahead of expressions
+ ** that derive from the right-hand table of a LEFT JOIN. The
+ ** Expr.iTable value is the table number for the right-hand table.
+ ** The expression is only evaluated if that table is not currently
+ ** on a LEFT JOIN NULL row.
+ */
case TK_IF_NULL_ROW: {
int addrINR;
+ u8 okConstFactor = pParse->okConstFactor;
addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
+ /* Temporarily disable factoring of constant expressions, since
+ ** even though expressions may appear to be constant, they are not
+ ** really constant because they originate from the right-hand side
+ ** of a LEFT JOIN. */
+ pParse->okConstFactor = 0;
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ pParse->okConstFactor = okConstFactor;
sqlite3VdbeJumpHere(v, addrINR);
sqlite3VdbeChangeP3(v, addrINR, inReg);
break;
@@ -101258,6 +101636,8 @@ expr_code_doover:
Expr opCompare; /* The X==Ei expression */
Expr *pX; /* The X expression */
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
assert(pExpr->x.pList->nExpr > 0);
@@ -101266,13 +101646,17 @@ expr_code_doover:
nExpr = pEList->nExpr;
endLabel = sqlite3VdbeMakeLabel(pParse);
if( (pX = pExpr->pLeft)!=0 ){
- exprNodeCopy(&tempX, pX);
+ pDel = sqlite3ExprDup(db, pX, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDel);
+ break;
+ }
testcase( pX->op==TK_COLUMN );
- exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
testcase( regFree1==0 );
memset(&opCompare, 0, sizeof(opCompare));
opCompare.op = TK_EQ;
- opCompare.pLeft = &tempX;
+ opCompare.pLeft = pDel;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
@@ -101300,32 +101684,33 @@ expr_code_doover:
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
+ sqlite3ExprDelete(db, pDel);
sqlite3VdbeResolveLabel(v, endLabel);
break;
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
- assert( pExpr->affinity==OE_Rollback
- || pExpr->affinity==OE_Abort
- || pExpr->affinity==OE_Fail
- || pExpr->affinity==OE_Ignore
+ assert( pExpr->affExpr==OE_Rollback
+ || pExpr->affExpr==OE_Abort
+ || pExpr->affExpr==OE_Fail
+ || pExpr->affExpr==OE_Ignore
);
if( !pParse->pTriggerTab ){
sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program");
return 0;
}
- if( pExpr->affinity==OE_Abort ){
+ if( pExpr->affExpr==OE_Abort ){
sqlite3MayAbort(pParse);
}
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- if( pExpr->affinity==OE_Ignore ){
+ if( pExpr->affExpr==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
VdbeCoverage(v);
}else{
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
- pExpr->affinity, pExpr->u.zToken, 0, 0);
+ pExpr->affExpr, pExpr->u.zToken, 0, 0);
}
break;
@@ -101390,7 +101775,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeAtInit(
*/
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
int r2;
- pExpr = sqlite3ExprSkipCollate(pExpr);
+ pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
if( ConstFactorOk(pParse)
&& pExpr->op!=TK_REGISTER
&& sqlite3ExprIsConstantNotJoin(pExpr)
@@ -101581,40 +101966,44 @@ static void exprCodeBetween(
void (*xJump)(Parse*,Expr*,int,int), /* Action to take */
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
){
- Expr exprAnd; /* The AND operator in x>=y AND x<=z */
+ Expr exprAnd; /* The AND operator in x>=y AND x<=z */
Expr compLeft; /* The x>=y term */
Expr compRight; /* The x<=z term */
- Expr exprX; /* The x subexpression */
int regFree1 = 0; /* Temporary use register */
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
memset(&compLeft, 0, sizeof(Expr));
memset(&compRight, 0, sizeof(Expr));
memset(&exprAnd, 0, sizeof(Expr));
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- exprNodeCopy(&exprX, pExpr->pLeft);
- exprAnd.op = TK_AND;
- exprAnd.pLeft = &compLeft;
- exprAnd.pRight = &compRight;
- compLeft.op = TK_GE;
- compLeft.pLeft = &exprX;
- compLeft.pRight = pExpr->x.pList->a[0].pExpr;
- compRight.op = TK_LE;
- compRight.pLeft = &exprX;
- compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
- if( xJump ){
- xJump(pParse, &exprAnd, dest, jumpIfNull);
- }else{
- /* Mark the expression is being from the ON or USING clause of a join
- ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
- ** it into the Parse.pConstExpr list. We should use a new bit for this,
- ** for clarity, but we are out of bits in the Expr.flags field so we
- ** have to reuse the EP_FromJoin bit. Bummer. */
- exprX.flags |= EP_FromJoin;
- sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ pDel = sqlite3ExprDup(db, pExpr->pLeft, 0);
+ if( db->mallocFailed==0 ){
+ exprAnd.op = TK_AND;
+ exprAnd.pLeft = &compLeft;
+ exprAnd.pRight = &compRight;
+ compLeft.op = TK_GE;
+ compLeft.pLeft = pDel;
+ compLeft.pRight = pExpr->x.pList->a[0].pExpr;
+ compRight.op = TK_LE;
+ compRight.pLeft = pDel;
+ compRight.pRight = pExpr->x.pList->a[1].pExpr;
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
+ if( xJump ){
+ xJump(pParse, &exprAnd, dest, jumpIfNull);
+ }else{
+ /* Mark the expression is being from the ON or USING clause of a join
+ ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
+ ** it into the Parse.pConstExpr list. We should use a new bit for this,
+ ** for clarity, but we are out of bits in the Expr.flags field so we
+ ** have to reuse the EP_FromJoin bit. Bummer. */
+ pDel->flags |= EP_FromJoin;
+ sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ }
+ sqlite3ReleaseTempReg(pParse, regFree1);
}
- sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ExprDelete(db, pDel);
/* Ensure adequate test coverage */
testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 );
@@ -102053,20 +102442,17 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
return 2;
}
if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
- if( pA->op==TK_FUNCTION ){
+ if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
#ifndef SQLITE_OMIT_WINDOWFUNC
- /* Justification for the assert():
- ** window functions have p->op==TK_FUNCTION but aggregate functions
- ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
- ** function and a window function should have failed before reaching
- ** this point. And, it is not possible to have a window function and
- ** a scalar function with the same name and number of arguments. So
- ** if we reach this point, either A and B both window functions or
- ** neither are a window functions. */
- assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) );
+ assert( pA->op==pB->op );
+ if( ExprHasProperty(pA,EP_WinFunc)!=ExprHasProperty(pB,EP_WinFunc) ){
+ return 2;
+ }
if( ExprHasProperty(pA,EP_WinFunc) ){
- if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2;
+ if( sqlite3WindowCompare(pParse, pA->y.pWin, pB->y.pWin, 1)!=0 ){
+ return 2;
+ }
}
#endif
}else if( pA->op==TK_NULL ){
@@ -102090,7 +102476,8 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
){
if( pA->iColumn!=pB->iColumn ) return 2;
if( pA->op2!=pB->op2 ) return 2;
- if( pA->iTable!=pB->iTable
+ if( pA->op!=TK_IN
+ && pA->iTable!=pB->iTable
&& (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
}
}
@@ -102120,7 +102507,7 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
for(i=0; i<pA->nExpr; i++){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
- if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
+ if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1;
if( sqlite3ExprCompare(0, pExprA, pExprB, iTab) ) return 1;
}
return 0;
@@ -102132,42 +102519,47 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){
return sqlite3ExprCompare(0,
- sqlite3ExprSkipCollate(pA),
- sqlite3ExprSkipCollate(pB),
+ sqlite3ExprSkipCollateAndLikely(pA),
+ sqlite3ExprSkipCollateAndLikely(pB),
iTab);
}
/*
** Return non-zero if Expr p can only be true if pNN is not NULL.
+**
+** Or if seenNot is true, return non-zero if Expr p can only be
+** non-NULL if pNN is not NULL
*/
static int exprImpliesNotNull(
Parse *pParse, /* Parsing context */
Expr *p, /* The expression to be checked */
Expr *pNN, /* The expression that is NOT NULL */
int iTab, /* Table being evaluated */
- int seenNot /* True if p is an operand of NOT */
+ int seenNot /* Return true only if p can be any non-NULL value */
){
assert( p );
assert( pNN );
- if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ) return 1;
+ if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ){
+ return pNN->op!=TK_NULL;
+ }
switch( p->op ){
case TK_IN: {
if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0;
assert( ExprHasProperty(p,EP_xIsSelect)
|| (p->x.pList!=0 && p->x.pList->nExpr>0) );
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
case TK_BETWEEN: {
ExprList *pList = p->x.pList;
assert( pList!=0 );
assert( pList->nExpr==2 );
if( seenNot ) return 0;
- if( exprImpliesNotNull(pParse, pList->a[0].pExpr, pNN, iTab, seenNot)
- || exprImpliesNotNull(pParse, pList->a[1].pExpr, pNN, iTab, seenNot)
+ if( exprImpliesNotNull(pParse, pList->a[0].pExpr, pNN, iTab, 1)
+ || exprImpliesNotNull(pParse, pList->a[1].pExpr, pNN, iTab, 1)
){
return 1;
}
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
case TK_EQ:
case TK_NE:
@@ -102177,20 +102569,21 @@ static int exprImpliesNotNull(
case TK_GE:
case TK_PLUS:
case TK_MINUS:
- case TK_STAR:
- case TK_REM:
- case TK_BITAND:
case TK_BITOR:
- case TK_SLASH:
case TK_LSHIFT:
case TK_RSHIFT:
- case TK_CONCAT: {
+ case TK_CONCAT:
+ seenNot = 1;
+ /* Fall thru */
+ case TK_STAR:
+ case TK_REM:
+ case TK_BITAND:
+ case TK_SLASH: {
if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1;
/* Fall thru into the next case */
}
case TK_SPAN:
case TK_COLLATE:
- case TK_BITNOT:
case TK_UPLUS:
case TK_UMINUS: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
@@ -102198,8 +102591,9 @@ static int exprImpliesNotNull(
case TK_TRUTH: {
if( seenNot ) return 0;
if( p->op2!=TK_IS ) return 0;
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
+ case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
@@ -102265,7 +102659,6 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
switch( pExpr->op ){
case TK_ISNOT:
- case TK_NOT:
case TK_ISNULL:
case TK_NOTNULL:
case TK_IS:
@@ -102273,8 +102666,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
case TK_CASE:
case TK_IN:
case TK_FUNCTION:
+ case TK_TRUTH:
testcase( pExpr->op==TK_ISNOT );
- testcase( pExpr->op==TK_NOT );
testcase( pExpr->op==TK_ISNULL );
testcase( pExpr->op==TK_NOTNULL );
testcase( pExpr->op==TK_IS );
@@ -102282,6 +102675,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_CASE );
testcase( pExpr->op==TK_IN );
testcase( pExpr->op==TK_FUNCTION );
+ testcase( pExpr->op==TK_TRUTH );
return WRC_Prune;
case TK_COLUMN:
if( pWalker->u.iCur==pExpr->iTable ){
@@ -102290,6 +102684,18 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
}
return WRC_Prune;
+ case TK_AND:
+ if( sqlite3ExprImpliesNonNullRow(pExpr->pLeft, pWalker->u.iCur)
+ && sqlite3ExprImpliesNonNullRow(pExpr->pRight, pWalker->u.iCur)
+ ){
+ pWalker->eCode = 1;
+ }
+ return WRC_Prune;
+
+ case TK_BETWEEN:
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ return WRC_Prune;
+
/* Virtual tables are allowed to use constraints like x=NULL. So
** a term of the form x=y does not prove that y is not null if x
** is the column of a virtual table */
@@ -102310,6 +102716,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
){
return WRC_Prune;
}
+
default:
return WRC_Continue;
}
@@ -102339,7 +102746,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
*/
SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
Walker w;
- p = sqlite3ExprSkipCollate(p);
+ p = sqlite3ExprSkipCollateAndLikely(p);
while( p ){
if( p->op==TK_NOTNULL ){
p = p->pLeft;
@@ -102445,7 +102852,10 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){
}
if( i<nSrc ){
p->nThis++;
- }else{
+ }else if( nSrc==0 || pExpr->iTable<pSrc->a[0].iCursor ){
+ /* In a well-formed parse tree (no name resolution errors),
+ ** TK_COLUMN nodes with smaller Expr.iTable values are in an
+ ** outer context. Those are the only ones to count as "other" */
p->nOther++;
}
}
@@ -102462,8 +102872,9 @@ SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
Walker w;
struct SrcCount cnt;
assert( pExpr->op==TK_AGG_FUNCTION );
+ memset(&w, 0, sizeof(w));
w.xExprCallback = exprSrcCount;
- w.xSelectCallback = 0;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
w.u.pSrcCount = &cnt;
cnt.pSrc = pSrcList;
cnt.nThis = 0;
@@ -102732,6 +103143,11 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
/*
** Mark all temporary registers as being unavailable for reuse.
+**
+** Always invoke this procedure after coding a subroutine or co-routine
+** that might be invoked from other parts of the code, to ensure that
+** the sub/co-routine does not use registers in common with the code that
+** invokes the sub/co-routine.
*/
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nTempReg = 0;
@@ -102901,8 +103317,8 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_rename_table;
}
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
- exit_rename_table;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){
+ goto exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
@@ -103200,6 +103616,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
goto exit_begin_add_column;
}
+ sqlite3MayAbort(pParse);
assert( pTab->addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -104463,13 +104880,13 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
-** created and used by SQLite versions 3.7.9 and later and with
+** created and used by SQLite versions 3.7.9 through 3.29.0 when
** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
-** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
-** version of sqlite_stat3 and is only available when compiled with
-** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
-** not possible to enable both STAT3 and STAT4 at the same time. If they
-** are both enabled, then STAT4 takes precedence.
+** is a superset of sqlite_stat2 and is also now deprecated. The
+** sqlite_stat4 is an enhanced version of sqlite_stat3 and is only
+** available when compiled with SQLITE_ENABLE_STAT4 and in SQLite
+** versions 3.8.1 and later. STAT4 is the only variant that is still
+** supported.
**
** For most applications, sqlite_stat1 provides all the statistics required
** for the query planner to make good choices.
@@ -104580,17 +104997,11 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
#if defined(SQLITE_ENABLE_STAT4)
# define IsStat4 1
-# define IsStat3 0
-#elif defined(SQLITE_ENABLE_STAT3)
-# define IsStat4 0
-# define IsStat3 1
#else
# define IsStat4 0
-# define IsStat3 0
# undef SQLITE_STAT4_SAMPLES
# define SQLITE_STAT4_SAMPLES 1
#endif
-#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
/*
** This routine generates code that opens the sqlite_statN tables.
@@ -104619,14 +105030,10 @@ static void openStatTable(
{ "sqlite_stat1", "tbl,idx,stat" },
#if defined(SQLITE_ENABLE_STAT4)
{ "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat3", 0 },
-#elif defined(SQLITE_ENABLE_STAT3)
- { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat4", 0 },
#else
- { "sqlite_stat3", 0 },
{ "sqlite_stat4", 0 },
#endif
+ { "sqlite_stat3", 0 },
};
int i;
sqlite3 *db = pParse->db;
@@ -104707,7 +105114,7 @@ typedef struct Stat4Sample Stat4Sample;
struct Stat4Sample {
tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -104738,7 +105145,7 @@ struct Stat4Accum {
/* Reclaim memory used by a Stat4Sample
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleClear(sqlite3 *db, Stat4Sample *p){
assert( db!=0 );
if( p->nRowid ){
@@ -104750,7 +105157,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
/* Initialize the BLOB value of a ROWID
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
@@ -104766,7 +105173,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
/* Initialize the INTEGER value of a ROWID.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
@@ -104779,7 +105186,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
/*
** Copy the contents of object (*pFrom) into (*pTo).
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
pTo->isPSample = pFrom->isPSample;
pTo->iCol = pFrom->iCol;
@@ -104800,7 +105207,7 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
*/
static void stat4Destructor(void *pOld){
Stat4Accum *p = (Stat4Accum*)pOld;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int i;
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
@@ -104820,7 +105227,7 @@ static void stat4Destructor(void *pOld){
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
** total number of columns in the table.
**
-** Note 2: C is only used for STAT3 and STAT4.
+** Note 2: C is only used for STAT4.
**
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
@@ -104843,7 +105250,7 @@ static void statInit(
int nColUp; /* nCol rounded up for alignment */
int n; /* Bytes of space to allocate */
sqlite3 *db; /* Database connection */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int mxSample = SQLITE_STAT4_SAMPLES;
#endif
@@ -104860,7 +105267,7 @@ static void statInit(
n = sizeof(*p)
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
@@ -104880,7 +105287,7 @@ static void statInit(
p->current.anDLt = (tRowcnt*)&p[1];
p->current.anEq = &p->current.anDLt[nColUp];
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
{
u8 *pSpace; /* Allocated space not yet assigned */
int i; /* Used to iterate through p->aSample[] */
@@ -104915,7 +105322,7 @@ static void statInit(
sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
}
static const FuncDef statInitFuncdef = {
- 2+IsStat34, /* nArg */
+ 2+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -104955,7 +105362,7 @@ static int sampleIsBetterPost(
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return true if pNew is to be preferred over pOld.
**
@@ -104974,15 +105381,11 @@ static int sampleIsBetter(
assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
if( (nEqNew>nEqOld) ) return 1;
-#ifdef SQLITE_ENABLE_STAT4
if( nEqNew==nEqOld ){
if( pNew->iCol<pOld->iCol ) return 1;
return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
}
return 0;
-#else
- return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
-#endif
}
/*
@@ -104995,7 +105398,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
assert( IsStat4 || nEqZero==0 );
-#ifdef SQLITE_ENABLE_STAT4
/* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
** values in the anEq[] array of any sample in Stat4Accum.a[]. In
** other words, if nMaxEqZero is n, then it is guaranteed that there
@@ -105029,7 +105431,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
goto find_new_min;
}
}
-#endif
/* If necessary, remove sample iMin to make room for the new sample. */
if( p->nSample>=p->mxSample ){
@@ -105050,10 +105451,8 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* The "rows less-than" for the rowid column must be greater than that
** for the last sample in the p->a[] array. Otherwise, the samples would
** be out of order. */
-#ifdef SQLITE_ENABLE_STAT4
assert( p->nSample==0
|| pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
-#endif
/* Insert the new sample */
pSample = &p->a[p->nSample];
@@ -105063,9 +105462,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* Zero the first nEqZero entries in the anEq[] array. */
memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
-#ifdef SQLITE_ENABLE_STAT4
- find_new_min:
-#endif
+find_new_min:
if( p->nSample>=p->mxSample ){
int iMin = -1;
for(i=0; i<p->mxSample; i++){
@@ -105078,7 +105475,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
p->iMin = iMin;
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** Field iChng of the index being scanned has changed. So at this point
@@ -105119,28 +105516,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
}
#endif
-#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
- if( iChng==0 ){
- tRowcnt nLt = p->current.anLt[0];
- tRowcnt nEq = p->current.anEq[0];
-
- /* Check if this is to be a periodic sample. If so, add it. */
- if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
- p->current.isPSample = 1;
- sampleInsert(p, &p->current, 0);
- p->current.isPSample = 0;
- }else
-
- /* Or if it is a non-periodic sample. Add it in this case too. */
- if( p->nSample<p->mxSample
- || sampleIsBetter(p, &p->current, &p->a[p->iMin])
- ){
- sampleInsert(p, &p->current, 0);
- }
- }
-#endif
-
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
UNUSED_PARAMETER( p );
UNUSED_PARAMETER( iChng );
#endif
@@ -105160,7 +105536,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
** index being analyzed. The stat_get() SQL function will later be used to
** extract relevant information for constructing the sqlite_statN tables.
**
-** The R parameter is only used for STAT3 and STAT4
+** The R parameter is only used for STAT4
*/
static void statPush(
sqlite3_context *context,
@@ -105192,14 +105568,14 @@ static void statPush(
}
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
p->current.anLt[i] += p->current.anEq[i];
#endif
p->current.anEq[i] = 1;
}
}
p->nRow++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
}else{
@@ -105232,7 +105608,7 @@ static void statPush(
#endif
}
static const FuncDef statPushFuncdef = {
- 2+IsStat34, /* nArg */
+ 2+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -105263,7 +105639,7 @@ static const FuncDef statPushFuncdef = {
** parameter will always be a poiner to a Stat4Accum object, never a
** NULL.
**
-** If neither STAT3 nor STAT4 are enabled, then J is always
+** If STAT4 is not enabled, then J is always
** STAT_GET_STAT1 and is hence omitted and this routine becomes
** a one-parameter function, stat_get(P), that always returns the
** stat1 table entry information.
@@ -105274,8 +105650,8 @@ static void statGet(
sqlite3_value **argv
){
Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* STAT3 and STAT4 have a parameter on this routine. */
+#ifdef SQLITE_ENABLE_STAT4
+ /* STAT4 has a parameter on this routine. */
int eCall = sqlite3_value_int(argv[1]);
assert( argc==2 );
assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
@@ -105330,7 +105706,7 @@ static void statGet(
sqlite3_result_text(context, zRet, -1, sqlite3_free);
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
@@ -105359,9 +105735,7 @@ static void statGet(
}
}
- if( IsStat3 ){
- sqlite3_result_int64(context, (i64)aCnt[0]);
- }else{
+ {
char *zRet = sqlite3MallocZero(p->nCol * 25);
if( zRet==0 ){
sqlite3_result_error_nomem(context);
@@ -105378,13 +105752,13 @@ static void statGet(
}
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifndef SQLITE_DEBUG
UNUSED_PARAMETER( argc );
#endif
}
static const FuncDef statGetFuncdef = {
- 1+IsStat34, /* nArg */
+ 1+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -105397,7 +105771,7 @@ static const FuncDef statGetFuncdef = {
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
assert( regOut!=regStat4 && regOut!=regStat4+1 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
#elif SQLITE_DEBUG
assert( iParam==STAT_GET_STAT1 );
@@ -105406,7 +105780,7 @@ static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
#endif
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
(char*)&statGetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 1 + IsStat34);
+ sqlite3VdbeChangeP5(v, 1 + IsStat4);
}
/*
@@ -105433,7 +105807,7 @@ static void analyzeOneTable(
int regNewRowid = iMem++; /* Rowid for the inserted record */
int regStat4 = iMem++; /* Register to hold Stat4Accum object */
int regChng = iMem++; /* Index of changed index field */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
#endif
int regTemp = iMem++; /* Temporary use register */
@@ -105567,16 +105941,16 @@ static void analyzeOneTable(
** (3) the number of rows in the index,
**
**
- ** The third argument is only used for STAT3 and STAT4
+ ** The third argument is only used for STAT4
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
#endif
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
(char*)&statInitFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeChangeP5(v, 2+IsStat4);
/* Implementation of the following:
**
@@ -105647,12 +106021,12 @@ static void analyzeOneTable(
/*
** chng_addr_N:
- ** regRowid = idx(rowid) // STAT34 only
- ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** regRowid = idx(rowid) // STAT4 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
** Next csr
** if !eof(csr) goto next_row;
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
assert( regRowid==(regStat4+2) );
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
@@ -105673,7 +106047,7 @@ static void analyzeOneTable(
assert( regChng==(regStat4+1) );
sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
(char*)&statPushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeChangeP5(v, 2+IsStat4);
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
/* Add the entry to the stat1 table. */
@@ -105687,8 +106061,8 @@ static void analyzeOneTable(
#endif
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- /* Add the entries to the stat3 or stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* Add the entries to the stat4 table. */
+#ifdef SQLITE_ENABLE_STAT4
{
int regEq = regStat1;
int regLt = regStat1+1;
@@ -105711,21 +106085,17 @@ static void analyzeOneTable(
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
VdbeCoverage(v);
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
-#else
for(i=0; i<nCol; i++){
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
-#endif
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
sqlite3VdbeJumpHere(v, addrIsNull);
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
sqlite3VdbeJumpHere(v, addrRewind);
@@ -105900,7 +106270,7 @@ static void decodeIntArray(
int i;
tRowcnt v;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( z==0 ) z = "";
#else
assert( z!=0 );
@@ -105911,7 +106281,7 @@ static void decodeIntArray(
v = v*10 + c - '0';
z++;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( aOut ) aOut[i] = v;
if( aLog ) aLog[i] = sqlite3LogEst(v);
#else
@@ -105922,7 +106292,7 @@ static void decodeIntArray(
#endif
if( *z==' ' ) z++;
}
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
assert( pIndex!=0 ); {
#else
if( pIndex ){
@@ -105933,7 +106303,9 @@ static void decodeIntArray(
if( sqlite3_strglob("unordered*", z)==0 ){
pIndex->bUnordered = 1;
}else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
- pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
+ int sz = sqlite3Atoi(z+3);
+ if( sz<2 ) sz = 2;
+ pIndex->szIdxRow = sqlite3LogEst(sz);
}else if( sqlite3_strglob("noskipscan*", z)==0 ){
pIndex->noSkipScan = 1;
}
@@ -105987,7 +106359,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pIndex ){
tRowcnt *aiRowEst = 0;
int nCol = pIndex->nKeyCol+1;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/* Index.aiRowEst may already be set here if there are duplicate
** sqlite_stat1 entries for this index. In that case just clobber
** the old data with the new instead of allocating a new array. */
@@ -106023,7 +106395,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
@@ -106039,10 +106411,10 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Populate the pIdx->aAvgEq[] array based on the samples currently
** stored in pIdx->aSample[].
@@ -106120,12 +106492,11 @@ static Index *findIndexOrPrimaryKey(
}
/*
-** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** Load the content from either the sqlite_stat4
** into the relevant Index.aSample[] arrays.
**
** Arguments zSql1 and zSql2 must point to SQL statements that return
-** data equivalent to the following (statements are different for stat3,
-** see the caller of this function for details):
+** data equivalent to the following:
**
** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
@@ -106134,7 +106505,6 @@ static Index *findIndexOrPrimaryKey(
*/
static int loadStatTbl(
sqlite3 *db, /* Database handle */
- int bStat3, /* Assume single column records only */
const char *zSql1, /* SQL statement 1 (see above) */
const char *zSql2, /* SQL statement 2 (see above) */
const char *zDb /* Database name (e.g. "main") */
@@ -106168,17 +106538,13 @@ static int loadStatTbl(
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
- assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
- /* Index.nSample is non-zero at this point if data has already been
- ** loaded from the stat4 table. In this case ignore stat3 data. */
- if( pIdx==0 || pIdx->nSample ) continue;
- if( bStat3==0 ){
- assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
- if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
- nIdxCol = pIdx->nKeyCol;
- }else{
- nIdxCol = pIdx->nColumn;
- }
+ assert( pIdx==0 || pIdx->nSample==0 );
+ if( pIdx==0 ) continue;
+ assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nIdxCol = pIdx->nKeyCol;
+ }else{
+ nIdxCol = pIdx->nColumn;
}
pIdx->nSampleCol = nIdxCol;
nByte = sizeof(IndexSample) * nSample;
@@ -106220,9 +106586,8 @@ static int loadStatTbl(
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
/* This next condition is true if data has already been loaded from
- ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ ** the sqlite_stat4 table. */
nCol = pIdx->nSampleCol;
- if( bStat3 && nCol>1 ) continue;
if( pIdx!=pPrevIdx ){
initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
@@ -106255,7 +106620,7 @@ static int loadStatTbl(
}
/*
-** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** Load content from the sqlite_stat4 table into
** the Index.aSample[] arrays of all indices.
*/
static int loadStat4(sqlite3 *db, const char *zDb){
@@ -106263,37 +106628,28 @@ static int loadStat4(sqlite3 *db, const char *zDb){
assert( db->lookaside.bDisable );
if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
- rc = loadStatTbl(db, 0,
+ rc = loadStatTbl(db,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
zDb
);
}
-
- if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
- rc = loadStatTbl(db, 1,
- "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
- "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
- zDb
- );
- }
-
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
-** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
+** Load the content of the sqlite_stat1 and sqlite_stat4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat3/4 are used to populate the
+** arrays. The contents of sqlite_stat4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
-** during compilation and the sqlite_stat3/4 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT4 was defined
+** during compilation and the sqlite_stat4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** If SQLITE_ENABLE_STAT4 was defined during compilation and the
** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
@@ -106321,7 +106677,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
pIdx->hasStat1 = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
@@ -106349,7 +106705,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
/* Load the statistics from the sqlite_stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( rc==SQLITE_OK ){
db->lookaside.bDisable++;
rc = loadStat4(db, sInfo.zDatabase);
@@ -106674,6 +107030,7 @@ static void detachFunc(
sqlite3 *db = sqlite3_context_db_handle(context);
int i;
Db *pDb = 0;
+ HashElem *pEntry;
char zErr[128];
UNUSED_PARAMETER(NotUsed);
@@ -106698,6 +107055,18 @@ static void detachFunc(
goto detach_error;
}
+ /* If any TEMP triggers reference the schema being detached, move those
+ ** triggers to reference the TEMP schema itself. */
+ assert( db->aDb[1].pSchema );
+ pEntry = sqliteHashFirst(&db->aDb[1].pSchema->trigHash);
+ while( pEntry ){
+ Trigger *pTrig = (Trigger*)sqliteHashData(pEntry);
+ if( pTrig->pTabSchema==pDb->pSchema ){
+ pTrig->pTabSchema = pTrig->pSchema;
+ }
+ pEntry = sqliteHashNext(pEntry);
+ }
+
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
@@ -106935,6 +107304,7 @@ SQLITE_PRIVATE int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */
){
while( pExpr ){
+ ExprSetProperty(pExpr, EP_Indirect);
if( pExpr->op==TK_VARIABLE ){
if( pFix->pParse->db->init.busy ){
pExpr->op = TK_NULL;
@@ -107087,7 +107457,7 @@ SQLITE_API int sqlite3_set_authorizer(
sqlite3_mutex_enter(db->mutex);
db->xAuth = (sqlite3_xauth)xAuth;
db->pAuthArg = pArg;
- sqlite3ExpirePreparedStatements(db, 0);
+ if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1);
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -107741,7 +108111,7 @@ SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3 *db, Index *p){
sqlite3ExprListDelete(db, p->aColExpr);
sqlite3DbFree(db, p->zColAff);
if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3_free(p->aiRowEst);
#endif
sqlite3DbFree(db, p);
@@ -108114,13 +108484,40 @@ SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){
** trigger). All names are legal except those that begin with the string
** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
** is reserved for internal use.
+**
+** When parsing the sqlite_master table, this routine also checks to
+** make sure the "type", "name", and "tbl_name" columns are consistent
+** with the SQL.
*/
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
- if( !pParse->db->init.busy && pParse->nested==0
- && sqlite3WritableSchema(pParse->db)==0
- && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
- sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
- return SQLITE_ERROR;
+SQLITE_PRIVATE int sqlite3CheckObjectName(
+ Parse *pParse, /* Parsing context */
+ const char *zName, /* Name of the object to check */
+ const char *zType, /* Type of this object */
+ const char *zTblName /* Parent table name for triggers and indexes */
+){
+ sqlite3 *db = pParse->db;
+ if( sqlite3WritableSchema(db) || db->init.imposterTable ){
+ /* Skip these error checks for writable_schema=ON */
+ return SQLITE_OK;
+ }
+ if( db->init.busy ){
+ if( sqlite3_stricmp(zType, db->init.azInit[0])
+ || sqlite3_stricmp(zName, db->init.azInit[1])
+ || sqlite3_stricmp(zTblName, db->init.azInit[2])
+ ){
+ if( sqlite3Config.bExtraSchemaChecks ){
+ sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
+ return SQLITE_ERROR;
+ }
+ }
+ }else{
+ if( pParse->nested==0
+ && 0==sqlite3StrNICmp(zName, "sqlite_", 7)
+ ){
+ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s",
+ zName);
+ return SQLITE_ERROR;
+ }
}
return SQLITE_OK;
}
@@ -108201,7 +108598,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
}
pParse->sNameToken = *pName;
if( zName==0 ) return;
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( sqlite3CheckObjectName(pParse, zName, isView?"view":"table", zName) ){
goto begin_table_error;
}
if( db->init.iDb==1 ) isTemp = 1;
@@ -108701,7 +109098,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
- if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
+ if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
@@ -109116,6 +109513,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
Index *pIdx;
Index *pPk;
int nPk;
+ int nExtra;
int i, j;
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
@@ -109151,13 +109549,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
}
- pList->a[0].sortOrder = pParse->iPkSortOrder;
+ pList->a[0].sortFlags = pParse->iPkSortOrder;
assert( pParse->pNewTable==pTab );
pTab->iPKey = -1;
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
SQLITE_IDXTYPE_PRIMARYKEY);
if( db->mallocFailed || pParse->nErr ) return;
pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk->nKeyCol==1 );
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
@@ -109172,6 +109571,8 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
pPk->nColumn--;
}else{
testcase( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) );
+ pPk->azColl[j] = pPk->azColl[i];
+ pPk->aSortOrder[j] = pPk->aSortOrder[i];
pPk->aiColumn[j++] = pPk->aiColumn[i];
}
}
@@ -109180,7 +109581,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
assert( pPk!=0 );
pPk->isCovering = 1;
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
- nPk = pPk->nKeyCol;
+ nPk = pPk->nColumn = pPk->nKeyCol;
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
** table entry. This is only required if currently generating VDBE
@@ -109230,21 +109631,21 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
/* Add all table columns to the PRIMARY KEY index
*/
- if( nPk<pTab->nCol ){
- if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
- for(i=0, j=nPk; i<pTab->nCol; i++){
- if( !hasColumn(pPk->aiColumn, j, i) ){
- assert( j<pPk->nColumn );
- pPk->aiColumn[j] = i;
- pPk->azColl[j] = sqlite3StrBINARY;
- j++;
- }
+ nExtra = 0;
+ for(i=0; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, nPk, i) ) nExtra++;
+ }
+ if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
+ for(i=0, j=nPk; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, j, i) ){
+ assert( j<pPk->nColumn );
+ pPk->aiColumn[j] = i;
+ pPk->azColl[j] = sqlite3StrBINARY;
+ j++;
}
- assert( pPk->nColumn==j );
- assert( pTab->nCol==j );
- }else{
- pPk->nColumn = pTab->nCol;
}
+ assert( pPk->nColumn==j );
+ assert( pTab->nCol<=j );
recomputeColumnsNotIndexed(pPk);
}
@@ -109441,7 +109842,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
if( pParse->nErr ) return;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB);
if( pSelTab==0 ) return;
assert( p->aCol==0 );
p->nCol = pSelTab->nCol;
@@ -109705,10 +110106,10 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
db->xAuth = xAuth;
#else
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
#endif
pParse->nTab = n;
if( pTable->pCheck ){
@@ -109724,7 +110125,8 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
&& pParse->nErr==0
&& pTable->nCol==pSel->pEList->nExpr
){
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
+ SQLITE_AFF_NONE);
}
}else if( pSelTab ){
/* CREATE VIEW name AS... without an argument list. Construct
@@ -110069,7 +110471,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
}
#endif
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
- && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
+ && sqlite3StrNICmp(pTab->zName+7, "stat", 4)!=0
+ && sqlite3StrNICmp(pTab->zName+7, "parameters", 10)!=0 ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -110407,6 +110810,27 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
}
/*
+** If expression list pList contains an expression that was parsed with
+** an explicit "NULLS FIRST" or "NULLS LAST" clause, leave an error in
+** pParse and return non-zero. Otherwise, return zero.
+*/
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){
+ if( pList ){
+ int i;
+ for(i=0; i<pList->nExpr; i++){
+ if( pList->a[i].bNulls ){
+ u8 sf = pList->a[i].sortFlags;
+ sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s",
+ (sf==0 || sf==3) ? "FIRST" : "LAST"
+ );
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
** Create a new index for an SQL table. pName1.pName2 is the name of the index
** and pTblList is the name of the table that is to be indexed. Both will
** be NULL for a primary key or an index that is created to satisfy a
@@ -110457,6 +110881,9 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_create_index;
}
+ if( sqlite3HasExplicitNulls(pParse, pList) ){
+ goto exit_create_index;
+ }
/*
** Find the table that is to be indexed. Return early if not found.
@@ -110555,7 +110982,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) goto exit_create_index;
assert( pName->z!=0 );
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName,"index",pTab->zName) ){
goto exit_create_index;
}
if( !IN_RENAME_OBJECT ){
@@ -110621,7 +111048,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
assert( pList->nExpr==1 );
- sqlite3ExprListSetSortOrder(pList, sortOrder);
+ sqlite3ExprListSetSortOrder(pList, sortOrder, SQLITE_SO_UNDEFINED);
}else{
sqlite3ExprListCheckLength(pParse, pList, "index");
if( pParse->nErr ) goto exit_create_index;
@@ -110739,7 +111166,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
goto exit_create_index;
}
pIndex->azColl[i] = zColl;
- requestedSortOrder = pListItem->sortOrder & sortOrderMask;
+ requestedSortOrder = pListItem->sortFlags & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
}
@@ -110914,6 +111341,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
/* Gather the complete text of the CREATE INDEX statement into
** the zStmt variable
*/
+ assert( pName!=0 || pStart==0 );
if( pStart ){
int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
if( pName->z[n-1]==';' ) n--;
@@ -111956,7 +112384,8 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
const char *zColl = pIdx->azColl[i];
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
- pKey->aSortOrder[i] = pIdx->aSortOrder[i];
+ pKey->aSortFlags[i] = pIdx->aSortOrder[i];
+ assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
@@ -113712,6 +114141,8 @@ static void instrFunc(
int N = 1;
int isText;
unsigned char firstChar;
+ sqlite3_value *pC1 = 0;
+ sqlite3_value *pC2 = 0;
UNUSED_PARAMETER(argc);
typeHaystack = sqlite3_value_type(argv[0]);
@@ -113724,12 +114155,22 @@ static void instrFunc(
zHaystack = sqlite3_value_blob(argv[0]);
zNeedle = sqlite3_value_blob(argv[1]);
isText = 0;
- }else{
+ }else if( typeHaystack!=SQLITE_BLOB && typeNeedle!=SQLITE_BLOB ){
zHaystack = sqlite3_value_text(argv[0]);
zNeedle = sqlite3_value_text(argv[1]);
isText = 1;
+ }else{
+ pC1 = sqlite3_value_dup(argv[0]);
+ zHaystack = sqlite3_value_text(pC1);
+ if( zHaystack==0 ) goto endInstrOOM;
+ nHaystack = sqlite3_value_bytes(pC1);
+ pC2 = sqlite3_value_dup(argv[1]);
+ zNeedle = sqlite3_value_text(pC2);
+ if( zNeedle==0 ) goto endInstrOOM;
+ nNeedle = sqlite3_value_bytes(pC2);
+ isText = 1;
}
- if( zNeedle==0 || (nHaystack && zHaystack==0) ) return;
+ if( zNeedle==0 || (nHaystack && zHaystack==0) ) goto endInstrOOM;
firstChar = zNeedle[0];
while( nNeedle<=nHaystack
&& (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0)
@@ -113743,6 +114184,13 @@ static void instrFunc(
if( nNeedle>nHaystack ) N = 0;
}
sqlite3_result_int(context, N);
+endInstr:
+ sqlite3_value_free(pC1);
+ sqlite3_value_free(pC2);
+ return;
+endInstrOOM:
+ sqlite3_result_error_nomem(context);
+ goto endInstr;
}
/*
@@ -115495,9 +115943,6 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
-#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
- sqlite3AnalyzeFunctions();
-#endif
sqlite3RegisterDateTimeFunctions();
sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
@@ -116000,13 +116445,13 @@ static Expr *exprTableRegister(
if( iCol>=0 && iCol!=pTab->iPKey ){
pCol = &pTab->aCol[iCol];
pExpr->iTable = regBase + iCol + 1;
- pExpr->affinity = pCol->affinity;
+ pExpr->affExpr = pCol->affinity;
zColl = pCol->zColl;
if( zColl==0 ) zColl = db->pDfltColl->zName;
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
}else{
pExpr->iTable = regBase;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
}
return pExpr;
@@ -116809,7 +117254,7 @@ static Trigger *fkActionTrigger(
tFrom.n = nFrom;
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
- pRaise->affinity = OE_Abort;
+ pRaise->affExpr = OE_Abort;
}
pSelect = sqlite3SelectNew(pParse,
sqlite3ExprListAppend(pParse, 0, pRaise),
@@ -116854,6 +117299,7 @@ static Trigger *fkActionTrigger(
return 0;
}
assert( pStep!=0 );
+ assert( pTrigger!=0 );
switch( action ){
case OE_Restrict:
@@ -117044,18 +117490,19 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
}
for(n=0; n<pIdx->nColumn; n++){
i16 x = pIdx->aiColumn[n];
+ char aff;
if( x>=0 ){
- pIdx->zColAff[n] = pTab->aCol[x].affinity;
+ aff = pTab->aCol[x].affinity;
}else if( x==XN_ROWID ){
- pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
+ aff = SQLITE_AFF_INTEGER;
}else{
- char aff;
assert( x==XN_EXPR );
assert( pIdx->aColExpr!=0 );
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
- if( aff==0 ) aff = SQLITE_AFF_BLOB;
- pIdx->zColAff[n] = aff;
}
+ if( aff<SQLITE_AFF_BLOB ) aff = SQLITE_AFF_BLOB;
+ if( aff>SQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC;
+ pIdx->zColAff[n] = aff;
}
pIdx->zColAff[n] = 0;
}
@@ -117095,11 +117542,12 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
}
for(i=0; i<pTab->nCol; i++){
+ assert( pTab->aCol[i].affinity!=0 );
zColAff[i] = pTab->aCol[i].affinity;
}
do{
zColAff[i--] = 0;
- }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
+ }while( i>=0 && zColAff[i]<=SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
assert( zColAff!=0 );
@@ -117788,6 +118236,9 @@ SQLITE_PRIVATE void sqlite3Insert(
pTab->zName);
goto insert_cleanup;
}
+ if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
+ goto insert_cleanup;
+ }
pTabList->a[0].iCursor = iDataCur;
pUpsert->pUpsertSrc = pTabList;
pUpsert->regData = regData;
@@ -119901,6 +120352,8 @@ struct sqlite3_api_routines {
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
+ /* Version 3.30.0 and later */
+ int (*drop_modules)(sqlite3*,const char**);
};
/*
@@ -120193,6 +120646,8 @@ typedef int (*sqlite3_loadext_entry)(
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->isexplain
#define sqlite3_value_frombind sqlite3_api->frombind
+/* Version 3.30.0 and later */
+#define sqlite3_drop_modules sqlite3_api->drop_modules
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -120658,7 +121113,13 @@ static const sqlite3_api_routines sqlite3Apis = {
#endif
/* Version 3.28.0 and later */
sqlite3_stmt_isexplain,
- sqlite3_value_frombind
+ sqlite3_value_frombind,
+ /* Version 3.30.0 and later */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_drop_modules,
+#else
+ 0,
+#endif
};
/*
@@ -121371,7 +121832,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121495,7 +121956,7 @@ static const PragmaName aPragmaName[] = {
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_VIRTUALTABLE)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "module_list",
/* ePragTyp: */ PragTyp_MODULE_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121530,7 +121991,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_ParserTrace },
#endif
#endif
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "pragma_list",
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121728,7 +122189,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
-/* Number of pragmas: 62 on by default, 81 total. */
+/* Number of pragmas: 65 on by default, 81 total. */
/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/
@@ -122860,6 +123321,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
Index *pIdx;
Table *pTab;
pIdx = sqlite3FindIndex(db, zRight, zDb);
+ if( pIdx==0 ){
+ /* If there is no index named zRight, check to see if there is a
+ ** WITHOUT ROWID table named zRight, and if there is, show the
+ ** structure of the PRIMARY KEY index for that table. */
+ pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
+ if( pTab && !HasRowid(pTab) ){
+ pIdx = sqlite3PrimaryKeyIndex(pTab);
+ }
+ }
if( pIdx ){
int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
int i;
@@ -122939,7 +123409,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
break;
-#ifdef SQLITE_INTROSPECTION_PRAGMAS
+#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS
case PragTyp_FUNCTION_LIST: {
int i;
HashElem *j;
@@ -124274,9 +124744,11 @@ SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index *pIndex){
**
** Each callback contains the following information:
**
-** argv[0] = name of thing being created
-** argv[1] = root page number for table or index. 0 for trigger or view.
-** argv[2] = SQL text for the CREATE statement.
+** argv[0] = type of object: "table", "index", "trigger", or "view".
+** argv[1] = name of thing being created
+** argv[2] = associated table if an index or trigger
+** argv[3] = root page number for table or index. 0 for trigger or view.
+** argv[4] = SQL text for the CREATE statement.
**
*/
SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
@@ -124284,21 +124756,21 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
sqlite3 *db = pData->db;
int iDb = pData->iDb;
- assert( argc==3 );
+ assert( argc==5 );
UNUSED_PARAMETER2(NotUsed, argc);
assert( sqlite3_mutex_held(db->mutex) );
DbClearProperty(db, iDb, DB_Empty);
pData->nInitRow++;
if( db->mallocFailed ){
- corruptSchema(pData, argv[0], 0);
+ corruptSchema(pData, argv[1], 0);
return 1;
}
assert( iDb>=0 && iDb<db->nDb );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
- if( argv[1]==0 ){
- corruptSchema(pData, argv[0], 0);
- }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
+ if( argv[3]==0 ){
+ corruptSchema(pData, argv[1], 0);
+ }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
@@ -124311,9 +124783,10 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
assert( db->init.busy );
db->init.iDb = iDb;
- db->init.newTnum = sqlite3Atoi(argv[1]);
+ db->init.newTnum = sqlite3Atoi(argv[3]);
db->init.orphanTrigger = 0;
- TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
+ db->init.azInit = argv;
+ TESTONLY(rcp = ) sqlite3_prepare(db, argv[4], -1, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
@@ -124322,17 +124795,17 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
- pData->rc = rc;
+ if( rc > pData->rc ) pData->rc = rc;
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
- corruptSchema(pData, argv[0], sqlite3_errmsg(db));
+ corruptSchema(pData, argv[1], sqlite3_errmsg(db));
}
}
}
sqlite3_finalize(pStmt);
- }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
- corruptSchema(pData, argv[0], 0);
+ }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
+ corruptSchema(pData, argv[1], 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -124341,13 +124814,13 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** to do here is record the root page number for that index.
*/
Index *pIndex;
- pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
+ pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
if( pIndex==0
- || sqlite3GetInt32(argv[1],&pIndex->tnum)==0
+ || sqlite3GetInt32(argv[3],&pIndex->tnum)==0
|| pIndex->tnum<2
|| sqlite3IndexHasDuplicateRootPage(pIndex)
){
- corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
+ corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index");
}
}
return 0;
@@ -124368,7 +124841,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
int size;
#endif
Db *pDb;
- char const *azArg[4];
+ char const *azArg[6];
int meta[5];
InitData initData;
const char *zMasterName;
@@ -124387,18 +124860,20 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
** table name will be inserted automatically by the parser so we can just
** use the abbreviation "x" here. The parser will also automatically tag
** the schema table as read-only. */
- azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
- azArg[1] = "1";
- azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
+ azArg[0] = "table";
+ azArg[1] = zMasterName = SCHEMA_TABLE(iDb);
+ azArg[2] = azArg[1];
+ azArg[3] = "1";
+ azArg[4] = "CREATE TABLE x(type text,name text,tbl_name text,"
"rootpage int,sql text)";
- azArg[3] = 0;
+ azArg[5] = 0;
initData.db = db;
initData.iDb = iDb;
initData.rc = SQLITE_OK;
initData.pzErrMsg = pzErrMsg;
initData.mInitFlags = mFlags;
initData.nInitRow = 0;
- sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
+ sqlite3InitCallback(&initData, 5, (char **)azArg, 0);
if( initData.rc ){
rc = initData.rc;
goto error_out;
@@ -124524,7 +124999,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
{
char *zSql;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
+ "SELECT*FROM\"%w\".%s ORDER BY rowid",
db->aDb[iDb].zDbSName, zMasterName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
@@ -124845,7 +125320,10 @@ static int sqlite3Prepare(
rc = sParse.rc;
#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
+ /* Justification for the ALWAYS(): The only way for rc to be SQLITE_OK and
+ ** sParse.pVdbe to be NULL is if the input SQL is an empty string, but in
+ ** that case, sParse.explain will be false. */
+ if( sParse.explain && rc==SQLITE_OK && ALWAYS(sParse.pVdbe) ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"id", "parent", "notused", "detail"
@@ -124870,8 +125348,8 @@ static int sqlite3Prepare(
if( db->init.busy==0 ){
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
}
- if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(sParse.pVdbe);
+ if( rc!=SQLITE_OK || db->mallocFailed ){
+ if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt));
}else{
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
@@ -125242,6 +125720,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
sqlite3WindowListDelete(db, p->pWinDefn);
}
+ assert( p->pWin==0 );
#endif
if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
if( bFree ) sqlite3DbFreeNN(db, p);
@@ -125805,7 +126284,7 @@ static void pushOntoSorter(
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
- memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
+ memset(pKI->aSortFlags, 0, pKI->nKeyField); /* Makes OP_Jump testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
testcase( pKI->nAllField > pKI->nKeyField+2 );
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
@@ -126416,7 +126895,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
- p->aSortOrder = (u8*)&p->aColl[N+X];
+ p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
@@ -126493,7 +126972,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(
assert( sqlite3KeyInfoIsWriteable(pInfo) );
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
- pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
+ pInfo->aSortFlags[i-iStart] = pItem->sortFlags;
}
}
return pInfo;
@@ -126785,8 +127264,6 @@ static const char *columnTypeImpl(
assert( pExpr!=0 );
assert( pNC->pSrcList!=0 );
- assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
- ** are processed */
switch( pExpr->op ){
case TK_COLUMN: {
/* The expression is a column. Locate the table the column is being
@@ -127103,12 +127580,11 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
}else{
- Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
+ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
while( pColExpr->op==TK_DOT ){
pColExpr = pColExpr->pRight;
assert( pColExpr!=0 );
}
- assert( pColExpr->op!=TK_AGG_COLUMN );
if( pColExpr->op==TK_COLUMN ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
@@ -127176,7 +127652,8 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
Table *pTab, /* Add column type information to this table */
- Select *pSelect /* SELECT used to determine types and collations */
+ Select *pSelect, /* SELECT used to determine types and collations */
+ char aff /* Default affinity for columns */
){
sqlite3 *db = pParse->db;
NameContext sNC;
@@ -127209,7 +127686,7 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
- if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
+ if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl && pCol->zColl==0 ){
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
@@ -127222,7 +127699,7 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
** Given a SELECT statement, generate a Table structure that describes
** the result set of that SELECT.
*/
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){
Table *pTab;
sqlite3 *db = pParse->db;
u64 savedFlags;
@@ -127242,7 +127719,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -127396,7 +127873,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
}
assert( sqlite3KeyInfoIsWriteable(pRet) );
pRet->aColl[i] = pColl;
- pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags;
}
}
@@ -128107,11 +128584,14 @@ static int generateOutputSubroutine(
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
- ** of the scan loop.
+ ** of the scan loop. Note that the select might return multiple columns
+ ** if it is the RHS of a row-value IN operator.
*/
case SRT_Mem: {
- assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
+ if( pParse->nErr==0 ){
+ testcase( pIn->nSdst>1 );
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
+ }
/* The LIMIT clause will jump out of the loop for us */
break;
}
@@ -128368,7 +128848,7 @@ static int multiSelectOrderBy(
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
- pKeyDup->aSortOrder[i] = 0;
+ pKeyDup->aSortFlags[i] = 0;
}
}
}
@@ -128618,6 +129098,18 @@ static Expr *substExpr(
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
+
+ /* Ensure that the expression now has an implicit collation sequence,
+ ** just as it did when it was a column of a view or sub-query. */
+ if( pExpr ){
+ if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){
+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
+ pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
+ (pColl ? pColl->zName : "BINARY")
+ );
+ }
+ ExprClearProperty(pExpr, EP_Collate);
+ }
}
}
}else{
@@ -128631,6 +129123,14 @@ static Expr *substExpr(
}else{
substExprList(pSubst, pExpr->x.pList);
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window *pWin = pExpr->y.pWin;
+ pWin->pFilter = substExpr(pSubst, pWin->pFilter);
+ substExprList(pSubst, pWin->pPartition);
+ substExprList(pSubst, pWin->pOrderBy);
+ }
+#endif
}
return pExpr;
}
@@ -129091,6 +129591,7 @@ static int flattenSubquery(
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
+ assert( pSub!=0 );
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
@@ -129541,24 +130042,27 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
const char *zFunc; /* Name of aggregate function pFunc */
ExprList *pOrderBy;
- u8 sortOrder;
+ u8 sortFlags;
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
- if( pEList==0 || pEList->nExpr!=1 ) return eRet;
+ assert( !IsWindowFunc(pFunc) );
+ if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){
+ return eRet;
+ }
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
- sortOrder = SQLITE_SO_ASC;
+ sortFlags = KEYINFO_ORDER_BIGNULL;
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
eRet = WHERE_ORDERBY_MAX;
- sortOrder = SQLITE_SO_DESC;
+ sortFlags = KEYINFO_ORDER_DESC;
}else{
return eRet;
}
*ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
assert( pOrderBy!=0 || db->mallocFailed );
- if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
+ if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags;
return eRet;
}
@@ -129592,7 +130096,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
- if( pExpr->flags&EP_Distinct ) return 0;
+ if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0;
return pTab;
}
@@ -130039,6 +130543,10 @@ static int selectExpander(Walker *pWalker, Select *p){
u8 eCodeOrig = pWalker->eCode;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
+ if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){
+ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
+ pTab->zName);
+ }
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
nCol = pTab->nCol;
pTab->nCol = -1;
@@ -130332,7 +130840,8 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Select *pSel = pFrom->pSelect;
if( pSel ){
while( pSel->pPrior ) pSel = pSel->pPrior;
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
+ SQLITE_AFF_NONE);
}
}
}
@@ -130472,6 +130981,25 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
int regAgg;
ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
+ assert( !IsWindowFunc(pF->pExpr) );
+ if( ExprHasProperty(pF->pExpr, EP_WinFunc) ){
+ Expr *pFilter = pF->pExpr->y.pWin->pFilter;
+ if( pAggInfo->nAccumulator
+ && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ ){
+ if( regHit==0 ) regHit = ++pParse->nMem;
+ /* If this is the first row of the group (regAcc==0), clear the
+ ** "magnet" register regHit so that the accumulator registers
+ ** are populated if the FILTER clause jumps over the the
+ ** invocation of min() or max() altogether. Or, if this is not
+ ** the first row (regAcc==1), set the magnet register so that the
+ ** accumulators are not populated unless the min()/max() is invoked and
+ ** indicates that they should be. */
+ sqlite3VdbeAddOp2(v, OP_Copy, regAcc, regHit);
+ }
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
+ }
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
@@ -130481,7 +131009,9 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
regAgg = 0;
}
if( pF->iDistinct>=0 ){
- addrNext = sqlite3VdbeMakeLabel(pParse);
+ if( addrNext==0 ){
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ }
testcase( nArg==0 ); /* Error condition */
testcase( nArg>1 ); /* Also an error */
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
@@ -130517,6 +131047,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
}
+
pAggInfo->directMode = 0;
if( addrHitTest ){
sqlite3VdbeJumpHere(v, addrHitTest);
@@ -130562,7 +131093,7 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
Select *pS = pWalker->u.pSelect;
if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
sqlite3 *db = pWalker->pParse->db;
- Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
+ Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
if( pNew ){
Expr *pWhere = pS->pWhere;
SWAP(Expr, *pNew, *pExpr);
@@ -130984,7 +131515,7 @@ SQLITE_PRIVATE int sqlite3Select(
** assume the column name is non-NULL and segfault. The use of an empty
** string for the fake column name seems safer.
*/
- if( pItem->colUsed==0 ){
+ if( pItem->colUsed==0 && pItem->zName!=0 ){
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
}
@@ -130998,8 +131529,15 @@ SQLITE_PRIVATE int sqlite3Select(
** technically harmless for it to be generated multiple times. The
** following assert() will detect if something changes to cause
** the same subquery to be coded multiple times, as a signal to the
- ** developers to try to optimize the situation. */
- assert( pItem->addrFillSub==0 );
+ ** developers to try to optimize the situation.
+ **
+ ** Update 2019-07-24:
+ ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40.
+ ** The dbsqlfuzz fuzzer found a case where the same subquery gets
+ ** coded twice. So this assert() now becomes a testcase(). It should
+ ** be very rare, though.
+ */
+ testcase( pItem->addrFillSub!=0 );
/* Increment Parse.nHeight by the height of the largest expression
** tree referred to by this, the parent select. The child select
@@ -131073,7 +131611,7 @@ SQLITE_PRIVATE int sqlite3Select(
int retAddr;
struct SrcList_item *pPrior;
- assert( pItem->addrFillSub==0 );
+ testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
@@ -131313,23 +131851,35 @@ SQLITE_PRIVATE int sqlite3Select(
}
assert( 66==sqlite3LogEst(100) );
if( p->nSelectRow>66 ) p->nSelectRow = 66;
+
+ /* If there is both a GROUP BY and an ORDER BY clause and they are
+ ** identical, then it may be possible to disable the ORDER BY clause
+ ** on the grounds that the GROUP BY will cause elements to come out
+ ** in the correct order. It also may not - the GROUP BY might use a
+ ** database index that causes rows to be grouped together as required
+ ** but not actually sorted. Either way, record the fact that the
+ ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
+ ** variable. */
+ if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){
+ int ii;
+ /* The GROUP BY processing doesn't care whether rows are delivered in
+ ** ASC or DESC order - only that each group is returned contiguously.
+ ** So set the ASC/DESC flags in the GROUP BY to match those in the
+ ** ORDER BY to maximize the chances of rows being delivered in an
+ ** order that makes the ORDER BY redundant. */
+ for(ii=0; ii<pGroupBy->nExpr; ii++){
+ u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC;
+ pGroupBy->a[ii].sortFlags = sortFlags;
+ }
+ if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
+ orderByGrp = 1;
+ }
+ }
}else{
assert( 0==sqlite3LogEst(1) );
p->nSelectRow = 0;
}
- /* If there is both a GROUP BY and an ORDER BY clause and they are
- ** identical, then it may be possible to disable the ORDER BY clause
- ** on the grounds that the GROUP BY will cause elements to come out
- ** in the correct order. It also may not - the GROUP BY might use a
- ** database index that causes rows to be grouped together as required
- ** but not actually sorted. Either way, record the fact that the
- ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
- ** variable. */
- if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
- orderByGrp = 1;
- }
-
/* Create a label to jump to when we want to abort the query */
addrEnd = sqlite3VdbeMakeLabel(pParse);
@@ -131364,9 +131914,16 @@ SQLITE_PRIVATE int sqlite3Select(
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
for(i=0; i<sAggInfo.nFunc; i++){
- assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
+ Expr *pExpr = sAggInfo.aFunc[i].pExpr;
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
sNC.ncFlags |= NC_InAggFunc;
- sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
+ sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ assert( !IsWindowFunc(pExpr) );
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
+ }
+#endif
sNC.ncFlags &= ~NC_InAggFunc;
}
sAggInfo.mxReg = pParse->nMem;
@@ -131678,13 +132235,18 @@ SQLITE_PRIVATE int sqlite3Select(
{
int regAcc = 0; /* "populate accumulators" flag */
- /* If there are accumulator registers but no min() or max() functions,
- ** allocate register regAcc. Register regAcc will contain 0 the first
- ** time the inner loop runs, and 1 thereafter. The code generated
- ** by updateAccumulator() only updates the accumulator registers if
- ** regAcc contains 0. */
+ /* If there are accumulator registers but no min() or max() functions
+ ** without FILTER clauses, allocate register regAcc. Register regAcc
+ ** will contain 0 the first time the inner loop runs, and 1 thereafter.
+ ** The code generated by updateAccumulator() uses this to ensure
+ ** that the accumulator registers are (a) updated only once if
+ ** there are no min() or max functions or (b) always updated for the
+ ** first row visited by the aggregate, so that they are updated at
+ ** least once even if the FILTER clause means the min() or max()
+ ** function visits zero rows. */
if( sAggInfo.nAccumulator ){
for(i=0; i<sAggInfo.nFunc; i++){
+ if( ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_WinFunc) ) continue;
if( sAggInfo.aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ) break;
}
if( i==sAggInfo.nFunc ){
@@ -132155,7 +132717,11 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
zName = sqlite3NameFromToken(db, pName);
- if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( zName==0 ){
+ assert( db->mallocFailed );
+ goto trigger_cleanup;
+ }
+ if( sqlite3CheckObjectName(pParse, zName, "trigger", pTab->zName) ){
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@@ -132318,6 +132884,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( pLink!=0 );
pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
sqlite3OomFault(db);
@@ -132436,6 +133003,9 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->pIdList = pColumn;
pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
+ if( pUpsert ){
+ sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget);
+ }
}else{
testcase( pColumn );
sqlite3IdListDelete(db, pColumn);
@@ -132591,10 +133161,9 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
assert( iDb>=0 && iDb<db->nDb );
pTable = tableOfTrigger(pTrigger);
- assert( pTable );
- assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
+ assert( (pTable && pTable->pSchema==pTrigger->pSchema) || iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
- {
+ if( pTable ){
int code = SQLITE_DROP_TRIGGER;
const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
@@ -132608,7 +133177,6 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
/* Generate code to destroy the database record of the trigger.
*/
- assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
@@ -132632,9 +133200,11 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const ch
if( ALWAYS(pTrigger) ){
if( pTrigger->pSchema==pTrigger->pTabSchema ){
Table *pTab = tableOfTrigger(pTrigger);
- Trigger **pp;
- for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
- *pp = (*pp)->pNext;
+ if( pTab ){
+ Trigger **pp;
+ for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
+ *pp = (*pp)->pNext;
+ }
}
sqlite3DeleteTrigger(db, pTrigger);
db->mDbFlags |= DBFLAG_SchemaChange;
@@ -133882,28 +134452,30 @@ SQLITE_PRIVATE void sqlite3Update(
}
if( !isView ){
- int addr1 = 0; /* Address of jump instruction */
-
/* Do constraint checks. */
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
aXRef, 0);
- /* Do FK constraint checks. */
- if( hasFK ){
- sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
- }
-
- /* Delete the index entries associated with the current record. */
+ /* If REPLACE conflict handling may have been used, or if the PK of the
+ ** row is changing, then the GenerateConstraintChecks() above may have
+ ** moved cursor iDataCur. Reseek it. */
if( bReplace || chngKey ){
if( pPk ){
- addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
}else{
- addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
}
VdbeCoverageNeverTaken(v);
}
+
+ /* Do FK constraint checks. */
+ if( hasFK ){
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
+ }
+
+ /* Delete the index entries associated with the current record. */
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
/* If changing the rowid value, or if there are foreign key constraints
@@ -133933,9 +134505,6 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
#endif
- if( bReplace || chngKey ){
- sqlite3VdbeJumpHere(v, addr1);
- }
if( hasFK ){
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
@@ -134374,6 +134943,7 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
sqlite3 *db = pParse->db;
SrcList *pSrc; /* FROM clause for the UPDATE */
int iDataCur;
+ int i;
assert( v!=0 );
assert( pUpsert!=0 );
@@ -134390,7 +134960,6 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
int nPk = pPk->nKeyCol;
int iPk = pParse->nMem+1;
- int i;
pParse->nMem += nPk;
for(i=0; i<nPk; i++){
int k;
@@ -134411,6 +134980,12 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
/* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
** we have to make a copy before passing it down into sqlite3Update() */
pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
+ /* excluded.* columns of type REAL need to be converted to a hard real */
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i);
+ }
+ }
sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
@@ -134874,6 +135449,9 @@ struct VtabCtx {
** Construct and install a Module object for a virtual table. When this
** routine is called, it is guaranteed that all appropriate locks are held
** and the module is not already part of the connection.
+**
+** If there already exists a module with zName, replace it with the new one.
+** If pModule==0, then delete the module zName if it exists.
*/
SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
sqlite3 *db, /* Database in which module is registered */
@@ -134883,25 +135461,36 @@ SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
void (*xDestroy)(void *) /* Module destructor function */
){
Module *pMod;
- int nName = sqlite3Strlen30(zName);
- pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
- if( pMod==0 ){
- sqlite3OomFault(db);
+ Module *pDel;
+ char *zCopy;
+ if( pModule==0 ){
+ zCopy = (char*)zName;
+ pMod = 0;
}else{
- Module *pDel;
- char *zCopy = (char *)(&pMod[1]);
+ int nName = sqlite3Strlen30(zName);
+ pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
+ if( pMod==0 ){
+ sqlite3OomFault(db);
+ return 0;
+ }
+ zCopy = (char *)(&pMod[1]);
memcpy(zCopy, zName, nName+1);
pMod->zName = zCopy;
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
pMod->pEpoTab = 0;
- pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
- assert( pDel==0 || pDel==pMod );
- if( pDel ){
+ pMod->nRefModule = 1;
+ }
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
+ if( pDel ){
+ if( pDel==pMod ){
sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pMod = 0;
+ }else{
+ sqlite3VtabEponymousTableClear(db, pDel);
+ sqlite3VtabModuleUnref(db, pDel);
}
}
return pMod;
@@ -134922,11 +135511,7 @@ static int createModule(
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
- if( sqlite3HashFind(&db->aModule, zName) ){
- rc = SQLITE_MISUSE_BKPT;
- }else{
- (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
- }
+ (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
rc = sqlite3ApiExit(db, rc);
if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
sqlite3_mutex_leave(db->mutex);
@@ -134966,6 +135551,44 @@ SQLITE_API int sqlite3_create_module_v2(
}
/*
+** External API to drop all virtual-table modules, except those named
+** on the azNames list.
+*/
+SQLITE_API int sqlite3_drop_modules(sqlite3 *db, const char** azNames){
+ HashElem *pThis, *pNext;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ for(pThis=sqliteHashFirst(&db->aModule); pThis; pThis=pNext){
+ Module *pMod = (Module*)sqliteHashData(pThis);
+ pNext = sqliteHashNext(pThis);
+ if( azNames ){
+ int ii;
+ for(ii=0; azNames[ii]!=0 && strcmp(azNames[ii],pMod->zName)!=0; ii++){}
+ if( azNames[ii]!=0 ) continue;
+ }
+ createModule(db, pMod->zName, 0, 0, 0);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Decrement the reference count on a Module object. Destroy the
+** module when the reference count reaches zero.
+*/
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3 *db, Module *pMod){
+ assert( pMod->nRefModule>0 );
+ pMod->nRefModule--;
+ if( pMod->nRefModule==0 ){
+ if( pMod->xDestroy ){
+ pMod->xDestroy(pMod->pAux);
+ }
+ assert( pMod->pEpoTab==0 );
+ sqlite3DbFree(db, pMod);
+ }
+}
+
+/*
** Lock the virtual table so that it cannot be disconnected.
** Locks nest. Every lock should have a corresponding unlock.
** If an unlock is omitted, resources leaks will occur.
@@ -135004,6 +135627,7 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
pVTab->nRef--;
if( pVTab->nRef==0 ){
sqlite3_vtab *p = pVTab->pVtab;
+ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
if( p ){
p->pModule->xDisconnect(p);
}
@@ -135408,6 +136032,7 @@ static int vtabCallConstructor(
** the sqlite3_vtab object if successful. */
memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
+ pMod->nRefModule++;
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
@@ -136192,13 +136817,15 @@ struct WhereLevel {
int addrCont; /* Jump here to continue with the next loop cycle */
int addrFirst; /* First instruction of interior of the loop */
int addrBody; /* Beginning of the body of this loop */
+ int regBignull; /* big-null flag reg. True if a NULL-scan is needed */
+ int addrBignull; /* Jump here for next part of big-null scan */
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
int addrLikeRep; /* LIKE range processing address */
#endif
u8 iFrom; /* Which entry in the FROM clause */
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
+ int p1, p2; /* Operands of the opcode used to end the loop */
union { /* Information that depends on pWLoop->wsFlags */
struct {
int nIn; /* Number of entries in aInLoop[] */
@@ -136249,7 +136876,7 @@ struct WhereLoop {
u16 nEq; /* Number of equality constraints */
u16 nBtm; /* Size of BTM vector */
u16 nTop; /* Size of TOP vector */
- u16 nIdxCol; /* Index column used for ORDER BY */
+ u16 nDistinctCol; /* Index columns used to sort for DISTINCT */
Index *pIndex; /* Index used, or NULL */
} btree;
struct { /* Information for virtual tables */
@@ -136400,16 +137027,17 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
+# define TERM_VNULL 0x00 /* Disabled if not using stat4 */
#endif
#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */
#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */
#define TERM_LIKE 0x400 /* The original LIKE operator */
#define TERM_IS 0x800 /* Term.pExpr is an IS operator */
#define TERM_VARSELECT 0x1000 /* Term.pExpr contains a correlated sub-query */
+#define TERM_NOPARTIDX 0x2000 /* Not for use to enable a partial index */
/*
** An instance of the WhereScan object is used as an iterator for locating
@@ -136520,7 +137148,7 @@ struct WhereLoopBuilder {
ExprList *pOrderBy; /* ORDER BY clause */
WhereLoop *pNew; /* Template WhereLoop */
WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
UnpackedRecord *pRec; /* Probe for stat4 (if required) */
int nRecValid; /* Number of valid fields currently in pRec */
#endif
@@ -136707,6 +137335,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
+#define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */
#endif /* !defined(SQLITE_WHEREINT_H) */
@@ -137011,9 +137640,9 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
** Code an OP_Affinity opcode to apply the column affinity string zAff
** to the n registers starting at base.
**
-** As an optimization, SQLITE_AFF_BLOB entries (which are no-ops) at the
-** beginning and end of zAff are ignored. If all entries in zAff are
-** SQLITE_AFF_BLOB, then no code gets generated.
+** As an optimization, SQLITE_AFF_BLOB and SQLITE_AFF_NONE entries (which
+** are no-ops) at the beginning and end of zAff are ignored. If all entries
+** in zAff are SQLITE_AFF_BLOB or SQLITE_AFF_NONE, then no code gets generated.
**
** This routine makes its own copy of zAff so that the caller is free
** to modify zAff after this routine returns.
@@ -137026,15 +137655,16 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
assert( v!=0 );
- /* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
- ** and end of the affinity string.
+ /* Adjust base and n to skip over SQLITE_AFF_BLOB and SQLITE_AFF_NONE
+ ** entries at the beginning and end of the affinity string.
*/
- while( n>0 && zAff[0]==SQLITE_AFF_BLOB ){
+ assert( SQLITE_AFF_NONE<SQLITE_AFF_BLOB );
+ while( n>0 && zAff[0]<=SQLITE_AFF_BLOB ){
n--;
base++;
zAff++;
}
- while( n>1 && zAff[n-1]==SQLITE_AFF_BLOB ){
+ while( n>1 && zAff[n-1]<=SQLITE_AFF_BLOB ){
n--;
}
@@ -137809,6 +138439,7 @@ typedef struct IdxExprTrans {
static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
IdxExprTrans *pX = p->u.pIdxTrans;
if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){
+ pExpr->affExpr = sqlite3ExprAffinity(pExpr);
pExpr->op = TK_COLUMN;
pExpr->iTable = pX->iIdxCur;
pExpr->iColumn = pX->iIdxCol;
@@ -138241,32 +138872,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
int omitTable; /* True if we use the index only */
-
+ int regBignull = 0; /* big-null flag register */
pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
assert( nEq>=pLoop->nSkip );
- /* If this loop satisfies a sort order (pOrderBy) request that
- ** was passed to this function to implement a "SELECT min(x) ..."
- ** query, then the caller will only allow the loop to run for
- ** a single iteration. This means that the first row returned
- ** should not have a NULL value stored in 'x'. If column 'x' is
- ** the first one after the nEq equality constraints in the index,
- ** this requires some special handling.
- */
- assert( pWInfo->pOrderBy==0
- || pWInfo->pOrderBy->nExpr==1
- || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
- if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && pWInfo->nOBSat>0
- && (pIdx->nKeyCol>nEq)
- ){
- assert( pLoop->nSkip==0 );
- bSeekPastNull = 1;
- nExtraReg = 1;
- }
-
/* Find any inequality constraint terms for the start and end
** of the range.
*/
@@ -138307,6 +138918,25 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
+ /* If the WHERE_BIGNULL_SORT flag is set, then index column nEq uses
+ ** a non-default "big-null" sort (either ASC NULLS LAST or DESC NULLS
+ ** FIRST). In both cases separate ordered scans are made of those
+ ** index entries for which the column is null and for those for which
+ ** it is not. For an ASC sort, the non-NULL entries are scanned first.
+ ** For DESC, NULL entries are scanned first.
+ */
+ if( (pLoop->wsFlags & (WHERE_TOP_LIMIT|WHERE_BTM_LIMIT))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)!=0
+ ){
+ assert( bSeekPastNull==0 && nExtraReg==0 && nBtm==0 && nTop==0 );
+ assert( pRangeEnd==0 && pRangeStart==0 );
+ assert( pLoop->nSkip==0 );
+ nExtraReg = 1;
+ bSeekPastNull = 1;
+ pLevel->regBignull = regBignull = ++pParse->nMem;
+ pLevel->addrBignull = sqlite3VdbeMakeLabel(pParse);
+ }
+
/* If we are doing a reverse order scan on an ascending index, or
** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd).
@@ -138329,7 +138959,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( zStartAff && nTop ){
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
}
- addrNxt = pLevel->addrNxt;
+ addrNxt = (regBignull ? pLevel->addrBignull : pLevel->addrNxt);
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
@@ -138363,10 +138993,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
bSeekPastNull = 0;
}else if( bSeekPastNull ){
+ startEq = 0;
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ start_constraints = 1;
nConstraint++;
- startEq = 0;
+ }else if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
start_constraints = 1;
+ nConstraint++;
}
codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
@@ -138377,6 +139011,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
}
+ if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull);
+ VdbeComment((v, "NULL-scan pass ctr"));
+ }
+
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 );
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
@@ -138387,6 +139026,23 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
+
+ assert( bSeekPastNull==0 || bStopAtNull==0 );
+ if( regBignull ){
+ assert( bSeekPastNull==1 || bStopAtNull==1 );
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bStopAtNull==startEq );
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
+ op = aStartOp[(nConstraint>1)*4 + 2 + bRev];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint-startEq);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ assert( op==OP_Rewind || op==OP_Last || op==OP_SeekGE || op==OP_SeekLE);
+ }
}
/* Load the value for the inequality constraint at the end of the
@@ -138418,8 +139074,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
endEq = 1;
}
}else if( bStopAtNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- endEq = 0;
+ if( regBignull==0 ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ }
nConstraint++;
}
sqlite3DbFree(db, zStartAff);
@@ -138430,6 +139088,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
/* Check if the index cursor is past the end of the range. */
if( nConstraint ){
+ if( regBignull ){
+ /* Except, skip the end-of-range check while doing the NULL-scan */
+ sqlite3VdbeAddOp2(v, OP_IfNot, regBignull, sqlite3VdbeCurrentAddr(v)+3);
+ VdbeComment((v, "If NULL-scan 2nd pass"));
+ VdbeCoverage(v);
+ }
op = aEndOp[bRev*2 + endEq];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
@@ -138437,6 +139101,23 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
+ if( regBignull ){
+ /* During a NULL-scan, check to see if we have reached the end of
+ ** the NULLs */
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bSeekPastNull+bStopAtNull==1 );
+ assert( nConstraint+bSeekPastNull>0 );
+ sqlite3VdbeAddOp2(v, OP_If, regBignull, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeComment((v, "If NULL-scan 1st pass"));
+ VdbeCoverage(v);
+ op = aEndOp[bRev*2 + bSeekPastNull];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint+bSeekPastNull);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
+ }
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);
@@ -139063,7 +139744,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
}else{
pTerm->truthProb = 1;
}
- pTerm->pExpr = sqlite3ExprSkipCollate(p);
+ pTerm->pExpr = sqlite3ExprSkipCollateAndLikely(p);
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
pTerm->iParent = -1;
@@ -139096,10 +139777,16 @@ static int allowedOp(int op){
** the left hand side of a comparison overrides any collation sequence
** attached to the right. For the same reason the EP_Collate flag
** is not commuted.
+**
+** The return value is extra flags that are added to the WhereTerm object
+** after it is commuted. The only extra flag ever added is TERM_NOPARTIDX
+** which prevents the term from being used to enable a partial index if
+** COLLATE changes have been made.
*/
-static void exprCommute(Parse *pParse, Expr *pExpr){
+static u16 exprCommute(Parse *pParse, Expr *pExpr){
u16 expRight = (pExpr->pRight->flags & EP_Collate);
u16 expLeft = (pExpr->pLeft->flags & EP_Collate);
+ u16 wtFlags = 0;
assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN );
if( expRight==expLeft ){
/* Either X and Y both have COLLATE operator or neither do */
@@ -139107,11 +139794,13 @@ static void exprCommute(Parse *pParse, Expr *pExpr){
/* Both X and Y have COLLATE operators. Make sure X is always
** used by clearing the EP_Collate flag from Y. */
pExpr->pRight->flags &= ~EP_Collate;
+ wtFlags |= TERM_NOPARTIDX;
}else if( sqlite3ExprCollSeq(pParse, pExpr->pLeft)!=0 ){
/* Neither X nor Y have COLLATE operators, but X has a non-default
** collating sequence. So add the EP_Collate marker on X to cause
** it to be searched first. */
pExpr->pLeft->flags |= EP_Collate;
+ wtFlags |= TERM_NOPARTIDX;
}
}
SWAP(Expr*,pExpr->pRight,pExpr->pLeft);
@@ -139123,6 +139812,7 @@ static void exprCommute(Parse *pParse, Expr *pExpr){
assert( pExpr->op>=TK_GT && pExpr->op<=TK_GE );
pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT;
}
+ return wtFlags;
}
/*
@@ -139254,6 +139944,7 @@ static int isLikeOrGlob(
** 2019-05-02 https://sqlite.org/src/info/b043a54c3de54b28
** 2019-06-10 https://sqlite.org/src/info/fd76310a5e843e07
** 2019-06-14 https://sqlite.org/src/info/ce8717f0885af975
+ ** 2019-09-03 https://sqlite.org/src/info/0f0428096f17252a
*/
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
@@ -139263,9 +139954,13 @@ static int isLikeOrGlob(
double rDummy;
isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
if( isNum<=0 ){
- zNew[iTo-1]++;
- isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
- zNew[iTo-1]--;
+ if( iTo==1 && zNew[0]=='-' ){
+ isNum = +1;
+ }else{
+ zNew[iTo-1]++;
+ isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+ zNew[iTo-1]--;
+ }
}
if( isNum>0 ){
sqlite3ExprDelete(db, pPrefix);
@@ -140119,7 +140814,7 @@ static void exprAnalyze(
pDup = pExpr;
pNew = pTerm;
}
- exprCommute(pParse, pDup);
+ pNew->wtFlags |= exprCommute(pParse, pDup);
pNew->leftCursor = aiCurCol[0];
pNew->u.leftColumn = aiCurCol[1];
testcase( (prereqLeft | extraRight) != prereqLeft );
@@ -140360,8 +141055,8 @@ static void exprAnalyze(
}
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* When sqlite_stat3 histogram data is available an operator of the
+#ifdef SQLITE_ENABLE_STAT4
+ /* When sqlite_stat4 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
@@ -140372,7 +141067,7 @@ static void exprAnalyze(
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
&& !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Stat34)
+ && OptimizationEnabled(db, SQLITE_Stat4)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -140397,7 +141092,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -140430,7 +141125,7 @@ static void exprAnalyze(
** all terms of the WHERE clause.
*/
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
- Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
pWC->op = op;
if( pE2==0 ) return;
if( pE2->op!=op ){
@@ -140845,7 +141540,8 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
){
if( (pTerm->eOperator & WO_EQUIV)!=0
&& pScan->nEquiv<ArraySize(pScan->aiCur)
- && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
+ && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op
+ ==TK_COLUMN
){
int j;
for(j=0; j<pScan->nEquiv; j++){
@@ -141041,7 +141737,7 @@ static int findIndexCol(
const char *zColl = pIdx->azColl[iCol];
for(i=0; i<pList->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
if( p->op==TK_COLUMN
&& p->iColumn==pIdx->aiColumn[iCol]
&& p->iTable==iBase
@@ -141105,7 +141801,7 @@ static int isDistinctRedundant(
** current SELECT is a correlated sub-query.
*/
for(i=0; i<pDistinct->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
}
@@ -141525,6 +142221,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<n; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break;
+ if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break;
}
if( i==n){
nOrderBy = n;
@@ -141623,7 +142320,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
pIdxOrderBy[i].iColumn = pExpr->iColumn;
- pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
+ pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC;
}
*pmNoOmit = mNoOmit;
@@ -141669,7 +142366,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
@@ -141862,7 +142559,7 @@ static int whereKeyStats(
pRec->nField = nField;
return i;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** If it is not NULL, pTerm is a term that provides an upper or lower
@@ -141888,7 +142585,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return the affinity for a single column of an index.
*/
@@ -141897,12 +142594,13 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
}
+ assert( pIdx->zColAff[iCol]!=0 );
return pIdx->zColAff[iCol];
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** This function is called to estimate the number of rows visited by a
** range-scan on a skip-scan index. For example:
@@ -142008,7 +142706,7 @@ static int whereRangeSkipScanEst(
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -142061,12 +142759,12 @@ static int whereRangeScanEst(
int nOut = pLoop->nOut;
LogEst nNew;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
Index *p = pLoop->u.btree.pIndex;
int nEq = pLoop->u.btree.nEq;
- if( p->nSample>0 && nEq<p->nSampleCol
- && OptimizationEnabled(pParse->db, SQLITE_Stat34)
+ if( p->nSample>0 && ALWAYS(nEq<p->nSampleCol)
+ && OptimizationEnabled(pParse->db, SQLITE_Stat4)
){
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
@@ -142164,7 +142862,7 @@ static int whereRangeScanEst(
/* TUNING: If both iUpper and iLower are derived from the same
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
- ** if estimated without the use of STAT3/4 tables. */
+ ** if estimated without the use of STAT4 tables. */
if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
@@ -142213,12 +142911,12 @@ static int whereRangeScanEst(
return rc;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
-** column of an index and sqlite_stat3 histogram data is available
+** column of an index and sqlite_stat4 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
@@ -142276,9 +142974,9 @@ static int whereEqualScanEst(
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -142325,7 +143023,7 @@ static int whereInScanEst(
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifdef WHERETRACE_ENABLED
@@ -142857,11 +143555,12 @@ static void whereLoopOutputAdjust(
){
WhereTerm *pTerm, *pX;
Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
- int i, j, k;
+ int i, j;
LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */
assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
+ assert( pTerm!=0 );
if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
@@ -142882,6 +143581,7 @@ static void whereLoopOutputAdjust(
pLoop->nOut--;
if( pTerm->eOperator&(WO_EQ|WO_IS) ){
Expr *pRight = pTerm->pExpr->pRight;
+ int k = 0;
testcase( pTerm->pExpr->op==TK_IS );
if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
k = 10;
@@ -143045,7 +143745,7 @@ static int whereLoopAddBtreeIndex(
LogEst rCostIdx;
LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
int nIn = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int nRecValid = pBuilder->nRecValid;
#endif
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
@@ -143106,8 +143806,6 @@ static int whereLoopAddBtreeIndex(
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
- assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
- ** changes "x IN (?)" into "x=?". */
}
if( pProbe->hasStat1 ){
LogEst M, logK, safetyMargin;
@@ -143203,7 +143901,7 @@ static int whereLoopAddBtreeIndex(
** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
assert( pNew->nOut==saved_nOut );
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
- /* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
+ /* Adjust nOut using stat4 data. Or, if there is no stat4
** data, using some other estimate. */
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
}else{
@@ -143217,13 +143915,13 @@ static int whereLoopAddBtreeIndex(
pNew->nOut += pTerm->truthProb;
pNew->nOut -= nIn;
}else{
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
tRowcnt nOut = 0;
if( nInMul==0
&& pProbe->nSample
&& pNew->u.btree.nEq<=pProbe->nSampleCol
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
- && OptimizationEnabled(db, SQLITE_Stat34)
+ && OptimizationEnabled(db, SQLITE_Stat4)
){
Expr *pExpr = pTerm->pExpr;
if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
@@ -143260,6 +143958,7 @@ static int whereLoopAddBtreeIndex(
** it to pNew->rRun, which is currently set to the cost of the index
** seek only. Then, if this is a non-covering index, add the cost of
** visiting the rows in the main table. */
+ assert( pSrc->pTab->szTabRow>0 );
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
@@ -143285,7 +143984,7 @@ static int whereLoopAddBtreeIndex(
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
}
pNew->nOut = saved_nOut;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
pBuilder->nRecValid = nRecValid;
#endif
}
@@ -143358,7 +144057,7 @@ static int indexMightHelpWithOrderBy(
if( pIndex->bUnordered ) return 0;
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
for(ii=0; ii<pOB->nExpr; ii++){
- Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
@@ -143389,7 +144088,9 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
}
if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
- Expr *pExpr = pTerm->pExpr;
+ Expr *pExpr;
+ if( pTerm->wtFlags & TERM_NOPARTIDX ) continue;
+ pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab)
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
){
@@ -143658,7 +144359,7 @@ static int whereLoopAddBtree(
** plan */
pTab->tabFlags |= TF_StatsUsed;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
pBuilder->nRecValid = 0;
pBuilder->pRec = 0;
@@ -144286,8 +144987,8 @@ static i8 wherePathSatisfiesOrderBy(
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
if( pLoop->u.vtab.isOrdered ) obSat = obDone;
break;
- }else{
- pLoop->u.btree.nIdxCol = 0;
+ }else if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = 0;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
@@ -144298,7 +144999,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
for(i=0; i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
if( pOBExpr->op!=TK_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
@@ -144335,7 +145036,8 @@ static i8 wherePathSatisfiesOrderBy(
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
|| !HasRowid(pIndex->pTable));
- isOrderDistinct = IsUniqueIndex(pIndex);
+ isOrderDistinct = IsUniqueIndex(pIndex)
+ && (pLoop->wsFlags & WHERE_SKIPSCAN)==0;
}
/* Loop through all columns of the index and deal with the ones
@@ -144353,15 +145055,21 @@ static i8 wherePathSatisfiesOrderBy(
u16 eOp = pLoop->aLTerm[j]->eOperator;
/* Skip over == and IS and ISNULL terms. (Also skip IN terms when
- ** doing WHERE_ORDERBY_LIMIT processing).
+ ** doing WHERE_ORDERBY_LIMIT processing). Except, IS and ISNULL
+ ** terms imply that the index is not UNIQUE NOT NULL in which case
+ ** the loop need to be marked as not order-distinct because it can
+ ** have repeated NULL rows.
**
** If the current term is a column of an ((?,?) IN (SELECT...))
** expression for which the SELECT returns more than one column,
** check that it is the only column used by this loop. Otherwise,
** if it is one of two or more, none of the columns can be
- ** considered to match an ORDER BY term. */
+ ** considered to match an ORDER BY term.
+ */
if( (eOp & eqOpMask)!=0 ){
- if( eOp & WO_ISNULL ){
+ if( eOp & (WO_ISNULL|WO_IS) ){
+ testcase( eOp & WO_ISNULL );
+ testcase( eOp & WO_IS );
testcase( isOrderDistinct );
isOrderDistinct = 0;
}
@@ -144387,7 +145095,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
if( pIndex ){
iColumn = pIndex->aiColumn[j];
- revIdx = pIndex->aSortOrder[j];
+ revIdx = pIndex->aSortOrder[j] & KEYINFO_ORDER_DESC;
if( iColumn==pIndex->pTable->iPKey ) iColumn = XN_ROWID;
}else{
iColumn = XN_ROWID;
@@ -144411,7 +145119,7 @@ static i8 wherePathSatisfiesOrderBy(
isMatch = 0;
for(i=0; bOnce && i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
testcase( wctrlFlags & WHERE_GROUPBY );
testcase( wctrlFlags & WHERE_DISTINCTBY );
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
@@ -144429,7 +145137,9 @@ static i8 wherePathSatisfiesOrderBy(
pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
}
- pLoop->u.btree.nIdxCol = j+1;
+ if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = j+1;
+ }
isMatch = 1;
break;
}
@@ -144437,13 +145147,22 @@ static i8 wherePathSatisfiesOrderBy(
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
- if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
+ if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){
+ isMatch = 0;
+ }
}else{
- rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC);
if( rev ) *pRevMask |= MASKBIT(iLoop);
revSet = 1;
}
}
+ if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){
+ if( j==pLoop->u.btree.nEq ){
+ pLoop->wsFlags |= WHERE_BIGNULL_SORT;
+ }else{
+ isMatch = 0;
+ }
+ }
if( isMatch ){
if( iColumn==XN_ROWID ){
testcase( distinctColumns==0 );
@@ -145357,6 +146076,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3DebugPrintf(", limit: %d", iAuxArg);
}
sqlite3DebugPrintf(")\n");
+ if( sqlite3WhereTrace & 0x100 ){
+ Select sSelect;
+ memset(&sSelect, 0, sizeof(sSelect));
+ sSelect.selFlags = SF_WhereBegin;
+ sSelect.pSrc = pTabList;
+ sSelect.pWhere = pWhere;
+ sSelect.pOrderBy = pOrderBy;
+ sSelect.pEList = pResultSet;
+ sqlite3TreeViewSelect(0, &sSelect, 0);
+ }
}
if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
sqlite3WhereClausePrint(sWLB.pWC);
@@ -145633,6 +146362,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0
&& (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0
&& (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0
&& pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED
){
@@ -145750,7 +146480,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
&& i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */
&& (pLoop->wsFlags & WHERE_INDEXED)!=0
&& (pIdx = pLoop->u.btree.pIndex)->hasStat1
- && (n = pLoop->u.btree.nIdxCol)>0
+ && (n = pLoop->u.btree.nDistinctCol)>0
&& pIdx->aiRowLogEst[n]>=36
){
int r1 = pParse->nMem+1;
@@ -145774,6 +146504,11 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
VdbeCoverageIf(v, pLevel->op==OP_Next);
VdbeCoverageIf(v, pLevel->op==OP_Prev);
VdbeCoverageIf(v, pLevel->op==OP_VNext);
+ if( pLevel->regBignull ){
+ sqlite3VdbeResolveLabel(v, pLevel->addrBignull);
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, pLevel->regBignull, pLevel->p2-1);
+ VdbeCoverage(v);
+ }
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
#endif
@@ -146713,6 +147448,8 @@ struct WindowRewrite {
static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
struct WindowRewrite *p = pWalker->u.pRewrite;
Parse *pParse = pWalker->pParse;
+ assert( p!=0 );
+ assert( p->pWin!=0 );
/* If this function is being called from within a scalar sub-select
** that used by the SELECT statement being processed, only process
@@ -146812,6 +147549,7 @@ static void selectWindowRewriteEList(
Walker sWalker;
WindowRewrite sRewrite;
+ assert( pWin!=0 );
memset(&sWalker, 0, sizeof(Walker));
memset(&sRewrite, 0, sizeof(WindowRewrite));
@@ -146850,7 +147588,7 @@ static ExprList *exprListAppendList(
pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse);
}
pList = sqlite3ExprListAppend(pParse, pList, pDup);
- if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
+ if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags;
}
}
return pList;
@@ -146896,11 +147634,14 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
** redundant, remove the ORDER BY from the parent SELECT. */
pSort = sqlite3ExprListDup(db, pMWin->pPartition, 0);
pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy, 1);
- if( pSort && p->pOrderBy ){
+ if( pSort && p->pOrderBy && p->pOrderBy->nExpr<=pSort->nExpr ){
+ int nSave = pSort->nExpr;
+ pSort->nExpr = p->pOrderBy->nExpr;
if( sqlite3ExprListCompare(pSort, p->pOrderBy, -1)==0 ){
sqlite3ExprListDelete(db, p->pOrderBy);
p->pOrderBy = 0;
}
+ pSort->nExpr = nSave;
}
/* Assign a cursor number for the ephemeral table used to buffer rows.
@@ -146924,8 +147665,15 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
** window function - one for the accumulator, another for interim
** results. */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
- pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList, 0);
+ ExprList *pArgs = pWin->pOwner->x.pList;
+ if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
+ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pWin->bExprArgs = 1;
+ }else{
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pSublist = exprListAppendList(pParse, pSublist, pArgs, 0);
+ }
if( pWin->pFilter ){
Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0);
pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter);
@@ -146943,7 +147691,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
*/
if( pSublist==0 ){
pSublist = sqlite3ExprListAppend(pParse, 0,
- sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0)
+ sqlite3Expr(db, TK_INTEGER, "0")
);
}
@@ -146956,7 +147704,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
p->pSrc->a[0].pSelect = pSub;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded;
- pTab2 = sqlite3ResultSetOfSelect(pParse, pSub);
+ pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
if( pTab2==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -146980,10 +147728,23 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
}
/*
+** Unlink the Window object from the Select to which it is attached,
+** if it is attached.
+*/
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window *p){
+ if( p->ppThis ){
+ *p->ppThis = p->pNextWin;
+ if( p->pNextWin ) p->pNextWin->ppThis = p->ppThis;
+ p->ppThis = 0;
+ }
+}
+
+/*
** Free the Window object passed as the second argument.
*/
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){
if( p ){
+ sqlite3WindowUnlinkFromSelect(p);
sqlite3ExprDelete(db, p->pFilter);
sqlite3ExprListDelete(db, p->pPartition);
sqlite3ExprListDelete(db, p->pOrderBy);
@@ -147161,17 +147922,14 @@ SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pLis
SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
- /* This routine is only called for the parser. If pWin was not
- ** allocated due to an OOM, then the parser would fail before ever
- ** invoking this routine */
- if( ALWAYS(pWin) ){
- p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
- pWin->pOwner = p;
- if( p->flags & EP_Distinct ){
- sqlite3ErrorMsg(pParse,
- "DISTINCT is not supported for window functions");
- }
+ assert( pWin );
+ p->y.pWin = pWin;
+ ExprSetProperty(p, EP_WinFunc);
+ pWin->pOwner = p;
+ if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
+ sqlite3ErrorMsg(pParse,
+ "DISTINCT is not supported for window functions"
+ );
}
}else{
sqlite3WindowDelete(pParse->db, pWin);
@@ -147179,10 +147937,29 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
}
/*
+** Possibly link window pWin into the list at pSel->pWin (window functions
+** to be processed as part of SELECT statement pSel). The window is linked
+** in if either (a) there are no other windows already linked to this
+** SELECT, or (b) the windows already linked use a compatible window frame.
+*/
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
+ if( 0==pSel->pWin
+ || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0)
+ ){
+ pWin->pNextWin = pSel->pWin;
+ if( pSel->pWin ){
+ pSel->pWin->ppThis = &pWin->pNextWin;
+ }
+ pSel->pWin = pWin;
+ pWin->ppThis = &pSel->pWin;
+ }
+}
+
+/*
** Return 0 if the two window objects are identical, or non-zero otherwise.
** Identical window objects can be processed in a single scan.
*/
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){
if( p1->eFrmType!=p2->eFrmType ) return 1;
if( p1->eStart!=p2->eStart ) return 1;
if( p1->eEnd!=p2->eEnd ) return 1;
@@ -147191,6 +147968,9 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1;
if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1;
if( sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1) ) return 1;
+ if( bFilter ){
+ if( sqlite3ExprCompare(pParse, p1->pFilter, p2->pFilter, -1) ) return 1;
+ }
return 0;
}
@@ -147242,8 +148022,8 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
pWin->regApp = pParse->nMem+1;
pParse->nMem += 3;
if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){
- assert( pKeyInfo->aSortOrder[0]==0 );
- pKeyInfo->aSortOrder[0] = 1;
+ assert( pKeyInfo->aSortFlags[0]==0 );
+ pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC;
}
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->csrApp, 2);
sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
@@ -147328,6 +148108,108 @@ static int windowArgCount(Window *pWin){
return (pList ? pList->nExpr : 0);
}
+typedef struct WindowCodeArg WindowCodeArg;
+typedef struct WindowCsrAndReg WindowCsrAndReg;
+
+/*
+** See comments above struct WindowCodeArg.
+*/
+struct WindowCsrAndReg {
+ int csr; /* Cursor number */
+ int reg; /* First in array of peer values */
+};
+
+/*
+** A single instance of this structure is allocated on the stack by
+** sqlite3WindowCodeStep() and a pointer to it passed to the various helper
+** routines. This is to reduce the number of arguments required by each
+** helper function.
+**
+** regArg:
+** Each window function requires an accumulator register (just as an
+** ordinary aggregate function does). This variable is set to the first
+** in an array of accumulator registers - one for each window function
+** in the WindowCodeArg.pMWin list.
+**
+** eDelete:
+** The window functions implementation sometimes caches the input rows
+** that it processes in a temporary table. If it is not zero, this
+** variable indicates when rows may be removed from the temp table (in
+** order to reduce memory requirements - it would always be safe just
+** to leave them there). Possible values for eDelete are:
+**
+** WINDOW_RETURN_ROW:
+** An input row can be discarded after it is returned to the caller.
+**
+** WINDOW_AGGINVERSE:
+** An input row can be discarded after the window functions xInverse()
+** callbacks have been invoked in it.
+**
+** WINDOW_AGGSTEP:
+** An input row can be discarded after the window functions xStep()
+** callbacks have been invoked in it.
+**
+** start,current,end
+** Consider a window-frame similar to the following:
+**
+** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
+**
+** The windows functions implmentation caches the input rows in a temp
+** table, sorted by "a, b" (it actually populates the cache lazily, and
+** aggressively removes rows once they are no longer required, but that's
+** a mere detail). It keeps three cursors open on the temp table. One
+** (current) that points to the next row to return to the query engine
+** once its window function values have been calculated. Another (end)
+** points to the next row to call the xStep() method of each window function
+** on (so that it is 2 groups ahead of current). And a third (start) that
+** points to the next row to call the xInverse() method of each window
+** function on.
+**
+** Each cursor (start, current and end) consists of a VDBE cursor
+** (WindowCsrAndReg.csr) and an array of registers (starting at
+** WindowCodeArg.reg) that always contains a copy of the peer values
+** read from the corresponding cursor.
+**
+** Depending on the window-frame in question, all three cursors may not
+** be required. In this case both WindowCodeArg.csr and reg are set to
+** 0.
+*/
+struct WindowCodeArg {
+ Parse *pParse; /* Parse context */
+ Window *pMWin; /* First in list of functions being processed */
+ Vdbe *pVdbe; /* VDBE object */
+ int addrGosub; /* OP_Gosub to this address to return one row */
+ int regGosub; /* Register used with OP_Gosub(addrGosub) */
+ int regArg; /* First in array of accumulator registers */
+ int eDelete; /* See above */
+
+ WindowCsrAndReg start;
+ WindowCsrAndReg current;
+ WindowCsrAndReg end;
+};
+
+/*
+** Generate VM code to read the window frames peer values from cursor csr into
+** an array of registers starting at reg.
+*/
+static void windowReadPeerValues(
+ WindowCodeArg *p,
+ int csr,
+ int reg
+){
+ Window *pMWin = p->pMWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ if( pOrderBy ){
+ Vdbe *v = sqlite3GetVdbe(p->pParse);
+ ExprList *pPart = pMWin->pPartition;
+ int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
+ int i;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
+ }
+ }
+}
+
/*
** Generate VM code to invoke either xStep() (if bInverse is 0) or
** xInverse (if bInverse is non-zero) for each window function in the
@@ -147348,20 +148230,27 @@ static int windowArgCount(Window *pWin){
** number of rows in the current partition.
*/
static void windowAggStep(
- Parse *pParse,
+ WindowCodeArg *p,
Window *pMWin, /* Linked list of window functions */
int csr, /* Read arguments from this cursor */
int bInverse, /* True to invoke xInverse instead of xStep */
int reg /* Array of registers */
){
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pFunc;
int regArg;
- int nArg = windowArgCount(pWin);
+ int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
+ assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
+
+ /* All OVER clauses in the same window function aggregate step must
+ ** be the same. */
+ assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)==0 );
+
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
@@ -147399,14 +148288,30 @@ static void windowAggStep(
int addrIf = 0;
if( pWin->pFilter ){
int regTmp;
- assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr );
- assert( nArg || pWin->pOwner->x.pList==0 );
+ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
+ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
regTmp = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
+
+ if( pWin->bExprArgs ){
+ int iStart = sqlite3VdbeCurrentAddr(v);
+ VdbeOp *pOp, *pEnd;
+
+ nArg = pWin->pOwner->x.pList->nExpr;
+ regArg = sqlite3GetTempRange(pParse, nArg);
+ sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
+
+ pEnd = sqlite3VdbeGetOp(v, -1);
+ for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){
+ if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
+ pOp->p1 = csr;
+ }
+ }
+ }
if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl;
assert( nArg>0 );
@@ -147417,32 +148322,14 @@ static void windowAggStep(
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
+ if( pWin->bExprArgs ){
+ sqlite3ReleaseTempRange(pParse, regArg, nArg);
+ }
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
}
-typedef struct WindowCodeArg WindowCodeArg;
-typedef struct WindowCsrAndReg WindowCsrAndReg;
-struct WindowCsrAndReg {
- int csr;
- int reg;
-};
-
-struct WindowCodeArg {
- Parse *pParse;
- Window *pMWin;
- Vdbe *pVdbe;
- int regGosub;
- int addrGosub;
- int regArg;
- int eDelete;
-
- WindowCsrAndReg start;
- WindowCsrAndReg current;
- WindowCsrAndReg end;
-};
-
/*
** Values that may be passed as the second argument to windowCodeOp().
*/
@@ -147451,28 +148338,6 @@ struct WindowCodeArg {
#define WINDOW_AGGSTEP 3
/*
-** Generate VM code to read the window frames peer values from cursor csr into
-** an array of registers starting at reg.
-*/
-static void windowReadPeerValues(
- WindowCodeArg *p,
- int csr,
- int reg
-){
- Window *pMWin = p->pMWin;
- ExprList *pOrderBy = pMWin->pOrderBy;
- if( pOrderBy ){
- Vdbe *v = sqlite3GetVdbe(p->pParse);
- ExprList *pPart = pMWin->pPartition;
- int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
- int i;
- for(i=0; i<pOrderBy->nExpr; i++){
- sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
- }
- }
-}
-
-/*
** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
** (bFin==1) for each window function in the linked list starting at
** pMWin. Or, for built-in window-functions that do not use the standard
@@ -147532,8 +148397,12 @@ static void windowFullScan(WindowCodeArg *p){
int lblNext;
int lblBrk;
int addrNext;
- int csr = pMWin->csrApp;
+ int csr;
+
+ VdbeModuleComment((v, "windowFullScan begin"));
+ assert( pMWin!=0 );
+ csr = pMWin->csrApp;
nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
lblNext = sqlite3VdbeMakeLabel(pParse);
@@ -147588,7 +148457,7 @@ static void windowFullScan(WindowCodeArg *p){
if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
}
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
sqlite3VdbeResolveLabel(v, lblNext);
sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
@@ -147603,6 +148472,7 @@ static void windowFullScan(WindowCodeArg *p){
}
windowAggFinal(p, 1);
+ VdbeModuleComment((v, "windowFullScan end"));
}
/*
@@ -147777,34 +148647,46 @@ static void windowIfNewPeer(
/*
** This function is called as part of generating VM programs for RANGE
** offset PRECEDING/FOLLOWING frame boundaries. Assuming "ASC" order for
-** the ORDER BY term in the window, it generates code equivalent to:
+** the ORDER BY term in the window, and that argument op is OP_Ge, it generates
+** code equivalent to:
**
** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
**
-** A special type of arithmetic is used such that if csr.peerVal is not
-** a numeric type (real or integer), then the result of the addition is
-** a copy of csr1.peerVal.
+** The value of parameter op may also be OP_Gt or OP_Le. In these cases the
+** operator in the above pseudo-code is replaced with ">" or "<=", respectively.
+**
+** If the sort-order for the ORDER BY term in the window is DESC, then the
+** comparison is reversed. Instead of adding regVal to csr1.peerVal, it is
+** subtracted. And the comparison operator is inverted to - ">=" becomes "<=",
+** ">" becomes "<", and so on. So, with DESC sort order, if the argument op
+** is OP_Ge, the generated code is equivalent to:
+**
+** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl;
+**
+** A special type of arithmetic is used such that if csr1.peerVal is not
+** a numeric type (real or integer), then the result of the addition addition
+** or subtraction is a a copy of csr1.peerVal.
*/
static void windowCodeRangeTest(
WindowCodeArg *p,
- int op, /* OP_Ge or OP_Gt */
- int csr1,
- int regVal,
- int csr2,
- int lbl
+ int op, /* OP_Ge, OP_Gt, or OP_Le */
+ int csr1, /* Cursor number for cursor 1 */
+ int regVal, /* Register containing non-negative number */
+ int csr2, /* Cursor number for cursor 2 */
+ int lbl /* Jump destination if condition is true */
){
Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
- int reg1 = sqlite3GetTempReg(pParse);
- int reg2 = sqlite3GetTempReg(pParse);
- int arith = OP_Add;
- int addrGe;
-
- int regString = ++pParse->nMem;
+ ExprList *pOrderBy = p->pMWin->pOrderBy; /* ORDER BY clause for window */
+ int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
+ int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
+ int regString = ++pParse->nMem; /* Reg. for constant value '' */
+ int arith = OP_Add; /* OP_Add or OP_Subtract */
+ int addrGe; /* Jump destination */
assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
- assert( p->pMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 );
- if( p->pMWin->pOrderBy->a[0].sortOrder ){
+ assert( pOrderBy && pOrderBy->nExpr==1 );
+ if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
switch( op ){
case OP_Ge: op = OP_Le; break;
case OP_Gt: op = OP_Lt; break;
@@ -147813,27 +148695,95 @@ static void windowCodeRangeTest(
arith = OP_Subtract;
}
+ /* Read the peer-value from each cursor into a register */
windowReadPeerValues(p, csr1, reg1);
windowReadPeerValues(p, csr2, reg2);
- /* Check if the peer value for csr1 value is a text or blob by comparing
- ** it to the smallest possible string - ''. If it is, jump over the
- ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */
+ VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl",
+ reg1, (arith==OP_Add ? "+" : "-"), regVal,
+ ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2
+ ));
+
+ /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
+ ** This block adds (or subtracts for DESC) the numeric value in regVal
+ ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob),
+ ** then leave reg1 as it is. In pseudo-code, this is implemented as:
+ **
+ ** if( reg1>='' ) goto addrGe;
+ ** reg1 = reg1 +/- regVal
+ ** addrGe:
+ **
+ ** Since all strings and blobs are greater-than-or-equal-to an empty string,
+ ** the add/subtract is skipped for these, as required. If reg1 is a NULL,
+ ** then the arithmetic is performed, but since adding or subtracting from
+ ** NULL is always NULL anyway, this case is handled as required too. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
sqlite3VdbeJumpHere(v, addrGe);
+
+ /* If the BIGNULL flag is set for the ORDER BY, then it is required to
+ ** consider NULL values to be larger than all other values, instead of
+ ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this
+ ** (and adding that capability causes a performance regression), so
+ ** instead if the BIGNULL flag is set then cases where either reg1 or
+ ** reg2 are NULL are handled separately in the following block. The code
+ ** generated is equivalent to:
+ **
+ ** if( reg1 IS NULL ){
+ ** if( op==OP_Ge ) goto lbl;
+ ** if( op==OP_Gt && reg2 IS NOT NULL ) goto lbl;
+ ** if( op==OP_Le && reg2 IS NULL ) goto lbl;
+ ** }else if( reg2 IS NULL ){
+ ** if( op==OP_Le ) goto lbl;
+ ** }
+ **
+ ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is
+ ** not taken, control jumps over the comparison operator coded below this
+ ** block. */
+ if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){
+ /* This block runs if reg1 contains a NULL. */
+ int addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v);
+ switch( op ){
+ case OP_Ge:
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lbl);
+ break;
+ case OP_Gt:
+ sqlite3VdbeAddOp2(v, OP_NotNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ case OP_Le:
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ default: assert( op==OP_Lt ); /* no-op */ break;
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+
+ /* This block runs if reg1 is not NULL, but reg2 is. */
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v);
+ if( op==OP_Gt || op==OP_Ge ){
+ sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1);
+ }
+ }
+
+ /* Compare registers reg2 and reg1, taking the jump if required. Note that
+ ** control skips over this test if the BIGNULL flag is set and either
+ ** reg1 or reg2 contain a NULL value. */
sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+
assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le);
testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt);
-
sqlite3ReleaseTempReg(pParse, reg1);
sqlite3ReleaseTempReg(pParse, reg2);
+
+ VdbeModuleComment((v, "CodeRangeTest: end"));
}
/*
@@ -147853,9 +148803,7 @@ static int windowCodeOp(
Window *pMWin = p->pMWin;
int ret = 0;
Vdbe *v = p->pVdbe;
- int addrIf = 0;
int addrContinue = 0;
- int addrGoto = 0;
int bPeer = (pMWin->eFrmType!=TK_ROWS);
int lblDone = sqlite3VdbeMakeLabel(pParse);
@@ -147888,7 +148836,7 @@ static int windowCodeOp(
);
}
}else{
- addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, lblDone, 1);
VdbeCoverage(v);
}
}
@@ -147897,6 +148845,25 @@ static int windowCodeOp(
windowAggFinal(p, 0);
}
addrContinue = sqlite3VdbeCurrentAddr(v);
+
+ /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or
+ ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the
+ ** start cursor does not advance past the end cursor within the
+ ** temporary table. It otherwise might, if (a>b). */
+ if( pMWin->eStart==pMWin->eEnd && regCountdown
+ && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE
+ ){
+ int regRowid1 = sqlite3GetTempReg(pParse);
+ int regRowid2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2);
+ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, regRowid1);
+ sqlite3ReleaseTempReg(pParse, regRowid2);
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING );
+ }
+
switch( op ){
case WINDOW_RETURN_ROW:
csr = p->current.csr;
@@ -147911,7 +148878,7 @@ static int windowCodeOp(
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 1, p->regArg);
+ windowAggStep(p, pMWin, csr, 1, p->regArg);
}
break;
@@ -147923,7 +148890,7 @@ static int windowCodeOp(
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
}
break;
}
@@ -147941,7 +148908,7 @@ static int windowCodeOp(
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
VdbeCoverage(v);
if( bPeer ){
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblDone);
}
}
@@ -147957,8 +148924,6 @@ static int windowCodeOp(
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
}
sqlite3VdbeResolveLabel(v, lblDone);
- if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
return ret;
}
@@ -147974,6 +148939,7 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
+ pNew->zBase = sqlite3DbStrDup(db, p->zBase);
pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
pNew->pFunc = p->pFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
@@ -147982,9 +148948,11 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
pNew->eExclude = p->eExclude;
+ pNew->regResult = p->regResult;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
+ pNew->bImplicitFrame = p->bImplicitFrame;
}
}
return pNew;
@@ -148308,7 +149276,7 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){
** regEnd = <expr2>
** regStart = <expr1>
** }else{
-** if( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
** AGGSTEP
** }
** while( (csrStart.key + regStart) < csrCurrent.key ){
@@ -148381,8 +149349,6 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
int addrInteger = 0; /* Address of OP_Integer */
int addrEmpty; /* Address of OP_Rewind in flush: */
- int regStart = 0; /* Value of <expr> PRECEDING */
- int regEnd = 0; /* Value of <expr> FOLLOWING */
int regNew; /* Array of registers holding new input row */
int regRecord; /* regNew array in record form */
int regRowid; /* Rowid for regRecord in eph table */
@@ -148391,6 +149357,8 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int regFlushPart = 0; /* Register for "Gosub flush_partition" */
WindowCodeArg s; /* Context object for sub-routines */
int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */
+ int regStart = 0; /* Value of <expr> PRECEDING */
+ int regEnd = 0; /* Value of <expr> FOLLOWING */
assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
|| pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
@@ -148521,14 +149489,14 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
if( regStart ){
sqlite3ExprCode(pParse, pMWin->pStart, regStart);
- windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE?3:0));
}
if( regEnd ){
sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
- windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE?3:0));
}
- if( pMWin->eStart==pMWin->eEnd && regStart ){
+ if( pMWin->eFrmType!=TK_RANGE && pMWin->eStart==pMWin->eEnd && regStart ){
int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
@@ -148788,6 +149756,7 @@ static void disableLookaside(Parse *pParse){
** SQLITE_LIMIT_COMPOUND_SELECT.
*/
static void parserDoubleLinkSelect(Parse *pParse, Select *p){
+ assert( p!=0 );
if( p->pPrior ){
Select *pNext = 0, *pLoop;
int mxSelect, cnt = 0;
@@ -148814,7 +149783,7 @@ static void disableLookaside(Parse *pParse){
if( p ){
/* memset(p, 0, sizeof(Expr)); */
p->op = (u8)op;
- p->affinity = 0;
+ p->affExpr = 0;
p->flags = EP_Leaf;
p->iAgg = -1;
p->pLeft = p->pRight = 0;
@@ -148941,28 +149910,28 @@ static void disableLookaside(Parse *pParse){
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 302
+#define YYNOCODE 307
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 95
+#define YYWILDCARD 98
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- TriggerStep* yy11;
- IdList* yy76;
- ExprList* yy94;
- Upsert* yy95;
- int yy100;
- Expr* yy102;
- struct {int value; int mask;} yy199;
- u8 yy218;
- With* yy243;
- struct TrigEvent yy298;
- Window* yy379;
- struct FrameBound yy389;
- Select* yy391;
- SrcList* yy407;
- const char* yy528;
+ const char* yy8;
+ Select* yy25;
+ int yy32;
+ Expr* yy46;
+ struct FrameBound yy57;
+ u8 yy118;
+ ExprList* yy138;
+ Upsert* yy288;
+ With* yy297;
+ IdList* yy406;
+ Window* yy455;
+ struct {int value; int mask;} yy495;
+ TriggerStep* yy527;
+ struct TrigEvent yy572;
+ SrcList* yy609;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -148978,17 +149947,17 @@ typedef union {
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 540
-#define YYNRULE 376
-#define YYNTOKEN 176
-#define YY_MAX_SHIFT 539
-#define YY_MIN_SHIFTREDUCE 783
-#define YY_MAX_SHIFTREDUCE 1158
-#define YY_ERROR_ACTION 1159
-#define YY_ACCEPT_ACTION 1160
-#define YY_NO_ACTION 1161
-#define YY_MIN_REDUCE 1162
-#define YY_MAX_REDUCE 1537
+#define YYNSTATE 543
+#define YYNRULE 381
+#define YYNTOKEN 179
+#define YY_MAX_SHIFT 542
+#define YY_MIN_SHIFTREDUCE 790
+#define YY_MAX_SHIFTREDUCE 1170
+#define YY_ERROR_ACTION 1171
+#define YY_ACCEPT_ACTION 1172
+#define YY_NO_ACTION 1173
+#define YY_MIN_REDUCE 1174
+#define YY_MAX_REDUCE 1554
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -149055,601 +150024,573 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2142)
+#define YY_ACTTAB_COUNT (1913)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 112, 109, 209, 112, 109, 209, 1160, 1, 1, 539,
- /* 10 */ 2, 1164, 490, 1193, 1293, 534, 289, 1196, 134, 383,
- /* 20 */ 1485, 1428, 1164, 1229, 1208, 1242, 1195, 289, 491, 134,
- /* 30 */ 373, 915, 1229, 443, 16, 16, 1242, 70, 70, 916,
- /* 40 */ 242, 1292, 296, 119, 120, 110, 1136, 1136, 981, 984,
- /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 264, 264,
- /* 60 */ 190, 264, 264, 264, 264, 112, 109, 209, 362, 264,
- /* 70 */ 264, 531, 376, 497, 531, 1134, 531, 1501, 239, 206,
- /* 80 */ 338, 9, 531, 242, 219, 1203, 118, 118, 118, 118,
- /* 90 */ 111, 439, 112, 109, 209, 219, 116, 116, 116, 116,
- /* 100 */ 115, 115, 114, 114, 114, 113, 414, 115, 115, 114,
- /* 110 */ 114, 114, 113, 414, 418, 12, 383, 400, 1134, 114,
- /* 120 */ 114, 114, 113, 414, 1115, 418, 1134, 1392, 116, 116,
- /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 414, 961,
- /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
- /* 150 */ 117, 118, 118, 118, 118, 952, 534, 414, 941, 951,
- /* 160 */ 1481, 539, 2, 1164, 1505, 534, 160, 175, 289, 1134,
- /* 170 */ 134, 434, 312, 297, 1115, 1116, 1117, 1242, 70, 70,
- /* 180 */ 1089, 338, 1089, 118, 118, 118, 118, 42, 42, 448,
- /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114,
- /* 200 */ 114, 114, 113, 414, 1115, 311, 264, 264, 82, 441,
- /* 210 */ 264, 264, 190, 383, 284, 12, 288, 525, 407, 531,
- /* 220 */ 96, 159, 458, 531, 371, 116, 116, 116, 116, 115,
- /* 230 */ 115, 114, 114, 114, 113, 414, 219, 119, 120, 110,
- /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 250 */ 118, 118, 511, 1477, 1115, 1116, 1117, 113, 414, 534,
- /* 260 */ 528, 528, 528, 121, 534, 1427, 418, 116, 116, 116,
- /* 270 */ 116, 115, 115, 114, 114, 114, 113, 414, 1464, 351,
- /* 280 */ 270, 42, 42, 383, 187, 1115, 70, 70, 533, 433,
- /* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 300 */ 414, 534, 1339, 405, 159, 411, 410, 119, 120, 110,
- /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 320 */ 118, 118, 285, 42, 42, 349, 411, 410, 514, 479,
- /* 330 */ 1458, 79, 1084, 6, 1140, 1115, 1116, 1117, 480, 1142,
- /* 340 */ 501, 1115, 1084, 123, 238, 1084, 136, 1141, 1234, 1234,
- /* 350 */ 1143, 383, 1143, 1115, 167, 426, 80, 447, 512, 1451,
- /* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 370 */ 414, 1143, 1466, 1143, 350, 119, 120, 110, 1136, 1136,
- /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118,
- /* 390 */ 402, 1115, 1116, 1117, 500, 534, 250, 267, 336, 474,
- /* 400 */ 331, 473, 236, 1115, 1116, 1117, 231, 1115, 329, 471,
- /* 410 */ 468, 467, 509, 1458, 1464, 505, 6, 70, 70, 466,
- /* 420 */ 181, 380, 379, 534, 971, 971, 982, 985, 116, 116,
- /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 414, 1115,
- /* 440 */ 412, 412, 412, 496, 1115, 69, 69, 235, 383, 288,
- /* 450 */ 525, 273, 326, 516, 337, 458, 1084, 1115, 1116, 1117,
- /* 460 */ 1232, 1232, 492, 160, 508, 441, 1084, 1067, 1531, 1084,
- /* 470 */ 207, 1531, 119, 120, 110, 1136, 1136, 981, 984, 974,
- /* 480 */ 974, 117, 117, 118, 118, 118, 118, 881, 534, 1115,
- /* 490 */ 1116, 1117, 975, 534, 1115, 1116, 1117, 534, 421, 534,
- /* 500 */ 141, 534, 176, 356, 517, 1119, 32, 511, 482, 388,
- /* 510 */ 70, 70, 818, 288, 525, 70, 70, 441, 499, 50,
- /* 520 */ 50, 70, 70, 70, 70, 116, 116, 116, 116, 115,
- /* 530 */ 115, 114, 114, 114, 113, 414, 274, 264, 264, 1115,
- /* 540 */ 1065, 264, 264, 1115, 355, 383, 409, 961, 1439, 822,
- /* 550 */ 531, 516, 190, 419, 531, 483, 1119, 516, 337, 516,
- /* 560 */ 518, 1115, 818, 952, 382, 458, 515, 951, 481, 119,
- /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 580 */ 118, 118, 118, 118, 1338, 278, 1045, 278, 275, 1115,
- /* 590 */ 1116, 1117, 259, 1115, 1116, 1117, 534, 5, 951, 951,
- /* 600 */ 953, 1046, 231, 3, 143, 471, 468, 467, 1391, 463,
- /* 610 */ 1115, 1115, 1116, 1117, 1452, 466, 1047, 836, 70, 70,
- /* 620 */ 480, 534, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 630 */ 114, 113, 414, 95, 1115, 287, 235, 856, 902, 420,
- /* 640 */ 1115, 534, 383, 13, 13, 381, 815, 857, 472, 112,
- /* 650 */ 109, 209, 1115, 337, 413, 309, 837, 394, 1436, 534,
- /* 660 */ 1115, 1116, 1117, 54, 54, 291, 119, 120, 110, 1136,
- /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118,
- /* 680 */ 118, 13, 13, 1084, 1115, 1116, 1117, 901, 264, 264,
- /* 690 */ 1115, 1116, 1117, 1084, 292, 399, 1084, 800, 388, 140,
- /* 700 */ 295, 531, 1115, 1116, 1117, 403, 447, 532, 534, 870,
- /* 710 */ 870, 534, 1240, 534, 329, 534, 1185, 389, 534, 116,
- /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 414,
- /* 730 */ 13, 13, 1024, 13, 13, 13, 13, 13, 13, 383,
- /* 740 */ 13, 13, 424, 1100, 401, 264, 264, 277, 160, 184,
- /* 750 */ 1182, 185, 1533, 369, 513, 484, 432, 487, 531, 424,
- /* 760 */ 423, 1397, 941, 119, 120, 110, 1136, 1136, 981, 984,
- /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 1397, 1399,
- /* 780 */ 425, 519, 392, 264, 264, 1029, 1029, 455, 264, 264,
- /* 790 */ 264, 264, 1004, 304, 261, 1278, 531, 900, 288, 525,
- /* 800 */ 310, 531, 493, 531, 1067, 1532, 458, 387, 1532, 311,
- /* 810 */ 429, 299, 534, 107, 264, 264, 116, 116, 116, 116,
- /* 820 */ 115, 115, 114, 114, 114, 113, 414, 531, 424, 1384,
- /* 830 */ 507, 258, 258, 1246, 55, 55, 383, 1277, 265, 265,
- /* 840 */ 962, 324, 434, 312, 531, 531, 506, 1397, 1026, 1241,
- /* 850 */ 298, 531, 1026, 445, 301, 1095, 303, 534, 368, 1156,
- /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
- /* 870 */ 117, 118, 118, 118, 118, 1045, 534, 1065, 534, 15,
- /* 880 */ 15, 1084, 208, 1324, 453, 452, 534, 1324, 534, 449,
- /* 890 */ 1046, 1084, 494, 458, 1084, 234, 233, 232, 44, 44,
- /* 900 */ 56, 56, 319, 1095, 322, 1047, 534, 900, 57, 57,
- /* 910 */ 58, 58, 534, 116, 116, 116, 116, 115, 115, 114,
- /* 920 */ 114, 114, 113, 414, 534, 514, 522, 534, 59, 59,
- /* 930 */ 302, 1157, 534, 383, 60, 60, 1237, 946, 788, 789,
- /* 940 */ 790, 1459, 1456, 446, 6, 6, 61, 61, 1212, 45,
- /* 950 */ 45, 534, 396, 383, 46, 46, 397, 119, 120, 110,
- /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 970 */ 118, 118, 428, 48, 48, 534, 392, 119, 120, 110,
- /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 990 */ 118, 118, 1324, 368, 1066, 447, 825, 49, 49, 534,
- /* 1000 */ 458, 357, 534, 353, 534, 138, 534, 337, 1478, 478,
- /* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 1020 */ 414, 62, 62, 392, 63, 63, 64, 64, 14, 14,
- /* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 1040 */ 414, 534, 810, 317, 271, 534, 1457, 825, 534, 6,
- /* 1050 */ 534, 1324, 534, 142, 534, 1442, 534, 212, 534, 1324,
- /* 1060 */ 534, 398, 305, 65, 65, 534, 1157, 125, 125, 476,
- /* 1070 */ 66, 66, 51, 51, 67, 67, 68, 68, 52, 52,
- /* 1080 */ 147, 147, 148, 148, 534, 98, 534, 75, 75, 276,
- /* 1090 */ 534, 272, 534, 810, 534, 876, 534, 527, 389, 534,
- /* 1100 */ 875, 534, 1151, 202, 534, 383, 53, 53, 71, 71,
- /* 1110 */ 288, 525, 126, 126, 72, 72, 127, 127, 128, 128,
- /* 1120 */ 454, 124, 124, 146, 146, 383, 145, 145, 408, 119,
- /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 1140 */ 118, 118, 118, 118, 534, 900, 534, 95, 534, 119,
- /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 1160 */ 118, 118, 118, 118, 390, 161, 132, 132, 131, 131,
- /* 1170 */ 129, 129, 534, 915, 534, 1455, 534, 1454, 6, 1416,
- /* 1180 */ 6, 916, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 1190 */ 114, 113, 414, 1415, 130, 130, 74, 74, 76, 76,
- /* 1200 */ 534, 30, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 1210 */ 114, 113, 414, 534, 263, 206, 534, 1133, 1504, 93,
- /* 1220 */ 876, 845, 73, 73, 102, 875, 100, 139, 17, 38,
- /* 1230 */ 208, 1062, 31, 450, 370, 43, 43, 101, 47, 47,
- /* 1240 */ 827, 216, 436, 308, 943, 440, 95, 241, 241, 442,
- /* 1250 */ 313, 464, 241, 95, 237, 900, 327, 383, 266, 95,
- /* 1260 */ 835, 834, 193, 335, 938, 314, 1011, 435, 842, 843,
- /* 1270 */ 955, 1007, 909, 334, 237, 241, 873, 383, 1023, 107,
- /* 1280 */ 1023, 119, 120, 110, 1136, 1136, 981, 984, 974, 974,
- /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 808, 1022, 1274,
- /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974,
- /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1011, 318, 107,
- /* 1320 */ 321, 955, 323, 325, 1225, 1211, 197, 1210, 1209, 330,
- /* 1330 */ 339, 1265, 340, 283, 116, 116, 116, 116, 115, 115,
- /* 1340 */ 114, 114, 114, 113, 414, 1286, 1323, 1261, 1471, 1272,
- /* 1350 */ 520, 218, 521, 1329, 116, 116, 116, 116, 115, 115,
- /* 1360 */ 114, 114, 114, 113, 414, 1192, 1184, 1173, 1172, 1174,
- /* 1370 */ 1494, 1488, 459, 256, 383, 1258, 342, 199, 367, 344,
- /* 1380 */ 211, 195, 307, 444, 11, 346, 469, 333, 1308, 1316,
- /* 1390 */ 375, 427, 203, 360, 383, 1388, 188, 1387, 189, 120,
- /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
- /* 1410 */ 118, 118, 118, 1208, 1151, 300, 348, 1491, 245, 1148,
- /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
- /* 1430 */ 118, 118, 118, 198, 1435, 1433, 524, 78, 391, 163,
- /* 1440 */ 82, 1393, 438, 173, 81, 105, 526, 1313, 4, 35,
- /* 1450 */ 157, 116, 116, 116, 116, 115, 115, 114, 114, 114,
- /* 1460 */ 113, 414, 529, 165, 93, 430, 1305, 168, 169, 431,
- /* 1470 */ 462, 116, 116, 116, 116, 115, 115, 114, 114, 114,
- /* 1480 */ 113, 414, 170, 171, 221, 415, 372, 437, 1319, 177,
- /* 1490 */ 374, 36, 451, 225, 1382, 87, 457, 523, 257, 1404,
- /* 1500 */ 316, 105, 526, 227, 4, 182, 460, 160, 320, 228,
- /* 1510 */ 377, 1175, 475, 229, 1228, 404, 1227, 1226, 529, 827,
- /* 1520 */ 961, 1219, 378, 1200, 1199, 406, 103, 103, 1218, 332,
- /* 1530 */ 8, 281, 1198, 104, 1503, 415, 536, 535, 486, 282,
- /* 1540 */ 951, 415, 489, 495, 92, 244, 1269, 341, 243, 122,
- /* 1550 */ 1270, 343, 514, 523, 1268, 1462, 10, 288, 525, 345,
- /* 1560 */ 1461, 354, 99, 352, 503, 94, 1267, 347, 1251, 502,
- /* 1570 */ 498, 951, 951, 953, 954, 27, 961, 1250, 194, 358,
- /* 1580 */ 251, 359, 103, 103, 1181, 34, 537, 1110, 252, 104,
- /* 1590 */ 254, 415, 536, 535, 255, 1368, 951, 1420, 286, 538,
- /* 1600 */ 1170, 1165, 1421, 135, 1419, 1418, 149, 150, 279, 784,
- /* 1610 */ 416, 196, 151, 290, 210, 200, 77, 385, 269, 386,
- /* 1620 */ 133, 162, 935, 1021, 201, 1019, 153, 951, 951, 953,
- /* 1630 */ 954, 27, 1480, 1104, 417, 164, 217, 268, 859, 166,
- /* 1640 */ 306, 1035, 366, 366, 365, 253, 363, 220, 172, 797,
- /* 1650 */ 939, 155, 105, 526, 393, 4, 395, 174, 156, 83,
- /* 1660 */ 1038, 84, 213, 85, 294, 222, 86, 223, 1034, 529,
- /* 1670 */ 144, 18, 293, 224, 315, 456, 241, 1027, 1145, 178,
- /* 1680 */ 226, 179, 37, 799, 334, 461, 230, 465, 470, 838,
- /* 1690 */ 180, 88, 415, 19, 280, 328, 20, 89, 90, 158,
- /* 1700 */ 191, 477, 215, 1097, 523, 204, 192, 987, 91, 1070,
- /* 1710 */ 152, 39, 485, 154, 1071, 503, 40, 488, 205, 260,
- /* 1720 */ 504, 262, 105, 526, 214, 4, 908, 961, 183, 240,
- /* 1730 */ 903, 107, 1086, 103, 103, 21, 22, 1088, 23, 529,
- /* 1740 */ 104, 24, 415, 536, 535, 1090, 1093, 951, 1094, 25,
- /* 1750 */ 1074, 33, 7, 26, 510, 1002, 247, 186, 384, 95,
- /* 1760 */ 988, 986, 415, 288, 525, 990, 1044, 246, 1043, 991,
- /* 1770 */ 28, 41, 530, 956, 523, 809, 106, 29, 951, 951,
- /* 1780 */ 953, 954, 27, 869, 361, 503, 422, 248, 364, 1105,
- /* 1790 */ 502, 249, 1161, 1496, 1495, 1161, 1161, 961, 1161, 1161,
- /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161,
- /* 1810 */ 104, 1161, 415, 536, 535, 1104, 417, 951, 1161, 268,
- /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161,
- /* 1830 */ 1161, 797, 1161, 1161, 1161, 1161, 105, 526, 1161, 4,
- /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951,
- /* 1850 */ 953, 954, 27, 529, 293, 1161, 1161, 1161, 1161, 1161,
- /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 415, 1161, 1161, 1161,
- /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 523, 1161,
- /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 526, 1161, 4,
- /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161,
- /* 1910 */ 1161, 961, 1161, 529, 1161, 1161, 1161, 103, 103, 880,
- /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 415, 1161, 1161, 1161,
- /* 1940 */ 384, 1161, 1161, 1161, 1161, 288, 525, 1161, 523, 1161,
- /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 526, 1161, 4,
- /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 422, 1161,
- /* 1970 */ 1161, 961, 1161, 529, 1161, 1161, 1161, 103, 103, 1161,
- /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 415, 366, 366, 365,
- /* 2000 */ 253, 363, 1161, 1161, 797, 1161, 1161, 1161, 523, 1161,
- /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294,
- /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161,
- /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161,
- /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161,
- /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161,
- /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214,
- /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161,
- /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2110 */ 1161, 1161, 1161, 384, 1161, 1161, 1161, 1161, 288, 525,
- /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2140 */ 1161, 422,
+ /* 0 */ 537, 339, 537, 1241, 1220, 537, 12, 537, 112, 109,
+ /* 10 */ 209, 537, 1241, 537, 1205, 462, 112, 109, 209, 386,
+ /* 20 */ 338, 462, 42, 42, 42, 42, 445, 42, 42, 70,
+ /* 30 */ 70, 922, 1208, 70, 70, 70, 70, 1443, 403, 923,
+ /* 40 */ 531, 531, 531, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 50 */ 984, 984, 117, 117, 118, 118, 118, 118, 425, 386,
+ /* 60 */ 1498, 542, 2, 1176, 1442, 519, 141, 1518, 289, 519,
+ /* 70 */ 134, 519, 95, 259, 495, 1215, 189, 1254, 518, 494,
+ /* 80 */ 484, 437, 296, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 90 */ 984, 984, 117, 117, 118, 118, 118, 118, 270, 116,
+ /* 100 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 110 */ 264, 264, 264, 264, 423, 1479, 352, 1481, 123, 351,
+ /* 120 */ 1479, 508, 1094, 534, 1034, 534, 1099, 386, 1099, 239,
+ /* 130 */ 206, 112, 109, 209, 96, 1094, 376, 219, 1094, 116,
+ /* 140 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 150 */ 480, 119, 120, 110, 1148, 1148, 991, 994, 984, 984,
+ /* 160 */ 117, 117, 118, 118, 118, 118, 353, 422, 1407, 264,
+ /* 170 */ 264, 114, 114, 114, 113, 418, 883, 121, 416, 416,
+ /* 180 */ 416, 882, 534, 116, 116, 116, 116, 115, 115, 114,
+ /* 190 */ 114, 114, 113, 418, 212, 415, 414, 386, 443, 383,
+ /* 200 */ 382, 118, 118, 118, 118, 111, 177, 116, 116, 116,
+ /* 210 */ 116, 115, 115, 114, 114, 114, 113, 418, 112, 109,
+ /* 220 */ 209, 119, 120, 110, 1148, 1148, 991, 994, 984, 984,
+ /* 230 */ 117, 117, 118, 118, 118, 118, 386, 438, 312, 1163,
+ /* 240 */ 1155, 80, 1155, 1127, 514, 79, 116, 116, 116, 116,
+ /* 250 */ 115, 115, 114, 114, 114, 113, 418, 514, 428, 418,
+ /* 260 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 270 */ 117, 118, 118, 118, 118, 428, 427, 116, 116, 116,
+ /* 280 */ 116, 115, 115, 114, 114, 114, 113, 418, 115, 115,
+ /* 290 */ 114, 114, 114, 113, 418, 1127, 1127, 1128, 1129, 1094,
+ /* 300 */ 258, 258, 192, 386, 408, 371, 1168, 326, 118, 118,
+ /* 310 */ 118, 118, 1094, 534, 374, 1094, 116, 116, 116, 116,
+ /* 320 */ 115, 115, 114, 114, 114, 113, 418, 119, 120, 110,
+ /* 330 */ 1148, 1148, 991, 994, 984, 984, 117, 117, 118, 118,
+ /* 340 */ 118, 118, 386, 354, 445, 428, 829, 238, 1127, 1128,
+ /* 350 */ 1129, 515, 1466, 116, 116, 116, 116, 115, 115, 114,
+ /* 360 */ 114, 114, 113, 418, 1127, 1467, 119, 120, 110, 1148,
+ /* 370 */ 1148, 991, 994, 984, 984, 117, 117, 118, 118, 118,
+ /* 380 */ 118, 1169, 82, 116, 116, 116, 116, 115, 115, 114,
+ /* 390 */ 114, 114, 113, 418, 405, 112, 109, 209, 161, 445,
+ /* 400 */ 250, 267, 336, 478, 331, 477, 236, 951, 1127, 386,
+ /* 410 */ 888, 1521, 329, 822, 852, 162, 274, 1127, 1128, 1129,
+ /* 420 */ 338, 169, 116, 116, 116, 116, 115, 115, 114, 114,
+ /* 430 */ 114, 113, 418, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 440 */ 984, 984, 117, 117, 118, 118, 118, 118, 386, 438,
+ /* 450 */ 312, 1502, 1112, 1176, 161, 288, 528, 311, 289, 883,
+ /* 460 */ 134, 1127, 1128, 1129, 882, 537, 143, 1254, 288, 528,
+ /* 470 */ 297, 275, 119, 120, 110, 1148, 1148, 991, 994, 984,
+ /* 480 */ 984, 117, 117, 118, 118, 118, 118, 70, 70, 116,
+ /* 490 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 500 */ 264, 264, 12, 264, 264, 395, 1127, 483, 1473, 1094,
+ /* 510 */ 204, 482, 6, 534, 1258, 386, 534, 1474, 825, 972,
+ /* 520 */ 504, 6, 1094, 500, 95, 1094, 534, 219, 116, 116,
+ /* 530 */ 116, 116, 115, 115, 114, 114, 114, 113, 418, 119,
+ /* 540 */ 120, 110, 1148, 1148, 991, 994, 984, 984, 117, 117,
+ /* 550 */ 118, 118, 118, 118, 386, 1339, 971, 422, 956, 1127,
+ /* 560 */ 1128, 1129, 231, 512, 1473, 475, 472, 471, 6, 113,
+ /* 570 */ 418, 825, 962, 298, 503, 470, 961, 452, 119, 120,
+ /* 580 */ 110, 1148, 1148, 991, 994, 984, 984, 117, 117, 118,
+ /* 590 */ 118, 118, 118, 395, 537, 116, 116, 116, 116, 115,
+ /* 600 */ 115, 114, 114, 114, 113, 418, 202, 961, 961, 963,
+ /* 610 */ 231, 971, 1127, 475, 472, 471, 13, 13, 951, 1127,
+ /* 620 */ 834, 386, 1207, 470, 399, 183, 447, 962, 462, 162,
+ /* 630 */ 397, 961, 1246, 1246, 116, 116, 116, 116, 115, 115,
+ /* 640 */ 114, 114, 114, 113, 418, 119, 120, 110, 1148, 1148,
+ /* 650 */ 991, 994, 984, 984, 117, 117, 118, 118, 118, 118,
+ /* 660 */ 386, 271, 961, 961, 963, 1127, 1128, 1129, 311, 433,
+ /* 670 */ 299, 1406, 1127, 1128, 1129, 178, 1471, 138, 162, 32,
+ /* 680 */ 6, 1127, 288, 528, 119, 120, 110, 1148, 1148, 991,
+ /* 690 */ 994, 984, 984, 117, 117, 118, 118, 118, 118, 909,
+ /* 700 */ 390, 116, 116, 116, 116, 115, 115, 114, 114, 114,
+ /* 710 */ 113, 418, 1127, 429, 817, 537, 1127, 265, 265, 981,
+ /* 720 */ 981, 992, 995, 324, 1055, 93, 520, 5, 338, 537,
+ /* 730 */ 534, 288, 528, 1522, 1127, 1128, 1129, 70, 70, 1056,
+ /* 740 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 750 */ 418, 70, 70, 1495, 1057, 537, 98, 1244, 1244, 264,
+ /* 760 */ 264, 908, 371, 1076, 1127, 1127, 1128, 1129, 817, 1127,
+ /* 770 */ 1128, 1129, 534, 519, 140, 863, 386, 13, 13, 456,
+ /* 780 */ 192, 193, 521, 453, 319, 864, 322, 284, 365, 430,
+ /* 790 */ 985, 402, 379, 1077, 1548, 101, 386, 1548, 3, 395,
+ /* 800 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 810 */ 117, 118, 118, 118, 118, 386, 451, 1127, 1128, 1129,
+ /* 820 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 830 */ 117, 118, 118, 118, 118, 1127, 1354, 1412, 1169, 119,
+ /* 840 */ 108, 110, 1148, 1148, 991, 994, 984, 984, 117, 117,
+ /* 850 */ 118, 118, 118, 118, 1412, 1414, 116, 116, 116, 116,
+ /* 860 */ 115, 115, 114, 114, 114, 113, 418, 272, 535, 1075,
+ /* 870 */ 877, 877, 337, 1492, 309, 462, 116, 116, 116, 116,
+ /* 880 */ 115, 115, 114, 114, 114, 113, 418, 537, 1127, 1128,
+ /* 890 */ 1129, 537, 360, 537, 356, 116, 116, 116, 116, 115,
+ /* 900 */ 115, 114, 114, 114, 113, 418, 386, 264, 264, 13,
+ /* 910 */ 13, 273, 1127, 13, 13, 13, 13, 304, 1253, 386,
+ /* 920 */ 534, 1077, 1549, 404, 1412, 1549, 496, 277, 451, 186,
+ /* 930 */ 1252, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 940 */ 117, 118, 118, 118, 118, 110, 1148, 1148, 991, 994,
+ /* 950 */ 984, 984, 117, 117, 118, 118, 118, 118, 105, 529,
+ /* 960 */ 537, 4, 1339, 264, 264, 1127, 1128, 1129, 1039, 1039,
+ /* 970 */ 459, 795, 796, 797, 536, 532, 534, 242, 301, 807,
+ /* 980 */ 303, 462, 69, 69, 451, 1353, 116, 116, 116, 116,
+ /* 990 */ 115, 115, 114, 114, 114, 113, 418, 1075, 419, 116,
+ /* 1000 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 1010 */ 526, 537, 1146, 192, 350, 105, 529, 537, 4, 497,
+ /* 1020 */ 162, 337, 1492, 310, 1249, 385, 1550, 372, 9, 462,
+ /* 1030 */ 242, 400, 532, 13, 13, 499, 971, 843, 436, 70,
+ /* 1040 */ 70, 359, 103, 103, 8, 339, 278, 187, 278, 104,
+ /* 1050 */ 1127, 419, 539, 538, 1339, 419, 961, 302, 1339, 1172,
+ /* 1060 */ 1, 1, 542, 2, 1176, 1146, 1146, 526, 476, 289,
+ /* 1070 */ 30, 134, 317, 288, 528, 285, 844, 1014, 1254, 276,
+ /* 1080 */ 1472, 506, 410, 1194, 6, 207, 505, 961, 961, 963,
+ /* 1090 */ 964, 27, 449, 971, 415, 414, 234, 233, 232, 103,
+ /* 1100 */ 103, 31, 1152, 1127, 1128, 1129, 104, 1154, 419, 539,
+ /* 1110 */ 538, 264, 264, 961, 1399, 1153, 264, 264, 1470, 1146,
+ /* 1120 */ 537, 216, 6, 401, 534, 1197, 392, 458, 406, 534,
+ /* 1130 */ 537, 485, 358, 537, 261, 537, 1339, 907, 219, 1155,
+ /* 1140 */ 467, 1155, 50, 50, 961, 961, 963, 964, 27, 1497,
+ /* 1150 */ 1116, 421, 70, 70, 268, 70, 70, 13, 13, 369,
+ /* 1160 */ 369, 368, 253, 366, 264, 264, 804, 235, 422, 105,
+ /* 1170 */ 529, 516, 4, 287, 487, 510, 493, 534, 486, 213,
+ /* 1180 */ 1055, 294, 490, 384, 1127, 450, 532, 338, 413, 293,
+ /* 1190 */ 522, 417, 335, 1036, 509, 1056, 107, 1036, 16, 16,
+ /* 1200 */ 1469, 1094, 334, 1105, 6, 411, 1145, 264, 264, 419,
+ /* 1210 */ 1057, 102, 511, 100, 1094, 264, 264, 1094, 922, 215,
+ /* 1220 */ 534, 526, 907, 264, 264, 208, 923, 154, 534, 457,
+ /* 1230 */ 156, 525, 391, 142, 218, 506, 534, 1127, 1128, 1129,
+ /* 1240 */ 507, 139, 1131, 38, 214, 530, 392, 971, 329, 1454,
+ /* 1250 */ 907, 1105, 537, 103, 103, 105, 529, 537, 4, 537,
+ /* 1260 */ 104, 424, 419, 539, 538, 537, 502, 961, 517, 537,
+ /* 1270 */ 1072, 537, 532, 373, 54, 54, 288, 528, 387, 55,
+ /* 1280 */ 55, 15, 15, 288, 528, 17, 136, 44, 44, 1451,
+ /* 1290 */ 537, 56, 56, 57, 57, 419, 1131, 291, 961, 961,
+ /* 1300 */ 963, 964, 27, 393, 163, 537, 426, 526, 263, 206,
+ /* 1310 */ 208, 517, 58, 58, 235, 440, 842, 841, 197, 105,
+ /* 1320 */ 529, 506, 4, 1033, 439, 1033, 505, 59, 59, 308,
+ /* 1330 */ 849, 850, 95, 971, 537, 907, 532, 948, 832, 103,
+ /* 1340 */ 103, 105, 529, 537, 4, 1021, 104, 537, 419, 539,
+ /* 1350 */ 538, 1116, 421, 961, 537, 268, 60, 60, 532, 419,
+ /* 1360 */ 369, 369, 368, 253, 366, 61, 61, 804, 965, 45,
+ /* 1370 */ 45, 526, 537, 1032, 1277, 1032, 46, 46, 537, 391,
+ /* 1380 */ 213, 419, 294, 266, 961, 961, 963, 964, 27, 292,
+ /* 1390 */ 293, 295, 832, 526, 48, 48, 1290, 971, 1289, 1021,
+ /* 1400 */ 49, 49, 432, 103, 103, 887, 953, 537, 1457, 241,
+ /* 1410 */ 104, 305, 419, 539, 538, 925, 926, 961, 444, 971,
+ /* 1420 */ 215, 241, 965, 1224, 537, 103, 103, 1431, 154, 62,
+ /* 1430 */ 62, 156, 104, 1430, 419, 539, 538, 97, 529, 961,
+ /* 1440 */ 4, 537, 454, 537, 314, 214, 63, 63, 961, 961,
+ /* 1450 */ 963, 964, 27, 537, 532, 446, 1286, 318, 241, 537,
+ /* 1460 */ 321, 323, 325, 64, 64, 14, 14, 1237, 537, 1223,
+ /* 1470 */ 961, 961, 963, 964, 27, 65, 65, 419, 537, 387,
+ /* 1480 */ 537, 125, 125, 537, 288, 528, 537, 1486, 537, 526,
+ /* 1490 */ 66, 66, 313, 524, 537, 95, 468, 1221, 1511, 237,
+ /* 1500 */ 51, 51, 67, 67, 330, 68, 68, 426, 52, 52,
+ /* 1510 */ 149, 149, 1222, 340, 341, 971, 150, 150, 1298, 463,
+ /* 1520 */ 327, 103, 103, 95, 537, 1338, 1273, 537, 104, 537,
+ /* 1530 */ 419, 539, 538, 1284, 537, 961, 268, 283, 523, 1344,
+ /* 1540 */ 1204, 369, 369, 368, 253, 366, 75, 75, 804, 53,
+ /* 1550 */ 53, 71, 71, 537, 1196, 537, 126, 126, 537, 1017,
+ /* 1560 */ 537, 213, 237, 294, 537, 1185, 961, 961, 963, 964,
+ /* 1570 */ 27, 293, 537, 1184, 537, 72, 72, 127, 127, 1186,
+ /* 1580 */ 128, 128, 124, 124, 1505, 537, 148, 148, 537, 256,
+ /* 1590 */ 195, 537, 1270, 537, 147, 147, 132, 132, 537, 11,
+ /* 1600 */ 537, 215, 537, 199, 343, 345, 347, 131, 131, 154,
+ /* 1610 */ 129, 129, 156, 130, 130, 74, 74, 537, 370, 1323,
+ /* 1620 */ 76, 76, 73, 73, 43, 43, 214, 431, 211, 1331,
+ /* 1630 */ 300, 916, 880, 815, 241, 107, 137, 307, 881, 47,
+ /* 1640 */ 47, 107, 473, 378, 203, 448, 333, 1403, 1220, 1402,
+ /* 1650 */ 349, 190, 527, 191, 363, 198, 1508, 1163, 245, 165,
+ /* 1660 */ 387, 1450, 1448, 1160, 78, 288, 528, 1408, 81, 394,
+ /* 1670 */ 82, 442, 175, 159, 167, 93, 1328, 35, 1320, 434,
+ /* 1680 */ 170, 171, 172, 173, 435, 466, 221, 375, 426, 377,
+ /* 1690 */ 1334, 179, 455, 441, 1397, 225, 87, 36, 461, 1419,
+ /* 1700 */ 316, 257, 227, 184, 320, 464, 228, 479, 1187, 229,
+ /* 1710 */ 380, 1240, 1239, 407, 1238, 1212, 834, 332, 1231, 381,
+ /* 1720 */ 409, 1211, 204, 1210, 1491, 498, 1520, 1281, 92, 281,
+ /* 1730 */ 1230, 489, 282, 492, 342, 243, 1282, 344, 244, 1280,
+ /* 1740 */ 346, 412, 1279, 1477, 348, 122, 1476, 517, 10, 357,
+ /* 1750 */ 286, 1305, 1304, 99, 1383, 94, 501, 251, 1193, 34,
+ /* 1760 */ 1263, 355, 540, 194, 1262, 361, 362, 1122, 252, 254,
+ /* 1770 */ 255, 388, 541, 1182, 1177, 151, 1435, 389, 1436, 1434,
+ /* 1780 */ 1433, 791, 152, 135, 279, 200, 201, 420, 196, 77,
+ /* 1790 */ 153, 290, 269, 210, 1031, 133, 1029, 945, 166, 155,
+ /* 1800 */ 217, 168, 866, 306, 220, 1045, 174, 949, 157, 396,
+ /* 1810 */ 83, 398, 176, 84, 85, 164, 86, 158, 1048, 222,
+ /* 1820 */ 223, 1044, 144, 18, 224, 315, 1037, 180, 241, 460,
+ /* 1830 */ 1157, 226, 181, 37, 806, 465, 334, 230, 328, 469,
+ /* 1840 */ 182, 88, 474, 19, 20, 160, 89, 280, 145, 90,
+ /* 1850 */ 481, 845, 1110, 146, 997, 205, 1080, 39, 91, 40,
+ /* 1860 */ 488, 1081, 915, 491, 260, 262, 185, 910, 240, 107,
+ /* 1870 */ 1100, 1096, 1098, 1104, 21, 1084, 33, 513, 247, 22,
+ /* 1880 */ 23, 24, 1103, 25, 188, 95, 1012, 998, 996, 26,
+ /* 1890 */ 1000, 1054, 7, 1053, 1001, 246, 28, 41, 533, 966,
+ /* 1900 */ 816, 106, 29, 367, 248, 249, 1513, 1512, 364, 1117,
+ /* 1910 */ 1173, 1173, 876,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 260, 261, 262, 260, 261, 262, 176, 177, 178, 179,
- /* 10 */ 180, 181, 184, 206, 209, 184, 186, 206, 188, 19,
- /* 20 */ 179, 281, 181, 213, 214, 195, 206, 186, 195, 188,
- /* 30 */ 195, 31, 222, 184, 206, 207, 195, 206, 207, 39,
- /* 40 */ 24, 209, 184, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 228, 229,
- /* 60 */ 184, 228, 229, 228, 229, 260, 261, 262, 192, 228,
- /* 70 */ 229, 241, 196, 242, 241, 59, 241, 205, 245, 246,
- /* 80 */ 184, 22, 241, 24, 254, 213, 54, 55, 56, 57,
- /* 90 */ 58, 256, 260, 261, 262, 254, 96, 97, 98, 99,
- /* 100 */ 100, 101, 102, 103, 104, 105, 106, 100, 101, 102,
- /* 110 */ 103, 104, 105, 106, 284, 203, 19, 221, 59, 102,
- /* 120 */ 103, 104, 105, 106, 59, 284, 110, 269, 96, 97,
- /* 130 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 94,
- /* 140 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 150 */ 53, 54, 55, 56, 57, 110, 184, 106, 73, 114,
- /* 160 */ 178, 179, 180, 181, 219, 184, 81, 22, 186, 110,
- /* 170 */ 188, 121, 122, 195, 109, 110, 111, 195, 206, 207,
- /* 180 */ 83, 184, 85, 54, 55, 56, 57, 206, 207, 277,
- /* 190 */ 145, 146, 147, 96, 97, 98, 99, 100, 101, 102,
- /* 200 */ 103, 104, 105, 106, 59, 120, 228, 229, 143, 184,
- /* 210 */ 228, 229, 184, 19, 242, 203, 131, 132, 221, 241,
- /* 220 */ 26, 184, 184, 241, 196, 96, 97, 98, 99, 100,
- /* 230 */ 101, 102, 103, 104, 105, 106, 254, 43, 44, 45,
- /* 240 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 250 */ 56, 57, 184, 184, 109, 110, 111, 105, 106, 184,
- /* 260 */ 200, 201, 202, 69, 184, 227, 284, 96, 97, 98,
- /* 270 */ 99, 100, 101, 102, 103, 104, 105, 106, 297, 298,
- /* 280 */ 255, 206, 207, 19, 272, 59, 206, 207, 184, 277,
- /* 290 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 300 */ 106, 184, 259, 19, 184, 100, 101, 43, 44, 45,
- /* 310 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 320 */ 56, 57, 242, 206, 207, 184, 100, 101, 138, 292,
- /* 330 */ 293, 67, 76, 296, 108, 109, 110, 111, 295, 113,
- /* 340 */ 84, 59, 86, 22, 26, 89, 156, 121, 224, 225,
- /* 350 */ 145, 19, 147, 59, 72, 256, 24, 184, 290, 291,
- /* 360 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 370 */ 106, 145, 297, 147, 299, 43, 44, 45, 46, 47,
- /* 380 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 390 */ 106, 109, 110, 111, 138, 184, 112, 113, 114, 115,
- /* 400 */ 116, 117, 118, 109, 110, 111, 112, 59, 124, 115,
- /* 410 */ 116, 117, 292, 293, 297, 298, 296, 206, 207, 125,
- /* 420 */ 72, 100, 101, 184, 46, 47, 48, 49, 96, 97,
- /* 430 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 59,
- /* 440 */ 200, 201, 202, 184, 59, 206, 207, 46, 19, 131,
- /* 450 */ 132, 278, 23, 242, 184, 184, 76, 109, 110, 111,
- /* 460 */ 224, 225, 251, 81, 84, 184, 86, 22, 23, 89,
- /* 470 */ 184, 26, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 480 */ 51, 52, 53, 54, 55, 56, 57, 102, 184, 109,
- /* 490 */ 110, 111, 114, 184, 109, 110, 111, 184, 227, 184,
- /* 500 */ 230, 184, 22, 264, 195, 59, 22, 184, 195, 108,
- /* 510 */ 206, 207, 59, 131, 132, 206, 207, 184, 138, 206,
- /* 520 */ 207, 206, 207, 206, 207, 96, 97, 98, 99, 100,
- /* 530 */ 101, 102, 103, 104, 105, 106, 255, 228, 229, 59,
- /* 540 */ 95, 228, 229, 59, 184, 19, 242, 94, 184, 23,
- /* 550 */ 241, 242, 184, 282, 241, 242, 110, 242, 184, 242,
- /* 560 */ 251, 59, 109, 110, 196, 184, 251, 114, 251, 43,
- /* 570 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 580 */ 54, 55, 56, 57, 259, 217, 12, 219, 255, 109,
- /* 590 */ 110, 111, 203, 109, 110, 111, 184, 22, 145, 146,
- /* 600 */ 147, 27, 112, 22, 230, 115, 116, 117, 227, 19,
- /* 610 */ 59, 109, 110, 111, 291, 125, 42, 35, 206, 207,
- /* 620 */ 295, 184, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 630 */ 104, 105, 106, 26, 59, 233, 46, 63, 136, 184,
- /* 640 */ 59, 184, 19, 206, 207, 243, 23, 73, 66, 260,
- /* 650 */ 261, 262, 59, 184, 242, 195, 74, 220, 184, 184,
- /* 660 */ 109, 110, 111, 206, 207, 184, 43, 44, 45, 46,
- /* 670 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 680 */ 57, 206, 207, 76, 109, 110, 111, 136, 228, 229,
- /* 690 */ 109, 110, 111, 86, 184, 220, 89, 21, 108, 230,
- /* 700 */ 184, 241, 109, 110, 111, 123, 184, 127, 184, 129,
- /* 710 */ 130, 184, 195, 184, 124, 184, 198, 199, 184, 96,
- /* 720 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
- /* 730 */ 206, 207, 11, 206, 207, 206, 207, 206, 207, 19,
- /* 740 */ 206, 207, 184, 23, 220, 228, 229, 220, 81, 220,
- /* 750 */ 195, 220, 287, 288, 220, 195, 80, 195, 241, 201,
- /* 760 */ 202, 184, 73, 43, 44, 45, 46, 47, 48, 49,
- /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 201, 202,
- /* 780 */ 113, 195, 184, 228, 229, 120, 121, 122, 228, 229,
- /* 790 */ 228, 229, 116, 16, 23, 184, 241, 26, 131, 132,
- /* 800 */ 278, 241, 19, 241, 22, 23, 184, 189, 26, 120,
- /* 810 */ 121, 122, 184, 26, 228, 229, 96, 97, 98, 99,
- /* 820 */ 100, 101, 102, 103, 104, 105, 106, 241, 270, 153,
- /* 830 */ 66, 228, 229, 229, 206, 207, 19, 184, 228, 229,
- /* 840 */ 23, 16, 121, 122, 241, 241, 82, 270, 29, 227,
- /* 850 */ 252, 241, 33, 19, 77, 91, 79, 184, 22, 23,
- /* 860 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 870 */ 53, 54, 55, 56, 57, 12, 184, 95, 184, 206,
- /* 880 */ 207, 76, 111, 184, 65, 267, 184, 184, 184, 271,
- /* 890 */ 27, 86, 109, 184, 89, 120, 121, 122, 206, 207,
- /* 900 */ 206, 207, 77, 139, 79, 42, 184, 136, 206, 207,
- /* 910 */ 206, 207, 184, 96, 97, 98, 99, 100, 101, 102,
- /* 920 */ 103, 104, 105, 106, 184, 138, 63, 184, 206, 207,
- /* 930 */ 153, 95, 184, 19, 206, 207, 227, 23, 7, 8,
- /* 940 */ 9, 293, 293, 109, 296, 296, 206, 207, 215, 206,
- /* 950 */ 207, 184, 253, 19, 206, 207, 253, 43, 44, 45,
- /* 960 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 970 */ 56, 57, 184, 206, 207, 184, 184, 43, 44, 45,
- /* 980 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 990 */ 56, 57, 184, 22, 23, 184, 59, 206, 207, 184,
- /* 1000 */ 184, 238, 184, 240, 184, 22, 184, 184, 157, 158,
- /* 1010 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 1020 */ 106, 206, 207, 184, 206, 207, 206, 207, 206, 207,
- /* 1030 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 1040 */ 106, 184, 59, 227, 252, 184, 293, 110, 184, 296,
- /* 1050 */ 184, 184, 184, 230, 184, 184, 184, 15, 184, 184,
- /* 1060 */ 184, 253, 184, 206, 207, 184, 95, 206, 207, 102,
- /* 1070 */ 206, 207, 206, 207, 206, 207, 206, 207, 206, 207,
- /* 1080 */ 206, 207, 206, 207, 184, 151, 184, 206, 207, 278,
- /* 1090 */ 184, 252, 184, 110, 184, 128, 184, 198, 199, 184,
- /* 1100 */ 133, 184, 60, 26, 184, 19, 206, 207, 206, 207,
- /* 1110 */ 131, 132, 206, 207, 206, 207, 206, 207, 206, 207,
- /* 1120 */ 253, 206, 207, 206, 207, 19, 206, 207, 253, 43,
- /* 1130 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1140 */ 54, 55, 56, 57, 184, 26, 184, 26, 184, 43,
- /* 1150 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1160 */ 54, 55, 56, 57, 285, 286, 206, 207, 206, 207,
- /* 1170 */ 206, 207, 184, 31, 184, 293, 184, 293, 296, 184,
- /* 1180 */ 296, 39, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 1190 */ 104, 105, 106, 184, 206, 207, 206, 207, 206, 207,
- /* 1200 */ 184, 22, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 1210 */ 104, 105, 106, 184, 245, 246, 184, 26, 23, 142,
- /* 1220 */ 128, 26, 206, 207, 150, 133, 152, 22, 22, 24,
- /* 1230 */ 111, 23, 53, 184, 26, 206, 207, 151, 206, 207,
- /* 1240 */ 119, 24, 122, 23, 23, 23, 26, 26, 26, 23,
- /* 1250 */ 23, 23, 26, 26, 26, 136, 23, 19, 22, 26,
- /* 1260 */ 113, 114, 24, 114, 144, 184, 59, 61, 7, 8,
- /* 1270 */ 59, 23, 23, 124, 26, 26, 23, 19, 145, 26,
- /* 1280 */ 147, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 1290 */ 52, 53, 54, 55, 56, 57, 145, 23, 147, 184,
- /* 1300 */ 26, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 1310 */ 52, 53, 54, 55, 56, 57, 23, 110, 184, 26,
- /* 1320 */ 184, 110, 184, 184, 184, 215, 135, 215, 184, 184,
- /* 1330 */ 184, 247, 184, 244, 96, 97, 98, 99, 100, 101,
- /* 1340 */ 102, 103, 104, 105, 106, 184, 184, 184, 301, 184,
- /* 1350 */ 184, 134, 225, 184, 96, 97, 98, 99, 100, 101,
- /* 1360 */ 102, 103, 104, 105, 106, 184, 184, 184, 184, 184,
- /* 1370 */ 134, 184, 274, 273, 19, 244, 244, 204, 182, 244,
- /* 1380 */ 283, 231, 279, 279, 232, 244, 210, 209, 235, 235,
- /* 1390 */ 235, 248, 218, 234, 19, 209, 238, 209, 238, 44,
- /* 1400 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 1410 */ 55, 56, 57, 214, 60, 248, 248, 187, 134, 38,
- /* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 1430 */ 55, 56, 57, 232, 191, 191, 266, 280, 191, 283,
- /* 1440 */ 143, 269, 108, 22, 280, 19, 20, 258, 22, 257,
- /* 1450 */ 43, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- /* 1460 */ 105, 106, 36, 223, 142, 18, 235, 226, 226, 191,
- /* 1470 */ 18, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- /* 1480 */ 105, 106, 226, 226, 190, 59, 235, 235, 223, 223,
- /* 1490 */ 258, 257, 191, 190, 235, 150, 62, 71, 191, 276,
- /* 1500 */ 275, 19, 20, 190, 22, 22, 211, 81, 191, 190,
- /* 1510 */ 211, 191, 108, 190, 208, 64, 208, 208, 36, 119,
- /* 1520 */ 94, 216, 211, 208, 210, 106, 100, 101, 216, 208,
- /* 1530 */ 48, 268, 208, 107, 208, 109, 110, 111, 211, 268,
- /* 1540 */ 114, 59, 211, 137, 108, 88, 250, 249, 191, 141,
- /* 1550 */ 250, 249, 138, 71, 250, 300, 22, 131, 132, 249,
- /* 1560 */ 300, 191, 150, 238, 82, 140, 250, 249, 239, 87,
- /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 239, 237, 236,
- /* 1580 */ 25, 235, 100, 101, 194, 26, 193, 13, 185, 107,
- /* 1590 */ 185, 109, 110, 111, 6, 263, 114, 203, 265, 183,
- /* 1600 */ 183, 183, 203, 212, 203, 203, 197, 197, 212, 4,
- /* 1610 */ 3, 22, 197, 155, 15, 204, 203, 289, 93, 289,
- /* 1620 */ 16, 286, 132, 23, 204, 23, 123, 145, 146, 147,
- /* 1630 */ 148, 149, 0, 1, 2, 143, 24, 5, 20, 135,
- /* 1640 */ 16, 1, 10, 11, 12, 13, 14, 137, 135, 17,
- /* 1650 */ 144, 123, 19, 20, 61, 22, 37, 143, 123, 53,
- /* 1660 */ 109, 53, 30, 53, 32, 34, 53, 134, 1, 36,
- /* 1670 */ 5, 22, 40, 108, 153, 41, 26, 68, 75, 68,
- /* 1680 */ 134, 108, 24, 20, 124, 19, 118, 67, 67, 28,
- /* 1690 */ 22, 22, 59, 22, 67, 23, 22, 22, 142, 37,
- /* 1700 */ 23, 22, 70, 23, 71, 157, 23, 23, 26, 23,
- /* 1710 */ 78, 22, 24, 81, 23, 82, 22, 24, 134, 23,
- /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 22, 34,
- /* 1730 */ 136, 26, 85, 100, 101, 34, 34, 83, 34, 36,
- /* 1740 */ 107, 34, 109, 110, 111, 75, 90, 114, 75, 34,
- /* 1750 */ 23, 22, 44, 34, 24, 23, 22, 26, 126, 26,
- /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 11,
- /* 1770 */ 22, 22, 26, 23, 71, 23, 22, 22, 145, 146,
- /* 1780 */ 147, 148, 149, 128, 23, 82, 154, 134, 15, 1,
- /* 1790 */ 87, 134, 302, 134, 134, 302, 302, 94, 302, 302,
- /* 1800 */ 302, 302, 302, 100, 101, 302, 302, 302, 302, 302,
- /* 1810 */ 107, 302, 109, 110, 111, 1, 2, 114, 302, 5,
- /* 1820 */ 302, 302, 302, 302, 10, 11, 12, 13, 14, 302,
- /* 1830 */ 302, 17, 302, 302, 302, 302, 19, 20, 302, 22,
- /* 1840 */ 302, 302, 302, 302, 30, 302, 32, 302, 145, 146,
- /* 1850 */ 147, 148, 149, 36, 40, 302, 302, 302, 302, 302,
- /* 1860 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 1870 */ 302, 302, 302, 302, 302, 302, 59, 302, 302, 302,
- /* 1880 */ 302, 302, 302, 302, 70, 302, 302, 302, 71, 302,
- /* 1890 */ 302, 302, 78, 302, 302, 81, 19, 20, 302, 22,
- /* 1900 */ 302, 302, 302, 302, 302, 302, 92, 302, 302, 302,
- /* 1910 */ 302, 94, 302, 36, 302, 302, 302, 100, 101, 102,
- /* 1920 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 1930 */ 302, 114, 302, 302, 302, 302, 59, 302, 302, 302,
- /* 1940 */ 126, 302, 302, 302, 302, 131, 132, 302, 71, 302,
- /* 1950 */ 302, 302, 302, 302, 302, 302, 19, 20, 302, 22,
- /* 1960 */ 302, 302, 145, 146, 147, 148, 149, 302, 154, 302,
- /* 1970 */ 302, 94, 302, 36, 302, 302, 302, 100, 101, 302,
- /* 1980 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 1990 */ 302, 114, 5, 302, 302, 302, 59, 10, 11, 12,
- /* 2000 */ 13, 14, 302, 302, 17, 302, 302, 302, 71, 302,
- /* 2010 */ 302, 302, 302, 302, 302, 302, 302, 30, 302, 32,
- /* 2020 */ 302, 302, 145, 146, 147, 148, 149, 40, 302, 302,
- /* 2030 */ 302, 94, 302, 302, 302, 302, 302, 100, 101, 302,
- /* 2040 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 2050 */ 302, 114, 302, 302, 302, 302, 302, 70, 302, 302,
- /* 2060 */ 302, 302, 302, 302, 302, 78, 302, 302, 81, 302,
- /* 2070 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 92,
- /* 2080 */ 302, 302, 145, 146, 147, 148, 149, 302, 302, 302,
- /* 2090 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2100 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2110 */ 302, 302, 302, 126, 302, 302, 302, 302, 131, 132,
- /* 2120 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2130 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2140 */ 302, 154, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2150 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2160 */ 302, 302, 302, 302, 302, 302, 302, 302, 302,
+ /* 0 */ 187, 187, 187, 216, 217, 187, 206, 187, 264, 265,
+ /* 10 */ 266, 187, 225, 187, 209, 187, 264, 265, 266, 19,
+ /* 20 */ 187, 187, 209, 210, 209, 210, 187, 209, 210, 209,
+ /* 30 */ 210, 31, 209, 209, 210, 209, 210, 285, 224, 39,
+ /* 40 */ 203, 204, 205, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 230, 19,
+ /* 60 */ 181, 182, 183, 184, 230, 245, 233, 208, 189, 245,
+ /* 70 */ 191, 245, 26, 206, 254, 216, 276, 198, 254, 198,
+ /* 80 */ 254, 281, 187, 43, 44, 45, 46, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 259, 99,
+ /* 100 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 231, 232, 231, 232, 286, 302, 303, 302, 22, 304,
+ /* 120 */ 302, 303, 76, 244, 11, 244, 86, 19, 88, 248,
+ /* 130 */ 249, 264, 265, 266, 26, 89, 198, 258, 92, 99,
+ /* 140 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 150 */ 105, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 160 */ 52, 53, 54, 55, 56, 57, 212, 288, 273, 231,
+ /* 170 */ 232, 105, 106, 107, 108, 109, 131, 69, 203, 204,
+ /* 180 */ 205, 136, 244, 99, 100, 101, 102, 103, 104, 105,
+ /* 190 */ 106, 107, 108, 109, 15, 103, 104, 19, 260, 103,
+ /* 200 */ 104, 54, 55, 56, 57, 58, 22, 99, 100, 101,
+ /* 210 */ 102, 103, 104, 105, 106, 107, 108, 109, 264, 265,
+ /* 220 */ 266, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 230 */ 52, 53, 54, 55, 56, 57, 19, 124, 125, 60,
+ /* 240 */ 148, 24, 150, 59, 187, 67, 99, 100, 101, 102,
+ /* 250 */ 103, 104, 105, 106, 107, 108, 109, 187, 187, 109,
+ /* 260 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 270 */ 53, 54, 55, 56, 57, 204, 205, 99, 100, 101,
+ /* 280 */ 102, 103, 104, 105, 106, 107, 108, 109, 103, 104,
+ /* 290 */ 105, 106, 107, 108, 109, 59, 112, 113, 114, 76,
+ /* 300 */ 231, 232, 187, 19, 19, 22, 23, 23, 54, 55,
+ /* 310 */ 56, 57, 89, 244, 199, 92, 99, 100, 101, 102,
+ /* 320 */ 103, 104, 105, 106, 107, 108, 109, 43, 44, 45,
+ /* 330 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 340 */ 56, 57, 19, 212, 187, 274, 23, 26, 112, 113,
+ /* 350 */ 114, 294, 295, 99, 100, 101, 102, 103, 104, 105,
+ /* 360 */ 106, 107, 108, 109, 59, 295, 43, 44, 45, 46,
+ /* 370 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 380 */ 57, 98, 146, 99, 100, 101, 102, 103, 104, 105,
+ /* 390 */ 106, 107, 108, 109, 109, 264, 265, 266, 187, 187,
+ /* 400 */ 115, 116, 117, 118, 119, 120, 121, 73, 59, 19,
+ /* 410 */ 105, 23, 127, 23, 26, 81, 259, 112, 113, 114,
+ /* 420 */ 187, 72, 99, 100, 101, 102, 103, 104, 105, 106,
+ /* 430 */ 107, 108, 109, 43, 44, 45, 46, 47, 48, 49,
+ /* 440 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 124,
+ /* 450 */ 125, 182, 23, 184, 187, 134, 135, 123, 189, 131,
+ /* 460 */ 191, 112, 113, 114, 136, 187, 233, 198, 134, 135,
+ /* 470 */ 198, 259, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 480 */ 51, 52, 53, 54, 55, 56, 57, 209, 210, 99,
+ /* 490 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 500 */ 231, 232, 206, 231, 232, 187, 59, 296, 297, 76,
+ /* 510 */ 160, 161, 301, 244, 232, 19, 244, 297, 59, 23,
+ /* 520 */ 87, 301, 89, 245, 26, 92, 244, 258, 99, 100,
+ /* 530 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 43,
+ /* 540 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 550 */ 54, 55, 56, 57, 19, 187, 97, 288, 23, 112,
+ /* 560 */ 113, 114, 115, 296, 297, 118, 119, 120, 301, 108,
+ /* 570 */ 109, 112, 113, 255, 141, 128, 117, 281, 43, 44,
+ /* 580 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ /* 590 */ 55, 56, 57, 187, 187, 99, 100, 101, 102, 103,
+ /* 600 */ 104, 105, 106, 107, 108, 109, 26, 148, 149, 150,
+ /* 610 */ 115, 97, 59, 118, 119, 120, 209, 210, 73, 59,
+ /* 620 */ 122, 19, 209, 128, 256, 72, 187, 113, 187, 81,
+ /* 630 */ 223, 117, 227, 228, 99, 100, 101, 102, 103, 104,
+ /* 640 */ 105, 106, 107, 108, 109, 43, 44, 45, 46, 47,
+ /* 650 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 660 */ 19, 255, 148, 149, 150, 112, 113, 114, 123, 124,
+ /* 670 */ 125, 230, 112, 113, 114, 22, 297, 22, 81, 22,
+ /* 680 */ 301, 59, 134, 135, 43, 44, 45, 46, 47, 48,
+ /* 690 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 139,
+ /* 700 */ 192, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ /* 710 */ 108, 109, 59, 116, 59, 187, 59, 231, 232, 46,
+ /* 720 */ 47, 48, 49, 16, 12, 145, 198, 22, 187, 187,
+ /* 730 */ 244, 134, 135, 222, 112, 113, 114, 209, 210, 27,
+ /* 740 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ /* 750 */ 109, 209, 210, 187, 42, 187, 154, 227, 228, 231,
+ /* 760 */ 232, 139, 22, 23, 59, 112, 113, 114, 113, 112,
+ /* 770 */ 113, 114, 244, 245, 233, 63, 19, 209, 210, 271,
+ /* 780 */ 187, 24, 254, 275, 77, 73, 79, 245, 195, 260,
+ /* 790 */ 117, 223, 199, 22, 23, 154, 19, 26, 22, 187,
+ /* 800 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 810 */ 53, 54, 55, 56, 57, 19, 187, 112, 113, 114,
+ /* 820 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 830 */ 53, 54, 55, 56, 57, 59, 263, 187, 98, 43,
+ /* 840 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 850 */ 54, 55, 56, 57, 204, 205, 99, 100, 101, 102,
+ /* 860 */ 103, 104, 105, 106, 107, 108, 109, 255, 130, 98,
+ /* 870 */ 132, 133, 299, 300, 198, 187, 99, 100, 101, 102,
+ /* 880 */ 103, 104, 105, 106, 107, 108, 109, 187, 112, 113,
+ /* 890 */ 114, 187, 241, 187, 243, 99, 100, 101, 102, 103,
+ /* 900 */ 104, 105, 106, 107, 108, 109, 19, 231, 232, 209,
+ /* 910 */ 210, 282, 59, 209, 210, 209, 210, 16, 230, 19,
+ /* 920 */ 244, 22, 23, 223, 274, 26, 19, 223, 187, 223,
+ /* 930 */ 198, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 940 */ 53, 54, 55, 56, 57, 45, 46, 47, 48, 49,
+ /* 950 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 20,
+ /* 960 */ 187, 22, 187, 231, 232, 112, 113, 114, 123, 124,
+ /* 970 */ 125, 7, 8, 9, 187, 36, 244, 24, 77, 21,
+ /* 980 */ 79, 187, 209, 210, 187, 263, 99, 100, 101, 102,
+ /* 990 */ 103, 104, 105, 106, 107, 108, 109, 98, 59, 99,
+ /* 1000 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 1010 */ 71, 187, 59, 187, 187, 19, 20, 187, 22, 112,
+ /* 1020 */ 81, 299, 300, 282, 230, 199, 291, 292, 22, 187,
+ /* 1030 */ 24, 256, 36, 209, 210, 187, 97, 35, 80, 209,
+ /* 1040 */ 210, 268, 103, 104, 48, 187, 220, 223, 222, 110,
+ /* 1050 */ 59, 112, 113, 114, 187, 59, 117, 156, 187, 179,
+ /* 1060 */ 180, 181, 182, 183, 184, 59, 113, 71, 66, 189,
+ /* 1070 */ 22, 191, 230, 134, 135, 245, 74, 119, 198, 282,
+ /* 1080 */ 297, 85, 224, 198, 301, 187, 90, 148, 149, 150,
+ /* 1090 */ 151, 152, 19, 97, 103, 104, 123, 124, 125, 103,
+ /* 1100 */ 104, 53, 111, 112, 113, 114, 110, 116, 112, 113,
+ /* 1110 */ 114, 231, 232, 117, 156, 124, 231, 232, 297, 113,
+ /* 1120 */ 187, 24, 301, 256, 244, 201, 202, 256, 126, 244,
+ /* 1130 */ 187, 198, 187, 187, 23, 187, 187, 26, 258, 148,
+ /* 1140 */ 19, 150, 209, 210, 148, 149, 150, 151, 152, 0,
+ /* 1150 */ 1, 2, 209, 210, 5, 209, 210, 209, 210, 10,
+ /* 1160 */ 11, 12, 13, 14, 231, 232, 17, 46, 288, 19,
+ /* 1170 */ 20, 223, 22, 236, 198, 66, 187, 244, 245, 30,
+ /* 1180 */ 12, 32, 198, 246, 59, 112, 36, 187, 245, 40,
+ /* 1190 */ 198, 245, 117, 29, 85, 27, 26, 33, 209, 210,
+ /* 1200 */ 297, 76, 127, 94, 301, 256, 26, 231, 232, 59,
+ /* 1210 */ 42, 153, 87, 155, 89, 231, 232, 92, 31, 70,
+ /* 1220 */ 244, 71, 26, 231, 232, 114, 39, 78, 244, 65,
+ /* 1230 */ 81, 63, 111, 233, 137, 85, 244, 112, 113, 114,
+ /* 1240 */ 90, 22, 59, 24, 95, 201, 202, 97, 127, 187,
+ /* 1250 */ 139, 142, 187, 103, 104, 19, 20, 187, 22, 187,
+ /* 1260 */ 110, 187, 112, 113, 114, 187, 141, 117, 141, 187,
+ /* 1270 */ 23, 187, 36, 26, 209, 210, 134, 135, 129, 209,
+ /* 1280 */ 210, 209, 210, 134, 135, 22, 159, 209, 210, 187,
+ /* 1290 */ 187, 209, 210, 209, 210, 59, 113, 187, 148, 149,
+ /* 1300 */ 150, 151, 152, 289, 290, 187, 157, 71, 248, 249,
+ /* 1310 */ 114, 141, 209, 210, 46, 125, 116, 117, 138, 19,
+ /* 1320 */ 20, 85, 22, 148, 61, 150, 90, 209, 210, 23,
+ /* 1330 */ 7, 8, 26, 97, 187, 139, 36, 147, 59, 103,
+ /* 1340 */ 104, 19, 20, 187, 22, 59, 110, 187, 112, 113,
+ /* 1350 */ 114, 1, 2, 117, 187, 5, 209, 210, 36, 59,
+ /* 1360 */ 10, 11, 12, 13, 14, 209, 210, 17, 59, 209,
+ /* 1370 */ 210, 71, 187, 148, 250, 150, 209, 210, 187, 111,
+ /* 1380 */ 30, 59, 32, 22, 148, 149, 150, 151, 152, 187,
+ /* 1390 */ 40, 187, 113, 71, 209, 210, 187, 97, 187, 113,
+ /* 1400 */ 209, 210, 187, 103, 104, 105, 23, 187, 187, 26,
+ /* 1410 */ 110, 187, 112, 113, 114, 83, 84, 117, 23, 97,
+ /* 1420 */ 70, 26, 113, 218, 187, 103, 104, 187, 78, 209,
+ /* 1430 */ 210, 81, 110, 187, 112, 113, 114, 19, 20, 117,
+ /* 1440 */ 22, 187, 187, 187, 187, 95, 209, 210, 148, 149,
+ /* 1450 */ 150, 151, 152, 187, 36, 23, 187, 187, 26, 187,
+ /* 1460 */ 187, 187, 187, 209, 210, 209, 210, 187, 187, 218,
+ /* 1470 */ 148, 149, 150, 151, 152, 209, 210, 59, 187, 129,
+ /* 1480 */ 187, 209, 210, 187, 134, 135, 187, 306, 187, 71,
+ /* 1490 */ 209, 210, 23, 228, 187, 26, 23, 187, 137, 26,
+ /* 1500 */ 209, 210, 209, 210, 187, 209, 210, 157, 209, 210,
+ /* 1510 */ 209, 210, 218, 187, 187, 97, 209, 210, 187, 278,
+ /* 1520 */ 23, 103, 104, 26, 187, 187, 187, 187, 110, 187,
+ /* 1530 */ 112, 113, 114, 187, 187, 117, 5, 247, 187, 187,
+ /* 1540 */ 187, 10, 11, 12, 13, 14, 209, 210, 17, 209,
+ /* 1550 */ 210, 209, 210, 187, 187, 187, 209, 210, 187, 23,
+ /* 1560 */ 187, 30, 26, 32, 187, 187, 148, 149, 150, 151,
+ /* 1570 */ 152, 40, 187, 187, 187, 209, 210, 209, 210, 187,
+ /* 1580 */ 209, 210, 209, 210, 187, 187, 209, 210, 187, 277,
+ /* 1590 */ 234, 187, 247, 187, 209, 210, 209, 210, 187, 235,
+ /* 1600 */ 187, 70, 187, 207, 247, 247, 247, 209, 210, 78,
+ /* 1610 */ 209, 210, 81, 209, 210, 209, 210, 187, 185, 238,
+ /* 1620 */ 209, 210, 209, 210, 209, 210, 95, 251, 287, 238,
+ /* 1630 */ 251, 23, 23, 23, 26, 26, 26, 283, 23, 209,
+ /* 1640 */ 210, 26, 213, 238, 221, 283, 212, 212, 217, 212,
+ /* 1650 */ 251, 241, 270, 241, 237, 235, 190, 60, 137, 287,
+ /* 1660 */ 129, 194, 194, 38, 284, 134, 135, 273, 284, 194,
+ /* 1670 */ 146, 111, 22, 43, 226, 145, 262, 261, 238, 18,
+ /* 1680 */ 229, 229, 229, 229, 194, 18, 193, 238, 157, 262,
+ /* 1690 */ 226, 226, 194, 238, 238, 193, 153, 261, 62, 280,
+ /* 1700 */ 279, 194, 193, 22, 194, 214, 193, 111, 194, 193,
+ /* 1710 */ 214, 211, 211, 64, 211, 211, 122, 211, 219, 214,
+ /* 1720 */ 109, 213, 160, 211, 300, 140, 211, 253, 111, 272,
+ /* 1730 */ 219, 214, 272, 214, 252, 194, 253, 252, 91, 253,
+ /* 1740 */ 252, 82, 253, 305, 252, 144, 305, 141, 22, 194,
+ /* 1750 */ 269, 257, 257, 153, 267, 143, 142, 25, 197, 26,
+ /* 1760 */ 242, 241, 196, 240, 242, 239, 238, 13, 188, 188,
+ /* 1770 */ 6, 293, 186, 186, 186, 200, 206, 293, 206, 206,
+ /* 1780 */ 206, 4, 200, 215, 215, 207, 207, 3, 22, 206,
+ /* 1790 */ 200, 158, 96, 15, 23, 16, 23, 135, 146, 126,
+ /* 1800 */ 24, 138, 20, 16, 140, 1, 138, 147, 126, 61,
+ /* 1810 */ 53, 37, 146, 53, 53, 290, 53, 126, 112, 34,
+ /* 1820 */ 137, 1, 5, 22, 111, 156, 68, 68, 26, 41,
+ /* 1830 */ 75, 137, 111, 24, 20, 19, 127, 121, 23, 67,
+ /* 1840 */ 22, 22, 67, 22, 22, 37, 22, 67, 23, 145,
+ /* 1850 */ 22, 28, 23, 23, 23, 137, 23, 22, 26, 22,
+ /* 1860 */ 24, 23, 112, 24, 23, 23, 22, 139, 34, 26,
+ /* 1870 */ 75, 88, 86, 75, 34, 23, 22, 24, 22, 34,
+ /* 1880 */ 34, 34, 93, 34, 26, 26, 23, 23, 23, 34,
+ /* 1890 */ 23, 23, 44, 23, 11, 26, 22, 22, 26, 23,
+ /* 1900 */ 23, 22, 22, 15, 137, 137, 137, 137, 23, 1,
+ /* 1910 */ 307, 307, 131, 307, 307, 307, 307, 307, 307, 307,
+ /* 1920 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1930 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1940 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1950 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1960 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1970 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1980 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1990 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2000 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2010 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2020 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2030 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2040 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2050 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2060 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2070 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2080 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2090 */ 307, 307,
};
-#define YY_SHIFT_COUNT (539)
+#define YY_SHIFT_COUNT (542)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (1987)
+#define YY_SHIFT_MAX (1908)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1814, 1632, 1987, 1426, 1426, 382, 1482, 1633, 1703, 1877,
- /* 10 */ 1877, 1877, 85, 0, 0, 264, 1106, 1877, 1877, 1877,
- /* 20 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 30 */ 226, 226, 380, 380, 294, 667, 382, 382, 382, 382,
- /* 40 */ 382, 382, 97, 194, 332, 429, 526, 623, 720, 817,
- /* 50 */ 914, 934, 1086, 1238, 1106, 1106, 1106, 1106, 1106, 1106,
- /* 60 */ 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106,
- /* 70 */ 1106, 1106, 1258, 1106, 1355, 1375, 1375, 1817, 1877, 1877,
- /* 80 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 90 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 100 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 110 */ 1937, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 120 */ 1877, 1877, 1877, 1877, 32, 129, 129, 129, 129, 129,
- /* 130 */ 171, 7, 17, 593, 676, 590, 593, 205, 205, 593,
- /* 140 */ 318, 318, 318, 318, 50, 152, 51, 2142, 2142, 284,
- /* 150 */ 284, 284, 65, 145, 282, 145, 145, 574, 574, 256,
- /* 160 */ 348, 445, 782, 593, 593, 593, 593, 593, 593, 593,
- /* 170 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 593,
- /* 180 */ 593, 593, 593, 593, 607, 607, 593, 721, 805, 805,
- /* 190 */ 446, 851, 851, 446, 190, 979, 2142, 2142, 2142, 453,
- /* 200 */ 45, 45, 480, 490, 484, 385, 575, 502, 551, 581,
- /* 210 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 689,
- /* 220 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 593,
- /* 230 */ 593, 593, 582, 582, 582, 593, 593, 593, 593, 771,
- /* 240 */ 593, 593, 593, 59, 764, 593, 593, 863, 593, 593,
- /* 250 */ 593, 593, 593, 593, 593, 593, 665, 819, 580, 16,
- /* 260 */ 16, 16, 16, 1119, 580, 580, 967, 321, 931, 1042,
- /* 270 */ 1077, 783, 783, 834, 1077, 1077, 834, 1121, 1195, 401,
- /* 280 */ 1142, 1142, 1142, 783, 787, 787, 1074, 1191, 1092, 1205,
- /* 290 */ 1354, 1284, 1284, 1381, 1381, 1284, 1297, 1334, 1421, 1407,
- /* 300 */ 1322, 1447, 1447, 1447, 1447, 1284, 1452, 1322, 1322, 1334,
- /* 310 */ 1421, 1407, 1407, 1322, 1284, 1452, 1345, 1434, 1284, 1452,
- /* 320 */ 1483, 1284, 1452, 1284, 1452, 1483, 1404, 1404, 1404, 1451,
- /* 330 */ 1483, 1404, 1400, 1404, 1451, 1404, 1404, 1483, 1419, 1419,
- /* 340 */ 1483, 1406, 1436, 1406, 1436, 1406, 1436, 1406, 1436, 1284,
- /* 350 */ 1457, 1457, 1408, 1414, 1534, 1284, 1412, 1408, 1425, 1431,
- /* 360 */ 1322, 1555, 1559, 1574, 1574, 1588, 1588, 1588, 2142, 2142,
- /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
- /* 380 */ 2142, 2142, 2142, 378, 777, 836, 971, 825, 775, 983,
- /* 390 */ 1208, 1179, 1217, 1120, 1220, 1206, 1221, 1222, 1226, 1227,
- /* 400 */ 1228, 1233, 937, 1147, 1261, 1149, 1207, 1248, 1249, 1253,
- /* 410 */ 1133, 1151, 1274, 1293, 1211, 1236, 1605, 1607, 1589, 1458,
- /* 420 */ 1599, 1525, 1604, 1600, 1602, 1490, 1492, 1503, 1612, 1504,
- /* 430 */ 1618, 1510, 1624, 1640, 1513, 1506, 1528, 1593, 1619, 1514,
- /* 440 */ 1606, 1608, 1610, 1613, 1535, 1551, 1631, 1533, 1667, 1665,
- /* 450 */ 1649, 1565, 1521, 1609, 1650, 1611, 1603, 1634, 1546, 1573,
- /* 460 */ 1658, 1663, 1666, 1560, 1568, 1668, 1620, 1669, 1671, 1672,
- /* 470 */ 1674, 1621, 1661, 1675, 1627, 1662, 1677, 1556, 1679, 1680,
- /* 480 */ 1548, 1683, 1684, 1682, 1686, 1689, 1688, 1691, 1694, 1693,
- /* 490 */ 1584, 1696, 1698, 1617, 1695, 1706, 1594, 1705, 1701, 1702,
- /* 500 */ 1704, 1707, 1647, 1670, 1654, 1708, 1673, 1656, 1715, 1727,
- /* 510 */ 1729, 1730, 1731, 1733, 1719, 1732, 1705, 1737, 1738, 1742,
- /* 520 */ 1743, 1741, 1745, 1734, 1758, 1748, 1749, 1750, 1752, 1754,
- /* 530 */ 1755, 1746, 1655, 1653, 1657, 1659, 1660, 1761, 1773, 1788,
+ /* 0 */ 1350, 1149, 1531, 939, 939, 548, 996, 1150, 1236, 1322,
+ /* 10 */ 1322, 1322, 334, 0, 0, 178, 777, 1322, 1322, 1322,
+ /* 20 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 30 */ 991, 991, 1125, 1125, 447, 597, 548, 548, 548, 548,
+ /* 40 */ 548, 548, 40, 108, 217, 284, 323, 390, 429, 496,
+ /* 50 */ 535, 602, 641, 757, 777, 777, 777, 777, 777, 777,
+ /* 60 */ 777, 777, 777, 777, 777, 777, 777, 777, 777, 777,
+ /* 70 */ 777, 777, 796, 777, 887, 900, 900, 1300, 1322, 1322,
+ /* 80 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 90 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 100 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 110 */ 1418, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 120 */ 1322, 1322, 1322, 1322, 147, 254, 254, 254, 254, 254,
+ /* 130 */ 84, 185, 66, 853, 958, 1121, 853, 92, 92, 853,
+ /* 140 */ 321, 321, 321, 321, 325, 350, 350, 461, 150, 1913,
+ /* 150 */ 1913, 285, 285, 285, 236, 184, 349, 184, 184, 712,
+ /* 160 */ 712, 433, 553, 771, 899, 853, 853, 853, 853, 853,
+ /* 170 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
+ /* 180 */ 853, 853, 853, 853, 853, 853, 46, 46, 853, 113,
+ /* 190 */ 223, 223, 1183, 1183, 1127, 1142, 1913, 1913, 1913, 459,
+ /* 200 */ 514, 514, 653, 495, 657, 305, 705, 560, 622, 776,
+ /* 210 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 545,
+ /* 220 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
+ /* 230 */ 853, 853, 1002, 1002, 1002, 853, 853, 853, 853, 1111,
+ /* 240 */ 853, 853, 853, 1006, 1109, 853, 853, 1168, 853, 853,
+ /* 250 */ 853, 853, 853, 853, 853, 853, 845, 1164, 738, 953,
+ /* 260 */ 953, 953, 953, 1196, 738, 738, 45, 96, 964, 179,
+ /* 270 */ 580, 907, 907, 1073, 580, 580, 1073, 498, 388, 1268,
+ /* 280 */ 1187, 1187, 1187, 907, 1170, 1170, 1058, 1180, 328, 1219,
+ /* 290 */ 1597, 1521, 1521, 1625, 1625, 1521, 1524, 1560, 1650, 1630,
+ /* 300 */ 1530, 1661, 1661, 1661, 1661, 1521, 1667, 1530, 1530, 1560,
+ /* 310 */ 1650, 1630, 1630, 1530, 1521, 1667, 1543, 1636, 1521, 1667,
+ /* 320 */ 1681, 1521, 1667, 1521, 1667, 1681, 1596, 1596, 1596, 1649,
+ /* 330 */ 1681, 1596, 1594, 1596, 1649, 1596, 1596, 1562, 1681, 1611,
+ /* 340 */ 1611, 1681, 1585, 1617, 1585, 1617, 1585, 1617, 1585, 1617,
+ /* 350 */ 1521, 1647, 1647, 1659, 1659, 1601, 1606, 1726, 1521, 1600,
+ /* 360 */ 1601, 1612, 1614, 1530, 1732, 1733, 1754, 1754, 1764, 1764,
+ /* 370 */ 1764, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913,
+ /* 380 */ 1913, 1913, 1913, 1913, 1913, 1913, 673, 901, 283, 740,
+ /* 390 */ 707, 973, 655, 1247, 1048, 1097, 1190, 1306, 1263, 1383,
+ /* 400 */ 1395, 1432, 1469, 1473, 1497, 1279, 1200, 1323, 1075, 1286,
+ /* 410 */ 1536, 1608, 1332, 1609, 1175, 1225, 1610, 1615, 1309, 1361,
+ /* 420 */ 1777, 1784, 1766, 1633, 1778, 1696, 1779, 1771, 1773, 1662,
+ /* 430 */ 1652, 1673, 1776, 1663, 1782, 1664, 1787, 1804, 1668, 1660,
+ /* 440 */ 1682, 1748, 1774, 1666, 1757, 1760, 1761, 1763, 1691, 1706,
+ /* 450 */ 1785, 1683, 1820, 1817, 1801, 1713, 1669, 1758, 1802, 1759,
+ /* 460 */ 1755, 1788, 1694, 1721, 1809, 1814, 1816, 1709, 1716, 1818,
+ /* 470 */ 1772, 1819, 1821, 1815, 1822, 1775, 1823, 1824, 1780, 1808,
+ /* 480 */ 1825, 1704, 1828, 1829, 1830, 1831, 1832, 1833, 1835, 1836,
+ /* 490 */ 1838, 1837, 1839, 1718, 1841, 1842, 1750, 1834, 1844, 1728,
+ /* 500 */ 1843, 1840, 1845, 1846, 1847, 1783, 1795, 1786, 1848, 1798,
+ /* 510 */ 1789, 1849, 1852, 1854, 1853, 1858, 1859, 1855, 1863, 1843,
+ /* 520 */ 1864, 1865, 1867, 1868, 1869, 1870, 1856, 1883, 1874, 1875,
+ /* 530 */ 1876, 1877, 1879, 1880, 1872, 1781, 1767, 1768, 1769, 1770,
+ /* 540 */ 1885, 1888, 1908,
};
-#define YY_REDUCE_COUNT (382)
-#define YY_REDUCE_MIN (-260)
-#define YY_REDUCE_MAX (1420)
+#define YY_REDUCE_COUNT (385)
+#define YY_REDUCE_MIN (-256)
+#define YY_REDUCE_MAX (1590)
static const short yy_reduce_ofst[] = {
- /* 0 */ -170, -18, -159, 309, 313, -167, -19, 75, 117, 211,
- /* 10 */ 315, 317, -165, -195, -168, -260, 389, 437, 475, 524,
- /* 20 */ 527, -169, 529, 531, -28, 80, 534, 239, 304, 412,
- /* 30 */ 558, 577, 37, 120, 368, -22, 460, 517, 555, 560,
- /* 40 */ 562, 586, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 50 */ -257, -257, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 60 */ -257, -257, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 70 */ -257, -257, -257, -257, -257, -257, -257, -172, 457, 628,
- /* 80 */ 673, 692, 694, 702, 704, 722, 728, 740, 743, 748,
- /* 90 */ 767, 791, 815, 818, 820, 822, 857, 861, 864, 866,
- /* 100 */ 868, 870, 872, 874, 876, 881, 900, 902, 906, 908,
- /* 110 */ 910, 912, 915, 917, 920, 960, 962, 964, 988, 990,
- /* 120 */ 992, 1016, 1029, 1032, -257, -257, -257, -257, -257, -257,
- /* 130 */ -257, -257, -257, 271, 618, -190, 68, 60, 240, -124,
- /* 140 */ 603, 610, 603, 610, 12, -257, -257, -257, -257, -128,
- /* 150 */ -128, -128, -142, 25, 270, 281, 333, 124, 236, 648,
- /* 160 */ 374, 465, 465, 28, 598, 792, 839, 469, 38, 381,
- /* 170 */ 622, 709, 173, 699, 522, 703, 808, 811, 867, 816,
- /* 180 */ -104, 823, -3, 875, 649, 753, 323, -88, 882, 884,
- /* 190 */ 518, 43, 325, 899, 763, 604, 879, 969, 402, -193,
- /* 200 */ -189, -180, -151, -55, 69, 104, 141, 259, 286, 360,
- /* 210 */ 364, 455, 474, 481, 510, 516, 611, 653, 788, 99,
- /* 220 */ 871, 878, 995, 1009, 1049, 1081, 1115, 1134, 1136, 1138,
- /* 230 */ 1139, 1140, 733, 1110, 1112, 1144, 1145, 1146, 1148, 1084,
- /* 240 */ 1161, 1162, 1163, 1089, 1047, 1165, 1166, 1127, 1169, 104,
- /* 250 */ 1181, 1182, 1183, 1184, 1185, 1187, 1098, 1100, 1150, 1131,
- /* 260 */ 1132, 1135, 1141, 1084, 1150, 1150, 1152, 1173, 1196, 1097,
- /* 270 */ 1153, 1143, 1167, 1103, 1154, 1155, 1104, 1176, 1174, 1199,
- /* 280 */ 1178, 1186, 1188, 1168, 1158, 1160, 1170, 1159, 1201, 1230,
- /* 290 */ 1156, 1243, 1244, 1157, 1164, 1247, 1172, 1189, 1192, 1240,
- /* 300 */ 1231, 1241, 1242, 1256, 1257, 1278, 1294, 1251, 1252, 1232,
- /* 310 */ 1234, 1265, 1266, 1259, 1301, 1303, 1223, 1225, 1307, 1313,
- /* 320 */ 1295, 1317, 1319, 1320, 1323, 1299, 1306, 1308, 1309, 1305,
- /* 330 */ 1311, 1315, 1314, 1321, 1312, 1324, 1326, 1327, 1263, 1271,
- /* 340 */ 1331, 1296, 1298, 1300, 1302, 1304, 1310, 1316, 1318, 1357,
- /* 350 */ 1255, 1260, 1329, 1325, 1332, 1370, 1333, 1338, 1341, 1343,
- /* 360 */ 1346, 1390, 1393, 1403, 1405, 1416, 1417, 1418, 1328, 1330,
- /* 370 */ 1335, 1409, 1394, 1399, 1401, 1402, 1410, 1391, 1396, 1411,
- /* 380 */ 1420, 1413, 1415,
+ /* 0 */ 880, -121, 269, 528, 933, -119, -187, -185, -182, -180,
+ /* 10 */ -176, -174, -62, -46, 131, -248, -133, 407, 568, 700,
+ /* 20 */ 704, 278, 706, 824, 542, 830, 948, 773, 943, 946,
+ /* 30 */ 71, 650, 211, 267, 826, 272, 676, 732, 885, 976,
+ /* 40 */ 984, 992, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 50 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 60 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 70 */ -256, -256, -256, -256, -256, -256, -256, 989, 1065, 1070,
+ /* 80 */ 1072, 1078, 1082, 1084, 1103, 1118, 1147, 1156, 1160, 1167,
+ /* 90 */ 1185, 1191, 1220, 1237, 1254, 1256, 1266, 1272, 1281, 1291,
+ /* 100 */ 1293, 1296, 1299, 1301, 1307, 1337, 1340, 1342, 1347, 1366,
+ /* 110 */ 1368, 1371, 1373, 1377, 1385, 1387, 1398, 1401, 1404, 1406,
+ /* 120 */ 1411, 1413, 1415, 1430, -256, -256, -256, -256, -256, -256,
+ /* 130 */ -256, -256, -256, -172, 508, -213, 57, -163, -25, 593,
+ /* 140 */ 69, 486, 69, 486, -200, 573, 722, -256, -256, -256,
+ /* 150 */ -256, -141, -141, -141, -105, -161, -167, 157, 212, 405,
+ /* 160 */ 530, 220, 233, 735, 735, 115, 318, 406, 612, 541,
+ /* 170 */ -166, 441, 688, 794, 629, 368, 741, 775, 867, 797,
+ /* 180 */ 871, 842, -186, 1000, 858, 949, 379, 783, 70, 296,
+ /* 190 */ 821, 903, 924, 1044, 651, 282, 1014, 1060, 937, -195,
+ /* 200 */ -177, 413, 439, 511, 566, 787, 827, 848, 898, 945,
+ /* 210 */ 1062, 1074, 1102, 1110, 1202, 1204, 1209, 1211, 1215, 529,
+ /* 220 */ 1221, 1224, 1240, 1246, 1255, 1257, 1269, 1270, 1273, 1274,
+ /* 230 */ 1275, 1280, 1205, 1251, 1294, 1310, 1317, 1326, 1327, 1124,
+ /* 240 */ 1331, 1338, 1339, 1290, 1181, 1346, 1351, 1265, 1352, 787,
+ /* 250 */ 1353, 1367, 1378, 1386, 1392, 1397, 1241, 1312, 1356, 1345,
+ /* 260 */ 1357, 1358, 1359, 1124, 1356, 1356, 1364, 1396, 1433, 1341,
+ /* 270 */ 1381, 1376, 1379, 1354, 1391, 1405, 1362, 1429, 1423, 1431,
+ /* 280 */ 1434, 1435, 1437, 1399, 1410, 1412, 1382, 1417, 1420, 1466,
+ /* 290 */ 1372, 1467, 1468, 1380, 1384, 1475, 1394, 1414, 1416, 1448,
+ /* 300 */ 1440, 1451, 1452, 1453, 1454, 1490, 1493, 1449, 1455, 1427,
+ /* 310 */ 1436, 1464, 1465, 1456, 1498, 1502, 1419, 1421, 1507, 1509,
+ /* 320 */ 1491, 1510, 1513, 1514, 1516, 1496, 1500, 1501, 1503, 1499,
+ /* 330 */ 1505, 1504, 1508, 1506, 1511, 1512, 1515, 1424, 1517, 1457,
+ /* 340 */ 1460, 1519, 1474, 1482, 1483, 1485, 1486, 1488, 1489, 1492,
+ /* 350 */ 1541, 1438, 1441, 1494, 1495, 1518, 1520, 1487, 1555, 1481,
+ /* 360 */ 1522, 1523, 1526, 1528, 1561, 1566, 1580, 1581, 1586, 1587,
+ /* 370 */ 1588, 1478, 1484, 1525, 1575, 1570, 1572, 1573, 1574, 1582,
+ /* 380 */ 1568, 1569, 1578, 1579, 1583, 1590,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1537, 1537, 1537, 1377, 1159, 1266, 1159, 1159, 1159, 1377,
- /* 10 */ 1377, 1377, 1159, 1296, 1296, 1430, 1190, 1159, 1159, 1159,
- /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1376, 1159, 1159,
- /* 30 */ 1159, 1159, 1460, 1460, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 40 */ 1159, 1159, 1159, 1302, 1159, 1159, 1159, 1159, 1159, 1378,
- /* 50 */ 1379, 1159, 1159, 1159, 1429, 1431, 1394, 1312, 1311, 1310,
- /* 60 */ 1309, 1412, 1283, 1307, 1300, 1304, 1372, 1373, 1371, 1375,
- /* 70 */ 1379, 1378, 1159, 1303, 1343, 1357, 1342, 1159, 1159, 1159,
- /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 120 */ 1159, 1159, 1159, 1159, 1351, 1356, 1362, 1355, 1352, 1345,
- /* 130 */ 1344, 1346, 1347, 1159, 1180, 1230, 1159, 1159, 1159, 1159,
- /* 140 */ 1448, 1447, 1159, 1159, 1190, 1348, 1349, 1359, 1358, 1437,
- /* 150 */ 1493, 1492, 1395, 1159, 1159, 1159, 1159, 1159, 1159, 1460,
- /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 180 */ 1159, 1159, 1159, 1159, 1460, 1460, 1159, 1190, 1460, 1460,
- /* 190 */ 1186, 1337, 1336, 1186, 1290, 1159, 1443, 1266, 1257, 1159,
- /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 210 */ 1159, 1159, 1159, 1434, 1432, 1159, 1159, 1159, 1159, 1159,
- /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 240 */ 1159, 1159, 1159, 1262, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 250 */ 1159, 1159, 1159, 1159, 1159, 1487, 1159, 1407, 1244, 1262,
- /* 260 */ 1262, 1262, 1262, 1264, 1245, 1243, 1256, 1191, 1166, 1529,
- /* 270 */ 1306, 1285, 1285, 1526, 1306, 1306, 1526, 1205, 1507, 1202,
- /* 280 */ 1296, 1296, 1296, 1285, 1290, 1290, 1374, 1263, 1256, 1159,
- /* 290 */ 1529, 1271, 1271, 1528, 1528, 1271, 1395, 1315, 1321, 1233,
- /* 300 */ 1306, 1239, 1239, 1239, 1239, 1271, 1177, 1306, 1306, 1315,
- /* 310 */ 1321, 1233, 1233, 1306, 1271, 1177, 1411, 1523, 1271, 1177,
- /* 320 */ 1385, 1271, 1177, 1271, 1177, 1385, 1231, 1231, 1231, 1220,
- /* 330 */ 1385, 1231, 1205, 1231, 1220, 1231, 1231, 1385, 1389, 1389,
- /* 340 */ 1385, 1289, 1284, 1289, 1284, 1289, 1284, 1289, 1284, 1271,
- /* 350 */ 1470, 1470, 1301, 1290, 1380, 1271, 1159, 1301, 1299, 1297,
- /* 360 */ 1306, 1183, 1223, 1490, 1490, 1486, 1486, 1486, 1534, 1534,
- /* 370 */ 1443, 1502, 1190, 1190, 1190, 1190, 1502, 1207, 1207, 1191,
- /* 380 */ 1191, 1190, 1502, 1159, 1159, 1159, 1159, 1159, 1159, 1497,
- /* 390 */ 1159, 1396, 1275, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 410 */ 1159, 1159, 1159, 1159, 1159, 1326, 1159, 1162, 1440, 1159,
- /* 420 */ 1159, 1438, 1159, 1159, 1159, 1159, 1159, 1159, 1276, 1159,
- /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1525, 1159, 1159,
- /* 450 */ 1159, 1159, 1159, 1159, 1410, 1409, 1159, 1159, 1273, 1159,
- /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1298, 1159, 1159,
- /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 510 */ 1159, 1159, 1475, 1291, 1159, 1159, 1516, 1159, 1159, 1159,
- /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 530 */ 1159, 1511, 1247, 1328, 1159, 1327, 1331, 1159, 1171, 1159,
+ /* 0 */ 1554, 1554, 1554, 1392, 1171, 1278, 1171, 1171, 1171, 1392,
+ /* 10 */ 1392, 1392, 1171, 1308, 1308, 1445, 1202, 1171, 1171, 1171,
+ /* 20 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1391, 1171, 1171,
+ /* 30 */ 1171, 1171, 1475, 1475, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 40 */ 1171, 1171, 1171, 1317, 1171, 1171, 1171, 1171, 1171, 1393,
+ /* 50 */ 1394, 1171, 1171, 1171, 1444, 1446, 1409, 1327, 1326, 1325,
+ /* 60 */ 1324, 1427, 1295, 1322, 1315, 1319, 1387, 1388, 1386, 1390,
+ /* 70 */ 1394, 1393, 1171, 1318, 1358, 1372, 1357, 1171, 1171, 1171,
+ /* 80 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 90 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 100 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 110 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 120 */ 1171, 1171, 1171, 1171, 1366, 1371, 1377, 1370, 1367, 1360,
+ /* 130 */ 1359, 1361, 1362, 1171, 1192, 1242, 1171, 1171, 1171, 1171,
+ /* 140 */ 1463, 1462, 1171, 1171, 1202, 1352, 1351, 1363, 1364, 1374,
+ /* 150 */ 1373, 1452, 1510, 1509, 1410, 1171, 1171, 1171, 1171, 1171,
+ /* 160 */ 1171, 1475, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 170 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 180 */ 1171, 1171, 1171, 1171, 1171, 1171, 1475, 1475, 1171, 1202,
+ /* 190 */ 1475, 1475, 1198, 1198, 1302, 1171, 1458, 1278, 1269, 1171,
+ /* 200 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 210 */ 1171, 1171, 1171, 1449, 1447, 1171, 1171, 1171, 1171, 1171,
+ /* 220 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 230 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 240 */ 1171, 1171, 1171, 1274, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 250 */ 1171, 1171, 1171, 1171, 1171, 1504, 1171, 1422, 1256, 1274,
+ /* 260 */ 1274, 1274, 1274, 1276, 1257, 1255, 1268, 1203, 1178, 1546,
+ /* 270 */ 1321, 1297, 1297, 1543, 1321, 1321, 1543, 1217, 1524, 1214,
+ /* 280 */ 1308, 1308, 1308, 1297, 1302, 1302, 1389, 1275, 1268, 1171,
+ /* 290 */ 1546, 1283, 1283, 1545, 1545, 1283, 1410, 1330, 1336, 1245,
+ /* 300 */ 1321, 1251, 1251, 1251, 1251, 1283, 1189, 1321, 1321, 1330,
+ /* 310 */ 1336, 1245, 1245, 1321, 1283, 1189, 1426, 1540, 1283, 1189,
+ /* 320 */ 1400, 1283, 1189, 1283, 1189, 1400, 1243, 1243, 1243, 1232,
+ /* 330 */ 1400, 1243, 1217, 1243, 1232, 1243, 1243, 1493, 1400, 1404,
+ /* 340 */ 1404, 1400, 1301, 1296, 1301, 1296, 1301, 1296, 1301, 1296,
+ /* 350 */ 1283, 1485, 1485, 1311, 1311, 1316, 1302, 1395, 1283, 1171,
+ /* 360 */ 1316, 1314, 1312, 1321, 1195, 1235, 1507, 1507, 1503, 1503,
+ /* 370 */ 1503, 1551, 1551, 1458, 1519, 1202, 1202, 1202, 1202, 1519,
+ /* 380 */ 1219, 1219, 1203, 1203, 1202, 1519, 1171, 1171, 1171, 1171,
+ /* 390 */ 1171, 1171, 1514, 1171, 1411, 1287, 1171, 1171, 1171, 1171,
+ /* 400 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 410 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1341,
+ /* 420 */ 1171, 1174, 1455, 1171, 1171, 1453, 1171, 1171, 1171, 1171,
+ /* 430 */ 1171, 1171, 1288, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 440 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 450 */ 1171, 1542, 1171, 1171, 1171, 1171, 1171, 1171, 1425, 1424,
+ /* 460 */ 1171, 1171, 1285, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 470 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 480 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 490 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 500 */ 1313, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 510 */ 1171, 1171, 1171, 1171, 1171, 1490, 1303, 1171, 1171, 1533,
+ /* 520 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 530 */ 1171, 1171, 1171, 1171, 1528, 1259, 1343, 1171, 1342, 1346,
+ /* 540 */ 1171, 1183, 1171,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -149751,6 +150692,9 @@ static const YYCODETYPE yyFallback[] = {
59, /* VIEW => ID */
59, /* VIRTUAL => ID */
59, /* WITH => ID */
+ 59, /* NULLS => ID */
+ 59, /* FIRST => ID */
+ 59, /* LAST => ID */
59, /* CURRENT => ID */
59, /* FOLLOWING => ID */
59, /* PARTITION => ID */
@@ -149764,6 +150708,87 @@ static const YYCODETYPE yyFallback[] = {
59, /* REINDEX => ID */
59, /* RENAME => ID */
59, /* CTIME_KW => ID */
+ 0, /* ANY => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* BITNOT => nothing */
+ 0, /* ON => nothing */
+ 0, /* INDEXED => nothing */
+ 0, /* STRING => nothing */
+ 0, /* JOIN_KW => nothing */
+ 0, /* CONSTRAINT => nothing */
+ 0, /* DEFAULT => nothing */
+ 0, /* NULL => nothing */
+ 0, /* PRIMARY => nothing */
+ 0, /* UNIQUE => nothing */
+ 0, /* CHECK => nothing */
+ 0, /* REFERENCES => nothing */
+ 0, /* AUTOINCR => nothing */
+ 0, /* INSERT => nothing */
+ 0, /* DELETE => nothing */
+ 0, /* UPDATE => nothing */
+ 0, /* SET => nothing */
+ 0, /* DEFERRABLE => nothing */
+ 0, /* FOREIGN => nothing */
+ 0, /* DROP => nothing */
+ 0, /* UNION => nothing */
+ 0, /* ALL => nothing */
+ 0, /* EXCEPT => nothing */
+ 0, /* INTERSECT => nothing */
+ 0, /* SELECT => nothing */
+ 0, /* VALUES => nothing */
+ 0, /* DISTINCT => nothing */
+ 0, /* DOT => nothing */
+ 0, /* FROM => nothing */
+ 0, /* JOIN => nothing */
+ 0, /* USING => nothing */
+ 0, /* ORDER => nothing */
+ 0, /* GROUP => nothing */
+ 0, /* HAVING => nothing */
+ 0, /* LIMIT => nothing */
+ 0, /* WHERE => nothing */
+ 0, /* INTO => nothing */
+ 0, /* NOTHING => nothing */
+ 0, /* FLOAT => nothing */
+ 0, /* BLOB => nothing */
+ 0, /* INTEGER => nothing */
+ 0, /* VARIABLE => nothing */
+ 0, /* CASE => nothing */
+ 0, /* WHEN => nothing */
+ 0, /* THEN => nothing */
+ 0, /* ELSE => nothing */
+ 0, /* INDEX => nothing */
+ 0, /* ALTER => nothing */
+ 0, /* ADD => nothing */
+ 0, /* WINDOW => nothing */
+ 0, /* OVER => nothing */
+ 0, /* FILTER => nothing */
+ 0, /* COLUMN => nothing */
+ 0, /* AGG_FUNCTION => nothing */
+ 0, /* AGG_COLUMN => nothing */
+ 0, /* TRUEFALSE => nothing */
+ 0, /* ISNOT => nothing */
+ 0, /* FUNCTION => nothing */
+ 0, /* UMINUS => nothing */
+ 0, /* UPLUS => nothing */
+ 0, /* TRUTH => nothing */
+ 0, /* REGISTER => nothing */
+ 0, /* VECTOR => nothing */
+ 0, /* SELECT_COLUMN => nothing */
+ 0, /* IF_NULL_ROW => nothing */
+ 0, /* ASTERISK => nothing */
+ 0, /* SPAN => nothing */
+ 0, /* SPACE => nothing */
+ 0, /* ILLEGAL => nothing */
};
#endif /* YYFALLBACK */
@@ -149933,226 +150958,231 @@ static const char *const yyTokenName[] = {
/* 79 */ "VIEW",
/* 80 */ "VIRTUAL",
/* 81 */ "WITH",
- /* 82 */ "CURRENT",
- /* 83 */ "FOLLOWING",
- /* 84 */ "PARTITION",
- /* 85 */ "PRECEDING",
- /* 86 */ "RANGE",
- /* 87 */ "UNBOUNDED",
- /* 88 */ "EXCLUDE",
- /* 89 */ "GROUPS",
- /* 90 */ "OTHERS",
- /* 91 */ "TIES",
- /* 92 */ "REINDEX",
- /* 93 */ "RENAME",
- /* 94 */ "CTIME_KW",
- /* 95 */ "ANY",
- /* 96 */ "BITAND",
- /* 97 */ "BITOR",
- /* 98 */ "LSHIFT",
- /* 99 */ "RSHIFT",
- /* 100 */ "PLUS",
- /* 101 */ "MINUS",
- /* 102 */ "STAR",
- /* 103 */ "SLASH",
- /* 104 */ "REM",
- /* 105 */ "CONCAT",
- /* 106 */ "COLLATE",
- /* 107 */ "BITNOT",
- /* 108 */ "ON",
- /* 109 */ "INDEXED",
- /* 110 */ "STRING",
- /* 111 */ "JOIN_KW",
- /* 112 */ "CONSTRAINT",
- /* 113 */ "DEFAULT",
- /* 114 */ "NULL",
- /* 115 */ "PRIMARY",
- /* 116 */ "UNIQUE",
- /* 117 */ "CHECK",
- /* 118 */ "REFERENCES",
- /* 119 */ "AUTOINCR",
- /* 120 */ "INSERT",
- /* 121 */ "DELETE",
- /* 122 */ "UPDATE",
- /* 123 */ "SET",
- /* 124 */ "DEFERRABLE",
- /* 125 */ "FOREIGN",
- /* 126 */ "DROP",
- /* 127 */ "UNION",
- /* 128 */ "ALL",
- /* 129 */ "EXCEPT",
- /* 130 */ "INTERSECT",
- /* 131 */ "SELECT",
- /* 132 */ "VALUES",
- /* 133 */ "DISTINCT",
- /* 134 */ "DOT",
- /* 135 */ "FROM",
- /* 136 */ "JOIN",
- /* 137 */ "USING",
- /* 138 */ "ORDER",
- /* 139 */ "GROUP",
- /* 140 */ "HAVING",
- /* 141 */ "LIMIT",
- /* 142 */ "WHERE",
- /* 143 */ "INTO",
- /* 144 */ "NOTHING",
- /* 145 */ "FLOAT",
- /* 146 */ "BLOB",
- /* 147 */ "INTEGER",
- /* 148 */ "VARIABLE",
- /* 149 */ "CASE",
- /* 150 */ "WHEN",
- /* 151 */ "THEN",
- /* 152 */ "ELSE",
- /* 153 */ "INDEX",
- /* 154 */ "ALTER",
- /* 155 */ "ADD",
- /* 156 */ "WINDOW",
- /* 157 */ "OVER",
- /* 158 */ "FILTER",
- /* 159 */ "TRUEFALSE",
- /* 160 */ "ISNOT",
- /* 161 */ "FUNCTION",
+ /* 82 */ "NULLS",
+ /* 83 */ "FIRST",
+ /* 84 */ "LAST",
+ /* 85 */ "CURRENT",
+ /* 86 */ "FOLLOWING",
+ /* 87 */ "PARTITION",
+ /* 88 */ "PRECEDING",
+ /* 89 */ "RANGE",
+ /* 90 */ "UNBOUNDED",
+ /* 91 */ "EXCLUDE",
+ /* 92 */ "GROUPS",
+ /* 93 */ "OTHERS",
+ /* 94 */ "TIES",
+ /* 95 */ "REINDEX",
+ /* 96 */ "RENAME",
+ /* 97 */ "CTIME_KW",
+ /* 98 */ "ANY",
+ /* 99 */ "BITAND",
+ /* 100 */ "BITOR",
+ /* 101 */ "LSHIFT",
+ /* 102 */ "RSHIFT",
+ /* 103 */ "PLUS",
+ /* 104 */ "MINUS",
+ /* 105 */ "STAR",
+ /* 106 */ "SLASH",
+ /* 107 */ "REM",
+ /* 108 */ "CONCAT",
+ /* 109 */ "COLLATE",
+ /* 110 */ "BITNOT",
+ /* 111 */ "ON",
+ /* 112 */ "INDEXED",
+ /* 113 */ "STRING",
+ /* 114 */ "JOIN_KW",
+ /* 115 */ "CONSTRAINT",
+ /* 116 */ "DEFAULT",
+ /* 117 */ "NULL",
+ /* 118 */ "PRIMARY",
+ /* 119 */ "UNIQUE",
+ /* 120 */ "CHECK",
+ /* 121 */ "REFERENCES",
+ /* 122 */ "AUTOINCR",
+ /* 123 */ "INSERT",
+ /* 124 */ "DELETE",
+ /* 125 */ "UPDATE",
+ /* 126 */ "SET",
+ /* 127 */ "DEFERRABLE",
+ /* 128 */ "FOREIGN",
+ /* 129 */ "DROP",
+ /* 130 */ "UNION",
+ /* 131 */ "ALL",
+ /* 132 */ "EXCEPT",
+ /* 133 */ "INTERSECT",
+ /* 134 */ "SELECT",
+ /* 135 */ "VALUES",
+ /* 136 */ "DISTINCT",
+ /* 137 */ "DOT",
+ /* 138 */ "FROM",
+ /* 139 */ "JOIN",
+ /* 140 */ "USING",
+ /* 141 */ "ORDER",
+ /* 142 */ "GROUP",
+ /* 143 */ "HAVING",
+ /* 144 */ "LIMIT",
+ /* 145 */ "WHERE",
+ /* 146 */ "INTO",
+ /* 147 */ "NOTHING",
+ /* 148 */ "FLOAT",
+ /* 149 */ "BLOB",
+ /* 150 */ "INTEGER",
+ /* 151 */ "VARIABLE",
+ /* 152 */ "CASE",
+ /* 153 */ "WHEN",
+ /* 154 */ "THEN",
+ /* 155 */ "ELSE",
+ /* 156 */ "INDEX",
+ /* 157 */ "ALTER",
+ /* 158 */ "ADD",
+ /* 159 */ "WINDOW",
+ /* 160 */ "OVER",
+ /* 161 */ "FILTER",
/* 162 */ "COLUMN",
/* 163 */ "AGG_FUNCTION",
/* 164 */ "AGG_COLUMN",
- /* 165 */ "UMINUS",
- /* 166 */ "UPLUS",
- /* 167 */ "TRUTH",
- /* 168 */ "REGISTER",
- /* 169 */ "VECTOR",
- /* 170 */ "SELECT_COLUMN",
- /* 171 */ "IF_NULL_ROW",
- /* 172 */ "ASTERISK",
- /* 173 */ "SPAN",
- /* 174 */ "SPACE",
- /* 175 */ "ILLEGAL",
- /* 176 */ "input",
- /* 177 */ "cmdlist",
- /* 178 */ "ecmd",
- /* 179 */ "cmdx",
- /* 180 */ "explain",
- /* 181 */ "cmd",
- /* 182 */ "transtype",
- /* 183 */ "trans_opt",
- /* 184 */ "nm",
- /* 185 */ "savepoint_opt",
- /* 186 */ "create_table",
- /* 187 */ "create_table_args",
- /* 188 */ "createkw",
- /* 189 */ "temp",
- /* 190 */ "ifnotexists",
- /* 191 */ "dbnm",
- /* 192 */ "columnlist",
- /* 193 */ "conslist_opt",
- /* 194 */ "table_options",
- /* 195 */ "select",
- /* 196 */ "columnname",
- /* 197 */ "carglist",
- /* 198 */ "typetoken",
- /* 199 */ "typename",
- /* 200 */ "signed",
- /* 201 */ "plus_num",
- /* 202 */ "minus_num",
- /* 203 */ "scanpt",
- /* 204 */ "scantok",
- /* 205 */ "ccons",
- /* 206 */ "term",
- /* 207 */ "expr",
- /* 208 */ "onconf",
- /* 209 */ "sortorder",
- /* 210 */ "autoinc",
- /* 211 */ "eidlist_opt",
- /* 212 */ "refargs",
- /* 213 */ "defer_subclause",
- /* 214 */ "refarg",
- /* 215 */ "refact",
- /* 216 */ "init_deferred_pred_opt",
- /* 217 */ "conslist",
- /* 218 */ "tconscomma",
- /* 219 */ "tcons",
- /* 220 */ "sortlist",
- /* 221 */ "eidlist",
- /* 222 */ "defer_subclause_opt",
- /* 223 */ "orconf",
- /* 224 */ "resolvetype",
- /* 225 */ "raisetype",
- /* 226 */ "ifexists",
- /* 227 */ "fullname",
- /* 228 */ "selectnowith",
- /* 229 */ "oneselect",
- /* 230 */ "wqlist",
- /* 231 */ "multiselect_op",
- /* 232 */ "distinct",
- /* 233 */ "selcollist",
- /* 234 */ "from",
- /* 235 */ "where_opt",
- /* 236 */ "groupby_opt",
- /* 237 */ "having_opt",
- /* 238 */ "orderby_opt",
- /* 239 */ "limit_opt",
- /* 240 */ "window_clause",
- /* 241 */ "values",
- /* 242 */ "nexprlist",
- /* 243 */ "sclp",
- /* 244 */ "as",
- /* 245 */ "seltablist",
- /* 246 */ "stl_prefix",
- /* 247 */ "joinop",
- /* 248 */ "indexed_opt",
- /* 249 */ "on_opt",
- /* 250 */ "using_opt",
- /* 251 */ "exprlist",
- /* 252 */ "xfullname",
- /* 253 */ "idlist",
- /* 254 */ "with",
- /* 255 */ "setlist",
- /* 256 */ "insert_cmd",
- /* 257 */ "idlist_opt",
- /* 258 */ "upsert",
- /* 259 */ "over_clause",
- /* 260 */ "likeop",
- /* 261 */ "between_op",
- /* 262 */ "in_op",
- /* 263 */ "paren_exprlist",
- /* 264 */ "case_operand",
- /* 265 */ "case_exprlist",
- /* 266 */ "case_else",
- /* 267 */ "uniqueflag",
- /* 268 */ "collate",
- /* 269 */ "vinto",
- /* 270 */ "nmnum",
- /* 271 */ "trigger_decl",
- /* 272 */ "trigger_cmd_list",
- /* 273 */ "trigger_time",
- /* 274 */ "trigger_event",
- /* 275 */ "foreach_clause",
- /* 276 */ "when_clause",
- /* 277 */ "trigger_cmd",
- /* 278 */ "trnm",
- /* 279 */ "tridxby",
- /* 280 */ "database_kw_opt",
- /* 281 */ "key_opt",
- /* 282 */ "add_column_fullname",
- /* 283 */ "kwcolumn_opt",
- /* 284 */ "create_vtab",
- /* 285 */ "vtabarglist",
- /* 286 */ "vtabarg",
- /* 287 */ "vtabargtoken",
- /* 288 */ "lp",
- /* 289 */ "anylist",
- /* 290 */ "windowdefn_list",
- /* 291 */ "windowdefn",
- /* 292 */ "window",
- /* 293 */ "frame_opt",
- /* 294 */ "part_opt",
- /* 295 */ "filter_opt",
- /* 296 */ "range_or_rows",
- /* 297 */ "frame_bound",
- /* 298 */ "frame_bound_s",
- /* 299 */ "frame_bound_e",
- /* 300 */ "frame_exclude_opt",
- /* 301 */ "frame_exclude",
+ /* 165 */ "TRUEFALSE",
+ /* 166 */ "ISNOT",
+ /* 167 */ "FUNCTION",
+ /* 168 */ "UMINUS",
+ /* 169 */ "UPLUS",
+ /* 170 */ "TRUTH",
+ /* 171 */ "REGISTER",
+ /* 172 */ "VECTOR",
+ /* 173 */ "SELECT_COLUMN",
+ /* 174 */ "IF_NULL_ROW",
+ /* 175 */ "ASTERISK",
+ /* 176 */ "SPAN",
+ /* 177 */ "SPACE",
+ /* 178 */ "ILLEGAL",
+ /* 179 */ "input",
+ /* 180 */ "cmdlist",
+ /* 181 */ "ecmd",
+ /* 182 */ "cmdx",
+ /* 183 */ "explain",
+ /* 184 */ "cmd",
+ /* 185 */ "transtype",
+ /* 186 */ "trans_opt",
+ /* 187 */ "nm",
+ /* 188 */ "savepoint_opt",
+ /* 189 */ "create_table",
+ /* 190 */ "create_table_args",
+ /* 191 */ "createkw",
+ /* 192 */ "temp",
+ /* 193 */ "ifnotexists",
+ /* 194 */ "dbnm",
+ /* 195 */ "columnlist",
+ /* 196 */ "conslist_opt",
+ /* 197 */ "table_options",
+ /* 198 */ "select",
+ /* 199 */ "columnname",
+ /* 200 */ "carglist",
+ /* 201 */ "typetoken",
+ /* 202 */ "typename",
+ /* 203 */ "signed",
+ /* 204 */ "plus_num",
+ /* 205 */ "minus_num",
+ /* 206 */ "scanpt",
+ /* 207 */ "scantok",
+ /* 208 */ "ccons",
+ /* 209 */ "term",
+ /* 210 */ "expr",
+ /* 211 */ "onconf",
+ /* 212 */ "sortorder",
+ /* 213 */ "autoinc",
+ /* 214 */ "eidlist_opt",
+ /* 215 */ "refargs",
+ /* 216 */ "defer_subclause",
+ /* 217 */ "refarg",
+ /* 218 */ "refact",
+ /* 219 */ "init_deferred_pred_opt",
+ /* 220 */ "conslist",
+ /* 221 */ "tconscomma",
+ /* 222 */ "tcons",
+ /* 223 */ "sortlist",
+ /* 224 */ "eidlist",
+ /* 225 */ "defer_subclause_opt",
+ /* 226 */ "orconf",
+ /* 227 */ "resolvetype",
+ /* 228 */ "raisetype",
+ /* 229 */ "ifexists",
+ /* 230 */ "fullname",
+ /* 231 */ "selectnowith",
+ /* 232 */ "oneselect",
+ /* 233 */ "wqlist",
+ /* 234 */ "multiselect_op",
+ /* 235 */ "distinct",
+ /* 236 */ "selcollist",
+ /* 237 */ "from",
+ /* 238 */ "where_opt",
+ /* 239 */ "groupby_opt",
+ /* 240 */ "having_opt",
+ /* 241 */ "orderby_opt",
+ /* 242 */ "limit_opt",
+ /* 243 */ "window_clause",
+ /* 244 */ "values",
+ /* 245 */ "nexprlist",
+ /* 246 */ "sclp",
+ /* 247 */ "as",
+ /* 248 */ "seltablist",
+ /* 249 */ "stl_prefix",
+ /* 250 */ "joinop",
+ /* 251 */ "indexed_opt",
+ /* 252 */ "on_opt",
+ /* 253 */ "using_opt",
+ /* 254 */ "exprlist",
+ /* 255 */ "xfullname",
+ /* 256 */ "idlist",
+ /* 257 */ "nulls",
+ /* 258 */ "with",
+ /* 259 */ "setlist",
+ /* 260 */ "insert_cmd",
+ /* 261 */ "idlist_opt",
+ /* 262 */ "upsert",
+ /* 263 */ "filter_over",
+ /* 264 */ "likeop",
+ /* 265 */ "between_op",
+ /* 266 */ "in_op",
+ /* 267 */ "paren_exprlist",
+ /* 268 */ "case_operand",
+ /* 269 */ "case_exprlist",
+ /* 270 */ "case_else",
+ /* 271 */ "uniqueflag",
+ /* 272 */ "collate",
+ /* 273 */ "vinto",
+ /* 274 */ "nmnum",
+ /* 275 */ "trigger_decl",
+ /* 276 */ "trigger_cmd_list",
+ /* 277 */ "trigger_time",
+ /* 278 */ "trigger_event",
+ /* 279 */ "foreach_clause",
+ /* 280 */ "when_clause",
+ /* 281 */ "trigger_cmd",
+ /* 282 */ "trnm",
+ /* 283 */ "tridxby",
+ /* 284 */ "database_kw_opt",
+ /* 285 */ "key_opt",
+ /* 286 */ "add_column_fullname",
+ /* 287 */ "kwcolumn_opt",
+ /* 288 */ "create_vtab",
+ /* 289 */ "vtabarglist",
+ /* 290 */ "vtabarg",
+ /* 291 */ "vtabargtoken",
+ /* 292 */ "lp",
+ /* 293 */ "anylist",
+ /* 294 */ "windowdefn_list",
+ /* 295 */ "windowdefn",
+ /* 296 */ "window",
+ /* 297 */ "frame_opt",
+ /* 298 */ "part_opt",
+ /* 299 */ "filter_clause",
+ /* 300 */ "over_clause",
+ /* 301 */ "range_or_rows",
+ /* 302 */ "frame_bound",
+ /* 303 */ "frame_bound_s",
+ /* 304 */ "frame_bound_e",
+ /* 305 */ "frame_exclude_opt",
+ /* 306 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -150290,252 +151320,257 @@ static const char *const yyRuleName[] = {
/* 127 */ "using_opt ::=",
/* 128 */ "orderby_opt ::=",
/* 129 */ "orderby_opt ::= ORDER BY sortlist",
- /* 130 */ "sortlist ::= sortlist COMMA expr sortorder",
- /* 131 */ "sortlist ::= expr sortorder",
+ /* 130 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
+ /* 131 */ "sortlist ::= expr sortorder nulls",
/* 132 */ "sortorder ::= ASC",
/* 133 */ "sortorder ::= DESC",
/* 134 */ "sortorder ::=",
- /* 135 */ "groupby_opt ::=",
- /* 136 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 137 */ "having_opt ::=",
- /* 138 */ "having_opt ::= HAVING expr",
- /* 139 */ "limit_opt ::=",
- /* 140 */ "limit_opt ::= LIMIT expr",
- /* 141 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 142 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 143 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
- /* 144 */ "where_opt ::=",
- /* 145 */ "where_opt ::= WHERE expr",
- /* 146 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
- /* 147 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 148 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
- /* 149 */ "setlist ::= nm EQ expr",
- /* 150 */ "setlist ::= LP idlist RP EQ expr",
- /* 151 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
- /* 152 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
- /* 153 */ "upsert ::=",
- /* 154 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
- /* 155 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
- /* 156 */ "upsert ::= ON CONFLICT DO NOTHING",
- /* 157 */ "insert_cmd ::= INSERT orconf",
- /* 158 */ "insert_cmd ::= REPLACE",
- /* 159 */ "idlist_opt ::=",
- /* 160 */ "idlist_opt ::= LP idlist RP",
- /* 161 */ "idlist ::= idlist COMMA nm",
- /* 162 */ "idlist ::= nm",
- /* 163 */ "expr ::= LP expr RP",
- /* 164 */ "expr ::= ID|INDEXED",
- /* 165 */ "expr ::= JOIN_KW",
- /* 166 */ "expr ::= nm DOT nm",
- /* 167 */ "expr ::= nm DOT nm DOT nm",
- /* 168 */ "term ::= NULL|FLOAT|BLOB",
- /* 169 */ "term ::= STRING",
- /* 170 */ "term ::= INTEGER",
- /* 171 */ "expr ::= VARIABLE",
- /* 172 */ "expr ::= expr COLLATE ID|STRING",
- /* 173 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 174 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
- /* 175 */ "expr ::= ID|INDEXED LP STAR RP",
- /* 176 */ "expr ::= ID|INDEXED LP distinct exprlist RP over_clause",
- /* 177 */ "expr ::= ID|INDEXED LP STAR RP over_clause",
- /* 178 */ "term ::= CTIME_KW",
- /* 179 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 180 */ "expr ::= expr AND expr",
- /* 181 */ "expr ::= expr OR expr",
- /* 182 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 183 */ "expr ::= expr EQ|NE expr",
- /* 184 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 185 */ "expr ::= expr PLUS|MINUS expr",
- /* 186 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 187 */ "expr ::= expr CONCAT expr",
- /* 188 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 189 */ "expr ::= expr likeop expr",
- /* 190 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 191 */ "expr ::= expr ISNULL|NOTNULL",
- /* 192 */ "expr ::= expr NOT NULL",
- /* 193 */ "expr ::= expr IS expr",
- /* 194 */ "expr ::= expr IS NOT expr",
- /* 195 */ "expr ::= NOT expr",
- /* 196 */ "expr ::= BITNOT expr",
- /* 197 */ "expr ::= PLUS|MINUS expr",
- /* 198 */ "between_op ::= BETWEEN",
- /* 199 */ "between_op ::= NOT BETWEEN",
- /* 200 */ "expr ::= expr between_op expr AND expr",
- /* 201 */ "in_op ::= IN",
- /* 202 */ "in_op ::= NOT IN",
- /* 203 */ "expr ::= expr in_op LP exprlist RP",
- /* 204 */ "expr ::= LP select RP",
- /* 205 */ "expr ::= expr in_op LP select RP",
- /* 206 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 207 */ "expr ::= EXISTS LP select RP",
- /* 208 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 209 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 210 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 211 */ "case_else ::= ELSE expr",
- /* 212 */ "case_else ::=",
- /* 213 */ "case_operand ::= expr",
- /* 214 */ "case_operand ::=",
- /* 215 */ "exprlist ::=",
- /* 216 */ "nexprlist ::= nexprlist COMMA expr",
- /* 217 */ "nexprlist ::= expr",
- /* 218 */ "paren_exprlist ::=",
- /* 219 */ "paren_exprlist ::= LP exprlist RP",
- /* 220 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 221 */ "uniqueflag ::= UNIQUE",
- /* 222 */ "uniqueflag ::=",
- /* 223 */ "eidlist_opt ::=",
- /* 224 */ "eidlist_opt ::= LP eidlist RP",
- /* 225 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 226 */ "eidlist ::= nm collate sortorder",
- /* 227 */ "collate ::=",
- /* 228 */ "collate ::= COLLATE ID|STRING",
- /* 229 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 230 */ "cmd ::= VACUUM vinto",
- /* 231 */ "cmd ::= VACUUM nm vinto",
- /* 232 */ "vinto ::= INTO expr",
- /* 233 */ "vinto ::=",
- /* 234 */ "cmd ::= PRAGMA nm dbnm",
- /* 235 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 236 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 237 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 238 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 239 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 240 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 241 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 242 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 243 */ "trigger_time ::= BEFORE|AFTER",
- /* 244 */ "trigger_time ::= INSTEAD OF",
- /* 245 */ "trigger_time ::=",
- /* 246 */ "trigger_event ::= DELETE|INSERT",
- /* 247 */ "trigger_event ::= UPDATE",
- /* 248 */ "trigger_event ::= UPDATE OF idlist",
- /* 249 */ "when_clause ::=",
- /* 250 */ "when_clause ::= WHEN expr",
- /* 251 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 252 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 253 */ "trnm ::= nm DOT nm",
- /* 254 */ "tridxby ::= INDEXED BY nm",
- /* 255 */ "tridxby ::= NOT INDEXED",
- /* 256 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
- /* 257 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 258 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 259 */ "trigger_cmd ::= scanpt select scanpt",
- /* 260 */ "expr ::= RAISE LP IGNORE RP",
- /* 261 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 262 */ "raisetype ::= ROLLBACK",
- /* 263 */ "raisetype ::= ABORT",
- /* 264 */ "raisetype ::= FAIL",
- /* 265 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 266 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 267 */ "cmd ::= DETACH database_kw_opt expr",
- /* 268 */ "key_opt ::=",
- /* 269 */ "key_opt ::= KEY expr",
- /* 270 */ "cmd ::= REINDEX",
- /* 271 */ "cmd ::= REINDEX nm dbnm",
- /* 272 */ "cmd ::= ANALYZE",
- /* 273 */ "cmd ::= ANALYZE nm dbnm",
- /* 274 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 275 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 276 */ "add_column_fullname ::= fullname",
- /* 277 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 278 */ "cmd ::= create_vtab",
- /* 279 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 280 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 281 */ "vtabarg ::=",
- /* 282 */ "vtabargtoken ::= ANY",
- /* 283 */ "vtabargtoken ::= lp anylist RP",
- /* 284 */ "lp ::= LP",
- /* 285 */ "with ::= WITH wqlist",
- /* 286 */ "with ::= WITH RECURSIVE wqlist",
- /* 287 */ "wqlist ::= nm eidlist_opt AS LP select RP",
- /* 288 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
- /* 289 */ "windowdefn_list ::= windowdefn",
- /* 290 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 291 */ "windowdefn ::= nm AS LP window RP",
- /* 292 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 293 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 294 */ "window ::= ORDER BY sortlist frame_opt",
- /* 295 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 296 */ "window ::= frame_opt",
- /* 297 */ "window ::= nm frame_opt",
- /* 298 */ "frame_opt ::=",
- /* 299 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 300 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 301 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 302 */ "frame_bound_s ::= frame_bound",
- /* 303 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 304 */ "frame_bound_e ::= frame_bound",
- /* 305 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 306 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 307 */ "frame_bound ::= CURRENT ROW",
- /* 308 */ "frame_exclude_opt ::=",
- /* 309 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 310 */ "frame_exclude ::= NO OTHERS",
- /* 311 */ "frame_exclude ::= CURRENT ROW",
- /* 312 */ "frame_exclude ::= GROUP|TIES",
- /* 313 */ "window_clause ::= WINDOW windowdefn_list",
- /* 314 */ "over_clause ::= filter_opt OVER LP window RP",
- /* 315 */ "over_clause ::= filter_opt OVER nm",
- /* 316 */ "filter_opt ::=",
- /* 317 */ "filter_opt ::= FILTER LP WHERE expr RP",
- /* 318 */ "input ::= cmdlist",
- /* 319 */ "cmdlist ::= cmdlist ecmd",
- /* 320 */ "cmdlist ::= ecmd",
- /* 321 */ "ecmd ::= SEMI",
- /* 322 */ "ecmd ::= cmdx SEMI",
- /* 323 */ "ecmd ::= explain cmdx",
- /* 324 */ "trans_opt ::=",
- /* 325 */ "trans_opt ::= TRANSACTION",
- /* 326 */ "trans_opt ::= TRANSACTION nm",
- /* 327 */ "savepoint_opt ::= SAVEPOINT",
- /* 328 */ "savepoint_opt ::=",
- /* 329 */ "cmd ::= create_table create_table_args",
- /* 330 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 331 */ "columnlist ::= columnname carglist",
- /* 332 */ "nm ::= ID|INDEXED",
- /* 333 */ "nm ::= STRING",
- /* 334 */ "nm ::= JOIN_KW",
- /* 335 */ "typetoken ::= typename",
- /* 336 */ "typename ::= ID|STRING",
- /* 337 */ "signed ::= plus_num",
- /* 338 */ "signed ::= minus_num",
- /* 339 */ "carglist ::= carglist ccons",
- /* 340 */ "carglist ::=",
- /* 341 */ "ccons ::= NULL onconf",
- /* 342 */ "conslist_opt ::= COMMA conslist",
- /* 343 */ "conslist ::= conslist tconscomma tcons",
- /* 344 */ "conslist ::= tcons",
- /* 345 */ "tconscomma ::=",
- /* 346 */ "defer_subclause_opt ::= defer_subclause",
- /* 347 */ "resolvetype ::= raisetype",
- /* 348 */ "selectnowith ::= oneselect",
- /* 349 */ "oneselect ::= values",
- /* 350 */ "sclp ::= selcollist COMMA",
- /* 351 */ "as ::= ID|STRING",
- /* 352 */ "expr ::= term",
- /* 353 */ "likeop ::= LIKE_KW|MATCH",
- /* 354 */ "exprlist ::= nexprlist",
- /* 355 */ "nmnum ::= plus_num",
- /* 356 */ "nmnum ::= nm",
- /* 357 */ "nmnum ::= ON",
- /* 358 */ "nmnum ::= DELETE",
- /* 359 */ "nmnum ::= DEFAULT",
- /* 360 */ "plus_num ::= INTEGER|FLOAT",
- /* 361 */ "foreach_clause ::=",
- /* 362 */ "foreach_clause ::= FOR EACH ROW",
- /* 363 */ "trnm ::= nm",
- /* 364 */ "tridxby ::=",
- /* 365 */ "database_kw_opt ::= DATABASE",
- /* 366 */ "database_kw_opt ::=",
- /* 367 */ "kwcolumn_opt ::=",
- /* 368 */ "kwcolumn_opt ::= COLUMNKW",
- /* 369 */ "vtabarglist ::= vtabarg",
- /* 370 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 371 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 372 */ "anylist ::=",
- /* 373 */ "anylist ::= anylist LP anylist RP",
- /* 374 */ "anylist ::= anylist ANY",
- /* 375 */ "with ::=",
+ /* 135 */ "nulls ::= NULLS FIRST",
+ /* 136 */ "nulls ::= NULLS LAST",
+ /* 137 */ "nulls ::=",
+ /* 138 */ "groupby_opt ::=",
+ /* 139 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 140 */ "having_opt ::=",
+ /* 141 */ "having_opt ::= HAVING expr",
+ /* 142 */ "limit_opt ::=",
+ /* 143 */ "limit_opt ::= LIMIT expr",
+ /* 144 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 145 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 146 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
+ /* 147 */ "where_opt ::=",
+ /* 148 */ "where_opt ::= WHERE expr",
+ /* 149 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
+ /* 150 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 151 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 152 */ "setlist ::= nm EQ expr",
+ /* 153 */ "setlist ::= LP idlist RP EQ expr",
+ /* 154 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
+ /* 155 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
+ /* 156 */ "upsert ::=",
+ /* 157 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
+ /* 158 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
+ /* 159 */ "upsert ::= ON CONFLICT DO NOTHING",
+ /* 160 */ "insert_cmd ::= INSERT orconf",
+ /* 161 */ "insert_cmd ::= REPLACE",
+ /* 162 */ "idlist_opt ::=",
+ /* 163 */ "idlist_opt ::= LP idlist RP",
+ /* 164 */ "idlist ::= idlist COMMA nm",
+ /* 165 */ "idlist ::= nm",
+ /* 166 */ "expr ::= LP expr RP",
+ /* 167 */ "expr ::= ID|INDEXED",
+ /* 168 */ "expr ::= JOIN_KW",
+ /* 169 */ "expr ::= nm DOT nm",
+ /* 170 */ "expr ::= nm DOT nm DOT nm",
+ /* 171 */ "term ::= NULL|FLOAT|BLOB",
+ /* 172 */ "term ::= STRING",
+ /* 173 */ "term ::= INTEGER",
+ /* 174 */ "expr ::= VARIABLE",
+ /* 175 */ "expr ::= expr COLLATE ID|STRING",
+ /* 176 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 177 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
+ /* 178 */ "expr ::= ID|INDEXED LP STAR RP",
+ /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over",
+ /* 180 */ "expr ::= ID|INDEXED LP STAR RP filter_over",
+ /* 181 */ "term ::= CTIME_KW",
+ /* 182 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 183 */ "expr ::= expr AND expr",
+ /* 184 */ "expr ::= expr OR expr",
+ /* 185 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 186 */ "expr ::= expr EQ|NE expr",
+ /* 187 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 188 */ "expr ::= expr PLUS|MINUS expr",
+ /* 189 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 190 */ "expr ::= expr CONCAT expr",
+ /* 191 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 192 */ "expr ::= expr likeop expr",
+ /* 193 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 194 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 195 */ "expr ::= expr NOT NULL",
+ /* 196 */ "expr ::= expr IS expr",
+ /* 197 */ "expr ::= expr IS NOT expr",
+ /* 198 */ "expr ::= NOT expr",
+ /* 199 */ "expr ::= BITNOT expr",
+ /* 200 */ "expr ::= PLUS|MINUS expr",
+ /* 201 */ "between_op ::= BETWEEN",
+ /* 202 */ "between_op ::= NOT BETWEEN",
+ /* 203 */ "expr ::= expr between_op expr AND expr",
+ /* 204 */ "in_op ::= IN",
+ /* 205 */ "in_op ::= NOT IN",
+ /* 206 */ "expr ::= expr in_op LP exprlist RP",
+ /* 207 */ "expr ::= LP select RP",
+ /* 208 */ "expr ::= expr in_op LP select RP",
+ /* 209 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 210 */ "expr ::= EXISTS LP select RP",
+ /* 211 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 212 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 213 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 214 */ "case_else ::= ELSE expr",
+ /* 215 */ "case_else ::=",
+ /* 216 */ "case_operand ::= expr",
+ /* 217 */ "case_operand ::=",
+ /* 218 */ "exprlist ::=",
+ /* 219 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 220 */ "nexprlist ::= expr",
+ /* 221 */ "paren_exprlist ::=",
+ /* 222 */ "paren_exprlist ::= LP exprlist RP",
+ /* 223 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 224 */ "uniqueflag ::= UNIQUE",
+ /* 225 */ "uniqueflag ::=",
+ /* 226 */ "eidlist_opt ::=",
+ /* 227 */ "eidlist_opt ::= LP eidlist RP",
+ /* 228 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 229 */ "eidlist ::= nm collate sortorder",
+ /* 230 */ "collate ::=",
+ /* 231 */ "collate ::= COLLATE ID|STRING",
+ /* 232 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 233 */ "cmd ::= VACUUM vinto",
+ /* 234 */ "cmd ::= VACUUM nm vinto",
+ /* 235 */ "vinto ::= INTO expr",
+ /* 236 */ "vinto ::=",
+ /* 237 */ "cmd ::= PRAGMA nm dbnm",
+ /* 238 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 239 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 240 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 241 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 242 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 243 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 244 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 245 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 246 */ "trigger_time ::= BEFORE|AFTER",
+ /* 247 */ "trigger_time ::= INSTEAD OF",
+ /* 248 */ "trigger_time ::=",
+ /* 249 */ "trigger_event ::= DELETE|INSERT",
+ /* 250 */ "trigger_event ::= UPDATE",
+ /* 251 */ "trigger_event ::= UPDATE OF idlist",
+ /* 252 */ "when_clause ::=",
+ /* 253 */ "when_clause ::= WHEN expr",
+ /* 254 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 255 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 256 */ "trnm ::= nm DOT nm",
+ /* 257 */ "tridxby ::= INDEXED BY nm",
+ /* 258 */ "tridxby ::= NOT INDEXED",
+ /* 259 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
+ /* 260 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 261 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 262 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 263 */ "expr ::= RAISE LP IGNORE RP",
+ /* 264 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 265 */ "raisetype ::= ROLLBACK",
+ /* 266 */ "raisetype ::= ABORT",
+ /* 267 */ "raisetype ::= FAIL",
+ /* 268 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 269 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 270 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 271 */ "key_opt ::=",
+ /* 272 */ "key_opt ::= KEY expr",
+ /* 273 */ "cmd ::= REINDEX",
+ /* 274 */ "cmd ::= REINDEX nm dbnm",
+ /* 275 */ "cmd ::= ANALYZE",
+ /* 276 */ "cmd ::= ANALYZE nm dbnm",
+ /* 277 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 278 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 279 */ "add_column_fullname ::= fullname",
+ /* 280 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 281 */ "cmd ::= create_vtab",
+ /* 282 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 283 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 284 */ "vtabarg ::=",
+ /* 285 */ "vtabargtoken ::= ANY",
+ /* 286 */ "vtabargtoken ::= lp anylist RP",
+ /* 287 */ "lp ::= LP",
+ /* 288 */ "with ::= WITH wqlist",
+ /* 289 */ "with ::= WITH RECURSIVE wqlist",
+ /* 290 */ "wqlist ::= nm eidlist_opt AS LP select RP",
+ /* 291 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
+ /* 292 */ "windowdefn_list ::= windowdefn",
+ /* 293 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 294 */ "windowdefn ::= nm AS LP window RP",
+ /* 295 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 296 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 297 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 298 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 299 */ "window ::= frame_opt",
+ /* 300 */ "window ::= nm frame_opt",
+ /* 301 */ "frame_opt ::=",
+ /* 302 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 303 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 304 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 305 */ "frame_bound_s ::= frame_bound",
+ /* 306 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 307 */ "frame_bound_e ::= frame_bound",
+ /* 308 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 309 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 310 */ "frame_bound ::= CURRENT ROW",
+ /* 311 */ "frame_exclude_opt ::=",
+ /* 312 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 313 */ "frame_exclude ::= NO OTHERS",
+ /* 314 */ "frame_exclude ::= CURRENT ROW",
+ /* 315 */ "frame_exclude ::= GROUP|TIES",
+ /* 316 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 317 */ "filter_over ::= filter_clause over_clause",
+ /* 318 */ "filter_over ::= over_clause",
+ /* 319 */ "filter_over ::= filter_clause",
+ /* 320 */ "over_clause ::= OVER LP window RP",
+ /* 321 */ "over_clause ::= OVER nm",
+ /* 322 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 323 */ "input ::= cmdlist",
+ /* 324 */ "cmdlist ::= cmdlist ecmd",
+ /* 325 */ "cmdlist ::= ecmd",
+ /* 326 */ "ecmd ::= SEMI",
+ /* 327 */ "ecmd ::= cmdx SEMI",
+ /* 328 */ "ecmd ::= explain cmdx",
+ /* 329 */ "trans_opt ::=",
+ /* 330 */ "trans_opt ::= TRANSACTION",
+ /* 331 */ "trans_opt ::= TRANSACTION nm",
+ /* 332 */ "savepoint_opt ::= SAVEPOINT",
+ /* 333 */ "savepoint_opt ::=",
+ /* 334 */ "cmd ::= create_table create_table_args",
+ /* 335 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 336 */ "columnlist ::= columnname carglist",
+ /* 337 */ "nm ::= ID|INDEXED",
+ /* 338 */ "nm ::= STRING",
+ /* 339 */ "nm ::= JOIN_KW",
+ /* 340 */ "typetoken ::= typename",
+ /* 341 */ "typename ::= ID|STRING",
+ /* 342 */ "signed ::= plus_num",
+ /* 343 */ "signed ::= minus_num",
+ /* 344 */ "carglist ::= carglist ccons",
+ /* 345 */ "carglist ::=",
+ /* 346 */ "ccons ::= NULL onconf",
+ /* 347 */ "conslist_opt ::= COMMA conslist",
+ /* 348 */ "conslist ::= conslist tconscomma tcons",
+ /* 349 */ "conslist ::= tcons",
+ /* 350 */ "tconscomma ::=",
+ /* 351 */ "defer_subclause_opt ::= defer_subclause",
+ /* 352 */ "resolvetype ::= raisetype",
+ /* 353 */ "selectnowith ::= oneselect",
+ /* 354 */ "oneselect ::= values",
+ /* 355 */ "sclp ::= selcollist COMMA",
+ /* 356 */ "as ::= ID|STRING",
+ /* 357 */ "expr ::= term",
+ /* 358 */ "likeop ::= LIKE_KW|MATCH",
+ /* 359 */ "exprlist ::= nexprlist",
+ /* 360 */ "nmnum ::= plus_num",
+ /* 361 */ "nmnum ::= nm",
+ /* 362 */ "nmnum ::= ON",
+ /* 363 */ "nmnum ::= DELETE",
+ /* 364 */ "nmnum ::= DEFAULT",
+ /* 365 */ "plus_num ::= INTEGER|FLOAT",
+ /* 366 */ "foreach_clause ::=",
+ /* 367 */ "foreach_clause ::= FOR EACH ROW",
+ /* 368 */ "trnm ::= nm",
+ /* 369 */ "tridxby ::=",
+ /* 370 */ "database_kw_opt ::= DATABASE",
+ /* 371 */ "database_kw_opt ::=",
+ /* 372 */ "kwcolumn_opt ::=",
+ /* 373 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 374 */ "vtabarglist ::= vtabarg",
+ /* 375 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 376 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 377 */ "anylist ::=",
+ /* 378 */ "anylist ::= anylist LP anylist RP",
+ /* 379 */ "anylist ::= anylist ANY",
+ /* 380 */ "with ::=",
};
#endif /* NDEBUG */
@@ -150661,97 +151696,98 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 195: /* select */
- case 228: /* selectnowith */
- case 229: /* oneselect */
- case 241: /* values */
+ case 198: /* select */
+ case 231: /* selectnowith */
+ case 232: /* oneselect */
+ case 244: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy391));
+sqlite3SelectDelete(pParse->db, (yypminor->yy25));
}
break;
- case 206: /* term */
- case 207: /* expr */
- case 235: /* where_opt */
- case 237: /* having_opt */
- case 249: /* on_opt */
- case 264: /* case_operand */
- case 266: /* case_else */
- case 269: /* vinto */
- case 276: /* when_clause */
- case 281: /* key_opt */
- case 295: /* filter_opt */
+ case 209: /* term */
+ case 210: /* expr */
+ case 238: /* where_opt */
+ case 240: /* having_opt */
+ case 252: /* on_opt */
+ case 268: /* case_operand */
+ case 270: /* case_else */
+ case 273: /* vinto */
+ case 280: /* when_clause */
+ case 285: /* key_opt */
+ case 299: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy102));
+sqlite3ExprDelete(pParse->db, (yypminor->yy46));
}
break;
- case 211: /* eidlist_opt */
- case 220: /* sortlist */
- case 221: /* eidlist */
- case 233: /* selcollist */
- case 236: /* groupby_opt */
- case 238: /* orderby_opt */
- case 242: /* nexprlist */
- case 243: /* sclp */
- case 251: /* exprlist */
- case 255: /* setlist */
- case 263: /* paren_exprlist */
- case 265: /* case_exprlist */
- case 294: /* part_opt */
+ case 214: /* eidlist_opt */
+ case 223: /* sortlist */
+ case 224: /* eidlist */
+ case 236: /* selcollist */
+ case 239: /* groupby_opt */
+ case 241: /* orderby_opt */
+ case 245: /* nexprlist */
+ case 246: /* sclp */
+ case 254: /* exprlist */
+ case 259: /* setlist */
+ case 267: /* paren_exprlist */
+ case 269: /* case_exprlist */
+ case 298: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy94));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy138));
}
break;
- case 227: /* fullname */
- case 234: /* from */
- case 245: /* seltablist */
- case 246: /* stl_prefix */
- case 252: /* xfullname */
+ case 230: /* fullname */
+ case 237: /* from */
+ case 248: /* seltablist */
+ case 249: /* stl_prefix */
+ case 255: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy407));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy609));
}
break;
- case 230: /* wqlist */
+ case 233: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy243));
+sqlite3WithDelete(pParse->db, (yypminor->yy297));
}
break;
- case 240: /* window_clause */
- case 290: /* windowdefn_list */
+ case 243: /* window_clause */
+ case 294: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy379));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy455));
}
break;
- case 250: /* using_opt */
- case 253: /* idlist */
- case 257: /* idlist_opt */
+ case 253: /* using_opt */
+ case 256: /* idlist */
+ case 261: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy76));
+sqlite3IdListDelete(pParse->db, (yypminor->yy406));
}
break;
- case 259: /* over_clause */
- case 291: /* windowdefn */
- case 292: /* window */
- case 293: /* frame_opt */
+ case 263: /* filter_over */
+ case 295: /* windowdefn */
+ case 296: /* window */
+ case 297: /* frame_opt */
+ case 300: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy379));
+sqlite3WindowDelete(pParse->db, (yypminor->yy455));
}
break;
- case 272: /* trigger_cmd_list */
- case 277: /* trigger_cmd */
+ case 276: /* trigger_cmd_list */
+ case 281: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy11));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy527));
}
break;
- case 274: /* trigger_event */
+ case 278: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy298).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy572).b);
}
break;
- case 297: /* frame_bound */
- case 298: /* frame_bound_s */
- case 299: /* frame_bound_e */
+ case 302: /* frame_bound */
+ case 303: /* frame_bound_s */
+ case 304: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy389).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy57).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -150877,15 +151913,18 @@ static YYACTIONTYPE yy_find_shift_action(
do{
i = yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); */
+ assert( i<=YY_ACTTAB_COUNT );
+ assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
assert( iLookAhead!=YYNOCODE );
assert( iLookAhead < YYNTOKEN );
i += iLookAhead;
- if( i>=YY_NLOOKAHEAD || yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)YY_NLOOKAHEAD );
+ if( yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
- && (iFallback = yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
+ iFallback = yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -150900,16 +151939,8 @@ static YYACTIONTYPE yy_find_shift_action(
#ifdef YYWILDCARD
{
int j = i - iLookAhead + YYWILDCARD;
- if(
-#if YY_SHIFT_MIN+YYWILDCARD<0
- j>=0 &&
-#endif
-#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
- j<YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) &&
- yy_lookahead[j]==YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
+ if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -150923,6 +151954,7 @@ static YYACTIONTYPE yy_find_shift_action(
#endif /* YYWILDCARD */
return yy_default[stateno];
}else{
+ assert( i>=0 && i<sizeof(yy_action)/sizeof(yy_action[0]) );
return yy_action[i];
}
}while(1);
@@ -151046,382 +152078,387 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 180, /* (0) explain ::= EXPLAIN */
- 180, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 179, /* (2) cmdx ::= cmd */
- 181, /* (3) cmd ::= BEGIN transtype trans_opt */
- 182, /* (4) transtype ::= */
- 182, /* (5) transtype ::= DEFERRED */
- 182, /* (6) transtype ::= IMMEDIATE */
- 182, /* (7) transtype ::= EXCLUSIVE */
- 181, /* (8) cmd ::= COMMIT|END trans_opt */
- 181, /* (9) cmd ::= ROLLBACK trans_opt */
- 181, /* (10) cmd ::= SAVEPOINT nm */
- 181, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 181, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 186, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 188, /* (14) createkw ::= CREATE */
- 190, /* (15) ifnotexists ::= */
- 190, /* (16) ifnotexists ::= IF NOT EXISTS */
- 189, /* (17) temp ::= TEMP */
- 189, /* (18) temp ::= */
- 187, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
- 187, /* (20) create_table_args ::= AS select */
- 194, /* (21) table_options ::= */
- 194, /* (22) table_options ::= WITHOUT nm */
- 196, /* (23) columnname ::= nm typetoken */
- 198, /* (24) typetoken ::= */
- 198, /* (25) typetoken ::= typename LP signed RP */
- 198, /* (26) typetoken ::= typename LP signed COMMA signed RP */
- 199, /* (27) typename ::= typename ID|STRING */
- 203, /* (28) scanpt ::= */
- 204, /* (29) scantok ::= */
- 205, /* (30) ccons ::= CONSTRAINT nm */
- 205, /* (31) ccons ::= DEFAULT scantok term */
- 205, /* (32) ccons ::= DEFAULT LP expr RP */
- 205, /* (33) ccons ::= DEFAULT PLUS scantok term */
- 205, /* (34) ccons ::= DEFAULT MINUS scantok term */
- 205, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */
- 205, /* (36) ccons ::= NOT NULL onconf */
- 205, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 205, /* (38) ccons ::= UNIQUE onconf */
- 205, /* (39) ccons ::= CHECK LP expr RP */
- 205, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */
- 205, /* (41) ccons ::= defer_subclause */
- 205, /* (42) ccons ::= COLLATE ID|STRING */
- 210, /* (43) autoinc ::= */
- 210, /* (44) autoinc ::= AUTOINCR */
- 212, /* (45) refargs ::= */
- 212, /* (46) refargs ::= refargs refarg */
- 214, /* (47) refarg ::= MATCH nm */
- 214, /* (48) refarg ::= ON INSERT refact */
- 214, /* (49) refarg ::= ON DELETE refact */
- 214, /* (50) refarg ::= ON UPDATE refact */
- 215, /* (51) refact ::= SET NULL */
- 215, /* (52) refact ::= SET DEFAULT */
- 215, /* (53) refact ::= CASCADE */
- 215, /* (54) refact ::= RESTRICT */
- 215, /* (55) refact ::= NO ACTION */
- 213, /* (56) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 213, /* (57) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 216, /* (58) init_deferred_pred_opt ::= */
- 216, /* (59) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 216, /* (60) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 193, /* (61) conslist_opt ::= */
- 218, /* (62) tconscomma ::= COMMA */
- 219, /* (63) tcons ::= CONSTRAINT nm */
- 219, /* (64) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 219, /* (65) tcons ::= UNIQUE LP sortlist RP onconf */
- 219, /* (66) tcons ::= CHECK LP expr RP onconf */
- 219, /* (67) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 222, /* (68) defer_subclause_opt ::= */
- 208, /* (69) onconf ::= */
- 208, /* (70) onconf ::= ON CONFLICT resolvetype */
- 223, /* (71) orconf ::= */
- 223, /* (72) orconf ::= OR resolvetype */
- 224, /* (73) resolvetype ::= IGNORE */
- 224, /* (74) resolvetype ::= REPLACE */
- 181, /* (75) cmd ::= DROP TABLE ifexists fullname */
- 226, /* (76) ifexists ::= IF EXISTS */
- 226, /* (77) ifexists ::= */
- 181, /* (78) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 181, /* (79) cmd ::= DROP VIEW ifexists fullname */
- 181, /* (80) cmd ::= select */
- 195, /* (81) select ::= WITH wqlist selectnowith */
- 195, /* (82) select ::= WITH RECURSIVE wqlist selectnowith */
- 195, /* (83) select ::= selectnowith */
- 228, /* (84) selectnowith ::= selectnowith multiselect_op oneselect */
- 231, /* (85) multiselect_op ::= UNION */
- 231, /* (86) multiselect_op ::= UNION ALL */
- 231, /* (87) multiselect_op ::= EXCEPT|INTERSECT */
- 229, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 229, /* (89) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 241, /* (90) values ::= VALUES LP nexprlist RP */
- 241, /* (91) values ::= values COMMA LP nexprlist RP */
- 232, /* (92) distinct ::= DISTINCT */
- 232, /* (93) distinct ::= ALL */
- 232, /* (94) distinct ::= */
- 243, /* (95) sclp ::= */
- 233, /* (96) selcollist ::= sclp scanpt expr scanpt as */
- 233, /* (97) selcollist ::= sclp scanpt STAR */
- 233, /* (98) selcollist ::= sclp scanpt nm DOT STAR */
- 244, /* (99) as ::= AS nm */
- 244, /* (100) as ::= */
- 234, /* (101) from ::= */
- 234, /* (102) from ::= FROM seltablist */
- 246, /* (103) stl_prefix ::= seltablist joinop */
- 246, /* (104) stl_prefix ::= */
- 245, /* (105) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
- 245, /* (106) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
- 245, /* (107) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
- 245, /* (108) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
- 191, /* (109) dbnm ::= */
- 191, /* (110) dbnm ::= DOT nm */
- 227, /* (111) fullname ::= nm */
- 227, /* (112) fullname ::= nm DOT nm */
- 252, /* (113) xfullname ::= nm */
- 252, /* (114) xfullname ::= nm DOT nm */
- 252, /* (115) xfullname ::= nm DOT nm AS nm */
- 252, /* (116) xfullname ::= nm AS nm */
- 247, /* (117) joinop ::= COMMA|JOIN */
- 247, /* (118) joinop ::= JOIN_KW JOIN */
- 247, /* (119) joinop ::= JOIN_KW nm JOIN */
- 247, /* (120) joinop ::= JOIN_KW nm nm JOIN */
- 249, /* (121) on_opt ::= ON expr */
- 249, /* (122) on_opt ::= */
- 248, /* (123) indexed_opt ::= */
- 248, /* (124) indexed_opt ::= INDEXED BY nm */
- 248, /* (125) indexed_opt ::= NOT INDEXED */
- 250, /* (126) using_opt ::= USING LP idlist RP */
- 250, /* (127) using_opt ::= */
- 238, /* (128) orderby_opt ::= */
- 238, /* (129) orderby_opt ::= ORDER BY sortlist */
- 220, /* (130) sortlist ::= sortlist COMMA expr sortorder */
- 220, /* (131) sortlist ::= expr sortorder */
- 209, /* (132) sortorder ::= ASC */
- 209, /* (133) sortorder ::= DESC */
- 209, /* (134) sortorder ::= */
- 236, /* (135) groupby_opt ::= */
- 236, /* (136) groupby_opt ::= GROUP BY nexprlist */
- 237, /* (137) having_opt ::= */
- 237, /* (138) having_opt ::= HAVING expr */
- 239, /* (139) limit_opt ::= */
- 239, /* (140) limit_opt ::= LIMIT expr */
- 239, /* (141) limit_opt ::= LIMIT expr OFFSET expr */
- 239, /* (142) limit_opt ::= LIMIT expr COMMA expr */
- 181, /* (143) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- 235, /* (144) where_opt ::= */
- 235, /* (145) where_opt ::= WHERE expr */
- 181, /* (146) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- 255, /* (147) setlist ::= setlist COMMA nm EQ expr */
- 255, /* (148) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 255, /* (149) setlist ::= nm EQ expr */
- 255, /* (150) setlist ::= LP idlist RP EQ expr */
- 181, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 181, /* (152) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- 258, /* (153) upsert ::= */
- 258, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- 258, /* (155) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- 258, /* (156) upsert ::= ON CONFLICT DO NOTHING */
- 256, /* (157) insert_cmd ::= INSERT orconf */
- 256, /* (158) insert_cmd ::= REPLACE */
- 257, /* (159) idlist_opt ::= */
- 257, /* (160) idlist_opt ::= LP idlist RP */
- 253, /* (161) idlist ::= idlist COMMA nm */
- 253, /* (162) idlist ::= nm */
- 207, /* (163) expr ::= LP expr RP */
- 207, /* (164) expr ::= ID|INDEXED */
- 207, /* (165) expr ::= JOIN_KW */
- 207, /* (166) expr ::= nm DOT nm */
- 207, /* (167) expr ::= nm DOT nm DOT nm */
- 206, /* (168) term ::= NULL|FLOAT|BLOB */
- 206, /* (169) term ::= STRING */
- 206, /* (170) term ::= INTEGER */
- 207, /* (171) expr ::= VARIABLE */
- 207, /* (172) expr ::= expr COLLATE ID|STRING */
- 207, /* (173) expr ::= CAST LP expr AS typetoken RP */
- 207, /* (174) expr ::= ID|INDEXED LP distinct exprlist RP */
- 207, /* (175) expr ::= ID|INDEXED LP STAR RP */
- 207, /* (176) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- 207, /* (177) expr ::= ID|INDEXED LP STAR RP over_clause */
- 206, /* (178) term ::= CTIME_KW */
- 207, /* (179) expr ::= LP nexprlist COMMA expr RP */
- 207, /* (180) expr ::= expr AND expr */
- 207, /* (181) expr ::= expr OR expr */
- 207, /* (182) expr ::= expr LT|GT|GE|LE expr */
- 207, /* (183) expr ::= expr EQ|NE expr */
- 207, /* (184) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 207, /* (185) expr ::= expr PLUS|MINUS expr */
- 207, /* (186) expr ::= expr STAR|SLASH|REM expr */
- 207, /* (187) expr ::= expr CONCAT expr */
- 260, /* (188) likeop ::= NOT LIKE_KW|MATCH */
- 207, /* (189) expr ::= expr likeop expr */
- 207, /* (190) expr ::= expr likeop expr ESCAPE expr */
- 207, /* (191) expr ::= expr ISNULL|NOTNULL */
- 207, /* (192) expr ::= expr NOT NULL */
- 207, /* (193) expr ::= expr IS expr */
- 207, /* (194) expr ::= expr IS NOT expr */
- 207, /* (195) expr ::= NOT expr */
- 207, /* (196) expr ::= BITNOT expr */
- 207, /* (197) expr ::= PLUS|MINUS expr */
- 261, /* (198) between_op ::= BETWEEN */
- 261, /* (199) between_op ::= NOT BETWEEN */
- 207, /* (200) expr ::= expr between_op expr AND expr */
- 262, /* (201) in_op ::= IN */
- 262, /* (202) in_op ::= NOT IN */
- 207, /* (203) expr ::= expr in_op LP exprlist RP */
- 207, /* (204) expr ::= LP select RP */
- 207, /* (205) expr ::= expr in_op LP select RP */
- 207, /* (206) expr ::= expr in_op nm dbnm paren_exprlist */
- 207, /* (207) expr ::= EXISTS LP select RP */
- 207, /* (208) expr ::= CASE case_operand case_exprlist case_else END */
- 265, /* (209) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 265, /* (210) case_exprlist ::= WHEN expr THEN expr */
- 266, /* (211) case_else ::= ELSE expr */
- 266, /* (212) case_else ::= */
- 264, /* (213) case_operand ::= expr */
- 264, /* (214) case_operand ::= */
- 251, /* (215) exprlist ::= */
- 242, /* (216) nexprlist ::= nexprlist COMMA expr */
- 242, /* (217) nexprlist ::= expr */
- 263, /* (218) paren_exprlist ::= */
- 263, /* (219) paren_exprlist ::= LP exprlist RP */
- 181, /* (220) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 267, /* (221) uniqueflag ::= UNIQUE */
- 267, /* (222) uniqueflag ::= */
- 211, /* (223) eidlist_opt ::= */
- 211, /* (224) eidlist_opt ::= LP eidlist RP */
- 221, /* (225) eidlist ::= eidlist COMMA nm collate sortorder */
- 221, /* (226) eidlist ::= nm collate sortorder */
- 268, /* (227) collate ::= */
- 268, /* (228) collate ::= COLLATE ID|STRING */
- 181, /* (229) cmd ::= DROP INDEX ifexists fullname */
- 181, /* (230) cmd ::= VACUUM vinto */
- 181, /* (231) cmd ::= VACUUM nm vinto */
- 269, /* (232) vinto ::= INTO expr */
- 269, /* (233) vinto ::= */
- 181, /* (234) cmd ::= PRAGMA nm dbnm */
- 181, /* (235) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 181, /* (236) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 181, /* (237) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 181, /* (238) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 201, /* (239) plus_num ::= PLUS INTEGER|FLOAT */
- 202, /* (240) minus_num ::= MINUS INTEGER|FLOAT */
- 181, /* (241) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 271, /* (242) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 273, /* (243) trigger_time ::= BEFORE|AFTER */
- 273, /* (244) trigger_time ::= INSTEAD OF */
- 273, /* (245) trigger_time ::= */
- 274, /* (246) trigger_event ::= DELETE|INSERT */
- 274, /* (247) trigger_event ::= UPDATE */
- 274, /* (248) trigger_event ::= UPDATE OF idlist */
- 276, /* (249) when_clause ::= */
- 276, /* (250) when_clause ::= WHEN expr */
- 272, /* (251) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 272, /* (252) trigger_cmd_list ::= trigger_cmd SEMI */
- 278, /* (253) trnm ::= nm DOT nm */
- 279, /* (254) tridxby ::= INDEXED BY nm */
- 279, /* (255) tridxby ::= NOT INDEXED */
- 277, /* (256) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- 277, /* (257) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 277, /* (258) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 277, /* (259) trigger_cmd ::= scanpt select scanpt */
- 207, /* (260) expr ::= RAISE LP IGNORE RP */
- 207, /* (261) expr ::= RAISE LP raisetype COMMA nm RP */
- 225, /* (262) raisetype ::= ROLLBACK */
- 225, /* (263) raisetype ::= ABORT */
- 225, /* (264) raisetype ::= FAIL */
- 181, /* (265) cmd ::= DROP TRIGGER ifexists fullname */
- 181, /* (266) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 181, /* (267) cmd ::= DETACH database_kw_opt expr */
- 281, /* (268) key_opt ::= */
- 281, /* (269) key_opt ::= KEY expr */
- 181, /* (270) cmd ::= REINDEX */
- 181, /* (271) cmd ::= REINDEX nm dbnm */
- 181, /* (272) cmd ::= ANALYZE */
- 181, /* (273) cmd ::= ANALYZE nm dbnm */
- 181, /* (274) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 181, /* (275) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 282, /* (276) add_column_fullname ::= fullname */
- 181, /* (277) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 181, /* (278) cmd ::= create_vtab */
- 181, /* (279) cmd ::= create_vtab LP vtabarglist RP */
- 284, /* (280) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 286, /* (281) vtabarg ::= */
- 287, /* (282) vtabargtoken ::= ANY */
- 287, /* (283) vtabargtoken ::= lp anylist RP */
- 288, /* (284) lp ::= LP */
- 254, /* (285) with ::= WITH wqlist */
- 254, /* (286) with ::= WITH RECURSIVE wqlist */
- 230, /* (287) wqlist ::= nm eidlist_opt AS LP select RP */
- 230, /* (288) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- 290, /* (289) windowdefn_list ::= windowdefn */
- 290, /* (290) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 291, /* (291) windowdefn ::= nm AS LP window RP */
- 292, /* (292) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 292, /* (293) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 292, /* (294) window ::= ORDER BY sortlist frame_opt */
- 292, /* (295) window ::= nm ORDER BY sortlist frame_opt */
- 292, /* (296) window ::= frame_opt */
- 292, /* (297) window ::= nm frame_opt */
- 293, /* (298) frame_opt ::= */
- 293, /* (299) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 293, /* (300) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 296, /* (301) range_or_rows ::= RANGE|ROWS|GROUPS */
- 298, /* (302) frame_bound_s ::= frame_bound */
- 298, /* (303) frame_bound_s ::= UNBOUNDED PRECEDING */
- 299, /* (304) frame_bound_e ::= frame_bound */
- 299, /* (305) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 297, /* (306) frame_bound ::= expr PRECEDING|FOLLOWING */
- 297, /* (307) frame_bound ::= CURRENT ROW */
- 300, /* (308) frame_exclude_opt ::= */
- 300, /* (309) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 301, /* (310) frame_exclude ::= NO OTHERS */
- 301, /* (311) frame_exclude ::= CURRENT ROW */
- 301, /* (312) frame_exclude ::= GROUP|TIES */
- 240, /* (313) window_clause ::= WINDOW windowdefn_list */
- 259, /* (314) over_clause ::= filter_opt OVER LP window RP */
- 259, /* (315) over_clause ::= filter_opt OVER nm */
- 295, /* (316) filter_opt ::= */
- 295, /* (317) filter_opt ::= FILTER LP WHERE expr RP */
- 176, /* (318) input ::= cmdlist */
- 177, /* (319) cmdlist ::= cmdlist ecmd */
- 177, /* (320) cmdlist ::= ecmd */
- 178, /* (321) ecmd ::= SEMI */
- 178, /* (322) ecmd ::= cmdx SEMI */
- 178, /* (323) ecmd ::= explain cmdx */
- 183, /* (324) trans_opt ::= */
- 183, /* (325) trans_opt ::= TRANSACTION */
- 183, /* (326) trans_opt ::= TRANSACTION nm */
- 185, /* (327) savepoint_opt ::= SAVEPOINT */
- 185, /* (328) savepoint_opt ::= */
- 181, /* (329) cmd ::= create_table create_table_args */
- 192, /* (330) columnlist ::= columnlist COMMA columnname carglist */
- 192, /* (331) columnlist ::= columnname carglist */
- 184, /* (332) nm ::= ID|INDEXED */
- 184, /* (333) nm ::= STRING */
- 184, /* (334) nm ::= JOIN_KW */
- 198, /* (335) typetoken ::= typename */
- 199, /* (336) typename ::= ID|STRING */
- 200, /* (337) signed ::= plus_num */
- 200, /* (338) signed ::= minus_num */
- 197, /* (339) carglist ::= carglist ccons */
- 197, /* (340) carglist ::= */
- 205, /* (341) ccons ::= NULL onconf */
- 193, /* (342) conslist_opt ::= COMMA conslist */
- 217, /* (343) conslist ::= conslist tconscomma tcons */
- 217, /* (344) conslist ::= tcons */
- 218, /* (345) tconscomma ::= */
- 222, /* (346) defer_subclause_opt ::= defer_subclause */
- 224, /* (347) resolvetype ::= raisetype */
- 228, /* (348) selectnowith ::= oneselect */
- 229, /* (349) oneselect ::= values */
- 243, /* (350) sclp ::= selcollist COMMA */
- 244, /* (351) as ::= ID|STRING */
- 207, /* (352) expr ::= term */
- 260, /* (353) likeop ::= LIKE_KW|MATCH */
- 251, /* (354) exprlist ::= nexprlist */
- 270, /* (355) nmnum ::= plus_num */
- 270, /* (356) nmnum ::= nm */
- 270, /* (357) nmnum ::= ON */
- 270, /* (358) nmnum ::= DELETE */
- 270, /* (359) nmnum ::= DEFAULT */
- 201, /* (360) plus_num ::= INTEGER|FLOAT */
- 275, /* (361) foreach_clause ::= */
- 275, /* (362) foreach_clause ::= FOR EACH ROW */
- 278, /* (363) trnm ::= nm */
- 279, /* (364) tridxby ::= */
- 280, /* (365) database_kw_opt ::= DATABASE */
- 280, /* (366) database_kw_opt ::= */
- 283, /* (367) kwcolumn_opt ::= */
- 283, /* (368) kwcolumn_opt ::= COLUMNKW */
- 285, /* (369) vtabarglist ::= vtabarg */
- 285, /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */
- 286, /* (371) vtabarg ::= vtabarg vtabargtoken */
- 289, /* (372) anylist ::= */
- 289, /* (373) anylist ::= anylist LP anylist RP */
- 289, /* (374) anylist ::= anylist ANY */
- 254, /* (375) with ::= */
+ 183, /* (0) explain ::= EXPLAIN */
+ 183, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 182, /* (2) cmdx ::= cmd */
+ 184, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 185, /* (4) transtype ::= */
+ 185, /* (5) transtype ::= DEFERRED */
+ 185, /* (6) transtype ::= IMMEDIATE */
+ 185, /* (7) transtype ::= EXCLUSIVE */
+ 184, /* (8) cmd ::= COMMIT|END trans_opt */
+ 184, /* (9) cmd ::= ROLLBACK trans_opt */
+ 184, /* (10) cmd ::= SAVEPOINT nm */
+ 184, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 184, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 189, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 191, /* (14) createkw ::= CREATE */
+ 193, /* (15) ifnotexists ::= */
+ 193, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 192, /* (17) temp ::= TEMP */
+ 192, /* (18) temp ::= */
+ 190, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
+ 190, /* (20) create_table_args ::= AS select */
+ 197, /* (21) table_options ::= */
+ 197, /* (22) table_options ::= WITHOUT nm */
+ 199, /* (23) columnname ::= nm typetoken */
+ 201, /* (24) typetoken ::= */
+ 201, /* (25) typetoken ::= typename LP signed RP */
+ 201, /* (26) typetoken ::= typename LP signed COMMA signed RP */
+ 202, /* (27) typename ::= typename ID|STRING */
+ 206, /* (28) scanpt ::= */
+ 207, /* (29) scantok ::= */
+ 208, /* (30) ccons ::= CONSTRAINT nm */
+ 208, /* (31) ccons ::= DEFAULT scantok term */
+ 208, /* (32) ccons ::= DEFAULT LP expr RP */
+ 208, /* (33) ccons ::= DEFAULT PLUS scantok term */
+ 208, /* (34) ccons ::= DEFAULT MINUS scantok term */
+ 208, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */
+ 208, /* (36) ccons ::= NOT NULL onconf */
+ 208, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 208, /* (38) ccons ::= UNIQUE onconf */
+ 208, /* (39) ccons ::= CHECK LP expr RP */
+ 208, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 208, /* (41) ccons ::= defer_subclause */
+ 208, /* (42) ccons ::= COLLATE ID|STRING */
+ 213, /* (43) autoinc ::= */
+ 213, /* (44) autoinc ::= AUTOINCR */
+ 215, /* (45) refargs ::= */
+ 215, /* (46) refargs ::= refargs refarg */
+ 217, /* (47) refarg ::= MATCH nm */
+ 217, /* (48) refarg ::= ON INSERT refact */
+ 217, /* (49) refarg ::= ON DELETE refact */
+ 217, /* (50) refarg ::= ON UPDATE refact */
+ 218, /* (51) refact ::= SET NULL */
+ 218, /* (52) refact ::= SET DEFAULT */
+ 218, /* (53) refact ::= CASCADE */
+ 218, /* (54) refact ::= RESTRICT */
+ 218, /* (55) refact ::= NO ACTION */
+ 216, /* (56) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 216, /* (57) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 219, /* (58) init_deferred_pred_opt ::= */
+ 219, /* (59) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 219, /* (60) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 196, /* (61) conslist_opt ::= */
+ 221, /* (62) tconscomma ::= COMMA */
+ 222, /* (63) tcons ::= CONSTRAINT nm */
+ 222, /* (64) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 222, /* (65) tcons ::= UNIQUE LP sortlist RP onconf */
+ 222, /* (66) tcons ::= CHECK LP expr RP onconf */
+ 222, /* (67) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 225, /* (68) defer_subclause_opt ::= */
+ 211, /* (69) onconf ::= */
+ 211, /* (70) onconf ::= ON CONFLICT resolvetype */
+ 226, /* (71) orconf ::= */
+ 226, /* (72) orconf ::= OR resolvetype */
+ 227, /* (73) resolvetype ::= IGNORE */
+ 227, /* (74) resolvetype ::= REPLACE */
+ 184, /* (75) cmd ::= DROP TABLE ifexists fullname */
+ 229, /* (76) ifexists ::= IF EXISTS */
+ 229, /* (77) ifexists ::= */
+ 184, /* (78) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 184, /* (79) cmd ::= DROP VIEW ifexists fullname */
+ 184, /* (80) cmd ::= select */
+ 198, /* (81) select ::= WITH wqlist selectnowith */
+ 198, /* (82) select ::= WITH RECURSIVE wqlist selectnowith */
+ 198, /* (83) select ::= selectnowith */
+ 231, /* (84) selectnowith ::= selectnowith multiselect_op oneselect */
+ 234, /* (85) multiselect_op ::= UNION */
+ 234, /* (86) multiselect_op ::= UNION ALL */
+ 234, /* (87) multiselect_op ::= EXCEPT|INTERSECT */
+ 232, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 232, /* (89) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 244, /* (90) values ::= VALUES LP nexprlist RP */
+ 244, /* (91) values ::= values COMMA LP nexprlist RP */
+ 235, /* (92) distinct ::= DISTINCT */
+ 235, /* (93) distinct ::= ALL */
+ 235, /* (94) distinct ::= */
+ 246, /* (95) sclp ::= */
+ 236, /* (96) selcollist ::= sclp scanpt expr scanpt as */
+ 236, /* (97) selcollist ::= sclp scanpt STAR */
+ 236, /* (98) selcollist ::= sclp scanpt nm DOT STAR */
+ 247, /* (99) as ::= AS nm */
+ 247, /* (100) as ::= */
+ 237, /* (101) from ::= */
+ 237, /* (102) from ::= FROM seltablist */
+ 249, /* (103) stl_prefix ::= seltablist joinop */
+ 249, /* (104) stl_prefix ::= */
+ 248, /* (105) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ 248, /* (106) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
+ 248, /* (107) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ 248, /* (108) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ 194, /* (109) dbnm ::= */
+ 194, /* (110) dbnm ::= DOT nm */
+ 230, /* (111) fullname ::= nm */
+ 230, /* (112) fullname ::= nm DOT nm */
+ 255, /* (113) xfullname ::= nm */
+ 255, /* (114) xfullname ::= nm DOT nm */
+ 255, /* (115) xfullname ::= nm DOT nm AS nm */
+ 255, /* (116) xfullname ::= nm AS nm */
+ 250, /* (117) joinop ::= COMMA|JOIN */
+ 250, /* (118) joinop ::= JOIN_KW JOIN */
+ 250, /* (119) joinop ::= JOIN_KW nm JOIN */
+ 250, /* (120) joinop ::= JOIN_KW nm nm JOIN */
+ 252, /* (121) on_opt ::= ON expr */
+ 252, /* (122) on_opt ::= */
+ 251, /* (123) indexed_opt ::= */
+ 251, /* (124) indexed_opt ::= INDEXED BY nm */
+ 251, /* (125) indexed_opt ::= NOT INDEXED */
+ 253, /* (126) using_opt ::= USING LP idlist RP */
+ 253, /* (127) using_opt ::= */
+ 241, /* (128) orderby_opt ::= */
+ 241, /* (129) orderby_opt ::= ORDER BY sortlist */
+ 223, /* (130) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 223, /* (131) sortlist ::= expr sortorder nulls */
+ 212, /* (132) sortorder ::= ASC */
+ 212, /* (133) sortorder ::= DESC */
+ 212, /* (134) sortorder ::= */
+ 257, /* (135) nulls ::= NULLS FIRST */
+ 257, /* (136) nulls ::= NULLS LAST */
+ 257, /* (137) nulls ::= */
+ 239, /* (138) groupby_opt ::= */
+ 239, /* (139) groupby_opt ::= GROUP BY nexprlist */
+ 240, /* (140) having_opt ::= */
+ 240, /* (141) having_opt ::= HAVING expr */
+ 242, /* (142) limit_opt ::= */
+ 242, /* (143) limit_opt ::= LIMIT expr */
+ 242, /* (144) limit_opt ::= LIMIT expr OFFSET expr */
+ 242, /* (145) limit_opt ::= LIMIT expr COMMA expr */
+ 184, /* (146) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ 238, /* (147) where_opt ::= */
+ 238, /* (148) where_opt ::= WHERE expr */
+ 184, /* (149) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ 259, /* (150) setlist ::= setlist COMMA nm EQ expr */
+ 259, /* (151) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 259, /* (152) setlist ::= nm EQ expr */
+ 259, /* (153) setlist ::= LP idlist RP EQ expr */
+ 184, /* (154) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 184, /* (155) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ 262, /* (156) upsert ::= */
+ 262, /* (157) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+ 262, /* (158) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+ 262, /* (159) upsert ::= ON CONFLICT DO NOTHING */
+ 260, /* (160) insert_cmd ::= INSERT orconf */
+ 260, /* (161) insert_cmd ::= REPLACE */
+ 261, /* (162) idlist_opt ::= */
+ 261, /* (163) idlist_opt ::= LP idlist RP */
+ 256, /* (164) idlist ::= idlist COMMA nm */
+ 256, /* (165) idlist ::= nm */
+ 210, /* (166) expr ::= LP expr RP */
+ 210, /* (167) expr ::= ID|INDEXED */
+ 210, /* (168) expr ::= JOIN_KW */
+ 210, /* (169) expr ::= nm DOT nm */
+ 210, /* (170) expr ::= nm DOT nm DOT nm */
+ 209, /* (171) term ::= NULL|FLOAT|BLOB */
+ 209, /* (172) term ::= STRING */
+ 209, /* (173) term ::= INTEGER */
+ 210, /* (174) expr ::= VARIABLE */
+ 210, /* (175) expr ::= expr COLLATE ID|STRING */
+ 210, /* (176) expr ::= CAST LP expr AS typetoken RP */
+ 210, /* (177) expr ::= ID|INDEXED LP distinct exprlist RP */
+ 210, /* (178) expr ::= ID|INDEXED LP STAR RP */
+ 210, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
+ 210, /* (180) expr ::= ID|INDEXED LP STAR RP filter_over */
+ 209, /* (181) term ::= CTIME_KW */
+ 210, /* (182) expr ::= LP nexprlist COMMA expr RP */
+ 210, /* (183) expr ::= expr AND expr */
+ 210, /* (184) expr ::= expr OR expr */
+ 210, /* (185) expr ::= expr LT|GT|GE|LE expr */
+ 210, /* (186) expr ::= expr EQ|NE expr */
+ 210, /* (187) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 210, /* (188) expr ::= expr PLUS|MINUS expr */
+ 210, /* (189) expr ::= expr STAR|SLASH|REM expr */
+ 210, /* (190) expr ::= expr CONCAT expr */
+ 264, /* (191) likeop ::= NOT LIKE_KW|MATCH */
+ 210, /* (192) expr ::= expr likeop expr */
+ 210, /* (193) expr ::= expr likeop expr ESCAPE expr */
+ 210, /* (194) expr ::= expr ISNULL|NOTNULL */
+ 210, /* (195) expr ::= expr NOT NULL */
+ 210, /* (196) expr ::= expr IS expr */
+ 210, /* (197) expr ::= expr IS NOT expr */
+ 210, /* (198) expr ::= NOT expr */
+ 210, /* (199) expr ::= BITNOT expr */
+ 210, /* (200) expr ::= PLUS|MINUS expr */
+ 265, /* (201) between_op ::= BETWEEN */
+ 265, /* (202) between_op ::= NOT BETWEEN */
+ 210, /* (203) expr ::= expr between_op expr AND expr */
+ 266, /* (204) in_op ::= IN */
+ 266, /* (205) in_op ::= NOT IN */
+ 210, /* (206) expr ::= expr in_op LP exprlist RP */
+ 210, /* (207) expr ::= LP select RP */
+ 210, /* (208) expr ::= expr in_op LP select RP */
+ 210, /* (209) expr ::= expr in_op nm dbnm paren_exprlist */
+ 210, /* (210) expr ::= EXISTS LP select RP */
+ 210, /* (211) expr ::= CASE case_operand case_exprlist case_else END */
+ 269, /* (212) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 269, /* (213) case_exprlist ::= WHEN expr THEN expr */
+ 270, /* (214) case_else ::= ELSE expr */
+ 270, /* (215) case_else ::= */
+ 268, /* (216) case_operand ::= expr */
+ 268, /* (217) case_operand ::= */
+ 254, /* (218) exprlist ::= */
+ 245, /* (219) nexprlist ::= nexprlist COMMA expr */
+ 245, /* (220) nexprlist ::= expr */
+ 267, /* (221) paren_exprlist ::= */
+ 267, /* (222) paren_exprlist ::= LP exprlist RP */
+ 184, /* (223) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 271, /* (224) uniqueflag ::= UNIQUE */
+ 271, /* (225) uniqueflag ::= */
+ 214, /* (226) eidlist_opt ::= */
+ 214, /* (227) eidlist_opt ::= LP eidlist RP */
+ 224, /* (228) eidlist ::= eidlist COMMA nm collate sortorder */
+ 224, /* (229) eidlist ::= nm collate sortorder */
+ 272, /* (230) collate ::= */
+ 272, /* (231) collate ::= COLLATE ID|STRING */
+ 184, /* (232) cmd ::= DROP INDEX ifexists fullname */
+ 184, /* (233) cmd ::= VACUUM vinto */
+ 184, /* (234) cmd ::= VACUUM nm vinto */
+ 273, /* (235) vinto ::= INTO expr */
+ 273, /* (236) vinto ::= */
+ 184, /* (237) cmd ::= PRAGMA nm dbnm */
+ 184, /* (238) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 184, /* (239) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 184, /* (240) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 184, /* (241) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 204, /* (242) plus_num ::= PLUS INTEGER|FLOAT */
+ 205, /* (243) minus_num ::= MINUS INTEGER|FLOAT */
+ 184, /* (244) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 275, /* (245) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 277, /* (246) trigger_time ::= BEFORE|AFTER */
+ 277, /* (247) trigger_time ::= INSTEAD OF */
+ 277, /* (248) trigger_time ::= */
+ 278, /* (249) trigger_event ::= DELETE|INSERT */
+ 278, /* (250) trigger_event ::= UPDATE */
+ 278, /* (251) trigger_event ::= UPDATE OF idlist */
+ 280, /* (252) when_clause ::= */
+ 280, /* (253) when_clause ::= WHEN expr */
+ 276, /* (254) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 276, /* (255) trigger_cmd_list ::= trigger_cmd SEMI */
+ 282, /* (256) trnm ::= nm DOT nm */
+ 283, /* (257) tridxby ::= INDEXED BY nm */
+ 283, /* (258) tridxby ::= NOT INDEXED */
+ 281, /* (259) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+ 281, /* (260) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 281, /* (261) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 281, /* (262) trigger_cmd ::= scanpt select scanpt */
+ 210, /* (263) expr ::= RAISE LP IGNORE RP */
+ 210, /* (264) expr ::= RAISE LP raisetype COMMA nm RP */
+ 228, /* (265) raisetype ::= ROLLBACK */
+ 228, /* (266) raisetype ::= ABORT */
+ 228, /* (267) raisetype ::= FAIL */
+ 184, /* (268) cmd ::= DROP TRIGGER ifexists fullname */
+ 184, /* (269) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 184, /* (270) cmd ::= DETACH database_kw_opt expr */
+ 285, /* (271) key_opt ::= */
+ 285, /* (272) key_opt ::= KEY expr */
+ 184, /* (273) cmd ::= REINDEX */
+ 184, /* (274) cmd ::= REINDEX nm dbnm */
+ 184, /* (275) cmd ::= ANALYZE */
+ 184, /* (276) cmd ::= ANALYZE nm dbnm */
+ 184, /* (277) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 184, /* (278) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 286, /* (279) add_column_fullname ::= fullname */
+ 184, /* (280) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 184, /* (281) cmd ::= create_vtab */
+ 184, /* (282) cmd ::= create_vtab LP vtabarglist RP */
+ 288, /* (283) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 290, /* (284) vtabarg ::= */
+ 291, /* (285) vtabargtoken ::= ANY */
+ 291, /* (286) vtabargtoken ::= lp anylist RP */
+ 292, /* (287) lp ::= LP */
+ 258, /* (288) with ::= WITH wqlist */
+ 258, /* (289) with ::= WITH RECURSIVE wqlist */
+ 233, /* (290) wqlist ::= nm eidlist_opt AS LP select RP */
+ 233, /* (291) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ 294, /* (292) windowdefn_list ::= windowdefn */
+ 294, /* (293) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 295, /* (294) windowdefn ::= nm AS LP window RP */
+ 296, /* (295) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 296, /* (296) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 296, /* (297) window ::= ORDER BY sortlist frame_opt */
+ 296, /* (298) window ::= nm ORDER BY sortlist frame_opt */
+ 296, /* (299) window ::= frame_opt */
+ 296, /* (300) window ::= nm frame_opt */
+ 297, /* (301) frame_opt ::= */
+ 297, /* (302) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 297, /* (303) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 301, /* (304) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 303, /* (305) frame_bound_s ::= frame_bound */
+ 303, /* (306) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 304, /* (307) frame_bound_e ::= frame_bound */
+ 304, /* (308) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 302, /* (309) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 302, /* (310) frame_bound ::= CURRENT ROW */
+ 305, /* (311) frame_exclude_opt ::= */
+ 305, /* (312) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 306, /* (313) frame_exclude ::= NO OTHERS */
+ 306, /* (314) frame_exclude ::= CURRENT ROW */
+ 306, /* (315) frame_exclude ::= GROUP|TIES */
+ 243, /* (316) window_clause ::= WINDOW windowdefn_list */
+ 263, /* (317) filter_over ::= filter_clause over_clause */
+ 263, /* (318) filter_over ::= over_clause */
+ 263, /* (319) filter_over ::= filter_clause */
+ 300, /* (320) over_clause ::= OVER LP window RP */
+ 300, /* (321) over_clause ::= OVER nm */
+ 299, /* (322) filter_clause ::= FILTER LP WHERE expr RP */
+ 179, /* (323) input ::= cmdlist */
+ 180, /* (324) cmdlist ::= cmdlist ecmd */
+ 180, /* (325) cmdlist ::= ecmd */
+ 181, /* (326) ecmd ::= SEMI */
+ 181, /* (327) ecmd ::= cmdx SEMI */
+ 181, /* (328) ecmd ::= explain cmdx */
+ 186, /* (329) trans_opt ::= */
+ 186, /* (330) trans_opt ::= TRANSACTION */
+ 186, /* (331) trans_opt ::= TRANSACTION nm */
+ 188, /* (332) savepoint_opt ::= SAVEPOINT */
+ 188, /* (333) savepoint_opt ::= */
+ 184, /* (334) cmd ::= create_table create_table_args */
+ 195, /* (335) columnlist ::= columnlist COMMA columnname carglist */
+ 195, /* (336) columnlist ::= columnname carglist */
+ 187, /* (337) nm ::= ID|INDEXED */
+ 187, /* (338) nm ::= STRING */
+ 187, /* (339) nm ::= JOIN_KW */
+ 201, /* (340) typetoken ::= typename */
+ 202, /* (341) typename ::= ID|STRING */
+ 203, /* (342) signed ::= plus_num */
+ 203, /* (343) signed ::= minus_num */
+ 200, /* (344) carglist ::= carglist ccons */
+ 200, /* (345) carglist ::= */
+ 208, /* (346) ccons ::= NULL onconf */
+ 196, /* (347) conslist_opt ::= COMMA conslist */
+ 220, /* (348) conslist ::= conslist tconscomma tcons */
+ 220, /* (349) conslist ::= tcons */
+ 221, /* (350) tconscomma ::= */
+ 225, /* (351) defer_subclause_opt ::= defer_subclause */
+ 227, /* (352) resolvetype ::= raisetype */
+ 231, /* (353) selectnowith ::= oneselect */
+ 232, /* (354) oneselect ::= values */
+ 246, /* (355) sclp ::= selcollist COMMA */
+ 247, /* (356) as ::= ID|STRING */
+ 210, /* (357) expr ::= term */
+ 264, /* (358) likeop ::= LIKE_KW|MATCH */
+ 254, /* (359) exprlist ::= nexprlist */
+ 274, /* (360) nmnum ::= plus_num */
+ 274, /* (361) nmnum ::= nm */
+ 274, /* (362) nmnum ::= ON */
+ 274, /* (363) nmnum ::= DELETE */
+ 274, /* (364) nmnum ::= DEFAULT */
+ 204, /* (365) plus_num ::= INTEGER|FLOAT */
+ 279, /* (366) foreach_clause ::= */
+ 279, /* (367) foreach_clause ::= FOR EACH ROW */
+ 282, /* (368) trnm ::= nm */
+ 283, /* (369) tridxby ::= */
+ 284, /* (370) database_kw_opt ::= DATABASE */
+ 284, /* (371) database_kw_opt ::= */
+ 287, /* (372) kwcolumn_opt ::= */
+ 287, /* (373) kwcolumn_opt ::= COLUMNKW */
+ 289, /* (374) vtabarglist ::= vtabarg */
+ 289, /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 290, /* (376) vtabarg ::= vtabarg vtabargtoken */
+ 293, /* (377) anylist ::= */
+ 293, /* (378) anylist ::= anylist LP anylist RP */
+ 293, /* (379) anylist ::= anylist ANY */
+ 258, /* (380) with ::= */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -151557,252 +152594,257 @@ static const signed char yyRuleInfoNRhs[] = {
0, /* (127) using_opt ::= */
0, /* (128) orderby_opt ::= */
-3, /* (129) orderby_opt ::= ORDER BY sortlist */
- -4, /* (130) sortlist ::= sortlist COMMA expr sortorder */
- -2, /* (131) sortlist ::= expr sortorder */
+ -5, /* (130) sortlist ::= sortlist COMMA expr sortorder nulls */
+ -3, /* (131) sortlist ::= expr sortorder nulls */
-1, /* (132) sortorder ::= ASC */
-1, /* (133) sortorder ::= DESC */
0, /* (134) sortorder ::= */
- 0, /* (135) groupby_opt ::= */
- -3, /* (136) groupby_opt ::= GROUP BY nexprlist */
- 0, /* (137) having_opt ::= */
- -2, /* (138) having_opt ::= HAVING expr */
- 0, /* (139) limit_opt ::= */
- -2, /* (140) limit_opt ::= LIMIT expr */
- -4, /* (141) limit_opt ::= LIMIT expr OFFSET expr */
- -4, /* (142) limit_opt ::= LIMIT expr COMMA expr */
- -6, /* (143) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- 0, /* (144) where_opt ::= */
- -2, /* (145) where_opt ::= WHERE expr */
- -8, /* (146) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- -5, /* (147) setlist ::= setlist COMMA nm EQ expr */
- -7, /* (148) setlist ::= setlist COMMA LP idlist RP EQ expr */
- -3, /* (149) setlist ::= nm EQ expr */
- -5, /* (150) setlist ::= LP idlist RP EQ expr */
- -7, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- -7, /* (152) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- 0, /* (153) upsert ::= */
- -11, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- -8, /* (155) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- -4, /* (156) upsert ::= ON CONFLICT DO NOTHING */
- -2, /* (157) insert_cmd ::= INSERT orconf */
- -1, /* (158) insert_cmd ::= REPLACE */
- 0, /* (159) idlist_opt ::= */
- -3, /* (160) idlist_opt ::= LP idlist RP */
- -3, /* (161) idlist ::= idlist COMMA nm */
- -1, /* (162) idlist ::= nm */
- -3, /* (163) expr ::= LP expr RP */
- -1, /* (164) expr ::= ID|INDEXED */
- -1, /* (165) expr ::= JOIN_KW */
- -3, /* (166) expr ::= nm DOT nm */
- -5, /* (167) expr ::= nm DOT nm DOT nm */
- -1, /* (168) term ::= NULL|FLOAT|BLOB */
- -1, /* (169) term ::= STRING */
- -1, /* (170) term ::= INTEGER */
- -1, /* (171) expr ::= VARIABLE */
- -3, /* (172) expr ::= expr COLLATE ID|STRING */
- -6, /* (173) expr ::= CAST LP expr AS typetoken RP */
- -5, /* (174) expr ::= ID|INDEXED LP distinct exprlist RP */
- -4, /* (175) expr ::= ID|INDEXED LP STAR RP */
- -6, /* (176) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- -5, /* (177) expr ::= ID|INDEXED LP STAR RP over_clause */
- -1, /* (178) term ::= CTIME_KW */
- -5, /* (179) expr ::= LP nexprlist COMMA expr RP */
- -3, /* (180) expr ::= expr AND expr */
- -3, /* (181) expr ::= expr OR expr */
- -3, /* (182) expr ::= expr LT|GT|GE|LE expr */
- -3, /* (183) expr ::= expr EQ|NE expr */
- -3, /* (184) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- -3, /* (185) expr ::= expr PLUS|MINUS expr */
- -3, /* (186) expr ::= expr STAR|SLASH|REM expr */
- -3, /* (187) expr ::= expr CONCAT expr */
- -2, /* (188) likeop ::= NOT LIKE_KW|MATCH */
- -3, /* (189) expr ::= expr likeop expr */
- -5, /* (190) expr ::= expr likeop expr ESCAPE expr */
- -2, /* (191) expr ::= expr ISNULL|NOTNULL */
- -3, /* (192) expr ::= expr NOT NULL */
- -3, /* (193) expr ::= expr IS expr */
- -4, /* (194) expr ::= expr IS NOT expr */
- -2, /* (195) expr ::= NOT expr */
- -2, /* (196) expr ::= BITNOT expr */
- -2, /* (197) expr ::= PLUS|MINUS expr */
- -1, /* (198) between_op ::= BETWEEN */
- -2, /* (199) between_op ::= NOT BETWEEN */
- -5, /* (200) expr ::= expr between_op expr AND expr */
- -1, /* (201) in_op ::= IN */
- -2, /* (202) in_op ::= NOT IN */
- -5, /* (203) expr ::= expr in_op LP exprlist RP */
- -3, /* (204) expr ::= LP select RP */
- -5, /* (205) expr ::= expr in_op LP select RP */
- -5, /* (206) expr ::= expr in_op nm dbnm paren_exprlist */
- -4, /* (207) expr ::= EXISTS LP select RP */
- -5, /* (208) expr ::= CASE case_operand case_exprlist case_else END */
- -5, /* (209) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- -4, /* (210) case_exprlist ::= WHEN expr THEN expr */
- -2, /* (211) case_else ::= ELSE expr */
- 0, /* (212) case_else ::= */
- -1, /* (213) case_operand ::= expr */
- 0, /* (214) case_operand ::= */
- 0, /* (215) exprlist ::= */
- -3, /* (216) nexprlist ::= nexprlist COMMA expr */
- -1, /* (217) nexprlist ::= expr */
- 0, /* (218) paren_exprlist ::= */
- -3, /* (219) paren_exprlist ::= LP exprlist RP */
- -12, /* (220) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- -1, /* (221) uniqueflag ::= UNIQUE */
- 0, /* (222) uniqueflag ::= */
- 0, /* (223) eidlist_opt ::= */
- -3, /* (224) eidlist_opt ::= LP eidlist RP */
- -5, /* (225) eidlist ::= eidlist COMMA nm collate sortorder */
- -3, /* (226) eidlist ::= nm collate sortorder */
- 0, /* (227) collate ::= */
- -2, /* (228) collate ::= COLLATE ID|STRING */
- -4, /* (229) cmd ::= DROP INDEX ifexists fullname */
- -2, /* (230) cmd ::= VACUUM vinto */
- -3, /* (231) cmd ::= VACUUM nm vinto */
- -2, /* (232) vinto ::= INTO expr */
- 0, /* (233) vinto ::= */
- -3, /* (234) cmd ::= PRAGMA nm dbnm */
- -5, /* (235) cmd ::= PRAGMA nm dbnm EQ nmnum */
- -6, /* (236) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- -5, /* (237) cmd ::= PRAGMA nm dbnm EQ minus_num */
- -6, /* (238) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- -2, /* (239) plus_num ::= PLUS INTEGER|FLOAT */
- -2, /* (240) minus_num ::= MINUS INTEGER|FLOAT */
- -5, /* (241) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- -11, /* (242) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- -1, /* (243) trigger_time ::= BEFORE|AFTER */
- -2, /* (244) trigger_time ::= INSTEAD OF */
- 0, /* (245) trigger_time ::= */
- -1, /* (246) trigger_event ::= DELETE|INSERT */
- -1, /* (247) trigger_event ::= UPDATE */
- -3, /* (248) trigger_event ::= UPDATE OF idlist */
- 0, /* (249) when_clause ::= */
- -2, /* (250) when_clause ::= WHEN expr */
- -3, /* (251) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- -2, /* (252) trigger_cmd_list ::= trigger_cmd SEMI */
- -3, /* (253) trnm ::= nm DOT nm */
- -3, /* (254) tridxby ::= INDEXED BY nm */
- -2, /* (255) tridxby ::= NOT INDEXED */
- -8, /* (256) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- -8, /* (257) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- -6, /* (258) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- -3, /* (259) trigger_cmd ::= scanpt select scanpt */
- -4, /* (260) expr ::= RAISE LP IGNORE RP */
- -6, /* (261) expr ::= RAISE LP raisetype COMMA nm RP */
- -1, /* (262) raisetype ::= ROLLBACK */
- -1, /* (263) raisetype ::= ABORT */
- -1, /* (264) raisetype ::= FAIL */
- -4, /* (265) cmd ::= DROP TRIGGER ifexists fullname */
- -6, /* (266) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- -3, /* (267) cmd ::= DETACH database_kw_opt expr */
- 0, /* (268) key_opt ::= */
- -2, /* (269) key_opt ::= KEY expr */
- -1, /* (270) cmd ::= REINDEX */
- -3, /* (271) cmd ::= REINDEX nm dbnm */
- -1, /* (272) cmd ::= ANALYZE */
- -3, /* (273) cmd ::= ANALYZE nm dbnm */
- -6, /* (274) cmd ::= ALTER TABLE fullname RENAME TO nm */
- -7, /* (275) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- -1, /* (276) add_column_fullname ::= fullname */
- -8, /* (277) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- -1, /* (278) cmd ::= create_vtab */
- -4, /* (279) cmd ::= create_vtab LP vtabarglist RP */
- -8, /* (280) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 0, /* (281) vtabarg ::= */
- -1, /* (282) vtabargtoken ::= ANY */
- -3, /* (283) vtabargtoken ::= lp anylist RP */
- -1, /* (284) lp ::= LP */
- -2, /* (285) with ::= WITH wqlist */
- -3, /* (286) with ::= WITH RECURSIVE wqlist */
- -6, /* (287) wqlist ::= nm eidlist_opt AS LP select RP */
- -8, /* (288) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- -1, /* (289) windowdefn_list ::= windowdefn */
- -3, /* (290) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -5, /* (291) windowdefn ::= nm AS LP window RP */
- -5, /* (292) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- -6, /* (293) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- -4, /* (294) window ::= ORDER BY sortlist frame_opt */
- -5, /* (295) window ::= nm ORDER BY sortlist frame_opt */
- -1, /* (296) window ::= frame_opt */
- -2, /* (297) window ::= nm frame_opt */
- 0, /* (298) frame_opt ::= */
- -3, /* (299) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- -6, /* (300) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- -1, /* (301) range_or_rows ::= RANGE|ROWS|GROUPS */
- -1, /* (302) frame_bound_s ::= frame_bound */
- -2, /* (303) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (304) frame_bound_e ::= frame_bound */
- -2, /* (305) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (306) frame_bound ::= expr PRECEDING|FOLLOWING */
- -2, /* (307) frame_bound ::= CURRENT ROW */
- 0, /* (308) frame_exclude_opt ::= */
- -2, /* (309) frame_exclude_opt ::= EXCLUDE frame_exclude */
- -2, /* (310) frame_exclude ::= NO OTHERS */
- -2, /* (311) frame_exclude ::= CURRENT ROW */
- -1, /* (312) frame_exclude ::= GROUP|TIES */
- -2, /* (313) window_clause ::= WINDOW windowdefn_list */
- -5, /* (314) over_clause ::= filter_opt OVER LP window RP */
- -3, /* (315) over_clause ::= filter_opt OVER nm */
- 0, /* (316) filter_opt ::= */
- -5, /* (317) filter_opt ::= FILTER LP WHERE expr RP */
- -1, /* (318) input ::= cmdlist */
- -2, /* (319) cmdlist ::= cmdlist ecmd */
- -1, /* (320) cmdlist ::= ecmd */
- -1, /* (321) ecmd ::= SEMI */
- -2, /* (322) ecmd ::= cmdx SEMI */
- -2, /* (323) ecmd ::= explain cmdx */
- 0, /* (324) trans_opt ::= */
- -1, /* (325) trans_opt ::= TRANSACTION */
- -2, /* (326) trans_opt ::= TRANSACTION nm */
- -1, /* (327) savepoint_opt ::= SAVEPOINT */
- 0, /* (328) savepoint_opt ::= */
- -2, /* (329) cmd ::= create_table create_table_args */
- -4, /* (330) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (331) columnlist ::= columnname carglist */
- -1, /* (332) nm ::= ID|INDEXED */
- -1, /* (333) nm ::= STRING */
- -1, /* (334) nm ::= JOIN_KW */
- -1, /* (335) typetoken ::= typename */
- -1, /* (336) typename ::= ID|STRING */
- -1, /* (337) signed ::= plus_num */
- -1, /* (338) signed ::= minus_num */
- -2, /* (339) carglist ::= carglist ccons */
- 0, /* (340) carglist ::= */
- -2, /* (341) ccons ::= NULL onconf */
- -2, /* (342) conslist_opt ::= COMMA conslist */
- -3, /* (343) conslist ::= conslist tconscomma tcons */
- -1, /* (344) conslist ::= tcons */
- 0, /* (345) tconscomma ::= */
- -1, /* (346) defer_subclause_opt ::= defer_subclause */
- -1, /* (347) resolvetype ::= raisetype */
- -1, /* (348) selectnowith ::= oneselect */
- -1, /* (349) oneselect ::= values */
- -2, /* (350) sclp ::= selcollist COMMA */
- -1, /* (351) as ::= ID|STRING */
- -1, /* (352) expr ::= term */
- -1, /* (353) likeop ::= LIKE_KW|MATCH */
- -1, /* (354) exprlist ::= nexprlist */
- -1, /* (355) nmnum ::= plus_num */
- -1, /* (356) nmnum ::= nm */
- -1, /* (357) nmnum ::= ON */
- -1, /* (358) nmnum ::= DELETE */
- -1, /* (359) nmnum ::= DEFAULT */
- -1, /* (360) plus_num ::= INTEGER|FLOAT */
- 0, /* (361) foreach_clause ::= */
- -3, /* (362) foreach_clause ::= FOR EACH ROW */
- -1, /* (363) trnm ::= nm */
- 0, /* (364) tridxby ::= */
- -1, /* (365) database_kw_opt ::= DATABASE */
- 0, /* (366) database_kw_opt ::= */
- 0, /* (367) kwcolumn_opt ::= */
- -1, /* (368) kwcolumn_opt ::= COLUMNKW */
- -1, /* (369) vtabarglist ::= vtabarg */
- -3, /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (371) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (372) anylist ::= */
- -4, /* (373) anylist ::= anylist LP anylist RP */
- -2, /* (374) anylist ::= anylist ANY */
- 0, /* (375) with ::= */
+ -2, /* (135) nulls ::= NULLS FIRST */
+ -2, /* (136) nulls ::= NULLS LAST */
+ 0, /* (137) nulls ::= */
+ 0, /* (138) groupby_opt ::= */
+ -3, /* (139) groupby_opt ::= GROUP BY nexprlist */
+ 0, /* (140) having_opt ::= */
+ -2, /* (141) having_opt ::= HAVING expr */
+ 0, /* (142) limit_opt ::= */
+ -2, /* (143) limit_opt ::= LIMIT expr */
+ -4, /* (144) limit_opt ::= LIMIT expr OFFSET expr */
+ -4, /* (145) limit_opt ::= LIMIT expr COMMA expr */
+ -6, /* (146) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ 0, /* (147) where_opt ::= */
+ -2, /* (148) where_opt ::= WHERE expr */
+ -8, /* (149) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ -5, /* (150) setlist ::= setlist COMMA nm EQ expr */
+ -7, /* (151) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ -3, /* (152) setlist ::= nm EQ expr */
+ -5, /* (153) setlist ::= LP idlist RP EQ expr */
+ -7, /* (154) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ -7, /* (155) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ 0, /* (156) upsert ::= */
+ -11, /* (157) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+ -8, /* (158) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+ -4, /* (159) upsert ::= ON CONFLICT DO NOTHING */
+ -2, /* (160) insert_cmd ::= INSERT orconf */
+ -1, /* (161) insert_cmd ::= REPLACE */
+ 0, /* (162) idlist_opt ::= */
+ -3, /* (163) idlist_opt ::= LP idlist RP */
+ -3, /* (164) idlist ::= idlist COMMA nm */
+ -1, /* (165) idlist ::= nm */
+ -3, /* (166) expr ::= LP expr RP */
+ -1, /* (167) expr ::= ID|INDEXED */
+ -1, /* (168) expr ::= JOIN_KW */
+ -3, /* (169) expr ::= nm DOT nm */
+ -5, /* (170) expr ::= nm DOT nm DOT nm */
+ -1, /* (171) term ::= NULL|FLOAT|BLOB */
+ -1, /* (172) term ::= STRING */
+ -1, /* (173) term ::= INTEGER */
+ -1, /* (174) expr ::= VARIABLE */
+ -3, /* (175) expr ::= expr COLLATE ID|STRING */
+ -6, /* (176) expr ::= CAST LP expr AS typetoken RP */
+ -5, /* (177) expr ::= ID|INDEXED LP distinct exprlist RP */
+ -4, /* (178) expr ::= ID|INDEXED LP STAR RP */
+ -6, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
+ -5, /* (180) expr ::= ID|INDEXED LP STAR RP filter_over */
+ -1, /* (181) term ::= CTIME_KW */
+ -5, /* (182) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (183) expr ::= expr AND expr */
+ -3, /* (184) expr ::= expr OR expr */
+ -3, /* (185) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (186) expr ::= expr EQ|NE expr */
+ -3, /* (187) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (188) expr ::= expr PLUS|MINUS expr */
+ -3, /* (189) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (190) expr ::= expr CONCAT expr */
+ -2, /* (191) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (192) expr ::= expr likeop expr */
+ -5, /* (193) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (194) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (195) expr ::= expr NOT NULL */
+ -3, /* (196) expr ::= expr IS expr */
+ -4, /* (197) expr ::= expr IS NOT expr */
+ -2, /* (198) expr ::= NOT expr */
+ -2, /* (199) expr ::= BITNOT expr */
+ -2, /* (200) expr ::= PLUS|MINUS expr */
+ -1, /* (201) between_op ::= BETWEEN */
+ -2, /* (202) between_op ::= NOT BETWEEN */
+ -5, /* (203) expr ::= expr between_op expr AND expr */
+ -1, /* (204) in_op ::= IN */
+ -2, /* (205) in_op ::= NOT IN */
+ -5, /* (206) expr ::= expr in_op LP exprlist RP */
+ -3, /* (207) expr ::= LP select RP */
+ -5, /* (208) expr ::= expr in_op LP select RP */
+ -5, /* (209) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (210) expr ::= EXISTS LP select RP */
+ -5, /* (211) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (212) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (213) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (214) case_else ::= ELSE expr */
+ 0, /* (215) case_else ::= */
+ -1, /* (216) case_operand ::= expr */
+ 0, /* (217) case_operand ::= */
+ 0, /* (218) exprlist ::= */
+ -3, /* (219) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (220) nexprlist ::= expr */
+ 0, /* (221) paren_exprlist ::= */
+ -3, /* (222) paren_exprlist ::= LP exprlist RP */
+ -12, /* (223) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (224) uniqueflag ::= UNIQUE */
+ 0, /* (225) uniqueflag ::= */
+ 0, /* (226) eidlist_opt ::= */
+ -3, /* (227) eidlist_opt ::= LP eidlist RP */
+ -5, /* (228) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (229) eidlist ::= nm collate sortorder */
+ 0, /* (230) collate ::= */
+ -2, /* (231) collate ::= COLLATE ID|STRING */
+ -4, /* (232) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (233) cmd ::= VACUUM vinto */
+ -3, /* (234) cmd ::= VACUUM nm vinto */
+ -2, /* (235) vinto ::= INTO expr */
+ 0, /* (236) vinto ::= */
+ -3, /* (237) cmd ::= PRAGMA nm dbnm */
+ -5, /* (238) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (239) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (240) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (241) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (242) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (243) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (244) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (245) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (246) trigger_time ::= BEFORE|AFTER */
+ -2, /* (247) trigger_time ::= INSTEAD OF */
+ 0, /* (248) trigger_time ::= */
+ -1, /* (249) trigger_event ::= DELETE|INSERT */
+ -1, /* (250) trigger_event ::= UPDATE */
+ -3, /* (251) trigger_event ::= UPDATE OF idlist */
+ 0, /* (252) when_clause ::= */
+ -2, /* (253) when_clause ::= WHEN expr */
+ -3, /* (254) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (255) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (256) trnm ::= nm DOT nm */
+ -3, /* (257) tridxby ::= INDEXED BY nm */
+ -2, /* (258) tridxby ::= NOT INDEXED */
+ -8, /* (259) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+ -8, /* (260) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (261) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (262) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (263) expr ::= RAISE LP IGNORE RP */
+ -6, /* (264) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (265) raisetype ::= ROLLBACK */
+ -1, /* (266) raisetype ::= ABORT */
+ -1, /* (267) raisetype ::= FAIL */
+ -4, /* (268) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (269) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (270) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (271) key_opt ::= */
+ -2, /* (272) key_opt ::= KEY expr */
+ -1, /* (273) cmd ::= REINDEX */
+ -3, /* (274) cmd ::= REINDEX nm dbnm */
+ -1, /* (275) cmd ::= ANALYZE */
+ -3, /* (276) cmd ::= ANALYZE nm dbnm */
+ -6, /* (277) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (278) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -1, /* (279) add_column_fullname ::= fullname */
+ -8, /* (280) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (281) cmd ::= create_vtab */
+ -4, /* (282) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (283) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (284) vtabarg ::= */
+ -1, /* (285) vtabargtoken ::= ANY */
+ -3, /* (286) vtabargtoken ::= lp anylist RP */
+ -1, /* (287) lp ::= LP */
+ -2, /* (288) with ::= WITH wqlist */
+ -3, /* (289) with ::= WITH RECURSIVE wqlist */
+ -6, /* (290) wqlist ::= nm eidlist_opt AS LP select RP */
+ -8, /* (291) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ -1, /* (292) windowdefn_list ::= windowdefn */
+ -3, /* (293) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (294) windowdefn ::= nm AS LP window RP */
+ -5, /* (295) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (296) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (297) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (298) window ::= nm ORDER BY sortlist frame_opt */
+ -1, /* (299) window ::= frame_opt */
+ -2, /* (300) window ::= nm frame_opt */
+ 0, /* (301) frame_opt ::= */
+ -3, /* (302) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (303) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (304) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (305) frame_bound_s ::= frame_bound */
+ -2, /* (306) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (307) frame_bound_e ::= frame_bound */
+ -2, /* (308) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (309) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (310) frame_bound ::= CURRENT ROW */
+ 0, /* (311) frame_exclude_opt ::= */
+ -2, /* (312) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (313) frame_exclude ::= NO OTHERS */
+ -2, /* (314) frame_exclude ::= CURRENT ROW */
+ -1, /* (315) frame_exclude ::= GROUP|TIES */
+ -2, /* (316) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (317) filter_over ::= filter_clause over_clause */
+ -1, /* (318) filter_over ::= over_clause */
+ -1, /* (319) filter_over ::= filter_clause */
+ -4, /* (320) over_clause ::= OVER LP window RP */
+ -2, /* (321) over_clause ::= OVER nm */
+ -5, /* (322) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (323) input ::= cmdlist */
+ -2, /* (324) cmdlist ::= cmdlist ecmd */
+ -1, /* (325) cmdlist ::= ecmd */
+ -1, /* (326) ecmd ::= SEMI */
+ -2, /* (327) ecmd ::= cmdx SEMI */
+ -2, /* (328) ecmd ::= explain cmdx */
+ 0, /* (329) trans_opt ::= */
+ -1, /* (330) trans_opt ::= TRANSACTION */
+ -2, /* (331) trans_opt ::= TRANSACTION nm */
+ -1, /* (332) savepoint_opt ::= SAVEPOINT */
+ 0, /* (333) savepoint_opt ::= */
+ -2, /* (334) cmd ::= create_table create_table_args */
+ -4, /* (335) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (336) columnlist ::= columnname carglist */
+ -1, /* (337) nm ::= ID|INDEXED */
+ -1, /* (338) nm ::= STRING */
+ -1, /* (339) nm ::= JOIN_KW */
+ -1, /* (340) typetoken ::= typename */
+ -1, /* (341) typename ::= ID|STRING */
+ -1, /* (342) signed ::= plus_num */
+ -1, /* (343) signed ::= minus_num */
+ -2, /* (344) carglist ::= carglist ccons */
+ 0, /* (345) carglist ::= */
+ -2, /* (346) ccons ::= NULL onconf */
+ -2, /* (347) conslist_opt ::= COMMA conslist */
+ -3, /* (348) conslist ::= conslist tconscomma tcons */
+ -1, /* (349) conslist ::= tcons */
+ 0, /* (350) tconscomma ::= */
+ -1, /* (351) defer_subclause_opt ::= defer_subclause */
+ -1, /* (352) resolvetype ::= raisetype */
+ -1, /* (353) selectnowith ::= oneselect */
+ -1, /* (354) oneselect ::= values */
+ -2, /* (355) sclp ::= selcollist COMMA */
+ -1, /* (356) as ::= ID|STRING */
+ -1, /* (357) expr ::= term */
+ -1, /* (358) likeop ::= LIKE_KW|MATCH */
+ -1, /* (359) exprlist ::= nexprlist */
+ -1, /* (360) nmnum ::= plus_num */
+ -1, /* (361) nmnum ::= nm */
+ -1, /* (362) nmnum ::= ON */
+ -1, /* (363) nmnum ::= DELETE */
+ -1, /* (364) nmnum ::= DEFAULT */
+ -1, /* (365) plus_num ::= INTEGER|FLOAT */
+ 0, /* (366) foreach_clause ::= */
+ -3, /* (367) foreach_clause ::= FOR EACH ROW */
+ -1, /* (368) trnm ::= nm */
+ 0, /* (369) tridxby ::= */
+ -1, /* (370) database_kw_opt ::= DATABASE */
+ 0, /* (371) database_kw_opt ::= */
+ 0, /* (372) kwcolumn_opt ::= */
+ -1, /* (373) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (374) vtabarglist ::= vtabarg */
+ -3, /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (376) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (377) anylist ::= */
+ -4, /* (378) anylist ::= anylist LP anylist RP */
+ -2, /* (379) anylist ::= anylist ANY */
+ 0, /* (380) with ::= */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -151899,16 +152941,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy100);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy32);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy100 = TK_DEFERRED;}
+{yymsp[1].minor.yy32 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
- case 301: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==301);
-{yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-X*/}
+ case 304: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==304);
+{yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -151931,7 +152973,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy100,0,0,yymsp[-2].minor.yy100);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy32,0,0,yymsp[-2].minor.yy32);
}
break;
case 14: /* createkw ::= CREATE */
@@ -151945,33 +152987,33 @@ static YYACTIONTYPE yy_reduce(
case 68: /* defer_subclause_opt ::= */ yytestcase(yyruleno==68);
case 77: /* ifexists ::= */ yytestcase(yyruleno==77);
case 94: /* distinct ::= */ yytestcase(yyruleno==94);
- case 227: /* collate ::= */ yytestcase(yyruleno==227);
-{yymsp[1].minor.yy100 = 0;}
+ case 230: /* collate ::= */ yytestcase(yyruleno==230);
+{yymsp[1].minor.yy32 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy100 = 1;}
+{yymsp[-2].minor.yy32 = 1;}
break;
case 17: /* temp ::= TEMP */
case 44: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==44);
-{yymsp[0].minor.yy100 = 1;}
+{yymsp[0].minor.yy32 = 1;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy100,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy32,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy391);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy391);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy25);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy25);
}
break;
case 22: /* table_options ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy100 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy32 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy100 = 0;
+ yymsp[-1].minor.yy32 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -152000,7 +153042,7 @@ static YYACTIONTYPE yy_reduce(
case 28: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy528 = yyLookaheadToken.z;
+ yymsp[1].minor.yy8 = yyLookaheadToken.z;
}
break;
case 29: /* scantok ::= */
@@ -152014,17 +153056,17 @@ static YYACTIONTYPE yy_reduce(
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 31: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy102,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy46,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 32: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy102,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy46,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 33: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy102,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy46,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy102, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy46, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
@@ -152039,170 +153081,170 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 36: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy100);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy32);}
break;
case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy100,yymsp[0].minor.yy100,yymsp[-2].minor.yy100);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy32,yymsp[0].minor.yy32,yymsp[-2].minor.yy32);}
break;
case 38: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy100,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy32,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 39: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy102);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy46);}
break;
case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy94,yymsp[0].minor.yy100);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy138,yymsp[0].minor.yy32);}
break;
case 41: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy100);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy32);}
break;
case 42: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* refargs ::= */
-{ yymsp[1].minor.yy100 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy32 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 46: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy100 = (yymsp[-1].minor.yy100 & ~yymsp[0].minor.yy199.mask) | yymsp[0].minor.yy199.value; }
+{ yymsp[-1].minor.yy32 = (yymsp[-1].minor.yy32 & ~yymsp[0].minor.yy495.mask) | yymsp[0].minor.yy495.value; }
break;
case 47: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy199.value = 0; yymsp[-1].minor.yy199.mask = 0x000000; }
+{ yymsp[-1].minor.yy495.value = 0; yymsp[-1].minor.yy495.mask = 0x000000; }
break;
case 48: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy199.value = 0; yymsp[-2].minor.yy199.mask = 0x000000; }
+{ yymsp[-2].minor.yy495.value = 0; yymsp[-2].minor.yy495.mask = 0x000000; }
break;
case 49: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy199.value = yymsp[0].minor.yy100; yymsp[-2].minor.yy199.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy495.value = yymsp[0].minor.yy32; yymsp[-2].minor.yy495.mask = 0x0000ff; }
break;
case 50: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy199.value = yymsp[0].minor.yy100<<8; yymsp[-2].minor.yy199.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy495.value = yymsp[0].minor.yy32<<8; yymsp[-2].minor.yy495.mask = 0x00ff00; }
break;
case 51: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy100 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 52: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy100 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 53: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy100 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy32 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 54: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy100 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy32 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 55: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy100 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_None; /* EV: R-33326-45252 */}
break;
case 56: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy100 = 0;}
+{yymsp[-2].minor.yy32 = 0;}
break;
case 57: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 72: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==72);
- case 157: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==157);
-{yymsp[-1].minor.yy100 = yymsp[0].minor.yy100;}
+ case 160: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==160);
+{yymsp[-1].minor.yy32 = yymsp[0].minor.yy32;}
break;
case 59: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 76: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==76);
- case 199: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==199);
- case 202: /* in_op ::= NOT IN */ yytestcase(yyruleno==202);
- case 228: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==228);
-{yymsp[-1].minor.yy100 = 1;}
+ case 202: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==202);
+ case 205: /* in_op ::= NOT IN */ yytestcase(yyruleno==205);
+ case 231: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==231);
+{yymsp[-1].minor.yy32 = 1;}
break;
case 60: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy100 = 0;}
+{yymsp[-1].minor.yy32 = 0;}
break;
case 62: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 64: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy94,yymsp[0].minor.yy100,yymsp[-2].minor.yy100,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy138,yymsp[0].minor.yy32,yymsp[-2].minor.yy32,0);}
break;
case 65: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy94,yymsp[0].minor.yy100,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy138,yymsp[0].minor.yy32,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 66: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy102);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy46);}
break;
case 67: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy94, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy94, yymsp[-1].minor.yy100);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy100);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy138, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy138, yymsp[-1].minor.yy32);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy32);
}
break;
case 69: /* onconf ::= */
case 71: /* orconf ::= */ yytestcase(yyruleno==71);
-{yymsp[1].minor.yy100 = OE_Default;}
+{yymsp[1].minor.yy32 = OE_Default;}
break;
case 70: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy100 = yymsp[0].minor.yy100;}
+{yymsp[-2].minor.yy32 = yymsp[0].minor.yy32;}
break;
case 73: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy100 = OE_Ignore;}
+{yymsp[0].minor.yy32 = OE_Ignore;}
break;
case 74: /* resolvetype ::= REPLACE */
- case 158: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==158);
-{yymsp[0].minor.yy100 = OE_Replace;}
+ case 161: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==161);
+{yymsp[0].minor.yy32 = OE_Replace;}
break;
case 75: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy407, 0, yymsp[-1].minor.yy100);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy609, 0, yymsp[-1].minor.yy32);
}
break;
case 78: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy94, yymsp[0].minor.yy391, yymsp[-7].minor.yy100, yymsp[-5].minor.yy100);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy138, yymsp[0].minor.yy25, yymsp[-7].minor.yy32, yymsp[-5].minor.yy32);
}
break;
case 79: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy407, 1, yymsp[-1].minor.yy100);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy609, 1, yymsp[-1].minor.yy32);
}
break;
case 80: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy391, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy391);
+ sqlite3Select(pParse, yymsp[0].minor.yy25, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy25);
}
break;
case 81: /* select ::= WITH wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
- p->pWith = yymsp[-1].minor.yy243;
+ p->pWith = yymsp[-1].minor.yy297;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy243);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy297);
}
- yymsp[-2].minor.yy391 = p;
+ yymsp[-2].minor.yy25 = p;
}
break;
case 82: /* select ::= WITH RECURSIVE wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
- p->pWith = yymsp[-1].minor.yy243;
+ p->pWith = yymsp[-1].minor.yy297;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy243);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy297);
}
- yymsp[-3].minor.yy391 = p;
+ yymsp[-3].minor.yy25 = p;
}
break;
case 83: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
- yymsp[0].minor.yy391 = p; /*A-overwrites-X*/
+ yymsp[0].minor.yy25 = p; /*A-overwrites-X*/
}
break;
case 84: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy391;
- Select *pLhs = yymsp[-2].minor.yy391;
+ Select *pRhs = yymsp[0].minor.yy25;
+ Select *pLhs = yymsp[-2].minor.yy25;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -152212,83 +153254,83 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy100;
+ pRhs->op = (u8)yymsp[-1].minor.yy32;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy100!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy32!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy391 = pRhs;
+ yymsp[-2].minor.yy25 = pRhs;
}
break;
case 85: /* multiselect_op ::= UNION */
case 87: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==87);
-{yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 86: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy100 = TK_ALL;}
+{yymsp[-1].minor.yy32 = TK_ALL;}
break;
case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy94,yymsp[-5].minor.yy407,yymsp[-4].minor.yy102,yymsp[-3].minor.yy94,yymsp[-2].minor.yy102,yymsp[-1].minor.yy94,yymsp[-7].minor.yy100,yymsp[0].minor.yy102);
+ yymsp[-8].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy138,yymsp[-5].minor.yy609,yymsp[-4].minor.yy46,yymsp[-3].minor.yy138,yymsp[-2].minor.yy46,yymsp[-1].minor.yy138,yymsp[-7].minor.yy32,yymsp[0].minor.yy46);
}
break;
case 89: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy94,yymsp[-6].minor.yy407,yymsp[-5].minor.yy102,yymsp[-4].minor.yy94,yymsp[-3].minor.yy102,yymsp[-1].minor.yy94,yymsp[-8].minor.yy100,yymsp[0].minor.yy102);
- if( yymsp[-9].minor.yy391 ){
- yymsp[-9].minor.yy391->pWinDefn = yymsp[-2].minor.yy379;
+ yymsp[-9].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy138,yymsp[-6].minor.yy609,yymsp[-5].minor.yy46,yymsp[-4].minor.yy138,yymsp[-3].minor.yy46,yymsp[-1].minor.yy138,yymsp[-8].minor.yy32,yymsp[0].minor.yy46);
+ if( yymsp[-9].minor.yy25 ){
+ yymsp[-9].minor.yy25->pWinDefn = yymsp[-2].minor.yy455;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy379);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy455);
}
}
break;
case 90: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy94,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy138,0,0,0,0,0,SF_Values,0);
}
break;
case 91: /* values ::= values COMMA LP nexprlist RP */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy391;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy94,0,0,0,0,0,SF_Values|SF_MultiValue,0);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy25;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy138,0,0,0,0,0,SF_Values|SF_MultiValue,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
pRight->pPrior = pLeft;
- yymsp[-4].minor.yy391 = pRight;
+ yymsp[-4].minor.yy25 = pRight;
}else{
- yymsp[-4].minor.yy391 = pLeft;
+ yymsp[-4].minor.yy25 = pLeft;
}
}
break;
case 92: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy100 = SF_Distinct;}
+{yymsp[0].minor.yy32 = SF_Distinct;}
break;
case 93: /* distinct ::= ALL */
-{yymsp[0].minor.yy100 = SF_All;}
+{yymsp[0].minor.yy32 = SF_All;}
break;
case 95: /* sclp ::= */
case 128: /* orderby_opt ::= */ yytestcase(yyruleno==128);
- case 135: /* groupby_opt ::= */ yytestcase(yyruleno==135);
- case 215: /* exprlist ::= */ yytestcase(yyruleno==215);
- case 218: /* paren_exprlist ::= */ yytestcase(yyruleno==218);
- case 223: /* eidlist_opt ::= */ yytestcase(yyruleno==223);
-{yymsp[1].minor.yy94 = 0;}
+ case 138: /* groupby_opt ::= */ yytestcase(yyruleno==138);
+ case 218: /* exprlist ::= */ yytestcase(yyruleno==218);
+ case 221: /* paren_exprlist ::= */ yytestcase(yyruleno==221);
+ case 226: /* eidlist_opt ::= */ yytestcase(yyruleno==226);
+{yymsp[1].minor.yy138 = 0;}
break;
case 96: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy94, yymsp[-2].minor.yy102);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy94, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy94,yymsp[-3].minor.yy528,yymsp[-1].minor.yy528);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy138, yymsp[-2].minor.yy46);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy138, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy138,yymsp[-3].minor.yy8,yymsp[-1].minor.yy8);
}
break;
case 97: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
- yymsp[-2].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy94, p);
+ yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy138, p);
}
break;
case 98: /* selcollist ::= sclp scanpt nm DOT STAR */
@@ -152296,58 +153338,58 @@ static YYACTIONTYPE yy_reduce(
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, pDot);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, pDot);
}
break;
case 99: /* as ::= AS nm */
case 110: /* dbnm ::= DOT nm */ yytestcase(yyruleno==110);
- case 239: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==239);
- case 240: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==240);
+ case 242: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==242);
+ case 243: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==243);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 101: /* from ::= */
-{yymsp[1].minor.yy407 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy407));}
+{yymsp[1].minor.yy609 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy609));}
break;
case 102: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy407 = yymsp[0].minor.yy407;
- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy407);
+ yymsp[-1].minor.yy609 = yymsp[0].minor.yy609;
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy609);
}
break;
case 103: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy407 && yymsp[-1].minor.yy407->nSrc>0) ) yymsp[-1].minor.yy407->a[yymsp[-1].minor.yy407->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy100;
+ if( ALWAYS(yymsp[-1].minor.yy609 && yymsp[-1].minor.yy609->nSrc>0) ) yymsp[-1].minor.yy609->a[yymsp[-1].minor.yy609->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy32;
}
break;
case 104: /* stl_prefix ::= */
-{yymsp[1].minor.yy407 = 0;}
+{yymsp[1].minor.yy609 = 0;}
break;
case 105: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
{
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy407, &yymsp[-2].minor.yy0);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy609, &yymsp[-2].minor.yy0);
}
break;
case 106: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
- yymsp[-8].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy407,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy407, yymsp[-4].minor.yy94);
+ yymsp[-8].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy609,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy609, yymsp[-4].minor.yy138);
}
break;
case 107: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
{
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy391,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy25,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
}
break;
case 108: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
- if( yymsp[-6].minor.yy407==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy102==0 && yymsp[0].minor.yy76==0 ){
- yymsp[-6].minor.yy407 = yymsp[-4].minor.yy407;
- }else if( yymsp[-4].minor.yy407->nSrc==1 ){
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- if( yymsp[-6].minor.yy407 ){
- struct SrcList_item *pNew = &yymsp[-6].minor.yy407->a[yymsp[-6].minor.yy407->nSrc-1];
- struct SrcList_item *pOld = yymsp[-4].minor.yy407->a;
+ if( yymsp[-6].minor.yy609==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy46==0 && yymsp[0].minor.yy406==0 ){
+ yymsp[-6].minor.yy609 = yymsp[-4].minor.yy609;
+ }else if( yymsp[-4].minor.yy609->nSrc==1 ){
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ if( yymsp[-6].minor.yy609 ){
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy609->a[yymsp[-6].minor.yy609->nSrc-1];
+ struct SrcList_item *pOld = yymsp[-4].minor.yy609->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -152360,12 +153402,12 @@ static YYACTIONTYPE yy_reduce(
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy407);
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy609);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy407);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy407,0,0,0,0,SF_NestedFrom,0);
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy609);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy609,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
}
}
break;
@@ -152375,63 +153417,63 @@ static YYACTIONTYPE yy_reduce(
break;
case 111: /* fullname ::= nm */
{
- yylhsminor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy407 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy407->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy609 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy609->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy407 = yylhsminor.yy407;
+ yymsp[0].minor.yy609 = yylhsminor.yy609;
break;
case 112: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy407 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy407->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy609 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy609->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy407 = yylhsminor.yy407;
+ yymsp[-2].minor.yy609 = yylhsminor.yy609;
break;
case 113: /* xfullname ::= nm */
-{yymsp[0].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
case 114: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[-2].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 115: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy407 ) yymsp[-4].minor.yy407->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy609 ) yymsp[-4].minor.yy609->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 116: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy407 ) yymsp[-2].minor.yy407->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy609 ) yymsp[-2].minor.yy609->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 117: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy100 = JT_INNER; }
+{ yymsp[0].minor.yy32 = JT_INNER; }
break;
case 118: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+{yymsp[-1].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
case 119: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+{yymsp[-2].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
case 120: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+{yymsp[-3].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
case 121: /* on_opt ::= ON expr */
- case 138: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==138);
- case 145: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==145);
- case 211: /* case_else ::= ELSE expr */ yytestcase(yyruleno==211);
- case 232: /* vinto ::= INTO expr */ yytestcase(yyruleno==232);
-{yymsp[-1].minor.yy102 = yymsp[0].minor.yy102;}
+ case 141: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==141);
+ case 148: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==148);
+ case 214: /* case_else ::= ELSE expr */ yytestcase(yyruleno==214);
+ case 235: /* vinto ::= INTO expr */ yytestcase(yyruleno==235);
+{yymsp[-1].minor.yy46 = yymsp[0].minor.yy46;}
break;
case 122: /* on_opt ::= */
- case 137: /* having_opt ::= */ yytestcase(yyruleno==137);
- case 139: /* limit_opt ::= */ yytestcase(yyruleno==139);
- case 144: /* where_opt ::= */ yytestcase(yyruleno==144);
- case 212: /* case_else ::= */ yytestcase(yyruleno==212);
- case 214: /* case_operand ::= */ yytestcase(yyruleno==214);
- case 233: /* vinto ::= */ yytestcase(yyruleno==233);
-{yymsp[1].minor.yy102 = 0;}
+ case 140: /* having_opt ::= */ yytestcase(yyruleno==140);
+ case 142: /* limit_opt ::= */ yytestcase(yyruleno==142);
+ case 147: /* where_opt ::= */ yytestcase(yyruleno==147);
+ case 215: /* case_else ::= */ yytestcase(yyruleno==215);
+ case 217: /* case_operand ::= */ yytestcase(yyruleno==217);
+ case 236: /* vinto ::= */ yytestcase(yyruleno==236);
+{yymsp[1].minor.yy46 = 0;}
break;
case 124: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
@@ -152440,121 +153482,128 @@ static YYACTIONTYPE yy_reduce(
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
case 126: /* using_opt ::= USING LP idlist RP */
-{yymsp[-3].minor.yy76 = yymsp[-1].minor.yy76;}
+{yymsp[-3].minor.yy406 = yymsp[-1].minor.yy406;}
break;
case 127: /* using_opt ::= */
- case 159: /* idlist_opt ::= */ yytestcase(yyruleno==159);
-{yymsp[1].minor.yy76 = 0;}
+ case 162: /* idlist_opt ::= */ yytestcase(yyruleno==162);
+{yymsp[1].minor.yy406 = 0;}
break;
case 129: /* orderby_opt ::= ORDER BY sortlist */
- case 136: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==136);
-{yymsp[-2].minor.yy94 = yymsp[0].minor.yy94;}
+ case 139: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==139);
+{yymsp[-2].minor.yy138 = yymsp[0].minor.yy138;}
break;
- case 130: /* sortlist ::= sortlist COMMA expr sortorder */
+ case 130: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy94,yymsp[-1].minor.yy102);
- sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy94,yymsp[0].minor.yy100);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138,yymsp[-2].minor.yy46);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy138,yymsp[-1].minor.yy32,yymsp[0].minor.yy32);
}
break;
- case 131: /* sortlist ::= expr sortorder */
+ case 131: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-1].minor.yy94 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy102); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy94,yymsp[0].minor.yy100);
+ yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy46); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy138,yymsp[-1].minor.yy32,yymsp[0].minor.yy32);
}
break;
case 132: /* sortorder ::= ASC */
-{yymsp[0].minor.yy100 = SQLITE_SO_ASC;}
+{yymsp[0].minor.yy32 = SQLITE_SO_ASC;}
break;
case 133: /* sortorder ::= DESC */
-{yymsp[0].minor.yy100 = SQLITE_SO_DESC;}
+{yymsp[0].minor.yy32 = SQLITE_SO_DESC;}
break;
case 134: /* sortorder ::= */
-{yymsp[1].minor.yy100 = SQLITE_SO_UNDEFINED;}
+ case 137: /* nulls ::= */ yytestcase(yyruleno==137);
+{yymsp[1].minor.yy32 = SQLITE_SO_UNDEFINED;}
+ break;
+ case 135: /* nulls ::= NULLS FIRST */
+{yymsp[-1].minor.yy32 = SQLITE_SO_ASC;}
break;
- case 140: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy102,0);}
+ case 136: /* nulls ::= NULLS LAST */
+{yymsp[-1].minor.yy32 = SQLITE_SO_DESC;}
break;
- case 141: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 143: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy46,0);}
break;
- case 142: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy102,yymsp[-2].minor.yy102);}
+ case 144: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 143: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ case 145: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy46,yymsp[-2].minor.yy46);}
+ break;
+ case 146: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy407, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy407,yymsp[0].minor.yy102,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy609, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy609,yymsp[0].minor.yy46,0,0);
}
break;
- case 146: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ case 149: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy407, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy94,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy407,yymsp[-1].minor.yy94,yymsp[0].minor.yy102,yymsp[-5].minor.yy100,0,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy609, &yymsp[-3].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy138,"set list");
+ sqlite3Update(pParse,yymsp[-4].minor.yy609,yymsp[-1].minor.yy138,yymsp[0].minor.yy46,yymsp[-5].minor.yy32,0,0,0);
}
break;
- case 147: /* setlist ::= setlist COMMA nm EQ expr */
+ case 150: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy94, yymsp[0].minor.yy102);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy94, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy138, yymsp[0].minor.yy46);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy138, &yymsp[-2].minor.yy0, 1);
}
break;
- case 148: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+ case 151: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy94 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy94, yymsp[-3].minor.yy76, yymsp[0].minor.yy102);
+ yymsp[-6].minor.yy138 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy138, yymsp[-3].minor.yy406, yymsp[0].minor.yy46);
}
break;
- case 149: /* setlist ::= nm EQ expr */
+ case 152: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy94 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy102);
- sqlite3ExprListSetName(pParse, yylhsminor.yy94, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy138 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy46);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy138, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy94 = yylhsminor.yy94;
+ yymsp[-2].minor.yy138 = yylhsminor.yy138;
break;
- case 150: /* setlist ::= LP idlist RP EQ expr */
+ case 153: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy76, yymsp[0].minor.yy102);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy406, yymsp[0].minor.yy46);
}
break;
- case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ case 154: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy407, yymsp[-1].minor.yy391, yymsp[-2].minor.yy76, yymsp[-5].minor.yy100, yymsp[0].minor.yy95);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy609, yymsp[-1].minor.yy25, yymsp[-2].minor.yy406, yymsp[-5].minor.yy32, yymsp[0].minor.yy288);
}
break;
- case 152: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ case 155: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy407, 0, yymsp[-2].minor.yy76, yymsp[-5].minor.yy100, 0);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy609, 0, yymsp[-2].minor.yy406, yymsp[-5].minor.yy32, 0);
}
break;
- case 153: /* upsert ::= */
-{ yymsp[1].minor.yy95 = 0; }
+ case 156: /* upsert ::= */
+{ yymsp[1].minor.yy288 = 0; }
break;
- case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
-{ yymsp[-10].minor.yy95 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy94,yymsp[-5].minor.yy102,yymsp[-1].minor.yy94,yymsp[0].minor.yy102);}
+ case 157: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+{ yymsp[-10].minor.yy288 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy138,yymsp[-5].minor.yy46,yymsp[-1].minor.yy138,yymsp[0].minor.yy46);}
break;
- case 155: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
-{ yymsp[-7].minor.yy95 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy94,yymsp[-2].minor.yy102,0,0); }
+ case 158: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+{ yymsp[-7].minor.yy288 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy138,yymsp[-2].minor.yy46,0,0); }
break;
- case 156: /* upsert ::= ON CONFLICT DO NOTHING */
-{ yymsp[-3].minor.yy95 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+ case 159: /* upsert ::= ON CONFLICT DO NOTHING */
+{ yymsp[-3].minor.yy288 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
break;
- case 160: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy76 = yymsp[-1].minor.yy76;}
+ case 163: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy406 = yymsp[-1].minor.yy406;}
break;
- case 161: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy76 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy76,&yymsp[0].minor.yy0);}
+ case 164: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy406 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy406,&yymsp[0].minor.yy0);}
break;
- case 162: /* idlist ::= nm */
-{yymsp[0].minor.yy76 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ case 165: /* idlist ::= nm */
+{yymsp[0].minor.yy406 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 163: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy102 = yymsp[-1].minor.yy102;}
+ case 166: /* expr ::= LP expr RP */
+{yymsp[-2].minor.yy46 = yymsp[-1].minor.yy46;}
break;
- case 164: /* expr ::= ID|INDEXED */
- case 165: /* expr ::= JOIN_KW */ yytestcase(yyruleno==165);
-{yymsp[0].minor.yy102=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 167: /* expr ::= ID|INDEXED */
+ case 168: /* expr ::= JOIN_KW */ yytestcase(yyruleno==168);
+{yymsp[0].minor.yy46=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 166: /* expr ::= nm DOT nm */
+ case 169: /* expr ::= nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
@@ -152562,11 +153611,11 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy102 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy46 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy102 = yylhsminor.yy102;
+ yymsp[-2].minor.yy46 = yylhsminor.yy46;
break;
- case 167: /* expr ::= nm DOT nm DOT nm */
+ case 170: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
@@ -152576,26 +153625,26 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy102 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy46 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 168: /* term ::= NULL|FLOAT|BLOB */
- case 169: /* term ::= STRING */ yytestcase(yyruleno==169);
-{yymsp[0].minor.yy102=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 171: /* term ::= NULL|FLOAT|BLOB */
+ case 172: /* term ::= STRING */ yytestcase(yyruleno==172);
+{yymsp[0].minor.yy46=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 170: /* term ::= INTEGER */
+ case 173: /* term ::= INTEGER */
{
- yylhsminor.yy102 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy46 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
- yymsp[0].minor.yy102 = yylhsminor.yy102;
+ yymsp[0].minor.yy46 = yylhsminor.yy46;
break;
- case 171: /* expr ::= VARIABLE */
+ case 174: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy102 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy102, n);
+ yymsp[0].minor.yy46 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy46, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -152604,156 +153653,156 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy102 = 0;
+ yymsp[0].minor.yy46 = 0;
}else{
- yymsp[0].minor.yy102 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy102 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy102->iTable);
+ yymsp[0].minor.yy46 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy46 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy46->iTable);
}
}
}
break;
- case 172: /* expr ::= expr COLLATE ID|STRING */
+ case 175: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy102 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy102, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy46 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy46, &yymsp[0].minor.yy0, 1);
}
break;
- case 173: /* expr ::= CAST LP expr AS typetoken RP */
+ case 176: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy102, yymsp[-3].minor.yy102, 0);
+ yymsp[-5].minor.yy46 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy46, yymsp[-3].minor.yy46, 0);
}
break;
- case 174: /* expr ::= ID|INDEXED LP distinct exprlist RP */
+ case 177: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy94, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy100);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy138, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy32);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 175: /* expr ::= ID|INDEXED LP STAR RP */
+ case 178: /* expr ::= ID|INDEXED LP STAR RP */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-3].minor.yy102 = yylhsminor.yy102;
+ yymsp[-3].minor.yy46 = yylhsminor.yy46;
break;
- case 176: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
+ case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy94, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy100);
- sqlite3WindowAttach(pParse, yylhsminor.yy102, yymsp[0].minor.yy379);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy138, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy32);
+ sqlite3WindowAttach(pParse, yylhsminor.yy46, yymsp[0].minor.yy455);
}
- yymsp[-5].minor.yy102 = yylhsminor.yy102;
+ yymsp[-5].minor.yy46 = yylhsminor.yy46;
break;
- case 177: /* expr ::= ID|INDEXED LP STAR RP over_clause */
+ case 180: /* expr ::= ID|INDEXED LP STAR RP filter_over */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy102, yymsp[0].minor.yy379);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy46, yymsp[0].minor.yy455);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 178: /* term ::= CTIME_KW */
+ case 181: /* term ::= CTIME_KW */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
- yymsp[0].minor.yy102 = yylhsminor.yy102;
+ yymsp[0].minor.yy46 = yylhsminor.yy46;
break;
- case 179: /* expr ::= LP nexprlist COMMA expr RP */
+ case 182: /* expr ::= LP nexprlist COMMA expr RP */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy94, yymsp[-1].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy138, yymsp[-1].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
- case 180: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy102=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 183: /* expr ::= expr AND expr */
+{yymsp[-2].minor.yy46=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 181: /* expr ::= expr OR expr */
- case 182: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==182);
- case 183: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==183);
- case 184: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==184);
- case 185: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==185);
- case 186: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==186);
- case 187: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==187);
-{yymsp[-2].minor.yy102=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 184: /* expr ::= expr OR expr */
+ case 185: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==185);
+ case 186: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==186);
+ case 187: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==187);
+ case 188: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==188);
+ case 189: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==189);
+ case 190: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==190);
+{yymsp[-2].minor.yy46=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 188: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 191: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 189: /* expr ::= expr likeop expr */
+ case 192: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy102);
- yymsp[-2].minor.yy102 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy102, 0);
- if( yymsp[-2].minor.yy102 ) yymsp[-2].minor.yy102->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy46);
+ yymsp[-2].minor.yy46 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy46, 0);
+ if( yymsp[-2].minor.yy46 ) yymsp[-2].minor.yy46->flags |= EP_InfixFunc;
}
break;
- case 190: /* expr ::= expr likeop expr ESCAPE expr */
+ case 193: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ) yymsp[-4].minor.yy102->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ) yymsp[-4].minor.yy46->flags |= EP_InfixFunc;
}
break;
- case 191: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy102,0);}
+ case 194: /* expr ::= expr ISNULL|NOTNULL */
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy46,0);}
break;
- case 192: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy102 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy102,0);}
+ case 195: /* expr ::= expr NOT NULL */
+{yymsp[-2].minor.yy46 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy46,0);}
break;
- case 193: /* expr ::= expr IS expr */
+ case 196: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy102 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy102, yymsp[-2].minor.yy102, TK_ISNULL);
+ yymsp[-2].minor.yy46 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy46, yymsp[-2].minor.yy46, TK_ISNULL);
}
break;
- case 194: /* expr ::= expr IS NOT expr */
+ case 197: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy102,yymsp[0].minor.yy102);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy102, yymsp[-3].minor.yy102, TK_NOTNULL);
+ yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy46,yymsp[0].minor.yy46);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy46, yymsp[-3].minor.yy46, TK_NOTNULL);
}
break;
- case 195: /* expr ::= NOT expr */
- case 196: /* expr ::= BITNOT expr */ yytestcase(yyruleno==196);
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy102, 0);/*A-overwrites-B*/}
+ case 198: /* expr ::= NOT expr */
+ case 199: /* expr ::= BITNOT expr */ yytestcase(yyruleno==199);
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy46, 0);/*A-overwrites-B*/}
break;
- case 197: /* expr ::= PLUS|MINUS expr */
+ case 200: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy102 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy102, 0);
+ yymsp[-1].minor.yy46 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy46, 0);
/*A-overwrites-B*/
}
break;
- case 198: /* between_op ::= BETWEEN */
- case 201: /* in_op ::= IN */ yytestcase(yyruleno==201);
-{yymsp[0].minor.yy100 = 0;}
+ case 201: /* between_op ::= BETWEEN */
+ case 204: /* in_op ::= IN */ yytestcase(yyruleno==204);
+{yymsp[0].minor.yy32 = 0;}
break;
- case 200: /* expr ::= expr between_op expr AND expr */
+ case 203: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 203: /* expr ::= expr in_op LP exprlist RP */
+ case 206: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy94==0 ){
+ if( yymsp[-1].minor.yy138==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -152762,218 +153811,190 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy100],1);
- }else if( yymsp[-1].minor.yy94->nExpr==1 ){
- /* Expressions of the form:
- **
- ** expr1 IN (?1)
- ** expr1 NOT IN (?2)
- **
- ** with exactly one value on the RHS can be simplified to something
- ** like this:
- **
- ** expr1 == ?1
- ** expr1 <> ?2
- **
- ** But, the RHS of the == or <> is marked with the EP_Generic flag
- ** so that it may not contribute to the computation of comparison
- ** affinity or the collating sequence to use for comparison. Otherwise,
- ** the semantics would be subtly different from IN or NOT IN.
- */
- Expr *pRHS = yymsp[-1].minor.yy94->a[0].pExpr;
- yymsp[-1].minor.yy94->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy94);
- /* pRHS cannot be NULL because a malloc error would have been detected
- ** before now and control would have never reached this point */
- if( ALWAYS(pRHS) ){
- pRHS->flags &= ~EP_Collate;
- pRHS->flags |= EP_Generic;
- }
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, yymsp[-3].minor.yy100 ? TK_NE : TK_EQ, yymsp[-4].minor.yy102, pRHS);
- }else{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = yymsp[-1].minor.yy94;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy102);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy32 ? "1" : "0");
+ }else{
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = yymsp[-1].minor.yy138;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy46);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy94);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy138);
}
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
}
break;
- case 204: /* expr ::= LP select RP */
+ case 207: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy102 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy102, yymsp[-1].minor.yy391);
+ yymsp[-2].minor.yy46 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy46, yymsp[-1].minor.yy25);
}
break;
- case 205: /* expr ::= expr in_op LP select RP */
+ case 208: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy102, yymsp[-1].minor.yy391);
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy46, yymsp[-1].minor.yy25);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 206: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 209: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy94 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy94);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy102, pSelect);
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[0].minor.yy138 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy138);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy46, pSelect);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 207: /* expr ::= EXISTS LP select RP */
+ case 210: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy102 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy391);
+ p = yymsp[-3].minor.yy46 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy25);
}
break;
- case 208: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 211: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = yymsp[-1].minor.yy102 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy94,yymsp[-1].minor.yy102) : yymsp[-2].minor.yy94;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy102);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = yymsp[-1].minor.yy46 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy138,yymsp[-1].minor.yy46) : yymsp[-2].minor.yy138;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy46);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy94);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy102);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy138);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy46);
}
}
break;
- case 209: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 212: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, yymsp[-2].minor.yy102);
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, yymsp[0].minor.yy102);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, yymsp[-2].minor.yy46);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, yymsp[0].minor.yy46);
}
break;
- case 210: /* case_exprlist ::= WHEN expr THEN expr */
+ case 213: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy94, yymsp[0].minor.yy102);
+ yymsp[-3].minor.yy138 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ yymsp[-3].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy138, yymsp[0].minor.yy46);
}
break;
- case 213: /* case_operand ::= expr */
-{yymsp[0].minor.yy102 = yymsp[0].minor.yy102; /*A-overwrites-X*/}
+ case 216: /* case_operand ::= expr */
+{yymsp[0].minor.yy46 = yymsp[0].minor.yy46; /*A-overwrites-X*/}
break;
- case 216: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy94,yymsp[0].minor.yy102);}
+ case 219: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy138,yymsp[0].minor.yy46);}
break;
- case 217: /* nexprlist ::= expr */
-{yymsp[0].minor.yy94 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy102); /*A-overwrites-Y*/}
+ case 220: /* nexprlist ::= expr */
+{yymsp[0].minor.yy138 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy46); /*A-overwrites-Y*/}
break;
- case 219: /* paren_exprlist ::= LP exprlist RP */
- case 224: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==224);
-{yymsp[-2].minor.yy94 = yymsp[-1].minor.yy94;}
+ case 222: /* paren_exprlist ::= LP exprlist RP */
+ case 227: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==227);
+{yymsp[-2].minor.yy138 = yymsp[-1].minor.yy138;}
break;
- case 220: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 223: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy94, yymsp[-10].minor.yy100,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy102, SQLITE_SO_ASC, yymsp[-8].minor.yy100, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy138, yymsp[-10].minor.yy32,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy46, SQLITE_SO_ASC, yymsp[-8].minor.yy32, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
- case 221: /* uniqueflag ::= UNIQUE */
- case 263: /* raisetype ::= ABORT */ yytestcase(yyruleno==263);
-{yymsp[0].minor.yy100 = OE_Abort;}
+ case 224: /* uniqueflag ::= UNIQUE */
+ case 266: /* raisetype ::= ABORT */ yytestcase(yyruleno==266);
+{yymsp[0].minor.yy32 = OE_Abort;}
break;
- case 222: /* uniqueflag ::= */
-{yymsp[1].minor.yy100 = OE_None;}
+ case 225: /* uniqueflag ::= */
+{yymsp[1].minor.yy32 = OE_None;}
break;
- case 225: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 228: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy94 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy94, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy100, yymsp[0].minor.yy100);
+ yymsp[-4].minor.yy138 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy138, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy32, yymsp[0].minor.yy32);
}
break;
- case 226: /* eidlist ::= nm collate sortorder */
+ case 229: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy94 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy100, yymsp[0].minor.yy100); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy138 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy32, yymsp[0].minor.yy32); /*A-overwrites-Y*/
}
break;
- case 229: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy407, yymsp[-1].minor.yy100);}
+ case 232: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy609, yymsp[-1].minor.yy32);}
break;
- case 230: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy102);}
+ case 233: /* cmd ::= VACUUM vinto */
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy46);}
break;
- case 231: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy102);}
+ case 234: /* cmd ::= VACUUM nm vinto */
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy46);}
break;
- case 234: /* cmd ::= PRAGMA nm dbnm */
+ case 237: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 235: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 238: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 236: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 239: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 237: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 240: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 238: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 241: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 241: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 244: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy11, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy527, &all);
}
break;
- case 242: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 245: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy100, yymsp[-4].minor.yy298.a, yymsp[-4].minor.yy298.b, yymsp[-2].minor.yy407, yymsp[0].minor.yy102, yymsp[-10].minor.yy100, yymsp[-8].minor.yy100);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy32, yymsp[-4].minor.yy572.a, yymsp[-4].minor.yy572.b, yymsp[-2].minor.yy609, yymsp[0].minor.yy46, yymsp[-10].minor.yy32, yymsp[-8].minor.yy32);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 243: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-X*/ }
+ case 246: /* trigger_time ::= BEFORE|AFTER */
+{ yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 244: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy100 = TK_INSTEAD;}
+ case 247: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy32 = TK_INSTEAD;}
break;
- case 245: /* trigger_time ::= */
-{ yymsp[1].minor.yy100 = TK_BEFORE; }
+ case 248: /* trigger_time ::= */
+{ yymsp[1].minor.yy32 = TK_BEFORE; }
break;
- case 246: /* trigger_event ::= DELETE|INSERT */
- case 247: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==247);
-{yymsp[0].minor.yy298.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy298.b = 0;}
+ case 249: /* trigger_event ::= DELETE|INSERT */
+ case 250: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==250);
+{yymsp[0].minor.yy572.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy572.b = 0;}
break;
- case 248: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy298.a = TK_UPDATE; yymsp[-2].minor.yy298.b = yymsp[0].minor.yy76;}
+ case 251: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy572.a = TK_UPDATE; yymsp[-2].minor.yy572.b = yymsp[0].minor.yy406;}
break;
- case 249: /* when_clause ::= */
- case 268: /* key_opt ::= */ yytestcase(yyruleno==268);
- case 316: /* filter_opt ::= */ yytestcase(yyruleno==316);
-{ yymsp[1].minor.yy102 = 0; }
+ case 252: /* when_clause ::= */
+ case 271: /* key_opt ::= */ yytestcase(yyruleno==271);
+{ yymsp[1].minor.yy46 = 0; }
break;
- case 250: /* when_clause ::= WHEN expr */
- case 269: /* key_opt ::= KEY expr */ yytestcase(yyruleno==269);
-{ yymsp[-1].minor.yy102 = yymsp[0].minor.yy102; }
+ case 253: /* when_clause ::= WHEN expr */
+ case 272: /* key_opt ::= KEY expr */ yytestcase(yyruleno==272);
+{ yymsp[-1].minor.yy46 = yymsp[0].minor.yy46; }
break;
- case 251: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 254: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy11!=0 );
- yymsp[-2].minor.yy11->pLast->pNext = yymsp[-1].minor.yy11;
- yymsp[-2].minor.yy11->pLast = yymsp[-1].minor.yy11;
+ assert( yymsp[-2].minor.yy527!=0 );
+ yymsp[-2].minor.yy527->pLast->pNext = yymsp[-1].minor.yy527;
+ yymsp[-2].minor.yy527->pLast = yymsp[-1].minor.yy527;
}
break;
- case 252: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 255: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy11!=0 );
- yymsp[-1].minor.yy11->pLast = yymsp[-1].minor.yy11;
+ assert( yymsp[-1].minor.yy527!=0 );
+ yymsp[-1].minor.yy527->pLast = yymsp[-1].minor.yy527;
}
break;
- case 253: /* trnm ::= nm DOT nm */
+ case 256: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -152981,328 +154002,342 @@ static YYACTIONTYPE yy_reduce(
"statements within triggers");
}
break;
- case 254: /* tridxby ::= INDEXED BY nm */
+ case 257: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 255: /* tridxby ::= NOT INDEXED */
+ case 258: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 256: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
-{yylhsminor.yy11 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy94, yymsp[-1].minor.yy102, yymsp[-6].minor.yy100, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy528);}
- yymsp[-7].minor.yy11 = yylhsminor.yy11;
+ case 259: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+{yylhsminor.yy527 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy138, yymsp[-1].minor.yy46, yymsp[-6].minor.yy32, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy8);}
+ yymsp[-7].minor.yy527 = yylhsminor.yy527;
break;
- case 257: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 260: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy11 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy76,yymsp[-2].minor.yy391,yymsp[-6].minor.yy100,yymsp[-1].minor.yy95,yymsp[-7].minor.yy528,yymsp[0].minor.yy528);/*yylhsminor.yy11-overwrites-yymsp[-6].minor.yy100*/
+ yylhsminor.yy527 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy406,yymsp[-2].minor.yy25,yymsp[-6].minor.yy32,yymsp[-1].minor.yy288,yymsp[-7].minor.yy8,yymsp[0].minor.yy8);/*yylhsminor.yy527-overwrites-yymsp[-6].minor.yy32*/
}
- yymsp[-7].minor.yy11 = yylhsminor.yy11;
+ yymsp[-7].minor.yy527 = yylhsminor.yy527;
break;
- case 258: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy11 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy102, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy528);}
- yymsp[-5].minor.yy11 = yylhsminor.yy11;
+ case 261: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+{yylhsminor.yy527 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy46, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy8);}
+ yymsp[-5].minor.yy527 = yylhsminor.yy527;
break;
- case 259: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy11 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy391, yymsp[-2].minor.yy528, yymsp[0].minor.yy528); /*yylhsminor.yy11-overwrites-yymsp[-1].minor.yy391*/}
- yymsp[-2].minor.yy11 = yylhsminor.yy11;
+ case 262: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy527 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy25, yymsp[-2].minor.yy8, yymsp[0].minor.yy8); /*yylhsminor.yy527-overwrites-yymsp[-1].minor.yy25*/}
+ yymsp[-2].minor.yy527 = yylhsminor.yy527;
break;
- case 260: /* expr ::= RAISE LP IGNORE RP */
+ case 263: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy102 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy102 ){
- yymsp[-3].minor.yy102->affinity = OE_Ignore;
+ yymsp[-3].minor.yy46 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy46 ){
+ yymsp[-3].minor.yy46->affExpr = OE_Ignore;
}
}
break;
- case 261: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 264: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy102 ) {
- yymsp[-5].minor.yy102->affinity = (char)yymsp[-3].minor.yy100;
+ yymsp[-5].minor.yy46 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy46 ) {
+ yymsp[-5].minor.yy46->affExpr = (char)yymsp[-3].minor.yy32;
}
}
break;
- case 262: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy100 = OE_Rollback;}
+ case 265: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy32 = OE_Rollback;}
break;
- case 264: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy100 = OE_Fail;}
+ case 267: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy32 = OE_Fail;}
break;
- case 265: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 268: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy407,yymsp[-1].minor.yy100);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy609,yymsp[-1].minor.yy32);
}
break;
- case 266: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 269: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy102, yymsp[-1].minor.yy102, yymsp[0].minor.yy102);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy46, yymsp[-1].minor.yy46, yymsp[0].minor.yy46);
}
break;
- case 267: /* cmd ::= DETACH database_kw_opt expr */
+ case 270: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy102);
+ sqlite3Detach(pParse, yymsp[0].minor.yy46);
}
break;
- case 270: /* cmd ::= REINDEX */
+ case 273: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 271: /* cmd ::= REINDEX nm dbnm */
+ case 274: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 272: /* cmd ::= ANALYZE */
+ case 275: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 273: /* cmd ::= ANALYZE nm dbnm */
+ case 276: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 274: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 277: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy407,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy609,&yymsp[0].minor.yy0);
}
break;
- case 275: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 278: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 276: /* add_column_fullname ::= fullname */
+ case 279: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy407);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy609);
}
break;
- case 277: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 280: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy407, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy609, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 278: /* cmd ::= create_vtab */
+ case 281: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 279: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 282: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 280: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 283: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy100);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy32);
}
break;
- case 281: /* vtabarg ::= */
+ case 284: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 282: /* vtabargtoken ::= ANY */
- case 283: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==283);
- case 284: /* lp ::= LP */ yytestcase(yyruleno==284);
+ case 285: /* vtabargtoken ::= ANY */
+ case 286: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==286);
+ case 287: /* lp ::= LP */ yytestcase(yyruleno==287);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 285: /* with ::= WITH wqlist */
- case 286: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==286);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy243, 1); }
+ case 288: /* with ::= WITH wqlist */
+ case 289: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==289);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy297, 1); }
break;
- case 287: /* wqlist ::= nm eidlist_opt AS LP select RP */
+ case 290: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
- yymsp[-5].minor.yy243 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy94, yymsp[-1].minor.yy391); /*A-overwrites-X*/
+ yymsp[-5].minor.yy297 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy138, yymsp[-1].minor.yy25); /*A-overwrites-X*/
}
break;
- case 288: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ case 291: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
- yymsp[-7].minor.yy243 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy243, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy94, yymsp[-1].minor.yy391);
+ yymsp[-7].minor.yy297 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy297, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy138, yymsp[-1].minor.yy25);
}
break;
- case 289: /* windowdefn_list ::= windowdefn */
-{ yylhsminor.yy379 = yymsp[0].minor.yy379; }
- yymsp[0].minor.yy379 = yylhsminor.yy379;
+ case 292: /* windowdefn_list ::= windowdefn */
+{ yylhsminor.yy455 = yymsp[0].minor.yy455; }
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 290: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 293: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy379!=0 );
- sqlite3WindowChain(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy379);
- yymsp[0].minor.yy379->pNextWin = yymsp[-2].minor.yy379;
- yylhsminor.yy379 = yymsp[0].minor.yy379;
+ assert( yymsp[0].minor.yy455!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy455);
+ yymsp[0].minor.yy455->pNextWin = yymsp[-2].minor.yy455;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[-2].minor.yy455 = yylhsminor.yy455;
break;
- case 291: /* windowdefn ::= nm AS LP window RP */
+ case 294: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[-1].minor.yy379) ){
- yymsp[-1].minor.yy379->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy455) ){
+ yymsp[-1].minor.yy455->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy379 = yymsp[-1].minor.yy379;
+ yylhsminor.yy455 = yymsp[-1].minor.yy455;
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-4].minor.yy455 = yylhsminor.yy455;
break;
- case 292: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ case 295: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy94, yymsp[-1].minor.yy94, 0);
+ yymsp[-4].minor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy138, yymsp[-1].minor.yy138, 0);
}
break;
- case 293: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ case 296: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy94, yymsp[-1].minor.yy94, &yymsp[-5].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy138, yymsp[-1].minor.yy138, &yymsp[-5].minor.yy0);
}
- yymsp[-5].minor.yy379 = yylhsminor.yy379;
+ yymsp[-5].minor.yy455 = yylhsminor.yy455;
break;
- case 294: /* window ::= ORDER BY sortlist frame_opt */
+ case 297: /* window ::= ORDER BY sortlist frame_opt */
{
- yymsp[-3].minor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, yymsp[-1].minor.yy94, 0);
+ yymsp[-3].minor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, yymsp[-1].minor.yy138, 0);
}
break;
- case 295: /* window ::= nm ORDER BY sortlist frame_opt */
+ case 298: /* window ::= nm ORDER BY sortlist frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, yymsp[-1].minor.yy94, &yymsp[-4].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, yymsp[-1].minor.yy138, &yymsp[-4].minor.yy0);
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-4].minor.yy455 = yylhsminor.yy455;
break;
- case 296: /* window ::= frame_opt */
+ case 299: /* window ::= frame_opt */
+ case 318: /* filter_over ::= over_clause */ yytestcase(yyruleno==318);
{
- yylhsminor.yy379 = yymsp[0].minor.yy379;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[0].minor.yy379 = yylhsminor.yy379;
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 297: /* window ::= nm frame_opt */
+ case 300: /* window ::= nm frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, 0, &yymsp[-1].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy379 = yylhsminor.yy379;
+ yymsp[-1].minor.yy455 = yylhsminor.yy455;
break;
- case 298: /* frame_opt ::= */
+ case 301: /* frame_opt ::= */
{
- yymsp[1].minor.yy379 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+ yymsp[1].minor.yy455 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 299: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ case 302: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy379 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy100, yymsp[-1].minor.yy389.eType, yymsp[-1].minor.yy389.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy218);
+ yylhsminor.yy455 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy32, yymsp[-1].minor.yy57.eType, yymsp[-1].minor.yy57.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy118);
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[-2].minor.yy455 = yylhsminor.yy455;
break;
- case 300: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ case 303: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy379 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy100, yymsp[-3].minor.yy389.eType, yymsp[-3].minor.yy389.pExpr, yymsp[-1].minor.yy389.eType, yymsp[-1].minor.yy389.pExpr, yymsp[0].minor.yy218);
+ yylhsminor.yy455 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy32, yymsp[-3].minor.yy57.eType, yymsp[-3].minor.yy57.pExpr, yymsp[-1].minor.yy57.eType, yymsp[-1].minor.yy57.pExpr, yymsp[0].minor.yy118);
}
- yymsp[-5].minor.yy379 = yylhsminor.yy379;
+ yymsp[-5].minor.yy455 = yylhsminor.yy455;
break;
- case 302: /* frame_bound_s ::= frame_bound */
- case 304: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==304);
-{yylhsminor.yy389 = yymsp[0].minor.yy389;}
- yymsp[0].minor.yy389 = yylhsminor.yy389;
+ case 305: /* frame_bound_s ::= frame_bound */
+ case 307: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==307);
+{yylhsminor.yy57 = yymsp[0].minor.yy57;}
+ yymsp[0].minor.yy57 = yylhsminor.yy57;
break;
- case 303: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 305: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==305);
- case 307: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==307);
-{yylhsminor.yy389.eType = yymsp[-1].major; yylhsminor.yy389.pExpr = 0;}
- yymsp[-1].minor.yy389 = yylhsminor.yy389;
+ case 306: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 308: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==308);
+ case 310: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==310);
+{yylhsminor.yy57.eType = yymsp[-1].major; yylhsminor.yy57.pExpr = 0;}
+ yymsp[-1].minor.yy57 = yylhsminor.yy57;
break;
- case 306: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy389.eType = yymsp[0].major; yylhsminor.yy389.pExpr = yymsp[-1].minor.yy102;}
- yymsp[-1].minor.yy389 = yylhsminor.yy389;
+ case 309: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy57.eType = yymsp[0].major; yylhsminor.yy57.pExpr = yymsp[-1].minor.yy46;}
+ yymsp[-1].minor.yy57 = yylhsminor.yy57;
break;
- case 308: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy218 = 0;}
+ case 311: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy118 = 0;}
break;
- case 309: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy218 = yymsp[0].minor.yy218;}
+ case 312: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy118 = yymsp[0].minor.yy118;}
break;
- case 310: /* frame_exclude ::= NO OTHERS */
- case 311: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==311);
-{yymsp[-1].minor.yy218 = yymsp[-1].major; /*A-overwrites-X*/}
+ case 313: /* frame_exclude ::= NO OTHERS */
+ case 314: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==314);
+{yymsp[-1].minor.yy118 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 312: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy218 = yymsp[0].major; /*A-overwrites-X*/}
+ case 315: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy118 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 313: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy379 = yymsp[0].minor.yy379; }
+ case 316: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy455 = yymsp[0].minor.yy455; }
break;
- case 314: /* over_clause ::= filter_opt OVER LP window RP */
+ case 317: /* filter_over ::= filter_clause over_clause */
{
- yylhsminor.yy379 = yymsp[-1].minor.yy379;
- assert( yylhsminor.yy379!=0 );
- yylhsminor.yy379->pFilter = yymsp[-4].minor.yy102;
+ yymsp[0].minor.yy455->pFilter = yymsp[-1].minor.yy46;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-1].minor.yy455 = yylhsminor.yy455;
break;
- case 315: /* over_clause ::= filter_opt OVER nm */
+ case 319: /* filter_over ::= filter_clause */
{
- yylhsminor.yy379 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy379 ){
- yylhsminor.yy379->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
- yylhsminor.yy379->pFilter = yymsp[-2].minor.yy102;
+ yylhsminor.yy455 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy455 ){
+ yylhsminor.yy455->eFrmType = TK_FILTER;
+ yylhsminor.yy455->pFilter = yymsp[0].minor.yy46;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy102);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy46);
}
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 317: /* filter_opt ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy102 = yymsp[-1].minor.yy102; }
+ case 320: /* over_clause ::= OVER LP window RP */
+{
+ yymsp[-3].minor.yy455 = yymsp[-1].minor.yy455;
+ assert( yymsp[-3].minor.yy455!=0 );
+}
+ break;
+ case 321: /* over_clause ::= OVER nm */
+{
+ yymsp[-1].minor.yy455 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy455 ){
+ yymsp[-1].minor.yy455->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ }
+}
+ break;
+ case 322: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy46 = yymsp[-1].minor.yy46; }
break;
default:
- /* (318) input ::= cmdlist */ yytestcase(yyruleno==318);
- /* (319) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==319);
- /* (320) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=320);
- /* (321) ecmd ::= SEMI */ yytestcase(yyruleno==321);
- /* (322) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==322);
- /* (323) ecmd ::= explain cmdx */ yytestcase(yyruleno==323);
- /* (324) trans_opt ::= */ yytestcase(yyruleno==324);
- /* (325) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==325);
- /* (326) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==326);
- /* (327) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==327);
- /* (328) savepoint_opt ::= */ yytestcase(yyruleno==328);
- /* (329) cmd ::= create_table create_table_args */ yytestcase(yyruleno==329);
- /* (330) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==330);
- /* (331) columnlist ::= columnname carglist */ yytestcase(yyruleno==331);
- /* (332) nm ::= ID|INDEXED */ yytestcase(yyruleno==332);
- /* (333) nm ::= STRING */ yytestcase(yyruleno==333);
- /* (334) nm ::= JOIN_KW */ yytestcase(yyruleno==334);
- /* (335) typetoken ::= typename */ yytestcase(yyruleno==335);
- /* (336) typename ::= ID|STRING */ yytestcase(yyruleno==336);
- /* (337) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=337);
- /* (338) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=338);
- /* (339) carglist ::= carglist ccons */ yytestcase(yyruleno==339);
- /* (340) carglist ::= */ yytestcase(yyruleno==340);
- /* (341) ccons ::= NULL onconf */ yytestcase(yyruleno==341);
- /* (342) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==342);
- /* (343) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==343);
- /* (344) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=344);
- /* (345) tconscomma ::= */ yytestcase(yyruleno==345);
- /* (346) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=346);
- /* (347) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=347);
- /* (348) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=348);
- /* (349) oneselect ::= values */ yytestcase(yyruleno==349);
- /* (350) sclp ::= selcollist COMMA */ yytestcase(yyruleno==350);
- /* (351) as ::= ID|STRING */ yytestcase(yyruleno==351);
- /* (352) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=352);
- /* (353) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==353);
- /* (354) exprlist ::= nexprlist */ yytestcase(yyruleno==354);
- /* (355) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=355);
- /* (356) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=356);
- /* (357) nmnum ::= ON */ yytestcase(yyruleno==357);
- /* (358) nmnum ::= DELETE */ yytestcase(yyruleno==358);
- /* (359) nmnum ::= DEFAULT */ yytestcase(yyruleno==359);
- /* (360) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==360);
- /* (361) foreach_clause ::= */ yytestcase(yyruleno==361);
- /* (362) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==362);
- /* (363) trnm ::= nm */ yytestcase(yyruleno==363);
- /* (364) tridxby ::= */ yytestcase(yyruleno==364);
- /* (365) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==365);
- /* (366) database_kw_opt ::= */ yytestcase(yyruleno==366);
- /* (367) kwcolumn_opt ::= */ yytestcase(yyruleno==367);
- /* (368) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==368);
- /* (369) vtabarglist ::= vtabarg */ yytestcase(yyruleno==369);
- /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==370);
- /* (371) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==371);
- /* (372) anylist ::= */ yytestcase(yyruleno==372);
- /* (373) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==373);
- /* (374) anylist ::= anylist ANY */ yytestcase(yyruleno==374);
- /* (375) with ::= */ yytestcase(yyruleno==375);
+ /* (323) input ::= cmdlist */ yytestcase(yyruleno==323);
+ /* (324) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==324);
+ /* (325) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=325);
+ /* (326) ecmd ::= SEMI */ yytestcase(yyruleno==326);
+ /* (327) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==327);
+ /* (328) ecmd ::= explain cmdx */ yytestcase(yyruleno==328);
+ /* (329) trans_opt ::= */ yytestcase(yyruleno==329);
+ /* (330) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==330);
+ /* (331) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==331);
+ /* (332) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==332);
+ /* (333) savepoint_opt ::= */ yytestcase(yyruleno==333);
+ /* (334) cmd ::= create_table create_table_args */ yytestcase(yyruleno==334);
+ /* (335) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==335);
+ /* (336) columnlist ::= columnname carglist */ yytestcase(yyruleno==336);
+ /* (337) nm ::= ID|INDEXED */ yytestcase(yyruleno==337);
+ /* (338) nm ::= STRING */ yytestcase(yyruleno==338);
+ /* (339) nm ::= JOIN_KW */ yytestcase(yyruleno==339);
+ /* (340) typetoken ::= typename */ yytestcase(yyruleno==340);
+ /* (341) typename ::= ID|STRING */ yytestcase(yyruleno==341);
+ /* (342) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=342);
+ /* (343) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=343);
+ /* (344) carglist ::= carglist ccons */ yytestcase(yyruleno==344);
+ /* (345) carglist ::= */ yytestcase(yyruleno==345);
+ /* (346) ccons ::= NULL onconf */ yytestcase(yyruleno==346);
+ /* (347) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==347);
+ /* (348) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==348);
+ /* (349) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=349);
+ /* (350) tconscomma ::= */ yytestcase(yyruleno==350);
+ /* (351) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=351);
+ /* (352) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=352);
+ /* (353) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=353);
+ /* (354) oneselect ::= values */ yytestcase(yyruleno==354);
+ /* (355) sclp ::= selcollist COMMA */ yytestcase(yyruleno==355);
+ /* (356) as ::= ID|STRING */ yytestcase(yyruleno==356);
+ /* (357) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=357);
+ /* (358) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==358);
+ /* (359) exprlist ::= nexprlist */ yytestcase(yyruleno==359);
+ /* (360) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
+ /* (361) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=361);
+ /* (362) nmnum ::= ON */ yytestcase(yyruleno==362);
+ /* (363) nmnum ::= DELETE */ yytestcase(yyruleno==363);
+ /* (364) nmnum ::= DEFAULT */ yytestcase(yyruleno==364);
+ /* (365) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==365);
+ /* (366) foreach_clause ::= */ yytestcase(yyruleno==366);
+ /* (367) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==367);
+ /* (368) trnm ::= nm */ yytestcase(yyruleno==368);
+ /* (369) tridxby ::= */ yytestcase(yyruleno==369);
+ /* (370) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==370);
+ /* (371) database_kw_opt ::= */ yytestcase(yyruleno==371);
+ /* (372) kwcolumn_opt ::= */ yytestcase(yyruleno==372);
+ /* (373) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==373);
+ /* (374) vtabarglist ::= vtabarg */ yytestcase(yyruleno==374);
+ /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==375);
+ /* (376) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==376);
+ /* (377) anylist ::= */ yytestcase(yyruleno==377);
+ /* (378) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==378);
+ /* (379) anylist ::= anylist ANY */ yytestcase(yyruleno==379);
+ /* (380) with ::= */ yytestcase(yyruleno==380);
break;
/********** End reduce actions ************************************************/
};
@@ -153594,9 +154629,8 @@ SQLITE_PRIVATE void sqlite3Parser(
*/
SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
#ifdef YYFALLBACK
- if( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ){
- return yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
+ return yyFallback[iToken];
#else
(void)iToken;
#endif
@@ -153765,144 +154799,146 @@ const unsigned char ebcdicToAscii[] = {
** is substantially reduced. This is important for embedded applications
** on platforms with limited memory.
*/
-/* Hash score: 214 */
-/* zKWText[] encodes 950 bytes of keyword text in 629 bytes */
+/* Hash score: 221 */
+/* zKWText[] encodes 967 bytes of keyword text in 638 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
-/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYCONSTRAINTERSECTIES */
-/* AVEPOINTOFFSETRANSACTIONATURALTERAISEXCEPTRIGGEREFERENCES */
-/* UNIQUERYWITHOUTERELEASEXCLUSIVEXISTSATTACHAVINGLOBEGINNERANGE */
-/* BETWEENOTHINGROUPSCASCADETACHCASECOLLATECREATECURRENT_DATE */
-/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTUPDATEVALUES */
-/* VIRTUALIMITWHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULT */
+/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */
+/* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */
+/* CONSTRAINTOFFSETRIGGEREFERENCESUNIQUERYWITHOUTERELEASEATTACH */
+/* AVINGLOBEGINNERANGEBETWEENOTHINGROUPSCASCADETACHCASECOLLATE */
+/* CREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZEPRAGMABORT */
+/* UPDATEVALUESVIRTUALASTWHENWHERECURSIVEAFTERENAMEANDEFAULT */
/* AUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMP */
-/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWING */
-/* FROMFULLIFISNULLORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */
+/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILIMITFILTEREPLACEFIRST */
+/* FOLLOWINGFROMFULLIFORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */
/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */
-static const char zKWText[628] = {
+static const char zKWText[637] = {
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F',
'E','R','R','A','B','L','E','L','S','E','X','C','L','U','D','E','L','E',
- 'T','E','M','P','O','R','A','R','Y','C','O','N','S','T','R','A','I','N',
- 'T','E','R','S','E','C','T','I','E','S','A','V','E','P','O','I','N','T',
- 'O','F','F','S','E','T','R','A','N','S','A','C','T','I','O','N','A','T',
- 'U','R','A','L','T','E','R','A','I','S','E','X','C','E','P','T','R','I',
- 'G','G','E','R','E','F','E','R','E','N','C','E','S','U','N','I','Q','U',
- 'E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S','E',
- 'X','C','L','U','S','I','V','E','X','I','S','T','S','A','T','T','A','C',
- 'H','A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N',
- 'G','E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U',
- 'P','S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C',
- 'O','L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N',
- 'T','_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I',
- 'N','S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N',
- 'A','L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','U','P','D',
- 'A','T','E','V','A','L','U','E','S','V','I','R','T','U','A','L','I','M',
- 'I','T','W','H','E','N','O','T','N','U','L','L','W','H','E','R','E','C',
- 'U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E','A','N',
- 'D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E','M','E',
- 'N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M','I','T',
- 'C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R','R','E',
- 'N','T','_','T','I','M','E','S','T','A','M','P','A','R','T','I','T','I',
- 'O','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D',
- 'R','O','P','R','E','C','E','D','I','N','G','F','A','I','L','F','I','L',
- 'T','E','R','E','P','L','A','C','E','F','O','L','L','O','W','I','N','G',
- 'F','R','O','M','F','U','L','L','I','F','I','S','N','U','L','L','O','R',
- 'D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O','V',
- 'E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W','S',
- 'U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I','N',
- 'G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B','Y',
- 'I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y',
+ 'T','E','M','P','O','R','A','R','Y','I','S','N','U','L','L','S','A','V',
+ 'E','P','O','I','N','T','E','R','S','E','C','T','I','E','S','N','O','T',
+ 'N','U','L','L','I','K','E','X','C','E','P','T','R','A','N','S','A','C',
+ 'T','I','O','N','A','T','U','R','A','L','T','E','R','A','I','S','E','X',
+ 'C','L','U','S','I','V','E','X','I','S','T','S','C','O','N','S','T','R',
+ 'A','I','N','T','O','F','F','S','E','T','R','I','G','G','E','R','E','F',
+ 'E','R','E','N','C','E','S','U','N','I','Q','U','E','R','Y','W','I','T',
+ 'H','O','U','T','E','R','E','L','E','A','S','E','A','T','T','A','C','H',
+ 'A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N','G',
+ 'E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U','P',
+ 'S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C','O',
+ 'L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N','T',
+ '_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I','N',
+ 'S','E','R','T','M','A','T','C','H','P','L','A','N','A','L','Y','Z','E',
+ 'P','R','A','G','M','A','B','O','R','T','U','P','D','A','T','E','V','A',
+ 'L','U','E','S','V','I','R','T','U','A','L','A','S','T','W','H','E','N',
+ 'W','H','E','R','E','C','U','R','S','I','V','E','A','F','T','E','R','E',
+ 'N','A','M','E','A','N','D','E','F','A','U','L','T','A','U','T','O','I',
+ 'N','C','R','E','M','E','N','T','C','A','S','T','C','O','L','U','M','N',
+ 'C','O','M','M','I','T','C','O','N','F','L','I','C','T','C','R','O','S',
+ 'S','C','U','R','R','E','N','T','_','T','I','M','E','S','T','A','M','P',
+ 'A','R','T','I','T','I','O','N','D','E','F','E','R','R','E','D','I','S',
+ 'T','I','N','C','T','D','R','O','P','R','E','C','E','D','I','N','G','F',
+ 'A','I','L','I','M','I','T','F','I','L','T','E','R','E','P','L','A','C',
+ 'E','F','I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O',
+ 'M','F','U','L','L','I','F','O','R','D','E','R','E','S','T','R','I','C',
+ 'T','O','T','H','E','R','S','O','V','E','R','I','G','H','T','R','O','L',
+ 'L','B','A','C','K','R','O','W','S','U','N','B','O','U','N','D','E','D',
+ 'U','N','I','O','N','U','S','I','N','G','V','A','C','U','U','M','V','I',
+ 'E','W','I','N','D','O','W','B','Y','I','N','I','T','I','A','L','L','Y',
+ 'P','R','I','M','A','R','Y',
};
/* aKWHash[i] is the hash value for the i-th keyword */
static const unsigned char aKWHash[127] = {
- 75, 111, 127, 73, 108, 29, 0, 0, 83, 0, 77, 63, 0,
- 37, 33, 78, 15, 0, 126, 86, 57, 120, 128, 19, 0, 0,
- 133, 0, 131, 123, 0, 22, 98, 0, 9, 0, 0, 117, 71,
- 0, 69, 6, 0, 49, 95, 140, 0, 129, 106, 0, 0, 54,
- 0, 109, 24, 0, 17, 0, 134, 56, 23, 26, 5, 58, 135,
- 101, 0, 0, 139, 112, 62, 138, 59, 115, 65, 0, 96, 0,
- 105, 45, 0, 104, 0, 0, 0, 100, 97, 102, 107, 119, 14,
- 31, 118, 0, 81, 0, 136, 116, 137, 61, 124, 132, 80, 121,
- 88, 30, 85, 0, 0, 99, 35, 125, 122, 0, 130, 0, 0,
- 41, 0, 91, 89, 90, 0, 20, 87, 113, 82,
+ 82, 113, 130, 80, 110, 29, 0, 0, 89, 0, 83, 70, 0,
+ 53, 35, 84, 15, 0, 129, 92, 64, 124, 131, 19, 0, 0,
+ 136, 0, 134, 126, 0, 22, 100, 0, 9, 0, 0, 121, 78,
+ 0, 76, 6, 0, 58, 97, 143, 0, 132, 108, 0, 0, 48,
+ 0, 111, 24, 0, 17, 0, 137, 63, 23, 26, 5, 65, 138,
+ 103, 120, 0, 142, 114, 69, 141, 66, 118, 72, 0, 98, 0,
+ 107, 41, 0, 106, 0, 0, 0, 102, 99, 104, 109, 123, 14,
+ 50, 122, 0, 87, 0, 139, 119, 140, 68, 127, 135, 86, 81,
+ 37, 91, 117, 0, 0, 101, 51, 128, 125, 0, 133, 0, 0,
+ 44, 0, 93, 67, 39, 0, 20, 45, 115, 88,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
-static const unsigned char aKWNext[140] = {
+static const unsigned char aKWNext[143] = {
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
- 0, 0, 0, 21, 0, 0, 12, 0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 51, 28, 0, 0, 38, 0, 0, 0, 44, 0, 0, 0, 3,
- 0, 0, 67, 1, 66, 0, 0, 0, 36, 0, 47, 0, 0,
- 0, 0, 0, 48, 50, 76, 0, 0, 42, 0, 60, 0, 0,
- 0, 43, 0, 16, 55, 10, 0, 0, 0, 0, 0, 0, 0,
- 11, 72, 93, 0, 0, 8, 0, 110, 0, 103, 40, 53, 70,
- 0, 114, 0, 74, 52, 0, 0, 92, 39, 46, 0, 68, 32,
- 84, 0, 34, 27, 25, 18, 94, 0, 64, 79,
+ 0, 0, 0, 21, 0, 0, 0, 0, 12, 0, 0, 0, 0,
+ 0, 0, 0, 7, 0, 36, 0, 0, 28, 0, 0, 0, 31,
+ 0, 0, 0, 40, 0, 0, 0, 0, 0, 60, 0, 54, 0,
+ 0, 38, 47, 0, 0, 0, 3, 0, 0, 74, 1, 73, 0,
+ 0, 0, 52, 0, 0, 0, 0, 0, 0, 57, 59, 56, 30,
+ 0, 0, 0, 46, 0, 16, 49, 10, 0, 0, 0, 0, 0,
+ 0, 0, 11, 79, 95, 0, 0, 8, 0, 112, 0, 105, 0,
+ 43, 62, 0, 77, 0, 116, 0, 61, 0, 0, 94, 42, 55,
+ 0, 75, 34, 90, 32, 33, 27, 25, 18, 96, 0, 71, 85,
};
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
-static const unsigned char aKWLen[140] = {
+static const unsigned char aKWLen[143] = {
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7,
- 6, 9, 4, 2, 10, 9, 4, 9, 4, 6, 2, 3, 11,
- 6, 2, 7, 5, 5, 6, 7, 10, 6, 5, 7, 4, 5,
- 7, 9, 6, 6, 6, 4, 5, 5, 5, 7, 7, 6, 5,
- 7, 3, 6, 4, 7, 6, 12, 9, 4, 6, 4, 5, 4,
- 7, 6, 5, 6, 6, 7, 5, 4, 7, 3, 2, 4, 5,
- 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5,
- 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9,
- 4, 4, 2, 6, 5, 8, 6, 4, 5, 8, 4, 3, 9,
- 5, 5, 6, 4, 6, 2, 2, 9, 3, 7,
+ 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4,
+ 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6,
+ 2, 3, 7, 10, 6, 5, 7, 4, 5, 7, 6, 6, 4,
+ 5, 5, 5, 7, 7, 6, 5, 7, 3, 6, 4, 7, 6,
+ 12, 9, 4, 6, 5, 4, 7, 6, 5, 6, 6, 7, 4,
+ 4, 5, 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6,
+ 8, 5, 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 5,
+ 6, 7, 5, 9, 4, 4, 2, 5, 8, 6, 4, 5, 8,
+ 4, 3, 9, 5, 5, 6, 4, 6, 2, 2, 9, 3, 7,
};
/* aKWOffset[i] is the index into zKWText[] of the start of
** the text for the i-th keyword. */
-static const unsigned short int aKWOffset[140] = {
+static const unsigned short int aKWOffset[143] = {
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
- 86, 90, 90, 94, 99, 106, 114, 117, 123, 126, 126, 129, 131,
- 136, 140, 141, 146, 150, 154, 159, 165, 175, 178, 183, 183, 187,
- 191, 197, 205, 211, 216, 221, 224, 227, 231, 236, 242, 248, 248,
- 254, 255, 259, 265, 269, 276, 282, 294, 303, 305, 311, 315, 320,
- 322, 329, 334, 339, 345, 351, 357, 362, 365, 365, 365, 368, 372,
- 375, 384, 388, 394, 396, 403, 405, 407, 416, 420, 426, 432, 440,
- 445, 445, 445, 461, 470, 477, 478, 485, 488, 497, 501, 506, 513,
- 522, 526, 530, 532, 538, 542, 550, 556, 559, 564, 572, 572, 576,
- 585, 590, 595, 601, 604, 607, 610, 612, 617, 621,
+ 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126,
+ 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184,
+ 184, 187, 189, 195, 205, 208, 213, 213, 217, 221, 228, 233, 238,
+ 241, 244, 248, 253, 259, 265, 265, 271, 272, 276, 282, 286, 293,
+ 299, 311, 320, 322, 328, 333, 335, 342, 347, 352, 358, 364, 370,
+ 374, 378, 381, 390, 394, 400, 402, 409, 411, 413, 422, 426, 432,
+ 438, 446, 451, 451, 451, 467, 476, 483, 484, 491, 494, 503, 506,
+ 511, 516, 523, 528, 537, 541, 545, 547, 551, 559, 565, 568, 573,
+ 581, 581, 585, 594, 599, 604, 610, 613, 616, 619, 621, 626, 630,
};
/* aKWCode[i] is the parser symbol code for the i-th keyword */
-static const unsigned char aKWCode[140] = {
+static const unsigned char aKWCode[143] = {
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR,
- TK_CONSTRAINT, TK_INTERSECT, TK_TIES, TK_SAVEPOINT, TK_INTO,
- TK_OFFSET, TK_OF, TK_SET, TK_TRANSACTION,TK_ACTION,
- TK_ON, TK_JOIN_KW, TK_ALTER, TK_RAISE, TK_EXCEPT,
- TK_TRIGGER, TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT,
- TK_WITH, TK_JOIN_KW, TK_RELEASE, TK_EXCLUSIVE, TK_EXISTS,
- TK_ATTACH, TK_HAVING, TK_LIKE_KW, TK_BEGIN, TK_JOIN_KW,
- TK_RANGE, TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP,
- TK_CASCADE, TK_ASC, TK_DETACH, TK_CASE, TK_COLLATE,
- TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT,
- TK_LIKE_KW, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA,
- TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL, TK_LIMIT,
- TK_WHEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL,
- TK_WHERE, TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND,
- TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST,
- TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW,
- TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DEFERRED, TK_DISTINCT,
- TK_IS, TK_DROP, TK_PRECEDING, TK_FAIL, TK_FILTER,
- TK_REPLACE, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF,
- TK_ISNULL, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER,
- TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED,
- TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW,
- TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY,
+ TK_ISNULL, TK_NULLS, TK_SAVEPOINT, TK_INTERSECT, TK_TIES,
+ TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, TK_LIKE_KW,
+ TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
+ TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_CONSTRAINT,
+ TK_INTO, TK_OFFSET, TK_OF, TK_SET, TK_TRIGGER,
+ TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
+ TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_LIKE_KW,
+ TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN, TK_NOTHING,
+ TK_GROUPS, TK_GROUP, TK_CASCADE, TK_ASC, TK_DETACH,
+ TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE,
+ TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE,
+ TK_PRAGMA, TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL,
+ TK_LAST, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_AFTER,
+ TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO,
+ TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT,
+ TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, TK_PARTITION,
+ TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP, TK_PRECEDING,
+ TK_FAIL, TK_LIMIT, TK_FILTER, TK_REPLACE, TK_FIRST,
+ TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF, TK_ORDER,
+ TK_RESTRICT, TK_OTHERS, TK_OVER, TK_JOIN_KW, TK_ROLLBACK,
+ TK_ROWS, TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING,
+ TK_VACUUM, TK_VIEW, TK_WINDOW, TK_DO, TK_BY,
+ TK_INITIALLY, TK_ALL, TK_PRIMARY,
};
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType. Always
@@ -153953,116 +154989,119 @@ static int keywordCode(const char *z, int n, int *pType){
testcase( i==27 ); /* TEMPORARY */
testcase( i==28 ); /* TEMP */
testcase( i==29 ); /* OR */
- testcase( i==30 ); /* CONSTRAINT */
- testcase( i==31 ); /* INTERSECT */
- testcase( i==32 ); /* TIES */
- testcase( i==33 ); /* SAVEPOINT */
- testcase( i==34 ); /* INTO */
- testcase( i==35 ); /* OFFSET */
- testcase( i==36 ); /* OF */
- testcase( i==37 ); /* SET */
- testcase( i==38 ); /* TRANSACTION */
- testcase( i==39 ); /* ACTION */
- testcase( i==40 ); /* ON */
- testcase( i==41 ); /* NATURAL */
- testcase( i==42 ); /* ALTER */
- testcase( i==43 ); /* RAISE */
- testcase( i==44 ); /* EXCEPT */
- testcase( i==45 ); /* TRIGGER */
- testcase( i==46 ); /* REFERENCES */
- testcase( i==47 ); /* UNIQUE */
- testcase( i==48 ); /* QUERY */
- testcase( i==49 ); /* WITHOUT */
- testcase( i==50 ); /* WITH */
- testcase( i==51 ); /* OUTER */
- testcase( i==52 ); /* RELEASE */
- testcase( i==53 ); /* EXCLUSIVE */
- testcase( i==54 ); /* EXISTS */
- testcase( i==55 ); /* ATTACH */
- testcase( i==56 ); /* HAVING */
- testcase( i==57 ); /* GLOB */
- testcase( i==58 ); /* BEGIN */
- testcase( i==59 ); /* INNER */
- testcase( i==60 ); /* RANGE */
- testcase( i==61 ); /* BETWEEN */
- testcase( i==62 ); /* NOTHING */
- testcase( i==63 ); /* GROUPS */
- testcase( i==64 ); /* GROUP */
- testcase( i==65 ); /* CASCADE */
- testcase( i==66 ); /* ASC */
- testcase( i==67 ); /* DETACH */
- testcase( i==68 ); /* CASE */
- testcase( i==69 ); /* COLLATE */
- testcase( i==70 ); /* CREATE */
- testcase( i==71 ); /* CURRENT_DATE */
- testcase( i==72 ); /* IMMEDIATE */
- testcase( i==73 ); /* JOIN */
- testcase( i==74 ); /* INSERT */
- testcase( i==75 ); /* LIKE */
- testcase( i==76 ); /* MATCH */
- testcase( i==77 ); /* PLAN */
- testcase( i==78 ); /* ANALYZE */
- testcase( i==79 ); /* PRAGMA */
- testcase( i==80 ); /* ABORT */
- testcase( i==81 ); /* UPDATE */
- testcase( i==82 ); /* VALUES */
- testcase( i==83 ); /* VIRTUAL */
- testcase( i==84 ); /* LIMIT */
- testcase( i==85 ); /* WHEN */
- testcase( i==86 ); /* NOTNULL */
- testcase( i==87 ); /* NOT */
- testcase( i==88 ); /* NO */
- testcase( i==89 ); /* NULL */
- testcase( i==90 ); /* WHERE */
- testcase( i==91 ); /* RECURSIVE */
- testcase( i==92 ); /* AFTER */
- testcase( i==93 ); /* RENAME */
- testcase( i==94 ); /* AND */
- testcase( i==95 ); /* DEFAULT */
- testcase( i==96 ); /* AUTOINCREMENT */
- testcase( i==97 ); /* TO */
- testcase( i==98 ); /* IN */
- testcase( i==99 ); /* CAST */
- testcase( i==100 ); /* COLUMN */
- testcase( i==101 ); /* COMMIT */
- testcase( i==102 ); /* CONFLICT */
- testcase( i==103 ); /* CROSS */
- testcase( i==104 ); /* CURRENT_TIMESTAMP */
- testcase( i==105 ); /* CURRENT_TIME */
- testcase( i==106 ); /* CURRENT */
- testcase( i==107 ); /* PARTITION */
- testcase( i==108 ); /* DEFERRED */
- testcase( i==109 ); /* DISTINCT */
- testcase( i==110 ); /* IS */
- testcase( i==111 ); /* DROP */
- testcase( i==112 ); /* PRECEDING */
- testcase( i==113 ); /* FAIL */
- testcase( i==114 ); /* FILTER */
- testcase( i==115 ); /* REPLACE */
- testcase( i==116 ); /* FOLLOWING */
- testcase( i==117 ); /* FROM */
- testcase( i==118 ); /* FULL */
- testcase( i==119 ); /* IF */
- testcase( i==120 ); /* ISNULL */
- testcase( i==121 ); /* ORDER */
- testcase( i==122 ); /* RESTRICT */
- testcase( i==123 ); /* OTHERS */
- testcase( i==124 ); /* OVER */
- testcase( i==125 ); /* RIGHT */
- testcase( i==126 ); /* ROLLBACK */
- testcase( i==127 ); /* ROWS */
- testcase( i==128 ); /* ROW */
- testcase( i==129 ); /* UNBOUNDED */
- testcase( i==130 ); /* UNION */
- testcase( i==131 ); /* USING */
- testcase( i==132 ); /* VACUUM */
- testcase( i==133 ); /* VIEW */
- testcase( i==134 ); /* WINDOW */
- testcase( i==135 ); /* DO */
- testcase( i==136 ); /* BY */
- testcase( i==137 ); /* INITIALLY */
- testcase( i==138 ); /* ALL */
- testcase( i==139 ); /* PRIMARY */
+ testcase( i==30 ); /* ISNULL */
+ testcase( i==31 ); /* NULLS */
+ testcase( i==32 ); /* SAVEPOINT */
+ testcase( i==33 ); /* INTERSECT */
+ testcase( i==34 ); /* TIES */
+ testcase( i==35 ); /* NOTNULL */
+ testcase( i==36 ); /* NOT */
+ testcase( i==37 ); /* NO */
+ testcase( i==38 ); /* NULL */
+ testcase( i==39 ); /* LIKE */
+ testcase( i==40 ); /* EXCEPT */
+ testcase( i==41 ); /* TRANSACTION */
+ testcase( i==42 ); /* ACTION */
+ testcase( i==43 ); /* ON */
+ testcase( i==44 ); /* NATURAL */
+ testcase( i==45 ); /* ALTER */
+ testcase( i==46 ); /* RAISE */
+ testcase( i==47 ); /* EXCLUSIVE */
+ testcase( i==48 ); /* EXISTS */
+ testcase( i==49 ); /* CONSTRAINT */
+ testcase( i==50 ); /* INTO */
+ testcase( i==51 ); /* OFFSET */
+ testcase( i==52 ); /* OF */
+ testcase( i==53 ); /* SET */
+ testcase( i==54 ); /* TRIGGER */
+ testcase( i==55 ); /* REFERENCES */
+ testcase( i==56 ); /* UNIQUE */
+ testcase( i==57 ); /* QUERY */
+ testcase( i==58 ); /* WITHOUT */
+ testcase( i==59 ); /* WITH */
+ testcase( i==60 ); /* OUTER */
+ testcase( i==61 ); /* RELEASE */
+ testcase( i==62 ); /* ATTACH */
+ testcase( i==63 ); /* HAVING */
+ testcase( i==64 ); /* GLOB */
+ testcase( i==65 ); /* BEGIN */
+ testcase( i==66 ); /* INNER */
+ testcase( i==67 ); /* RANGE */
+ testcase( i==68 ); /* BETWEEN */
+ testcase( i==69 ); /* NOTHING */
+ testcase( i==70 ); /* GROUPS */
+ testcase( i==71 ); /* GROUP */
+ testcase( i==72 ); /* CASCADE */
+ testcase( i==73 ); /* ASC */
+ testcase( i==74 ); /* DETACH */
+ testcase( i==75 ); /* CASE */
+ testcase( i==76 ); /* COLLATE */
+ testcase( i==77 ); /* CREATE */
+ testcase( i==78 ); /* CURRENT_DATE */
+ testcase( i==79 ); /* IMMEDIATE */
+ testcase( i==80 ); /* JOIN */
+ testcase( i==81 ); /* INSERT */
+ testcase( i==82 ); /* MATCH */
+ testcase( i==83 ); /* PLAN */
+ testcase( i==84 ); /* ANALYZE */
+ testcase( i==85 ); /* PRAGMA */
+ testcase( i==86 ); /* ABORT */
+ testcase( i==87 ); /* UPDATE */
+ testcase( i==88 ); /* VALUES */
+ testcase( i==89 ); /* VIRTUAL */
+ testcase( i==90 ); /* LAST */
+ testcase( i==91 ); /* WHEN */
+ testcase( i==92 ); /* WHERE */
+ testcase( i==93 ); /* RECURSIVE */
+ testcase( i==94 ); /* AFTER */
+ testcase( i==95 ); /* RENAME */
+ testcase( i==96 ); /* AND */
+ testcase( i==97 ); /* DEFAULT */
+ testcase( i==98 ); /* AUTOINCREMENT */
+ testcase( i==99 ); /* TO */
+ testcase( i==100 ); /* IN */
+ testcase( i==101 ); /* CAST */
+ testcase( i==102 ); /* COLUMN */
+ testcase( i==103 ); /* COMMIT */
+ testcase( i==104 ); /* CONFLICT */
+ testcase( i==105 ); /* CROSS */
+ testcase( i==106 ); /* CURRENT_TIMESTAMP */
+ testcase( i==107 ); /* CURRENT_TIME */
+ testcase( i==108 ); /* CURRENT */
+ testcase( i==109 ); /* PARTITION */
+ testcase( i==110 ); /* DEFERRED */
+ testcase( i==111 ); /* DISTINCT */
+ testcase( i==112 ); /* IS */
+ testcase( i==113 ); /* DROP */
+ testcase( i==114 ); /* PRECEDING */
+ testcase( i==115 ); /* FAIL */
+ testcase( i==116 ); /* LIMIT */
+ testcase( i==117 ); /* FILTER */
+ testcase( i==118 ); /* REPLACE */
+ testcase( i==119 ); /* FIRST */
+ testcase( i==120 ); /* FOLLOWING */
+ testcase( i==121 ); /* FROM */
+ testcase( i==122 ); /* FULL */
+ testcase( i==123 ); /* IF */
+ testcase( i==124 ); /* ORDER */
+ testcase( i==125 ); /* RESTRICT */
+ testcase( i==126 ); /* OTHERS */
+ testcase( i==127 ); /* OVER */
+ testcase( i==128 ); /* RIGHT */
+ testcase( i==129 ); /* ROLLBACK */
+ testcase( i==130 ); /* ROWS */
+ testcase( i==131 ); /* ROW */
+ testcase( i==132 ); /* UNBOUNDED */
+ testcase( i==133 ); /* UNION */
+ testcase( i==134 ); /* USING */
+ testcase( i==135 ); /* VACUUM */
+ testcase( i==136 ); /* VIEW */
+ testcase( i==137 ); /* WINDOW */
+ testcase( i==138 ); /* DO */
+ testcase( i==139 ); /* BY */
+ testcase( i==140 ); /* INITIALLY */
+ testcase( i==141 ); /* ALL */
+ testcase( i==142 ); /* PRIMARY */
*pType = aKWCode[i];
break;
}
@@ -154074,7 +155113,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
keywordCode((char*)z, n, &id);
return id;
}
-#define SQLITE_N_KEYWORD 140
+#define SQLITE_N_KEYWORD 143
SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
*pzName = zKWText + aKWOffset[i];
@@ -156035,6 +157074,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
} aFlagOp[] = {
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_VIEW, SQLITE_EnableView },
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
@@ -156434,11 +157474,8 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){
Module *pMod = (Module *)sqliteHashData(i);
- if( pMod->xDestroy ){
- pMod->xDestroy(pMod->pAux);
- }
sqlite3VtabEponymousTableClear(db, pMod);
- sqlite3DbFree(db, pMod);
+ sqlite3VtabModuleUnref(db, pMod);
}
sqlite3HashClear(&db->aModule);
#endif
@@ -156919,7 +157956,8 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
}
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
- extraFlags = enc & SQLITE_DETERMINISTIC;
+ assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
+ extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|SQLITE_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
#ifndef SQLITE_OMIT_UTF16
@@ -156982,6 +158020,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
p->u.pDestructor = pDestructor;
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
+ testcase( p->funcFlags & SQLITE_DIRECTONLY );
p->xSFunc = xSFunc ? xSFunc : xStep;
p->xFinalize = xFinal;
p->xValue = xValue;
@@ -158274,6 +159313,7 @@ static int openDatabase(
db->nMaxSorterMmap = 0x7FFFFFFF;
db->flags |= SQLITE_ShortColNames
| SQLITE_EnableTrigger
+ | SQLITE_EnableView
| SQLITE_CacheSpill
/* The SQLITE_DQS compile-time option determines the default settings
@@ -159023,12 +160063,33 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
- /*
- ** Reset the PRNG back to its uninitialized state. The next call
- ** to sqlite3_randomness() will reseed the PRNG using a single call
- ** to the xRandomness method of the default VFS.
+ /* sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, int x, sqlite3 *db);
+ **
+ ** Control the seed for the pseudo-random number generator (PRNG) that
+ ** is built into SQLite. Cases:
+ **
+ ** x!=0 && db!=0 Seed the PRNG to the current value of the
+ ** schema cookie in the main database for db, or
+ ** x if the schema cookie is zero. This case
+ ** is convenient to use with database fuzzers
+ ** as it allows the fuzzer some control over the
+ ** the PRNG seed.
+ **
+ ** x!=0 && db==0 Seed the PRNG to the value of x.
+ **
+ ** x==0 && db==0 Revert to default behavior of using the
+ ** xRandomness method on the primary VFS.
+ **
+ ** This test-control also resets the PRNG so that the new seed will
+ ** be used for the next call to sqlite3_randomness().
*/
- case SQLITE_TESTCTRL_PRNG_RESET: {
+ case SQLITE_TESTCTRL_PRNG_SEED: {
+ int x = va_arg(ap, int);
+ int y;
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ assert( db==0 || db->aDb[0].pSchema!=0 );
+ if( db && (y = db->aDb[0].pSchema->schema_cookie)!=0 ){ x = y; }
+ sqlite3Config.iPrngSeed = x;
sqlite3_randomness(0,0);
break;
}
@@ -159241,6 +160302,17 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int);
+ **
+ ** Set or clear a flag that causes SQLite to verify that type, name,
+ ** and tbl_name fields of the sqlite_master table. This is normally
+ ** on, but it is sometimes useful to turn it off for testing.
+ */
+ case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
+ sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int);
+ break;
+ }
+
/* Set the threshold at which OP_Once counters reset back to zero.
** By default this is 0x7ffffffe (over 2 billion), but that value is
** too big to test in a reasonable amount of time, so this control is
@@ -161187,6 +162259,18 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
SQLITE_EXTENSION_INIT1
#endif
+/*
+** The following are copied from sqliteInt.h.
+**
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#ifndef SQLITE_AMALGAMATION
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
@@ -162965,10 +164049,11 @@ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
}
/*
-** Value used to signify the end of an position-list. This is safe because
-** it is not possible to have a document with 2^31 terms.
+** Value used to signify the end of an position-list. This must be
+** as large or larger than any value that might appear on the
+** position-list, even a position list that has been corrupted.
*/
-#define POSITION_LIST_END 0x7fffffff
+#define POSITION_LIST_END LARGEST_INT64
/*
** This function is used to help parse position-lists. When this function is
@@ -163044,14 +164129,14 @@ static int fts3PoslistMerge(
fts3GetVarint32(&p1[1], &iCol1);
if( iCol1==0 ) return FTS_CORRUPT_VTAB;
}
- else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
+ else if( *p1==POS_END ) iCol1 = 0x7fffffff;
else iCol1 = 0;
if( *p2==POS_COLUMN ){
fts3GetVarint32(&p2[1], &iCol2);
if( iCol2==0 ) return FTS_CORRUPT_VTAB;
}
- else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
+ else if( *p2==POS_END ) iCol2 = 0x7fffffff;
else iCol2 = 0;
if( iCol1==iCol2 ){
@@ -163353,7 +164438,8 @@ static void fts3PutDeltaVarint3(
iWrite = *piPrev - iVal;
}
assert( *pbFirst || *piPrev==0 );
- assert( *pbFirst==0 || iWrite>0 );
+ assert_fts3_nc( *pbFirst==0 || iWrite>0 );
+ assert( *pbFirst==0 || iWrite>=0 );
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
*piPrev = iVal;
*pbFirst = 1;
@@ -163459,6 +164545,8 @@ static int fts3DoclistOrMerge(
fts3PoslistCopy(&p, &p2);
fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}
+
+ assert( (p-aOut)<=((p1?(p1-a1):n1)+(p2?(p2-a2):n2)+FTS3_VARINT_MAX-1) );
}
if( rc!=SQLITE_OK ){
@@ -164059,18 +165147,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
-** The following are copied from sqliteInt.h.
-**
-** Constants for the largest and smallest possible 64-bit signed integers.
-** These macros are designed to work correctly on both 32-bit and 64-bit
-** compilers.
-*/
-#ifndef SQLITE_AMALGAMATION
-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
-#endif
-
-/*
** If the numeric type of argument pVal is "integer", then return it
** converted to a 64-bit signed integer. Otherwise, return a copy of
** the second parameter, iDefault.
@@ -174799,14 +175875,14 @@ static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){
p->nNode = nNode;
/* Figure out if this is a leaf or an internal node. */
- if( p->aNode[0] ){
+ if( aNode && aNode[0] ){
/* An internal node. */
p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild);
}else{
p->iOff = 1;
}
- return nodeReaderNext(p);
+ return aNode ? nodeReaderNext(p) : SQLITE_OK;
}
/*
@@ -174943,6 +176019,7 @@ static int fts3AppendToNode(
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
+ if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
memcpy(pPrev->a, zTerm, nTerm);
pPrev->n = nTerm;
@@ -175297,8 +176374,8 @@ static int fts3IncrmergeLoad(
NodeReader reader;
pNode = &pWriter->aNodeWriter[i];
- rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
- if( reader.aNode ){
+ if( pNode->block.a){
+ rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
if( rc==SQLITE_OK ){
@@ -177163,10 +178240,10 @@ static void fts3SnippetDetails(
while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){
int j;
- u64 mPhrase = (u64)1 << i;
+ u64 mPhrase = (u64)1 << (i%64);
u64 mPos = (u64)1 << (iCsr - iStart);
assert( iCsr>=iStart && (iCsr - iStart)<=64 );
- assert( i>=0 && i<=64 );
+ assert( i>=0 );
if( (mCover|mCovered)&mPhrase ){
iScore++;
}else{
@@ -180327,6 +181404,7 @@ static JsonNode *jsonLookupStep(
const char *zKey;
JsonNode *pRoot = &pParse->aNode[iRoot];
if( zPath[0]==0 ) return pRoot;
+ if( pRoot->jnFlags & JNODE_REPLACE ) return 0;
if( zPath[0]=='.' ){
if( pRoot->eType!=JSON_OBJECT ) return 0;
zPath++;
@@ -181063,7 +182141,7 @@ static void jsonArrayStep(
if( pStr->zBuf==0 ){
jsonInit(pStr, ctx);
jsonAppendChar(pStr, '[');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
pStr->pCtx = ctx;
}
@@ -181111,9 +182189,11 @@ static void jsonGroupInverse(
int argc,
sqlite3_value **argv
){
- int i;
+ unsigned int i;
int inStr = 0;
+ int nNest = 0;
char *z;
+ char c;
JsonString *pStr;
UNUSED_PARAM(argc);
UNUSED_PARAM(argv);
@@ -181124,12 +182204,18 @@ static void jsonGroupInverse(
if( NEVER(!pStr) ) return;
#endif
z = pStr->zBuf;
- for(i=1; z[i]!=',' || inStr; i++){
- assert( i<pStr->nUsed );
- if( z[i]=='"' ){
+ for(i=1; (c = z[i])!=',' || inStr || nNest; i++){
+ if( i>=pStr->nUsed ){
+ pStr->nUsed = 1;
+ return;
+ }
+ if( c=='"' ){
inStr = !inStr;
- }else if( z[i]=='\\' ){
+ }else if( c=='\\' ){
i++;
+ }else if( !inStr ){
+ if( c=='{' || c=='[' ) nNest++;
+ if( c=='}' || c==']' ) nNest--;
}
}
pStr->nUsed -= i;
@@ -181159,7 +182245,7 @@ static void jsonObjectStep(
if( pStr->zBuf==0 ){
jsonInit(pStr, ctx);
jsonAppendChar(pStr, '{');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
pStr->pCtx = ctx;
}
@@ -181747,14 +182833,14 @@ SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
#endif
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC,
(void*)&aFunc[i].flag,
aFunc[i].xFunc, 0, 0);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
+ SQLITE_SUBTYPE | SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
aAgg[i].xStep, aAgg[i].xFinal,
aAgg[i].xValue, jsonGroupInverse, 0);
}
@@ -182457,7 +183543,6 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
- assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
if( pParent && !pNode->pParent ){
if( nodeInParentChain(pNode, pParent) ){
RTREE_IS_CORRUPT(pRtree);
@@ -182465,6 +183550,9 @@ static int nodeAcquire(
}
pParent->nRef++;
pNode->pParent = pParent;
+ }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
}
pNode->nRef++;
*ppNode = pNode;
@@ -183352,13 +184440,14 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
eInt = pRtree->eCoordType==RTREE_COORD_INT32;
while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+ u8 *pCellData;
pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
if( rc ) return rc;
nCell = NCELL(pNode);
assert( nCell<200 );
+ pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
while( p->iCell<nCell ){
sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
- u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
eWithin = FULLY_WITHIN;
for(ii=0; ii<nConstraint; ii++){
RtreeConstraint *pConstraint = pCur->aConstraint + ii;
@@ -183371,13 +184460,23 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
}else{
rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
}
- if( eWithin==NOT_WITHIN ) break;
+ if( eWithin==NOT_WITHIN ){
+ p->iCell++;
+ pCellData += pRtree->nBytesPerCell;
+ break;
+ }
}
- p->iCell++;
if( eWithin==NOT_WITHIN ) continue;
+ p->iCell++;
x.iLevel = p->iLevel - 1;
if( x.iLevel ){
x.id = readInt64(pCellData);
+ for(ii=0; ii<pCur->nPoint; ii++){
+ if( pCur->aPoint[ii].id==x.id ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
+ }
+ }
x.iCell = 0;
}else{
x.id = p->id;
@@ -189685,6 +190784,7 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
typedef struct RbuFrame RbuFrame;
typedef struct RbuObjIter RbuObjIter;
typedef struct RbuState RbuState;
+typedef struct RbuSpan RbuSpan;
typedef struct rbu_vfs rbu_vfs;
typedef struct rbu_file rbu_file;
typedef struct RbuUpdateStmt RbuUpdateStmt;
@@ -189729,6 +190829,11 @@ struct RbuUpdateStmt {
RbuUpdateStmt *pNext;
};
+struct RbuSpan {
+ const char *zSpan;
+ int nSpan;
+};
+
/*
** An iterator of this type is used to iterate through all objects in
** the target database that require updating. For each such table, the
@@ -189778,6 +190883,9 @@ struct RbuObjIter {
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
sqlite3_stmt *pDelete; /* Statement for DELETE ops */
sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
+ int nIdxCol;
+ RbuSpan *aIdxCol;
+ char *zIdxSql;
/* Last UPDATE used (for PK b-tree updates only), or NULL. */
RbuUpdateStmt *pRbuUpdate;
@@ -190312,6 +191420,8 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
sqlite3_free(pUp);
pUp = pTmp;
}
+ sqlite3_free(pIter->aIdxCol);
+ sqlite3_free(pIter->zIdxSql);
pIter->pSelect = 0;
pIter->pInsert = 0;
@@ -190319,6 +191429,9 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
pIter->pRbuUpdate = 0;
pIter->pTmpInsert = 0;
pIter->nCol = 0;
+ pIter->nIdxCol = 0;
+ pIter->aIdxCol = 0;
+ pIter->zIdxSql = 0;
}
/*
@@ -190433,8 +191546,8 @@ static void rbuTargetNameFunc(
zIn = (const char*)sqlite3_value_text(argv[0]);
if( zIn ){
if( rbuIsVacuum(p) ){
- assert( argc==2 );
- if( 0==sqlite3_value_int(argv[1]) ){
+ assert( argc==2 || argc==1 );
+ if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
}
}else{
@@ -190592,14 +191705,15 @@ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
static char *rbuStrndup(const char *zStr, int *pRc){
char *zRet = 0;
- assert( *pRc==SQLITE_OK );
- if( zStr ){
- size_t nCopy = strlen(zStr) + 1;
- zRet = (char*)sqlite3_malloc64(nCopy);
- if( zRet ){
- memcpy(zRet, zStr, nCopy);
- }else{
- *pRc = SQLITE_NOMEM;
+ if( *pRc==SQLITE_OK ){
+ if( zStr ){
+ size_t nCopy = strlen(zStr) + 1;
+ zRet = (char*)sqlite3_malloc64(nCopy);
+ if( zRet ){
+ memcpy(zRet, zStr, nCopy);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
}
}
@@ -190771,6 +191885,9 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
int iCid = sqlite3_column_int(pXInfo, 1);
if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
+ if( iCid==-2 ){
+ memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
+ }
}
rbuFinalize(p, pXInfo);
bIndex = 1;
@@ -191182,29 +192299,37 @@ static char *rbuObjIterGetIndexCols(
int iCid = sqlite3_column_int(pXInfo, 1);
int bDesc = sqlite3_column_int(pXInfo, 3);
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
- const char *zCol;
+ const char *zCol = 0;
const char *zType;
- if( iCid<0 ){
- /* An integer primary key. If the table has an explicit IPK, use
- ** its name. Otherwise, use "rbu_rowid". */
- if( pIter->eType==RBU_PK_IPK ){
- int i;
- for(i=0; pIter->abTblPk[i]==0; i++);
- assert( i<pIter->nTblCol );
- zCol = pIter->azTblCol[i];
- }else if( rbuIsVacuum(p) ){
- zCol = "_rowid_";
+ if( iCid==-2 ){
+ int iSeq = sqlite3_column_int(pXInfo, 0);
+ zRet = sqlite3_mprintf("%z%s(%.*s) COLLATE %Q", zRet, zCom,
+ pIter->aIdxCol[iSeq].nSpan, pIter->aIdxCol[iSeq].zSpan, zCollate
+ );
+ zType = "";
+ }else {
+ if( iCid<0 ){
+ /* An integer primary key. If the table has an explicit IPK, use
+ ** its name. Otherwise, use "rbu_rowid". */
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else if( rbuIsVacuum(p) ){
+ zCol = "_rowid_";
+ }else{
+ zCol = "rbu_rowid";
+ }
+ zType = "INTEGER";
}else{
- zCol = "rbu_rowid";
+ zCol = pIter->azTblCol[iCid];
+ zType = pIter->azTblType[iCid];
}
- zType = "INTEGER";
- }else{
- zCol = pIter->azTblCol[iCid];
- zType = pIter->azTblType[iCid];
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom,zCol,zCollate);
}
- zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
const char *zOrder = (bDesc ? " DESC" : "");
zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
@@ -191684,6 +192809,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
int rc = p->rc;
char *zRet = 0;
+ assert( pIter->zIdxSql==0 && pIter->nIdxCol==0 && pIter->aIdxCol==0 );
+
if( rc==SQLITE_OK ){
rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
"SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
@@ -191693,21 +192820,50 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
int rc2;
rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC);
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
+ char *zSql = (char*)sqlite3_column_text(pStmt, 0);
+ if( zSql ){
+ pIter->zIdxSql = zSql = rbuStrndup(zSql, &rc);
+ }
if( zSql ){
int nParen = 0; /* Number of open parenthesis */
int i;
+ int iIdxCol = 0;
+ int nIdxAlloc = 0;
for(i=0; zSql[i]; i++){
char c = zSql[i];
+
+ /* If necessary, grow the pIter->aIdxCol[] array */
+ if( iIdxCol==nIdxAlloc ){
+ RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
+ pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
+ );
+ if( aIdxCol==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ pIter->aIdxCol = aIdxCol;
+ nIdxAlloc += 16;
+ }
+
if( c=='(' ){
+ if( nParen==0 ){
+ assert( iIdxCol==0 );
+ pIter->aIdxCol[0].zSpan = &zSql[i+1];
+ }
nParen++;
}
else if( c==')' ){
nParen--;
if( nParen==0 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
i++;
break;
}
+ }else if( c==',' && nParen==1 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
+ pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
}else if( c=='"' || c=='\'' || c=='`' ){
for(i++; 1; i++){
if( zSql[i]==c ){
@@ -191719,11 +192875,19 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
for(i++; 1; i++){
if( zSql[i]==']' ) break;
}
+ }else if( c=='-' && zSql[i+1]=='-' ){
+ for(i=i+2; zSql[i] && zSql[i]!='\n'; i++);
+ if( zSql[i]=='\0' ) break;
+ }else if( c=='/' && zSql[i+1]=='*' ){
+ for(i=i+2; zSql[i] && (zSql[i]!='*' || zSql[i+1]!='/'); i++);
+ if( zSql[i]=='\0' ) break;
+ i++;
}
}
if( zSql[i] ){
zRet = rbuStrndup(&zSql[i], &rc);
}
+ pIter->nIdxCol = iIdxCol;
}
}
@@ -191768,11 +192932,11 @@ static int rbuObjIterPrepareAll(
int nBind = 0;
assert( pIter->eType!=RBU_PK_VTAB );
+ zPart = rbuObjIterGetIndexWhere(p, pIter);
zCollist = rbuObjIterGetIndexCols(
p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
);
zBind = rbuObjIterGetBindlist(p, nBind);
- zPart = rbuObjIterGetIndexWhere(p, pIter);
/* Create the imposter table used to write to this index. */
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
@@ -193298,10 +194462,11 @@ static void rbuIndexCntFunc(
sqlite3_stmt *pStmt = 0;
char *zErrmsg = 0;
int rc;
+ sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
assert( nVal==1 );
- rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
+ rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
);
@@ -193316,7 +194481,7 @@ static void rbuIndexCntFunc(
if( rc==SQLITE_OK ){
sqlite3_result_int(pCtx, nIndex);
}else{
- sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
+ sqlite3_result_error(pCtx, sqlite3_errmsg(db), -1);
}
}
@@ -197759,7 +198924,7 @@ static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){
i64 nNew = p->nAlloc ? p->nAlloc : 128;
do {
nNew = nNew*2;
- }while( (nNew-p->nBuf)<nByte );
+ }while( (size_t)(nNew-p->nBuf)<nByte );
aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
if( 0==aNew ){
@@ -202160,6 +203325,7 @@ struct Fts5Config {
char *zContentExprlist;
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
+ int bLock; /* True when table is preparing statement */
/* Values loaded from the %_config table */
int iCookie; /* Incremented when %_config is modified */
@@ -202676,6 +203842,7 @@ static int sqlite3Fts5ExprEof(Fts5Expr*);
static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
static void sqlite3Fts5ExprFree(Fts5Expr*);
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
/* Called during startup to register a UDF with SQLite */
static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
@@ -203527,15 +204694,18 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
do{
i = fts5yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD ); */
+ assert( i<=fts5YY_ACTTAB_COUNT );
+ assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD );
assert( iLookAhead!=fts5YYNOCODE );
assert( iLookAhead < fts5YYNFTS5TOKEN );
i += iLookAhead;
- if( i>=fts5YY_NLOOKAHEAD || fts5yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)fts5YY_NLOOKAHEAD );
+ if( fts5yy_lookahead[i]!=iLookAhead ){
#ifdef fts5YYFALLBACK
fts5YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
- && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0]) );
+ iFallback = fts5yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -203550,16 +204720,8 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#ifdef fts5YYWILDCARD
{
int j = i - iLookAhead + fts5YYWILDCARD;
- if(
-#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
- j>=0 &&
-#endif
-#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
- j<fts5YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) &&
- fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) );
+ if( fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -203573,6 +204735,7 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#endif /* fts5YYWILDCARD */
return fts5yy_default[stateno];
}else{
+ assert( i>=0 && i<sizeof(fts5yy_action)/sizeof(fts5yy_action[0]) );
return fts5yy_action[i];
}
}while(1);
@@ -204273,9 +205436,8 @@ static void sqlite3Fts5Parser(
*/
static int sqlite3Fts5ParserFallback(int iToken){
#ifdef fts5YYFALLBACK
- if( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) ){
- return fts5yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) );
+ return fts5yyFallback[iToken];
#else
(void)iToken;
#endif
@@ -206088,7 +207250,7 @@ static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
rc = sqlite3_declare_vtab(pConfig->db, zSql);
sqlite3_free(zSql);
}
-
+
return rc;
}
@@ -206676,6 +207838,42 @@ static void sqlite3Fts5ExprFree(Fts5Expr *p){
}
}
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
+ Fts5Parse sParse;
+ memset(&sParse, 0, sizeof(sParse));
+
+ if( *pp1 ){
+ Fts5Expr *p1 = *pp1;
+ int nPhrase = p1->nPhrase + p2->nPhrase;
+
+ p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
+ p2->pRoot = 0;
+
+ if( sParse.rc==SQLITE_OK ){
+ Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
+ p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
+ );
+ if( ap==0 ){
+ sParse.rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*));
+ for(i=0; i<p2->nPhrase; i++){
+ ap[i] = p2->apExprPhrase[i];
+ }
+ p1->nPhrase = nPhrase;
+ p1->apExprPhrase = ap;
+ }
+ }
+ sqlite3_free(p2->apExprPhrase);
+ sqlite3_free(p2);
+ }else{
+ *pp1 = p2;
+ }
+
+ return sParse.rc;
+}
+
/*
** Argument pTerm must be a synonym iterator. Return the current rowid
** that it points to.
@@ -210472,6 +211670,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
}else{
/* TODO1: Fix this */
pRet->p[nByte] = 0x00;
+ pRet->p[nByte+1] = 0x00;
pRet->szLeaf = fts5GetU16(&pRet->p[2]);
}
}
@@ -210494,7 +211693,7 @@ static void fts5DataRelease(Fts5Data *pData){
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
Fts5Data *pRet = fts5DataRead(p, iRowid);
if( pRet ){
- if( pRet->szLeaf>pRet->nn ){
+ if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
p->rc = FTS5_CORRUPT;
fts5DataRelease(pRet);
pRet = 0;
@@ -214778,9 +215977,12 @@ static void fts5MergePrefixLists(
Fts5PoslistWriter writer;
memset(&writer, 0, sizeof(writer));
+ /* See the earlier comment in this function for an explanation of why
+ ** corrupt input position lists might cause the output to consume
+ ** at most 20 bytes of unexpected space. */
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
fts5BufferZero(&tmp);
- sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
+ sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10);
if( p->rc ) break;
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
@@ -214828,6 +216030,12 @@ static void fts5MergePrefixLists(
}
/* WRITEPOSLISTSIZE */
+ assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
+ assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
+ if( tmp.n>i1.nPoslist+i2.nPoslist ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ break;
+ }
fts5BufferSafeAppendVarint(&out, tmp.n * 2);
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
fts5DoclistIterNext(&i1);
@@ -216829,17 +218037,39 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
** Implementation of the xBestIndex method for FTS5 tables. Within the
** WHERE constraint, it searches for the following:
**
-** 1. A MATCH constraint against the special column.
+** 1. A MATCH constraint against the table column.
** 2. A MATCH constraint against the "rank" column.
-** 3. An == constraint against the rowid column.
-** 4. A < or <= constraint against the rowid column.
-** 5. A > or >= constraint against the rowid column.
+** 3. A MATCH constraint against some other column.
+** 4. An == constraint against the rowid column.
+** 5. A < or <= constraint against the rowid column.
+** 6. A > or >= constraint against the rowid column.
**
-** Within the ORDER BY, either:
+** Within the ORDER BY, the following are supported:
**
** 5. ORDER BY rank [ASC|DESC]
** 6. ORDER BY rowid [ASC|DESC]
**
+** Information for the xFilter call is passed via both the idxNum and
+** idxStr variables. Specifically, idxNum is a bitmask of the following
+** flags used to encode the ORDER BY clause:
+**
+** FTS5_BI_ORDER_RANK
+** FTS5_BI_ORDER_ROWID
+** FTS5_BI_ORDER_DESC
+**
+** idxStr is used to encode data from the WHERE clause. For each argument
+** passed to the xFilter method, the following is appended to idxStr:
+**
+** Match against table column: "m"
+** Match against rank column: "r"
+** Match against other column: "<column-number>"
+** Equality constraint against the rowid: "="
+** A < or <= against the rowid: "<"
+** A > or >= against the rowid: ">"
+**
+** This function ensures that there is at most one "r" or "=". And that if
+** there exists an "=" then there is no "<" or ">".
+**
** Costs are assigned as follows:
**
** a) If an unusable MATCH operator is present in the WHERE clause, the
@@ -216867,32 +218097,18 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Config *pConfig = pTab->pConfig;
const int nCol = pConfig->nCol;
int idxFlags = 0; /* Parameter passed through to xFilter() */
- int bHasMatch;
- int iNext;
int i;
- struct Constraint {
- int op; /* Mask against sqlite3_index_constraint.op */
- int fts5op; /* FTS5 mask for idxFlags */
- int iCol; /* 0==rowid, 1==tbl, 2==rank */
- int omit; /* True to omit this if found */
- int iConsIndex; /* Index in pInfo->aConstraint[] */
- } aConstraint[] = {
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_MATCH, 1, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_RANK, 2, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
- FTS5_BI_ROWID_LE, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
- FTS5_BI_ROWID_GE, 0, 0, -1},
- };
+ char *idxStr;
+ int iIdxStr = 0;
+ int iCons = 0;
+
+ int bSeenEq = 0;
+ int bSeenGt = 0;
+ int bSeenLt = 0;
+ int bSeenMatch = 0;
+ int bSeenRank = 0;
- int aColMap[3];
- aColMap[0] = -1;
- aColMap[1] = nCol;
- aColMap[2] = nCol+1;
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
@@ -216900,40 +218116,85 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
- /* Set idxFlags flags for all WHERE clause terms that will be used. */
+ if( pConfig->bLock ){
+ pTab->base.zErrMsg = sqlite3_mprintf(
+ "recursively defined fts5 content table"
+ );
+ return SQLITE_ERROR;
+ }
+
+ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
+ if( idxStr==0 ) return SQLITE_NOMEM;
+ pInfo->idxStr = idxStr;
+ pInfo->needToFreeIdxStr = 1;
+
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
int iCol = p->iColumn;
-
- if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol)
- || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol)
+ if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
+ || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
- if( p->usable ){
- idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16);
- aConstraint[0].iConsIndex = i;
- }else{
+ if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Set a prohibitively high cost. */
pInfo->estimatedCost = 1e50;
+ assert( iIdxStr < pInfo->nConstraint*6 + 1 );
+ idxStr[iIdxStr] = 0;
return SQLITE_OK;
+ }else{
+ if( iCol==nCol+1 ){
+ if( bSeenRank ) continue;
+ idxStr[iIdxStr++] = 'r';
+ bSeenRank = 1;
+ }else{
+ bSeenMatch = 1;
+ idxStr[iIdxStr++] = 'm';
+ if( iCol<nCol ){
+ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
+ idxStr += strlen(&idxStr[iIdxStr]);
+ assert( idxStr[iIdxStr]=='\0' );
+ }
+ }
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ pInfo->aConstraintUsage[i].omit = 1;
}
- }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
- int j;
- for(j=1; j<ArraySize(aConstraint); j++){
- struct Constraint *pC = &aConstraint[j];
- if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
- pC->iConsIndex = i;
- idxFlags |= pC->fts5op;
+ }
+ else if( p->usable && bSeenEq==0
+ && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
+ ){
+ idxStr[iIdxStr++] = '=';
+ bSeenEq = 1;
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ }
+ }
+
+ if( bSeenEq==0 ){
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ if( p->iColumn<0 && p->usable ){
+ int op = p->op;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
+ if( bSeenLt ) continue;
+ idxStr[iIdxStr++] = '<';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenLt = 1;
+ }else
+ if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
+ if( bSeenGt ) continue;
+ idxStr[iIdxStr++] = '>';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenGt = 1;
}
}
}
}
+ idxStr[iIdxStr] = '\0';
/* Set idxFlags flags for the ORDER BY clause */
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
- if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
+ if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
}else if( iSort==-1 ){
idxFlags |= FTS5_BI_ORDER_ROWID;
@@ -216947,26 +218208,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
/* Calculate the estimated cost based on the flags set in idxFlags. */
- bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
- if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
- pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
- if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
- }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
- }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
+ if( bSeenEq ){
+ pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0;
+ if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
+ }else if( bSeenLt && bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0;
+ }else if( bSeenLt || bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0;
}else{
- pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
- }
-
- /* Assign argvIndex values to each constraint in use. */
- iNext = 1;
- for(i=0; i<ArraySize(aConstraint); i++){
- struct Constraint *pC = &aConstraint[i];
- if( pC->iConsIndex>=0 ){
- pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
- pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
- }
+ pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0;
}
pInfo->idxNum = idxFlags;
@@ -217289,7 +218539,7 @@ static int fts5CursorFirstSorted(
**
** If SQLite a built-in statement cache, this wouldn't be a problem. */
rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
- "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
+ "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s",
pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
(zRankArgs ? ", " : ""),
(zRankArgs ? zRankArgs : ""),
@@ -217345,10 +218595,10 @@ static int fts5SpecialMatch(
assert( pTab->p.base.zErrMsg==0 );
pCsr->ePlan = FTS5_PLAN_SPECIAL;
- if( 0==sqlite3_strnicmp("reads", z, n) ){
+ if( n==5 && 0==sqlite3_strnicmp("reads", z, n) ){
pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
}
- else if( 0==sqlite3_strnicmp("id", z, n) ){
+ else if( n==2 && 0==sqlite3_strnicmp("id", z, n) ){
pCsr->iSpecial = pCsr->iCsrId;
}
else{
@@ -217489,7 +218739,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
static int fts5FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
- const char *zUnused, /* Unused */
+ const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
@@ -217497,19 +218747,17 @@ static int fts5FilterMethod(
Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK; /* Error code */
- int iVal = 0; /* Counter for apVal[] */
int bDesc; /* True if ORDER BY [rank|rowid] DESC */
int bOrderByRank; /* True if ORDER BY rank */
- sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
-
- UNUSED_PARAM(zUnused);
- UNUSED_PARAM(nVal);
+ int i;
+ int iIdxStr = 0;
+ Fts5Expr *pExpr = 0;
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
@@ -217522,23 +218770,60 @@ static int fts5FilterMethod(
assert( pCsr->pRank==0 );
assert( pCsr->zRank==0 );
assert( pCsr->zRankArgs==0 );
+ assert( pTab->pSortCsr==0 || nVal==0 );
assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- /* Decode the arguments passed through to this function.
- **
- ** Note: The following set of if(...) statements must be in the same
- ** order as the corresponding entries in the struct at the top of
- ** fts5BestIndexMethod(). */
- if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
- iCol = (idxNum>>16);
- assert( iCol>=0 && iCol<=pConfig->nCol );
- assert( iVal==nVal );
+ /* Decode the arguments passed through to this function. */
+ for(i=0; i<nVal; i++){
+ switch( idxStr[iIdxStr++] ){
+ case 'r':
+ pRank = apVal[i];
+ break;
+ case 'm': {
+ const char *zText = (const char*)sqlite3_value_text(apVal[i]);
+ if( zText==0 ) zText = "";
+
+ if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){
+ iCol = 0;
+ do{
+ iCol = iCol*10 + (idxStr[iIdxStr]-'0');
+ iIdxStr++;
+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
+ }else{
+ iCol = pConfig->nCol;
+ }
+
+ if( zText[0]=='*' ){
+ /* The user has issued a query of the form "MATCH '*...'". This
+ ** indicates that the MATCH expression is not a full text query,
+ ** but a request for an internal parameter. */
+ rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
+ goto filter_out;
+ }else{
+ char **pzErr = &pTab->p.base.zErrMsg;
+ rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
+ pExpr = 0;
+ }
+ if( rc!=SQLITE_OK ) goto filter_out;
+ }
+
+ break;
+ }
+ case '=':
+ pRowidEq = apVal[i];
+ break;
+ case '<':
+ pRowidLe = apVal[i];
+ break;
+ default: assert( idxStr[iIdxStr-1]=='>' );
+ pRowidGe = apVal[i];
+ break;
+ }
+ }
bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
@@ -217565,7 +218850,7 @@ static int fts5FilterMethod(
** (pCursor) is used to execute the query issued by function
** fts5CursorFirstSorted() above. */
assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
- assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
+ assert( nVal==0 && bOrderByRank==0 && bDesc==0 );
assert( pCsr->iLastRowid==LARGEST_INT64 );
assert( pCsr->iFirstRowid==SMALLEST_INT64 );
if( pTab->pSortCsr->bDesc ){
@@ -217578,29 +218863,15 @@ static int fts5FilterMethod(
pCsr->ePlan = FTS5_PLAN_SOURCE;
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }else if( pMatch ){
- const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
- if( zExpr==0 ) zExpr = "";
-
+ }else if( pCsr->pExpr ){
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
- if( zExpr[0]=='*' ){
- /* The user has issued a query of the form "MATCH '*...'". This
- ** indicates that the MATCH expression is not a full text query,
- ** but a request for an internal parameter. */
- rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
+ if( bOrderByRank ){
+ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
+ rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
}else{
- char **pzErr = &pTab->p.base.zErrMsg;
- rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr);
- if( rc==SQLITE_OK ){
- if( bOrderByRank ){
- pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
- rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
- }else{
- pCsr->ePlan = FTS5_PLAN_MATCH;
- rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }
- }
+ pCsr->ePlan = FTS5_PLAN_MATCH;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
}
}
}else if( pConfig->zContent==0 ){
@@ -217617,7 +218888,7 @@ static int fts5FilterMethod(
);
if( rc==SQLITE_OK ){
if( pCsr->ePlan==FTS5_PLAN_ROWID ){
- sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
}else{
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
@@ -217626,6 +218897,8 @@ static int fts5FilterMethod(
}
}
+ filter_out:
+ sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
return rc;
}
@@ -218596,7 +219869,7 @@ static void fts5ApiCallback(
iCsrId = sqlite3_value_int64(argv[0]);
pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
- if( pCsr==0 ){
+ if( pCsr==0 || pCsr->ePlan==0 ){
char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
@@ -219012,7 +220285,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b", -1, SQLITE_TRANSIENT);
}
/*
@@ -219284,7 +220557,9 @@ static int fts5StorageGetStmt(
}else{
int f = SQLITE_PREPARE_PERSISTENT;
if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
+ p->pConfig->bLock++;
rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
+ p->pConfig->bLock--;
sqlite3_free(zSql);
if( rc!=SQLITE_OK && pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
@@ -223778,9 +225053,9 @@ SQLITE_API int sqlite3_stmt_init(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
-#if __LINE__!=223781
+#if __LINE__!=225056
#undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88alt2"
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3dfalt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
diff --git a/db/sqlite3/src/sqlite3.h b/db/sqlite3/src/sqlite3.h
index a4bab0ad6..37bfac528 100644
--- a/db/sqlite3/src/sqlite3.h
+++ b/db/sqlite3/src/sqlite3.h
@@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.29.0"
-#define SQLITE_VERSION_NUMBER 3029000
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6"
+#define SQLITE_VERSION "3.30.1"
+#define SQLITE_VERSION_NUMBER 3030001
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2093,6 +2093,17 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back. </dd>
+**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the
@@ -2265,7 +2276,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1014 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -3814,7 +3826,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -4849,6 +4861,12 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
+** flag is recommended for any application-defined SQL function that has
+** side-effects.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -4965,8 +4983,30 @@ SQLITE_API int sqlite3_create_window_function(
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** The SQLITE_DETERMINISTIC flag means that the new function will always
+** maps the same inputs into the same output. The abs() function is
+** deterministic, for example, but randomblob() is not.
+**
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
+** a security feature which is recommended for all
+** [application-defined SQL functions] that have side-effects. This flag
+** prevents an attacker from adding triggers and views to a schema then
+** tricking a high-privilege application into causing unintended side-effects
+** while performing ordinary queries.
+**
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** Specifying this flag makes no difference for scalar or aggregate user
+** functions. However, if it is not specified for a user-defined window
+** function, then any sub-types belonging to arguments passed to the window
+** function may be discarded before the window function is called (i.e.
+** sqlite3_value_subtype() will always return 0).
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
/*
** CAPI3REF: Deprecated Functions
@@ -6612,6 +6652,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is create and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -6628,6 +6674,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -7335,7 +7398,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -7358,7 +7421,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
-#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking