diff options
author | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-12-07 10:20:41 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@wolfbeast.com> | 2019-12-07 10:20:41 +0100 |
commit | 0fddf6e728ddea66a463e1ccd007aa9d48498905 (patch) | |
tree | 65e28a16bbfcf1747ca41a6a808136ee578735d9 /db | |
parent | 210d6a87a2759887ce286288ab0815cbd0439e5a (diff) | |
parent | 18159927e8f37a1858f9757803b20744fcfff505 (diff) | |
download | UXP-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')
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, ®ToFree); + 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, ®ToFree); + } 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, ®Free1)); + exprToRegister(pDel, exprCodeVector(pParse, pDel, ®Free1)); 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, ®Free1)); - 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, ®Free1)); + 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 |