diff --git a/BUILD.gn b/BUILD.gn index 046c950970d53515980689e62ce5295bc1520ced..89d686990a667d188ea85c38224a6f03f69662ec 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -189,7 +189,6 @@ ohos_executable("sqlite3") { "LOG_DUMP", "FDSAN_ENABLE", "HARMONY_OS", - "SQLITE_ENABLE_BINLOG", ] cflags = [ @@ -233,7 +232,6 @@ if (is_mingw || is_mac) { "SQLITE_EXPORT_SYMBOLS", "SQLITE_SHARED_BLOCK_OPTIMIZATION", "OPENSSL_SUPPRESS_DEPRECATED", - "SQLITE_ENABLE_BINLOG", ] remove_configs = [ "//build/config/compiler:chromium_code" ] deps = [ @@ -299,7 +297,6 @@ if (is_cross_platform_build) { "SQLITE_CODEC_ATTACH_CHANGED", "SQLITE_ENABLE_DROPTABLE_CALLBACK", "OPENSSL_SUPPRESS_DEPRECATED", - "SQLITE_ENABLE_BINLOG", ] cflags_c = [ diff --git a/include/sqlite3.h b/include/sqlite3.h index 4e24bca219f7617799c1b4e974c45b7da8facce7..372d949f362b393fafcddbd1e6a5dfd0f1e75c54 100644 --- a/include/sqlite3.h +++ b/include/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.40.1" -#define SQLITE_VERSION_NUMBER 3040001 -#define SQLITE_SOURCE_ID "2022-12-28 14:03:47 df5c253c0b3dd24916e4ec7cf77d3db5294cc9fd45ae7b9c5e82ad8197f38a24" +#define SQLITE_VERSION "3.44.4" +#define SQLITE_VERSION_NUMBER 3044004 +#define SQLITE_SOURCE_ID "2025-02-19 00:18:53 f1e31fd9961ac82535a5d0702b127d84de8ca21d4df1c51c73e078ea0ad4afa8" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -528,6 +528,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) +#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) @@ -563,6 +564,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) +#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) @@ -1175,7 +1177,6 @@ struct sqlite3_io_methods { ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. -** ** **
+** The SQLITE_DIRECTONLY flag is recommended for any +** [application-defined SQL function] +** that has side-effects or that could potentially leak sensitive information. +** This will prevent attacks in which an application is tricked +** into using a database file that has had its schema surreptitiously +** modified to invoke the application-defined function in ways that are +** harmful. +**
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all +** [application-defined SQL functions], regardless of whether or not they +** are security sensitive, as doing so prevents those functions from being used +** inside of the database schema, and thus ensures that the database +** can be inspected and modified using generic tools (such as the [CLI]) +** that do not have access to the application-defined functions. **
Otherwise, "BINARY" is returned. ** */ -SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); +SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); /* ** CAPI3REF: Determine if a virtual table query is DISTINCT @@ -9775,7 +10030,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ** communicated to the xBestIndex method as a ** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** this constraint, it must set the corresponding -** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ** the usual mode of handling IN operators, SQLite generates [bytecode] ** that invokes the [xFilter|xFilter() method] once for each value ** on the right-hand side of the IN operator.)^ Thus the virtual table @@ -9844,21 +10099,20 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or -** sqlite3_vtab_in_next(X,P) must be one of the parameters to the +** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ** xFilter method which invokes these routines, and specifically ** a parameter that was previously selected for all-at-once IN constraint ** processing use the [sqlite3_vtab_in()] interface in the ** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ** an xFilter argument that was selected for all-at-once IN constraint -** processing, then these routines return [SQLITE_MISUSE])^ or perhaps -** exhibit some other undefined or harmful behavior. +** processing, then these routines return [SQLITE_ERROR].)^ ** ** ^(Use these routines to access all values on the right-hand side ** of the IN constraint using code like the following: ** **
** for(rc=sqlite3_vtab_in_first(pList, &pVal); -** rc==SQLITE_OK && pVal +** rc==SQLITE_OK && pVal; ** rc=sqlite3_vtab_in_next(pList, &pVal) ** ){ ** // do something with pVal @@ -9956,6 +10210,10 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** +** Not all values are available for all query elements. When a value is +** not available, the output variable is set to -1 if the value is numeric, +** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). +** **** [[SQLITE_SCANSTAT_NLOOP]]
*/ #define SQLITE_SCANSTAT_NLOOP 0 @@ -9997,12 +10267,14 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status ** METHOD: sqlite3_stmt ** -** This interface returns information about the predicted and measured +** These interfaces return information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. @@ -10013,19 +10285,25 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior -** of this interface is undefined. -** ^The requested measurement is written into a variable pointed to by -** the "pOut" parameter. -** Parameter "idx" identifies the specific loop to retrieve statistics for. -** Loops are numbered starting from zero. ^If idx is out of range - less than -** zero or greater than or equal to the total number of loops used to implement -** the statement - a non-zero value is returned and the variable that pOut -** points to is unchanged. -** -** ^Statistics might not be available for all loops in all statements. ^In cases -** where there exist loops with no available statistics, this function behaves -** as if the loop did not exist - it returns non-zero and leave the variable -** that pOut points to unchanged. +** of this interface is undefined. ^The requested measurement is written into +** a variable pointed to by the "pOut" parameter. +** +** The "flags" parameter must be passed a mask of flags. At present only +** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** is specified, then status information is available for all elements +** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements +** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of +** the EXPLAIN QUERY PLAN output) are available. Invoking API +** sqlite3_stmt_scanstatus() is equivalent to calling +** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. +** +** Parameter "idx" identifies the specific query element to retrieve statistics +** for. Query elements are numbered starting from zero. A value of -1 may be +** to query for statistics regarding the entire query. ^If idx is out of range +** - less than -1 or greater than or equal to the total number of query +** elements used to implement the statement - a non-zero value is returned and +** the variable that pOut points to is unchanged. ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ @@ -10035,6 +10313,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); +SQLITE_API int sqlite3_stmt_scanstatus_v2( + sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +/* +** CAPI3REF: Prepared Statement Scan Status +** KEYWORDS: {scan status flags} +*/ +#define SQLITE_SCANSTAT_COMPLEX 0x0001 /* ** CAPI3REF: Zero Scan-Status Counters @@ -10125,6 +10416,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** +** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from +** the previous call on the same [database connection] D, or NULL for +** the first call on D. +** ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines @@ -10164,7 +10459,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** When the [sqlite3_blob_write()] API is used to update a blob column, ** the pre-update hook is invoked with SQLITE_DELETE. This is because the ** in this case the new values are not available. In this case, when a -** callback made with op==SQLITE_DELETE is actuall a write using the +** callback made with op==SQLITE_DELETE is actually a write using the ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** the index of the column being written. In other cases, where the ** pre-update hook is being invoked for some other reason, including a @@ -10425,6 +10720,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. ** +** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, +** the returned buffer content will remain accessible and unchanged +** until either the next write operation on the connection or when +** the connection is closed, and applications must not modify the +** buffer. If the bit had been clear, the returned buffer will not +** be accessed by SQLite after the call. +** ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. @@ -10473,6 +10775,9 @@ SQLITE_API unsigned char *sqlite3_serialize( ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** +** Applications must not modify the buffer P or invalidate it before +** the database connection D is closed. +** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. @@ -10481,6 +10786,13 @@ SQLITE_API unsigned char *sqlite3_serialize( ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** function returns SQLITE_ERROR. ** +** The deserialized database should not be in [WAL mode]. If the database +** is in WAL mode, then any attempt to use the database file will result +** in an [SQLITE_CANTOPEN] error. The application can set the +** [file format version numbers] (bytes 18 and 19) of the input database P +** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the +** database file into rollback mode and work around this limitation. +** ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlite3_free()] is invoked on argument P prior to returning. @@ -10530,6 +10842,19 @@ SQLITE_API int sqlite3_deserialize( # undef double #endif +#if defined(__wasi__) +# undef SQLITE_WASI +# define SQLITE_WASI 1 +# undef SQLITE_OMIT_WAL +# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ +# ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +# endif +# ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +# endif +#endif + #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif @@ -10736,16 +11061,20 @@ SQLITE_API int sqlite3session_create( SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* -** CAPIREF: Conigure a Session Object +** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been -** created. At present the only valid value for the second parameter is -** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** created. At present the only valid values for the second parameter are +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** -** Arguments for sqlite3session_object_config() +*/ +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); + +/* +** CAPI3REF: Options for sqlite3session_object_config ** -** The following values may passed as the the 4th parameter to +** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** **- SQLITE_SCANSTAT_NLOOP
**- ^The [sqlite3_int64] variable pointed to by the V parameter will be @@ -9983,12 +10241,24 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** -** [[SQLITE_SCANSTAT_SELECTID]]
- SQLITE_SCANSTAT_SELECT
+** [[SQLITE_SCANSTAT_SELECTID]]- SQLITE_SCANSTAT_SELECTID
**- ^The "int" variable pointed to by the V parameter will be set to the -** "select-id" for the X-th loop. The select-id identifies which query or -** subquery the loop is part of. The main query has a select-id of zero. -** The select-id is the same value as is output in the first column -** of an [EXPLAIN QUERY PLAN] query. +** id for the X-th query plan element. The id value is unique within the +** statement. The select-id is the same value as is output in the first +** column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_PARENTID]]
- SQLITE_SCANSTAT_PARENTID
+**- The "int" variable pointed to by the V parameter will be set to the +** the id of the parent of the current query element, if applicable, or +** to zero if the query element has no parent. This is the same value as +** returned in the second column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_NCYCLE]]
- SQLITE_SCANSTAT_NCYCLE
+**- The sqlite3_int64 output value is set to the number of cycles, +** according to the processor time-stamp counter, that elapsed while the +** query element was being processed. This value is not available for +** all query elements - if it is unavailable the output variable is +** set to -1. **
SQLITE_SESSION_OBJCONFIG_SIZE @@ -10761,12 +11090,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. +** +** SQLITE_SESSION_OBJCONFIG_ROWID +** This option is used to set, clear or query the flag that enables +** collection of data for tables with no explicit PRIMARY KEY. +** +** Normally, tables with no explicit PRIMARY KEY are simply ignored +** by the sessions module. However, if this flag is set, it behaves +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted +** as their leftmost columns. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. */ -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); - -/* -*/ -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11527,6 +11865,18 @@ SQLITE_API int sqlite3changeset_concat( ); +/* +** CAPI3REF: Upgrade the Schema of a Changeset/Patchset +*/ +SQLITE_API int sqlite3changeset_upgrade( + sqlite3 *db, + const char *zDb, + int nIn, const void *pIn, /* Input changeset */ + int *pnOut, void **ppOut /* OUT: Inverse of input */ +); + + + /* ** CAPI3REF: Changegroup Handle ** @@ -11573,6 +11923,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; */ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); +/* +** CAPI3REF: Add a Schema to a Changegroup +** METHOD: sqlite3_changegroup_schema +** +** This method may be used to optionally enforce the rule that the changesets +** added to the changegroup handle must match the schema of database zDb +** ("main", "temp", or the name of an attached database). If +** sqlite3changegroup_add() is called to add a changeset that is not compatible +** with the configured schema, SQLITE_SCHEMA is returned and the changegroup +** object is left in an undefined state. +** +** A changeset schema is considered compatible with the database schema in +** the same way as for sqlite3changeset_apply(). Specifically, for each +** table in the changeset, there exists a database table with: +** +** +**
+** +** The output of the changegroup object always has the same schema as the +** database nominated using this function. In cases where changesets passed +** to sqlite3changegroup_add() have fewer columns than the corresponding table +** in the database schema, these are filled in using the default column +** values from the database schema. This makes it possible to combined +** changesets that have different numbers of columns for a single table +** within a changegroup, provided that they are otherwise compatible. +*/ +SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); + /* ** CAPI3REF: Add A Changeset To A Changegroup ** METHOD: sqlite3_changegroup @@ -11641,13 +12023,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); ** If the new changeset contains changes to a table that is already present ** in the changegroup, then the number of columns and the position of the ** primary key columns for the table must be consistent. If this is not the -** case, this function fails with SQLITE_SCHEMA. If the input changeset -** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is -** returned. Or, if an out-of-memory condition occurs during processing, this -** function returns SQLITE_NOMEM. In all cases, if an error occurs the state -** of the final contents of the changegroup is undefined. +** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup +** object has been configured with a database schema using the +** sqlite3changegroup_schema() API, then it is possible to combine changesets +** with different numbers of columns for a single table, provided that +** they are otherwise compatible. +** +** If the input changeset appears to be corrupt and the corruption is +** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition +** occurs during processing, this function returns SQLITE_NOMEM. ** -** If no error occurs, SQLITE_OK is returned. +** In all cases, if an error occurs the state of the final contents of the +** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); @@ -11899,9 +12286,30 @@ SQLITE_API int sqlite3changeset_apply_v2( ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. +** +**- The name identified by the changeset, and +**
- at least as many columns as recorded in the changeset, and +**
- the primary key columns in the same position as recorded in +** the changeset. +**
SQLITE_CHANGESETAPPLY_IGNORENOOP +** Do not invoke the conflict handler callback for any changes that +** would not actually modify the database even if they were applied. +** Specifically, this means that the conflict handler is not invoked +** for: +** +**
+** +**- a delete change if the row being deleted cannot be found, +**
- an update change if the modified fields are already set to +** their new values in the conflicting row, or +**
- an insert change if all fields of the conflicting row match +** the row being inserted. +**
SQLITE_CHANGESETAPPLY_FKNOACTION +** If this flag it set, then all foreign key constraints in the target +** database behave as if they were declared with "ON UPDATE NO ACTION ON +** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL +** or SET DEFAULT. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 +#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 +#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -12642,7 +13050,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 2 */ void *(*xUserData)(Fts5Context*); @@ -12871,8 +13279,8 @@ struct Fts5ExtensionApi { ** as separate queries of the FTS index are required for each synonym. ** ** When using methods (2) or (3), it is important that the tokenizer only -** provide synonyms when tokenizing document text (method (2)) or query -** text (method (3)), not both. Doing so will not cause any errors, but is +** provide synonyms when tokenizing document text (method (3)) or query +** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; @@ -12920,7 +13328,7 @@ struct fts5_api { int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_tokenizer *pTokenizer, void (*xDestroy)(void*) ); @@ -12929,7 +13337,7 @@ struct fts5_api { int (*xFindTokenizer)( fts5_api *pApi, const char *zName, - void **ppContext, + void **ppUserData, fts5_tokenizer *pTokenizer ); @@ -12937,7 +13345,7 @@ struct fts5_api { int (*xCreateFunction)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); diff --git a/include/sqlite3ext.h b/include/sqlite3ext.h index 03b6f35f107476e1672c5b329afa899b5aba51a8..898ab1310866f3ecbeac83920223171ad7b51022 100644 --- a/include/sqlite3ext.h +++ b/include/sqlite3ext.h @@ -359,6 +359,13 @@ struct sqlite3_api_routines { const char *(*db_name)(sqlite3*,int); /* Version 3.40.0 and later */ int (*value_encoding)(sqlite3_value*); + /* Version 3.41.0 and later */ + int (*is_interrupted)(sqlite3*); + /* Version 3.43.0 and later */ + int (*stmt_explain)(sqlite3_stmt*,int); + /* Version 3.44.0 and later */ + void *(*get_clientdata)(sqlite3*,const char*); + int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); #ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK /* handle after drop table done */ int (*set_droptable_handle)(sqlite3*,void(*)(sqlite3*,const char*,const char*)); @@ -693,6 +700,13 @@ extern const sqlite3_api_routines *sqlite3_export_symbols; #define sqlite3_db_name sqlite3_api->db_name /* Version 3.40.0 and later */ #define sqlite3_value_encoding sqlite3_api->value_encoding +/* Version 3.41.0 and later */ +#define sqlite3_is_interrupted sqlite3_api->is_interrupted +/* Version 3.43.0 and later */ +#define sqlite3_stmt_explain sqlite3_api->stmt_explain +/* Version 3.44.0 and later */ +#define sqlite3_get_clientdata sqlite3_api->get_clientdata +#define sqlite3_set_clientdata sqlite3_api->set_clientdata #ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK /* handle after drop table done */ #define sqlite3_set_droptable_handle sqlite3_api->set_droptable_handle diff --git a/include/sqlite3tokenizer.h b/include/sqlite3tokenizer.h index b1a1f097e5f4a98011323ad4afa18214f12b9f42..a78a2af24c232168a66927175d1db5ab9310b9ad 100644 --- a/include/sqlite3tokenizer.h +++ b/include/sqlite3tokenizer.h @@ -35,6 +35,9 @@ ** the tokenization rules supplied by a specific sqlite3_tokenizer ** object. */ +#ifndef SQLITE3TOKENIZER_H +#define SQLITE3TOKENIZER_H + typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module; typedef struct sqlite3_tokenizer sqlite3_tokenizer; typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor; @@ -142,4 +145,6 @@ struct sqlite3_tokenizer { struct sqlite3_tokenizer_cursor { sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */ /* Tokenizer implementations will typically add additional fields */ -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/patch/0001-BaselineWithHistoryOH.patch b/patch/0001-History-features-on-OH.patch similarity index 77% rename from patch/0001-BaselineWithHistoryOH.patch rename to patch/0001-History-features-on-OH.patch index af43fdf5e5ff2d285a9b0af819378282a77ac717..d33eeeebb9337fe95e1dc1de903c3a81092fe7e2 100644 --- a/patch/0001-BaselineWithHistoryOH.patch +++ b/patch/0001-History-features-on-OH.patch @@ -1,26 +1,18 @@ -From 04d6f09752f3c7ac5111c3274dbb031fd2530fb5 Mon Sep 17 00:00:00 2001 +From f71a3aaddebdcba87ad8af804d680397ecc5fa48 Mon Sep 17 00:00:00 2001 From: wanghaishuo -Date: Fri, 11 Apr 2025 17:36:47 +0800 -Subject: [PATCH 1/7] BaselineWithHistoryOH +Date: Mon, 12 May 2025 15:29:53 +0800 +Subject: [PATCH 1/6] History features on OH - Signed-off-by: ryne3366 +Signed-off-by: wanghaishuo --- - src/sqlite3.c | 2863 ++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 2823 insertions(+), 40 deletions(-) + src/sqlite3.c | 2451 ++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 2438 insertions(+), 13 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c -index 3469f92..a935685 100644 +index 7fb290f..ad78006 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -870,6 +870,7 @@ SQLITE_API int sqlite3_exec( - #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) - #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) - #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) -+#define SQLITE_WARNING_DUMP (SQLITE_WARNING | (2<<8)) - #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) - #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) - #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ -@@ -2832,6 +2833,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); +@@ -2915,6 +2915,11 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); */ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); @@ -32,7 +24,24 @@ index 3469f92..a935685 100644 /* ** CAPI3REF: Set the Last Insert Rowid value. ** METHOD: sqlite3 -@@ -5174,6 +5180,10 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +@@ -3274,6 +3279,16 @@ SQLITE_API int sqlite3_get_table( + ); + SQLITE_API void sqlite3_free_table(char **result); + ++// hw export the symbols ++#ifdef SQLITE_EXPORT_SYMBOLS ++#if defined(__GNUC__) ++# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) ++#elif defined(_MSC_VER) ++# define EXPORT_SYMBOLS __declspec(dllexport) ++#else ++# define EXPORT_SYMBOLS ++#endif ++#endif + /* + ** CAPI3REF: Formatted String Printing Functions + ** +@@ -5312,6 +5327,10 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); */ SQLITE_API int sqlite3_step(sqlite3_stmt*); @@ -43,7 +52,7 @@ index 3469f92..a935685 100644 /* ** CAPI3REF: Number of columns in a result set ** METHOD: sqlite3_stmt -@@ -6439,6 +6449,44 @@ SQLITE_API int sqlite3_collation_needed16( +@@ -6712,6 +6731,44 @@ SQLITE_API int sqlite3_collation_needed16( void(*)(void*,sqlite3*,int eTextRep,const void*) ); @@ -88,7 +97,7 @@ index 3469f92..a935685 100644 #ifdef SQLITE_ENABLE_CEROD /* ** Specify the activation key for a CEROD database. Unless -@@ -9872,6 +9920,27 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); +@@ -10149,6 +10206,27 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); */ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); @@ -116,21 +125,7 @@ index 3469f92..a935685 100644 /* ** CAPI3REF: Determine If Virtual Table Column Access Is For UPDATE ** -@@ -14914,13 +14983,6 @@ typedef int VList; - # define SQLITE_MAX_PATHLEN FILENAME_MAX - #endif - --/* Maximum number of symlinks that will be resolved while trying to --** expand a filename in xFullPathname() in the VFS. --*/ --#ifndef SQLITE_MAX_SYMLINK --# define SQLITE_MAX_SYMLINK 200 --#endif -- - /* - ** The default size of a disk sector - */ -@@ -15234,6 +15296,9 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); +@@ -15771,6 +15849,9 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); @@ -140,7 +135,7 @@ index 3469f92..a935685 100644 SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); -@@ -15330,6 +15395,10 @@ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); +@@ -15867,6 +15948,10 @@ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16); @@ -151,7 +146,7 @@ index 3469f92..a935685 100644 /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); -@@ -16933,6 +17002,21 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); +@@ -17497,6 +17582,21 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); */ #define SQLITE_MAX_DB (SQLITE_MAX_ATTACHED+2) @@ -173,7 +168,7 @@ index 3469f92..a935685 100644 /* ** Each database connection is an instance of the following structure. */ -@@ -17076,6 +17160,15 @@ struct sqlite3 { +@@ -17641,6 +17741,15 @@ struct sqlite3 { #ifdef SQLITE_USER_AUTHENTICATION sqlite3_userauth auth; /* User authentication information */ #endif @@ -189,7 +184,7 @@ index 3469f92..a935685 100644 }; /* -@@ -20046,7 +20139,14 @@ SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); +@@ -20733,7 +20842,14 @@ SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u32,Select*); SQLITE_PRIVATE void sqlite3AddReturning(Parse*,ExprList*); SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); @@ -205,18 +200,7 @@ index 3469f92..a935685 100644 SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE -@@ -21098,6 +21198,10 @@ SQLITE_API extern int sqlite3_open_file_count; - #define OpenCounter(X) - #endif /* defined(SQLITE_TEST) */ - -+#ifdef FDSAN_ENABLE -+#define SQLITE_FDSAN_TAG 5351 -+#endif /* FDSAN_ENABLE */ -+ - #endif /* !defined(_OS_COMMON_H_) */ - - /************** End of os_common.h *******************************************/ -@@ -21480,6 +21584,9 @@ static const char * const sqlite3azCompileOpt[] = { +@@ -22095,6 +22211,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_FTS5_NO_WITHOUT_ROWID "FTS5_NO_WITHOUT_ROWID", #endif @@ -226,7 +210,7 @@ index 3469f92..a935685 100644 #if HAVE_ISNAN || SQLITE_HAVE_ISNAN "HAVE_ISNAN", #endif -@@ -21488,6 +21595,9 @@ static const char * const sqlite3azCompileOpt[] = { +@@ -22103,6 +22222,9 @@ static const char * const sqlite3azCompileOpt[] = { "HOMEGROWN_RECURSIVE_MUTEX=" CTIMEOPT_VAL(SQLITE_HOMEGROWN_RECURSIVE_MUTEX), # endif #endif @@ -236,7 +220,7 @@ index 3469f92..a935685 100644 #ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS "IGNORE_AFP_LOCK_ERRORS", #endif -@@ -22057,9 +22167,16 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { +@@ -22678,9 +22800,16 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { ** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally ** disabled. The default value may be changed by compiling with the ** SQLITE_USE_URI symbol defined. @@ -254,7 +238,7 @@ index 3469f92..a935685 100644 #endif /* EVIDENCE-OF: R-38720-18127 The default setting is determined by the -@@ -22797,6 +22914,13 @@ struct Vdbe { +@@ -23449,6 +23578,13 @@ struct Vdbe { int nScan; /* Entries in aScan[] */ ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ #endif @@ -268,19 +252,7 @@ index 3469f92..a935685 100644 }; /* -@@ -31163,7 +31287,11 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - */ - static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ - StrAccum acc; /* String accumulator */ -+#ifndef LOG_DUMP - char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ -+#else -+ char zMsg[SQLITE_PRINT_BUF_SIZE*10]; /* Complete log message */ -+#endif /* !LOG_DUMP */ - - sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); - sqlite3_str_vappendf(&acc, zFormat, ap); -@@ -34841,7 +34969,7 @@ SQLITE_PRIVATE u8 sqlite3HexToInt(int h){ +@@ -35960,7 +36096,7 @@ SQLITE_PRIVATE u8 sqlite3HexToInt(int h){ return (u8)(h & 0xf); } @@ -289,7 +261,7 @@ index 3469f92..a935685 100644 /* ** Convert a BLOB literal of the form "x'hhhhhh'" into its binary ** value. Return a pointer to its binary value. Space to hold the -@@ -34862,7 +34990,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ +@@ -35981,7 +36117,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ } return zBlob; } @@ -298,510 +270,7 @@ index 3469f92..a935685 100644 /* ** Log an error that is an API call on a connection pointer that should -@@ -37023,6 +37151,29 @@ static pid_t randomnessPid = 0; - #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 - #endif /* __linux__ */ - -+#ifdef HARMONY_OS -+#define HMFS_MONITOR_FL 0x00000002 -+#define HMFS_IOCTL_HW_GET_FLAGS _IOR(0xf5, 70, unsigned int) -+#define HMFS_IOCTL_HW_SET_FLAGS _IOR(0xf5, 71, unsigned int) -+ -+static void enableDbFileDelMonitor(int32_t fd) -+{ -+ unsigned int flags = 0; -+ int ret = ioctl(fd, HMFS_IOCTL_HW_GET_FLAGS, &flags); -+ if (ret < 0) { -+ return; -+ } -+ if (flags & HMFS_MONITOR_FL) { -+ return; -+ } -+ flags |= HMFS_MONITOR_FL; -+ ret = ioctl(fd, HMFS_IOCTL_HW_SET_FLAGS, &flags); -+ if (ret < 0) { -+ sqlite3_log(SQLITE_WARNING, "Fd %d enable del monitor go wrong, errno = %d", fd, errno); -+ } -+} -+ -+#endif /* HARMONY_OS */ - - /* - ** Different Unix systems declare open() in different ways. Same use -@@ -37033,7 +37184,29 @@ static pid_t randomnessPid = 0; - ** which always has the same well-defined interface. - */ - static int posixOpen(const char *zFile, int flags, int mode){ -- return open(zFile, flags, mode); -+ int fd = open(zFile, flags, mode); -+#ifdef FDSAN_ENABLE -+ if( fd >= 0 ){ -+ fdsan_exchange_owner_tag(fd, 0, fdsan_create_owner_tag(FDSAN_OWNER_TYPE_FILE, SQLITE_FDSAN_TAG)); -+ } -+#endif /* FDSAN_ENABLE */ -+#ifdef HARMONY_OS -+ if( fd >= 0 ){ -+ enableDbFileDelMonitor(fd); -+ } -+#endif /* HARMONY_OS */ -+ return fd; -+} -+ -+/* -+** Change close to posixClose, use fdsan_close_with_tag when fdsan enable. -+*/ -+static int posixClose(int fd) { -+#ifdef FDSAN_ENABLE -+ return fdsan_close_with_tag(fd, fdsan_create_owner_tag(FDSAN_OWNER_TYPE_FILE, SQLITE_FDSAN_TAG)); -+#else -+ return close(fd); -+#endif /* FDSAN_ENABLE */ - } - - /* Forward reference */ -@@ -37054,7 +37227,7 @@ static struct unix_syscall { - { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, - #define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) - -- { "close", (sqlite3_syscall_ptr)close, 0 }, -+ { "close", (sqlite3_syscall_ptr)posixClose, 0 }, - #define osClose ((int(*)(int))aSyscall[1].pCurrent) - - { "access", (sqlite3_syscall_ptr)access, 0 }, -@@ -38048,6 +38221,9 @@ static int findInodeInfo( - #if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) - if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; - #endif -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR, "findInodeInfo-osFstat, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR; - } - -@@ -38066,11 +38242,17 @@ static int findInodeInfo( - do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); - if( rc!=1 ){ - storeLastErrno(pFile, errno); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR, "findInodeInfo-osWrite, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR; - } - rc = osFstat(fd, &statbuf); - if( rc!=0 ){ - storeLastErrno(pFile, errno); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR, "findInodeInfo-msdos-osFstat, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR; - } - } -@@ -39232,6 +39414,9 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { - #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS - return SQLITE_OK; - #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_UNLOCK, "IOERR_UNLOCK fd[%d], eFileLock[%d]", pFile->h, eFileLock); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_UNLOCK; - }else{ - pFile->eFileLock = NO_LOCK; -@@ -40059,8 +40244,14 @@ static int unixRead( - #ifdef EDEVERR - case EDEVERR: - #endif -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_CORRUPTFS, "unixRead-EDEVERR, fd:[%d], amt[%d], got[%d], offset[%lld]", pFile->h, amt, got, offset); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_CORRUPTFS; - } -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_READ, "unixRead-got<0, fd: [%d], amt[%d], got[%d], offset[%lld]", pFile->h, amt, got, offset); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_READ; - }else{ - storeLastErrno(pFile, 0); /* not a system error */ -@@ -40202,9 +40393,19 @@ static int unixWrite( - if( amt>wrote ){ - if( wrote<0 && pFile->lastErrno!=ENOSPC ){ - /* lastErrno set by seekAndWrite */ -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_WRITE, -+ "unixWrite, lastErrno set by seekAndWrite, fd[%d], offset[%lld], wrote[%d], amt[%d], lastErrno[%d]", -+ pFile->h, offset, wrote, amt, pFile->lastErrno); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_WRITE; - }else{ - storeLastErrno(pFile, 0); /* not a system error */ -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_FULL, -+ "unixWrite, not a system error, fd[%d], offset[%lld], wrote[%d], amt[%d], lastErrno[%d]", -+ pFile->h, offset, wrote, amt, pFile->lastErrno); -+#endif /* LOG_DUMP */ - return SQLITE_FULL; - } - } -@@ -40558,7 +40759,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ - do{ - err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); - }while( err==EINTR ); -- if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE; -+ if( err && err!=EINVAL ) { -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_WRITE, -+ "fcntlSizeHint-fallocate, fd[%d], bufSize[%lld], nSize[%lld] err[%d]", -+ pFile->h, buf.st_size, nSize, err); -+#endif /* LOG_DUMP */ -+ return SQLITE_IOERR_WRITE; -+ } - #else - /* If the OS does not have posix_fallocate(), fake it. Write a - ** single byte to the last byte in each block that falls entirely -@@ -40577,7 +40785,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ - for(/*no-op*/; iWrite =nSize ) iWrite = nSize - 1; - nWrite = seekAndWrite(pFile, iWrite, "", 1); -- if( nWrite!=1 ) return SQLITE_IOERR_WRITE; -+ if( nWrite!=1 ) { -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_WRITE, -+ "fcntlSizeHint-seekAndWrite, fd[%d], nWrite[%d], nSize[%d], nBlk[%d], iWrite[%lld]", -+ pFile->h, nWrite, nSize, nBlk, iWrite); -+#endif /* LOG_DUMP */ -+ return SQLITE_IOERR_WRITE; -+ } - } - #endif - } -@@ -40632,14 +40847,29 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ - #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) - case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); -+#ifdef LOG_DUMP -+ if( rc ){ -+ sqlite3_log(SQLITE_IOERR_BEGIN_ATOMIC, "unixFileControl-begin, fd[%d], op[%d]", pFile->h, op); -+ } -+#endif /* LOG_DUMP */ - return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK; - } - case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE); -+#ifdef LOG_DUMP -+ if( rc ){ -+ sqlite3_log(SQLITE_IOERR_COMMIT_ATOMIC, "unixFileControl-commit, fd[%d], op[%d]", pFile->h, op); -+ } -+#endif /* LOG_DUMP */ - return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK; - } - case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { - int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); -+#ifdef LOG_DUMP -+ if( rc ){ -+ sqlite3_log(SQLITE_IOERR_ROLLBACK_ATOMIC, "unixFileControl-rollback, fd[%d], op[%d]", pFile->h, op); -+ } -+#endif /* LOG_DUMP */ - return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; - } - #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ -@@ -41585,9 +41815,19 @@ static int unixShmLock( - int *aLock; - - p = pDbFd->pShm; -- if( p==0 ) return SQLITE_IOERR_SHMLOCK; -+ if( p==0 ) { -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShm, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags); -+#endif -+ return SQLITE_IOERR_SHMLOCK; -+ } - pShmNode = p->pShmNode; -- if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; -+ if( NEVER(pShmNode==0) ) { -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShmNode, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags); -+#endif -+ return SQLITE_IOERR_SHMLOCK; -+ } - aLock = pShmNode->aLock; - - assert( pShmNode==pDbFd->pInode->pShmNode ); -@@ -42959,6 +43199,9 @@ static int unixOpen( - if( fstatfs(fd, &fsInfo) == -1 ){ - storeLastErrno(p, errno); - robust_close(p, fd, __LINE__); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_ACCESS, "unixOpen, fd[%d], flags[%d], errno[%d]", fd, errno, flags); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_ACCESS; - } - if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { -@@ -43099,6 +43342,184 @@ static int unixAccess( - return SQLITE_OK; - } - -+#ifdef HARMONY_OS -+/* -+** If the last component of the pathname in z[0]..z[j-1] is something -+** other than ".." then back it out and return true. If the last -+** component is empty or if it is ".." then return false. -+*/ -+static int unixBackupDir(const char *z, int *pJ){ -+ int j = *pJ; -+ int i; -+ if( j<=0 ) return 0; -+ for(i=j-1; i>0 && z[i-1]!='/'; i--){} -+ if( i==0 ) return 0; -+ if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; -+ *pJ = i-1; -+ return 1; -+} -+ -+/* -+** Convert a relative pathname into a full pathname. Also -+** simplify the pathname as follows: -+** -+** Remove all instances of /./ -+** Remove all isntances of /X/../ for any X -+*/ -+static int mkFullPathname( -+ const char *zPath, /* Input path */ -+ char *zOut, /* Output buffer */ -+ int nOut /* Allocated size of buffer zOut */ -+){ -+ int nPath = sqlite3Strlen30(zPath); -+ int iOff = 0; -+ int i, j; -+ if( zPath[0]!='/' ){ -+ if( osGetcwd(zOut, nOut-2)==0 ){ -+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); -+ } -+ iOff = sqlite3Strlen30(zOut); -+ zOut[iOff++] = '/'; -+ } -+ if( (iOff+nPath+1)>nOut ){ -+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer -+ ** even if it returns an error. */ -+ zOut[iOff] = '\0'; -+ return SQLITE_CANTOPEN_BKPT; -+ } -+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); -+ -+ /* Remove duplicate '/' characters. Except, two // at the beginning -+ ** of a pathname is allowed since this is important on windows. */ -+ for(i=j=1; zOut[i]; i++){ -+ zOut[j++] = zOut[i]; -+ while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; -+ } -+ zOut[j] = 0; -+ -+ assert( zOut[0]=='/' ); -+ for(i=j=0; zOut[i]; i++){ -+ if( zOut[i]=='/' ){ -+ /* Skip over internal "/." directory components */ -+ if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ -+ i += 1; -+ continue; -+ } -+ -+ /* If this is a "/.." directory component then back out the -+ ** previous term of the directory if it is something other than "..". -+ */ -+ if( zOut[i+1]=='.' -+ && zOut[i+2]=='.' -+ && zOut[i+3]=='/' -+ && unixBackupDir(zOut, &j) -+ ){ -+ i += 2; -+ continue; -+ } -+ } -+ if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; -+ j++; -+ } -+ if( NEVER(j==0) ) zOut[j++] = '/'; -+ zOut[j] = 0; -+ return SQLITE_OK; -+} -+ -+/* -+** Turn a relative pathname into a full pathname. The relative path -+** is stored as a nul-terminated string in the buffer pointed to by -+** zPath. -+** -+** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes -+** (in this case, MAX_PATHNAME bytes). The full-path is written to -+** this buffer before returning. -+*/ -+static int unixFullPathname( -+ sqlite3_vfs *pVfs, /* Pointer to vfs object */ -+ const char *zPath, /* Possibly relative input path */ -+ int nOut, /* Size of output buffer in bytes */ -+ char *zOut /* Output buffer */ -+){ -+#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) -+ return mkFullPathname(zPath, zOut, nOut); -+#else -+ int rc = SQLITE_OK; -+ int nByte; -+ int nLink = 0; /* Number of symbolic links followed so far */ -+ const char *zIn = zPath; /* Input path for each iteration of loop */ -+ char *zDel = 0; -+ -+ assert( pVfs->mxPathname==MAX_PATHNAME ); -+ UNUSED_PARAMETER(pVfs); -+ -+ /* It's odd to simulate an io-error here, but really this is just -+ ** using the io-error infrastructure to test that SQLite handles this -+ ** function failing. This function could fail if, for example, the -+ ** current working directory has been unlinked. -+ */ -+ SimulateIOError( return SQLITE_ERROR ); -+ -+ do { -+ -+ /* Call stat() on path zIn. Set bLink to true if the path is a symbolic -+ ** link, or false otherwise. */ -+ int bLink = 0; -+ struct stat buf; -+ if( osLstat(zIn, &buf)!=0 ){ -+ if( errno!=ENOENT ){ -+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); -+ } -+ }else{ -+ bLink = S_ISLNK(buf.st_mode); -+ } -+ -+ if( bLink ){ -+ nLink++; -+ if( zDel==0 ){ -+ zDel = sqlite3_malloc(nOut); -+ if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; -+ }else if( nLink>=SQLITE_MAX_SYMLINKS ){ -+ rc = SQLITE_CANTOPEN_BKPT; -+ } -+ -+ if( rc==SQLITE_OK ){ -+ nByte = osReadlink(zIn, zDel, nOut-1); -+ if( nByte<0 ){ -+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); -+ }else{ -+ if( zDel[0]!='/' ){ -+ int n; -+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); -+ if( nByte+n+1>nOut ){ -+ rc = SQLITE_CANTOPEN_BKPT; -+ }else{ -+ memmove(&zDel[n], zDel, nByte+1); -+ memcpy(zDel, zIn, n); -+ nByte += n; -+ } -+ } -+ zDel[nByte] = '\0'; -+ } -+ } -+ -+ zIn = zDel; -+ } -+ -+ assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); -+ if( rc==SQLITE_OK && zIn!=zOut ){ -+ rc = mkFullPathname(zIn, zOut, nOut); -+ } -+ if( bLink==0 ) break; -+ zIn = zOut; -+ }while( rc==SQLITE_OK ); -+ -+ sqlite3_free(zDel); -+ if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; -+ return rc; -+#endif /* HAVE_READLINK && HAVE_LSTAT */ -+} -+#else - /* - ** A pathname under construction - */ -@@ -43156,7 +43577,7 @@ static void appendOnePathElement( - }else if( S_ISLNK(buf.st_mode) ){ - ssize_t got; - char zLnk[SQLITE_MAX_PATHLEN+2]; -- if( pPath->nSymlink++ > SQLITE_MAX_SYMLINK ){ -+ if( pPath->nSymlink++ > SQLITE_MAX_SYMLINKS ){ - pPath->rc = SQLITE_CANTOPEN_BKPT; - return; - } -@@ -43230,6 +43651,7 @@ static int unixFullPathname( - if( path.nSymlink ) return SQLITE_OK_SYMLINK; - return SQLITE_OK; - } -+#endif - - #ifndef SQLITE_OMIT_LOAD_EXTENSION - /* -@@ -43636,6 +44058,9 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ - if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ - OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", - lPath, errno, osGetpid(0))); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_LOCK, "proxyGetLockPath len[%d], dbLen[%d], i[%d]", len, dbLen, i); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_LOCK; - } - len = strlcat(lPath, "sqliteplocks", maxLen); -@@ -43754,6 +44179,9 @@ static int proxyCreateUnixFile( - case EACCES: - return SQLITE_PERM; - case EIO: -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_LOCK, "proxyCreateUnixFile-EIO, fd[%d]", fd); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_LOCK; /* even though it is the conch */ - default: - return SQLITE_CANTOPEN_BKPT; -@@ -43922,6 +44350,9 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - struct stat buf; - if( osFstat(conchFile->h, &buf) ){ - storeLastErrno(pFile, errno); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_LOCK, "proxyConchLock pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, errno); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_LOCK; - } - -@@ -43942,6 +44373,9 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ - int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); - if( len<0 ){ - storeLastErrno(pFile, errno); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_LOCK, "proxyConchLock tries 2, pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, errno); -+#endif /* LOG_DUMP */ - return SQLITE_IOERR_LOCK; - } - if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ -@@ -44016,6 +44450,9 @@ static int proxyTakeConch(unixFile *pFile){ - if( readLen<0 ){ - /* I/O error: lastErrno set by seekAndRead */ - storeLastErrno(pFile, conchFile->lastErrno); -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_IOERR_READ, "proxyTakeConch pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, conchFile->lastErrno); -+#endif /* LOG_DUMP */ - rc = SQLITE_IOERR_READ; - goto end_takeconch; - }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || -@@ -51878,13 +52315,6 @@ end_deserialize: - return rc; - } - --/* --** Return true if the VFS is the memvfs. --*/ --SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs *pVfs){ -- return pVfs==&memdb_vfs; --} -- - /* - ** This routine is called when the extension is loaded. - ** Register the new VFS. -@@ -55601,6 +56031,20 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ +@@ -56898,6 +57034,20 @@ int sqlite3PagerTrace=1; /* True to enable tracing */ */ #define UNKNOWN_LOCK (EXCLUSIVE_LOCK+1) @@ -822,7 +291,7 @@ index 3469f92..a935685 100644 /* ** The maximum allowed sector size. 64KiB. If the xSectorsize() method ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. -@@ -55889,6 +56333,12 @@ struct Pager { +@@ -57186,6 +57336,12 @@ struct Pager { #endif void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */ int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */ @@ -835,7 +304,7 @@ index 3469f92..a935685 100644 char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ PCache *pPCache; /* Pointer to page cache object */ #ifndef SQLITE_OMIT_WAL -@@ -56010,6 +56460,9 @@ static const unsigned char aJournalMagic[] = { +@@ -57307,6 +57463,9 @@ static const unsigned char aJournalMagic[] = { SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ if( pPager->fd->pMethods==0 ) return 0; if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; @@ -845,7 +314,7 @@ index 3469f92..a935685 100644 #ifndef SQLITE_OMIT_WAL if( pPager->pWal ){ u32 iRead = 0; -@@ -56243,7 +56696,11 @@ static void setGetterMethod(Pager *pPager){ +@@ -57540,7 +57699,11 @@ static void setGetterMethod(Pager *pPager){ if( pPager->errCode ){ pPager->xGet = getPageError; #if SQLITE_MAX_MMAP_SIZE>0 @@ -858,7 +327,7 @@ index 3469f92..a935685 100644 pPager->xGet = getPageMMap; #endif /* SQLITE_MAX_MMAP_SIZE>0 */ }else{ -@@ -57394,6 +57851,32 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ +@@ -58732,6 +58895,32 @@ static u32 pager_cksum(Pager *pPager, const u8 *aData){ return cksum; } @@ -891,7 +360,7 @@ index 3469f92..a935685 100644 /* ** Read a single page from either the journal file (if isMainJrnl==1) or ** from the sub-journal (if isMainJrnl==0) and playback that page. -@@ -57445,6 +57928,11 @@ static int pager_playback_one_page( +@@ -58783,6 +58972,11 @@ static int pager_playback_one_page( char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ int isSynced; /* True if journal page is synced */ @@ -903,15 +372,17 @@ index 3469f92..a935685 100644 assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ assert( (isSavepnt&~1)==0 ); /* isSavepnt is 0 or 1 */ -@@ -57574,12 +58062,26 @@ static int pager_playback_one_page( +@@ -58912,12 +59106,30 @@ static int pager_playback_one_page( ** is if the data was just read from an in-memory sub-journal. In that ** case it must be encrypted here before it is copied into the database ** file. */ +#ifdef SQLITE_HAS_CODEC + if( !jrnlEnc ){ -+ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); -+ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); -+ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); ++ CODEC2(pPager, aData, pgno, 7, rc=pPager->errCode, aData); ++ if (rc == SQLITE_OK) { ++ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); ++ CODEC1(pPager, aData, pgno, 3, rc=pPager->errCode); ++ } + }else +#endif /* SQLITE_HAS_CODEC */ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); @@ -922,35 +393,46 @@ index 3469f92..a935685 100644 if( pPager->pBackup ){ +#ifdef SQLITE_HAS_CODEC + if( jrnlEnc ){ -+ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); -+ sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); -+ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); ++ CODEC1(pPager, aData, pgno, 3, rc=pPager->errCode); ++ if (rc == SQLITE_OK) { ++ sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); ++ CODEC2(pPager, aData, pgno, 7, rc=pPager->errCode, aData); ++ } + }else +#endif /* SQLITE_HAS_CODEC */ sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); } }else if( !isMainJrnl && pPg==0 ){ -@@ -57630,6 +58132,10 @@ static int pager_playback_one_page( +@@ -58968,6 +59180,10 @@ static int pager_playback_one_page( if( pgno==1 ){ memcpy(&pPager->dbFileVers, &((u8*)pData)[24],sizeof(pPager->dbFileVers)); } +#if SQLITE_HAS_CODEC + /* Decode the page just read from disk */ -+ if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } ++ if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=pPager->errCode); } +#endif /* SQLITE_HAS_CODEC */ sqlite3PcacheRelease(pPg); } return rc; -@@ -58206,6 +58712,8 @@ static int readDbPage(PgHdr *pPg){ +@@ -59511,7 +59727,7 @@ static int readDbPage(PgHdr *pPg){ + assert( isOpen(pPager->fd) ); + + if( pagerUseWal(pPager) ){ +- rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); ++ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); // find in wal-index + if( rc ) return rc; + } + if( iFrame ){ +@@ -59546,6 +59762,8 @@ static int readDbPage(PgHdr *pPg){ memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); } } -+ CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); ++ CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = pPager->errCode); + PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pPg->pgno)); -@@ -59353,6 +59861,10 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ +@@ -60691,6 +60909,10 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3OsClose(pPager->fd); sqlite3PageFree(pTmp); sqlite3PcacheClose(pPager->pPCache); @@ -961,28 +443,28 @@ index 3469f92..a935685 100644 assert( !pPager->aSavepoint && !pPager->pInJournal ); assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) ); -@@ -59603,7 +60115,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ +@@ -60941,7 +61163,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); if( pList->pgno==1 ) pager_write_changecounter(pList); - pData = pList->pData; -+ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); ++ CODEC2(pPager, pList->pData, pgno, 6, return pPager->errCode, pData); /* Write out the page data. */ rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); -@@ -59692,6 +60204,11 @@ static int subjournalPage(PgHdr *pPg){ +@@ -61030,6 +61252,11 @@ static int subjournalPage(PgHdr *pPg){ void *pData = pPg->pData; i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize); char *pData2; +#if SQLITE_HAS_CODEC + if( !pPager->subjInMemory ){ -+ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); ++ CODEC2(pPager, pData, pPg->pgno, 7, return pPager->errCode, pData2); + }else +#endif /* SQLITE_HAS_CODEC */ pData2 = pData; PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno)); rc = write32bits(pPager->sjfd, offset, pPg->pgno); -@@ -60795,6 +61312,9 @@ static int getPageMMap( +@@ -62122,6 +62349,9 @@ static int getPageMMap( ); assert( USEFETCH(pPager) ); @@ -992,25 +474,25 @@ index 3469f92..a935685 100644 /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here ** allows the compiler optimizer to reuse the results of the "pgno>1" -@@ -61125,7 +61645,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ +@@ -62466,7 +62696,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); assert( pPager->journalHdr<=pPager->journalOff ); - pData2 = pPg->pData; -+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); ++ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return pPager->errCode, pData2); cksum = pager_cksum(pPager, (u8*)pData2); /* Even if an IO or diskfull error occurs while journalling the -@@ -61490,7 +62010,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ +@@ -62831,7 +63061,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ if( DIRECT_MODE ){ const void *zBuf; assert( pPager->dbFileSize>0 ); - zBuf = pPgHdr->pData; -+ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); ++ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=pPager->errCode, zBuf); if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); pPager->aStat[PAGER_STAT_WRITE]++; -@@ -62242,6 +62762,48 @@ SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ +@@ -63594,6 +63824,48 @@ SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){ return pPager->zJournal; } @@ -1059,40 +541,10 @@ index 3469f92..a935685 100644 #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Move the page pPg to location pgno in the file. -@@ -64937,6 +65499,11 @@ static void walRestartHdr(Wal *pWal, u32 salt1){ - assert( pInfo->aReadMark[0]==0 ); - } - -+#ifdef SQLITE_HDR_CHECK -+static int checkHeaderValid(Pager *pager, u8 *zBuf, const char *logStr); -+static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf); -+#endif /* SQLITE_HDR_CHECK */ -+ - /* - ** Copy as much content as we can from the WAL back into the database file - ** in response to an sqlite3_wal_checkpoint() request or the equivalent. -@@ -65077,6 +65644,10 @@ static int walCheckpoint( - if( rc!=SQLITE_OK ) break; - iOffset = (iDbpage-1)*(i64)szPage; - testcase( IS_BIG_INT(iOffset) ); -+#ifdef SQLITE_HDR_CHECK -+ rc = checkDbHeaderValid(db, iDbpage, zBuf); -+ if( rc!=SQLITE_OK ) break; -+#endif /* SQLITE_HDR_CHECK */ - rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); - if( rc!=SQLITE_OK ) break; - } -@@ -66471,7 +67042,18 @@ static int walWriteOneFrame( +@@ -68144,7 +68416,11 @@ static int walWriteOneFrame( int rc; /* Result code from subfunctions */ void *pData; /* Data actually written */ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ -+#ifdef SQLITE_HDR_CHECK -+ if( pPage->pgno==1 ){ -+ rc = checkHeaderValid(pPage->pPager, pPage->pData, "walWrite"); -+ if( rc!=SQLITE_OK ) return rc; -+ } -+#endif /* SQLITE_HDR_CHECK */ -+ +#ifdef SQLITE_HAS_CODEC + if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; +#else @@ -1101,7 +553,7 @@ index 3469f92..a935685 100644 walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame); rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset); if( rc ) return rc; -@@ -66654,7 +67236,11 @@ SQLITE_PRIVATE int sqlite3WalFrames( +@@ -68329,7 +68605,11 @@ static int walFrames( if( pWal->iReCksum==0 || iWrite iReCksum ){ pWal->iReCksum = iWrite; } @@ -1113,78 +565,7 @@ index 3469f92..a935685 100644 rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff); if( rc ) return rc; p->flags &= ~PGHDR_WAL_APPEND; -@@ -66765,6 +67351,10 @@ SQLITE_PRIVATE int sqlite3WalFrames( - return rc; - } - -+#ifdef LOG_DUMP -+static sqlite3_int64 g_lastCkptTime = 0; -+#endif /* LOG_DUMP */ -+ - /* - ** This routine is called to implement sqlite3_wal_checkpoint() and - ** related interfaces. -@@ -66888,6 +67478,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( - walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); - pWal->ckptLock = 0; - } -+#ifdef LOG_DUMP -+ if( rc ){ -+ sqlite3_log(SQLITE_NOTICE, "ckpt rc[%d]", rc); -+ } -+ sqlite3OsCurrentTimeInt64(db->pVfs, &g_lastCkptTime); -+#endif /* LOG_DUMP */ - WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); - #ifdef SQLITE_ENABLE_SETLK_TIMEOUT - if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; -@@ -68264,6 +68860,32 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ - } - #endif - -+#ifdef SQLITE_HDR_CHECK -+static int checkHeaderValid(Pager *pager, u8 *zBuf, const char *logStr){ -+#ifdef SQLITE_HAS_CODEC -+ if( pager==NULL || pager->pCodec ){ -+ return SQLITE_OK; -+ } -+#endif /* SQLITE_HAS_CODEC */ -+ if( zBuf && strncmp((const char *)zBuf, zMagicHeader, 16)!=0 ){ -+ sqlite3_log(SQLITE_NOTADB, "[%s]wrong header format, memory might be overwritten!", logStr); -+ return SQLITE_NOTADB; -+ } -+ return SQLITE_OK; -+} -+ -+static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf){ -+ if( iDbpage==1 && db->aDb ){ -+ Btree *p = db->aDb[0].pBt; -+ if( p && p->pBt ){ -+ Pager *pager = sqlite3BtreePager(p); -+ return checkHeaderValid(pager, zBuf, "ckpt"); -+ } -+ } -+ return SQLITE_OK; -+} -+#endif /* SQLITE_HDR_CHECK */ -+ - /* - ** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single - ** (MemPage*) as an argument. The (MemPage*) must not be NULL. -@@ -70094,6 +70716,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){ - pPage->intKeyLeaf = 0; - pPage->xCellSize = cellSizePtr; - pPage->xParseCell = btreeParseCellPtrIndex; -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_CORRUPT, -+ "database corruption at page[%u], flagByte[%x], isInit[%u], intKey[%u], intKeyLeaf[%u], leaf[%u], " -+ "childPtrSize[%u], cellOffset[%u], nCell[%u], hdrOffset[%u], minLocal[%u], maxLocal[%u], last ckpt time[%lld]", -+ pPage->pgno, flagByte, pPage->isInit, pPage->intKey, pPage->intKeyLeaf, pPage->leaf, -+ pPage->childPtrSize, pPage->cellOffset, pPage->nCell, pPage->hdrOffset, pPage->minLocal, pPage->maxLocal, g_lastCkptTime); -+#endif /* LOG_DUMP */ - return SQLITE_CORRUPT_PAGE(pPage); - } - pPage->max1bytePayload = pBt->max1bytePayload; -@@ -79481,6 +80110,13 @@ static int backupOnePage( +@@ -81473,6 +81753,13 @@ static int backupOnePage( int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; @@ -1198,9 +579,9 @@ index 3469f92..a935685 100644 int rc = SQLITE_OK; i64 iOff; -@@ -79497,6 +80133,26 @@ static int backupOnePage( - rc = SQLITE_READONLY; - } +@@ -81483,6 +81770,26 @@ static int backupOnePage( + assert( zSrcData ); + assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 ); +#ifdef SQLITE_HAS_CODEC + /* Backup is not possible if the page size of the destination is changing @@ -1225,7 +606,7 @@ index 3469f92..a935685 100644 /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. -@@ -79992,6 +80648,10 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ +@@ -81981,6 +82288,10 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){ b.pDest = pTo; b.iNext = 1; @@ -1236,7 +617,7 @@ index 3469f92..a935685 100644 /* 0x7FFFFFFF is the hard limit for the number of pages in a database ** file. By passing this as the number of pages to copy to ** sqlite3_backup_step(), we can guarantee that the copy finishes -@@ -88114,6 +88774,15 @@ end_of_step: +@@ -90447,6 +90758,15 @@ end_of_step: return (rc&db->errMask); } @@ -1252,7 +633,7 @@ index 3469f92..a935685 100644 /* ** This is the top-level implementation of sqlite3_step(). Call ** sqlite3Step() to do most of the work. If a schema error occurs, -@@ -88133,6 +88802,13 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ +@@ -90466,6 +90786,13 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ while( (rc = sqlite3Step(v))==SQLITE_SCHEMA && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){ int savedPc = v->pc; @@ -1266,7 +647,7 @@ index 3469f92..a935685 100644 rc = sqlite3Reprepare(v); if( rc!=SQLITE_OK ){ /* This case occurs after failing to recompile an sql statement. -@@ -88154,6 +88830,15 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ +@@ -90487,6 +90814,15 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ } break; } @@ -1282,7 +663,7 @@ index 3469f92..a935685 100644 sqlite3_reset(pStmt); if( savedPc>=0 ){ /* Setting minWriteFileFormat to 254 is a signal to the OP_Init and -@@ -88164,6 +88849,20 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ +@@ -90497,6 +90833,20 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){ } assert( v->expired==0 ); } @@ -1303,15 +684,7 @@ index 3469f92..a935685 100644 sqlite3_mutex_leave(db->mutex); return rc; } -@@ -90192,7 +90891,6 @@ static u16 numericType(Mem *pMem){ - testcase( pMem->flags & MEM_Str ); - testcase( pMem->flags & MEM_Blob ); - return computeNumericType(pMem); -- return 0; - } - - #ifdef SQLITE_DEBUG -@@ -90351,6 +91049,61 @@ static int checkSavepointCount(sqlite3 *db){ +@@ -92944,6 +93294,61 @@ static int checkSavepointCount(sqlite3 *db){ } #endif @@ -1373,7 +746,7 @@ index 3469f92..a935685 100644 /* ** Return the register of pOp->p2 after first preparing it to be ** overwritten with an integer value. -@@ -90852,6 +91605,12 @@ case OP_Halt: { +@@ -93542,6 +93947,12 @@ case OP_Halt: { VdbeFrame *pFrame; int pcx; @@ -1386,7 +759,7 @@ index 3469f92..a935685 100644 #ifdef SQLITE_DEBUG if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); } #endif -@@ -91295,6 +92054,43 @@ case OP_ResultRow: { +@@ -93991,6 +94402,43 @@ case OP_ResultRow: { assert( pOp->p1>0 || CORRUPT_DB ); assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 ); @@ -1428,9 +801,9 @@ index 3469f92..a935685 100644 +#endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */ + p->cacheCtr = (p->cacheCtr + 2)|1; - p->pResultSet = &aMem[pOp->p1]; + p->pResultRow = &aMem[pOp->p1]; #ifdef SQLITE_DEBUG -@@ -92091,6 +92887,17 @@ case OP_Compare: { +@@ -94793,6 +95241,17 @@ case OP_Compare: { ** This opcode must immediately follow an OP_Compare opcode. */ case OP_Jump: { /* jump */ @@ -1446,9 +819,9 @@ index 3469f92..a935685 100644 + } +#endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */ assert( pOp>aOp && pOp[-1].opcode==OP_Compare ); + assert( iCompareIsInit ); if( iCompare<0 ){ - VdbeBranchTaken(0,4); pOp = &aOp[pOp->p1 - 1]; -@@ -97106,6 +97913,20 @@ case OP_IfNotZero: { /* jump, in1 */ +@@ -99867,6 +100326,20 @@ case OP_IfNotZero: { /* jump, in1 */ ** and jump to P2 if the new value is exactly zero. */ case OP_DecrJumpZero: { /* jump, in1 */ @@ -1469,7 +842,7 @@ index 3469f92..a935685 100644 pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); if( pIn1->u.i>SMALLEST_INT64 ) pIn1->u.i--; -@@ -116053,6 +116874,40 @@ static void attachFunc( +@@ -119592,6 +120065,40 @@ static void attachFunc( if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -1510,7 +883,7 @@ index 3469f92..a935685 100644 sqlite3_free_filename( zPath ); /* If the file was opened successfully, read the schema for the new database. -@@ -117624,8 +118479,24 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char +@@ -121163,8 +121670,24 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0); @@ -1535,7 +908,7 @@ index 3469f92..a935685 100644 } /* -@@ -125898,10 +126769,16 @@ static void groupConcatValue(sqlite3_context *context){ +@@ -129771,10 +130294,16 @@ static void groupConcatValue(sqlite3_context *context){ */ SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){ int rc = sqlite3_overload_function(db, "MATCH", 2); @@ -1552,10 +925,10 @@ index 3469f92..a935685 100644 } /* -@@ -131535,6 +132412,10 @@ struct sqlite3_api_routines { - const char *(*db_name)(sqlite3*,int); - /* Version 3.40.0 and later */ - int (*value_encoding)(sqlite3_value*); +@@ -135492,6 +136021,10 @@ struct sqlite3_api_routines { + /* Version 3.44.0 and later */ + void *(*get_clientdata)(sqlite3*,const char*); + int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); +#ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK + /* handle after drop table done */ + int (*set_droptable_handle)(sqlite3*,void(*)(sqlite3*,const char*,const char*)); @@ -1563,10 +936,10 @@ index 3469f92..a935685 100644 }; /* -@@ -131861,6 +132742,10 @@ typedef int (*sqlite3_loadext_entry)( - #define sqlite3_db_name sqlite3_api->db_name - /* Version 3.40.0 and later */ - #define sqlite3_value_encoding sqlite3_api->value_encoding +@@ -135825,6 +136358,10 @@ typedef int (*sqlite3_loadext_entry)( + /* Version 3.44.0 and later */ + #define sqlite3_get_clientdata sqlite3_api->get_clientdata + #define sqlite3_set_clientdata sqlite3_api->set_clientdata +#ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK +/* handle after drop table done */ +#define sqlite3_set_droptable_handle sqlite3_api->set_droptable_handle @@ -1574,7 +947,7 @@ index 3469f92..a935685 100644 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) -@@ -131884,7 +132769,7 @@ typedef int (*sqlite3_loadext_entry)( +@@ -135848,7 +136385,7 @@ typedef int (*sqlite3_loadext_entry)( /************** Continuing where we left off in loadext.c ********************/ /* #include "sqliteInt.h" */ @@ -1583,7 +956,7 @@ index 3469f92..a935685 100644 /* ** Some API routines are omitted when various features are ** excluded from a build of SQLite. Substitute a NULL pointer -@@ -131898,7 +132783,9 @@ typedef int (*sqlite3_loadext_entry)( +@@ -135862,7 +136399,9 @@ typedef int (*sqlite3_loadext_entry)( # define sqlite3_column_origin_name 0 # define sqlite3_column_origin_name16 0 #endif @@ -1593,7 +966,7 @@ index 3469f92..a935685 100644 #ifdef SQLITE_OMIT_AUTHORIZATION # define sqlite3_set_authorizer 0 #endif -@@ -131979,6 +132866,7 @@ typedef int (*sqlite3_loadext_entry)( +@@ -135943,6 +136482,7 @@ typedef int (*sqlite3_loadext_entry)( #if defined(SQLITE_OMIT_TRACE) # define sqlite3_trace_v2 0 #endif @@ -1601,7 +974,7 @@ index 3469f92..a935685 100644 /* ** The following structure contains pointers to all SQLite API routines. -@@ -131995,6 +132883,7 @@ typedef int (*sqlite3_loadext_entry)( +@@ -135959,6 +136499,7 @@ typedef int (*sqlite3_loadext_entry)( ** also check to make sure that the pointer to the function is ** not NULL before calling it. */ @@ -1609,7 +982,7 @@ index 3469f92..a935685 100644 static const sqlite3_api_routines sqlite3Apis = { sqlite3_aggregate_context, #ifndef SQLITE_OMIT_DEPRECATED -@@ -132264,7 +133153,11 @@ static const sqlite3_api_routines sqlite3Apis = { +@@ -136228,7 +136769,11 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_bind_blob64, sqlite3_bind_text64, sqlite3_cancel_auto_extension, @@ -1621,19 +994,19 @@ index 3469f92..a935685 100644 sqlite3_malloc64, sqlite3_msize, sqlite3_realloc64, -@@ -132375,7 +133268,10 @@ static const sqlite3_api_routines sqlite3Apis = { - #endif - sqlite3_db_name, - /* Version 3.40.0 and later */ -- sqlite3_value_encoding -+ sqlite3_value_encoding, +@@ -136346,7 +136891,10 @@ static const sqlite3_api_routines sqlite3Apis = { + sqlite3_stmt_explain, + /* Version 3.44.0 and later */ + sqlite3_get_clientdata, +- sqlite3_set_clientdata ++ sqlite3_set_clientdata, +#ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK + sqlite3_set_droptable_handle +#endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */ }; /* True if x is the directory separator character -@@ -132386,6 +133282,9 @@ static const sqlite3_api_routines sqlite3Apis = { +@@ -136357,6 +136905,9 @@ static const sqlite3_api_routines sqlite3Apis = { # define DirSep(X) ((X)=='/') #endif @@ -1643,7 +1016,7 @@ index 3469f92..a935685 100644 /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a -@@ -132846,6 +133745,9 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ +@@ -136836,6 +137387,9 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #define PragTyp_WAL_CHECKPOINT 43 #define PragTyp_LOCK_STATUS 44 #define PragTyp_STATS 45 @@ -1653,7 +1026,7 @@ index 3469f92..a935685 100644 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ -@@ -133136,6 +134038,18 @@ static const PragmaName aPragmaName[] = { +@@ -137126,6 +137680,18 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_Result0, /* ColNames: */ 0, 0, /* iArg: */ 0 }, @@ -1672,7 +1045,7 @@ index 3469f92..a935685 100644 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) #if !defined(SQLITE_OMIT_CHECK) {/* zName: */ "ignore_check_constraints", -@@ -133188,6 +134102,13 @@ static const PragmaName aPragmaName[] = { +@@ -137178,6 +137744,13 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif @@ -1686,7 +1059,7 @@ index 3469f92..a935685 100644 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "legacy_alter_table", /* ePragTyp: */ PragTyp_FLAG, -@@ -133295,6 +134216,15 @@ static const PragmaName aPragmaName[] = { +@@ -137285,6 +137858,15 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_RecTriggers }, @@ -1702,7 +1075,7 @@ index 3469f92..a935685 100644 {/* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, -@@ -133383,6 +134313,18 @@ static const PragmaName aPragmaName[] = { +@@ -137373,6 +137955,18 @@ static const PragmaName aPragmaName[] = { /* ePragFlg: */ PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ 0 }, @@ -1721,7 +1094,7 @@ index 3469f92..a935685 100644 #endif {/* zName: */ "threads", /* ePragTyp: */ PragTyp_THREADS, -@@ -133824,6 +134766,10 @@ SQLITE_PRIVATE void sqlite3Pragma( +@@ -137814,6 +138408,10 @@ SQLITE_PRIVATE void sqlite3Pragma( Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */ const PragmaName *pPragma; /* The pragma */ @@ -1732,7 +1105,7 @@ index 3469f92..a935685 100644 if( v==0 ) return; sqlite3VdbeRunOnlyOnce(v); pParse->nMem = 2; -@@ -133893,6 +134839,13 @@ SQLITE_PRIVATE void sqlite3Pragma( +@@ -137883,6 +138481,14 @@ SQLITE_PRIVATE void sqlite3Pragma( goto pragma_out; } @@ -1741,12 +1114,13 @@ index 3469f92..a935685 100644 + /* sqlite3CodecPragma executes internal */ + goto pragma_out; + } -+#endif /* SQLITE_HAS_CODEC */ ++#endif ++ /* END CODEC */ + /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if( pPragma==0 ){ -@@ -135935,6 +136888,48 @@ SQLITE_PRIVATE void sqlite3Pragma( +@@ -140023,6 +140629,48 @@ SQLITE_PRIVATE void sqlite3Pragma( } #endif @@ -1795,7 +1169,7 @@ index 3469f92..a935685 100644 #if defined(SQLITE_ENABLE_CEROD) case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){ if( sqlite3StrNICmp(zRight, "cerod-", 6)==0 ){ -@@ -138460,9 +139455,25 @@ static void selectInnerLoop( +@@ -142579,9 +143227,25 @@ static void selectInnerLoop( assert( p->pEList!=0 ); hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP; if( pSort && pSort->pOrderBy==0 ) pSort = 0; @@ -1821,7 +1195,7 @@ index 3469f92..a935685 100644 } /* Pull the requested columns. -@@ -138591,6 +139602,16 @@ static void selectInnerLoop( +@@ -142710,6 +143374,16 @@ static void selectInnerLoop( fixDistinctOpenEph(pParse, eType, iTab, pDistinct->addrTnct); if( pSort==0 ){ codeOffset(v, p->iOffset, iContinue); @@ -1838,7 +1212,7 @@ index 3469f92..a935685 100644 } } -@@ -139037,11 +140058,23 @@ static void generateSortTail( +@@ -143173,11 +143847,23 @@ static void generateSortTail( addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); VdbeCoverage(v); assert( p->iLimit==0 && p->iOffset==0 ); @@ -1862,7 +1236,7 @@ index 3469f92..a935685 100644 iSortTab = iTab; bSeq = 1; if( p->iOffset>0 ){ -@@ -148844,6 +149877,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( +@@ -153658,6 +154344,17 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum( } nRes = sqlite3BtreeGetRequestedReserve(pMain); @@ -1880,7 +1254,15 @@ index 3469f92..a935685 100644 sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL); -@@ -173422,6 +174466,29 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ +@@ -177753,6 +178450,7 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db); + /************** End of rtree.h ***********************************************/ + /************** Continuing where we left off in main.c ***********************/ + #endif ++#include "sqlite3tokenizer.h" + #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) + /************** Include sqliteicu.h in the middle of main.c ******************/ + /************** Begin file sqliteicu.h ***************************************/ +@@ -178752,6 +179450,29 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ rc = setupLookaside(db, pBuf, sz, cnt); break; } @@ -1910,7 +1292,7 @@ index 3469f92..a935685 100644 default: { static const struct { int op; /* The opcode */ -@@ -175642,7 +176709,41 @@ static const char *uriParameter(const char *zFilename, const char *zParam){ +@@ -181006,7 +181727,41 @@ static const char *uriParameter(const char *zFilename, const char *zParam){ return 0; } @@ -1953,7 +1335,7 @@ index 3469f92..a935685 100644 /* ** This routine does the work of opening a database on behalf of -@@ -175987,6 +177088,12 @@ opendb_out: +@@ -181354,6 +182109,12 @@ opendb_out: }else if( rc!=SQLITE_OK ){ db->eOpenState = SQLITE_STATE_SICK; } @@ -1966,7 +1348,7 @@ index 3469f92..a935685 100644 *ppDb = db; #ifdef SQLITE_ENABLE_SQLLOG if( sqlite3GlobalConfig.xSqllog ){ -@@ -175995,6 +177102,14 @@ opendb_out: +@@ -181362,6 +182123,14 @@ opendb_out: sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0); } #endif @@ -1981,34 +1363,16 @@ index 3469f92..a935685 100644 sqlite3_free_filename(zOpen); return rc; } -@@ -217581,15 +218696,19 @@ static int sessionReadRecord( +@@ -234858,7 +235627,7 @@ static void sqlite3Fts5ParseSetDistance( + ); + return; } +- nNear = nNear * 10 + (p->p[i] - '0'); ++ if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0'); } - if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ -- sqlite3_int64 v = sessionGetI64(aVal); -- if( eType==SQLITE_INTEGER ){ -- sqlite3VdbeMemSetInt64(apOut[i], v); -+ if( (pIn->nData-pIn->iNext)<8 ){ -+ rc = SQLITE_CORRUPT_BKPT; - }else{ -- double d; -- memcpy(&d, &v, 8); -- sqlite3VdbeMemSetDouble(apOut[i], d); -+ sqlite3_int64 v = sessionGetI64(aVal); -+ if( eType==SQLITE_INTEGER ){ -+ sqlite3VdbeMemSetInt64(apOut[i], v); -+ }else{ -+ double d; -+ memcpy(&d, &v, 8); -+ sqlite3VdbeMemSetDouble(apOut[i], d); -+ } -+ pIn->iNext += 8; - } -- pIn->iNext += 8; - } - } - } -@@ -243614,3 +244733,1667 @@ SQLITE_API int sqlite3_stmt_init( + }else{ + nNear = FTS5_DEFAULT_NEARDIST; +@@ -252924,3 +253693,1659 @@ SQLITE_API int sqlite3_stmt_init( /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /************************** End of sqlite3.c ******************************/ @@ -3034,7 +2398,7 @@ index 3469f92..a935685 100644 + if(sqlite3CodecCheckHmac(keyCtx, pgno, inputBuffer.bufferSize + keyCtx->codecConst.initVectorSize, input, input + inputBuffer.bufferSize + keyCtx->codecConst.initVectorSize)){ + sqlite3_log(SQLITE_ERROR, "codec: check hmac error at page %d, hmac %d, kdf %d, pageSize %d, iter %d.", + pgno, keyCtx->codecConst.hmacAlgo, keyCtx->codecConst.kdfAlgo, keyCtx->codecConst.cipherPageSize, keyCtx->iter); -+ return SQLITE_ERROR; ++ return pgno == 1 ? SQLITE_NOTADB : SQLITE_CORRUPT; + } + unsigned char *initVector = input + inputBuffer.bufferSize; + void *cipherCtx = opensslGetCtx(keyCtx->codecConst.cipher, CODEC_OPERATION_DECRYPT, keyCtx->key, initVector); @@ -3057,7 +2421,7 @@ index 3469f92..a935685 100644 + int rc = SQLITE_OK; + errno_t memcpyRc = EOK; + if(ctx == NULL || data == NULL){ -+ return pData; ++ return NULL; + } + if(pgno == 1){ + offset = FILE_HEADER_SIZE; @@ -3071,12 +2435,13 @@ index 3469f92..a935685 100644 + memcpyRc = memcpy_s(pCtx->buffer, cipherPageSize, SQLITE_FILE_HEADER, FILE_HEADER_SIZE); + if(memcpyRc != EOK){ + sqlite3CodecSetError(pCtx, SQLITE_ERROR); -+ return pData; ++ return NULL; + } + } + rc = sqlite3CodecDecryptData(pCtx, OPERATE_CONTEXT_READ, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); + if(rc != SQLITE_OK){ + sqlite3CodecSetError(pCtx, rc); ++ return NULL; + } + (void)memcpy_s(pData, cipherPageSize, pCtx->buffer, cipherPageSize); + return pData; @@ -3086,13 +2451,13 @@ index 3469f92..a935685 100644 + memcpyRc = memcpy_s(pCtx->buffer, cipherPageSize, pCtx->salt, FILE_HEADER_SIZE); + if(memcpyRc != EOK){ + sqlite3CodecSetError(pCtx, SQLITE_ERROR); -+ return pData; ++ return NULL; + } + } + rc = sqlite3CodecEncryptData(pCtx, OPERATE_CONTEXT_WRITE, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); + if(rc != SQLITE_OK){ + sqlite3CodecSetError(pCtx, rc); -+ return pData; ++ return NULL; + } + return pCtx->buffer; + break; @@ -3101,18 +2466,18 @@ index 3469f92..a935685 100644 + memcpyRc = memcpy_s(pCtx->buffer, cipherPageSize, pCtx->salt, FILE_HEADER_SIZE); + if(memcpyRc != EOK){ + sqlite3CodecSetError(pCtx, SQLITE_ERROR); -+ return pData; ++ return NULL; + } + } + rc = sqlite3CodecEncryptData(pCtx, OPERATE_CONTEXT_READ, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); + if(rc != SQLITE_OK){ + sqlite3CodecSetError(pCtx, rc); -+ return pData; ++ return NULL; + } + return pCtx->buffer; + break; + default: -+ return pData; ++ return NULL; + break; + } +} @@ -3633,19 +2998,10 @@ index 3469f92..a935685 100644 + return; +} +/************** End file hw_codec.c *****************************************/ -+#endif /* SQLITE_HAS_CODEC */ -+ -+ -+#ifdef SQLITE_EXPORT_SYMBOLS -+/************** Begin hw export the symbols *****************************************/ -+#if defined(__GNUC__) -+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) -+#elif defined(_MSC_VER) -+# define EXPORT_SYMBOLS __declspec(dllexport) -+#else -+# define EXPORT_SYMBOLS +#endif + ++// hw export the symbols ++#ifdef SQLITE_EXPORT_SYMBOLS +struct sqlite3_api_routines_hw { + int (*initialize)(); + int (*config)(int,...); @@ -3677,5 +3033,5 @@ index 3469f92..a935685 100644 +/************** End hw export the symbols *****************************************/ +#endif /* SQLITE_EXPORT_SYMBOLS */ -- -2.47.0.windows.2 +2.34.1 diff --git a/patch/0004-Enable-and-optimize-ICU.patch b/patch/0002-Enable-and-optimize-ICU.patch similarity index 95% rename from patch/0004-Enable-and-optimize-ICU.patch rename to patch/0002-Enable-and-optimize-ICU.patch index 5f7fcc22ca7d9bd68f85abd9c32d88fcbe10a4d1..15c11154ae5736be81b095e12ce23fa4cd68f657 100644 --- a/patch/0004-Enable-and-optimize-ICU.patch +++ b/patch/0002-Enable-and-optimize-ICU.patch @@ -1,55 +1,44 @@ -From 289181f378cd66cd3fcb963912eca7a984d68db4 Mon Sep 17 00:00:00 2001 +From 57b5e5dc8b3a7f622e9666c20ec2a7f78997bf3c Mon Sep 17 00:00:00 2001 From: wanghaishuo -Date: Sat, 12 Apr 2025 10:09:45 +0800 -Subject: [PATCH 4/7] Enable and optimize ICU +Date: Mon, 12 May 2025 15:24:45 +0800 +Subject: [PATCH 2/6] Enable and optimize ICU -Signed-off-by: ryne3366 +Signed-off-by: wanghaishuo --- - src/sqlite3.c | 1033 ++++------------------------------------------ - src/sqlite3icu.c | 892 +++++++++++++++++++++++++++++++++++++++ - 2 files changed, 976 insertions(+), 949 deletions(-) + src/sqlite3.c | 1013 ++++------------------------------------------ + src/sqlite3icu.c | 888 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 962 insertions(+), 939 deletions(-) create mode 100644 src/sqlite3icu.c diff --git a/src/sqlite3.c b/src/sqlite3.c -index ddfc377..a25d540 100644 +index ad78006..8d8b8ae 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -2459,7 +2459,7 @@ struct sqlite3_mem_methods { - #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ +@@ -2502,6 +2502,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ -- -+#define SQLITE_CONFIG_ENABLE_ICU 31 /* boolean */ + #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ ++#define SQLITE_CONFIG_ENABLE_ICU 41 /* boolean */ + /* ** CAPI3REF: Database Connection Configuration Options - ** -@@ -3193,6 +3193,17 @@ SQLITE_API int sqlite3_get_table( - ); - SQLITE_API void sqlite3_free_table(char **result); - -+// hw export the symbols -+#ifdef SQLITE_EXPORT_SYMBOLS -+#if defined(__GNUC__) -+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) -+#elif defined(_MSC_VER) -+# define EXPORT_SYMBOLS __declspec(dllexport) -+#else -+# define EXPORT_SYMBOLS -+#endif -+#endif // SQLITE_EXPORT_SYMBOLS +@@ -3289,6 +3290,7 @@ SQLITE_API void sqlite3_free_table(char **result); + # define EXPORT_SYMBOLS + #endif + #endif + /* ** CAPI3REF: Formatted String Printing Functions ** -@@ -173713,6 +173724,7 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db); - /************** End of rtree.h ***********************************************/ +@@ -178413,6 +178415,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db); + /************** End of fts3.h ************************************************/ /************** Continuing where we left off in main.c ***********************/ #endif -+#include "sqlite3tokenizer.h" - #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) - /************** Include sqliteicu.h in the middle of main.c ******************/ - /************** Begin file sqliteicu.h ***************************************/ -@@ -173737,13 +173749,54 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db); ++ + #ifdef SQLITE_ENABLE_RTREE + /************** Include rtree.h in the middle of main.c **********************/ + /************** Begin file rtree.h *******************************************/ +@@ -178475,13 +178478,54 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db); extern "C" { #endif /* __cplusplus */ @@ -105,7 +94,7 @@ index ddfc377..a25d540 100644 /************** Continuing where we left off in main.c ***********************/ #endif -@@ -173793,7 +173846,7 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { +@@ -178521,7 +178565,7 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { sqlite3Fts5Init, #endif #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS) @@ -114,7 +103,7 @@ index ddfc377..a25d540 100644 #endif #ifdef SQLITE_ENABLE_RTREE sqlite3RtreeInit, -@@ -174150,12 +174203,24 @@ SQLITE_API int sqlite3_shutdown(void){ +@@ -178913,6 +178957,19 @@ SQLITE_API int sqlite3_shutdown(void){ SQLITE_API int sqlite3_config(int op, ...){ va_list ap; int rc = SQLITE_OK; @@ -132,15 +121,17 @@ index ddfc377..a25d540 100644 + } +#endif /* SQLITE_ENABLE_ICU */ - /* sqlite3_config() shall return SQLITE_MISUSE if it is invoked while - ** the SQLite library is in use. */ - if( sqlite3GlobalConfig.isInit ) return SQLITE_MISUSE_BKPT; + /* sqlite3_config() normally returns SQLITE_MISUSE if it is invoked while + ** the SQLite library is in use. Except, a few selected opcodes +@@ -178930,7 +178987,6 @@ SQLITE_API int sqlite3_config(int op, ...){ + testcase( op==SQLITE_CONFIG_PCACHE_HDRSZ ); + } - va_start(ap, op); switch( op ){ /* Mutex configuration options are only available in a threadsafe -@@ -177223,6 +177288,12 @@ static int openDatabase( +@@ -182053,6 +182109,12 @@ static int openDatabase( sqlite3RegisterPerConnectionBuiltinFunctions(db); rc = sqlite3_errcode(db); @@ -153,7 +144,7 @@ index ddfc377..a25d540 100644 /* Load compiled-in extensions */ for(i=0; rc==SQLITE_OK && i hash, "unicode61", 10, (void *)pUnicode) #endif #ifdef SQLITE_ENABLE_ICU @@ -303,7 +294,7 @@ index ddfc377..a25d540 100644 #endif ){ rc = SQLITE_NOMEM; -@@ -207668,829 +207635,6 @@ SQLITE_API int sqlite3_rtree_init( +@@ -213799,829 +213757,6 @@ SQLITE_API int sqlite3_rtree_init( #endif /************** End of rtree.c ***********************************************/ @@ -1133,28 +1124,12 @@ index ddfc377..a25d540 100644 /************** Begin file sqlite3rbu.c **************************************/ /* ** 2014 August 30 -@@ -247837,15 +246981,6 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime - #endif - // hw export the symbols - #ifdef SQLITE_EXPORT_SYMBOLS --/************** Begin hw export the symbols *****************************************/ --#if defined(__GNUC__) --# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) --#elif defined(_MSC_VER) --# define EXPORT_SYMBOLS __declspec(dllexport) --#else --# define EXPORT_SYMBOLS --#endif -- - struct sqlite3_api_routines_hw { - int (*initialize)(); - int (*config)(int,...); diff --git a/src/sqlite3icu.c b/src/sqlite3icu.c new file mode 100644 -index 0000000..6a581fc +index 0000000..b5944d5 --- /dev/null +++ b/src/sqlite3icu.c -@@ -0,0 +1,892 @@ +@@ -0,0 +1,888 @@ +/****************************************************************************** +** This file is an amalgamation of many separate C source files from SQLite +** version 3.40.1. By combining all the individual C code files into this @@ -1217,10 +1192,6 @@ index 0000000..6a581fc +#include "sqlite3icu.h" +#include "sqlite3.h" + -+#ifdef HARMONY_OS -+#include "common/unicode/putil.h" -+#endif -+ +#if !defined(SQLITE_CORE) \ + || defined(SQLITE_ENABLE_ICU) \ + || defined(SQLITE_ENABLE_ICU_COLLATIONS) @@ -2047,6 +2018,7 @@ index 0000000..6a581fc +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ + +/************** End of fts3_icu.c ********************************************/ +\ No newline at end of file -- -2.47.0.windows.2 +2.34.1 diff --git a/patch/0002-busy-debug.patch b/patch/0003-Busy-debug-and-log-dump.patch similarity index 48% rename from patch/0002-busy-debug.patch rename to patch/0003-Busy-debug-and-log-dump.patch index a4366e89a7c31444641128b3276f3107fecf1226..15d6408869b888269a7e1e88f1c060256f2fb5f1 100644 --- a/patch/0002-busy-debug.patch +++ b/patch/0003-Busy-debug-and-log-dump.patch @@ -1,18 +1,49 @@ -From 7e94a0e2ef2ba3eed067ece5f0df3cd0ac5b693e Mon Sep 17 00:00:00 2001 +From c56ae11e3fe85ab6fa71c9d49ac37a433b01fb4b Mon Sep 17 00:00:00 2001 From: wanghaishuo -Date: Fri, 11 Apr 2025 19:47:08 +0800 -Subject: [PATCH 2/7] busy debug +Date: Mon, 12 May 2025 15:31:54 +0800 +Subject: [PATCH 3/6] Busy debug and log dump - Signed-off-by: ryne3366 +Signed-off-by: wanghaishuo --- - src/sqlite3.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 366 insertions(+), 11 deletions(-) + src/sqlite3.c | 745 +++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 731 insertions(+), 14 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c -index a935685..237d37e 100644 +index 8d8b8ae..574a76a 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -36790,6 +36790,45 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ +@@ -879,6 +879,7 @@ SQLITE_API int sqlite3_exec( + #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) + #define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) + #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) ++#define SQLITE_WARNING_DUMP (SQLITE_WARNING | (2<<8)) + #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) + #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) + #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ +@@ -21831,6 +21832,10 @@ SQLITE_API extern int sqlite3_open_file_count; + #define OpenCounter(X) + #endif /* defined(SQLITE_TEST) */ + ++#ifdef FDSAN_ENABLE ++#define SQLITE_FDSAN_TAG 5351 ++#endif /* FDSAN_ENABLE */ ++ + #endif /* !defined(_OS_COMMON_H_) */ + + /************** End of os_common.h *******************************************/ +@@ -32201,7 +32206,11 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ + */ + static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ + StrAccum acc; /* String accumulator */ ++#ifndef LOG_DUMP + char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ ++#else ++ char zMsg[SQLITE_PRINT_BUF_SIZE*10]; /* Complete log message */ ++#endif /* !LOG_DUMP */ + + sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); + sqlite3_str_vappendf(&acc, zFormat, ap); +@@ -38026,6 +38035,45 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ #endif /************** End of os_kv.c ***********************************************/ @@ -58,7 +89,105 @@ index a935685..237d37e 100644 /************** Begin file os_unix.c *****************************************/ /* ** 2004 May 22 -@@ -38564,6 +38603,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -38425,6 +38473,29 @@ static pid_t randomnessPid = 0; + #define F2FS_FEATURE_ATOMIC_WRITE 0x0004 + #endif /* __linux__ */ + ++#ifdef HARMONY_OS ++#define HMFS_MONITOR_FL 0x00000002 ++#define HMFS_IOCTL_HW_GET_FLAGS _IOR(0xf5, 70, unsigned int) ++#define HMFS_IOCTL_HW_SET_FLAGS _IOR(0xf5, 71, unsigned int) ++ ++static void enableDbFileDelMonitor(int32_t fd) ++{ ++ unsigned int flags = 0; ++ int ret = ioctl(fd, HMFS_IOCTL_HW_GET_FLAGS, &flags); ++ if (ret < 0) { ++ return; ++ } ++ if (flags & HMFS_MONITOR_FL) { ++ return; ++ } ++ flags |= HMFS_MONITOR_FL; ++ ret = ioctl(fd, HMFS_IOCTL_HW_SET_FLAGS, &flags); ++ if (ret < 0) { ++ sqlite3_log(SQLITE_WARNING, "Fd %d enable del monitor go wrong, errno = %d", fd, errno); ++ } ++} ++ ++#endif /* HARMONY_OS */ + + /* + ** Different Unix systems declare open() in different ways. Same use +@@ -38435,7 +38506,29 @@ static pid_t randomnessPid = 0; + ** which always has the same well-defined interface. + */ + static int posixOpen(const char *zFile, int flags, int mode){ +- return open(zFile, flags, mode); ++ int fd = open(zFile, flags, mode); ++#ifdef FDSAN_ENABLE ++ if( fd >= 0 ){ ++ fdsan_exchange_owner_tag(fd, 0, fdsan_create_owner_tag(FDSAN_OWNER_TYPE_FILE, SQLITE_FDSAN_TAG)); ++ } ++#endif /* FDSAN_ENABLE */ ++#ifdef HARMONY_OS ++ if( fd >= 0 ){ ++ enableDbFileDelMonitor(fd); ++ } ++#endif /* HARMONY_OS */ ++ return fd; ++} ++ ++/* ++** Change close to posixClose, use fdsan_close_with_tag when fdsan enable. ++*/ ++static int posixClose(int fd) { ++#ifdef FDSAN_ENABLE ++ return fdsan_close_with_tag(fd, fdsan_create_owner_tag(FDSAN_OWNER_TYPE_FILE, SQLITE_FDSAN_TAG)); ++#else ++ return close(fd); ++#endif /* FDSAN_ENABLE */ + } + + /* Forward reference */ +@@ -38456,7 +38549,7 @@ static struct unix_syscall { + { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, + #define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) + +- { "close", (sqlite3_syscall_ptr)close, 0 }, ++ { "close", (sqlite3_syscall_ptr)posixClose, 0 }, + #define osClose ((int(*)(int))aSyscall[1].pCurrent) + + { "access", (sqlite3_syscall_ptr)access, 0 }, +@@ -39456,6 +39549,9 @@ static int findInodeInfo( + #if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) + if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; + #endif ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR, "findInodeInfo-osFstat, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR; + } + +@@ -39474,11 +39570,17 @@ static int findInodeInfo( + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); + if( rc!=1 ){ + storeLastErrno(pFile, errno); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR, "findInodeInfo-osWrite, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR; + } + rc = osFstat(fd, &statbuf); + if( rc!=0 ){ + storeLastErrno(pFile, errno); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR, "findInodeInfo-msdos-osFstat, fd[%d], errno[%d], osFstat[%d]", fd, errno, rc); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR; + } + } +@@ -39791,6 +39893,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( pFile->eFileLock>=eFileLock ){ OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, azFileLock(eFileLock))); @@ -66,7 +195,7 @@ index a935685..237d37e 100644 return SQLITE_OK; } -@@ -38588,6 +38628,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -39815,6 +39918,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) ){ rc = SQLITE_BUSY; @@ -74,7 +203,7 @@ index a935685..237d37e 100644 goto end_lock; } -@@ -38603,6 +38644,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -39830,6 +39934,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ pFile->eFileLock = SHARED_LOCK; pInode->nShared++; pInode->nLock++; @@ -82,20 +211,15 @@ index a935685..237d37e 100644 goto end_lock; } -@@ -38624,11 +38666,11 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -39851,6 +39956,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ if( rc!=SQLITE_BUSY ){ storeLastErrno(pFile, tErrno); } + MarkLockStatusByRc(rc, TRX_LOCK_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); goto end_lock; - } - } - -- - /* If control gets to this point, then actually go ahead and make - ** operating system calls for the specified lock. - */ -@@ -38644,7 +38686,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ + }else if( eFileLock==EXCLUSIVE_LOCK ){ + pFile->eFileLock = PENDING_LOCK; +@@ -39874,7 +39980,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } @@ -104,7 +228,7 @@ index a935685..237d37e 100644 /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; -@@ -38669,6 +38711,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -39899,6 +40005,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; @@ -112,7 +236,7 @@ index a935685..237d37e 100644 }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file -@@ -38693,6 +38736,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ +@@ -39923,6 +40030,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ storeLastErrno(pFile, tErrno); } } @@ -120,15 +244,114 @@ index a935685..237d37e 100644 } -@@ -38890,7 +38934,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ - pFile->eFileLock = NO_LOCK; +@@ -40640,6 +40748,9 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) { + #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS + return SQLITE_OK; + #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_UNLOCK, "IOERR_UNLOCK fd[%d], eFileLock[%d]", pFile->h, eFileLock); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_UNLOCK; + }else{ + pFile->eFileLock = NO_LOCK; +@@ -41452,8 +41563,14 @@ static int unixRead( + #ifdef EDEVERR + case EDEVERR: + #endif ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_CORRUPTFS, "unixRead-EDEVERR, fd:[%d], amt[%d], got[%d], offset[%lld]", pFile->h, amt, got, offset); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_CORRUPTFS; + } ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_READ, "unixRead-got<0, fd: [%d], amt[%d], got[%d], offset[%lld]", pFile->h, amt, got, offset); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_READ; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ +@@ -41595,9 +41712,19 @@ static int unixWrite( + if( amt>wrote ){ + if( wrote<0 && pFile->lastErrno!=ENOSPC ){ + /* lastErrno set by seekAndWrite */ ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_WRITE, ++ "unixWrite, lastErrno set by seekAndWrite, fd[%d], offset[%lld], wrote[%d], amt[%d], lastErrno[%d]", ++ pFile->h, offset, wrote, amt, pFile->lastErrno); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_WRITE; + }else{ + storeLastErrno(pFile, 0); /* not a system error */ ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_FULL, ++ "unixWrite, not a system error, fd[%d], offset[%lld], wrote[%d], amt[%d], lastErrno[%d]", ++ pFile->h, offset, wrote, amt, pFile->lastErrno); ++#endif /* LOG_DUMP */ + return SQLITE_FULL; + } + } +@@ -41951,7 +42078,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ + do{ + err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); + }while( err==EINTR ); +- if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE; ++ if( err && err!=EINVAL ) { ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_WRITE, ++ "fcntlSizeHint-fallocate, fd[%d], bufSize[%lld], nSize[%lld] err[%d]", ++ pFile->h, buf.st_size, nSize, err); ++#endif /* LOG_DUMP */ ++ return SQLITE_IOERR_WRITE; ++ } + #else + /* If the OS does not have posix_fallocate(), fake it. Write a + ** single byte to the last byte in each block that falls entirely +@@ -41970,7 +42104,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ + for(/*no-op*/; iWrite =nSize ) iWrite = nSize - 1; + nWrite = seekAndWrite(pFile, iWrite, "", 1); +- if( nWrite!=1 ) return SQLITE_IOERR_WRITE; ++ if( nWrite!=1 ) { ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_WRITE, ++ "fcntlSizeHint-seekAndWrite, fd[%d], nWrite[%d], nSize[%d], nBlk[%d], iWrite[%lld]", ++ pFile->h, nWrite, nSize, nBlk, iWrite); ++#endif /* LOG_DUMP */ ++ return SQLITE_IOERR_WRITE; ++ } } + #endif } -- - /* Decrement the count of locks against this same file. When the - ** count reaches zero, close any other file descriptors whose close - ** was deferred because of outstanding locks. -@@ -41187,6 +41230,7 @@ struct unixShmNode { +@@ -42025,14 +42166,29 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ + #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) + case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); ++#ifdef LOG_DUMP ++ if( rc ){ ++ sqlite3_log(SQLITE_IOERR_BEGIN_ATOMIC, "unixFileControl-begin, fd[%d], op[%d]", pFile->h, op); ++ } ++#endif /* LOG_DUMP */ + return rc ? SQLITE_IOERR_BEGIN_ATOMIC : SQLITE_OK; + } + case SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_COMMIT_ATOMIC_WRITE); ++#ifdef LOG_DUMP ++ if( rc ){ ++ sqlite3_log(SQLITE_IOERR_COMMIT_ATOMIC, "unixFileControl-commit, fd[%d], op[%d]", pFile->h, op); ++ } ++#endif /* LOG_DUMP */ + return rc ? SQLITE_IOERR_COMMIT_ATOMIC : SQLITE_OK; + } + case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: { + int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE); ++#ifdef LOG_DUMP ++ if( rc ){ ++ sqlite3_log(SQLITE_IOERR_ROLLBACK_ATOMIC, "unixFileControl-rollback, fd[%d], op[%d]", pFile->h, op); ++ } ++#endif /* LOG_DUMP */ + return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK; + } + #endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */ +@@ -42350,6 +42506,7 @@ struct unixShmNode { int nRef; /* Number of unixShm objects pointing to this */ unixShm *pFirst; /* All unixShm objects pointing to this */ int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */ @@ -136,7 +359,7 @@ index a935685..237d37e 100644 #ifdef SQLITE_DEBUG u8 exclMask; /* Mask of exclusive locks held */ u8 sharedMask; /* Mask of shared locks held */ -@@ -41429,6 +41473,8 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ +@@ -42592,6 +42749,8 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ rc = SQLITE_READONLY_CANTINIT; }else{ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); @@ -145,7 +368,7 @@ index a935685..237d37e 100644 /* The first connection to attach must truncate the -shm file. We ** truncate to 3 bytes (an arbitrary small number, less than the ** -shm header size) rather than 0 as a system debugging aid, to -@@ -41440,11 +41486,15 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ +@@ -42603,11 +42762,15 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ } }else if( lock.l_type==F_WRLCK ){ rc = SQLITE_BUSY; @@ -161,9 +384,45 @@ index a935685..237d37e 100644 } return rc; } -@@ -41829,7 +41879,8 @@ static int unixShmLock( - return SQLITE_IOERR_SHMLOCK; - } +@@ -42720,6 +42883,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ + (sStat.st_mode&0777)); + } + if( pShmNode->hShm<0 ){ ++#ifdef LOG_DUMP ++ int sysno = errno; ++#endif + pShmNode->hShm = robust_open(zShm, O_RDONLY|O_NOFOLLOW, + (sStat.st_mode&0777)); + if( pShmNode->hShm<0 ){ +@@ -42727,6 +42893,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ + goto shm_open_err; + } + pShmNode->isReadonly = 1; ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_WARNING_DUMP, "OpenSharedMemory node set to readonly sysno[%d]", sysno); ++#endif + } + + /* If this process is running as root, make sure that the SHM file +@@ -42978,11 +43147,22 @@ static int unixShmLock( + int *aLock; + + p = pDbFd->pShm; +- if( p==0 ) return SQLITE_IOERR_SHMLOCK; ++ if( p==0 ) { ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShm, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags); ++#endif ++ return SQLITE_IOERR_SHMLOCK; ++ } + pShmNode = p->pShmNode; +- if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; ++ if( NEVER(pShmNode==0) ) { ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShmNode, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags); ++#endif ++ return SQLITE_IOERR_SHMLOCK; ++ } aLock = pShmNode->aLock; - + int *aLockTid = pShmNode->aLockTid; @@ -171,7 +430,7 @@ index a935685..237d37e 100644 assert( pShmNode==pDbFd->pInode->pShmNode ); assert( pShmNode->pInode==pDbFd->pInode ); assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); -@@ -41884,15 +41935,17 @@ static int unixShmLock( +@@ -43037,15 +43217,17 @@ static int unixShmLock( if( rc==SQLITE_OK ){ memset(&aLock[ofst], 0, sizeof(int)*n); } @@ -190,7 +449,7 @@ index a935685..237d37e 100644 } } }else if( flags & SQLITE_SHM_SHARED ){ -@@ -41903,12 +41956,14 @@ static int unixShmLock( +@@ -43056,12 +43238,14 @@ static int unixShmLock( rc = SQLITE_BUSY; }else if( aLock[ofst]==0 ){ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); @@ -206,7 +465,7 @@ index a935685..237d37e 100644 } } }else{ -@@ -41927,14 +41982,17 @@ static int unixShmLock( +@@ -43080,14 +43264,17 @@ static int unixShmLock( ** also update the in-memory values. */ if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); @@ -224,7 +483,270 @@ index a935685..237d37e 100644 } assert( assertLockingArrayOk(pShmNode) ); sqlite3_mutex_leave(pShmNode->pShmMutex); -@@ -59933,6 +59991,7 @@ static int syncJournal(Pager *pPager, int newHdr){ +@@ -44298,6 +44485,9 @@ static int unixOpen( + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + isReadonly = 1; ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_WARNING, "Try open file readonly sysno %d", errno); ++#endif + fd = robust_open(zName, openFlags, openMode); + } + } +@@ -44357,6 +44547,9 @@ static int unixOpen( + if( fstatfs(fd, &fsInfo) == -1 ){ + storeLastErrno(p, errno); + robust_close(p, fd, __LINE__); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_ACCESS, "unixOpen, fd[%d], flags[%d], errno[%d]", fd, errno, flags); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_ACCESS; + } + if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { +@@ -44497,6 +44690,184 @@ static int unixAccess( + return SQLITE_OK; + } + ++#ifdef HARMONY_OS ++/* ++** If the last component of the pathname in z[0]..z[j-1] is something ++** other than ".." then back it out and return true. If the last ++** component is empty or if it is ".." then return false. ++*/ ++static int unixBackupDir(const char *z, int *pJ){ ++ int j = *pJ; ++ int i; ++ if( j<=0 ) return 0; ++ for(i=j-1; i>0 && z[i-1]!='/'; i--){} ++ if( i==0 ) return 0; ++ if( z[i]=='.' && i==j-2 && z[i+1]=='.' ) return 0; ++ *pJ = i-1; ++ return 1; ++} ++ ++/* ++** Convert a relative pathname into a full pathname. Also ++** simplify the pathname as follows: ++** ++** Remove all instances of /./ ++** Remove all isntances of /X/../ for any X ++*/ ++static int mkFullPathname( ++ const char *zPath, /* Input path */ ++ char *zOut, /* Output buffer */ ++ int nOut /* Allocated size of buffer zOut */ ++){ ++ int nPath = sqlite3Strlen30(zPath); ++ int iOff = 0; ++ int i, j; ++ if( zPath[0]!='/' ){ ++ if( osGetcwd(zOut, nOut-2)==0 ){ ++ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); ++ } ++ iOff = sqlite3Strlen30(zOut); ++ zOut[iOff++] = '/'; ++ } ++ if( (iOff+nPath+1)>nOut ){ ++ /* SQLite assumes that xFullPathname() nul-terminates the output buffer ++ ** even if it returns an error. */ ++ zOut[iOff] = '\0'; ++ return SQLITE_CANTOPEN_BKPT; ++ } ++ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath); ++ ++ /* Remove duplicate '/' characters. Except, two // at the beginning ++ ** of a pathname is allowed since this is important on windows. */ ++ for(i=j=1; zOut[i]; i++){ ++ zOut[j++] = zOut[i]; ++ while( zOut[i]=='/' && zOut[i+1]=='/' ) i++; ++ } ++ zOut[j] = 0; ++ ++ assert( zOut[0]=='/' ); ++ for(i=j=0; zOut[i]; i++){ ++ if( zOut[i]=='/' ){ ++ /* Skip over internal "/." directory components */ ++ if( zOut[i+1]=='.' && zOut[i+2]=='/' ){ ++ i += 1; ++ continue; ++ } ++ ++ /* If this is a "/.." directory component then back out the ++ ** previous term of the directory if it is something other than "..". ++ */ ++ if( zOut[i+1]=='.' ++ && zOut[i+2]=='.' ++ && zOut[i+3]=='/' ++ && unixBackupDir(zOut, &j) ++ ){ ++ i += 2; ++ continue; ++ } ++ } ++ if( ALWAYS(j>=0) ) zOut[j] = zOut[i]; ++ j++; ++ } ++ if( NEVER(j==0) ) zOut[j++] = '/'; ++ zOut[j] = 0; ++ return SQLITE_OK; ++} ++ ++/* ++** Turn a relative pathname into a full pathname. The relative path ++** is stored as a nul-terminated string in the buffer pointed to by ++** zPath. ++** ++** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes ++** (in this case, MAX_PATHNAME bytes). The full-path is written to ++** this buffer before returning. ++*/ ++static int unixFullPathname( ++ sqlite3_vfs *pVfs, /* Pointer to vfs object */ ++ const char *zPath, /* Possibly relative input path */ ++ int nOut, /* Size of output buffer in bytes */ ++ char *zOut /* Output buffer */ ++){ ++#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT) ++ return mkFullPathname(zPath, zOut, nOut); ++#else ++ int rc = SQLITE_OK; ++ int nByte; ++ int nLink = 0; /* Number of symbolic links followed so far */ ++ const char *zIn = zPath; /* Input path for each iteration of loop */ ++ char *zDel = 0; ++ ++ assert( pVfs->mxPathname==MAX_PATHNAME ); ++ UNUSED_PARAMETER(pVfs); ++ ++ /* It's odd to simulate an io-error here, but really this is just ++ ** using the io-error infrastructure to test that SQLite handles this ++ ** function failing. This function could fail if, for example, the ++ ** current working directory has been unlinked. ++ */ ++ SimulateIOError( return SQLITE_ERROR ); ++ ++ do { ++ ++ /* Call stat() on path zIn. Set bLink to true if the path is a symbolic ++ ** link, or false otherwise. */ ++ int bLink = 0; ++ struct stat buf; ++ if( osLstat(zIn, &buf)!=0 ){ ++ if( errno!=ENOENT ){ ++ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn); ++ } ++ }else{ ++ bLink = S_ISLNK(buf.st_mode); ++ } ++ ++ if( bLink ){ ++ nLink++; ++ if( zDel==0 ){ ++ zDel = sqlite3_malloc(nOut); ++ if( zDel==0 ) rc = SQLITE_NOMEM_BKPT; ++ }else if( nLink>=SQLITE_MAX_SYMLINK ){ ++ rc = SQLITE_CANTOPEN_BKPT; ++ } ++ ++ if( rc==SQLITE_OK ){ ++ nByte = osReadlink(zIn, zDel, nOut-1); ++ if( nByte<0 ){ ++ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn); ++ }else{ ++ if( zDel[0]!='/' ){ ++ int n; ++ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--); ++ if( nByte+n+1>nOut ){ ++ rc = SQLITE_CANTOPEN_BKPT; ++ }else{ ++ memmove(&zDel[n], zDel, nByte+1); ++ memcpy(zDel, zIn, n); ++ nByte += n; ++ } ++ } ++ zDel[nByte] = '\0'; ++ } ++ } ++ ++ zIn = zDel; ++ } ++ ++ assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' ); ++ if( rc==SQLITE_OK && zIn!=zOut ){ ++ rc = mkFullPathname(zIn, zOut, nOut); ++ } ++ if( bLink==0 ) break; ++ zIn = zOut; ++ }while( rc==SQLITE_OK ); ++ ++ sqlite3_free(zDel); ++ if( rc==SQLITE_OK && nLink ) rc = SQLITE_OK_SYMLINK; ++ return rc; ++#endif /* HAVE_READLINK && HAVE_LSTAT */ ++} ++#else + /* + ** A pathname under construction + */ +@@ -44626,6 +44997,7 @@ static int unixFullPathname( + if( path.nSymlink ) return SQLITE_OK_SYMLINK; + return SQLITE_OK; + } ++#endif + + #ifndef SQLITE_OMIT_LOAD_EXTENSION + /* +@@ -45037,6 +45409,9 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ + if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ + OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", + lPath, errno, osGetpid(0))); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_LOCK, "proxyGetLockPath len[%d], dbLen[%d], i[%d]", len, dbLen, i); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_LOCK; + } + len = strlcat(lPath, "sqliteplocks", maxLen); +@@ -45155,6 +45530,9 @@ static int proxyCreateUnixFile( + case EACCES: + return SQLITE_PERM; + case EIO: ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_LOCK, "proxyCreateUnixFile-EIO, fd[%d]", fd); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_LOCK; /* even though it is the conch */ + default: + return SQLITE_CANTOPEN_BKPT; +@@ -45323,6 +45701,9 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ + struct stat buf; + if( osFstat(conchFile->h, &buf) ){ + storeLastErrno(pFile, errno); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_LOCK, "proxyConchLock pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, errno); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_LOCK; + } + +@@ -45343,6 +45724,9 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); + if( len<0 ){ + storeLastErrno(pFile, errno); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_LOCK, "proxyConchLock tries 2, pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, errno); ++#endif /* LOG_DUMP */ + return SQLITE_IOERR_LOCK; + } + if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ +@@ -45417,6 +45801,9 @@ static int proxyTakeConch(unixFile *pFile){ + if( readLen<0 ){ + /* I/O error: lastErrno set by seekAndRead */ + storeLastErrno(pFile, conchFile->lastErrno); ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_IOERR_READ, "proxyTakeConch pFile fd[%d], conchFile fd[%d], lastErrno[%d]", pFile->h, conchFile->h, conchFile->lastErrno); ++#endif /* LOG_DUMP */ + rc = SQLITE_IOERR_READ; + goto end_takeconch; + }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || +@@ -60983,6 +61370,7 @@ static int syncJournal(Pager *pPager, int newHdr){ assert( !pagerUseWal(pPager) ); rc = sqlite3PagerExclusiveLock(pPager); @@ -232,7 +754,18 @@ index a935685..237d37e 100644 if( rc!=SQLITE_OK ) return rc; if( !pPager->noSync ){ -@@ -60840,6 +60899,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){ +@@ -61682,6 +62070,10 @@ SQLITE_PRIVATE int sqlite3PagerOpen( + if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0 + || sqlite3_uri_boolean(pPager->zFilename, "immutable", 0) ){ + vfsFlags |= SQLITE_OPEN_READONLY; ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_WARNING_DUMP, "Vfsflag set to readonly, readOnly[%d], vfsFlags[%d], sysno[%d]", ++ readOnly, vfsFlags, errno); ++#endif + goto act_like_temp_file; + } + } +@@ -61875,6 +62267,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); } @@ -240,7 +773,7 @@ index a935685..237d37e 100644 sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved -@@ -60929,6 +60989,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ +@@ -61964,6 +62357,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK ); rc = pager_wait_on_lock(pPager, SHARED_LOCK); @@ -248,7 +781,7 @@ index a935685..237d37e 100644 if( rc!=SQLITE_OK ){ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK ); goto failed; -@@ -60965,6 +61026,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ +@@ -62000,6 +62394,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ ** downgraded to SHARED_LOCK before this function returns. */ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); @@ -256,7 +789,7 @@ index a935685..237d37e 100644 if( rc!=SQLITE_OK ){ goto failed; } -@@ -61579,6 +61641,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory +@@ -62632,6 +63027,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory */ if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); @@ -264,7 +797,7 @@ index a935685..237d37e 100644 if( rc!=SQLITE_OK ){ return rc; } -@@ -61591,6 +61654,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory +@@ -62644,6 +63040,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory ** holds the write-lock. If possible, the upper layer will call it. */ rc = sqlite3WalBeginWriteTransaction(pPager->pWal); @@ -272,7 +805,7 @@ index a935685..237d37e 100644 }else{ /* Obtain a RESERVED lock on the database file. If the exFlag parameter ** is true, then immediately upgrade this to an EXCLUSIVE lock. The -@@ -61598,8 +61662,10 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory +@@ -62651,8 +63048,10 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory ** lock, but not when obtaining the RESERVED lock. */ rc = pagerLockDb(pPager, RESERVED_LOCK); @@ -283,7 +816,7 @@ index a935685..237d37e 100644 } } -@@ -63100,6 +63166,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ +@@ -64164,6 +64563,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){ if( pPager->eState==PAGER_READER ){ assert( rc==SQLITE_OK ); rc = pagerLockDb(pPager, RESERVED_LOCK); @@ -291,7 +824,7 @@ index a935685..237d37e 100644 } if( rc==SQLITE_OK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); -@@ -63264,6 +63331,7 @@ static int pagerOpenWal(Pager *pPager){ +@@ -64330,6 +64730,7 @@ static int pagerOpenWal(Pager *pPager){ */ if( pPager->exclusiveMode ){ rc = pagerExclusiveLock(pPager); @@ -299,7 +832,7 @@ index a935685..237d37e 100644 } /* Open the connection to the log file. If this operation fails, -@@ -63347,6 +63415,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ +@@ -64413,6 +64814,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ if( !pPager->pWal ){ int logexists = 0; rc = pagerLockDb(pPager, SHARED_LOCK); @@ -307,7 +840,7 @@ index a935685..237d37e 100644 if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists -@@ -63362,6 +63431,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ +@@ -64428,6 +64830,7 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); @@ -315,7 +848,17 @@ index a935685..237d37e 100644 if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); -@@ -64736,6 +64806,7 @@ static int walIndexRecover(Wal *pWal){ +@@ -65352,6 +65755,9 @@ static SQLITE_NOINLINE int walIndexPageRealloc( + if( iPage>0 && sqlite3FaultSim(600) ) rc = SQLITE_NOMEM; + }else if( (rc&0xff)==SQLITE_READONLY ){ + pWal->readOnly |= WAL_SHM_RDONLY; ++#ifdef LOG_DUMP ++ sqlite3_log(rc, "PagerRealloc wal set to readonly, sysno[%d]", errno); ++#endif + if( rc==SQLITE_READONLY ){ + rc = SQLITE_OK; + } +@@ -65962,6 +66368,7 @@ static int walIndexRecover(Wal *pWal){ iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock; rc = walLockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock); if( rc ){ @@ -323,27 +866,25 @@ index a935685..237d37e 100644 return rc; } -@@ -65576,6 +65647,7 @@ static int walCheckpoint( +@@ -66795,6 +67202,7 @@ static int walCheckpoint( if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); + MARK_LAST_BUSY_LINE(rc); if( rc==SQLITE_OK ){ u32 iMark = (i==1 ? mxSafeFrame : READMARK_NOT_USED); - AtomicStore(pInfo->aReadMark+i, iMark); -@@ -65670,8 +65742,9 @@ static int walCheckpoint( + AtomicStore(pInfo->aReadMark+i, iMark); SEH_INJECT_FAULT; +@@ -66885,6 +67293,8 @@ static int walCheckpoint( /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); + } else { + MARK_LAST_BUSY_LINE(rc); } -- + if( rc==SQLITE_BUSY ){ - /* Reset the return code so as not to report a checkpoint failure - ** just because there are active readers. */ -@@ -65688,11 +65761,13 @@ static int walCheckpoint( - assert( pWal->writeLock ); +@@ -66904,11 +67314,13 @@ static int walCheckpoint( + SEH_INJECT_FAULT; if( pInfo->nBackfill hdr.mxFrame ){ rc = SQLITE_BUSY; + MARK_LAST_BUSY_LINE(rc); @@ -356,7 +897,7 @@ index a935685..237d37e 100644 if( rc==SQLITE_OK ){ if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){ /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as -@@ -65790,6 +65865,8 @@ SQLITE_PRIVATE int sqlite3WalClose( +@@ -67096,6 +67508,8 @@ SQLITE_PRIVATE int sqlite3WalClose( walLimitSize(pWal, 0); } } @@ -365,7 +906,7 @@ index a935685..237d37e 100644 } walIndexClose(pWal, isDelete); -@@ -65944,6 +66021,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ +@@ -67250,6 +67664,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockShared(pWal, WAL_WRITE_LOCK); rc = SQLITE_READONLY_RECOVERY; } @@ -373,7 +914,7 @@ index a935685..237d37e 100644 }else{ int bWriteLock = pWal->writeLock; if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){ -@@ -65964,6 +66042,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ +@@ -67270,6 +67685,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); } } @@ -381,7 +922,7 @@ index a935685..237d37e 100644 } } -@@ -65983,6 +66062,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ +@@ -67289,6 +67705,7 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){ ** writer truncated the WAL out from under it. If that happens, it ** indicates that a writer has fixed the SHM file for us, so retry */ if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY; @@ -389,7 +930,7 @@ index a935685..237d37e 100644 } pWal->exclusiveMode = WAL_NORMAL_MODE; } -@@ -66214,6 +66294,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ +@@ -67520,6 +67937,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ ** so it takes care to hold an exclusive lock on the corresponding ** WAL_READ_LOCK() while changing values. */ @@ -397,7 +938,7 @@ index a935685..237d37e 100644 static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */ u32 mxReadMark; /* Largest aReadMark[] value */ -@@ -66251,6 +66332,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67557,6 +67975,9 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ return SQLITE_PROTOCOL; } if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39; @@ -407,7 +948,7 @@ index a935685..237d37e 100644 sqlite3OsSleep(pWal->pVfs, nDelay); } -@@ -66277,11 +66361,14 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67583,11 +68004,14 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** must be zeroed before the requested page is returned. */ rc = WAL_RETRY; @@ -422,7 +963,7 @@ index a935685..237d37e 100644 } } if( rc!=SQLITE_OK ){ -@@ -66304,6 +66391,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67611,6 +68035,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); @@ -430,7 +971,7 @@ index a935685..237d37e 100644 walShmBarrier(pWal); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ -@@ -66321,6 +66409,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67628,6 +68053,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ ** it finished. Leaving a corrupt image in the database file. */ walUnlockShared(pWal, WAL_READ_LOCK(0)); @@ -438,7 +979,7 @@ index a935685..237d37e 100644 return WAL_RETRY; } pWal->readLock = 0; -@@ -66365,6 +66454,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67672,6 +68098,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ }else if( rc!=SQLITE_BUSY ){ return rc; } @@ -446,7 +987,7 @@ index a935685..237d37e 100644 } } if( mxI==0 ){ -@@ -66374,6 +66464,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67681,6 +68108,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ @@ -454,7 +995,7 @@ index a935685..237d37e 100644 return rc==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the -@@ -66416,6 +66507,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ +@@ -67723,6 +68151,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); @@ -462,7 +1003,7 @@ index a935685..237d37e 100644 return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); -@@ -66542,6 +66634,7 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ +@@ -67860,6 +68289,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){ (void)walEnableBlocking(pWal); rc = walLockShared(pWal, WAL_CKPT_LOCK); walDisableBlocking(pWal); @@ -470,7 +1011,17 @@ index a935685..237d37e 100644 if( rc!=SQLITE_OK ){ return rc; -@@ -67365,6 +67458,7 @@ static sqlite3_int64 g_lastCkptTime = 0; +@@ -68722,6 +69152,9 @@ static int walFrames( + return rc; + } + ++#ifdef LOG_DUMP ++static sqlite3_int64 g_lastCkptTime = 0; ++#endif /* LOG_DUMP */ + /* + ** Write a set of frames to the log. The caller must hold the write-lock + ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). +@@ -68755,6 +69188,7 @@ SQLITE_PRIVATE int sqlite3WalFrames( ** If parameter xBusy is not NULL, it is a pointer to a busy-handler ** callback. In this case this function runs a blocking checkpoint. */ @@ -478,7 +1029,7 @@ index a935685..237d37e 100644 SQLITE_PRIVATE int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ sqlite3 *db, /* Check this handle's interrupt flag */ -@@ -67406,6 +67500,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( +@@ -68796,6 +69230,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( ** it will not be invoked in this case. */ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); @@ -486,7 +1037,7 @@ index a935685..237d37e 100644 testcase( rc==SQLITE_BUSY ); testcase( rc!=SQLITE_OK && xBusy2!=0 ); if( rc==SQLITE_OK ){ -@@ -67422,6 +67517,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( +@@ -68812,6 +69247,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1); @@ -494,20 +1045,48 @@ index a935685..237d37e 100644 if( rc==SQLITE_OK ){ pWal->writeLock = 1; }else if( rc==SQLITE_BUSY ){ -@@ -67449,7 +67545,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( - if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ - rc = SQLITE_CORRUPT_BKPT; - }else{ -+ sqlite3_int64 startTime; -+ sqlite3OsCurrentTimeInt64(db->pVfs, &startTime); - rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); -+ if (rc == SQLITE_OK){ -+ walLogCheckpointInfo(pWal, db, startTime); -+ } - } +@@ -68839,7 +69275,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ + rc = SQLITE_CORRUPT_BKPT; + }else{ +- rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf); ++ sqlite3_int64 startTime; ++ sqlite3OsCurrentTimeInt64(db->pVfs, &startTime); ++ rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf); ++ if (rc == SQLITE_OK){ ++ walLogCheckpointInfo(pWal, db, startTime); ++ } + } - /* If no error occurred, set the output variables. */ -@@ -71166,6 +71267,7 @@ static void pageReinit(DbPage *pData){ + /* If no error occurred, set the output variables. */ +@@ -68871,6 +69312,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( + walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); + pWal->ckptLock = 0; + } ++#ifdef LOG_DUMP ++ if( rc ){ ++ sqlite3_log(SQLITE_NOTICE, "ckpt rc[%d]", rc); ++ } ++ sqlite3OsCurrentTimeInt64(db->pVfs, &g_lastCkptTime); ++#endif /* LOG_DUMP */ + WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT + if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY; +@@ -72155,6 +72602,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){ + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtr; + pPage->xParseCell = btreeParseCellPtrIndex; ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_CORRUPT, ++ "database corruption at page[%u], flagByte[%x], isInit[%u], intKey[%u], intKeyLeaf[%u], leaf[%u], " ++ "childPtrSize[%u], cellOffset[%u], nCell[%u], hdrOffset[%u], minLocal[%u], maxLocal[%u], last ckpt time[%lld]", ++ pPage->pgno, flagByte, pPage->isInit, pPage->intKey, pPage->intKeyLeaf, pPage->leaf, ++ pPage->childPtrSize, pPage->cellOffset, pPage->nCell, pPage->hdrOffset, pPage->minLocal, pPage->maxLocal, g_lastCkptTime); ++#endif /* LOG_DUMP */ + return SQLITE_CORRUPT_PAGE(pPage); + } + } +@@ -72571,6 +73025,7 @@ static void pageReinit(DbPage *pData){ } } @@ -515,7 +1094,7 @@ index a935685..237d37e 100644 /* ** Invoke the busy handler for a btree. */ -@@ -71173,7 +71275,13 @@ static int btreeInvokeBusyHandler(void *pArg){ +@@ -72578,7 +73033,13 @@ static int btreeInvokeBusyHandler(void *pArg){ BtShared *pBt = (BtShared*)pArg; assert( pBt->db ); assert( sqlite3_mutex_held(pBt->db->mutex) ); @@ -530,7 +1109,7 @@ index a935685..237d37e 100644 } /* -@@ -71601,7 +71709,7 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ +@@ -73009,7 +73470,7 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){ } } #endif @@ -539,7 +1118,17 @@ index a935685..237d37e 100644 /* Rollback any active transaction and free the handle structure. ** The call to sqlite3BtreeRollback() drops any table-locks held by ** this handle. -@@ -72261,7 +72369,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVers +@@ -73393,6 +73854,9 @@ static int lockBtree(BtShared *pBt){ + #else + if( page1[18]>2 ){ + pBt->btsFlags |= BTS_READ_ONLY; ++#ifdef LOG_DUMP ++ sqlite3_log(SQLITE_WARNING_DUMP, "Mark bts readonly %u", get4byte(&page1[18])); ++#endif + } + if( page1[19]>2 ){ + goto page1_init_failed; +@@ -73674,7 +74138,7 @@ static SQLITE_NOINLINE int btreeBeginTrans( sqlite3BtreeEnter(p); btreeIntegrity(p); @@ -548,7 +1137,7 @@ index a935685..237d37e 100644 /* If the btree is already in a write-transaction, or it ** is already in a read-transaction and a read-transaction ** is requested, this is a no-op. -@@ -79629,8 +79737,10 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int * +@@ -81274,8 +81738,10 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int * if( p ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); @@ -559,7 +1148,7 @@ index a935685..237d37e 100644 }else{ rc = sqlite3PagerCheckpoint(pBt->pPager, p->db, eMode, pnLog, pnCkpt); } -@@ -85479,6 +85589,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ +@@ -87285,6 +87751,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ nTrans++; } rc = sqlite3PagerExclusiveLock(pPager); @@ -567,13 +1156,10 @@ index a935685..237d37e 100644 sqlite3BtreeLeave(pBt); } } -@@ -246354,9 +246465,253 @@ export_finish: - return; - } +@@ -254452,6 +254919,256 @@ export_finish: /************** End file hw_codec.c *****************************************/ --#endif /* SQLITE_HAS_CODEC */ -+#endif -+ + #endif + +#if SQLITE_OS_UNIX +#include +#include @@ -702,7 +1288,7 @@ index a935685..237d37e 100644 + } + return 0; +} - ++ +static void DumpTrxProcessLocks(unixFile *file, char *dumpBuf, int dumpBufLen) +{ + unixInodeInfo *inode = file->pInode; @@ -728,7 +1314,7 @@ index a935685..237d37e 100644 + sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Trx locks: %s", dumpBuf); + } +} - ++ +static void DumpWalLocks(unixFile *file, u8 walEnabled, char *dumpBuf, int dumpBufLen) +{ + if (file->pShm == NULL || file->pShm->pShmNode == NULL) { @@ -811,17 +1397,22 @@ index a935685..237d37e 100644 + sqlite3_int64 endTime; + sqlite3OsCurrentTimeInt64(db->pVfs, &endTime); + sqlite3_int64 timeUse = endTime - startTime; ++ static sqlite3_int64 lastDumpTime = 0; ++ static sqlite3_int64 ckptCnt = 0; + /* Only when timeUse > 1500ms or wal size > 50MB, default pageSize 4K, 50*1024/4 = 12800 */ -+ if (timeUse > 1500 || pWal->hdr.mxFrame > 12800) { -+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal ckpt use time: %lld(ms), wal frame: %u", -+ timeUse, pWal->hdr.mxFrame); ++ if (timeUse > 1500 || (pWal->hdr.mxFrame > 12800 && (lastDumpTime - endTime) > 2000)) { ++ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal try ckpt count %d use time: %lld(ms), wal frame: %u", ++ ckptCnt, timeUse, pWal->hdr.mxFrame); ++ lastDumpTime = endTime; ++ ckptCnt = 0; + } ++ ckptCnt++; +} +#endif -+// hw export the symbols ++ + // hw export the symbols #ifdef SQLITE_EXPORT_SYMBOLS - /************** Begin hw export the symbols *****************************************/ - #if defined(__GNUC__) + struct sqlite3_api_routines_hw { -- -2.47.0.windows.2 +2.34.1 diff --git a/patch/0003-support-meta-recovery.patch b/patch/0004-Support-meta-recovery.patch similarity index 91% rename from patch/0003-support-meta-recovery.patch rename to patch/0004-Support-meta-recovery.patch index 099b8658fb1bc04b0282716af2c7a3c0be1b79b7..d9f6e35e008da7624d2816873108ba4060702bdb 100644 --- a/patch/0003-support-meta-recovery.patch +++ b/patch/0004-Support-meta-recovery.patch @@ -1,18 +1,18 @@ -From 647f4ee711da7b5d98514eb3543cc543f303c423 Mon Sep 17 00:00:00 2001 +From cdf4fb30449a76dc055c92efef0eee7e128ccd56 Mon Sep 17 00:00:00 2001 From: wanghaishuo -Date: Sat, 12 Apr 2025 09:26:04 +0800 -Subject: [PATCH 3/7] support meta recovery +Date: Mon, 12 May 2025 15:33:13 +0800 +Subject: [PATCH 4/6] Support meta recovery -Signed-off-by: ryne3366 +Signed-off-by: wanghaishuo --- - src/sqlite3.c | 1148 ++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 1136 insertions(+), 12 deletions(-) + src/sqlite3.c | 1156 ++++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 1151 insertions(+), 5 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c -index 237d37e..ddfc377 100644 +index 574a76a..96ed35b 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -779,6 +779,7 @@ SQLITE_API int sqlite3_exec( +@@ -786,6 +786,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ @@ -20,15 +20,7 @@ index 237d37e..ddfc377 100644 /* end-of-error-codes */ /* -@@ -14964,7 +14965,6 @@ typedef int VList; - # define SQLITE_OS_UNIX 0 - #endif - -- - #endif /* SQLITE_OS_SETUP_H */ - - /************** End of os_setup.h ********************************************/ -@@ -56138,6 +56138,26 @@ struct PagerSavepoint { +@@ -57472,6 +57473,26 @@ struct PagerSavepoint { #endif }; @@ -55,7 +47,7 @@ index 237d37e..ddfc377 100644 /* ** Bits of the Pager.doNotSpill flag. See further description below. */ -@@ -56403,6 +56423,13 @@ struct Pager { +@@ -57737,6 +57758,13 @@ struct Pager { Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */ char *zWal; /* File name for write-ahead log */ #endif @@ -69,7 +61,7 @@ index 237d37e..ddfc377 100644 }; /* -@@ -56762,7 +56789,11 @@ static void setGetterMethod(Pager *pPager){ +@@ -58096,7 +58124,11 @@ static void setGetterMethod(Pager *pPager){ pPager->xGet = getPageMMap; #endif /* SQLITE_MAX_MMAP_SIZE>0 */ }else{ @@ -81,7 +73,7 @@ index 237d37e..ddfc377 100644 } } -@@ -57813,7 +57844,14 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ +@@ -59170,7 +59202,14 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){ } sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize); } @@ -97,7 +89,7 @@ index 237d37e..ddfc377 100644 if( pagerUseWal(pPager) ){ /* Drop the WAL write-lock, if any. Also, if the connection was in ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE -@@ -59890,6 +59928,9 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ +@@ -61269,6 +61308,9 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); pPager->pWal = 0; } @@ -107,7 +99,7 @@ index 237d37e..ddfc377 100644 #endif pager_reset(pPager); if( MEMDB ){ -@@ -60736,7 +60777,11 @@ act_like_temp_file: +@@ -62112,7 +62154,11 @@ act_like_temp_file: rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb, !memDb?pagerStress:0, (void *)pPager, pPager->pPCache); } @@ -120,23 +112,7 @@ index 237d37e..ddfc377 100644 /* If an error occurred above, free the Pager structure and close the file. */ if( rc!=SQLITE_OK ){ -@@ -61168,7 +61213,6 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ - if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ - rc = pagerPagecount(pPager, &pPager->dbSize); - } -- - failed: - if( rc!=SQLITE_OK ){ - assert( !MEMDB ); -@@ -62436,7 +62480,6 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){ - pPager->eState = PAGER_READER; - return SQLITE_OK; - } -- - PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); - rc = pager_end_transaction(pPager, pPager->setSuper, 1); - return pager_error(pPager, rc); -@@ -68267,6 +68310,10 @@ struct BtShared { +@@ -70005,6 +70051,10 @@ struct BtShared { #endif u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */ int nPreformatSize; /* Size of last cell written by TransferRow() */ @@ -147,17 +123,7 @@ index 237d37e..ddfc377 100644 }; /* -@@ -71563,8 +71610,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( - } - } - #endif -- *ppBtree = p; - -+ *ppBtree = p; - btree_open_out: - if( rc!=SQLITE_OK ){ - if( pBt && pBt->pPager ){ -@@ -72529,7 +72576,12 @@ trans_begun: +@@ -74298,7 +74348,12 @@ trans_begun: rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); } } @@ -171,7 +137,7 @@ index 237d37e..ddfc377 100644 btreeIntegrity(p); sqlite3BtreeLeave(p); return rc; -@@ -72896,6 +72948,9 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){ +@@ -74687,6 +74742,9 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){ if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); put4byte(&pBt->pPage1->aData[28], pBt->nPage); @@ -181,7 +147,7 @@ index 237d37e..ddfc377 100644 } }else{ rc = SQLITE_DONE; -@@ -72980,6 +73035,9 @@ static int autoVacuumCommit(Btree *p){ +@@ -74771,6 +74829,9 @@ static int autoVacuumCommit(Btree *p){ put4byte(&pBt->pPage1->aData[28], nFin); pBt->bDoTruncate = 1; pBt->nPage = nFin; @@ -191,7 +157,7 @@ index 237d37e..ddfc377 100644 } if( rc!=SQLITE_OK ){ sqlite3PagerRollback(pPager); -@@ -73036,6 +73094,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ +@@ -74827,6 +74888,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){ if( pBt->bDoTruncate ){ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); } @@ -201,7 +167,7 @@ index 237d37e..ddfc377 100644 #endif rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0); sqlite3BtreeLeave(p); -@@ -78937,6 +78998,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ +@@ -80898,6 +80962,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ assert( iMeta==0 || iMeta==1 ); pBt->incrVacuum = (u8)iMeta; } @@ -213,23 +179,7 @@ index 237d37e..ddfc377 100644 #endif } sqlite3BtreeLeave(p); -@@ -79307,7 +79373,6 @@ static int checkTreePage( - "unable to get the page. error code=%d", rc); - goto end_of_check; - } -- - /* Clear MemPage.isInit to make sure the corruption detection code in - ** btreeInitPage() is executed. */ - savedIsInit = pPage->isInit; -@@ -80514,6 +80579,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ - } - } - } -+ - if( rc==SQLITE_OK ){ - rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); - } -@@ -80546,7 +80612,12 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ +@@ -82544,7 +82613,12 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){ sqlite3PagerTruncateImage(pDestPager, nDestTruncate); rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } @@ -243,14 +193,11 @@ index 237d37e..ddfc377 100644 /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) -@@ -134955,8 +135026,14 @@ SQLITE_PRIVATE void sqlite3Pragma( - /* sqlite3CodecPragma executes internal */ - goto pragma_out; +@@ -138957,7 +139031,12 @@ SQLITE_PRIVATE void sqlite3Pragma( } --#endif /* SQLITE_HAS_CODEC */ + #endif + /* END CODEC */ - -+#endif -+/* END CODEC */ +#ifdef SQLITE_META_DWR + if(PragmaMetaDoubleWrie(db, iDb, pParse, zLeft, zRight)) { + /* PragmaMetaDoubleWrie executes internal */ @@ -260,15 +207,7 @@ index 237d37e..ddfc377 100644 /* Locate the pragma in the lookup table */ pPragma = pragmaLocate(zLeft); if( pPragma==0 ){ -@@ -149391,7 +149468,6 @@ static void updateVirtualTable( - } - } - -- - if( eOnePass==ONEPASS_OFF ){ - /* End the virtual table scan */ - if( pSrc->nSrc==1 ){ -@@ -175302,6 +175378,10 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){ +@@ -180709,6 +180788,10 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){ zErr = "no more rows available"; break; } @@ -279,7 +218,7 @@ index 237d37e..ddfc377 100644 default: { rc &= 0xff; if( ALWAYS(rc>=0) && rc mutex); + // only support enabled meta double write + int rc = MetaDwrOpenAndCheck(pBt); -+ if (rc != SQLITE_OK) { ++ if (rc != SQLITE_OK && rc != SQLITE_PERM) { + parse->nErr++; + parse->rc = rc; + } @@ -475,7 +414,7 @@ index 237d37e..ddfc377 100644 + if (pgno > btreePagecount(pBt)) { + return SQLITE_OK; + } -+ rc = getAndInitPage(pBt, pgno, &pPage, 0, 0); ++ rc = getAndInitPage(pBt, pgno, &pPage, 0); + if (rc) { + return rc; + } @@ -828,6 +767,29 @@ index 237d37e..ddfc377 100644 + return pPager->metaFd == NULL ? NULL : ((const char *)pPager->metaFd + ROUND8(pPager->pVfs->szOsFile)); +} + ++static int MetaDwrCheckPerm(sqlite3_vfs *pVfs, u8 openCreate, char *metaPath) { ++ int exists = 0; ++ int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists); ++ if (rc != SQLITE_OK) { ++ return rc; ++ } ++ if (!exists && !openCreate) { ++ return SQLITE_PERM; ++ } ++#ifdef HARMONY_OS ++ // check if the path have enough permission ++ rc = osAccess(metaPath, W_OK|R_OK); ++ if (rc == 0 || errno == ENOENT) { ++ return SQLITE_OK; ++ } ++ rc = SQLITE_PERM; ++ if (openCreate) { ++ sqlite3_log(SQLITE_WARNING_DUMP, "Meta double write disabled, sysno %d", errno); ++ } ++#endif ++ return rc; ++} ++ +static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) { + if (pPager->metaFd || pPager->zFilename == NULL) { + return SQLITE_OK; @@ -844,15 +806,10 @@ index 237d37e..ddfc377 100644 + return SQLITE_NOMEM_BKPT; + } + sqlite3_snprintf(size, metaPath, "%s-dwr", pPager->zFilename); -+ int exists = 0; -+ int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists); ++ int rc = MetaDwrCheckPerm(pVfs, openCreate, metaPath); + if (rc != SQLITE_OK) { + goto INIT_META_OUT; + } -+ if (!exists && !openCreate) { -+ sqlite3_free(metaFd); -+ goto INIT_META_OUT; -+ } + u32 flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_SUPER_JOURNAL); + if (openCreate) { + flags |= SQLITE_OPEN_CREATE; @@ -1117,15 +1074,16 @@ index 237d37e..ddfc377 100644 +static int MetaDwrCheckMeta(Btree *pBt) { + int nErr = 0; + Pgno aRoot[2] = {0, 1}; // quick check and only check root btree -+ char *errStr = sqlite3BtreeIntegrityCheck(pBt->db, pBt, &aRoot[0], 2, SQLITE_INTEGRITY_CHECK_ERROR_MAX, -+ &nErr); ++ char *errStr = NULL; ++ int rc = sqlite3BtreeIntegrityCheck(pBt->db, pBt, &aRoot[0], 2, SQLITE_INTEGRITY_CHECK_ERROR_MAX, ++ &nErr, &errStr); + if (nErr == 0) { + assert(errStr == 0); + return SQLITE_OK; + } -+ if (errStr == 0) { -+ sqlite3_log(SQLITE_NOMEM, "Meta integrity check no mem"); -+ return SQLITE_NOMEM; ++ if (rc != SQLITE_OK) { ++ sqlite3_log(rc, "Meta integrity check go wrong"); ++ return rc; + } + sqlite3_log(SQLITE_WARNING_DUMP, "Meta integrity check %s", errStr); + sqlite3DbFree(pBt->db, errStr); @@ -1332,5 +1290,5 @@ index 237d37e..ddfc377 100644 #include #include -- -2.47.0.windows.2 +2.34.1 diff --git a/patch/0005-Report-corruption-when-runtime-detected.patch b/patch/0005-Enhance-dfx-ability-report-corruption-and-check-page.patch similarity index 51% rename from patch/0005-Report-corruption-when-runtime-detected.patch rename to patch/0005-Enhance-dfx-ability-report-corruption-and-check-page.patch index 0bbf18275279f57cad1d3486f13457963dfeb519..bc8e8da1db00148831aa2843ad750e90b3d0e65b 100644 --- a/patch/0005-Report-corruption-when-runtime-detected.patch +++ b/patch/0005-Enhance-dfx-ability-report-corruption-and-check-page.patch @@ -1,26 +1,972 @@ -From 75beaaa7d0cc8a392bc1ca2d164f2227c0c5b7f8 Mon Sep 17 00:00:00 2001 +From ee3361c659ae4f46615bdb9f703cb3469c595214 Mon Sep 17 00:00:00 2001 From: wanghaishuo -Date: Sat, 12 Apr 2025 10:53:34 +0800 -Subject: [PATCH 5/7] Report corruption when runtime detected +Date: Mon, 12 May 2025 15:42:10 +0800 +Subject: [PATCH 5/6] Enhance dfx ability, report corruption and check pages -Signed-off-by: ryne3366 +Signed-off-by: wanghaishuo --- - src/sqlite3.c | 649 +++++++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 563 insertions(+), 86 deletions(-) + ext/misc/cksumvfs.c | 916 ++++++++++++++++++++++++++++++++++++++++++++ + src/sqlite3.c | 818 ++++++++++++++++++++++++++++++++++----- + 2 files changed, 1634 insertions(+), 100 deletions(-) + create mode 100644 ext/misc/cksumvfs.c +diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c +new file mode 100644 +index 0000000..6f4c55c +--- /dev/null ++++ b/ext/misc/cksumvfs.c +@@ -0,0 +1,916 @@ ++/* ++** 2020-04-20 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++** ++****************************************************************************** ++** ++** This file implements a VFS shim that writes a checksum on each page ++** of an SQLite database file. When reading pages, the checksum is verified ++** and an error is raised if the checksum is incorrect. ++** ++** COMPILING ++** ++** This extension requires SQLite 3.32.0 or later. It uses the ++** sqlite3_database_file_object() interface which was added in ++** version 3.32.0, so it will not link with an earlier version of ++** SQLite. ++** ++** To build this extension as a separately loaded shared library or ++** DLL, use compiler command-lines similar to the following: ++** ++** (linux) gcc -fPIC -shared cksumvfs.c -o cksumvfs.so ++** (mac) clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib ++** (windows) cl cksumvfs.c -link -dll -out:cksumvfs.dll ++** ++** You may want to add additional compiler options, of course, ++** according to the needs of your project. ++** ++** If you want to statically link this extension with your product, ++** then compile it like any other C-language module but add the ++** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that ++** it is being statically linked rather than dynamically linked ++** ++** LOADING ++** ++** To load this extension as a shared library, you first have to ++** bring up a dummy SQLite database connection to use as the argument ++** to the sqlite3_load_extension() API call. Then you invoke the ++** sqlite3_load_extension() API and shutdown the dummy database ++** connection. All subsequent database connections that are opened ++** will include this extension. For example: ++** ++** sqlite3 *db; ++** sqlite3_open(":memory:", &db); ++** sqlite3_load_extension(db, "./cksumvfs"); ++** sqlite3_close(db); ++** ++** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and ++** statically linked against the application, initialize it using ++** a single API call as follows: ++** ++** sqlite3_register_cksumvfs(); ++** ++** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new ++** default VFS and it uses the prior default VFS as the next VFS ++** down in the stack. This is normally what you want. However, in ++** complex situations where multiple VFS shims are being loaded, ++** it might be important to ensure that cksumvfs is loaded in the ++** correct order so that it sequences itself into the default VFS ++** Shim stack in the right order. ++** ++** USING ++** ++** Open database connections using the sqlite3_open() or ++** sqlite3_open_v2() interfaces, as normal. Ordinary database files ++** (without a checksum) will operate normally. Databases with ++** checksums will return an SQLITE_IOERR_DATA error if a page is ++** encountered that contains an invalid checksum. ++** ++** Checksumming only works on databases that have a reserve-bytes ++** value of exactly 8. The default value for reserve-bytes is 0. ++** Hence, newly created database files will omit the checksum by ++** default. To create a database that includes a checksum, change ++** the reserve-bytes value to 8 by runing: ++** ++** int n = 8; ++** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n); ++** ++** If you do this immediately after creating a new database file, ++** before anything else has been written into the file, then that ++** might be all that you need to do. Otherwise, the API call ++** above should be followed by: ++** ++** sqlite3_exec(db, "VACUUM", 0, 0, 0); ++** ++** It never hurts to run the VACUUM, even if you don't need it. ++** If the database is in WAL mode, you should shutdown and ++** reopen all database connections before continuing. ++** ++** From the CLI, use the ".filectrl reserve_bytes 8" command, ++** followed by "VACUUM;". ++** ++** Note that SQLite allows the number of reserve-bytes to be ++** increased but not decreased. So if a database file already ++** has a reserve-bytes value greater than 8, there is no way to ++** activate checksumming on that database, other than to dump ++** and restore the database file. Note also that other extensions ++** might also make use of the reserve-bytes. Checksumming will ++** be incompatible with those other extensions. ++** ++** VERIFICATION OF CHECKSUMS ++** ++** If any checksum is incorrect, the "PRAGMA quick_check" command ++** will find it. To verify that checksums are actually enabled ++** and running, use the following query: ++** ++** SELECT count(*), verify_checksum(data) ++** FROM sqlite_dbpage ++** GROUP BY 2; ++** ++** There are three possible outputs form the verify_checksum() ++** function: 1, 0, and NULL. 1 is returned if the checksum is ++** correct. 0 is returned if the checksum is incorrect. NULL ++** is returned if the page is unreadable. If checksumming is ++** enabled, the read will fail if the checksum is wrong, so the ++** usual result from verify_checksum() on a bad checksum is NULL. ++** ++** If everything is OK, the query above should return a single ++** row where the second column is 1. Any other result indicates ++** either that there is a checksum error, or checksum validation ++** is disabled. ++** ++** CONTROLLING CHECKSUM VERIFICATION ++** ++** The cksumvfs extension implements a new PRAGMA statement that can ++** be used to disable, re-enable, or query the status of checksum ++** verification: ++** ++** PRAGMA checksum_verification; -- query status ++** PRAGMA checksum_verification=OFF; -- disable verification ++** PRAGMA checksum_verification=ON; -- re-enable verification ++** ++** The "checksum_verification" pragma will return "1" (true) or "0" ++** (false) if checksum verification is enabled or disabled, respectively. ++** "Verification" in this context means the feature that causes ++** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while ++** reading. Checksums are always kept up-to-date as long as the ++** reserve-bytes value of the database is 8, regardless of the setting ++** of this pragma. Checksum verification can be disabled (for example) ++** to do forensic analysis of a database that has previously reported ++** a checksum error. ++** ++** The "checksum_verification" pragma will always respond with "0" if ++** the database file does not have a reserve-bytes value of 8. The ++** pragma will return no rows at all if the cksumvfs extension is ++** not loaded. ++** ++** IMPLEMENTATION NOTES ++** ++** The checksum is stored in the last 8 bytes of each page. This ++** module only operates if the "bytes of reserved space on each page" ++** value at offset 20 the SQLite database header is exactly 8. If ++** the reserved-space value is not 8, this module is a no-op. ++*/ ++#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC) ++# define SQLITE_CKSUMVFS_STATIC ++#endif ++#ifdef SQLITE_CKSUMVFS_STATIC ++# include "sqlite3.h" ++#else ++# include "sqlite3ext.h" ++ SQLITE_EXTENSION_INIT1 ++#endif ++#include ++#include ++ ++// hw export the symbols ++#ifdef SQLITE_EXPORT_SYMBOLS ++#if defined(__GNUC__) ++# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) ++#elif defined(_MSC_VER) ++# define EXPORT_SYMBOLS __declspec(dllexport) ++#else ++# define EXPORT_SYMBOLS ++#endif ++#endif ++ ++/* ++** Forward declaration of objects used by this utility ++*/ ++typedef struct sqlite3_vfs CksmVfs; ++typedef struct CksmFile CksmFile; ++ ++/* ++** Useful datatype abbreviations ++*/ ++#if !defined(SQLITE_AMALGAMATION) ++ typedef unsigned char u8; ++ typedef unsigned int u32; ++#endif ++ ++/* Access to a lower-level VFS that (might) implement dynamic loading, ++** access to randomness, etc. ++*/ ++#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) ++#define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1)) ++ ++/* An open file */ ++struct CksmFile { ++ sqlite3_file base; /* IO methods */ ++ const char *zFName; /* Original name of the file */ ++ char computeCksm; /* True to compute checksums. ++ ** Always true if reserve size is 8. */ ++ char verifyCksm; /* True to verify checksums */ ++ char isWal; /* True if processing a WAL file */ ++ char inCkpt; /* Currently doing a checkpoint */ ++ CksmFile *pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */ ++}; ++ ++/* ++** Methods for CksmFile ++*/ ++static int cksmClose(sqlite3_file*); ++static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); ++static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); ++static int cksmTruncate(sqlite3_file*, sqlite3_int64 size); ++static int cksmSync(sqlite3_file*, int flags); ++static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize); ++static int cksmLock(sqlite3_file*, int); ++static int cksmUnlock(sqlite3_file*, int); ++static int cksmCheckReservedLock(sqlite3_file*, int *pResOut); ++static int cksmFileControl(sqlite3_file*, int op, void *pArg); ++static int cksmSectorSize(sqlite3_file*); ++static int cksmDeviceCharacteristics(sqlite3_file*); ++static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); ++static int cksmShmLock(sqlite3_file*, int offset, int n, int flags); ++static void cksmShmBarrier(sqlite3_file*); ++static int cksmShmUnmap(sqlite3_file*, int deleteFlag); ++static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); ++static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); ++ ++/* ++** Methods for CksmVfs ++*/ ++static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); ++static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir); ++static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *); ++static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); ++static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename); ++static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg); ++static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); ++static void cksmDlClose(sqlite3_vfs*, void*); ++static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut); ++static int cksmSleep(sqlite3_vfs*, int microseconds); ++static int cksmCurrentTime(sqlite3_vfs*, double*); ++static int cksmGetLastError(sqlite3_vfs*, int, char *); ++static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); ++static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr); ++static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z); ++static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName); ++ ++static sqlite3_vfs cksm_vfs = { ++ 3, /* iVersion (set when registered) */ ++ 0, /* szOsFile (set when registered) */ ++ 1024, /* mxPathname */ ++ 0, /* pNext */ ++ "cksmvfs", /* zName */ ++ 0, /* pAppData (set when registered) */ ++ cksmOpen, /* xOpen */ ++ cksmDelete, /* xDelete */ ++ cksmAccess, /* xAccess */ ++ cksmFullPathname, /* xFullPathname */ ++ cksmDlOpen, /* xDlOpen */ ++ cksmDlError, /* xDlError */ ++ cksmDlSym, /* xDlSym */ ++ cksmDlClose, /* xDlClose */ ++ cksmRandomness, /* xRandomness */ ++ cksmSleep, /* xSleep */ ++ cksmCurrentTime, /* xCurrentTime */ ++ cksmGetLastError, /* xGetLastError */ ++ cksmCurrentTimeInt64, /* xCurrentTimeInt64 */ ++ cksmSetSystemCall, /* xSetSystemCall */ ++ cksmGetSystemCall, /* xGetSystemCall */ ++ cksmNextSystemCall /* xNextSystemCall */ ++}; ++ ++static const sqlite3_io_methods cksm_io_methods = { ++ 3, /* iVersion */ ++ cksmClose, /* xClose */ ++ cksmRead, /* xRead */ ++ cksmWrite, /* xWrite */ ++ cksmTruncate, /* xTruncate */ ++ cksmSync, /* xSync */ ++ cksmFileSize, /* xFileSize */ ++ cksmLock, /* xLock */ ++ cksmUnlock, /* xUnlock */ ++ cksmCheckReservedLock, /* xCheckReservedLock */ ++ cksmFileControl, /* xFileControl */ ++ cksmSectorSize, /* xSectorSize */ ++ cksmDeviceCharacteristics, /* xDeviceCharacteristics */ ++ cksmShmMap, /* xShmMap */ ++ cksmShmLock, /* xShmLock */ ++ cksmShmBarrier, /* xShmBarrier */ ++ cksmShmUnmap, /* xShmUnmap */ ++ cksmFetch, /* xFetch */ ++ cksmUnfetch /* xUnfetch */ ++}; ++ ++/* Do byte swapping on a unsigned 32-bit integer */ ++#define BYTESWAP32(x) ( \ ++ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ ++ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ ++) ++ ++/* Compute a checksum on a buffer */ ++static void cksmCompute( ++ u8 *a, /* Content to be checksummed */ ++ int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ ++ u8 *aOut /* OUT: Final 8-byte checksum value output */ ++){ ++ u32 s1 = 0, s2 = 0; ++ u32 *aData = (u32*)a; ++ u32 *aEnd = (u32*)&a[nByte]; ++ u32 x = 1; ++ ++ assert( nByte>=8 ); ++ assert( (nByte&0x00000007)==0 ); ++ assert( nByte<=65536 ); ++ ++ if( 1 == *(u8*)&x ){ ++ /* Little-endian */ ++ do { ++ s1 += *aData++ + s2; ++ s2 += *aData++ + s1; ++ }while( aData 65536 || (nByte & (nByte-1))!=0 ) return; ++ cksmCompute(data, nByte-8, cksum); ++ sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0); ++} ++ ++#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME ++/* ++** SQL function: initialize_cksumvfs(SCHEMANAME) ++** ++** This SQL functions (whose name is actually determined at compile-time ++** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes: ++** ++** sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n); ++** ++** In order to set the reserve bytes value to 8, so that cksumvfs will ++** operation. This feature is provided (if and only if the ++** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string ++** which is the name of the SQL function) so as to provide the ability ++** to invoke the file-control in programming languages that lack ++** direct access to the sqlite3_file_control() interface (ex: Java). ++** ++** This interface is undocumented, apart from this comment. Usage ++** example: ++** ++** 1. Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init" ++** 2. Run: "SELECT cksum_init('main'); VACUUM;" ++*/ ++static void cksmInitFunc( ++ sqlite3_context *context, ++ int argc, ++ sqlite3_value **argv ++){ ++ int nByte = 8; ++ const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]); ++ sqlite3 *db = sqlite3_context_db_handle(context); ++ sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte); ++ /* Return NULL */ ++} ++#endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */ ++ ++/* ++** Close a cksm-file. ++*/ ++static int cksmClose(sqlite3_file *pFile){ ++ CksmFile *p = (CksmFile *)pFile; ++ if( p->pPartner ){ ++ assert( p->pPartner->pPartner==p ); ++ p->pPartner->pPartner = 0; ++ p->pPartner = 0; ++ } ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xClose(pFile); ++} ++ ++/* ++** Set the computeCkSm and verifyCksm flags, if they need to be ++** changed. ++*/ ++static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){ ++ if( hasCorrectReserveSize!=p->computeCksm ){ ++ p->computeCksm = p->verifyCksm = hasCorrectReserveSize; ++ if( p->pPartner ){ ++ p->pPartner->verifyCksm = hasCorrectReserveSize; ++ p->pPartner->computeCksm = hasCorrectReserveSize; ++ } ++ } ++} ++ ++static void EncodeReservedBytesIntoBase16(const u8 *reserved, int len, char *encodeStr, int maxLen){ ++ static const char baseCode[] = "0123456789ABCDEF"; ++ for(int i=0; i > 4) & 0x0F]; ++ *encodeStr++ = baseCode[reserved[i] & 0x0F]; ++ } ++ *encodeStr = '0'; ++} ++ ++/* ++** Read data from a cksm-file. ++*/ ++static int cksmRead( ++ sqlite3_file *pFile, ++ void *zBuf, ++ int iAmt, ++ sqlite_int64 iOfst ++){ ++ int rc; ++ CksmFile *p = (CksmFile *)pFile; ++ pFile = ORIGFILE(pFile); ++ rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst); ++ if( rc==SQLITE_OK ){ ++ if( iOfst==0 && iAmt>=100 && ( ++ memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0 ++ )){ ++ u8 *d = (u8*)zBuf; ++ char hasCorrectReserveSize = (d[20]==8); ++ cksmSetFlags(p, hasCorrectReserveSize); ++ } ++ /* Verify the checksum if ++ ** (1) the size indicates that we are dealing with a complete ++ ** database page, only support pageSize:4K ++ ** (2) checksum verification is enabled ++ ** (3) we are not in the middle of checkpoint ++ */ ++ if( iAmt==4096 /* (1) */ ++ && p->verifyCksm /* (2) */ ++ && !p->inCkpt /* (3) */ ++ ){ ++ u8 cksum[8]; ++ cksmCompute((u8*)zBuf, iAmt-8, cksum); ++ if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){ ++ char expect[18] = {0}; ++ char actual[18] = {0}; ++ EncodeReservedBytesIntoBase16((u8 *)zBuf+iAmt-8, 8, expect, 18); ++ EncodeReservedBytesIntoBase16(cksum, 8, actual, 18); ++ sqlite3_log(SQLITE_IOERR_DATA, "checksum fault offset %lld of \"%s\", amt:%d, expect:%s, actual:%s", ++ iOfst, p->zFName, iAmt, expect, actual); ++ rc = SQLITE_IOERR_DATA; ++ } ++ } ++ } ++ return rc; ++} ++ ++/* ++** Write data to a cksm-file. ++*/ ++static int cksmWrite( ++ sqlite3_file *pFile, ++ const void *zBuf, ++ int iAmt, ++ sqlite_int64 iOfst ++){ ++ CksmFile *p = (CksmFile *)pFile; ++ pFile = ORIGFILE(pFile); ++ if( iOfst==0 && iAmt>=100 && ( ++ memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0 ++ )){ ++ u8 *d = (u8*)zBuf; ++ char hasCorrectReserveSize = (d[20]==8); ++ cksmSetFlags(p, hasCorrectReserveSize); ++ } ++ /* If the write size is appropriate for a database page and if ++ ** checksums where ever enabled, then it will be safe to compute ++ ** the checksums. The reserve byte size might have increased, but ++ ** it will never decrease. And because it cannot decrease, the ++ ** checksum will not overwrite anything. ++ */ ++ if( iAmt==4096 ++ && p->computeCksm ++ && !p->inCkpt ++ ){ ++ cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8); ++ } ++ return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst); ++} ++ ++/* ++** Truncate a cksm-file. ++*/ ++static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xTruncate(pFile, size); ++} ++ ++/* ++** Sync a cksm-file. ++*/ ++static int cksmSync(sqlite3_file *pFile, int flags){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xSync(pFile, flags); ++} ++ ++/* ++** Return the current file-size of a cksm-file. ++*/ ++static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ ++ CksmFile *p = (CksmFile *)pFile; ++ pFile = ORIGFILE(p); ++ return pFile->pMethods->xFileSize(pFile, pSize); ++} ++ ++/* ++** Lock a cksm-file. ++*/ ++static int cksmLock(sqlite3_file *pFile, int eLock){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xLock(pFile, eLock); ++} ++ ++/* ++** Unlock a cksm-file. ++*/ ++static int cksmUnlock(sqlite3_file *pFile, int eLock){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xUnlock(pFile, eLock); ++} ++ ++/* ++** Check if another file-handle holds a RESERVED lock on a cksm-file. ++*/ ++static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xCheckReservedLock(pFile, pResOut); ++} ++ ++/* ++** File control method. For custom operations on a cksm-file. ++*/ ++static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){ ++ int rc; ++ CksmFile *p = (CksmFile*)pFile; ++ pFile = ORIGFILE(pFile); ++ if( op==SQLITE_FCNTL_PRAGMA ){ ++ char **azArg = (char**)pArg; ++ assert( azArg[1]!=0 ); ++ if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){ ++ char *zArg = azArg[2]; ++ if( zArg!=0 ){ ++ if( (zArg[0]>='1' && zArg[0]<='9') ++ || sqlite3_strlike("enable%",zArg,0)==0 ++ || sqlite3_stricmp("yes",zArg)==0 ++ || sqlite3_stricmp("on",zArg)==0 ++ ){ ++ p->verifyCksm = p->computeCksm; ++ }else{ ++ p->verifyCksm = 0; ++ } ++ if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm; ++ } ++ azArg[0] = sqlite3_mprintf("%d",p->verifyCksm); ++ return SQLITE_OK; ++ }else if( p->computeCksm && azArg[2]!=0 ++ && sqlite3_stricmp(azArg[1], "page_size")==0 ){ ++ /* Do not allow page size changes on a checksum database */ ++ return SQLITE_OK; ++ } ++ }else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){ ++ p->inCkpt = op==SQLITE_FCNTL_CKPT_START; ++ if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt; ++ }else if( op==SQLITE_FCNTL_CKSM_FILE ){ ++ /* This VFS needs to obtain a pointer to the corresponding database ++ ** file handle from within xOpen() calls to open wal files. To do this, ++ ** it uses the sqlite3_database_file_object() API to obtain a pointer ++ ** to the file-handle used by SQLite to access the db file. This is ++ ** fine if cksmvfs happens to be the top-level VFS, but not if there ++ ** are one or more wrapper VFS. To handle this case, this file-control ++ ** is used to extract the cksmvfs file-handle from any wrapper file ++ ** handle. */ ++ sqlite3_file **ppFile = (sqlite3_file**)pArg; ++ *ppFile = (sqlite3_file*)p; ++ return SQLITE_OK; ++ } ++ rc = pFile->pMethods->xFileControl(pFile, op, pArg); ++ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){ ++ *(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg); ++ } ++ return rc; ++} ++ ++/* ++** Return the sector-size in bytes for a cksm-file. ++*/ ++static int cksmSectorSize(sqlite3_file *pFile){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xSectorSize(pFile); ++} ++ ++/* ++** Return the device characteristic flags supported by a cksm-file. ++*/ ++static int cksmDeviceCharacteristics(sqlite3_file *pFile){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xDeviceCharacteristics(pFile); ++} ++ ++/* Create a shared memory file mapping */ ++static int cksmShmMap( ++ sqlite3_file *pFile, ++ int iPg, ++ int pgsz, ++ int bExtend, ++ void volatile **pp ++){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp); ++} ++ ++/* Perform locking on a shared-memory segment */ ++static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xShmLock(pFile,offset,n,flags); ++} ++ ++/* Memory barrier operation on shared memory */ ++static void cksmShmBarrier(sqlite3_file *pFile){ ++ pFile = ORIGFILE(pFile); ++ pFile->pMethods->xShmBarrier(pFile); ++} ++ ++/* Unmap a shared memory segment */ ++static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){ ++ pFile = ORIGFILE(pFile); ++ return pFile->pMethods->xShmUnmap(pFile,deleteFlag); ++} ++ ++/* Fetch a page of a memory-mapped file */ ++static int cksmFetch( ++ sqlite3_file *pFile, ++ sqlite3_int64 iOfst, ++ int iAmt, ++ void **pp ++){ ++ CksmFile *p = (CksmFile *)pFile; ++ if( p->computeCksm ){ ++ *pp = 0; ++ return SQLITE_OK; ++ } ++ pFile = ORIGFILE(pFile); ++ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){ ++ return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp); ++ } ++ *pp = 0; ++ return SQLITE_OK; ++} ++ ++/* Release a memory-mapped page */ ++static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ ++ pFile = ORIGFILE(pFile); ++ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){ ++ return pFile->pMethods->xUnfetch(pFile, iOfst, pPage); ++ } ++ return SQLITE_OK; ++} ++ ++/* ++** Open a cksm file handle. ++*/ ++static int cksmOpen( ++ sqlite3_vfs *pVfs, ++ const char *zName, ++ sqlite3_file *pFile, ++ int flags, ++ int *pOutFlags ++){ ++ CksmFile *p; ++ sqlite3_file *pSubFile; ++ sqlite3_vfs *pSubVfs; ++ int rc; ++ pSubVfs = ORIGVFS(pVfs); ++ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){ ++ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); ++ } ++ p = (CksmFile*)pFile; ++ memset(p, 0, sizeof(*p)); ++ pSubFile = ORIGFILE(pFile); ++ pFile->pMethods = &cksm_io_methods; ++ rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); ++ if( rc ) goto cksm_open_done; ++ if( flags & SQLITE_OPEN_WAL ){ ++ sqlite3_file *pDb = sqlite3_database_file_object(zName); ++ rc = pDb->pMethods->xFileControl(pDb, SQLITE_FCNTL_CKSM_FILE, (void*)&pDb); ++ assert( rc==SQLITE_OK ); ++ p->pPartner = (CksmFile*)pDb; ++ assert( p->pPartner->pPartner==0 ); ++ p->pPartner->pPartner = p; ++ p->isWal = 1; ++ p->computeCksm = p->pPartner->computeCksm; ++ }else{ ++ p->isWal = 0; ++ p->computeCksm = 0; ++ } ++ p->zFName = zName; ++cksm_open_done: ++ if( rc ) pFile->pMethods = 0; ++ return rc; ++} ++ ++/* ++** All other VFS methods are pass-thrus. ++*/ ++static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ ++ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync); ++} ++static int cksmAccess( ++ sqlite3_vfs *pVfs, ++ const char *zPath, ++ int flags, ++ int *pResOut ++){ ++ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut); ++} ++static int cksmFullPathname( ++ sqlite3_vfs *pVfs, ++ const char *zPath, ++ int nOut, ++ char *zOut ++){ ++ return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut); ++} ++static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){ ++ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); ++} ++static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ ++ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); ++} ++static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ ++ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); ++} ++static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){ ++ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); ++} ++static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ ++ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); ++} ++static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){ ++ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); ++} ++static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ ++ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); ++} ++static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){ ++ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); ++} ++static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ ++ sqlite3_vfs *pOrig = ORIGVFS(pVfs); ++ int rc; ++ assert( pOrig->iVersion>=2 ); ++ if( pOrig->xCurrentTimeInt64 ){ ++ rc = pOrig->xCurrentTimeInt64(pOrig, p); ++ }else{ ++ double r; ++ rc = pOrig->xCurrentTime(pOrig, &r); ++ *p = (sqlite3_int64)(r*86400000.0); ++ } ++ return rc; ++} ++static int cksmSetSystemCall( ++ sqlite3_vfs *pVfs, ++ const char *zName, ++ sqlite3_syscall_ptr pCall ++){ ++ return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall); ++} ++static sqlite3_syscall_ptr cksmGetSystemCall( ++ sqlite3_vfs *pVfs, ++ const char *zName ++){ ++ return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName); ++} ++static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ ++ return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName); ++} ++ ++/* Register the verify_checksum() SQL function. ++*/ ++static int cksmRegisterFunc( ++ sqlite3 *db, ++ char **pzErrMsg, ++ const sqlite3_api_routines *pApi ++){ ++ int rc; ++ if( db==0 ) return SQLITE_OK; ++ rc = sqlite3_create_function(db, "verify_checksum", 1, ++ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, ++ 0, cksmVerifyFunc, 0, 0); ++#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME ++ (void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1, ++ SQLITE_UTF8|SQLITE_DIRECTONLY, ++ 0, cksmInitFunc, 0, 0); ++#endif ++ return rc; ++} ++ ++/* ++** Register the cksum VFS as the default VFS for the system. ++** Also make arrangements to automatically register the "verify_checksum()" ++** SQL function on each new database connection. ++*/ ++static int cksmRegisterVfs(void){ ++ int rc = SQLITE_OK; ++ sqlite3_vfs *pOrig; ++ if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK; ++ pOrig = sqlite3_vfs_find(0); ++ if( pOrig==0 ) return SQLITE_ERROR; ++ cksm_vfs.iVersion = pOrig->iVersion; ++ cksm_vfs.pAppData = pOrig; ++ cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile); ++ rc = sqlite3_vfs_register(&cksm_vfs, 1); ++ if( rc==SQLITE_OK ){ ++ rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc); ++ } ++ return rc; ++} ++ ++#if defined(SQLITE_CKSUMVFS_STATIC) ++/* This variant of the initializer runs when the extension is ++** statically linked. ++*/ ++int sqlite3_register_cksumvfs(const char *NotUsed){ ++ (void)NotUsed; ++ return cksmRegisterVfs(); ++} ++int sqlite3_unregister_cksumvfs(void){ ++ if( sqlite3_vfs_find("cksmvfs") ){ ++ sqlite3_vfs_unregister(&cksm_vfs); ++ sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc); ++ } ++ return SQLITE_OK; ++} ++#endif /* defined(SQLITE_CKSUMVFS_STATIC */ ++ ++#if !defined(SQLITE_CKSUMVFS_STATIC) ++/* This variant of the initializer function is used when the ++** extension is shared library to be loaded at run-time. ++*/ ++#ifdef _WIN32 ++__declspec(dllexport) ++#endif ++/* ++** This routine is called by sqlite3_load_extension() when the ++** extension is first loaded. ++***/ ++int sqlite3_cksumvfs_init( ++ sqlite3 *db, ++ char **pzErrMsg, ++ const sqlite3_api_routines *pApi ++){ ++ int rc; ++ SQLITE_EXTENSION_INIT2(pApi); ++ (void)pzErrMsg; /* not used */ ++ rc = cksmRegisterFunc(db, 0, 0); ++ if( rc==SQLITE_OK ){ ++ rc = cksmRegisterVfs(); ++ } ++ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; ++ return rc; ++} ++#endif /* !defined(SQLITE_CKSUMVFS_STATIC) */ ++ ++#ifdef SQLITE_CKSUMVFS_STATIC ++struct sqlite3_api_routines_cksumvfs { ++ int (*register_cksumvfs)(const char *); ++ int (*unregister_cksumvfs)(); ++}; ++typedef struct sqlite3_api_routines_cksumvfs sqlite3_api_routines_cksumvfs; ++static const sqlite3_api_routines_cksumvfs sqlite3CksumvfsApis = { ++ sqlite3_register_cksumvfs, ++ sqlite3_unregister_cksumvfs ++}; ++ ++EXPORT_SYMBOLS const sqlite3_api_routines_cksumvfs *sqlite3_export_cksumvfs_symbols = &sqlite3CksumvfsApis; ++#endif diff --git a/src/sqlite3.c b/src/sqlite3.c -index a25d540..aa629b5 100644 +index 96ed35b..2a4e87f 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -2459,6 +2459,7 @@ struct sqlite3_mem_methods { - #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ +@@ -2472,6 +2472,21 @@ struct sqlite3_mem_methods { + ** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and + ** recommended case) then the integer is always filled with zero, regardless + ** if its initial value. ++** ++** [[SQLITE_CONFIG_CORRUPTION]] SQLITE_CONFIG_CORRUPTION ++**The SQLITE_CONFIG_CORRUPTION option is used to configure the SQLite ++** global [ corruption error]. ++** (^The SQLITE_CONFIG_CORRUPTION option takes two arguments: a pointer to a ++** function with a call signature of void(*)(void*,const void*), ++** and a pointer to void. ^If the function pointer is not NULL, it is ++** invoked to process each data corruption event. ^If the ++** function pointer is NULL, no=op will do when corruption detect. ++** ^The void pointer that is the second argument to SQLITE_CONFIG_CORRUPTION is ++** passed through as the first parameter to the application-defined corruption ++** function whenever that function is invoked. ^The second parameter to ++** the corruption function is a corruption message after formatting via [sqlite3_snprintf()]. ++** In a multi-threaded application, the application-defined corruption ++** function must be threadsafe. + ** + */ + #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +@@ -2504,6 +2519,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ -+#define SQLITE_CONFIG_CORRUPTION 30 /* xCorruption */ - #define SQLITE_CONFIG_ENABLE_ICU 31 /* boolean */ + #define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ ++#define SQLITE_CONFIG_CORRUPTION 40 /* xCorruption */ + #define SQLITE_CONFIG_ENABLE_ICU 41 /* boolean */ + /* - ** CAPI3REF: Database Connection Configuration Options -@@ -19554,6 +19555,8 @@ struct Sqlite3Config { +@@ -20211,6 +20227,8 @@ struct Sqlite3Config { 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 */ @@ -29,7 +975,7 @@ index a25d540..aa629b5 100644 /* vvvv--- must be last ---vvv */ #ifdef SQLITE_DEBUG sqlite3_int64 aTune[SQLITE_NTUNE]; /* Tuning parameters */ -@@ -19805,6 +19808,57 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis +@@ -20475,6 +20493,57 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis } \ } @@ -87,7 +1033,7 @@ index a25d540..aa629b5 100644 /* ** The SQLITE_*_BKPT macros are substitutes for the error codes with ** the same name but without the _BKPT suffix. These macros invoke -@@ -19813,10 +19867,11 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis +@@ -20483,10 +20552,11 @@ SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprLis ** to set a debugger breakpoint. */ SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType); @@ -101,7 +1047,7 @@ index a25d540..aa629b5 100644 #define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__) #define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__) #ifdef SQLITE_DEBUG -@@ -19828,12 +19883,13 @@ SQLITE_PRIVATE int sqlite3IoerrnomemError(int); +@@ -20498,12 +20568,13 @@ SQLITE_PRIVATE int sqlite3IoerrnomemError(int); # define SQLITE_NOMEM_BKPT SQLITE_NOMEM # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM #endif @@ -118,25 +1064,127 @@ index a25d540..aa629b5 100644 /* ** FTS3 and FTS4 both require virtual table support -@@ -22312,6 +22368,8 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { +@@ -22946,6 +23017,8 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ + 0, /* xCorruption */ + 0, /* pCorruptionArg */ #ifdef SQLITE_DEBUG - {0,0,0,0,0,0} /* aTune */ + {0,0,0,0,0,0}, /* aTune */ #endif -@@ -58784,7 +58842,7 @@ static int readDbPage(PgHdr *pPg){ - assert( isOpen(pPager->fd) ); +@@ -58312,21 +58385,80 @@ static int jrnlBufferSize(Pager *pPager){ + ** and debugging only. + */ + #ifdef SQLITE_CHECK_PAGES +-/* +-** Return a 32-bit hash of the page data for pPage. +-*/ +-static u32 pager_datahash(int nByte, unsigned char *pData){ ++#ifndef SQLITE_MIN_CHECK_PAGE_SIZE ++#define SQLITE_MIN_CHECK_PAGE_SIZE 4096 ++#endif ++#if defined (__arm__) || defined (__aarch64__) ++#include++u32 deep_fast_hash_arm(void *src, int srcLen){ ++ uint16_t chunkSize = srcLen/4; ++ uint8_t *u8p_src = (uint8_t *)src; ++ uint16x8_t m_prime = vdupq_n_u16(44497); ++ uint16x8_t m_res0 = vdupq_n_u16(0); ++ uint16x8_t m_res1 = vdupq_n_u16(0); ++ uint16x8_t m_res2 = vdupq_n_u16(0); ++ uint16x8_t m_res3 = vdupq_n_u16(0); ++ uint16x8_t m_res4 = vdupq_n_u16(0); ++ uint16x8_t m_res5 = vdupq_n_u16(0); ++ uint16x8_t m_res6 = vdupq_n_u16(0); ++ uint16x8_t m_res7 = vdupq_n_u16(0); ++ ++ for(int i=0; i pPager->pageSize, (unsigned char *)pPage->pData); + } + static void pager_set_pagehash(PgHdr *pPage){ ++ if( pPage->pPager->pageSize pageHash = pager_pagehash(pPage); + } - if( pagerUseWal(pPager) ){ -- rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); -+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame); // find in wal-index - if( rc ) return rc; +@@ -58338,8 +58470,12 @@ static void pager_set_pagehash(PgHdr *pPage){ + #define CHECK_PAGE(x) checkPage(x) + static void checkPage(PgHdr *pPg){ + Pager *pPager = pPg->pPager; +- assert( pPager->eState!=PAGER_ERROR ); +- assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); ++ if( pPager->pageSize eState==PAGER_ERROR || pPg->flags&PGHDR_DIRTY ) { ++ return; ++ } ++ if( pPg->pgno!=1 && pPg->pageHash!=pager_pagehash(pPg) ){ ++ sqlite3_log(SQLITE_CORRUPT, "cache corruption occurs through checking page(%u)", pPg->pgno); ++ } + } + + #else +@@ -60354,6 +60490,9 @@ static int pagerWalFrames( } - if( iFrame ){ -@@ -61324,7 +61382,12 @@ static int getPageNormal( + + #ifdef SQLITE_CHECK_PAGES ++ if( pPager->pageSize pPCache); + for(p=pList; p; p=p->pDirty){ + pager_set_pagehash(p); +@@ -62683,7 +62822,12 @@ static int getPageNormal( assert( assert_pager_state(pPager) ); assert( pPager->hasHeldSharedLock==1 ); @@ -150,7 +1198,7 @@ index a25d540..aa629b5 100644 pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3); if( pBase==0 ){ pPg = 0; -@@ -61356,7 +61419,11 @@ static int getPageNormal( +@@ -62715,7 +62859,11 @@ static int getPageNormal( ** (2) Never try to fetch the locking page */ if( pgno==PAGER_SJ_PGNO(pPager) ){ @@ -163,7 +1211,7 @@ index a25d540..aa629b5 100644 goto pager_acquire_err; } -@@ -61438,7 +61505,10 @@ static int getPageMMap( +@@ -62801,7 +62949,10 @@ static int getPageMMap( ** test in the previous statement, and avoid testing pgno==0 in the ** common case where pgno is large. */ if( pgno<=1 && pgno==0 ){ @@ -175,7 +1223,7 @@ index a25d540..aa629b5 100644 } assert( pPager->eState>=PAGER_READER ); assert( assert_pager_state(pPager) ); -@@ -63024,7 +63094,11 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i +@@ -64413,7 +64564,11 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i if( pPgOld ){ if( NEVER(pPgOld->nRef>1) ){ sqlite3PagerUnrefNotNull(pPgOld); @@ -188,7 +1236,7 @@ index a25d540..aa629b5 100644 } pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); if( pPager->tempFile ){ -@@ -64792,7 +64866,13 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ +@@ -66346,7 +66501,13 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){ /* Write the aPgno[] array entry and the hash-table slot. */ nCollide = idx; for(iKey=walHash(iPage); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){ @@ -203,7 +1251,19 @@ index a25d540..aa629b5 100644 } sLoc.aPgno[idx-1] = iPage; AtomicStore(&sLoc.aHash[iKey], (ht_slot)idx); -@@ -65745,7 +65825,13 @@ static int walCheckpoint( +@@ -67176,6 +67337,11 @@ static void walRestartHdr(Wal *pWal, u32 salt1){ + assert( pInfo->aReadMark[0]==0 ); + } + ++#ifdef SQLITE_HDR_CHECK ++static int checkHeaderValid(Pager *pager, u8 *zBuf, const char *logStr); ++static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf); ++#endif /* SQLITE_HDR_CHECK */ ++ + /* + ** Copy as much content as we can from the WAL back into the database file + ** in response to an sqlite3_wal_checkpoint() request or the equivalent. +@@ -67291,7 +67457,13 @@ static int walCheckpoint( ** database plus the amount of data in the wal file, plus the ** maximum size of the pending-byte page (65536 bytes), then ** must be corruption somewhere. */ @@ -218,7 +1278,18 @@ index a25d540..aa629b5 100644 }else{ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); } -@@ -66856,7 +66942,11 @@ SQLITE_PRIVATE int sqlite3WalFindFrame( +@@ -67317,6 +67489,10 @@ static int walCheckpoint( + if( rc!=SQLITE_OK ) break; + iOffset = (iDbpage-1)*(i64)szPage; + testcase( IS_BIG_INT(iOffset) ); ++#ifdef SQLITE_HDR_CHECK ++ rc = checkDbHeaderValid(db, iDbpage, zBuf); ++ if( rc!=SQLITE_OK ) break; ++#endif /* SQLITE_HDR_CHECK */ + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset); + if( rc!=SQLITE_OK ) break; + } +@@ -68526,7 +68702,11 @@ static int walFindFrame( iRead = iFrame; } if( (nCollide--)==0 ){ @@ -231,21 +1302,68 @@ index a25d540..aa629b5 100644 } iKey = walNextHash(iKey); } -@@ -67597,7 +67687,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( - if( rc==SQLITE_OK ){ +@@ -68894,6 +69074,13 @@ static int walWriteOneFrame( + int rc; /* Result code from subfunctions */ + void *pData; /* Data actually written */ + u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */ ++#ifdef SQLITE_HDR_CHECK ++ if( pPage->pgno==1 ){ ++ rc = checkHeaderValid(pPage->pPager, pPage->pData, "walWrite"); ++ if( rc!=SQLITE_OK ) return rc; ++ } ++#endif /* SQLITE_HDR_CHECK */ ++ + #ifdef SQLITE_HAS_CODEC + if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT; + #else +@@ -69319,7 +69506,12 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint( + /* Copy data from the log to the database file. */ + if( rc==SQLITE_OK ){ + if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ +- rc = SQLITE_CORRUPT_BKPT; ++ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; ++ sqlite3_snprintf(sizeof(zMsg), zMsg, "mis-match between pageSize=%d and bufferSize=%d, mxFrame=%u", ++ walPagesize(pWal),nBuf,pWal->hdr.mxFrame); ++ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pWal->hdr.nPage, 0, CORRUPT_TYPE_FRAME_WAL, ++ -1, 0, zMsg, NULL); ++ rc = SQLITE_CORRUPT_REPORT(&context); + }else{ + sqlite3_int64 startTime; + sqlite3OsCurrentTimeInt64(db->pVfs, &startTime); +@@ -70751,6 +70943,32 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ + } + #endif - if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ -- rc = SQLITE_CORRUPT_BKPT; -+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; -+ sqlite3_snprintf(sizeof(zMsg), zMsg, "mis-match between pageSize=%d and bufferSize=%d, mxFrame=%u", -+ walPagesize(pWal),nBuf,pWal->hdr.mxFrame); -+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pWal->hdr.nPage, 0, CORRUPT_TYPE_FRAME_WAL, -+ -1, 0, zMsg, NULL); -+ rc = SQLITE_CORRUPT_REPORT(&context); - }else{ - sqlite3_int64 startTime; - sqlite3OsCurrentTimeInt64(db->pVfs, &startTime); -@@ -69055,7 +69150,7 @@ static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf){ ++#ifdef SQLITE_HDR_CHECK ++static int checkHeaderValid(Pager *pager, u8 *zBuf, const char *logStr){ ++#ifdef SQLITE_HAS_CODEC ++ if( pager==NULL || pager->pCodec ){ ++ return SQLITE_OK; ++ } ++#endif /* SQLITE_HAS_CODEC */ ++ if( zBuf && strncmp((const char *)zBuf, zMagicHeader, 16)!=0 ){ ++ sqlite3_log(SQLITE_NOTADB, "[%s]wrong header format, memory might be overwritten!", logStr); ++ return SQLITE_NOTADB; ++ } ++ return SQLITE_OK; ++} ++ ++static int checkDbHeaderValid(sqlite3 *db, int iDbpage, u8 *zBuf){ ++ if( iDbpage==1 && db->aDb ){ ++ Btree *p = db->aDb[0].pBt; ++ if( p && p->pBt ){ ++ Pager *pager = sqlite3BtreePager(p); ++ return checkHeaderValid(pager, zBuf, "ckpt"); ++ } ++ } ++ return SQLITE_OK; ++} ++#endif /* SQLITE_HDR_CHECK */ ++ + /* + ** Implementation of the SQLITE_CORRUPT_PAGE() macro. Takes a single + ** (MemPage*) as an argument. The (MemPage*) must not be NULL. +@@ -70761,22 +70979,22 @@ SQLITE_PRIVATE sqlite3_uint64 sqlite3BtreeSeekCount(Btree *pBt){ ** with the page number and filename associated with the (MemPage*). */ #ifdef SQLITE_DEBUG @@ -253,8 +1371,13 @@ index a25d540..aa629b5 100644 +int corruptPageError(int lineno, MemPage *p, sqlite3CorruptContext *context){ char *zMsg; sqlite3BeginBenignMalloc(); - zMsg = sqlite3_mprintf("database corruption page %d of %s", -@@ -69066,11 +69161,11 @@ int corruptPageError(int lineno, MemPage *p){ +- zMsg = sqlite3_mprintf("database corruption page %u of %s", +- p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) ++ zMsg = sqlite3_mprintf("database corruption page %d of %s", ++ (int)p->pgno, sqlite3PagerFilename(p->pBt->pPager, 0) + ); + sqlite3EndBenignMalloc(); + if( zMsg ){ sqlite3ReportError(SQLITE_CORRUPT, lineno, zMsg); } sqlite3_free(zMsg); @@ -269,7 +1392,7 @@ index a25d540..aa629b5 100644 #endif #ifndef SQLITE_OMIT_SHARED_CACHE -@@ -69748,7 +69843,12 @@ static int btreeMoveto( +@@ -71454,7 +71672,12 @@ static int btreeMoveto( if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ @@ -283,7 +1406,7 @@ index a25d540..aa629b5 100644 }else{ rc = sqlite3BtreeIndexMoveto(pCur, pIdxKey, pRes); } -@@ -69928,7 +70028,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ +@@ -71651,7 +71874,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); assert( pBt->autoVacuum ); @@ -292,7 +1415,7 @@ index a25d540..aa629b5 100644 *pRC = SQLITE_CORRUPT_BKPT; return; } -@@ -69942,12 +70042,24 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ +@@ -71665,12 +71888,24 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ /* The first byte of the extra data is the MemPage.isInit byte. ** If that byte is set, it means this page is also being used ** as a btree page. */ @@ -319,7 +1442,7 @@ index a25d540..aa629b5 100644 goto ptrmap_exit; } assert( offset <= (int)pBt->usableSize-5 ); -@@ -69992,7 +70104,12 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ +@@ -71715,7 +71950,12 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ sqlite3PagerUnref(pDbPage); @@ -333,7 +1456,7 @@ index a25d540..aa629b5 100644 } assert( offset <= (int)pBt->usableSize-5 ); assert( pEType!=0 ); -@@ -70000,7 +70117,15 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ +@@ -71723,7 +71963,15 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); sqlite3PagerUnref(pDbPage); @@ -350,9 +1473,9 @@ index a25d540..aa629b5 100644 return SQLITE_OK; } -@@ -70392,7 +70517,14 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ +@@ -72163,7 +72411,14 @@ static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ Pgno ovfl; - if( SQLITE_WITHIN(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){ + if( SQLITE_OVERFLOW(pSrc->aDataEnd, pCell, pCell+info.nLocal) ){ testcase( pSrc!=pPage ); - *pRC = SQLITE_CORRUPT_BKPT; + char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; @@ -366,7 +1489,7 @@ index a25d540..aa629b5 100644 return; } ovfl = get4byte(&pCell[info.nSize-4]); -@@ -70450,10 +70582,29 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ +@@ -72221,10 +72476,29 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); @@ -398,7 +1521,7 @@ index a25d540..aa629b5 100644 if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ u8 *pEnd = &data[cellOffset + nCell*2]; u8 *pAddr; -@@ -70461,16 +70612,51 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ +@@ -72232,16 +72506,51 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ int sz = get2byte(&data[iFree+2]); int top = get2byte(&data[hdr+5]); if( top>=iFree ){ @@ -454,11 +1577,13 @@ index a25d540..aa629b5 100644 } cbrk = top+sz; -@@ -70503,13 +70689,24 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ +@@ -72273,14 +72582,25 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ + /* These conditions have already been verified in btreeInitPage() ** if PRAGMA cell_size_check=ON. */ - if( pc iCellLast ){ +- if( pc>iCellLast ){ - return SQLITE_CORRUPT_PAGE(pPage); ++ if( pc iCellLast ){ + char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; + char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; // Print 4 bytes belong to 1st block + (void)sqlite3base16Encode(data + cellOffset + i*2, 2, xBuffer, sizeof(xBuffer)); @@ -468,7 +1593,7 @@ index a25d540..aa629b5 100644 + cellOffset + i*2, 2, zMsg, NULL); + return SQLITE_CORRUPT_PAGE(&context, pPage); } - assert( pc>=iCellStart && pc<=iCellLast ); + assert( pc>=0 && pc<=iCellLast ); size = pPage->xCellSize(pPage, &src[pc]); cbrk -= size; if( cbrk usableSize ){ @@ -481,7 +1606,7 @@ index a25d540..aa629b5 100644 } assert( cbrk+size<=usableSize && cbrk>=iCellStart ); testcase( cbrk+size==usableSize ); -@@ -70523,7 +70720,13 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ +@@ -72294,7 +72614,13 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ defragment_out: assert( pPage->nFree>=0 ); if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ @@ -496,7 +1621,7 @@ index a25d540..aa629b5 100644 } assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); -@@ -70548,7 +70751,7 @@ defragment_out: +@@ -72319,7 +72645,7 @@ defragment_out: ** will be ignored if adding the extra space to the fragmentation count ** causes the fragmentation count to exceed 60. */ @@ -505,7 +1630,7 @@ index a25d540..aa629b5 100644 const int hdr = pPg->hdrOffset; /* Offset to page header */ u8 * const aData = pPg->aData; /* Page data */ int iAddr = hdr + 1; /* Address of ptr to pc */ -@@ -70580,7 +70783,15 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ +@@ -72351,7 +72677,15 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ return &aData[pc]; }else if( x+pc > maxPC ){ /* This slot extends off the end of the usable part of the page */ @@ -522,7 +1647,7 @@ index a25d540..aa629b5 100644 return 0; }else{ /* The slot remains on the free-list. Reduce its size to account -@@ -70595,14 +70806,25 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ +@@ -72366,14 +72700,25 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ if( pc<=iAddr ){ if( pc ){ /* The next slot in the chain comes before the current slot */ @@ -550,7 +1675,7 @@ index a25d540..aa629b5 100644 } return 0; } -@@ -70651,7 +70873,13 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ +@@ -72421,10 +72766,16 @@ static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ if( top==0 && pPage->pBt->usableSize==65536 ){ top = 65536; }else{ @@ -563,9 +1688,13 @@ index a25d540..aa629b5 100644 + CORRUPT_TYPE_PAGE_BTREE_LEAF, 0, 8, zMsg, NULL); + return SQLITE_CORRUPT_PAGE(&context, pPage); } + }else if( top>(int)pPage->pBt->usableSize ){ +- return SQLITE_CORRUPT_PAGE(pPage); ++ return SQLITE_CORRUPT_PAGE(NULL, pPage); } -@@ -70669,7 +70897,11 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ + /* If there is enough space between gap and top for one more cell pointer, +@@ -72441,7 +72792,11 @@ static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ assert( pSpace+nByte<=data+pPage->pBt->usableSize ); *pIdx = g2 = (int)(pSpace-data); if( g2<=gap ){ @@ -578,7 +1707,7 @@ index a25d540..aa629b5 100644 }else{ return SQLITE_OK; } -@@ -70748,12 +70980,23 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ +@@ -72520,12 +72875,23 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ while( (iFreeBlk = get2byte(&data[iPtr])) iPtr || iFreeBlk==0 || CORRUPT_DB ); -@@ -70765,10 +71008,24 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ +@@ -72537,10 +72903,24 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ */ if( iFreeBlk && iEnd+3>=iFreeBlk ){ nFrag = iFreeBlk - iEnd; @@ -631,7 +1760,7 @@ index a25d540..aa629b5 100644 } iSize = iEnd - iStart; iFreeBlk = get2byte(&data[iFreeBlk]); -@@ -70781,13 +71038,27 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ +@@ -72553,13 +72933,27 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ if( iPtr>hdr+1 ){ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); if( iPtrEnd+3>=iStart ){ @@ -661,7 +1790,7 @@ index a25d540..aa629b5 100644 data[hdr+7] -= nFrag; } pTmp = &data[hdr+5]; -@@ -70796,8 +71067,21 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ +@@ -72573,8 +72967,21 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ /* The new freeblock is at the beginning of the cell content area, ** so just extend the cell content area rather than create another ** freelist entry */ @@ -685,22 +1814,37 @@ index a25d540..aa629b5 100644 put2byte(&data[hdr+1], iFreeBlk); put2byte(&data[hdr+5], iEnd); }else{ -@@ -70882,7 +71166,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){ +@@ -72628,7 +73035,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){ + pPage->intKeyLeaf = 0; + pPage->xCellSize = cellSizePtrIdxLeaf; + pPage->xParseCell = btreeParseCellPtrIndex; +- return SQLITE_CORRUPT_PAGE(pPage); ++ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; ++ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; ++ (void)sqlite3base16Encode(pPage->aData, 8, xBuffer, sizeof(xBuffer)); ++ sqlite3_snprintf(sizeof(zMsg), zMsg, "unrecognized flag:%d, base16:%s", flagByte, xBuffer); ++ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno, ++ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL); ++ return SQLITE_CORRUPT_PAGE(&context, pPage); + } + }else{ + pPage->childPtrSize = 4; +@@ -72659,7 +73072,13 @@ static int decodeFlags(MemPage *pPage, int flagByte){ pPage->pgno, flagByte, pPage->isInit, pPage->intKey, pPage->intKeyLeaf, pPage->leaf, pPage->childPtrSize, pPage->cellOffset, pPage->nCell, pPage->hdrOffset, pPage->minLocal, pPage->maxLocal, g_lastCkptTime); #endif /* LOG_DUMP */ -- return SQLITE_CORRUPT_PAGE(pPage); -+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; -+ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; -+ (void)sqlite3base16Encode(pPage->aData, 8, xBuffer, sizeof(xBuffer)); -+ sqlite3_snprintf(sizeof(zMsg), zMsg, "unrecognized flag:%d, base16:%s", flagByte, xBuffer); -+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno, -+ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL); -+ return SQLITE_CORRUPT_PAGE(&context, pPage); +- return SQLITE_CORRUPT_PAGE(pPage); ++ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; ++ char xBuffer[SQLITE_PRINT_BUF_SIZE] = {0}; ++ (void)sqlite3base16Encode(pPage->aData, 8, xBuffer, sizeof(xBuffer)); ++ sqlite3_snprintf(sizeof(zMsg), zMsg, "unrecognized flag:%d, base16:%s", flagByte, xBuffer); ++ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pPage->pBt->nPage, pPage->pgno, ++ CORRUPT_TYPE_PAGE_BTREE_LEAF, -1, 0, zMsg, NULL); ++ return SQLITE_CORRUPT_PAGE(&context, pPage); + } } - pPage->max1bytePayload = pBt->max1bytePayload; return SQLITE_OK; -@@ -70933,12 +71223,20 @@ static int btreeComputeFreeSpace(MemPage *pPage){ +@@ -72710,12 +73129,20 @@ static int btreeComputeFreeSpace(MemPage *pPage){ /* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will ** always be at least one cell before the first freeblock. */ @@ -723,7 +1867,7 @@ index a25d540..aa629b5 100644 } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); -@@ -70948,11 +71246,19 @@ static int btreeComputeFreeSpace(MemPage *pPage){ +@@ -72725,11 +73152,19 @@ static int btreeComputeFreeSpace(MemPage *pPage){ } if( next>0 ){ /* Freeblock not in ascending order */ @@ -745,7 +1889,7 @@ index a25d540..aa629b5 100644 } } -@@ -70964,7 +71270,13 @@ static int btreeComputeFreeSpace(MemPage *pPage){ +@@ -72741,7 +73176,13 @@ static int btreeComputeFreeSpace(MemPage *pPage){ ** area, according to the page header, lies within the page. */ if( nFree>usableSize || nFree nFree = (u16)(nFree - iCellFirst); return SQLITE_OK; -@@ -70995,12 +71307,20 @@ static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ +@@ -72772,12 +73213,20 @@ static SQLITE_NOINLINE int btreeCellSizeCheck(MemPage *pPage){ testcase( pc==iCellFirst ); testcase( pc==iCellLast ); if( pc iCellLast ){ @@ -783,7 +1927,7 @@ index a25d540..aa629b5 100644 } } return SQLITE_OK; -@@ -71032,7 +71352,7 @@ static int btreeInitPage(MemPage *pPage){ +@@ -72809,7 +73258,7 @@ static int btreeInitPage(MemPage *pPage){ /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating ** the b-tree page type. */ if( decodeFlags(pPage, data[0]) ){ @@ -792,7 +1936,7 @@ index a25d540..aa629b5 100644 } assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); -@@ -71046,7 +71366,12 @@ static int btreeInitPage(MemPage *pPage){ +@@ -72823,7 +73272,12 @@ static int btreeInitPage(MemPage *pPage){ pPage->nCell = get2byte(&data[3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ @@ -806,34 +1950,20 @@ index a25d540..aa629b5 100644 } testcase( pPage->nCell==MX_CELL(pBt) ); /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only -@@ -71201,7 +71526,11 @@ static int getAndInitPage( - assert( pCur==0 || pCur->iPage>0 ); +@@ -72966,7 +73420,11 @@ static int getAndInitPage( if( pgno>btreePagecount(pBt) ){ -- rc = SQLITE_CORRUPT_BKPT; -+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; -+ sqlite3_snprintf(sizeof(zMsg), zMsg, "page number(%u) > db file size(%u)", pgno, btreePagecount(pBt)); -+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF, -+ -1, 0, zMsg, NULL); -+ rc = SQLITE_CORRUPT_REPORT(&context); - goto getAndInitPage_error1; + *ppPage = 0; +- return SQLITE_CORRUPT_BKPT; ++ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; ++ sqlite3_snprintf(sizeof(zMsg), zMsg, "page number(%u) > db file size(%u)", pgno, btreePagecount(pBt)); ++ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF, ++ -1, 0, zMsg, NULL); ++ return SQLITE_CORRUPT_REPORT(&context); } rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly); -@@ -71222,7 +71551,12 @@ static int getAndInitPage( - /* If obtaining a child page for a cursor, we must verify that the page is - ** compatible with the root page. */ - if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ -- rc = SQLITE_CORRUPT_PGNO(pgno); -+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; -+ sqlite3_snprintf(sizeof(zMsg), zMsg, "check btree page, nCell:%u, intKey:%u, cursor->curIntKey:%u", -+ (*ppPage)->nCell, (*ppPage)->intKey, pCur->curIntKey); -+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(pBt->nPage, pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF, -+ -1, 0, zMsg, NULL); -+ rc = SQLITE_CORRUPT_REPORT_PGNO(&context); - goto getAndInitPage_error2; - } - return SQLITE_OK; -@@ -72656,7 +72990,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ + if( rc ){ +@@ -74439,7 +74897,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ if( eType==PTRMAP_OVERFLOW2 ){ /* The pointer is always the first 4 bytes of the page in this case. */ if( get4byte(pPage->aData)!=iFrom ){ @@ -847,7 +1977,7 @@ index a25d540..aa629b5 100644 } put4byte(pPage->aData, iTo); }else{ -@@ -72675,7 +73014,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ +@@ -74458,7 +74921,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ pPage->xParseCell(pPage, pCell, &info); if( info.nLocal pPage->aData+pPage->pBt->usableSize ){ @@ -862,7 +1992,7 @@ index a25d540..aa629b5 100644 } if( iFrom==get4byte(pCell+info.nSize-4) ){ put4byte(pCell+info.nSize-4, iTo); -@@ -72684,7 +73029,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ +@@ -74467,7 +74936,13 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ } }else{ if( pCell+4 > pPage->aData+pPage->pBt->usableSize ){ @@ -877,7 +2007,7 @@ index a25d540..aa629b5 100644 } if( get4byte(pCell)==iFrom ){ put4byte(pCell, iTo); -@@ -72696,7 +73047,11 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ +@@ -74479,7 +74954,11 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ if( i==nCell ){ if( eType!=PTRMAP_BTREE || get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ @@ -890,7 +2020,7 @@ index a25d540..aa629b5 100644 } put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); } -@@ -72829,7 +73184,11 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ +@@ -74612,7 +75091,11 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){ return rc; } if( eType==PTRMAP_ROOTPAGE ){ @@ -903,7 +2033,7 @@ index a25d540..aa629b5 100644 } if( eType==PTRMAP_FREEPAGE ){ -@@ -73907,7 +74266,12 @@ static int accessPayload( +@@ -75688,7 +76171,12 @@ static int accessPayload( assert( eOp==0 || eOp==1 ); assert( pCur->eState==CURSOR_VALID ); if( pCur->ix>=pPage->nCell ){ @@ -917,7 +2047,7 @@ index a25d540..aa629b5 100644 } assert( cursorHoldsMutex(pCur) ); -@@ -73922,7 +74286,12 @@ static int accessPayload( +@@ -75703,7 +76191,12 @@ static int accessPayload( ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ** but is recast into its current form to avoid integer overflow problems */ @@ -931,7 +2061,7 @@ index a25d540..aa629b5 100644 } /* Check if data must be read/written to/from the btree page itself. */ -@@ -73984,7 +74353,16 @@ static int accessPayload( +@@ -75765,7 +76258,16 @@ static int accessPayload( assert( rc==SQLITE_OK && amt>0 ); while( nextPage ){ /* If required, populate the overflow page-list cache. */ @@ -949,7 +2079,7 @@ index a25d540..aa629b5 100644 assert( pCur->aOverflow[iIdx]==0 || pCur->aOverflow[iIdx]==nextPage || CORRUPT_DB ); -@@ -74069,7 +74447,11 @@ static int accessPayload( +@@ -75850,7 +76352,11 @@ static int accessPayload( if( rc==SQLITE_OK && amt>0 ){ /* Overflow chain ends prematurely */ @@ -962,7 +2092,16 @@ index a25d540..aa629b5 100644 } return rc; } -@@ -74345,7 +74727,12 @@ static int moveToRoot(BtCursor *pCur){ +@@ -76002,7 +76508,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){ + && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) + ){ + releasePage(pCur->pPage); +- rc = SQLITE_CORRUPT_PGNO(newPgno); ++ rc = SQLITE_CORRUPT_PGNO(newPgno, NULL); + } + if( rc ){ + pCur->pPage = pCur->apPage[--pCur->iPage]; +@@ -76137,7 +76643,12 @@ static int moveToRoot(BtCursor *pCur){ ** (or the freelist). */ assert( pRoot->intKey==1 || pRoot->intKey==0 ); if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ @@ -976,7 +2115,7 @@ index a25d540..aa629b5 100644 } skip_init: -@@ -74599,7 +74986,11 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( +@@ -76391,7 +76902,11 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto( if( pPage->intKeyLeaf ){ while( 0x80 <= *(pCell++) ){ if( pCell>=pPage->aDataEnd ){ @@ -989,7 +2128,7 @@ index a25d540..aa629b5 100644 } } } -@@ -74882,7 +75273,12 @@ bypass_moveto_root: +@@ -76674,7 +77189,12 @@ bypass_moveto_root: testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ @@ -1003,7 +2142,16 @@ index a25d540..aa629b5 100644 goto moveto_index_finish; } pCellKey = sqlite3Malloc( nCell+nOverrun ); -@@ -75210,7 +75606,13 @@ static int allocateBtreePage( +@@ -76748,7 +77268,7 @@ bypass_moveto_root: + && (pCur->pPage->nCell<1 || pCur->pPage->intKey!=pCur->curIntKey) + ){ + releasePage(pCur->pPage); +- rc = SQLITE_CORRUPT_PGNO(chldPg); ++ rc = SQLITE_CORRUPT_PGNO(chldPg, NULL); + } + if( rc ){ + pCur->pPage = pCur->apPage[--pCur->iPage]; +@@ -77032,7 +77552,13 @@ static int allocateBtreePage( n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); if( n>=mxPage ){ @@ -1018,7 +2166,7 @@ index a25d540..aa629b5 100644 } if( n>0 ){ /* There are pages on the freelist. Reuse one of those pages. */ -@@ -75266,7 +75668,12 @@ static int allocateBtreePage( +@@ -77088,7 +77614,12 @@ static int allocateBtreePage( } testcase( iTrunk==mxPage ); if( iTrunk>mxPage || nSearch++ > n ){ @@ -1032,8 +2180,8 @@ index a25d540..aa629b5 100644 }else{ rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0); } -@@ -75295,7 +75702,14 @@ static int allocateBtreePage( - TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); +@@ -77117,7 +77648,14 @@ static int allocateBtreePage( + TRACE(("ALLOCATE: %u trunk - %u free pages left\n", *pPgno, n-1)); }else if( k>(u32)(pBt->usableSize/4 - 2) ){ /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_PGNO(iTrunk); @@ -1048,7 +2196,7 @@ index a25d540..aa629b5 100644 goto end_allocate_page; #ifndef SQLITE_OMIT_AUTOVACUUM }else if( searchList -@@ -75329,7 +75743,14 @@ static int allocateBtreePage( +@@ -77151,7 +77689,14 @@ static int allocateBtreePage( MemPage *pNewTrunk; Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ @@ -1064,7 +2212,7 @@ index a25d540..aa629b5 100644 goto end_allocate_page; } testcase( iNewTrunk==mxPage ); -@@ -75394,7 +75815,14 @@ static int allocateBtreePage( +@@ -77216,7 +77761,14 @@ static int allocateBtreePage( iPage = get4byte(&aData[8+closest*4]); testcase( iPage==mxPage ); if( iPage>mxPage || iPage<2 ){ @@ -1080,7 +2228,7 @@ index a25d540..aa629b5 100644 goto end_allocate_page; } testcase( iPage==mxPage ); -@@ -75579,7 +76007,14 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ +@@ -77401,7 +77953,14 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){ nLeaf = get4byte(&pTrunk->aData[4]); assert( pBt->usableSize>32 ); if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ @@ -1096,7 +2244,7 @@ index a25d540..aa629b5 100644 goto freepage_out; } if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ -@@ -75668,7 +76103,12 @@ static SQLITE_NOINLINE int clearCellOverflow( +@@ -77490,7 +78049,12 @@ static SQLITE_NOINLINE int clearCellOverflow( testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd ); if( pCell + pInfo->nSize > pPage->aDataEnd ){ /* Cell extends past end of page */ @@ -1110,7 +2258,7 @@ index a25d540..aa629b5 100644 } ovflPgno = get4byte(pCell + pInfo->nSize - 4); pBt = pPage->pBt; -@@ -75685,7 +76125,14 @@ static SQLITE_NOINLINE int clearCellOverflow( +@@ -77507,7 +78071,14 @@ static SQLITE_NOINLINE int clearCellOverflow( /* 0 is not a legal page number and page 1 cannot be an ** overflow page. Therefore if ovflPgno<2 or past the end of the ** file the database must be corrupt. */ @@ -1126,7 +2274,7 @@ index a25d540..aa629b5 100644 } if( nOvfl ){ rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext); -@@ -75958,7 +76405,12 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ +@@ -77780,7 +78351,12 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ testcase( pc==(u32)get2byte(&data[hdr+5]) ); testcase( pc+sz==pPage->pBt->usableSize ); if( pc+sz > pPage->pBt->usableSize ){ @@ -1140,23 +2288,7 @@ index a25d540..aa629b5 100644 return; } rc = freeSpace(pPage, pc, sz); -@@ -77868,7 +78320,14 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){ - if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd - || pCur->info.pPayload < pPage->aData + pPage->cellOffset - ){ -- return SQLITE_CORRUPT_BKPT; -+ char zMsg[SQLITE_PRINT_CORRUPT_SIZE] = {0}; -+ sqlite3_snprintf(sizeof(zMsg), zMsg, -+ "cell payload cursor point to(%d), size:%u overlaps with non-cell content area:[%u, %d]", -+ (int)(pCur->info.pPayload - pPage->aData), pCur->info.nLocal, pPage->cellOffset, -+ (int)(pPage->aDataEnd - pPage->aData)); -+ sqlite3CorruptContext context = SQLITE_CORRUPT_CONTEXT(0, pPage->pgno, CORRUPT_TYPE_PAGE_BTREE_LEAF, -+ -1, 0, zMsg, NULL); -+ return SQLITE_CORRUPT_REPORT(&context); - } - /* Overwrite the local portion first */ - rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX, -@@ -83064,7 +83523,7 @@ static int growOpArray(Vdbe *v, int nOp){ +@@ -85096,7 +85672,7 @@ static int growOpArray(Vdbe *v, int nOp){ ** ** Other useful labels for breakpoints include: ** test_trace_breakpoint(pc,pOp) @@ -1165,7 +2297,7 @@ index a25d540..aa629b5 100644 ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ -@@ -90722,7 +91181,7 @@ SQLITE_API int sqlite3_found_count = 0; +@@ -93316,7 +93892,7 @@ SQLITE_API int sqlite3_found_count = 0; ** ** Other useful labels for breakpoints include: ** test_addop_breakpoint(pc,pOp) @@ -1174,11 +2306,19 @@ index a25d540..aa629b5 100644 ** sqlite3MisuseError(lineno) ** sqlite3CantopenError(lineno) */ -@@ -174509,6 +174968,12 @@ SQLITE_API int sqlite3_config(int op, ...){ +@@ -93686,7 +94262,6 @@ static u16 numericType(Mem *pMem){ + testcase( pMem->flags & MEM_Str ); + testcase( pMem->flags & MEM_Blob ); + return computeNumericType(pMem); +- return 0; + } + + #ifdef SQLITE_DEBUG +@@ -179837,6 +180412,12 @@ SQLITE_API int sqlite3_config(int op, ...){ + #endif break; } - #endif /* SQLITE_OMIT_DESERIALIZE */ -+ case SQLITE_CONFIG_CORRUPTION: { ++ case SQLITE_CONFIG_CORRUPTION: { + typedef void(*CORRUPTION_FUNC_t)(void*, const void*); + sqlite3GlobalConfig.xCorruption = va_arg(ap, CORRUPTION_FUNC_t); + sqlite3GlobalConfig.pCorruptionArg = va_arg(ap, void*); @@ -1187,7 +2327,7 @@ index a25d540..aa629b5 100644 default: { rc = SQLITE_ERROR; -@@ -177587,9 +178052,21 @@ SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType){ +@@ -183021,9 +183602,21 @@ SQLITE_PRIVATE int sqlite3ReportError(int iErr, int lineno, const char *zType){ zType, lineno, 20+sqlite3_sourceid()); return iErr; } @@ -1211,6 +2351,64 @@ index a25d540..aa629b5 100644 } SQLITE_PRIVATE int sqlite3MisuseError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); +@@ -183245,12 +183838,16 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo + *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); + rc = SQLITE_OK; + }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){ ++#ifndef SQLITE_CKSUMVFS_STATIC ++ rc = SQLITE_OK; ++#else + int iNew = *(int*)pArg; + *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree); + if( iNew>=0 && iNew<=255 ){ + sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); + } + rc = SQLITE_OK; ++#endif + }else if( op==SQLITE_FCNTL_RESET_CACHE ){ + sqlite3BtreeClearCache(pBtree); + rc = SQLITE_OK; +@@ -255862,10 +256459,11 @@ static int MetaDwrCheckMeta(Btree *pBt) { + } + if (rc != SQLITE_OK) { + sqlite3_log(rc, "Meta integrity check go wrong"); ++ sqlite3_free(errStr); + return rc; + } +- sqlite3_log(SQLITE_WARNING_DUMP, "Meta integrity check %s", errStr); +- sqlite3DbFree(pBt->db, errStr); ++ sqlite3_log(SQLITE_WARNING_DUMP, "Integrity check %s", errStr); ++ sqlite3_free(errStr); + return SQLITE_CORRUPT; + } + +@@ -256317,6 +256915,26 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime + + // hw export the symbols + #ifdef SQLITE_EXPORT_SYMBOLS ++#ifndef SQLITE_CKSUMVFS_STATIC ++int sqlite3_register_cksumvfs(const char *NotUsed){ ++ return SQLITE_MISUSE; ++} ++int sqlite3_unregister_cksumvfs(void){ ++ return SQLITE_MISUSE; ++} ++ ++struct sqlite3_api_routines_cksumvfs { ++ int (*register_cksumvfs)(const char *); ++ int (*unregister_cksumvfs)(); ++}; ++typedef struct sqlite3_api_routines_cksumvfs sqlite3_api_routines_cksumvfs; ++static const sqlite3_api_routines_cksumvfs sqlite3CksumvfsApis = { ++ sqlite3_register_cksumvfs, ++ sqlite3_unregister_cksumvfs ++}; ++ ++EXPORT_SYMBOLS const sqlite3_api_routines_cksumvfs *sqlite3_export_cksumvfs_symbols = &sqlite3CksumvfsApis; ++#endif + struct sqlite3_api_routines_hw { + int (*initialize)(); + int (*config)(int,...); -- -2.47.0.windows.2 +2.34.1 diff --git a/patch/0007-Support-Binlog.patch b/patch/0006-Support-Binlog.patch similarity index 95% rename from patch/0007-Support-Binlog.patch rename to patch/0006-Support-Binlog.patch index ce43a74b78ed554e54a1b70ad5604b1f70be58f6..6ca76478c1d9e0ad6d4294a19150c859415e778b 100644 --- a/patch/0007-Support-Binlog.patch +++ b/patch/0006-Support-Binlog.patch @@ -1,17 +1,17 @@ -From bd1f40f6fa41ab9470436efbe641e1f1c2ac9b33 Mon Sep 17 00:00:00 2001 -From: Liu Hongyang -Date: Fri, 16 May 2025 17:27:13 +0800 -Subject: [PATCH] Support-Binlog +From 71534219f865058cc99445f45bb98587dc5af6b3 Mon Sep 17 00:00:00 2001 +From: zeng +Date: Sun, 18 May 2025 23:41:00 +0800 +Subject: [PATCH] Support binlog --- src/sqlite3.c | 1288 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1279 insertions(+), 9 deletions(-) diff --git a/src/sqlite3.c b/src/sqlite3.c -index c82804c..8dddbf6 100644 +index 2a4e87f..01c8b5b 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c -@@ -2839,7 +2839,9 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +@@ -2938,7 +2938,9 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); #define SQLITE_DBCONFIG_SET_SHAREDBLOCK 2004 #define SQLITE_DBCONFIG_USE_SHAREDBLOCK 2005 #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */ @@ -22,7 +22,7 @@ index c82804c..8dddbf6 100644 /* ** CAPI3REF: Set the Last Insert Rowid value. ** METHOD: sqlite3 -@@ -5197,6 +5199,19 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); +@@ -5351,6 +5353,19 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); SQLITE_API int sqlite3_set_droptable_handle(sqlite3*, void (*xFunc)(sqlite3*,const char*,const char*)); #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */ @@ -42,7 +42,7 @@ index c82804c..8dddbf6 100644 /* ** CAPI3REF: Number of columns in a result set ** METHOD: sqlite3_stmt -@@ -17029,6 +17044,195 @@ typedef struct CodecParameter { +@@ -17617,6 +17632,195 @@ typedef struct CodecParameter { } CodecParameter; #endif /* defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) */ @@ -238,7 +238,7 @@ index c82804c..8dddbf6 100644 /* ** Each database connection is an instance of the following structure. */ -@@ -17181,6 +17385,9 @@ struct sqlite3 { +@@ -17770,6 +17974,9 @@ struct sqlite3 { #if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) CodecParameter codecParm; #endif /* defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) */ @@ -248,7 +248,7 @@ index c82804c..8dddbf6 100644 }; /* -@@ -22990,6 +23197,12 @@ struct Vdbe { +@@ -23666,6 +23873,12 @@ struct Vdbe { int startPos; int addedRows; #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */ @@ -261,7 +261,7 @@ index c82804c..8dddbf6 100644 }; /* -@@ -86224,7 +86437,11 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ +@@ -88435,7 +88648,11 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ rc = sqlite3BtreeCommitPhaseOne(pBt, 0); } } @@ -274,7 +274,7 @@ index c82804c..8dddbf6 100644 /* Do the commit only if all databases successfully complete phase 1. ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an ** IO error while deleting or truncating a journal file. It is unlikely, -@@ -86372,6 +86589,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ +@@ -88583,6 +88800,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){ ** transaction is already guaranteed, but some stray 'cold' journals ** may be lying around. Returning an error code won't help matters. */ @@ -284,7 +284,7 @@ index c82804c..8dddbf6 100644 disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i nDb; i++){ -@@ -86654,6 +86874,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ +@@ -88868,6 +89088,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ }else if( eStatementOp==0 ){ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){ eStatementOp = SAVEPOINT_RELEASE; @@ -294,7 +294,7 @@ index c82804c..8dddbf6 100644 }else if( p->errorAction==OE_Abort ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ -@@ -86874,6 +87097,9 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ +@@ -89090,6 +89313,9 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){ fclose(out); } } @@ -304,7 +304,7 @@ index c82804c..8dddbf6 100644 #endif return p->rc & db->errMask; } -@@ -86978,6 +87204,9 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ +@@ -89194,6 +89420,9 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ sqlite3DbFree(db, p->aScan); } #endif @@ -314,7 +314,7 @@ index c82804c..8dddbf6 100644 } /* -@@ -89495,6 +89724,44 @@ SQLITE_API int sqlite3_set_droptable_handle(sqlite3 *db, void (*xFunc)(sqlite3*, +@@ -91886,6 +92115,44 @@ SQLITE_API int sqlite3_set_droptable_handle(sqlite3 *db, void (*xFunc)(sqlite3*, } #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */ @@ -359,10 +359,10 @@ index c82804c..8dddbf6 100644 /* ** This is the top-level implementation of sqlite3_step(). Call ** sqlite3Step() to do most of the work. If a schema error occurs, -@@ -96801,6 +97068,15 @@ case OP_Insert: { - x.nZero = 0; +@@ -99584,6 +99851,15 @@ case OP_Insert: { } x.pKey = 0; + assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT ); +#ifdef SQLITE_ENABLE_BINLOG + if ( pOp->p4.pTab + && !sqlite3IsSkipWriteBinlog(p) @@ -375,7 +375,7 @@ index c82804c..8dddbf6 100644 rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), seekResult -@@ -96910,6 +97186,21 @@ case OP_Delete: { +@@ -99699,6 +99975,21 @@ case OP_Delete: { assert( CORRUPT_DB || pC->movetoTarget==iKey ); } #endif @@ -397,7 +397,7 @@ index c82804c..8dddbf6 100644 /* If the update-hook or pre-update-hook will be invoked, set zDb to ** the name of the db to pass as to it. Also set local pTab to a copy -@@ -97526,6 +97817,14 @@ case OP_IdxInsert: { /* in2 */ +@@ -100322,6 +100613,14 @@ case OP_IdxInsert: { /* in2 */ x.pKey = pIn2->z; x.aMem = aMem + pOp->p3; x.nMem = (u16)pOp->p4.i; @@ -412,7 +412,7 @@ index c82804c..8dddbf6 100644 rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)), ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0) -@@ -113449,7 +113748,11 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( +@@ -116953,7 +117252,11 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( renameReloadSchema(pParse, iDb, INITFLAG_AlterRename); renameTestSchema(pParse, zDb, iDb==1, "after rename", 0); @@ -425,7 +425,7 @@ index c82804c..8dddbf6 100644 exit_rename_table: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zName); -@@ -113626,6 +113929,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ +@@ -117135,6 +117438,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ zTab, zDb ); } @@ -435,7 +435,7 @@ index c82804c..8dddbf6 100644 } } -@@ -113837,7 +114143,13 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( +@@ -117346,7 +117652,13 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( /* Drop and reload the database schema. */ renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename); renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1); @@ -450,7 +450,7 @@ index c82804c..8dddbf6 100644 exit_rename_column: sqlite3SrcListDelete(db, pSrc); sqlite3DbFree(db, zOld); -@@ -115424,7 +115736,13 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T +@@ -118957,7 +119269,13 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr); } @@ -465,7 +465,7 @@ index c82804c..8dddbf6 100644 exit_drop_column: sqlite3DbFree(db, zCol); sqlite3SrcListDelete(db, pSrc); -@@ -119698,6 +120016,9 @@ SQLITE_PRIVATE void sqlite3StartTable( +@@ -123295,6 +123613,9 @@ SQLITE_PRIVATE void sqlite3StartTable( sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3VdbeAddOp0(v, OP_Close); @@ -475,7 +475,7 @@ index c82804c..8dddbf6 100644 } /* Normal (non-error) return. */ -@@ -121344,7 +121665,13 @@ SQLITE_PRIVATE void sqlite3CreateView( +@@ -124963,7 +125284,13 @@ SQLITE_PRIVATE void sqlite3CreateView( /* Use sqlite3EndTable() to add the view to the schema table */ sqlite3EndTable(pParse, 0, &sEnd, 0, 0); @@ -490,7 +490,7 @@ index c82804c..8dddbf6 100644 create_view_fail: sqlite3SelectDelete(db, pSelect); if( IN_RENAME_OBJECT ){ -@@ -121868,6 +122195,9 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, +@@ -125486,6 +125813,9 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, sqlite3FkDropTable(pParse, pName, pTab); } sqlite3CodeDropTable(pParse, pTab, iDb, isView); @@ -500,7 +500,7 @@ index c82804c..8dddbf6 100644 } exit_drop_table: -@@ -122726,6 +123056,9 @@ SQLITE_PRIVATE void sqlite3CreateIndex( +@@ -126344,6 +126674,9 @@ SQLITE_PRIVATE void sqlite3CreateIndex( /* A named index with an explicit CREATE INDEX statement */ zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s", onError==OE_None ? "" : " UNIQUE", n, pName->z); @@ -510,7 +510,7 @@ index c82804c..8dddbf6 100644 }else{ /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ /* zStmt = sqlite3MPrintf(""); */ -@@ -122928,6 +123261,9 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists +@@ -126546,6 +126879,9 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists sqlite3ChangeCookie(pParse, iDb); destroyRootPage(pParse, pIndex->tnum, iDb); sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0); @@ -520,7 +520,7 @@ index c82804c..8dddbf6 100644 } exit_drop_index: -@@ -123452,6 +123788,9 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){ +@@ -127070,6 +127406,9 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){ } } sqlite3VdbeAddOp0(v, OP_AutoCommit); @@ -530,7 +530,7 @@ index c82804c..8dddbf6 100644 } /* -@@ -123474,6 +123813,9 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse *pParse, int eType){ +@@ -127092,6 +127431,9 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse *pParse, int eType){ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, isRollback); @@ -540,7 +540,7 @@ index c82804c..8dddbf6 100644 } } -@@ -123494,6 +123836,9 @@ SQLITE_PRIVATE void sqlite3Savepoint(Parse *pParse, int op, Token *pName){ +@@ -127112,6 +127454,9 @@ SQLITE_PRIVATE void sqlite3Savepoint(Parse *pParse, int op, Token *pName){ return; } sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); @@ -550,7 +550,7 @@ index c82804c..8dddbf6 100644 } } -@@ -133458,6 +133803,13 @@ typedef int (*sqlite3_loadext_entry)( +@@ -137480,6 +137825,13 @@ typedef int (*sqlite3_loadext_entry)( /* handle after drop table done */ #define sqlite3_set_droptable_handle sqlite3_api->set_droptable_handle #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */ @@ -564,7 +564,7 @@ index c82804c..8dddbf6 100644 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) -@@ -135485,6 +135837,9 @@ SQLITE_PRIVATE void sqlite3Pragma( +@@ -139533,6 +139885,9 @@ SQLITE_PRIVATE void sqlite3Pragma( if( v==0 ) return; sqlite3VdbeRunOnlyOnce(v); pParse->nMem = 2; @@ -574,7 +574,7 @@ index c82804c..8dddbf6 100644 /* Interpret the [schema.] part of the pragma statement. iDb is the ** index of the database this pragma is being applied to in db.aDb[]. */ -@@ -147457,7 +147812,13 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( +@@ -152286,7 +152641,13 @@ SQLITE_PRIVATE void sqlite3BeginTrigger( if (tr_tm == TK_INSTEAD){ tr_tm = TK_BEFORE; } @@ -589,7 +589,7 @@ index c82804c..8dddbf6 100644 /* Build the Trigger object */ pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger)); if( pTrigger==0 ) goto trigger_cleanup; -@@ -147852,6 +148213,13 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) +@@ -152681,6 +153042,13 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr) goto drop_trigger_cleanup; } sqlite3DropTriggerPtr(pParse, pTrigger); @@ -603,7 +603,7 @@ index c82804c..8dddbf6 100644 drop_trigger_cleanup: sqlite3SrcListDelete(db, pName); -@@ -175266,6 +175634,13 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ +@@ -180656,6 +181024,13 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ break; } #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */ @@ -617,7 +617,7 @@ index c82804c..8dddbf6 100644 default: { static const struct { int op; /* The opcode */ -@@ -175683,6 +176058,9 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ +@@ -181083,6 +181458,9 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){ sqlite3CollapseDatabaseArray(db); assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); @@ -627,7 +627,7 @@ index c82804c..8dddbf6 100644 /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. -@@ -177881,6 +178259,9 @@ opendb_out: +@@ -183308,6 +183686,9 @@ opendb_out: db->mDropSchemaName = NULL; db->xDropTableHandle = NULL; #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */ @@ -637,10 +637,10 @@ index c82804c..8dddbf6 100644 *ppDb = db; #ifdef SQLITE_ENABLE_SQLLOG if( sqlite3GlobalConfig.xSqllog ){ -@@ -247520,6 +247901,883 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime - } +@@ -256913,6 +257294,883 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime } #endif + +#ifdef SQLITE_ENABLE_BINLOG +/************** Begin of binlog implement ************************************/ +SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db) @@ -1521,7 +1521,7 @@ index c82804c..8dddbf6 100644 // hw export the symbols #ifdef SQLITE_EXPORT_SYMBOLS #ifndef SQLITE_CKSUMVFS_STATIC -@@ -247550,6 +248808,9 @@ struct sqlite3_api_routines_hw { +@@ -256942,6 +258200,9 @@ struct sqlite3_api_routines_hw { int (*key_v2)(sqlite3*,const char*,const void*,int); int (*rekey)(sqlite3*,const void*,int); int (*rekey_v2)(sqlite3*,const char*,const void*,int); @@ -1531,7 +1531,7 @@ index c82804c..8dddbf6 100644 }; typedef struct sqlite3_api_routines_hw sqlite3_api_routines_hw; -@@ -247560,13 +248821,22 @@ static const sqlite3_api_routines_hw sqlite3HwApis = { +@@ -256952,13 +258213,22 @@ static const sqlite3_api_routines_hw sqlite3HwApis = { sqlite3_key, sqlite3_key_v2, sqlite3_rekey, @@ -1557,5 +1557,5 @@ index c82804c..8dddbf6 100644 EXPORT_SYMBOLS const sqlite3_api_routines *sqlite3_export_symbols = &sqlite3Apis; -- -2.25.1 +2.21.0.windows.1 diff --git a/patch/0006-add-extention-cksumvfs-and-check-page.patch b/patch/0006-add-extention-cksumvfs-and-check-page.patch deleted file mode 100644 index b22b8cf5049f160faf156ff0b3de9965c672bb54..0000000000000000000000000000000000000000 --- a/patch/0006-add-extention-cksumvfs-and-check-page.patch +++ /dev/null @@ -1,1080 +0,0 @@ -From 4c555db067d3fb7aa245c3c0597151e863b94d69 Mon Sep 17 00:00:00 2001 -From: wanghaishuo -Date: Sat, 12 Apr 2025 10:59:29 +0800 -Subject: [PATCH 6/7] add extention:cksumvfs and check page - -Signed-off-by: ryne3366 ---- - ext/misc/cksumvfs.c | 916 ++++++++++++++++++++++++++++++++++++++++++++ - src/sqlite3.c | 99 ++++- - 2 files changed, 1008 insertions(+), 7 deletions(-) - create mode 100644 ext/misc/cksumvfs.c - -diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c -new file mode 100644 -index 0000000..6f4c55c ---- /dev/null -+++ b/ext/misc/cksumvfs.c -@@ -0,0 +1,916 @@ -+/* -+** 2020-04-20 -+** -+** The author disclaims copyright to this source code. In place of -+** a legal notice, here is a blessing: -+** -+** May you do good and not evil. -+** May you find forgiveness for yourself and forgive others. -+** May you share freely, never taking more than you give. -+** -+****************************************************************************** -+** -+** This file implements a VFS shim that writes a checksum on each page -+** of an SQLite database file. When reading pages, the checksum is verified -+** and an error is raised if the checksum is incorrect. -+** -+** COMPILING -+** -+** This extension requires SQLite 3.32.0 or later. It uses the -+** sqlite3_database_file_object() interface which was added in -+** version 3.32.0, so it will not link with an earlier version of -+** SQLite. -+** -+** To build this extension as a separately loaded shared library or -+** DLL, use compiler command-lines similar to the following: -+** -+** (linux) gcc -fPIC -shared cksumvfs.c -o cksumvfs.so -+** (mac) clang -fPIC -dynamiclib cksumvfs.c -o cksumvfs.dylib -+** (windows) cl cksumvfs.c -link -dll -out:cksumvfs.dll -+** -+** You may want to add additional compiler options, of course, -+** according to the needs of your project. -+** -+** If you want to statically link this extension with your product, -+** then compile it like any other C-language module but add the -+** "-DSQLITE_CKSUMVFS_STATIC" option so that this module knows that -+** it is being statically linked rather than dynamically linked -+** -+** LOADING -+** -+** To load this extension as a shared library, you first have to -+** bring up a dummy SQLite database connection to use as the argument -+** to the sqlite3_load_extension() API call. Then you invoke the -+** sqlite3_load_extension() API and shutdown the dummy database -+** connection. All subsequent database connections that are opened -+** will include this extension. For example: -+** -+** sqlite3 *db; -+** sqlite3_open(":memory:", &db); -+** sqlite3_load_extension(db, "./cksumvfs"); -+** sqlite3_close(db); -+** -+** If this extension is compiled with -DSQLITE_CKSUMVFS_STATIC and -+** statically linked against the application, initialize it using -+** a single API call as follows: -+** -+** sqlite3_register_cksumvfs(); -+** -+** Cksumvfs is a VFS Shim. When loaded, "cksmvfs" becomes the new -+** default VFS and it uses the prior default VFS as the next VFS -+** down in the stack. This is normally what you want. However, in -+** complex situations where multiple VFS shims are being loaded, -+** it might be important to ensure that cksumvfs is loaded in the -+** correct order so that it sequences itself into the default VFS -+** Shim stack in the right order. -+** -+** USING -+** -+** Open database connections using the sqlite3_open() or -+** sqlite3_open_v2() interfaces, as normal. Ordinary database files -+** (without a checksum) will operate normally. Databases with -+** checksums will return an SQLITE_IOERR_DATA error if a page is -+** encountered that contains an invalid checksum. -+** -+** Checksumming only works on databases that have a reserve-bytes -+** value of exactly 8. The default value for reserve-bytes is 0. -+** Hence, newly created database files will omit the checksum by -+** default. To create a database that includes a checksum, change -+** the reserve-bytes value to 8 by runing: -+** -+** int n = 8; -+** sqlite3_file_control(db, 0, SQLITE_FCNTL_RESERVE_BYTES, &n); -+** -+** If you do this immediately after creating a new database file, -+** before anything else has been written into the file, then that -+** might be all that you need to do. Otherwise, the API call -+** above should be followed by: -+** -+** sqlite3_exec(db, "VACUUM", 0, 0, 0); -+** -+** It never hurts to run the VACUUM, even if you don't need it. -+** If the database is in WAL mode, you should shutdown and -+** reopen all database connections before continuing. -+** -+** From the CLI, use the ".filectrl reserve_bytes 8" command, -+** followed by "VACUUM;". -+** -+** Note that SQLite allows the number of reserve-bytes to be -+** increased but not decreased. So if a database file already -+** has a reserve-bytes value greater than 8, there is no way to -+** activate checksumming on that database, other than to dump -+** and restore the database file. Note also that other extensions -+** might also make use of the reserve-bytes. Checksumming will -+** be incompatible with those other extensions. -+** -+** VERIFICATION OF CHECKSUMS -+** -+** If any checksum is incorrect, the "PRAGMA quick_check" command -+** will find it. To verify that checksums are actually enabled -+** and running, use the following query: -+** -+** SELECT count(*), verify_checksum(data) -+** FROM sqlite_dbpage -+** GROUP BY 2; -+** -+** There are three possible outputs form the verify_checksum() -+** function: 1, 0, and NULL. 1 is returned if the checksum is -+** correct. 0 is returned if the checksum is incorrect. NULL -+** is returned if the page is unreadable. If checksumming is -+** enabled, the read will fail if the checksum is wrong, so the -+** usual result from verify_checksum() on a bad checksum is NULL. -+** -+** If everything is OK, the query above should return a single -+** row where the second column is 1. Any other result indicates -+** either that there is a checksum error, or checksum validation -+** is disabled. -+** -+** CONTROLLING CHECKSUM VERIFICATION -+** -+** The cksumvfs extension implements a new PRAGMA statement that can -+** be used to disable, re-enable, or query the status of checksum -+** verification: -+** -+** PRAGMA checksum_verification; -- query status -+** PRAGMA checksum_verification=OFF; -- disable verification -+** PRAGMA checksum_verification=ON; -- re-enable verification -+** -+** The "checksum_verification" pragma will return "1" (true) or "0" -+** (false) if checksum verification is enabled or disabled, respectively. -+** "Verification" in this context means the feature that causes -+** SQLITE_IOERR_DATA errors if a checksum mismatch is detected while -+** reading. Checksums are always kept up-to-date as long as the -+** reserve-bytes value of the database is 8, regardless of the setting -+** of this pragma. Checksum verification can be disabled (for example) -+** to do forensic analysis of a database that has previously reported -+** a checksum error. -+** -+** The "checksum_verification" pragma will always respond with "0" if -+** the database file does not have a reserve-bytes value of 8. The -+** pragma will return no rows at all if the cksumvfs extension is -+** not loaded. -+** -+** IMPLEMENTATION NOTES -+** -+** The checksum is stored in the last 8 bytes of each page. This -+** module only operates if the "bytes of reserved space on each page" -+** value at offset 20 the SQLite database header is exactly 8. If -+** the reserved-space value is not 8, this module is a no-op. -+*/ -+#if defined(SQLITE_AMALGAMATION) && !defined(SQLITE_CKSUMVFS_STATIC) -+# define SQLITE_CKSUMVFS_STATIC -+#endif -+#ifdef SQLITE_CKSUMVFS_STATIC -+# include "sqlite3.h" -+#else -+# include "sqlite3ext.h" -+ SQLITE_EXTENSION_INIT1 -+#endif -+#include -+#include -+ -+// hw export the symbols -+#ifdef SQLITE_EXPORT_SYMBOLS -+#if defined(__GNUC__) -+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) -+#elif defined(_MSC_VER) -+# define EXPORT_SYMBOLS __declspec(dllexport) -+#else -+# define EXPORT_SYMBOLS -+#endif -+#endif -+ -+/* -+** Forward declaration of objects used by this utility -+*/ -+typedef struct sqlite3_vfs CksmVfs; -+typedef struct CksmFile CksmFile; -+ -+/* -+** Useful datatype abbreviations -+*/ -+#if !defined(SQLITE_AMALGAMATION) -+ typedef unsigned char u8; -+ typedef unsigned int u32; -+#endif -+ -+/* Access to a lower-level VFS that (might) implement dynamic loading, -+** access to randomness, etc. -+*/ -+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) -+#define ORIGFILE(p) ((sqlite3_file*)(((CksmFile*)(p))+1)) -+ -+/* An open file */ -+struct CksmFile { -+ sqlite3_file base; /* IO methods */ -+ const char *zFName; /* Original name of the file */ -+ char computeCksm; /* True to compute checksums. -+ ** Always true if reserve size is 8. */ -+ char verifyCksm; /* True to verify checksums */ -+ char isWal; /* True if processing a WAL file */ -+ char inCkpt; /* Currently doing a checkpoint */ -+ CksmFile *pPartner; /* Ptr from WAL to main-db, or from main-db to WAL */ -+}; -+ -+/* -+** Methods for CksmFile -+*/ -+static int cksmClose(sqlite3_file*); -+static int cksmRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); -+static int cksmWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); -+static int cksmTruncate(sqlite3_file*, sqlite3_int64 size); -+static int cksmSync(sqlite3_file*, int flags); -+static int cksmFileSize(sqlite3_file*, sqlite3_int64 *pSize); -+static int cksmLock(sqlite3_file*, int); -+static int cksmUnlock(sqlite3_file*, int); -+static int cksmCheckReservedLock(sqlite3_file*, int *pResOut); -+static int cksmFileControl(sqlite3_file*, int op, void *pArg); -+static int cksmSectorSize(sqlite3_file*); -+static int cksmDeviceCharacteristics(sqlite3_file*); -+static int cksmShmMap(sqlite3_file*, int iPg, int pgsz, int, void volatile**); -+static int cksmShmLock(sqlite3_file*, int offset, int n, int flags); -+static void cksmShmBarrier(sqlite3_file*); -+static int cksmShmUnmap(sqlite3_file*, int deleteFlag); -+static int cksmFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp); -+static int cksmUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p); -+ -+/* -+** Methods for CksmVfs -+*/ -+static int cksmOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); -+static int cksmDelete(sqlite3_vfs*, const char *zName, int syncDir); -+static int cksmAccess(sqlite3_vfs*, const char *zName, int flags, int *); -+static int cksmFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); -+static void *cksmDlOpen(sqlite3_vfs*, const char *zFilename); -+static void cksmDlError(sqlite3_vfs*, int nByte, char *zErrMsg); -+static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void); -+static void cksmDlClose(sqlite3_vfs*, void*); -+static int cksmRandomness(sqlite3_vfs*, int nByte, char *zOut); -+static int cksmSleep(sqlite3_vfs*, int microseconds); -+static int cksmCurrentTime(sqlite3_vfs*, double*); -+static int cksmGetLastError(sqlite3_vfs*, int, char *); -+static int cksmCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); -+static int cksmSetSystemCall(sqlite3_vfs*, const char*,sqlite3_syscall_ptr); -+static sqlite3_syscall_ptr cksmGetSystemCall(sqlite3_vfs*, const char *z); -+static const char *cksmNextSystemCall(sqlite3_vfs*, const char *zName); -+ -+static sqlite3_vfs cksm_vfs = { -+ 3, /* iVersion (set when registered) */ -+ 0, /* szOsFile (set when registered) */ -+ 1024, /* mxPathname */ -+ 0, /* pNext */ -+ "cksmvfs", /* zName */ -+ 0, /* pAppData (set when registered) */ -+ cksmOpen, /* xOpen */ -+ cksmDelete, /* xDelete */ -+ cksmAccess, /* xAccess */ -+ cksmFullPathname, /* xFullPathname */ -+ cksmDlOpen, /* xDlOpen */ -+ cksmDlError, /* xDlError */ -+ cksmDlSym, /* xDlSym */ -+ cksmDlClose, /* xDlClose */ -+ cksmRandomness, /* xRandomness */ -+ cksmSleep, /* xSleep */ -+ cksmCurrentTime, /* xCurrentTime */ -+ cksmGetLastError, /* xGetLastError */ -+ cksmCurrentTimeInt64, /* xCurrentTimeInt64 */ -+ cksmSetSystemCall, /* xSetSystemCall */ -+ cksmGetSystemCall, /* xGetSystemCall */ -+ cksmNextSystemCall /* xNextSystemCall */ -+}; -+ -+static const sqlite3_io_methods cksm_io_methods = { -+ 3, /* iVersion */ -+ cksmClose, /* xClose */ -+ cksmRead, /* xRead */ -+ cksmWrite, /* xWrite */ -+ cksmTruncate, /* xTruncate */ -+ cksmSync, /* xSync */ -+ cksmFileSize, /* xFileSize */ -+ cksmLock, /* xLock */ -+ cksmUnlock, /* xUnlock */ -+ cksmCheckReservedLock, /* xCheckReservedLock */ -+ cksmFileControl, /* xFileControl */ -+ cksmSectorSize, /* xSectorSize */ -+ cksmDeviceCharacteristics, /* xDeviceCharacteristics */ -+ cksmShmMap, /* xShmMap */ -+ cksmShmLock, /* xShmLock */ -+ cksmShmBarrier, /* xShmBarrier */ -+ cksmShmUnmap, /* xShmUnmap */ -+ cksmFetch, /* xFetch */ -+ cksmUnfetch /* xUnfetch */ -+}; -+ -+/* Do byte swapping on a unsigned 32-bit integer */ -+#define BYTESWAP32(x) ( \ -+ (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8) \ -+ + (((x)&0x00FF0000)>>8) + (((x)&0xFF000000)>>24) \ -+) -+ -+/* Compute a checksum on a buffer */ -+static void cksmCompute( -+ u8 *a, /* Content to be checksummed */ -+ int nByte, /* Bytes of content in a[]. Must be a multiple of 8. */ -+ u8 *aOut /* OUT: Final 8-byte checksum value output */ -+){ -+ u32 s1 = 0, s2 = 0; -+ u32 *aData = (u32*)a; -+ u32 *aEnd = (u32*)&a[nByte]; -+ u32 x = 1; -+ -+ assert( nByte>=8 ); -+ assert( (nByte&0x00000007)==0 ); -+ assert( nByte<=65536 ); -+ -+ if( 1 == *(u8*)&x ){ -+ /* Little-endian */ -+ do { -+ s1 += *aData++ + s2; -+ s2 += *aData++ + s1; -+ }while( aData 65536 || (nByte & (nByte-1))!=0 ) return; -+ cksmCompute(data, nByte-8, cksum); -+ sqlite3_result_int(context, memcmp(data+nByte-8,cksum,8)==0); -+} -+ -+#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME -+/* -+** SQL function: initialize_cksumvfs(SCHEMANAME) -+** -+** This SQL functions (whose name is actually determined at compile-time -+** by the value of the SQLITE_CKSUMVFS_INIT_FUNCNAME macro) invokes: -+** -+** sqlite3_file_control(db, SCHEMANAME, SQLITE_FCNTL_RESERVE_BYTE, &n); -+** -+** In order to set the reserve bytes value to 8, so that cksumvfs will -+** operation. This feature is provided (if and only if the -+** SQLITE_CKSUMVFS_INIT_FUNCNAME compile-time option is set to a string -+** which is the name of the SQL function) so as to provide the ability -+** to invoke the file-control in programming languages that lack -+** direct access to the sqlite3_file_control() interface (ex: Java). -+** -+** This interface is undocumented, apart from this comment. Usage -+** example: -+** -+** 1. Compile with -DSQLITE_CKSUMVFS_INIT_FUNCNAME="ckvfs_init" -+** 2. Run: "SELECT cksum_init('main'); VACUUM;" -+*/ -+static void cksmInitFunc( -+ sqlite3_context *context, -+ int argc, -+ sqlite3_value **argv -+){ -+ int nByte = 8; -+ const char *zSchemaName = (const char*)sqlite3_value_text(argv[0]); -+ sqlite3 *db = sqlite3_context_db_handle(context); -+ sqlite3_file_control(db, zSchemaName, SQLITE_FCNTL_RESERVE_BYTES, &nByte); -+ /* Return NULL */ -+} -+#endif /* SQLITE_CKSUMBFS_INIT_FUNCNAME */ -+ -+/* -+** Close a cksm-file. -+*/ -+static int cksmClose(sqlite3_file *pFile){ -+ CksmFile *p = (CksmFile *)pFile; -+ if( p->pPartner ){ -+ assert( p->pPartner->pPartner==p ); -+ p->pPartner->pPartner = 0; -+ p->pPartner = 0; -+ } -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xClose(pFile); -+} -+ -+/* -+** Set the computeCkSm and verifyCksm flags, if they need to be -+** changed. -+*/ -+static void cksmSetFlags(CksmFile *p, int hasCorrectReserveSize){ -+ if( hasCorrectReserveSize!=p->computeCksm ){ -+ p->computeCksm = p->verifyCksm = hasCorrectReserveSize; -+ if( p->pPartner ){ -+ p->pPartner->verifyCksm = hasCorrectReserveSize; -+ p->pPartner->computeCksm = hasCorrectReserveSize; -+ } -+ } -+} -+ -+static void EncodeReservedBytesIntoBase16(const u8 *reserved, int len, char *encodeStr, int maxLen){ -+ static const char baseCode[] = "0123456789ABCDEF"; -+ for(int i=0; i > 4) & 0x0F]; -+ *encodeStr++ = baseCode[reserved[i] & 0x0F]; -+ } -+ *encodeStr = '0'; -+} -+ -+/* -+** Read data from a cksm-file. -+*/ -+static int cksmRead( -+ sqlite3_file *pFile, -+ void *zBuf, -+ int iAmt, -+ sqlite_int64 iOfst -+){ -+ int rc; -+ CksmFile *p = (CksmFile *)pFile; -+ pFile = ORIGFILE(pFile); -+ rc = pFile->pMethods->xRead(pFile, zBuf, iAmt, iOfst); -+ if( rc==SQLITE_OK ){ -+ if( iOfst==0 && iAmt>=100 && ( -+ memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0 -+ )){ -+ u8 *d = (u8*)zBuf; -+ char hasCorrectReserveSize = (d[20]==8); -+ cksmSetFlags(p, hasCorrectReserveSize); -+ } -+ /* Verify the checksum if -+ ** (1) the size indicates that we are dealing with a complete -+ ** database page, only support pageSize:4K -+ ** (2) checksum verification is enabled -+ ** (3) we are not in the middle of checkpoint -+ */ -+ if( iAmt==4096 /* (1) */ -+ && p->verifyCksm /* (2) */ -+ && !p->inCkpt /* (3) */ -+ ){ -+ u8 cksum[8]; -+ cksmCompute((u8*)zBuf, iAmt-8, cksum); -+ if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){ -+ char expect[18] = {0}; -+ char actual[18] = {0}; -+ EncodeReservedBytesIntoBase16((u8 *)zBuf+iAmt-8, 8, expect, 18); -+ EncodeReservedBytesIntoBase16(cksum, 8, actual, 18); -+ sqlite3_log(SQLITE_IOERR_DATA, "checksum fault offset %lld of \"%s\", amt:%d, expect:%s, actual:%s", -+ iOfst, p->zFName, iAmt, expect, actual); -+ rc = SQLITE_IOERR_DATA; -+ } -+ } -+ } -+ return rc; -+} -+ -+/* -+** Write data to a cksm-file. -+*/ -+static int cksmWrite( -+ sqlite3_file *pFile, -+ const void *zBuf, -+ int iAmt, -+ sqlite_int64 iOfst -+){ -+ CksmFile *p = (CksmFile *)pFile; -+ pFile = ORIGFILE(pFile); -+ if( iOfst==0 && iAmt>=100 && ( -+ memcmp(zBuf,"SQLite format 3",16)==0 || memcmp(zBuf,"ZV-",3)==0 -+ )){ -+ u8 *d = (u8*)zBuf; -+ char hasCorrectReserveSize = (d[20]==8); -+ cksmSetFlags(p, hasCorrectReserveSize); -+ } -+ /* If the write size is appropriate for a database page and if -+ ** checksums where ever enabled, then it will be safe to compute -+ ** the checksums. The reserve byte size might have increased, but -+ ** it will never decrease. And because it cannot decrease, the -+ ** checksum will not overwrite anything. -+ */ -+ if( iAmt==4096 -+ && p->computeCksm -+ && !p->inCkpt -+ ){ -+ cksmCompute((u8*)zBuf, iAmt-8, ((u8*)zBuf)+iAmt-8); -+ } -+ return pFile->pMethods->xWrite(pFile, zBuf, iAmt, iOfst); -+} -+ -+/* -+** Truncate a cksm-file. -+*/ -+static int cksmTruncate(sqlite3_file *pFile, sqlite_int64 size){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xTruncate(pFile, size); -+} -+ -+/* -+** Sync a cksm-file. -+*/ -+static int cksmSync(sqlite3_file *pFile, int flags){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xSync(pFile, flags); -+} -+ -+/* -+** Return the current file-size of a cksm-file. -+*/ -+static int cksmFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ -+ CksmFile *p = (CksmFile *)pFile; -+ pFile = ORIGFILE(p); -+ return pFile->pMethods->xFileSize(pFile, pSize); -+} -+ -+/* -+** Lock a cksm-file. -+*/ -+static int cksmLock(sqlite3_file *pFile, int eLock){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xLock(pFile, eLock); -+} -+ -+/* -+** Unlock a cksm-file. -+*/ -+static int cksmUnlock(sqlite3_file *pFile, int eLock){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xUnlock(pFile, eLock); -+} -+ -+/* -+** Check if another file-handle holds a RESERVED lock on a cksm-file. -+*/ -+static int cksmCheckReservedLock(sqlite3_file *pFile, int *pResOut){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xCheckReservedLock(pFile, pResOut); -+} -+ -+/* -+** File control method. For custom operations on a cksm-file. -+*/ -+static int cksmFileControl(sqlite3_file *pFile, int op, void *pArg){ -+ int rc; -+ CksmFile *p = (CksmFile*)pFile; -+ pFile = ORIGFILE(pFile); -+ if( op==SQLITE_FCNTL_PRAGMA ){ -+ char **azArg = (char**)pArg; -+ assert( azArg[1]!=0 ); -+ if( sqlite3_stricmp(azArg[1],"checksum_verification")==0 ){ -+ char *zArg = azArg[2]; -+ if( zArg!=0 ){ -+ if( (zArg[0]>='1' && zArg[0]<='9') -+ || sqlite3_strlike("enable%",zArg,0)==0 -+ || sqlite3_stricmp("yes",zArg)==0 -+ || sqlite3_stricmp("on",zArg)==0 -+ ){ -+ p->verifyCksm = p->computeCksm; -+ }else{ -+ p->verifyCksm = 0; -+ } -+ if( p->pPartner ) p->pPartner->verifyCksm = p->verifyCksm; -+ } -+ azArg[0] = sqlite3_mprintf("%d",p->verifyCksm); -+ return SQLITE_OK; -+ }else if( p->computeCksm && azArg[2]!=0 -+ && sqlite3_stricmp(azArg[1], "page_size")==0 ){ -+ /* Do not allow page size changes on a checksum database */ -+ return SQLITE_OK; -+ } -+ }else if( op==SQLITE_FCNTL_CKPT_START || op==SQLITE_FCNTL_CKPT_DONE ){ -+ p->inCkpt = op==SQLITE_FCNTL_CKPT_START; -+ if( p->pPartner ) p->pPartner->inCkpt = p->inCkpt; -+ }else if( op==SQLITE_FCNTL_CKSM_FILE ){ -+ /* This VFS needs to obtain a pointer to the corresponding database -+ ** file handle from within xOpen() calls to open wal files. To do this, -+ ** it uses the sqlite3_database_file_object() API to obtain a pointer -+ ** to the file-handle used by SQLite to access the db file. This is -+ ** fine if cksmvfs happens to be the top-level VFS, but not if there -+ ** are one or more wrapper VFS. To handle this case, this file-control -+ ** is used to extract the cksmvfs file-handle from any wrapper file -+ ** handle. */ -+ sqlite3_file **ppFile = (sqlite3_file**)pArg; -+ *ppFile = (sqlite3_file*)p; -+ return SQLITE_OK; -+ } -+ rc = pFile->pMethods->xFileControl(pFile, op, pArg); -+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){ -+ *(char**)pArg = sqlite3_mprintf("cksm/%z", *(char**)pArg); -+ } -+ return rc; -+} -+ -+/* -+** Return the sector-size in bytes for a cksm-file. -+*/ -+static int cksmSectorSize(sqlite3_file *pFile){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xSectorSize(pFile); -+} -+ -+/* -+** Return the device characteristic flags supported by a cksm-file. -+*/ -+static int cksmDeviceCharacteristics(sqlite3_file *pFile){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xDeviceCharacteristics(pFile); -+} -+ -+/* Create a shared memory file mapping */ -+static int cksmShmMap( -+ sqlite3_file *pFile, -+ int iPg, -+ int pgsz, -+ int bExtend, -+ void volatile **pp -+){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xShmMap(pFile,iPg,pgsz,bExtend,pp); -+} -+ -+/* Perform locking on a shared-memory segment */ -+static int cksmShmLock(sqlite3_file *pFile, int offset, int n, int flags){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xShmLock(pFile,offset,n,flags); -+} -+ -+/* Memory barrier operation on shared memory */ -+static void cksmShmBarrier(sqlite3_file *pFile){ -+ pFile = ORIGFILE(pFile); -+ pFile->pMethods->xShmBarrier(pFile); -+} -+ -+/* Unmap a shared memory segment */ -+static int cksmShmUnmap(sqlite3_file *pFile, int deleteFlag){ -+ pFile = ORIGFILE(pFile); -+ return pFile->pMethods->xShmUnmap(pFile,deleteFlag); -+} -+ -+/* Fetch a page of a memory-mapped file */ -+static int cksmFetch( -+ sqlite3_file *pFile, -+ sqlite3_int64 iOfst, -+ int iAmt, -+ void **pp -+){ -+ CksmFile *p = (CksmFile *)pFile; -+ if( p->computeCksm ){ -+ *pp = 0; -+ return SQLITE_OK; -+ } -+ pFile = ORIGFILE(pFile); -+ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){ -+ return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp); -+ } -+ *pp = 0; -+ return SQLITE_OK; -+} -+ -+/* Release a memory-mapped page */ -+static int cksmUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ -+ pFile = ORIGFILE(pFile); -+ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){ -+ return pFile->pMethods->xUnfetch(pFile, iOfst, pPage); -+ } -+ return SQLITE_OK; -+} -+ -+/* -+** Open a cksm file handle. -+*/ -+static int cksmOpen( -+ sqlite3_vfs *pVfs, -+ const char *zName, -+ sqlite3_file *pFile, -+ int flags, -+ int *pOutFlags -+){ -+ CksmFile *p; -+ sqlite3_file *pSubFile; -+ sqlite3_vfs *pSubVfs; -+ int rc; -+ pSubVfs = ORIGVFS(pVfs); -+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){ -+ return pSubVfs->xOpen(pSubVfs, zName, pFile, flags, pOutFlags); -+ } -+ p = (CksmFile*)pFile; -+ memset(p, 0, sizeof(*p)); -+ pSubFile = ORIGFILE(pFile); -+ pFile->pMethods = &cksm_io_methods; -+ rc = pSubVfs->xOpen(pSubVfs, zName, pSubFile, flags, pOutFlags); -+ if( rc ) goto cksm_open_done; -+ if( flags & SQLITE_OPEN_WAL ){ -+ sqlite3_file *pDb = sqlite3_database_file_object(zName); -+ rc = pDb->pMethods->xFileControl(pDb, SQLITE_FCNTL_CKSM_FILE, (void*)&pDb); -+ assert( rc==SQLITE_OK ); -+ p->pPartner = (CksmFile*)pDb; -+ assert( p->pPartner->pPartner==0 ); -+ p->pPartner->pPartner = p; -+ p->isWal = 1; -+ p->computeCksm = p->pPartner->computeCksm; -+ }else{ -+ p->isWal = 0; -+ p->computeCksm = 0; -+ } -+ p->zFName = zName; -+cksm_open_done: -+ if( rc ) pFile->pMethods = 0; -+ return rc; -+} -+ -+/* -+** All other VFS methods are pass-thrus. -+*/ -+static int cksmDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ -+ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zPath, dirSync); -+} -+static int cksmAccess( -+ sqlite3_vfs *pVfs, -+ const char *zPath, -+ int flags, -+ int *pResOut -+){ -+ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zPath, flags, pResOut); -+} -+static int cksmFullPathname( -+ sqlite3_vfs *pVfs, -+ const char *zPath, -+ int nOut, -+ char *zOut -+){ -+ return ORIGVFS(pVfs)->xFullPathname(ORIGVFS(pVfs),zPath,nOut,zOut); -+} -+static void *cksmDlOpen(sqlite3_vfs *pVfs, const char *zPath){ -+ return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath); -+} -+static void cksmDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ -+ ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg); -+} -+static void (*cksmDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){ -+ return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym); -+} -+static void cksmDlClose(sqlite3_vfs *pVfs, void *pHandle){ -+ ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle); -+} -+static int cksmRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ -+ return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut); -+} -+static int cksmSleep(sqlite3_vfs *pVfs, int nMicro){ -+ return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro); -+} -+static int cksmCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ -+ return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut); -+} -+static int cksmGetLastError(sqlite3_vfs *pVfs, int a, char *b){ -+ return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b); -+} -+static int cksmCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){ -+ sqlite3_vfs *pOrig = ORIGVFS(pVfs); -+ int rc; -+ assert( pOrig->iVersion>=2 ); -+ if( pOrig->xCurrentTimeInt64 ){ -+ rc = pOrig->xCurrentTimeInt64(pOrig, p); -+ }else{ -+ double r; -+ rc = pOrig->xCurrentTime(pOrig, &r); -+ *p = (sqlite3_int64)(r*86400000.0); -+ } -+ return rc; -+} -+static int cksmSetSystemCall( -+ sqlite3_vfs *pVfs, -+ const char *zName, -+ sqlite3_syscall_ptr pCall -+){ -+ return ORIGVFS(pVfs)->xSetSystemCall(ORIGVFS(pVfs),zName,pCall); -+} -+static sqlite3_syscall_ptr cksmGetSystemCall( -+ sqlite3_vfs *pVfs, -+ const char *zName -+){ -+ return ORIGVFS(pVfs)->xGetSystemCall(ORIGVFS(pVfs),zName); -+} -+static const char *cksmNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ -+ return ORIGVFS(pVfs)->xNextSystemCall(ORIGVFS(pVfs), zName); -+} -+ -+/* Register the verify_checksum() SQL function. -+*/ -+static int cksmRegisterFunc( -+ sqlite3 *db, -+ char **pzErrMsg, -+ const sqlite3_api_routines *pApi -+){ -+ int rc; -+ if( db==0 ) return SQLITE_OK; -+ rc = sqlite3_create_function(db, "verify_checksum", 1, -+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, -+ 0, cksmVerifyFunc, 0, 0); -+#ifdef SQLITE_CKSUMVFS_INIT_FUNCNAME -+ (void)sqlite3_create_function(db, SQLITE_CKSUMVFS_INIT_FUNCNAME, 1, -+ SQLITE_UTF8|SQLITE_DIRECTONLY, -+ 0, cksmInitFunc, 0, 0); -+#endif -+ return rc; -+} -+ -+/* -+** Register the cksum VFS as the default VFS for the system. -+** Also make arrangements to automatically register the "verify_checksum()" -+** SQL function on each new database connection. -+*/ -+static int cksmRegisterVfs(void){ -+ int rc = SQLITE_OK; -+ sqlite3_vfs *pOrig; -+ if( sqlite3_vfs_find("cksmvfs")!=0 ) return SQLITE_OK; -+ pOrig = sqlite3_vfs_find(0); -+ if( pOrig==0 ) return SQLITE_ERROR; -+ cksm_vfs.iVersion = pOrig->iVersion; -+ cksm_vfs.pAppData = pOrig; -+ cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile); -+ rc = sqlite3_vfs_register(&cksm_vfs, 1); -+ if( rc==SQLITE_OK ){ -+ rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc); -+ } -+ return rc; -+} -+ -+#if defined(SQLITE_CKSUMVFS_STATIC) -+/* This variant of the initializer runs when the extension is -+** statically linked. -+*/ -+int sqlite3_register_cksumvfs(const char *NotUsed){ -+ (void)NotUsed; -+ return cksmRegisterVfs(); -+} -+int sqlite3_unregister_cksumvfs(void){ -+ if( sqlite3_vfs_find("cksmvfs") ){ -+ sqlite3_vfs_unregister(&cksm_vfs); -+ sqlite3_cancel_auto_extension((void(*)(void))cksmRegisterFunc); -+ } -+ return SQLITE_OK; -+} -+#endif /* defined(SQLITE_CKSUMVFS_STATIC */ -+ -+#if !defined(SQLITE_CKSUMVFS_STATIC) -+/* This variant of the initializer function is used when the -+** extension is shared library to be loaded at run-time. -+*/ -+#ifdef _WIN32 -+__declspec(dllexport) -+#endif -+/* -+** This routine is called by sqlite3_load_extension() when the -+** extension is first loaded. -+***/ -+int sqlite3_cksumvfs_init( -+ sqlite3 *db, -+ char **pzErrMsg, -+ const sqlite3_api_routines *pApi -+){ -+ int rc; -+ SQLITE_EXTENSION_INIT2(pApi); -+ (void)pzErrMsg; /* not used */ -+ rc = cksmRegisterFunc(db, 0, 0); -+ if( rc==SQLITE_OK ){ -+ rc = cksmRegisterVfs(); -+ } -+ if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; -+ return rc; -+} -+#endif /* !defined(SQLITE_CKSUMVFS_STATIC) */ -+ -+#ifdef SQLITE_CKSUMVFS_STATIC -+struct sqlite3_api_routines_cksumvfs { -+ int (*register_cksumvfs)(const char *); -+ int (*unregister_cksumvfs)(); -+}; -+typedef struct sqlite3_api_routines_cksumvfs sqlite3_api_routines_cksumvfs; -+static const sqlite3_api_routines_cksumvfs sqlite3CksumvfsApis = { -+ sqlite3_register_cksumvfs, -+ sqlite3_unregister_cksumvfs -+}; -+ -+EXPORT_SYMBOLS const sqlite3_api_routines_cksumvfs *sqlite3_export_cksumvfs_symbols = &sqlite3CksumvfsApis; -+#endif -diff --git a/src/sqlite3.c b/src/sqlite3.c -index aa629b5..1063591 100644 ---- a/src/sqlite3.c -+++ b/src/sqlite3.c -@@ -57046,17 +57046,70 @@ static int jrnlBufferSize(Pager *pPager){ - ** and debugging only. - */ - #ifdef SQLITE_CHECK_PAGES --/* --** Return a 32-bit hash of the page data for pPage. --*/ --static u32 pager_datahash(int nByte, unsigned char *pData){ -+#if defined (__arm__) || defined (__aarch64__) -+#include -+u32 deep_fast_hash_arm(void *src, int srcLen){ -+ uint16_t chunkSize = srcLen/4; -+ uint8_t *u8p_src = (uint8_t *)src; -+ uint16x8_t m_prime = vdupq_n_u16(44497); -+ uint16x8_t m_res0 = vdupq_n_u16(0); -+ uint16x8_t m_res1 = vdupq_n_u16(0); -+ uint16x8_t m_res2 = vdupq_n_u16(0); -+ uint16x8_t m_res3 = vdupq_n_u16(0); -+ uint16x8_t m_res4 = vdupq_n_u16(0); -+ uint16x8_t m_res5 = vdupq_n_u16(0); -+ uint16x8_t m_res6 = vdupq_n_u16(0); -+ uint16x8_t m_res7 = vdupq_n_u16(0); -+ -+ for(int i=0; i pPager->pageSize, (unsigned char *)pPage->pData); - } -@@ -57072,8 +57125,15 @@ static void pager_set_pagehash(PgHdr *pPage){ - #define CHECK_PAGE(x) checkPage(x) - static void checkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; -- assert( pPager->eState!=PAGER_ERROR ); -- assert( (pPg->flags&PGHDR_DIRTY) || pPg->pageHash==pager_pagehash(pPg) ); -+ if( pPager->eState==PAGER_ERROR ){ -+ return; -+ } -+ if( pPg->flags&PGHDR_DIRTY ) { -+ return; -+ } -+ if( pPg->pgno!=1 && pPg->pageHash!=pager_pagehash(pPg) ){ -+ sqlite3_log(SQLITE_CORRUPT, "cache corruption occurs through checking page(%u)", pPg->pgno); -+ } - } - - #else -@@ -178288,12 +178348,16 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo - *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager); - rc = SQLITE_OK; - }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){ -+#ifndef SQLITE_CKSUMVFS_STATIC -+ rc = SQLITE_OK; -+#else - int iNew = *(int*)pArg; - *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree); - if( iNew>=0 && iNew<=255 ){ - sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0); - } - rc = SQLITE_OK; -+#endif - }else if( op==SQLITE_FCNTL_RESET_CACHE ){ - sqlite3BtreeClearCache(pBtree); - rc = SQLITE_OK; -@@ -247458,6 +247522,27 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime - #endif - // hw export the symbols - #ifdef SQLITE_EXPORT_SYMBOLS -+#ifndef SQLITE_CKSUMVFS_STATIC -+int sqlite3_register_cksumvfs(const char *NotUsed){ -+ return SQLITE_MISUSE; -+} -+int sqlite3_unregister_cksumvfs(void){ -+ return SQLITE_MISUSE; -+} -+ -+struct sqlite3_api_routines_cksumvfs { -+ int (*register_cksumvfs)(const char *); -+ int (*unregister_cksumvfs)(); -+}; -+typedef struct sqlite3_api_routines_cksumvfs sqlite3_api_routines_cksumvfs; -+static const sqlite3_api_routines_cksumvfs sqlite3CksumvfsApis = { -+ sqlite3_register_cksumvfs, -+ sqlite3_unregister_cksumvfs -+}; -+ -+EXPORT_SYMBOLS const sqlite3_api_routines_cksumvfs *sqlite3_export_cksumvfs_symbols = &sqlite3CksumvfsApis; -+#endif -+ - struct sqlite3_api_routines_hw { - int (*initialize)(); - int (*config)(int,...); --- -2.47.0.windows.2 - diff --git a/patch/0007-Bugfix-on-current-version.patch b/patch/0007-Bugfix-on-current-version.patch new file mode 100644 index 0000000000000000000000000000000000000000..6efab33635d169b00172df2a4d3712291cffce3b --- /dev/null +++ b/patch/0007-Bugfix-on-current-version.patch @@ -0,0 +1,69 @@ +From d74c8eab96ed96bef1d8763254468c6eb4b9fc8b Mon Sep 17 00:00:00 2001 +From: zeng +Date: Sun, 18 May 2025 22:37:42 +0800 +Subject: [PATCH 2/2] Bugfix on current version + +--- + src/sqlite3.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/src/sqlite3.c b/src/sqlite3.c +index 9af08d0..8f6a199 100644 +--- a/src/sqlite3.c ++++ b/src/sqlite3.c +@@ -204125,6 +204125,39 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){ + return rc; + } + ++/* ++** Expression node pExpr is an MSR phrase. This function restarts pExpr ++** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned ++** if successful, or an SQLite error code otherwise. ++*/ ++int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){ ++ int rc = SQLITE_OK; ++ if( pExpr->bEof==0 ){ ++ i64 iDocid = pExpr->iDocid; ++ fts3EvalRestart(pCsr, pExpr, &rc); ++ while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){ ++ fts3EvalNextRow(pCsr, pExpr, &rc); ++ if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB; ++ } ++ } ++ return rc; ++} ++ ++/* ++** If expression pExpr is a phrase expression that uses an MSR query, ++** restart it as a regular, non-incremental query. Return SQLITE_OK ++** if successful, or an SQLite error code otherwise. ++*/ ++static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){ ++ TermOffsetCtx *p = (TermOffsetCtx*)ctx; ++ int rc = SQLITE_OK; ++ if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){ ++ rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr); ++ pExpr->pPhrase->bIncr = 0; ++ } ++ return rc; ++} ++ + /* + ** Implementation of offsets() function. + */ +@@ -204161,6 +204194,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( + sCtx.iDocid = pCsr->iPrevId; + sCtx.pCsr = pCsr; + ++ /* If a query restart will be required, do it here, rather than later of ++ ** after pointers to poslist buffers that may be invalidated by a restart ++ ** have been saved. */ ++ rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx); ++ if( rc!=SQLITE_OK ) goto offsets_out; ++ + /* Loop through the table columns, appending offset information to + ** string-buffer res for each column. + */ +-- +2.21.0.windows.1 + diff --git a/patch/0008-BugFix-CurrVersion.patch b/patch/0008-BugFix-CurrVersion.patch deleted file mode 100644 index 2060234c4b8996429e16b0d47d699dce452521ef..0000000000000000000000000000000000000000 --- a/patch/0008-BugFix-CurrVersion.patch +++ /dev/null @@ -1,310 +0,0 @@ -From f8ebccc3bc248e080e1343315fa5b9d0b399dab8 Mon Sep 17 00:00:00 2001 -From: Liu Hongyang -Date: Sat, 17 May 2025 16:04:21 +0800 -Subject: [PATCH] BugFix-CurrVersion - ---- - src/sqlite3.c | 107 +++++++++++++++++++++++++++++++++----------------- - 1 file changed, 72 insertions(+), 35 deletions(-) - -diff --git a/src/sqlite3.c b/src/sqlite3.c -index 8dddbf6..777d1b1 100644 ---- a/src/sqlite3.c -+++ b/src/sqlite3.c -@@ -43480,6 +43480,9 @@ static int unixOpen( - flags |= SQLITE_OPEN_READONLY; - openFlags |= O_RDONLY; - isReadonly = 1; -+#ifdef LOG_DUMP -+ sqlite3_log(SQLITE_WARNING, "Try open file readonly sysno %d", errno); -+#endif - fd = robust_open(zName, openFlags, openMode); - } - } -@@ -57259,6 +57262,9 @@ static int jrnlBufferSize(Pager *pPager){ - ** and debugging only. - */ - #ifdef SQLITE_CHECK_PAGES -+#ifndef SQLITE_MIN_CHECK_PAGE_SIZE -+#define SQLITE_MIN_CHECK_PAGE_SIZE 4096 -+#endif - #if defined (__arm__) || defined (__aarch64__) - #include - u32 deep_fast_hash_arm(void *src, int srcLen){ -@@ -57327,6 +57333,9 @@ static u32 pager_pagehash(PgHdr *pPage){ - return pager_datahash(pPage->pPager->pageSize, (unsigned char *)pPage->pData); - } - static void pager_set_pagehash(PgHdr *pPage){ -+ if( pPage->pPager->pageSize pageHash = pager_pagehash(pPage); - } - -@@ -57338,10 +57347,7 @@ static void pager_set_pagehash(PgHdr *pPage){ - #define CHECK_PAGE(x) checkPage(x) - static void checkPage(PgHdr *pPg){ - Pager *pPager = pPg->pPager; -- if( pPager->eState==PAGER_ERROR ){ -- return; -- } -- if( pPg->flags&PGHDR_DIRTY ) { -+ if( pPager->pageSize eState==PAGER_ERROR || pPg->flags&PGHDR_DIRTY ) { - return; - } - if( pPg->pgno!=1 && pPg->pageHash!=pager_pagehash(pPg) ){ -@@ -58502,9 +58508,11 @@ static int pager_playback_one_page( - ** file. */ - #ifdef SQLITE_HAS_CODEC - if( !jrnlEnc ){ -- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData); -- rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); -- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); -+ CODEC2(pPager, aData, pgno, 7, rc=pPager->errCode, aData); -+ if (rc == SQLITE_OK) { -+ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); -+ CODEC1(pPager, aData, pgno, 3, rc=pPager->errCode); -+ } - }else - #endif /* SQLITE_HAS_CODEC */ - rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst); -@@ -58515,9 +58523,11 @@ static int pager_playback_one_page( - if( pPager->pBackup ){ - #ifdef SQLITE_HAS_CODEC - if( jrnlEnc ){ -- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT); -- sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); -- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT,aData); -+ CODEC1(pPager, aData, pgno, 3, rc=pPager->errCode); -+ if (rc == SQLITE_OK) { -+ sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); -+ CODEC2(pPager, aData, pgno, 7, rc=pPager->errCode, aData); -+ } - }else - #endif /* SQLITE_HAS_CODEC */ - sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); -@@ -58572,7 +58582,7 @@ static int pager_playback_one_page( - } - #if SQLITE_HAS_CODEC - /* Decode the page just read from disk */ -- if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT); } -+ if( jrnlEnc ){ CODEC1(pPager, pData, pPg->pgno, 3, rc=pPager->errCode); } - #endif /* SQLITE_HAS_CODEC */ - sqlite3PcacheRelease(pPg); - } -@@ -59150,7 +59160,7 @@ static int readDbPage(PgHdr *pPg){ - memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers)); - } - } -- CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = SQLITE_NOMEM_BKPT); -+ CODEC1(pPager, pPg->pData, pPg->pgno, 3, rc = pPager->errCode); - - PAGER_INCR(sqlite3_pager_readdb_count); - PAGER_INCR(pPager->nRead); -@@ -59314,6 +59324,9 @@ static int pagerWalFrames( - } - - #ifdef SQLITE_CHECK_PAGES -+ if( pPager->pageSize pPCache); - for(p=pList; p; p=p->pDirty){ - pager_set_pagehash(p); -@@ -60557,7 +60570,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ - assert( (pList->flags&PGHDR_NEED_SYNC)==0 ); - if( pList->pgno==1 ) pager_write_changecounter(pList); - -- CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData); -+ CODEC2(pPager, pList->pData, pgno, 6, return pPager->errCode, pData); - - /* Write out the page data. */ - rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); -@@ -60648,7 +60661,7 @@ static int subjournalPage(PgHdr *pPg){ - char *pData2; - #if SQLITE_HAS_CODEC - if( !pPager->subjInMemory ){ -- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); -+ CODEC2(pPager, pData, pPg->pgno, 7, return pPager->errCode, pData2); - }else - #endif /* SQLITE_HAS_CODEC */ - pData2 = pData; -@@ -62109,7 +62122,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ - assert( pPg->pgno!=PAGER_SJ_PGNO(pPager) ); - - assert( pPager->journalHdr<=pPager->journalOff ); -- CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2); -+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return pPager->errCode, pData2); - cksum = pager_cksum(pPager, (u8*)pData2); - - /* Even if an IO or diskfull error occurs while journalling the -@@ -62474,7 +62487,7 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){ - if( DIRECT_MODE ){ - const void *zBuf; - assert( pPager->dbFileSize>0 ); -- CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf); -+ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=pPager->errCode, zBuf); - if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - pPager->aStat[PAGER_STAT_WRITE]++; -@@ -246012,7 +246025,7 @@ CODEC_STATIC int sqlite3CodecDecryptData(CodecContext *ctx, OperateContext which - if(sqlite3CodecCheckHmac(keyCtx, pgno, inputBuffer.bufferSize + keyCtx->codecConst.initVectorSize, input, input + inputBuffer.bufferSize + keyCtx->codecConst.initVectorSize)){ - sqlite3_log(SQLITE_ERROR, "codec: check hmac error at page %d, hmac %d, kdf %d, pageSize %d, iter %d.", - pgno, keyCtx->codecConst.hmacAlgo, keyCtx->codecConst.kdfAlgo, keyCtx->codecConst.cipherPageSize, keyCtx->iter); -- return SQLITE_ERROR; -+ return pgno == 1 ? SQLITE_NOTADB : SQLITE_CORRUPT; - } - unsigned char *initVector = input + inputBuffer.bufferSize; - void *cipherCtx = opensslGetCtx(keyCtx->codecConst.cipher, CODEC_OPERATION_DECRYPT, keyCtx->key, initVector); -@@ -246035,7 +246048,7 @@ void* sqlite3Codec(void *ctx, void *data, Pgno pgno, int mode){ - int rc = SQLITE_OK; - errno_t memcpyRc = EOK; - if(ctx == NULL || data == NULL){ -- return pData; -+ return NULL; - } - if(pgno == 1){ - offset = FILE_HEADER_SIZE; -@@ -246049,12 +246062,13 @@ void* sqlite3Codec(void *ctx, void *data, Pgno pgno, int mode){ - memcpyRc = memcpy_s(pCtx->buffer, cipherPageSize, SQLITE_FILE_HEADER, FILE_HEADER_SIZE); - if(memcpyRc != EOK){ - sqlite3CodecSetError(pCtx, SQLITE_ERROR); -- return pData; -+ return NULL; - } - } - rc = sqlite3CodecDecryptData(pCtx, OPERATE_CONTEXT_READ, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); - if(rc != SQLITE_OK){ - sqlite3CodecSetError(pCtx, rc); -+ return NULL; - } - (void)memcpy_s(pData, cipherPageSize, pCtx->buffer, cipherPageSize); - return pData; -@@ -246064,13 +246078,13 @@ void* sqlite3Codec(void *ctx, void *data, Pgno pgno, int mode){ - memcpyRc = memcpy_s(pCtx->buffer, cipherPageSize, pCtx->salt, FILE_HEADER_SIZE); - if(memcpyRc != EOK){ - sqlite3CodecSetError(pCtx, SQLITE_ERROR); -- return pData; -+ return NULL; - } - } - rc = sqlite3CodecEncryptData(pCtx, OPERATE_CONTEXT_WRITE, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); - if(rc != SQLITE_OK){ - sqlite3CodecSetError(pCtx, rc); -- return pData; -+ return NULL; - } - return pCtx->buffer; - break; -@@ -246085,12 +246099,12 @@ void* sqlite3Codec(void *ctx, void *data, Pgno pgno, int mode){ - rc = sqlite3CodecEncryptData(pCtx, OPERATE_CONTEXT_READ, pgno, cipherPageSize - offset, (unsigned char *)(pData + offset), pCtx->buffer + offset); - if(rc != SQLITE_OK){ - sqlite3CodecSetError(pCtx, rc); -- return pData; -+ return NULL; - } - return pCtx->buffer; - break; - default: -- return pData; -+ return NULL; - break; - } - } -@@ -246701,7 +246715,7 @@ static int PragmaMetaDoubleWrie(sqlite3 *db, int iDb, Parse *parse, const char * - sqlite3_mutex_enter(db->mutex); - // only support enabled meta double write - int rc = MetaDwrOpenAndCheck(pBt); -- if (rc != SQLITE_OK) { -+ if (rc != SQLITE_OK && rc != SQLITE_PERM) { - parse->nErr++; - parse->rc = rc; - } -@@ -247111,7 +247125,7 @@ static int MetaDwrWriteOnePage(Btree *pBt, PgHdr *pPage, MetaDwrHdr *hdr, u8 cur - void *pData; - #if defined(SQLITE_HAS_CODEC) - if ((pData = sqlite3PagerCodec(pPage)) == 0) -- return SQLITE_NOMEM; -+ return pPager->errCode; - #else - pData = pPage->pData; - #endif -@@ -247157,6 +247171,29 @@ static inline const char *GetMetaFilePath(Pager *pPager) - return pPager->metaFd == NULL ? NULL : ((const char *)pPager->metaFd + ROUND8(pPager->pVfs->szOsFile)); - } - -+static int MetaDwrCheckPerm(sqlite3_vfs *pVfs, u8 openCreate, char *metaPath) { -+ int exists = 0; -+ int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists); -+ if (rc != SQLITE_OK) { -+ return rc; -+ } -+ if (!exists && !openCreate) { -+ return SQLITE_PERM; -+ } -+#ifdef HARMONY_OS -+ // check if the path have enough permission -+ rc = osAccess(metaPath, W_OK|R_OK); -+ if (rc == 0 || errno == ENOENT) { -+ return SQLITE_OK; -+ } -+ rc = SQLITE_PERM; -+ if (openCreate) { -+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta double write disabled, sysno %d", errno); -+ } -+#endif -+ return rc; -+} -+ - static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) { - if (pPager->metaFd || pPager->zFilename == NULL) { - return SQLITE_OK; -@@ -247173,15 +247210,10 @@ static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) { - return SQLITE_NOMEM_BKPT; - } - sqlite3_snprintf(size, metaPath, "%s-dwr", pPager->zFilename); -- int exists = 0; -- int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists); -+ int rc = MetaDwrCheckPerm(pVfs, openCreate, metaPath); - if (rc != SQLITE_OK) { - goto INIT_META_OUT; - } -- if (!exists && !openCreate) { -- sqlite3_free(metaFd); -- goto INIT_META_OUT; -- } - u32 flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_SUPER_JOURNAL); - if (openCreate) { - flags |= SQLITE_OPEN_CREATE; -@@ -247269,7 +247301,7 @@ static int MetaDwrLoadAndCheckMetaFile(BtShared *pBt, u8 reportErr) { - static int MetaDwrReadOnePage(Pager *pPager, MetaDwrHdr *hdr, int idx, u8 *pData) { - u64 ofs = CaculateMetaDwrWriteOffset(pPager->pageSize, idx, hdr->zones[idx]); - int rc = sqlite3OsRead(pPager->metaFd, pData, pPager->pageSize, ofs); -- CODEC1(pPager, pData, hdr->pages[idx], 3, rc = SQLITE_NOMEM_BKPT); -+ CODEC1(pPager, pData, hdr->pages[idx], 3, rc = pPager->errCode); - return rc; - } - -@@ -247894,11 +247926,16 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime - sqlite3_int64 endTime; - sqlite3OsCurrentTimeInt64(db->pVfs, &endTime); - sqlite3_int64 timeUse = endTime - startTime; -+ static sqlite3_int64 lastDumpTime = 0; -+ static sqlite3_int64 ckptCnt = 0; - /* Only when timeUse > 1500ms or wal size > 50MB, default pageSize 4K, 50*1024/4 = 12800 */ -- if (timeUse > 1500 || pWal->hdr.mxFrame > 12800) { -- sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal ckpt use time: %lld(ms), wal frame: %u", -- timeUse, pWal->hdr.mxFrame); -+ if (timeUse > 1500 || (pWal->hdr.mxFrame > 12800 && (lastDumpTime - endTime) > 2000)) { -+ sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Wal try ckpt count %d use time: %lld(ms), wal frame: %u", -+ ckptCnt, timeUse, pWal->hdr.mxFrame); -+ lastDumpTime = endTime; -+ ckptCnt = 0; - } -+ ckptCnt++; - } - #endif - #ifdef SQLITE_ENABLE_BINLOG --- -2.25.1 - diff --git a/src/shell.c b/src/shell.c index a951b2701958f58356f45d149251243a32d88fc0..7fb190e676b910b3b29c1dc967aaf44e64f0b0e8 100644 --- a/src/shell.c +++ b/src/shell.c @@ -39,7 +39,7 @@ typedef unsigned short int u16; /* ** Optionally #include a user-defined header, whereby compilation options -** may be set prior to where they take effect, but after platform setup. +** may be set prior to where they take effect, but after platform setup. ** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include ** file. Note that this macro has a like effect on sqlite3.c compilation. */ @@ -117,6 +117,7 @@ typedef unsigned short int u16; #include #include #include +#include #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; @@ -129,7 +130,7 @@ typedef unsigned char u8; #if !defined(_WIN32) && !defined(WIN32) # include -# if !defined(__RTP__) && !defined(_WRS_KERNEL) +# if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include # endif #endif @@ -184,6 +185,14 @@ typedef unsigned char u8; # define SHELL_USE_LOCAL_GETLINE 1 #endif +#ifndef deliberate_fall_through +/* Quiet some compilers about some of our intentional code. */ +# if defined(GCC_VERSION) && GCC_VERSION>=7000000 +# define deliberate_fall_through __attribute__((fallthrough)); +# else +# define deliberate_fall_through +# endif +#endif #if defined(_WIN32) || defined(WIN32) # if SQLITE_OS_WINRT @@ -210,7 +219,7 @@ typedef unsigned char u8; /* Make sure isatty() has a prototype. */ extern int isatty(int); -# if !defined(__RTP__) && !defined(_WRS_KERNEL) +# if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) /* popen and pclose are not C89 functions and so are ** sometimes omitted from the header */ extern FILE *popen(const char*,const char*); @@ -237,1385 +246,2665 @@ typedef unsigned char u8; #if SQLITE_OS_WINRT #include #endif +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #include /* string conversion routines only needed on Win32 */ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); -extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int); -extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif -/* On Windows, we normally run with output mode of TEXT so that \n characters -** are automatically translated into \r\n. However, this behavior needs -** to be disabled in some cases (ex: when generating CSV output and when -** rendering quoted strings that contain \n characters). The following -** routines take care of that. +/* Use console I/O package as a direct INCLUDE. */ +#define SQLITE_INTERNAL_LINKAGE static + +#ifdef SQLITE_SHELL_FIDDLE +/* Deselect most features from the console I/O package for Fiddle. */ +# define SQLITE_CIO_NO_REDIRECT +# define SQLITE_CIO_NO_CLASSIFY +# define SQLITE_CIO_NO_TRANSLATE +# define SQLITE_CIO_NO_SETMODE +#endif +/************************* Begin ../ext/consio/console_io.h ******************/ +/* +** 2023 November 1 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +******************************************************************************** +** This file exposes various interfaces used for console and other I/O +** by the SQLite project command-line tools. These interfaces are used +** at either source conglomeration time, compilation time, or run time. +** This source provides for either inclusion into conglomerated, +** "single-source" forms or separate compilation then linking. +** +** Platform dependencies are "hidden" here by various stratagems so +** that, provided certain conditions are met, the programs using this +** source or object code compiled from it need no explicit conditional +** compilation in their source for their console and stream I/O. +** +** The symbols and functionality exposed here are not a public API. +** This code may change in tandem with other project code as needed. +** +** When this .h file and its companion .c are directly incorporated into +** a source conglomeration (such as shell.c), the preprocessor symbol +** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O +** translation for Windows is effected for the build. */ -#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT -static void setBinaryMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_BINARY); -} -static void setTextMode(FILE *file, int isOutput){ - if( isOutput ) fflush(file); - _setmode(_fileno(file), _O_TEXT); -} + +#ifndef SQLITE_INTERNAL_LINKAGE +# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ +# include #else -# define setBinaryMode(X,Y) -# define setTextMode(X,Y) +# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ #endif -/* True if the timer is enabled */ -static int enableTimer = 0; - -/* A version of strcmp() that works with NULL values */ -static int cli_strcmp(const char *a, const char *b){ - if( a==0 ) a = ""; - if( b==0 ) b = ""; - return strcmp(a,b); -} -static int cli_strncmp(const char *a, const char *b, size_t n){ - if( a==0 ) a = ""; - if( b==0 ) b = ""; - return strncmp(a,b,n); -} - -/* Return the current wall-clock time */ -static sqlite3_int64 timeOfDay(void){ - static sqlite3_vfs *clockVfs = 0; - sqlite3_int64 t; - if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); - if( clockVfs==0 ) return 0; /* Never actually happens */ - if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ - clockVfs->xCurrentTimeInt64(clockVfs, &t); - }else{ - double r; - clockVfs->xCurrentTime(clockVfs, &r); - t = (sqlite3_int64)(r*86400000.0); - } - return t; -} - -#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) -#include -#include - -/* VxWorks does not support getrusage() as far as we can determine */ -#if defined(_WRS_KERNEL) || defined(__RTP__) -struct rusage { - struct timeval ru_utime; /* user CPU time used */ - struct timeval ru_stime; /* system CPU time used */ -}; -#define getrusage(A,B) memset(B,0,sizeof(*B)) +#ifndef SQLITE3_H +/* # include "sqlite3.h" */ #endif -/* Saved resource information for the beginning of an operation */ -static struct rusage sBegin; /* CPU time at start */ -static sqlite3_int64 iBegin; /* Wall-clock time at start */ - -/* -** Begin timing an operation -*/ -static void beginTimer(void){ - if( enableTimer ){ - getrusage(RUSAGE_SELF, &sBegin); - iBegin = timeOfDay(); - } -} +#ifndef SQLITE_CIO_NO_CLASSIFY -/* Return the difference of two time_structs in seconds */ -static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ - return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + - (double)(pEnd->tv_sec - pStart->tv_sec); -} +/* Define enum for use with following function. */ +typedef enum StreamsAreConsole { + SAC_NoConsole = 0, + SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4, + SAC_AnyConsole = 0x7 +} StreamsAreConsole; /* -** Print the timing results. +** Classify the three standard I/O streams according to whether +** they are connected to a console attached to the process. +** +** Returns the bit-wise OR of SAC_{In,Out,Err}Console values, +** or SAC_NoConsole if none of the streams reaches a console. +** +** This function should be called before any I/O is done with +** the given streams. As a side-effect, the given inputs are +** recorded so that later I/O operations on them may be done +** differently than the C library FILE* I/O would be done, +** iff the stream is used for the I/O functions that follow, +** and to support the ones that use an implicit stream. +** +** On some platforms, stream or console mode alteration (aka +** "Setup") may be made which is undone by consoleRestore(). */ -static void endTimer(void){ - if( enableTimer ){ - sqlite3_int64 iEnd = timeOfDay(); - struct rusage sEnd; - getrusage(RUSAGE_SELF, &sEnd); - printf("Run Time: real %.3f user %f sys %f\n", - (iEnd - iBegin)*0.001, - timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), - timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); - } -} - -#define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() -#define HAS_TIMER 1 - -#elif (defined(_WIN32) || defined(WIN32)) - -/* Saved resource information for the beginning of an operation */ -static HANDLE hProcess; -static FILETIME ftKernelBegin; -static FILETIME ftUserBegin; -static sqlite3_int64 ftWallBegin; -typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, - LPFILETIME, LPFILETIME); -static GETPROCTIMES getProcessTimesAddr = NULL; +SQLITE_INTERNAL_LINKAGE StreamsAreConsole +consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); +/* A usual call for convenience: */ +#define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr) /* -** Check to see if we have timer support. Return 1 if necessary -** support found (or found previously). +** After an initial call to consoleClassifySetup(...), renew +** the same setup it effected. (A call not after is an error.) +** This will restore state altered by consoleRestore(); +** +** Applications which run an inferior (child) process which +** inherits the same I/O streams may call this function after +** such a process exits to guard against console mode changes. */ -static int hasTimer(void){ - if( getProcessTimesAddr ){ - return 1; - } else { -#if !SQLITE_OS_WINRT - /* GetProcessTimes() isn't supported in WIN95 and some other Windows - ** versions. See if the version we are running on has it, and if it - ** does, save off a pointer to it and the current process handle. - */ - hProcess = GetCurrentProcess(); - if( hProcess ){ - HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); - if( NULL != hinstLib ){ - getProcessTimesAddr = - (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); - if( NULL != getProcessTimesAddr ){ - return 1; - } - FreeLibrary(hinstLib); - } - } -#endif - } - return 0; -} +SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void); /* -** Begin timing an operation +** Undo any side-effects left by consoleClassifySetup(...). +** +** This should be called after consoleClassifySetup() and +** before the process terminates normally. It is suitable +** for use with the atexit() C library procedure. After +** this call, no console I/O should be done until one of +** console{Classify or Renew}Setup(...) is called again. +** +** Applications which run an inferior (child) process that +** inherits the same I/O streams might call this procedure +** before so that said process will have a console setup +** however users have configured it or come to expect. */ -static void beginTimer(void){ - if( enableTimer && getProcessTimesAddr ){ - FILETIME ftCreation, ftExit; - getProcessTimesAddr(hProcess,&ftCreation,&ftExit, - &ftKernelBegin,&ftUserBegin); - ftWallBegin = timeOfDay(); - } -} +SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); -/* Return the difference of two FILETIME structs in seconds */ -static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ - sqlite_int64 i64Start = *((sqlite_int64 *) pStart); - sqlite_int64 i64End = *((sqlite_int64 *) pEnd); - return (double) ((i64End - i64Start) / 10000000.0); -} +#else /* defined(SQLITE_CIO_NO_CLASSIFY) */ +# define consoleClassifySetup(i,o,e) +# define consoleRenewSetup() +# define consoleRestore() +#endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ +#ifndef SQLITE_CIO_NO_REDIRECT /* -** Print the timing results. +** Set stream to be used for the functions below which write +** to "the designated X stream", where X is Output or Error. +** Returns the previous value. +** +** Alternatively, pass the special value, invalidFileStream, +** to get the designated stream value without setting it. +** +** Before the designated streams are set, they default to +** those passed to consoleClassifySetup(...), and before +** that is called they default to stdout and stderr. +** +** It is error to close a stream so designated, then, without +** designating another, use the corresponding {o,e}Emit(...). */ -static void endTimer(void){ - if( enableTimer && getProcessTimesAddr){ - FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; - sqlite3_int64 ftWallEnd = timeOfDay(); - getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); - printf("Run Time: real %.3f user %f sys %f\n", - (ftWallEnd - ftWallBegin)*0.001, - timeDiff(&ftUserBegin, &ftUserEnd), - timeDiff(&ftKernelBegin, &ftKernelEnd)); - } -} - -#define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() -#define HAS_TIMER hasTimer() - +SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; +SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); +# ifdef CONSIO_SET_ERROR_STREAM +SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); +# endif #else -#define BEGIN_TIMER -#define END_TIMER -#define HAS_TIMER 0 -#endif +# define setOutputStream(pf) +# define setErrorStream(pf) +#endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ +#ifndef SQLITE_CIO_NO_TRANSLATE /* -** Used to prevent warnings about unused parameters +** Emit output like fprintf(). If the output is going to the +** console and translation from UTF-8 is necessary, perform +** the needed translation. Otherwise, write formatted output +** to the provided stream almost as-is, possibly with newline +** translation as specified by set{Binary,Text}Mode(). */ -#define UNUSED_PARAMETER(x) (void)(x) +SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...); +/* Like fPrintfUtf8 except stream is always the designated output. */ +SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...); +/* Like fPrintfUtf8 except stream is always the designated error. */ +SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...); /* -** Number of elements in an array +** Emit output like fputs(). If the output is going to the +** console and translation from UTF-8 is necessary, perform +** the needed translation. Otherwise, write given text to the +** provided stream almost as-is, possibly with newline +** translation as specified by set{Binary,Text}Mode(). */ -#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) +SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO); +/* Like fPutsUtf8 except stream is always the designated output. */ +SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z); +/* Like fPutsUtf8 except stream is always the designated error. */ +SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); /* -** If the following flag is set, then command execution stops -** at an error if we are not interactive. +** Emit output like fPutsUtf8(), except that the length of the +** accepted char or character sequence is limited by nAccept. +** +** Returns the number of accepted char values. */ -static int bail_on_error = 0; +#ifdef CONSIO_SPUTB +SQLITE_INTERNAL_LINKAGE int +fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); +#endif +/* Like fPutbUtf8 except stream is always the designated output. */ +SQLITE_INTERNAL_LINKAGE int +oPutbUtf8(const char *cBuf, int nAccept); +/* Like fPutbUtf8 except stream is always the designated error. */ +#ifdef CONSIO_EPUTB +SQLITE_INTERNAL_LINKAGE int +ePutbUtf8(const char *cBuf, int nAccept); +#endif /* -** Threat stdin as an interactive input if the following variable -** is true. Otherwise, assume stdin is connected to a file or pipe. +** Collect input like fgets(...) with special provisions for input +** from the console on platforms that require same. Defers to the +** C library fgets() when input is not from the console. Newline +** translation may be done as set by set{Binary,Text}Mode(). As a +** convenience, pfIn==NULL is treated as stdin. */ -static int stdin_is_interactive = 1; +SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); +/* Like fGetsUtf8 except stream is always the designated input. */ +/* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ -/* -** On Windows systems we have to know if standard output is a console -** in order to translate UTF-8 into MBCS. The following variable is -** true if translation is required. -*/ -static int stdout_is_console = 1; +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ +#ifndef SQLITE_CIO_NO_SETMODE /* -** The following is the open SQLite database. We make a pointer -** to this database a static variable so that it can be accessed -** by the SIGINT handler to interrupt database processing. +** Set given stream for binary mode, where newline translation is +** not done, or for text mode where, for some platforms, newlines +** are translated to the platform's conventional char sequence. +** If bFlush true, flush the stream. +** +** An additional side-effect is that if the stream is one passed +** to consoleClassifySetup() as an output, it is flushed first. +** +** Note that binary/text mode has no effect on console I/O +** translation. On all platforms, newline to the console starts +** a new line and CR,LF chars from the console become a newline. */ -static sqlite3 *globalDb = 0; +SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); +SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); +#endif -/* -** True if an interrupt (Control-C) has been received. -*/ -static volatile int seenInterrupt = 0; +#ifdef SQLITE_CIO_PROMPTED_IN +typedef struct Prompts { + int numPrompts; + const char **azPrompts; +} Prompts; /* -** This is the name of our program. It is set in main(), used -** in a number of other places, mostly for error messages. +** Macros for use of a line editor. +** +** The following macros define operations involving use of a +** line-editing library or simple console interaction. +** A "T" argument is a text (char *) buffer or filename. +** A "N" argument is an integer. +** +** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. +** SHELL_READ_HISTORY(T) // Read history from file named by T. +** SHELL_WRITE_HISTORY(T) // Write history to file named by T. +** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. +** +** A console program which does interactive console input is +** expected to call: +** SHELL_READ_HISTORY(T) before collecting such input; +** SHELL_ADD_HISTORY(T) as record-worthy input is taken; +** SHELL_STIFLE_HISTORY(N) after console input ceases; then +** SHELL_WRITE_HISTORY(T) before the program exits. */ -static char *Argv0; /* -** Prompt strings. Initialized in main. Settable with -** .prompt main continue +** Retrieve a single line of input text from an input stream. +** +** If pfIn is the input stream passed to consoleClassifySetup(), +** and azPrompt is not NULL, then a prompt is issued before the +** line is collected, as selected by the isContinuation flag. +** Array azPrompt[{0,1}] holds the {main,continuation} prompt. +** +** If zBufPrior is not NULL then it is a buffer from a prior +** call to this routine that can be reused, or will be freed. +** +** The result is stored in space obtained from malloc() and +** must either be freed by the caller or else passed back to +** this function as zBufPrior for reuse. +** +** This function may call upon services of a line-editing +** library to interactively collect line edited input. */ -static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/ -static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */ - +SQLITE_INTERNAL_LINKAGE char * +shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, + short isContinuation, Prompts azPrompt); +#endif /* defined(SQLITE_CIO_PROMPTED_IN) */ /* -** Render output like fprintf(). Except, if the output is going to the -** console and if this is running on a Windows machine, translate the -** output from UTF-8 into MBCS. +** TBD: Define an interface for application(s) to generate +** completion candidates for use by the line-editor. +** +** This may be premature; the CLI is the only application +** that does this. Yet, getting line-editing melded into +** console I/O is desirable because a line-editing library +** may have to establish console operating mode, possibly +** in a way that interferes with the above functionality. */ -#if defined(_WIN32) || defined(WIN32) -void utf8_printf(FILE *out, const char *zFormat, ...){ - va_list ap; - va_start(ap, zFormat); - if( stdout_is_console && (out==stdout || out==stderr) ){ - char *z1 = sqlite3_vmprintf(zFormat, ap); - char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0); - sqlite3_free(z1); - fputs(z2, out); - sqlite3_free(z2); - }else{ - vfprintf(out, zFormat, ap); - } - va_end(ap); -} -#elif !defined(utf8_printf) -# define utf8_printf fprintf + +#if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE)) +/* Skip over as much z[] input char sequence as is valid UTF-8, +** limited per nAccept char's or whole characters and containing +** no char cn such that ((1< =0 => char count, nAccept<0 => character + */ +SQLITE_INTERNAL_LINKAGE const char* +zSkipValidUtf8(const char *z, int nAccept, long ccm); + #endif +/************************* End ../ext/consio/console_io.h ********************/ +/************************* Begin ../ext/consio/console_io.c ******************/ /* -** Render output like fprintf(). This should not be used on anything that -** includes string formatting (e.g. "%s"). +** 2023 November 4 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +******************************************************************************** +** This file implements various interfaces used for console and stream I/O +** by the SQLite project command-line tools, as explained in console_io.h . +** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there. */ -#if !defined(raw_printf) -# define raw_printf fprintf + +#ifndef SQLITE_CDECL +# define SQLITE_CDECL #endif -/* Indicate out-of-memory and exit. */ -static void shell_out_of_memory(void){ - raw_printf(stderr,"Error: out of memory\n"); - exit(1); -} +#ifndef SHELL_NO_SYSINC +# include +# include +# include +# include +# include +# include "console_io.h" +/* # include "sqlite3.h" */ +#endif -/* Check a pointer to see if it is NULL. If it is NULL, exit with an -** out-of-memory error. -*/ -static void shell_check_oom(void *p){ - if( p==0 ) shell_out_of_memory(); -} +#ifndef SQLITE_CIO_NO_TRANSLATE +# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT +# ifndef SHELL_NO_SYSINC +# include +# include +# undef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# include +# endif +# define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ +# else +# ifndef SHELL_NO_SYSINC +# include +# endif +# define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ +# endif +#else +# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ +#endif -/* -** Write I/O traces to the following stream. -*/ -#ifdef SQLITE_ENABLE_IOTRACE -static FILE *iotrace = 0; +#if CIO_WIN_WC_XLATE +/* Character used to represent a known-incomplete UTF-8 char group (�) */ +static WCHAR cBadGroup = 0xfffd; #endif -/* -** This routine works like printf in that its first argument is a -** format string and subsequent arguments are values to be substituted -** in place of % fields. The result of formatting this string -** is written to iotrace. -*/ -#ifdef SQLITE_ENABLE_IOTRACE -static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ - va_list ap; - char *z; - if( iotrace==0 ) return; - va_start(ap, zFormat); - z = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - utf8_printf(iotrace, "%s", z); - sqlite3_free(z); +#if CIO_WIN_WC_XLATE +static HANDLE handleOfFile(FILE *pf){ + int fileDesc = _fileno(pf); + union { intptr_t osfh; HANDLE fh; } fid = { + (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE + }; + return fid.fh; } #endif -/* -** Output string zUtf to stream pOut as w characters. If w is negative, -** then right-justify the text. W is the width in UTF-8 characters, not -** in bytes. This is different from the %*.*s specification in printf -** since with %*.*s the width is measured in bytes, not characters. -*/ -static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ - int i; - int n; - int aw = w<0 ? -w : w; - if( zUtf==0 ) zUtf = ""; - for(i=n=0; zUtf[i]; i++){ - if( (zUtf[i]&0xc0)!=0x80 ){ - n++; - if( n==aw ){ - do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); - break; - } - } - } - if( n>=aw ){ - utf8_printf(pOut, "%.*s", i, zUtf); - }else if( w<0 ){ - utf8_printf(pOut, "%*s%s", aw-n, "", zUtf); - }else{ - utf8_printf(pOut, "%s%*s", zUtf, aw-n, ""); - } +#ifndef SQLITE_CIO_NO_TRANSLATE +typedef struct PerStreamTags { +# if CIO_WIN_WC_XLATE + HANDLE hx; + DWORD consMode; + char acIncomplete[4]; +# else + short reachesConsole; +# endif + FILE *pf; +} PerStreamTags; + +/* Define NULL-like value for things which can validly be 0. */ +# define SHELL_INVALID_FILE_PTR ((FILE *)~0) +# if CIO_WIN_WC_XLATE +# define SHELL_INVALID_CONS_MODE 0xFFFF0000 +# endif + +# if CIO_WIN_WC_XLATE +# define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ + {0,0,0,0}, SHELL_INVALID_FILE_PTR } +# else +# define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } +# endif + +/* Quickly say whether a known output is going to the console. */ +# if CIO_WIN_WC_XLATE +static short pstReachesConsole(PerStreamTags *ppst){ + return (ppst->hx != INVALID_HANDLE_VALUE); +} +# else +# define pstReachesConsole(ppst) 0 +# endif + +# if CIO_WIN_WC_XLATE +static void restoreConsoleArb(PerStreamTags *ppst){ + if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode); } +# else +# define restoreConsoleArb(ppst) +# endif +/* Say whether FILE* appears to be a console, collect associated info. */ +static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ +# if CIO_WIN_WC_XLATE + short rv = 0; + DWORD dwCM = SHELL_INVALID_CONS_MODE; + HANDLE fh = handleOfFile(pf); + ppst->pf = pf; + if( INVALID_HANDLE_VALUE != fh ){ + rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM)); + } + ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE; + ppst->consMode = dwCM; + return rv; +# else + ppst->pf = pf; + ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); + return ppst->reachesConsole; +# endif +} -/* -** Determines if a string is a number of not. -*/ -static int isNumber(const char *z, int *realnum){ - if( *z=='-' || *z=='+' ) z++; - if( !IsDigit(*z) ){ - return 0; +# if CIO_WIN_WC_XLATE +/* Define console modes for use with the Windows Console API. */ +# define SHELL_CONI_MODE \ + (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ + | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) +# define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ + | ENABLE_VIRTUAL_TERMINAL_PROCESSING) +# endif + +typedef struct ConsoleInfo { + PerStreamTags pstSetup[3]; + PerStreamTags pstDesignated[3]; + StreamsAreConsole sacSetup; +} ConsoleInfo; + +static short isValidStreamInfo(PerStreamTags *ppst){ + return (ppst->pf != SHELL_INVALID_FILE_PTR); +} + +static ConsoleInfo consoleInfo = { + { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, + { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, + SAC_NoConsole /* sacSetup */ +}; + +SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; + +# if CIO_WIN_WC_XLATE +static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ + if( pstReachesConsole(ppst) ){ + DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; + SetConsoleMode(ppst->hx, cm); } - z++; - if( realnum ) *realnum = 0; - while( IsDigit(*z) ){ z++; } - if( *z=='.' ){ - z++; - if( !IsDigit(*z) ) return 0; - while( IsDigit(*z) ){ z++; } - if( realnum ) *realnum = 1; +} +# else +# define maybeSetupAsConsole(ppst,odir) +# endif + +SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ +# if CIO_WIN_WC_XLATE + int ix = 0; + while( ix < 6 ){ + PerStreamTags *ppst = (ix<3)? + &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3]; + maybeSetupAsConsole(ppst, (ix % 3)>0); + ++ix; } - if( *z=='e' || *z=='E' ){ - z++; - if( *z=='+' || *z=='-' ) z++; - if( !IsDigit(*z) ) return 0; - while( IsDigit(*z) ){ z++; } - if( realnum ) *realnum = 1; +# endif +} + +SQLITE_INTERNAL_LINKAGE StreamsAreConsole +consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ + StreamsAreConsole rv = SAC_NoConsole; + FILE* apf[3] = { pfIn, pfOut, pfErr }; + int ix; + for( ix = 2; ix >= 0; --ix ){ + PerStreamTags *ppst = &consoleInfo.pstSetup[ix]; + if( streamOfConsole(apf[ix], ppst) ){ + rv |= (SAC_InConsole< 0 ) fflush(apf[ix]); } - return *z==0; + consoleInfo.sacSetup = rv; + consoleRenewSetup(); + return rv; } -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -*/ -static int strlen30(const char *z){ - const char *z2 = z; - while( *z2 ){ z2++; } - return 0x3fffffff & (int)(z2 - z); +SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ +# if CIO_WIN_WC_XLATE + static ConsoleInfo *pci = &consoleInfo; + if( pci->sacSetup ){ + int ix; + for( ix=0; ix<3; ++ix ){ + if( pci->sacSetup & (SAC_InConsole< pstSetup[ix]; + SetConsoleMode(ppst->hx, ppst->consMode); + } + } + } +# endif } +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ -/* -** Return the length of a string in characters. Multibyte UTF8 characters -** count as a single character. +#ifdef SQLITE_CIO_INPUT_REDIR +/* Say whether given FILE* is among those known, via either +** consoleClassifySetup() or set{Output,Error}Stream, as +** readable, and return an associated PerStreamTags pointer +** if so. Otherwise, return 0. */ -static int strlenChar(const char *z){ - int n = 0; - while( *z ){ - if( (0xc0&*(z++))!=0x80 ) n++; - } - return n; +static PerStreamTags * isKnownReadable(FILE *pf){ + static PerStreamTags *apst[] = { + &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0 + }; + int ix = 0; + do { + if( apst[ix]->pf == pf ) break; + } while( apst[++ix] != 0 ); + return apst[ix]; } +#endif -/* -** Return open FILE * if zFile exists, can be opened for read -** and is an ordinary file or a character stream source. -** Otherwise return 0. +#ifndef SQLITE_CIO_NO_TRANSLATE +/* Say whether given FILE* is among those known, via either +** consoleClassifySetup() or set{Output,Error}Stream, as +** writable, and return an associated PerStreamTags pointer +** if so. Otherwise, return 0. */ -static FILE * openChrSource(const char *zFile){ -#ifdef _WIN32 - struct _stat x = {0}; -# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) - /* On Windows, open first, then check the stream nature. This order - ** is necessary because _stat() and sibs, when checking a named pipe, - ** effectively break the pipe as its supplier sees it. */ - FILE *rv = fopen(zFile, "rb"); - if( rv==0 ) return 0; - if( _fstat(_fileno(rv), &x) != 0 - || !STAT_CHR_SRC(x.st_mode)){ - fclose(rv); - rv = 0; +static PerStreamTags * isKnownWritable(FILE *pf){ + static PerStreamTags *apst[] = { + &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2], + &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0 + }; + int ix = 0; + do { + if( apst[ix]->pf == pf ) break; + } while( apst[++ix] != 0 ); + return apst[ix]; +} + +static FILE *designateEmitStream(FILE *pf, unsigned chix){ + FILE *rv = consoleInfo.pstDesignated[chix].pf; + if( pf == invalidFileStream ) return rv; + else{ + /* Setting a possibly new output stream. */ + PerStreamTags *ppst = isKnownWritable(pf); + if( ppst != 0 ){ + PerStreamTags pst = *ppst; + consoleInfo.pstDesignated[chix] = pst; + }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]); } return rv; -#else - struct stat x = {0}; - int rc = stat(zFile, &x); -# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) - if( rc!=0 ) return 0; - if( STAT_CHR_SRC(x.st_mode) ){ - return fopen(zFile, "rb"); - }else{ - return 0; - } -#endif -#undef STAT_CHR_SRC } -/* -** This routine reads a line of text from FILE in, stores -** the text in memory obtained from malloc() and returns a pointer -** to the text. NULL is returned at end of file, or if malloc() -** fails. -** -** If zLine is not NULL then it is a malloced buffer returned from -** a previous call to this routine that may be reused. -*/ -static char *local_getline(char *zLine, FILE *in){ - int nLine = zLine==0 ? 0 : 100; - int n = 0; +SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ + return designateEmitStream(pf, 1); +} +# ifdef CONSIO_SET_ERROR_STREAM +SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ + return designateEmitStream(pf, 2); +} +# endif +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - while( 1 ){ - if( n+100>nLine ){ - nLine = nLine*2 + 100; - zLine = realloc(zLine, nLine); - shell_check_oom(zLine); - } - if( fgets(&zLine[n], nLine - n, in)==0 ){ - if( n==0 ){ - free(zLine); - return 0; - } - zLine[n] = 0; - break; - } - while( zLine[n] ) n++; - if( n>0 && zLine[n-1]=='\n' ){ - n--; - if( n>0 && zLine[n-1]=='\r' ) n--; - zLine[n] = 0; - break; - } - } -#if defined(_WIN32) || defined(WIN32) - /* For interactive input on Windows systems, translate the - ** multi-byte characterset characters into UTF-8. */ - if( stdin_is_interactive && in==stdin ){ - char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0); - if( zTrans ){ - i64 nTrans = strlen(zTrans)+1; - if( nTrans>nLine ){ - zLine = realloc(zLine, nTrans); - shell_check_oom(zLine); +#ifndef SQLITE_CIO_NO_SETMODE +# if CIO_WIN_WC_XLATE +static void setModeFlushQ(FILE *pf, short bFlush, int mode){ + if( bFlush ) fflush(pf); + _setmode(_fileno(pf), mode); +} +# else +# define setModeFlushQ(f, b, m) if(b) fflush(f) +# endif + +SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ + setModeFlushQ(pf, bFlush, _O_BINARY); +} +SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ + setModeFlushQ(pf, bFlush, _O_TEXT); +} +# undef setModeFlushQ + +#else /* defined(SQLITE_CIO_NO_SETMODE) */ +# define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) +# define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) +#endif /* defined(SQLITE_CIO_NO_SETMODE) */ + +#ifndef SQLITE_CIO_NO_TRANSLATE +# if CIO_WIN_WC_XLATE +/* Write buffer cBuf as output to stream known to reach console, +** limited to ncTake char's. Return ncTake on success, else 0. */ +static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ + int rv = 0; + if( z!=NULL ){ + int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); + if( nwc > 0 ){ + WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); + if( zw!=NULL ){ + nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); + if( nwc > 0 ){ + /* Translation from UTF-8 to UTF-16, then WCHARs out. */ + if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ + rv = ncTake; + } + } + sqlite3_free(zw); } - memcpy(zLine, zTrans, nTrans); - sqlite3_free(zTrans); } } -#endif /* defined(_WIN32) || defined(WIN32) */ - return zLine; + return rv; } -/* -** Retrieve a single line of input text. -** -** If in==0 then read from standard input and prompt before each line. -** If isContinuation is true, then a continuation prompt is appropriate. -** If isContinuation is zero, then the main prompt should be used. -** -** If zPrior is not NULL then it is a buffer from a prior call to this -** routine that can be reused. -** -** The result is stored in space obtained from malloc() and must either -** be freed by the caller or else passed back into this routine via the -** zPrior argument for reuse. -*/ -#ifndef SQLITE_SHELL_FIDDLE -static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ - char *zPrompt; - char *zResult; - if( in!=0 ){ - zResult = local_getline(zPrior, in); +/* For {f,o,e}PrintfUtf8() when stream is known to reach console. */ +static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ + char *z = sqlite3_vmprintf(zFormat, ap); + if( z ){ + int rv = conZstrEmit(ppst, z, (int)strlen(z)); + sqlite3_free(z); + return rv; + }else return 0; +} +# endif /* CIO_WIN_WC_XLATE */ + +# ifdef CONSIO_GET_EMIT_STREAM +static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, + PerStreamTags *ppst){ + PerStreamTags *rv = isKnownWritable(pf); + short isValid = (rv!=0)? isValidStreamInfo(rv) : 0; + if( rv != 0 && isValid ) return rv; + streamOfConsole(pf, ppst); + return ppst; +} +# endif + +/* Get stream info, either for designated output or error stream when +** chix equals 1 or 2, or for an arbitrary stream when chix == 0. +** In either case, ppst references a caller-owned PerStreamTags +** struct which may be filled in if none of the known writable +** streams is being held by consoleInfo. The ppf parameter is a +** byref output when chix!=0 and a byref input when chix==0. + */ +static PerStreamTags * +getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, + /* in/out */ FILE **ppf){ + PerStreamTags *ppstTry; + FILE *pfEmit; + if( chix > 0 ){ + ppstTry = &consoleInfo.pstDesignated[chix]; + if( !isValidStreamInfo(ppstTry) ){ + ppstTry = &consoleInfo.pstSetup[chix]; + pfEmit = ppst->pf; + }else pfEmit = ppstTry->pf; + if( !isValidStreamInfo(ppstTry) ){ + pfEmit = (chix > 1)? stderr : stdout; + ppstTry = ppst; + streamOfConsole(pfEmit, ppstTry); + } + *ppf = pfEmit; }else{ - zPrompt = isContinuation ? continuePrompt : mainPrompt; -#if SHELL_USE_LOCAL_GETLINE - printf("%s", zPrompt); - fflush(stdout); - zResult = local_getline(zPrior, stdin); -#else - free(zPrior); - zResult = shell_readline(zPrompt); - if( zResult && *zResult ) shell_add_history(zResult); -#endif + ppstTry = isKnownWritable(*ppf); + if( ppstTry != 0 ) return ppstTry; + streamOfConsole(*ppf, ppst); + return ppst; } - return zResult; + return ppstTry; } -#endif /* !SQLITE_SHELL_FIDDLE */ -/* -** Return the value of a hexadecimal digit. Return -1 if the input -** is not a hex digit. -*/ -static int hexDigitValue(char c){ - if( c>='0' && c<='9' ) return c - '0'; - if( c>='a' && c<='f' ) return c - 'a' + 10; - if( c>='A' && c<='F' ) return c - 'A' + 10; - return -1; +SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ + va_list ap; + int rv; + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +# else + getEmitStreamInfo(1, &pst, &pfOut); +# endif + assert(zFormat!=0); + va_start(ap, zFormat); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + rv = conioVmPrintf(ppst, zFormat, ap); + }else{ +# endif + rv = vfprintf(pfOut, zFormat, ap); +# if CIO_WIN_WC_XLATE + } +# endif + va_end(ap); + return rv; } -/* -** Interpret zArg as an integer value, possibly with suffixes. -*/ -static sqlite3_int64 integerValue(const char *zArg){ - sqlite3_int64 v = 0; - static const struct { char *zSuffix; int iMult; } aMult[] = { - { "KiB", 1024 }, - { "MiB", 1024*1024 }, - { "GiB", 1024*1024*1024 }, - { "KB", 1000 }, - { "MB", 1000000 }, - { "GB", 1000000000 }, - { "K", 1000 }, - { "M", 1000000 }, - { "G", 1000000000 }, - }; - int i; - int isNeg = 0; - if( zArg[0]=='-' ){ - isNeg = 1; - zArg++; - }else if( zArg[0]=='+' ){ - zArg++; - } - if( zArg[0]=='0' && zArg[1]=='x' ){ - int x; - zArg += 2; - while( (x = hexDigitValue(zArg[0]))>=0 ){ - v = (v<<4) + x; - zArg++; - } +SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ + va_list ap; + int rv; + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); +# else + getEmitStreamInfo(2, &pst, &pfErr); +# endif + assert(zFormat!=0); + va_start(ap, zFormat); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + rv = conioVmPrintf(ppst, zFormat, ap); }else{ - while( IsDigit(zArg[0]) ){ - v = v*10 + zArg[0] - '0'; - zArg++; - } +# endif + rv = vfprintf(pfErr, zFormat, ap); +# if CIO_WIN_WC_XLATE } - for(i=0; i pf) ) restoreConsoleArb(ppst); + }else{ +# endif + rv = vfprintf(pfO, zFormat, ap); +# if CIO_WIN_WC_XLATE } - return isNeg? -v : v; +# endif + va_end(ap); + return rv; } -/* -** A variable length string to which one can append text. -*/ -typedef struct ShellText ShellText; -struct ShellText { - char *z; - int n; - int nAlloc; -}; +SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); +# else + getEmitStreamInfo(0, &pst, &pfO); +# endif + assert(z!=0); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + int rv; + maybeSetupAsConsole(ppst, 1); + rv = conZstrEmit(ppst, z, (int)strlen(z)); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + return rv; + }else { +# endif + return (fputs(z, pfO)<0)? 0 : (int)strlen(z); +# if CIO_WIN_WC_XLATE + } +# endif +} -/* -** Initialize and destroy a ShellText object -*/ -static void initText(ShellText *p){ - memset(p, 0, sizeof(*p)); +SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); +# else + getEmitStreamInfo(2, &pst, &pfErr); +# endif + assert(z!=0); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); + else { +# endif + return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); +# if CIO_WIN_WC_XLATE + } +# endif } -static void freeText(ShellText *p){ - free(p->z); - initText(p); + +SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +# else + getEmitStreamInfo(1, &pst, &pfOut); +# endif + assert(z!=0); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); + else { +# endif + return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); +# if CIO_WIN_WC_XLATE + } +# endif } -/* zIn is either a pointer to a NULL-terminated string in memory obtained -** from malloc(), or a NULL pointer. The string pointed to by zAppend is -** added to zIn, and the result returned in memory obtained from malloc(). -** zIn, if it was not NULL, is freed. -** -** If the third argument, quote, is not '\0', then it is used as a -** quote character for zAppend. -*/ -static void appendText(ShellText *p, const char *zAppend, char quote){ - i64 len; - i64 i; - i64 nAppend = strlen30(zAppend); +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ - len = nAppend+p->n+1; - if( quote ){ - len += 2; - for(i=0; i =0 => char count, nAccept<0 => character + */ +SQLITE_INTERNAL_LINKAGE const char* +zSkipValidUtf8(const char *z, int nAccept, long ccm){ + int ng = (nAccept<0)? -nAccept : 0; + const char *pcLimit = (nAccept>=0)? z+nAccept : 0; + assert(z!=0); + while( (pcLimit)? (z = pcLimit ) return z; + else{ + char ct = *zt++; + if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ + /* Trailing bytes are too few, too many, or invalid. */ + return z; + } + } + } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ + z = zt; } } + return z; +} +#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ + +#ifndef SQLITE_CIO_NO_TRANSLATE + +#ifdef CONSIO_SPUTB +SQLITE_INTERNAL_LINKAGE int +fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ + assert(pfO!=0); +# if CIO_WIN_WC_XLATE + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); + if( pstReachesConsole(ppst) ){ + int rv; + maybeSetupAsConsole(ppst, 1); + rv = conZstrEmit(ppst, cBuf, nAccept); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + return rv; + }else { +# endif + return (int)fwrite(cBuf, 1, nAccept, pfO); +# if CIO_WIN_WC_XLATE + } +# endif +} +#endif /* defined(CONSIO_SPUTB) */ - if( p->z==0 || p->n+len>=p->nAlloc ){ - p->nAlloc = p->nAlloc*2 + len + 20; - p->z = realloc(p->z, p->nAlloc); - shell_check_oom(p->z); +SQLITE_INTERNAL_LINKAGE int +oPutbUtf8(const char *cBuf, int nAccept){ + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ +# if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); +# else + getEmitStreamInfo(1, &pst, &pfOut); +# endif +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, nAccept); + }else { +# endif + return (int)fwrite(cBuf, 1, nAccept, pfOut); +# if CIO_WIN_WC_XLATE } +# endif +} - if( quote ){ - char *zCsr = p->z+p->n; - *zCsr++ = quote; - for(i=0; i n = (int)(zCsr - p->z); - *zCsr = '\0'; +# ifdef CONSIO_EPUTB +SQLITE_INTERNAL_LINKAGE int +ePutbUtf8(const char *cBuf, int nAccept){ + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); +# if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, nAccept); + }else { +# endif + return (int)fwrite(cBuf, 1, nAccept, pfErr); +# if CIO_WIN_WC_XLATE + } +# endif +} +# endif /* defined(CONSIO_EPUTB) */ + +SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ + if( pfIn==0 ) pfIn = stdin; +# if CIO_WIN_WC_XLATE + if( pfIn == consoleInfo.pstSetup[0].pf + && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ +# if CIO_WIN_WC_XLATE==1 +# define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ + WCHAR wcBuf[SHELL_GULP+1]; + int lend = 0, noc = 0; + if( ncMax > 0 ) cBuf[0] = 0; + while( noc < ncMax-8-1 && !lend ){ + /* There is room for at least 2 more characters and a 0-terminator. */ + int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; +# undef SHELL_GULP + DWORD nbr = 0; + BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0); + if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ + /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */ + DWORD nbrx; + bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0); + if( bRC ) nbr += nbrx; + } + if( !bRC || (noc==0 && nbr==0) ) return 0; + if( nbr > 0 ){ + int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); + if( nmb != 0 && noc+nmb <= ncMax ){ + int iseg = noc; + nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0); + noc += nmb; + /* Fixup line-ends as coded by Windows for CR (or "Enter".) + ** This is done without regard for any setMode{Text,Binary}() + ** call that might have been done on the interactive input. + */ + if( noc > 0 ){ + if( cBuf[noc-1]=='\n' ){ + lend = 1; + if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n'; + } + } + /* Check for ^Z (anywhere in line) too, to act as EOF. */ + while( iseg < noc ){ + if( cBuf[iseg]=='\x1a' ){ + noc = iseg; /* Chop ^Z and anything following. */ + lend = 1; /* Counts as end of line too. */ + break; + } + ++iseg; + } + }else break; /* Drop apparent garbage in. (Could assert.) */ + }else break; + } + /* If got nothing, (after ^Z chop), must be at end-of-file. */ + if( noc > 0 ){ + cBuf[noc] = 0; + return cBuf; + }else return 0; +# endif }else{ - memcpy(p->z+p->n, zAppend, nAppend); - p->n += nAppend; - p->z[p->n] = '\0'; +# endif + return fgets(cBuf, ncMax, pfIn); +# if CIO_WIN_WC_XLATE + } +# endif +} +#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + +#undef SHELL_INVALID_FILE_PTR + +/************************* End ../ext/consio/console_io.c ********************/ + +#ifndef SQLITE_SHELL_FIDDLE +/* From here onward, fgets() is redirected to the console_io library. */ +# define fgets(b,n,f) fGetsUtf8(b,n,f) +/* + * Define macros for emitting output text in various ways: + * sputz(s, z) => emit 0-terminated string z to given stream s + * sputf(s, f, ...) => emit varargs per format f to given stream s + * oputz(z) => emit 0-terminated string z to default stream + * oputf(f, ...) => emit varargs per format f to default stream + * eputz(z) => emit 0-terminated string z to error stream + * eputf(f, ...) => emit varargs per format f to error stream + * oputb(b, n) => emit char buffer b[0..n-1] to default stream + * + * Note that the default stream is whatever has been last set via: + * setOutputStream(FILE *pf) + * This is normally the stream that CLI normal output goes to. + * For the stand-alone CLI, it is stdout with no .output redirect. + */ +# define sputz(s,z) fPutsUtf8(z,s) +# define sputf fPrintfUtf8 +# define oputz(z) oPutsUtf8(z) +# define oputf oPrintfUtf8 +# define eputz(z) ePutsUtf8(z) +# define eputf ePrintfUtf8 +# define oputb(buf,na) oPutbUtf8(buf,na) +#else +/* For Fiddle, all console handling and emit redirection is omitted. */ +# define sputz(fp,z) fputs(z,fp) +# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) +# define oputz(z) fputs(z,stdout) +# define oputf(fmt, ...) printf(fmt,__VA_ARGS__) +# define eputz(z) fputs(z,stderr) +# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) +# define oputb(buf,na) fwrite(buf,1,na,stdout) +#endif + +/* True if the timer is enabled */ +static int enableTimer = 0; + +/* A version of strcmp() that works with NULL values */ +static int cli_strcmp(const char *a, const char *b){ + if( a==0 ) a = ""; + if( b==0 ) b = ""; + return strcmp(a,b); +} +static int cli_strncmp(const char *a, const char *b, size_t n){ + if( a==0 ) a = ""; + if( b==0 ) b = ""; + return strncmp(a,b,n); +} + +/* Return the current wall-clock time */ +static sqlite3_int64 timeOfDay(void){ + static sqlite3_vfs *clockVfs = 0; + sqlite3_int64 t; + if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ) return 0; /* Never actually happens */ + if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ + clockVfs->xCurrentTimeInt64(clockVfs, &t); + }else{ + double r; + clockVfs->xCurrentTime(clockVfs, &r); + t = (sqlite3_int64)(r*86400000.0); } + return t; } +#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) +#include +#include + +/* VxWorks does not support getrusage() as far as we can determine */ +#if defined(_WRS_KERNEL) || defined(__RTP__) +struct rusage { + struct timeval ru_utime; /* user CPU time used */ + struct timeval ru_stime; /* system CPU time used */ +}; +#define getrusage(A,B) memset(B,0,sizeof(*B)) +#endif + +/* Saved resource information for the beginning of an operation */ +static struct rusage sBegin; /* CPU time at start */ +static sqlite3_int64 iBegin; /* Wall-clock time at start */ + /* -** Attempt to determine if identifier zName needs to be quoted, either -** because it contains non-alphanumeric characters, or because it is an -** SQLite keyword. Be conservative in this estimate: When in doubt assume -** that quoting is required. -** -** Return '"' if quoting is required. Return 0 if no quoting is required. +** Begin timing an operation */ -static char quoteChar(const char *zName){ - int i; - if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; - for(i=0; zName[i]; i++){ - if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; +static void beginTimer(void){ + if( enableTimer ){ + getrusage(RUSAGE_SELF, &sBegin); + iBegin = timeOfDay(); } - return sqlite3_keyword_check(zName, i) ? '"' : 0; +} + +/* Return the difference of two time_structs in seconds */ +static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ + return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + + (double)(pEnd->tv_sec - pStart->tv_sec); } /* -** Construct a fake object name and column list to describe the structure -** of the view, virtual table, or table valued function zSchema.zName. +** Print the timing results. */ -static char *shellFakeSchema( - sqlite3 *db, /* The database connection containing the vtab */ - const char *zSchema, /* Schema of the database holding the vtab */ - const char *zName /* The name of the virtual table */ -){ - sqlite3_stmt *pStmt = 0; - char *zSql; - ShellText s; - char cQuote; - char *zDiv = "("; - int nRow = 0; +static void endTimer(void){ + if( enableTimer ){ + sqlite3_int64 iEnd = timeOfDay(); + struct rusage sEnd; + getrusage(RUSAGE_SELF, &sEnd); + oputf("Run Time: real %.3f user %f sys %f\n", + (iEnd - iBegin)*0.001, + timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), + timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); + } +} - zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;", - zSchema ? zSchema : "main", zName); - shell_check_oom(zSql); - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - initText(&s); - if( zSchema ){ - cQuote = quoteChar(zSchema); - if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0; - appendText(&s, zSchema, cQuote); - appendText(&s, ".", 0); - } - cQuote = quoteChar(zName); - appendText(&s, zName, cQuote); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - const char *zCol = (const char*)sqlite3_column_text(pStmt, 1); - nRow++; - appendText(&s, zDiv, 0); - zDiv = ","; - if( zCol==0 ) zCol = ""; - cQuote = quoteChar(zCol); - appendText(&s, zCol, cQuote); - } - appendText(&s, ")", 0); - sqlite3_finalize(pStmt); - if( nRow==0 ){ - freeText(&s); - s.z = 0; - } - return s.z; -} +#define BEGIN_TIMER beginTimer() +#define END_TIMER endTimer() +#define HAS_TIMER 1 -/* -** SQL function: shell_module_schema(X) -** -** Return a fake schema for the table-valued function or eponymous virtual -** table X. -*/ -static void shellModuleSchema( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - const char *zName; - char *zFake; - UNUSED_PARAMETER(nVal); - zName = (const char*)sqlite3_value_text(apVal[0]); - zFake = zName ? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0; - if( zFake ){ - sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), - -1, sqlite3_free); - free(zFake); - } -} +#elif (defined(_WIN32) || defined(WIN32)) + +/* Saved resource information for the beginning of an operation */ +static HANDLE hProcess; +static FILETIME ftKernelBegin; +static FILETIME ftUserBegin; +static sqlite3_int64 ftWallBegin; +typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, + LPFILETIME, LPFILETIME); +static GETPROCTIMES getProcessTimesAddr = NULL; /* -** SQL function: shell_add_schema(S,X) -** -** Add the schema name X to the CREATE statement in S and return the result. -** Examples: -** -** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); -** -** Also works on -** -** CREATE INDEX -** CREATE UNIQUE INDEX -** CREATE VIEW -** CREATE TRIGGER -** CREATE VIRTUAL TABLE -** -** This UDF is used by the .schema command to insert the schema name of -** attached databases into the middle of the sqlite_schema.sql field. +** Check to see if we have timer support. Return 1 if necessary +** support found (or found previously). */ -static void shellAddSchemaName( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - static const char *aPrefix[] = { - "TABLE", - "INDEX", - "UNIQUE INDEX", - "VIEW", - "TRIGGER", - "VIRTUAL TABLE" - }; - int i = 0; - const char *zIn = (const char*)sqlite3_value_text(apVal[0]); - const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); - const char *zName = (const char*)sqlite3_value_text(apVal[2]); - sqlite3 *db = sqlite3_context_db_handle(pCtx); - UNUSED_PARAMETER(nVal); - if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){ - for(i=0; i -#include -#include -#include -#include -#include -#include +#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) /* -** We may need several defines that should have been in "sys/stat.h". +** If the following flag is set, then command execution stops +** at an error if we are not interactive. */ - -#ifndef S_ISREG -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#endif - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif - -#ifndef S_ISLNK -#define S_ISLNK(mode) (0) -#endif +static int bail_on_error = 0; /* -** We may need to provide the "mode_t" type. +** Treat stdin as an interactive input if the following variable +** is true. Otherwise, assume stdin is connected to a file or pipe. */ - -#ifndef MODE_T_DEFINED - #define MODE_T_DEFINED - typedef unsigned short mode_t; -#endif +static int stdin_is_interactive = 1; /* -** We may need to provide the "ino_t" type. +** On Windows systems we need to know if standard output is a console +** in order to show that UTF-16 translation is done in the sign-on +** banner. The following variable is true if it is the console. */ - -#ifndef INO_T_DEFINED - #define INO_T_DEFINED - typedef unsigned short ino_t; -#endif +static int stdout_is_console = 1; /* -** We need to define "NAME_MAX" if it was not present in "limits.h". +** The following is the open SQLite database. We make a pointer +** to this database a static variable so that it can be accessed +** by the SIGINT handler to interrupt database processing. */ - -#ifndef NAME_MAX -# ifdef FILENAME_MAX -# define NAME_MAX (FILENAME_MAX) -# else -# define NAME_MAX (260) -# endif -#endif +static sqlite3 *globalDb = 0; /* -** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". +** True if an interrupt (Control-C) has been received. */ - -#ifndef NULL_INTPTR_T -# define NULL_INTPTR_T ((intptr_t)(0)) -#endif - -#ifndef BAD_INTPTR_T -# define BAD_INTPTR_T ((intptr_t)(-1)) -#endif +static volatile int seenInterrupt = 0; /* -** We need to provide the necessary structures and related types. +** This is the name of our program. It is set in main(), used +** in a number of other places, mostly for error messages. */ - -#ifndef DIRENT_DEFINED -#define DIRENT_DEFINED -typedef struct DIRENT DIRENT; -typedef DIRENT *LPDIRENT; -struct DIRENT { - ino_t d_ino; /* Sequence number, do not use. */ - unsigned d_attributes; /* Win32 file attributes. */ - char d_name[NAME_MAX + 1]; /* Name within the directory. */ -}; -#endif - -#ifndef DIR_DEFINED -#define DIR_DEFINED -typedef struct DIR DIR; -typedef DIR *LPDIR; -struct DIR { - intptr_t d_handle; /* Value returned by "_findfirst". */ - DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ - DIRENT d_next; /* DIRENT constructed based on "_findnext". */ -}; -#endif +static char *Argv0; /* -** Provide a macro, for use by the implementation, to determine if a -** particular directory entry should be skipped over when searching for -** the next directory entry that should be returned by the readdir() or -** readdir_r() functions. +** Prompt strings. Initialized in main. Settable with +** .prompt main continue */ +#define PROMPT_LEN_MAX 20 +/* First line prompt. default: "sqlite> " */ +static char mainPrompt[PROMPT_LEN_MAX]; +/* Continuation prompt. default: " ...> " */ +static char continuePrompt[PROMPT_LEN_MAX]; + +/* This is variant of the standard-library strncpy() routine with the +** one change that the destination string is always zero-terminated, even +** if there is no zero-terminator in the first n-1 characters of the source +** string. +*/ +static char *shell_strncpy(char *dest, const char *src, size_t n){ + size_t i; + for(i=0; i inParenLevel += ni; + if( ni==0 ) p->inParenLevel = 0; + p->zScannerAwaits = 0; +} + +/* Record that a lexeme is opened, or closed with args==0. */ +static void setLexemeOpen(struct DynaPrompt *p, char *s, char c){ + if( s!=0 || c==0 ){ + p->zScannerAwaits = s; + p->acAwait[0] = 0; + }else{ + p->acAwait[0] = c; + p->zScannerAwaits = p->acAwait; + } +} -#ifndef is_filtered -# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) -#endif +/* Upon demand, derive the continuation prompt to display. */ +static char *dynamicContinuePrompt(void){ + if( continuePrompt[0]==0 + || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){ + return continuePrompt; + }else{ + if( dynPrompt.zScannerAwaits ){ + size_t ncp = strlen(continuePrompt); + size_t ndp = strlen(dynPrompt.zScannerAwaits); + if( ndp > ncp-3 ) return continuePrompt; + strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); + while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' '; + shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, + PROMPT_LEN_MAX-4); + }else{ + if( dynPrompt.inParenLevel>9 ){ + shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4); + }else if( dynPrompt.inParenLevel<0 ){ + shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4); + }else{ + shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4); + dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel); + } + shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, + PROMPT_LEN_MAX-4); + } + } + return dynPrompt.dynamicPrompt; +} +#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ -/* -** Provide the function prototype for the POSIX compatiable getenv() -** function. This function is not thread-safe. -*/ +/* Indicate out-of-memory and exit. */ +static void shell_out_of_memory(void){ + eputz("Error: out of memory\n"); + exit(1); +} -extern const char *windirent_getenv(const char *name); +/* Check a pointer to see if it is NULL. If it is NULL, exit with an +** out-of-memory error. +*/ +static void shell_check_oom(const void *p){ + if( p==0 ) shell_out_of_memory(); +} /* -** Finally, we can provide the function prototypes for the opendir(), -** readdir(), readdir_r(), and closedir() POSIX functions. +** Write I/O traces to the following stream. */ +#ifdef SQLITE_ENABLE_IOTRACE +static FILE *iotrace = 0; +#endif -extern LPDIR opendir(const char *dirname); -extern LPDIRENT readdir(LPDIR dirp); -extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); -extern INT closedir(LPDIR dirp); - -#endif /* defined(WIN32) && defined(_MSC_VER) */ - -/************************* End test_windirent.h ********************/ -/************************* Begin test_windirent.c ******************/ /* -** 2015 November 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file contains code to implement most of the opendir() family of -** POSIX functions on Win32 using the MSVCRT. +** This routine works like printf in that its first argument is a +** format string and subsequent arguments are values to be substituted +** in place of % fields. The result of formatting this string +** is written to iotrace. */ - -#if defined(_WIN32) && defined(_MSC_VER) -/* #include "test_windirent.h" */ +#ifdef SQLITE_ENABLE_IOTRACE +static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ + va_list ap; + char *z; + if( iotrace==0 ) return; + va_start(ap, zFormat); + z = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + sputf(iotrace, "%s", z); + sqlite3_free(z); +} +#endif /* -** Implementation of the POSIX getenv() function using the Win32 API. -** This function is not thread-safe. +** Output string zUtf to Out stream as w characters. If w is negative, +** then right-justify the text. W is the width in UTF-8 characters, not +** in bytes. This is different from the %*.*s specification in printf +** since with %*.*s the width is measured in bytes, not characters. */ -const char *windirent_getenv( - const char *name -){ - static char value[32768]; /* Maximum length, per MSDN */ - DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */ - DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */ - - memset(value, 0, sizeof(value)); - dwRet = GetEnvironmentVariableA(name, value, dwSize); - if( dwRet==0 || dwRet>dwSize ){ - /* - ** The function call to GetEnvironmentVariableA() failed -OR- - ** the buffer is not large enough. Either way, return NULL. - */ - return 0; +static void utf8_width_print(int w, const char *zUtf){ + int i; + int n; + int aw = w<0 ? -w : w; + if( zUtf==0 ) zUtf = ""; + for(i=n=0; zUtf[i]; i++){ + if( (zUtf[i]&0xc0)!=0x80 ){ + n++; + if( n==aw ){ + do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); + break; + } + } + } + if( n>=aw ){ + oputf("%.*s", i, zUtf); + }else if( w<0 ){ + oputf("%*s%s", aw-n, "", zUtf); }else{ - /* - ** The function call to GetEnvironmentVariableA() succeeded - ** -AND- the buffer contains the entire value. - */ - return value; + oputf("%s%*s", zUtf, aw-n, ""); } } + /* -** Implementation of the POSIX opendir() function using the MSVCRT. +** Determines if a string is a number of not. */ -LPDIR opendir( - const char *dirname -){ - struct _finddata_t data; - LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); - SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); - - if( dirp==NULL ) return NULL; - memset(dirp, 0, sizeof(DIR)); - - /* TODO: Remove this if Unix-style root paths are not used. */ - if( sqlite3_stricmp(dirname, "/")==0 ){ - dirname = windirent_getenv("SystemDrive"); +static int isNumber(const char *z, int *realnum){ + if( *z=='-' || *z=='+' ) z++; + if( !IsDigit(*z) ){ + return 0; } - - memset(&data, 0, sizeof(struct _finddata_t)); - _snprintf(data.name, namesize, "%s\\*", dirname); - dirp->d_handle = _findfirst(data.name, &data); - - if( dirp->d_handle==BAD_INTPTR_T ){ - closedir(dirp); - return NULL; + z++; + if( realnum ) *realnum = 0; + while( IsDigit(*z) ){ z++; } + if( *z=='.' ){ + z++; + if( !IsDigit(*z) ) return 0; + while( IsDigit(*z) ){ z++; } + if( realnum ) *realnum = 1; } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ){ -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ){ - closedir(dirp); - return NULL; - } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; + if( *z=='e' || *z=='E' ){ + z++; + if( *z=='+' || *z=='-' ) z++; + if( !IsDigit(*z) ) return 0; + while( IsDigit(*z) ){ z++; } + if( realnum ) *realnum = 1; } - - dirp->d_first.d_attributes = data.attrib; - strncpy(dirp->d_first.d_name, data.name, NAME_MAX); - dirp->d_first.d_name[NAME_MAX] = '\0'; - - return dirp; + return *z==0; } /* -** Implementation of the POSIX readdir() function using the MSVCRT. +** Compute a string length that is limited to what can be stored in +** lower 30 bits of a 32-bit signed integer. */ -LPDIRENT readdir( - LPDIR dirp -){ - struct _finddata_t data; - - if( dirp==NULL ) return NULL; - - if( dirp->d_first.d_ino==0 ){ - dirp->d_first.d_ino++; - dirp->d_next.d_ino++; +static int strlen30(const char *z){ + const char *z2 = z; + while( *z2 ){ z2++; } + return 0x3fffffff & (int)(z2 - z); +} - return &dirp->d_first; +/* +** Return the length of a string in characters. Multibyte UTF8 characters +** count as a single character. +*/ +static int strlenChar(const char *z){ + int n = 0; + while( *z ){ + if( (0xc0&*(z++))!=0x80 ) n++; } - -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; - - dirp->d_next.d_ino++; - dirp->d_next.d_attributes = data.attrib; - strncpy(dirp->d_next.d_name, data.name, NAME_MAX); - dirp->d_next.d_name[NAME_MAX] = '\0'; - - return &dirp->d_next; + return n; } /* -** Implementation of the POSIX readdir_r() function using the MSVCRT. +** Return open FILE * if zFile exists, can be opened for read +** and is an ordinary file or a character stream source. +** Otherwise return 0. */ -INT readdir_r( - LPDIR dirp, - LPDIRENT entry, - LPDIRENT *result -){ - struct _finddata_t data; - - if( dirp==NULL ) return EBADF; - - if( dirp->d_first.d_ino==0 ){ - dirp->d_first.d_ino++; - dirp->d_next.d_ino++; - - entry->d_ino = dirp->d_first.d_ino; - entry->d_attributes = dirp->d_first.d_attributes; - strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); - entry->d_name[NAME_MAX] = '\0'; - - *result = entry; - return 0; +static FILE * openChrSource(const char *zFile){ +#if defined(_WIN32) || defined(WIN32) + struct _stat x = {0}; +# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) + /* On Windows, open first, then check the stream nature. This order + ** is necessary because _stat() and sibs, when checking a named pipe, + ** effectively break the pipe as its supplier sees it. */ + FILE *rv = fopen(zFile, "rb"); + if( rv==0 ) return 0; + if( _fstat(_fileno(rv), &x) != 0 + || !STAT_CHR_SRC(x.st_mode)){ + fclose(rv); + rv = 0; } - -next: - - memset(&data, 0, sizeof(struct _finddata_t)); - if( _findnext(dirp->d_handle, &data)==-1 ){ - *result = NULL; - return ENOENT; + return rv; +#else + struct stat x = {0}; + int rc = stat(zFile, &x); +# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) + if( rc!=0 ) return 0; + if( STAT_CHR_SRC(x.st_mode) ){ + return fopen(zFile, "rb"); + }else{ + return 0; } - - /* TODO: Remove this block to allow hidden and/or system files. */ - if( is_filtered(data) ) goto next; - - entry->d_ino = (ino_t)-1; /* not available */ - entry->d_attributes = data.attrib; - strncpy(entry->d_name, data.name, NAME_MAX); - entry->d_name[NAME_MAX] = '\0'; - - *result = entry; - return 0; +#endif +#undef STAT_CHR_SRC } /* -** Implementation of the POSIX closedir() function using the MSVCRT. +** This routine reads a line of text from FILE in, stores +** the text in memory obtained from malloc() and returns a pointer +** to the text. NULL is returned at end of file, or if malloc() +** fails. +** +** If zLine is not NULL then it is a malloced buffer returned from +** a previous call to this routine that may be reused. */ -INT closedir( - LPDIR dirp -){ - INT result = 0; - - if( dirp==NULL ) return EINVAL; +static char *local_getline(char *zLine, FILE *in){ + int nLine = zLine==0 ? 0 : 100; + int n = 0; - if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ - result = _findclose(dirp->d_handle); + while( 1 ){ + if( n+100>nLine ){ + nLine = nLine*2 + 100; + zLine = realloc(zLine, nLine); + shell_check_oom(zLine); + } + if( fgets(&zLine[n], nLine - n, in)==0 ){ + if( n==0 ){ + free(zLine); + return 0; + } + zLine[n] = 0; + break; + } + while( zLine[n] ) n++; + if( n>0 && zLine[n-1]=='\n' ){ + n--; + if( n>0 && zLine[n-1]=='\r' ) n--; + zLine[n] = 0; + break; + } } - - sqlite3_free(dirp); - return result; + return zLine; } -#endif /* defined(WIN32) && defined(_MSC_VER) */ - -/************************* End test_windirent.c ********************/ -#define dirent DIRENT -#endif -/************************* Begin ../ext/misc/memtrace.c ******************/ /* -** 2019-01-21 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* +** Retrieve a single line of input text. ** -** This file implements an extension that uses the SQLITE_CONFIG_MALLOC -** mechanism to add a tracing layer on top of SQLite. If this extension -** is registered prior to sqlite3_initialize(), it will cause all memory -** allocation activities to be logged on standard output, or to some other -** FILE specified by the initializer. +** If in==0 then read from standard input and prompt before each line. +** If isContinuation is true, then a continuation prompt is appropriate. +** If isContinuation is zero, then the main prompt should be used. ** -** This file needs to be compiled into the application that uses it. +** If zPrior is not NULL then it is a buffer from a prior call to this +** routine that can be reused. ** -** This extension is used to implement the --memtrace option of the -** command-line shell. +** The result is stored in space obtained from malloc() and must either +** be freed by the caller or else passed back into this routine via the +** zPrior argument for reuse. */ -#include -#include -#include - -/* The original memory allocation routines */ -static sqlite3_mem_methods memtraceBase; -static FILE *memtraceOut; - -/* Methods that trace memory allocations */ -static void *memtraceMalloc(int n){ - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n", - memtraceBase.xRoundup(n)); - } - return memtraceBase.xMalloc(n); -} -static void memtraceFree(void *p){ - if( p==0 ) return; - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p)); - } - memtraceBase.xFree(p); -} -static void *memtraceRealloc(void *p, int n){ - if( p==0 ) return memtraceMalloc(n); - if( n==0 ){ - memtraceFree(p); - return 0; - } - if( memtraceOut ){ - fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n", - memtraceBase.xSize(p), memtraceBase.xRoundup(n)); +#ifndef SQLITE_SHELL_FIDDLE +static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ + char *zPrompt; + char *zResult; + if( in!=0 ){ + zResult = local_getline(zPrior, in); + }else{ + zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt; +#if SHELL_USE_LOCAL_GETLINE + sputz(stdout, zPrompt); + fflush(stdout); + do{ + zResult = local_getline(zPrior, stdin); + zPrior = 0; + /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ + if( zResult==0 ) sqlite3_sleep(50); + }while( zResult==0 && seenInterrupt>0 ); +#else + free(zPrior); + zResult = shell_readline(zPrompt); + while( zResult==0 ){ + /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ + sqlite3_sleep(50); + if( seenInterrupt==0 ) break; + zResult = shell_readline(""); + } + if( zResult && *zResult ) shell_add_history(zResult); +#endif } - return memtraceBase.xRealloc(p, n); -} -static int memtraceSize(void *p){ - return memtraceBase.xSize(p); -} -static int memtraceRoundup(int n){ - return memtraceBase.xRoundup(n); -} -static int memtraceInit(void *p){ - return memtraceBase.xInit(p); -} -static void memtraceShutdown(void *p){ - memtraceBase.xShutdown(p); + return zResult; } +#endif /* !SQLITE_SHELL_FIDDLE */ -/* The substitute memory allocator */ -static sqlite3_mem_methods ersaztMethods = { - memtraceMalloc, - memtraceFree, - memtraceRealloc, - memtraceSize, - memtraceRoundup, - memtraceInit, - memtraceShutdown, - 0 -}; - -/* Begin tracing memory allocations to out. */ -int sqlite3MemTraceActivate(FILE *out){ - int rc = SQLITE_OK; - if( memtraceBase.xMalloc==0 ){ - rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase); - if( rc==SQLITE_OK ){ - rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods); - } - } - memtraceOut = out; - return rc; +/* +** Return the value of a hexadecimal digit. Return -1 if the input +** is not a hex digit. +*/ +static int hexDigitValue(char c){ + if( c>='0' && c<='9' ) return c - '0'; + if( c>='a' && c<='f' ) return c - 'a' + 10; + if( c>='A' && c<='F' ) return c - 'A' + 10; + return -1; } -/* Deactivate memory tracing */ -int sqlite3MemTraceDeactivate(void){ - int rc = SQLITE_OK; - if( memtraceBase.xMalloc!=0 ){ - rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase); - if( rc==SQLITE_OK ){ - memset(&memtraceBase, 0, sizeof(memtraceBase)); +/* +** Interpret zArg as an integer value, possibly with suffixes. +*/ +static sqlite3_int64 integerValue(const char *zArg){ + sqlite3_int64 v = 0; + static const struct { char *zSuffix; int iMult; } aMult[] = { + { "KiB", 1024 }, + { "MiB", 1024*1024 }, + { "GiB", 1024*1024*1024 }, + { "KB", 1000 }, + { "MB", 1000000 }, + { "GB", 1000000000 }, + { "K", 1000 }, + { "M", 1000000 }, + { "G", 1000000000 }, + }; + int i; + int isNeg = 0; + if( zArg[0]=='-' ){ + isNeg = 1; + zArg++; + }else if( zArg[0]=='+' ){ + zArg++; + } + if( zArg[0]=='0' && zArg[1]=='x' ){ + int x; + zArg += 2; + while( (x = hexDigitValue(zArg[0]))>=0 ){ + v = (v<<4) + x; + zArg++; + } + }else{ + while( IsDigit(zArg[0]) ){ + v = v*10 + zArg[0] - '0'; + zArg++; } } - memtraceOut = 0; - return rc; + for(i=0; i -#include -#include +typedef struct ShellText ShellText; +struct ShellText { + char *z; + int n; + int nAlloc; +}; -#ifndef SQLITE_AMALGAMATION -/* typedef sqlite3_uint64 u64; */ -#endif /* SQLITE_AMALGAMATION */ +/* +** Initialize and destroy a ShellText object +*/ +static void initText(ShellText *p){ + memset(p, 0, sizeof(*p)); +} +static void freeText(ShellText *p){ + free(p->z); + initText(p); +} -/****************************************************************************** -** The Hash Engine +/* zIn is either a pointer to a NULL-terminated string in memory obtained +** from malloc(), or a NULL pointer. The string pointed to by zAppend is +** added to zIn, and the result returned in memory obtained from malloc(). +** zIn, if it was not NULL, is freed. +** +** If the third argument, quote, is not '\0', then it is used as a +** quote character for zAppend. */ +static void appendText(ShellText *p, const char *zAppend, char quote){ + i64 len; + i64 i; + i64 nAppend = strlen30(zAppend); + + len = nAppend+p->n+1; + if( quote ){ + len += 2; + for(i=0; i z==0 || p->n+len>=p->nAlloc ){ + p->nAlloc = p->nAlloc*2 + len + 20; + p->z = realloc(p->z, p->nAlloc); + shell_check_oom(p->z); + } + + if( quote ){ + char *zCsr = p->z+p->n; + *zCsr++ = quote; + for(i=0; i n = (int)(zCsr - p->z); + *zCsr = '\0'; + }else{ + memcpy(p->z+p->n, zAppend, nAppend); + p->n += nAppend; + p->z[p->n] = '\0'; + } +} + /* -** Macros to determine whether the machine is big or little endian, -** and whether or not that determination is run-time or compile-time. +** Attempt to determine if identifier zName needs to be quoted, either +** because it contains non-alphanumeric characters, or because it is an +** SQLite keyword. Be conservative in this estimate: When in doubt assume +** that quoting is required. ** -** For best performance, an attempt is made to guess at the byte-order -** using C-preprocessor macros. If that is unsuccessful, or if -** -DSHA3_BYTEORDER=0 is set, then byte-order is determined -** at run-time. +** Return '"' if quoting is required. Return 0 if no quoting is required. */ -#ifndef SHA3_BYTEORDER -# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ - defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ - defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ - defined(__arm__) -# define SHA3_BYTEORDER 1234 -# elif defined(sparc) || defined(__ppc__) -# define SHA3_BYTEORDER 4321 -# else -# define SHA3_BYTEORDER 0 -# endif -#endif +static char quoteChar(const char *zName){ + int i; + if( zName==0 ) return '"'; + if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; + for(i=0; zName[i]; i++){ + if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; + } + return sqlite3_keyword_check(zName, i) ? '"' : 0; +} + +/* +** Construct a fake object name and column list to describe the structure +** of the view, virtual table, or table valued function zSchema.zName. +*/ +static char *shellFakeSchema( + sqlite3 *db, /* The database connection containing the vtab */ + const char *zSchema, /* Schema of the database holding the vtab */ + const char *zName /* The name of the virtual table */ +){ + sqlite3_stmt *pStmt = 0; + char *zSql; + ShellText s; + char cQuote; + char *zDiv = "("; + int nRow = 0; + zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;", + zSchema ? zSchema : "main", zName); + shell_check_oom(zSql); + sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + initText(&s); + if( zSchema ){ + cQuote = quoteChar(zSchema); + if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0; + appendText(&s, zSchema, cQuote); + appendText(&s, ".", 0); + } + cQuote = quoteChar(zName); + appendText(&s, zName, cQuote); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zCol = (const char*)sqlite3_column_text(pStmt, 1); + nRow++; + appendText(&s, zDiv, 0); + zDiv = ","; + if( zCol==0 ) zCol = ""; + cQuote = quoteChar(zCol); + appendText(&s, zCol, cQuote); + } + appendText(&s, ")", 0); + sqlite3_finalize(pStmt); + if( nRow==0 ){ + freeText(&s); + s.z = 0; + } + return s.z; +} /* -** State structure for a SHA3 hash in progress +** SQL function: strtod(X) +** +** Use the C-library strtod() function to convert string X into a double. +** Used for comparing the accuracy of SQLite's internal text-to-float conversion +** routines against the C-library. */ -typedef struct SHA3Context SHA3Context; -struct SHA3Context { - union { - u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ - unsigned char x[1600]; /* ... or 1600 bytes */ - } u; - unsigned nRate; /* Bytes of input accepted per Keccak iteration */ - unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ - unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ -}; +static void shellStrtod( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + char *z = (char*)sqlite3_value_text(apVal[0]); + UNUSED_PARAMETER(nVal); + if( z==0 ) return; + sqlite3_result_double(pCtx, strtod(z,0)); +} /* -** A single step of the Keccak mixing function for a 1600-bit state +** SQL function: dtostr(X) +** +** Use the C-library printf() function to convert real value X into a string. +** Used for comparing the accuracy of SQLite's internal float-to-text conversion +** routines against the C-library. */ -static void KeccakF1600Step(SHA3Context *p){ - int i; - u64 b0, b1, b2, b3, b4; - u64 c0, c1, c2, c3, c4; - u64 d0, d1, d2, d3, d4; - static const u64 RC[] = { - 0x0000000000000001ULL, 0x0000000000008082ULL, - 0x800000000000808aULL, 0x8000000080008000ULL, - 0x000000000000808bULL, 0x0000000080000001ULL, - 0x8000000080008081ULL, 0x8000000000008009ULL, +static void shellDtostr( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + double r = sqlite3_value_double(apVal[0]); + int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26; + char z[400]; + if( n<1 ) n = 1; + if( n>350 ) n = 350; + sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r); + sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); +} + + +/* +** SQL function: shell_module_schema(X) +** +** Return a fake schema for the table-valued function or eponymous virtual +** table X. +*/ +static void shellModuleSchema( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + const char *zName; + char *zFake; + UNUSED_PARAMETER(nVal); + zName = (const char*)sqlite3_value_text(apVal[0]); + zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0; + if( zFake ){ + sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), + -1, sqlite3_free); + free(zFake); + } +} + +/* +** SQL function: shell_add_schema(S,X) +** +** Add the schema name X to the CREATE statement in S and return the result. +** Examples: +** +** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); +** +** Also works on +** +** CREATE INDEX +** CREATE UNIQUE INDEX +** CREATE VIEW +** CREATE TRIGGER +** CREATE VIRTUAL TABLE +** +** This UDF is used by the .schema command to insert the schema name of +** attached databases into the middle of the sqlite_schema.sql field. +*/ +static void shellAddSchemaName( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + static const char *aPrefix[] = { + "TABLE", + "INDEX", + "UNIQUE INDEX", + "VIEW", + "TRIGGER", + "VIRTUAL TABLE" + }; + int i = 0; + const char *zIn = (const char*)sqlite3_value_text(apVal[0]); + const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); + const char *zName = (const char*)sqlite3_value_text(apVal[2]); + sqlite3 *db = sqlite3_context_db_handle(pCtx); + UNUSED_PARAMETER(nVal); + if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){ + for(i=0; i +#include +#include +#include +#include +#include +#include + +/* +** We may need several defines that should have been in "sys/stat.h". +*/ + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISLNK +#define S_ISLNK(mode) (0) +#endif + +/* +** We may need to provide the "mode_t" type. +*/ + +#ifndef MODE_T_DEFINED + #define MODE_T_DEFINED + typedef unsigned short mode_t; +#endif + +/* +** We may need to provide the "ino_t" type. +*/ + +#ifndef INO_T_DEFINED + #define INO_T_DEFINED + typedef unsigned short ino_t; +#endif + +/* +** We need to define "NAME_MAX" if it was not present in "limits.h". +*/ + +#ifndef NAME_MAX +# ifdef FILENAME_MAX +# define NAME_MAX (FILENAME_MAX) +# else +# define NAME_MAX (260) +# endif +#endif + +/* +** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". +*/ + +#ifndef NULL_INTPTR_T +# define NULL_INTPTR_T ((intptr_t)(0)) +#endif + +#ifndef BAD_INTPTR_T +# define BAD_INTPTR_T ((intptr_t)(-1)) +#endif + +/* +** We need to provide the necessary structures and related types. +*/ + +#ifndef DIRENT_DEFINED +#define DIRENT_DEFINED +typedef struct DIRENT DIRENT; +typedef DIRENT *LPDIRENT; +struct DIRENT { + ino_t d_ino; /* Sequence number, do not use. */ + unsigned d_attributes; /* Win32 file attributes. */ + char d_name[NAME_MAX + 1]; /* Name within the directory. */ +}; +#endif + +#ifndef DIR_DEFINED +#define DIR_DEFINED +typedef struct DIR DIR; +typedef DIR *LPDIR; +struct DIR { + intptr_t d_handle; /* Value returned by "_findfirst". */ + DIRENT d_first; /* DIRENT constructed based on "_findfirst". */ + DIRENT d_next; /* DIRENT constructed based on "_findnext". */ +}; +#endif + +/* +** Provide a macro, for use by the implementation, to determine if a +** particular directory entry should be skipped over when searching for +** the next directory entry that should be returned by the readdir() or +** readdir_r() functions. +*/ + +#ifndef is_filtered +# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) +#endif + +/* +** Provide the function prototype for the POSIX compatible getenv() +** function. This function is not thread-safe. +*/ + +extern const char *windirent_getenv(const char *name); + +/* +** Finally, we can provide the function prototypes for the opendir(), +** readdir(), readdir_r(), and closedir() POSIX functions. +*/ + +extern LPDIR opendir(const char *dirname); +extern LPDIRENT readdir(LPDIR dirp); +extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); +extern INT closedir(LPDIR dirp); + +#endif /* defined(WIN32) && defined(_MSC_VER) */ + +/************************* End test_windirent.h ********************/ +/************************* Begin test_windirent.c ******************/ +/* +** 2015 November 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains code to implement most of the opendir() family of +** POSIX functions on Win32 using the MSVCRT. +*/ + +#if defined(_WIN32) && defined(_MSC_VER) +/* #include "test_windirent.h" */ + +/* +** Implementation of the POSIX getenv() function using the Win32 API. +** This function is not thread-safe. +*/ +const char *windirent_getenv( + const char *name +){ + static char value[32768]; /* Maximum length, per MSDN */ + DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */ + DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */ + + memset(value, 0, sizeof(value)); + dwRet = GetEnvironmentVariableA(name, value, dwSize); + if( dwRet==0 || dwRet>dwSize ){ + /* + ** The function call to GetEnvironmentVariableA() failed -OR- + ** the buffer is not large enough. Either way, return NULL. + */ + return 0; + }else{ + /* + ** The function call to GetEnvironmentVariableA() succeeded + ** -AND- the buffer contains the entire value. + */ + return value; + } +} + +/* +** Implementation of the POSIX opendir() function using the MSVCRT. +*/ +LPDIR opendir( + const char *dirname +){ + struct _finddata_t data; + LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); + SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); + + if( dirp==NULL ) return NULL; + memset(dirp, 0, sizeof(DIR)); + + /* TODO: Remove this if Unix-style root paths are not used. */ + if( sqlite3_stricmp(dirname, "/")==0 ){ + dirname = windirent_getenv("SystemDrive"); + } + + memset(&data, 0, sizeof(struct _finddata_t)); + _snprintf(data.name, namesize, "%s\\*", dirname); + dirp->d_handle = _findfirst(data.name, &data); + + if( dirp->d_handle==BAD_INTPTR_T ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ){ +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ){ + closedir(dirp); + return NULL; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + } + + dirp->d_first.d_attributes = data.attrib; + strncpy(dirp->d_first.d_name, data.name, NAME_MAX); + dirp->d_first.d_name[NAME_MAX] = '\0'; + + return dirp; +} + +/* +** Implementation of the POSIX readdir() function using the MSVCRT. +*/ +LPDIRENT readdir( + LPDIR dirp +){ + struct _finddata_t data; + + if( dirp==NULL ) return NULL; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + return &dirp->d_first; + } + +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ) return NULL; + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + + dirp->d_next.d_ino++; + dirp->d_next.d_attributes = data.attrib; + strncpy(dirp->d_next.d_name, data.name, NAME_MAX); + dirp->d_next.d_name[NAME_MAX] = '\0'; + + return &dirp->d_next; +} + +/* +** Implementation of the POSIX readdir_r() function using the MSVCRT. +*/ +INT readdir_r( + LPDIR dirp, + LPDIRENT entry, + LPDIRENT *result +){ + struct _finddata_t data; + + if( dirp==NULL ) return EBADF; + + if( dirp->d_first.d_ino==0 ){ + dirp->d_first.d_ino++; + dirp->d_next.d_ino++; + + entry->d_ino = dirp->d_first.d_ino; + entry->d_attributes = dirp->d_first.d_attributes; + strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; + } + +next: + + memset(&data, 0, sizeof(struct _finddata_t)); + if( _findnext(dirp->d_handle, &data)==-1 ){ + *result = NULL; + return ENOENT; + } + + /* TODO: Remove this block to allow hidden and/or system files. */ + if( is_filtered(data) ) goto next; + + entry->d_ino = (ino_t)-1; /* not available */ + entry->d_attributes = data.attrib; + strncpy(entry->d_name, data.name, NAME_MAX); + entry->d_name[NAME_MAX] = '\0'; + + *result = entry; + return 0; +} + +/* +** Implementation of the POSIX closedir() function using the MSVCRT. +*/ +INT closedir( + LPDIR dirp +){ + INT result = 0; + + if( dirp==NULL ) return EINVAL; + + if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){ + result = _findclose(dirp->d_handle); + } + + sqlite3_free(dirp); + return result; +} + +#endif /* defined(WIN32) && defined(_MSC_VER) */ + +/************************* End test_windirent.c ********************/ +#define dirent DIRENT +#endif +/************************* Begin ../ext/misc/memtrace.c ******************/ +/* +** 2019-01-21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements an extension that uses the SQLITE_CONFIG_MALLOC +** mechanism to add a tracing layer on top of SQLite. If this extension +** is registered prior to sqlite3_initialize(), it will cause all memory +** allocation activities to be logged on standard output, or to some other +** FILE specified by the initializer. +** +** This file needs to be compiled into the application that uses it. +** +** This extension is used to implement the --memtrace option of the +** command-line shell. +*/ +#include +#include +#include + +/* The original memory allocation routines */ +static sqlite3_mem_methods memtraceBase; +static FILE *memtraceOut; + +/* Methods that trace memory allocations */ +static void *memtraceMalloc(int n){ + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n", + memtraceBase.xRoundup(n)); + } + return memtraceBase.xMalloc(n); +} +static void memtraceFree(void *p){ + if( p==0 ) return; + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p)); + } + memtraceBase.xFree(p); +} +static void *memtraceRealloc(void *p, int n){ + if( p==0 ) return memtraceMalloc(n); + if( n==0 ){ + memtraceFree(p); + return 0; + } + if( memtraceOut ){ + fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n", + memtraceBase.xSize(p), memtraceBase.xRoundup(n)); + } + return memtraceBase.xRealloc(p, n); +} +static int memtraceSize(void *p){ + return memtraceBase.xSize(p); +} +static int memtraceRoundup(int n){ + return memtraceBase.xRoundup(n); +} +static int memtraceInit(void *p){ + return memtraceBase.xInit(p); +} +static void memtraceShutdown(void *p){ + memtraceBase.xShutdown(p); +} + +/* The substitute memory allocator */ +static sqlite3_mem_methods ersaztMethods = { + memtraceMalloc, + memtraceFree, + memtraceRealloc, + memtraceSize, + memtraceRoundup, + memtraceInit, + memtraceShutdown, + 0 +}; + +/* Begin tracing memory allocations to out. */ +int sqlite3MemTraceActivate(FILE *out){ + int rc = SQLITE_OK; + if( memtraceBase.xMalloc==0 ){ + rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase); + if( rc==SQLITE_OK ){ + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods); + } + } + memtraceOut = out; + return rc; +} + +/* Deactivate memory tracing */ +int sqlite3MemTraceDeactivate(void){ + int rc = SQLITE_OK; + if( memtraceBase.xMalloc!=0 ){ + rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase); + if( rc==SQLITE_OK ){ + memset(&memtraceBase, 0, sizeof(memtraceBase)); + } + } + memtraceOut = 0; + return rc; +} + +/************************* End ../ext/misc/memtrace.c ********************/ +/************************* Begin ../ext/misc/pcachetrace.c ******************/ +/* +** 2023-06-21 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements an extension that uses the SQLITE_CONFIG_PCACHE2 +** mechanism to add a tracing layer on top of pluggable page cache of +** SQLite. If this extension is registered prior to sqlite3_initialize(), +** it will cause all page cache activities to be logged on standard output, +** or to some other FILE specified by the initializer. +** +** This file needs to be compiled into the application that uses it. +** +** This extension is used to implement the --pcachetrace option of the +** command-line shell. +*/ +#include +#include +#include + +/* The original page cache routines */ +static sqlite3_pcache_methods2 pcacheBase; +static FILE *pcachetraceOut; + +/* Methods that trace pcache activity */ +static int pcachetraceInit(void *pArg){ + int nRes; + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p)\n", pArg); + } + nRes = pcacheBase.xInit(pArg); + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xInit(%p) -> %d\n", pArg, nRes); + } + return nRes; +} +static void pcachetraceShutdown(void *pArg){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xShutdown(%p)\n", pArg); + } + pcacheBase.xShutdown(pArg); +} +static sqlite3_pcache *pcachetraceCreate(int szPage, int szExtra, int bPurge){ + sqlite3_pcache *pRes; + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d)\n", + szPage, szExtra, bPurge); + } + pRes = pcacheBase.xCreate(szPage, szExtra, bPurge); + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xCreate(%d,%d,%d) -> %p\n", + szPage, szExtra, bPurge, pRes); + } + return pRes; +} +static void pcachetraceCachesize(sqlite3_pcache *p, int nCachesize){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xCachesize(%p, %d)\n", p, nCachesize); + } + pcacheBase.xCachesize(p, nCachesize); +} +static int pcachetracePagecount(sqlite3_pcache *p){ + int nRes; + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p)\n", p); + } + nRes = pcacheBase.xPagecount(p); + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xPagecount(%p) -> %d\n", p, nRes); + } + return nRes; +} +static sqlite3_pcache_page *pcachetraceFetch( + sqlite3_pcache *p, + unsigned key, + int crFg +){ + sqlite3_pcache_page *pRes; + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d)\n", p, key, crFg); + } + pRes = pcacheBase.xFetch(p, key, crFg); + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xFetch(%p,%u,%d) -> %p\n", + p, key, crFg, pRes); + } + return pRes; +} +static void pcachetraceUnpin( + sqlite3_pcache *p, + sqlite3_pcache_page *pPg, + int bDiscard +){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xUnpin(%p, %p, %d)\n", + p, pPg, bDiscard); + } + pcacheBase.xUnpin(p, pPg, bDiscard); +} +static void pcachetraceRekey( + sqlite3_pcache *p, + sqlite3_pcache_page *pPg, + unsigned oldKey, + unsigned newKey +){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xRekey(%p, %p, %u, %u)\n", + p, pPg, oldKey, newKey); + } + pcacheBase.xRekey(p, pPg, oldKey, newKey); +} +static void pcachetraceTruncate(sqlite3_pcache *p, unsigned n){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xTruncate(%p, %u)\n", p, n); + } + pcacheBase.xTruncate(p, n); +} +static void pcachetraceDestroy(sqlite3_pcache *p){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xDestroy(%p)\n", p); + } + pcacheBase.xDestroy(p); +} +static void pcachetraceShrink(sqlite3_pcache *p){ + if( pcachetraceOut ){ + fprintf(pcachetraceOut, "PCACHETRACE: xShrink(%p)\n", p); + } + pcacheBase.xShrink(p); +} + +/* The substitute pcache methods */ +static sqlite3_pcache_methods2 ersaztPcacheMethods = { + 0, + 0, + pcachetraceInit, + pcachetraceShutdown, + pcachetraceCreate, + pcachetraceCachesize, + pcachetracePagecount, + pcachetraceFetch, + pcachetraceUnpin, + pcachetraceRekey, + pcachetraceTruncate, + pcachetraceDestroy, + pcachetraceShrink +}; + +/* Begin tracing memory allocations to out. */ +int sqlite3PcacheTraceActivate(FILE *out){ + int rc = SQLITE_OK; + if( pcacheBase.xFetch==0 ){ + rc = sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &pcacheBase); + if( rc==SQLITE_OK ){ + rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &ersaztPcacheMethods); + } + } + pcachetraceOut = out; + return rc; +} + +/* Deactivate memory tracing */ +int sqlite3PcacheTraceDeactivate(void){ + int rc = SQLITE_OK; + if( pcacheBase.xFetch!=0 ){ + rc = sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcacheBase); + if( rc==SQLITE_OK ){ + memset(&pcacheBase, 0, sizeof(pcacheBase)); + } + } + pcachetraceOut = 0; + return rc; +} + +/************************* End ../ext/misc/pcachetrace.c ********************/ +/************************* Begin ../ext/misc/shathree.c ******************/ +/* +** 2017-03-08 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This SQLite extension implements functions that compute SHA3 hashes +** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. +** Two SQL functions are implemented: +** +** sha3(X,SIZE) +** sha3_query(Y,SIZE) +** +** The sha3(X) function computes the SHA3 hash of the input X, or NULL if +** X is NULL. +** +** The sha3_query(Y) function evaluates all queries in the SQL statements of Y +** and returns a hash of their results. +** +** The SIZE argument is optional. If omitted, the SHA3-256 hash algorithm +** is used. If SIZE is included it must be one of the integers 224, 256, +** 384, or 512, to determine SHA3 hash variant that is computed. +*/ +/* #include "sqlite3ext.h" */ +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +#ifndef SQLITE_AMALGAMATION +/* typedef sqlite3_uint64 u64; */ +#endif /* SQLITE_AMALGAMATION */ + +/****************************************************************************** +** The Hash Engine +*/ +/* +** Macros to determine whether the machine is big or little endian, +** and whether or not that determination is run-time or compile-time. +** +** For best performance, an attempt is made to guess at the byte-order +** using C-preprocessor macros. If that is unsuccessful, or if +** -DSHA3_BYTEORDER=0 is set, then byte-order is determined +** at run-time. +*/ +#ifndef SHA3_BYTEORDER +# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ + defined(__arm__) +# define SHA3_BYTEORDER 1234 +# elif defined(sparc) || defined(__ppc__) +# define SHA3_BYTEORDER 4321 +# else +# define SHA3_BYTEORDER 0 +# endif +#endif + + +/* +** State structure for a SHA3 hash in progress +*/ +typedef struct SHA3Context SHA3Context; +struct SHA3Context { + union { + u64 s[25]; /* Keccak state. 5x5 lines of 64 bits each */ + unsigned char x[1600]; /* ... or 1600 bytes */ + } u; + unsigned nRate; /* Bytes of input accepted per Keccak iteration */ + unsigned nLoaded; /* Input bytes loaded into u.x[] so far this cycle */ + unsigned ixMask; /* Insert next input into u.x[nLoaded^ixMask]. */ +}; + +/* +** A single step of the Keccak mixing function for a 1600-bit state +*/ +static void KeccakF1600Step(SHA3Context *p){ + int i; + u64 b0, b1, b2, b3, b4; + u64 c0, c1, c2, c3, c4; + u64 d0, d1, d2, d3, d4; + static const u64 RC[] = { + 0x0000000000000001ULL, 0x0000000000008082ULL, + 0x800000000000808aULL, 0x8000000080008000ULL, + 0x000000000000808bULL, 0x0000000080000001ULL, + 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, @@ -2058,7 +3347,7 @@ static void sha3Func( /* Compute a string using sqlite3_vsnprintf() with a maximum length ** of 50 bytes and add it to the hash. */ -static void hash_step_vformat( +static void sha3_step_vformat( SHA3Context *p, /* Add content to this context */ const char *zFormat, ... @@ -2154,7 +3443,7 @@ static void sha3QueryFunc( z = sqlite3_sql(pStmt); if( z ){ n = (int)strlen(z); - hash_step_vformat(&cx,"S%d:",n); + sha3_step_vformat(&cx,"S%d:",n); SHA3Update(&cx,(unsigned char*)z,n); } @@ -2198,14 +3487,14 @@ static void sha3QueryFunc( case SQLITE_TEXT: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_text(pStmt, i); - hash_step_vformat(&cx,"T%d:",n2); + sha3_step_vformat(&cx,"T%d:",n2); SHA3Update(&cx, z2, n2); break; } case SQLITE_BLOB: { int n2 = sqlite3_column_bytes(pStmt, i); const unsigned char *z2 = sqlite3_column_blob(pStmt, i); - hash_step_vformat(&cx,"B%d:",n2); + sha3_step_vformat(&cx,"B%d:",n2); SHA3Update(&cx, z2, n2); break; } @@ -2407,41 +3696,24 @@ static void decimal_free(Decimal *p){ } /* -** Allocate a new Decimal object. Initialize it to the number given -** by the input string. +** Allocate a new Decimal object initialized to the text in zIn[]. +** Return NULL if any kind of error occurs. */ -static Decimal *decimal_new( - sqlite3_context *pCtx, - sqlite3_value *pIn, - int nAlt, - const unsigned char *zAlt -){ - Decimal *p; - int n, i; - const unsigned char *zIn; +static Decimal *decimalNewFromText(const char *zIn, int n){ + Decimal *p = 0; + int i; int iExp = 0; + p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) goto new_no_mem; + if( p==0 ) goto new_from_text_failed; p->sign = 0; p->oom = 0; p->isInit = 1; p->isNull = 0; p->nDigit = 0; p->nFrac = 0; - if( zAlt ){ - n = nAlt, - zIn = zAlt; - }else{ - if( sqlite3_value_type(pIn)==SQLITE_NULL ){ - p->a = 0; - p->isNull = 1; - return p; - } - n = sqlite3_value_bytes(pIn); - zIn = sqlite3_value_text(pIn); - } p->a = sqlite3_malloc64( n+1 ); - if( p->a==0 ) goto new_no_mem; + if( p->a==0 ) goto new_from_text_failed; for(i=0; isspace(zIn[i]); i++){} if( zIn[i]=='-' ){ p->sign = 1; @@ -2492,7 +3764,7 @@ static Decimal *decimal_new( } if( iExp>0 ){ p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); - if( p->a==0 ) goto new_no_mem; + if( p->a==0 ) goto new_from_text_failed; memset(p->a+p->nDigit, 0, iExp); p->nDigit += iExp; } @@ -2511,7 +3783,7 @@ static Decimal *decimal_new( } if( iExp>0 ){ p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 ); - if( p->a==0 ) goto new_no_mem; + if( p->a==0 ) goto new_from_text_failed; memmove(p->a+iExp, p->a, p->nDigit); memset(p->a, 0, iExp); p->nDigit += iExp; @@ -2520,7 +3792,76 @@ static Decimal *decimal_new( } return p; -new_no_mem: +new_from_text_failed: + if( p ){ + if( p->a ) sqlite3_free(p->a); + sqlite3_free(p); + } + return 0; +} + +/* Forward reference */ +static Decimal *decimalFromDouble(double); + +/* +** Allocate a new Decimal object from an sqlite3_value. Return a pointer +** to the new object, or NULL if there is an error. If the pCtx argument +** is not NULL, then errors are reported on it as well. +** +** If the pIn argument is SQLITE_TEXT or SQLITE_INTEGER, it is converted +** directly into a Decimal. For SQLITE_FLOAT or for SQLITE_BLOB of length +** 8 bytes, the resulting double value is expanded into its decimal equivalent. +** If pIn is NULL or if it is a BLOB that is not exactly 8 bytes in length, +** then NULL is returned. +*/ +static Decimal *decimal_new( + sqlite3_context *pCtx, /* Report error here, if not null */ + sqlite3_value *pIn, /* Construct the decimal object from this */ + int bTextOnly /* Always interpret pIn as text if true */ +){ + Decimal *p = 0; + int eType = sqlite3_value_type(pIn); + if( bTextOnly && (eType==SQLITE_FLOAT || eType==SQLITE_BLOB) ){ + eType = SQLITE_TEXT; + } + switch( eType ){ + case SQLITE_TEXT: + case SQLITE_INTEGER: { + const char *zIn = (const char*)sqlite3_value_text(pIn); + int n = sqlite3_value_bytes(pIn); + p = decimalNewFromText(zIn, n); + if( p==0 ) goto new_failed; + break; + } + + case SQLITE_FLOAT: { + p = decimalFromDouble(sqlite3_value_double(pIn)); + break; + } + + case SQLITE_BLOB: { + const unsigned char *x; + unsigned int i; + sqlite3_uint64 v = 0; + double r; + + if( sqlite3_value_bytes(pIn)!=sizeof(r) ) break; + x = sqlite3_value_blob(pIn); + for(i=0; i oom ){ + sqlite3_result_error_nomem(pCtx); + return; + } + if( p->isNull ){ + sqlite3_result_null(pCtx); + return; + } + for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){} + for(nZero=0; nZero a[nZero]==0; nZero++){} + nFrac = p->nFrac + (nDigit - p->nDigit); + nDigit -= nZero; + z = sqlite3_malloc( nDigit+20 ); + if( z==0 ){ + sqlite3_result_error_nomem(pCtx); + return; + } + if( nDigit==0 ){ + zero = 0; + a = &zero; + nDigit = 1; + nFrac = 0; + }else{ + a = &p->a[nZero]; + } + if( p->sign && nDigit>0 ){ + z[0] = '-'; + }else{ + z[0] = '+'; + } + z[1] = a[0]+'0'; + z[2] = '.'; + if( nDigit==1 ){ + z[3] = '0'; + i = 4; + }else{ + for(i=1; i isNull ) goto cmp_done; - pB = decimal_new(context, argv[1], 0, 0); + pB = decimal_new(context, argv[1], 1); if( pB==0 || pB->isNull ) goto cmp_done; rc = decimal_cmp(pA, pB); if( rc<0 ) rc = -1; @@ -2687,7 +4073,7 @@ static void decimal_expand(Decimal *p, int nDigit, int nFrac){ } /* -** Add the value pB into pA. +** Add the value pB into pA. A := A + B. ** ** Both pA and pB might become denormalized by this routine. */ @@ -2710,49 +4096,215 @@ static void decimal_add(Decimal *pA, Decimal *pB){ if( nSig nDigit-pB->nFrac ){ nSig = pB->nDigit - pB->nFrac; } - nFrac = pA->nFrac; - if( nFrac nFrac ) nFrac = pB->nFrac; - nDigit = nSig + nFrac + 1; - decimal_expand(pA, nDigit, nFrac); - decimal_expand(pB, nDigit, nFrac); - if( pA->oom || pB->oom ){ - pA->oom = 1; + nFrac = pA->nFrac; + if( nFrac nFrac ) nFrac = pB->nFrac; + nDigit = nSig + nFrac + 1; + decimal_expand(pA, nDigit, nFrac); + decimal_expand(pB, nDigit, nFrac); + if( pA->oom || pB->oom ){ + pA->oom = 1; + }else{ + if( pA->sign==pB->sign ){ + int carry = 0; + for(i=nDigit-1; i>=0; i--){ + int x = pA->a[i] + pB->a[i] + carry; + if( x>=10 ){ + carry = 1; + pA->a[i] = x - 10; + }else{ + carry = 0; + pA->a[i] = x; + } + } + }else{ + signed char *aA, *aB; + int borrow = 0; + rc = memcmp(pA->a, pB->a, nDigit); + if( rc<0 ){ + aA = pB->a; + aB = pA->a; + pA->sign = !pA->sign; + }else{ + aA = pA->a; + aB = pB->a; + } + for(i=nDigit-1; i>=0; i--){ + int x = aA[i] - aB[i] - borrow; + if( x<0 ){ + pA->a[i] = x+10; + borrow = 1; + }else{ + pA->a[i] = x; + borrow = 0; + } + } + } + } +} + +/* +** Multiply A by B. A := A * B +** +** All significant digits after the decimal point are retained. +** Trailing zeros after the decimal point are omitted as long as +** the number of digits after the decimal point is no less than +** either the number of digits in either input. +*/ +static void decimalMul(Decimal *pA, Decimal *pB){ + signed char *acc = 0; + int i, j, k; + int minFrac; + + if( pA==0 || pA->oom || pA->isNull + || pB==0 || pB->oom || pB->isNull + ){ + goto mul_end; + } + acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 ); + if( acc==0 ){ + pA->oom = 1; + goto mul_end; + } + memset(acc, 0, pA->nDigit + pB->nDigit + 2); + minFrac = pA->nFrac; + if( pB->nFrac nFrac; + for(i=pA->nDigit-1; i>=0; i--){ + signed char f = pA->a[i]; + int carry = 0, x; + for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){ + x = acc[k] + f*pB->a[j] + carry; + acc[k] = x%10; + carry = x/10; + } + x = acc[k] + carry; + acc[k] = x%10; + acc[k-1] += x/10; + } + sqlite3_free(pA->a); + pA->a = acc; + acc = 0; + pA->nDigit += pB->nDigit + 2; + pA->nFrac += pB->nFrac; + pA->sign ^= pB->sign; + while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){ + pA->nFrac--; + pA->nDigit--; + } + +mul_end: + sqlite3_free(acc); +} + +/* +** Create a new Decimal object that contains an integer power of 2. +*/ +static Decimal *decimalPow2(int N){ + Decimal *pA = 0; /* The result to be returned */ + Decimal *pX = 0; /* Multiplier */ + if( N<-20000 || N>20000 ) goto pow2_fault; + pA = decimalNewFromText("1.0", 3); + if( pA==0 || pA->oom ) goto pow2_fault; + if( N==0 ) return pA; + if( N>0 ){ + pX = decimalNewFromText("2.0", 3); + }else{ + N = -N; + pX = decimalNewFromText("0.5", 3); + } + if( pX==0 || pX->oom ) goto pow2_fault; + while( 1 /* Exit by break */ ){ + if( N & 1 ){ + decimalMul(pA, pX); + if( pA->oom ) goto pow2_fault; + } + N >>= 1; + if( N==0 ) break; + decimalMul(pX, pX); + } + decimal_free(pX); + return pA; + +pow2_fault: + decimal_free(pA); + decimal_free(pX); + return 0; +} + +/* +** Use an IEEE754 binary64 ("double") to generate a new Decimal object. +*/ +static Decimal *decimalFromDouble(double r){ + sqlite3_int64 m, a; + int e; + int isNeg; + Decimal *pA; + Decimal *pX; + char zNum[100]; + if( r<0.0 ){ + isNeg = 1; + r = -r; + }else{ + isNeg = 0; + } + memcpy(&a,&r,sizeof(a)); + if( a==0 ){ + e = 0; + m = 0; }else{ - if( pA->sign==pB->sign ){ - int carry = 0; - for(i=nDigit-1; i>=0; i--){ - int x = pA->a[i] + pB->a[i] + carry; - if( x>=10 ){ - carry = 1; - pA->a[i] = x - 10; - }else{ - carry = 0; - pA->a[i] = x; - } - } + e = a>>52; + m = a & ((((sqlite3_int64)1)<<52)-1); + if( e==0 ){ + m <<= 1; }else{ - signed char *aA, *aB; - int borrow = 0; - rc = memcmp(pA->a, pB->a, nDigit); - if( rc<0 ){ - aA = pB->a; - aB = pA->a; - pA->sign = !pA->sign; - }else{ - aA = pA->a; - aB = pB->a; - } - for(i=nDigit-1; i>=0; i--){ - int x = aA[i] - aB[i] - borrow; - if( x<0 ){ - pA->a[i] = x+10; - borrow = 1; - }else{ - pA->a[i] = x; - borrow = 0; - } - } + m |= ((sqlite3_int64)1)<<52; + } + while( e<1075 && m>0 && (m&1)==0 ){ + m >>= 1; + e++; + } + if( isNeg ) m = -m; + e = e - 1075; + if( e>971 ){ + return 0; /* A NaN or an Infinity */ + } + } + + /* At this point m is the integer significand and e is the exponent */ + sqlite3_snprintf(sizeof(zNum), zNum, "%lld", m); + pA = decimalNewFromText(zNum, (int)strlen(zNum)); + pX = decimalPow2(e); + decimalMul(pA, pX); + decimal_free(pX); + return pA; +} + +/* +** SQL Function: decimal(X) +** OR: decimal_exp(X) +** +** Convert input X into decimal and then back into text. +** +** If X is originally a float, then a full decimal expansion of that floating +** point value is done. Or if X is an 8-byte blob, it is interpreted +** as a float and similarly expanded. +** +** The decimal_exp(X) function returns the result in exponential notation. +** decimal(X) returns a complete decimal, without the e+NNN at the end. +*/ +static void decimalFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *p = decimal_new(context, argv[0], 0); + UNUSED_PARAMETER(argc); + if( p ){ + if( sqlite3_user_data(context)!=0 ){ + decimal_result_sci(context, p); + }else{ + decimal_result(context, p); } + decimal_free(p); } } @@ -2766,8 +4318,8 @@ static int decimalCollFunc( ){ const unsigned char *zA = (const unsigned char*)pKey1; const unsigned char *zB = (const unsigned char*)pKey2; - Decimal *pA = decimal_new(0, 0, nKey1, zA); - Decimal *pB = decimal_new(0, 0, nKey2, zB); + Decimal *pA = decimalNewFromText((const char*)zA, nKey1); + Decimal *pB = decimalNewFromText((const char*)zB, nKey2); int rc; UNUSED_PARAMETER(notUsed); if( pA==0 || pB==0 ){ @@ -2792,8 +4344,8 @@ static void decimalAddFunc( int argc, sqlite3_value **argv ){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); + Decimal *pA = decimal_new(context, argv[0], 1); + Decimal *pB = decimal_new(context, argv[1], 1); UNUSED_PARAMETER(argc); decimal_add(pA, pB); decimal_result(context, pA); @@ -2805,8 +4357,8 @@ static void decimalSubFunc( int argc, sqlite3_value **argv ){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); + Decimal *pA = decimal_new(context, argv[0], 1); + Decimal *pB = decimal_new(context, argv[1], 1); UNUSED_PARAMETER(argc); if( pB ){ pB->sign = !pB->sign; @@ -2844,7 +4396,7 @@ static void decimalSumStep( p->nFrac = 0; } if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pArg = decimal_new(context, argv[0], 0, 0); + pArg = decimal_new(context, argv[0], 1); decimal_add(p, pArg); decimal_free(pArg); } @@ -2859,7 +4411,7 @@ static void decimalSumInverse( p = sqlite3_aggregate_context(context, sizeof(*p)); if( p==0 ) return; if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; - pArg = decimal_new(context, argv[0], 0, 0); + pArg = decimal_new(context, argv[0], 1); if( pArg ) pArg->sign = !pArg->sign; decimal_add(p, pArg); decimal_free(pArg); @@ -2880,110 +4432,849 @@ static void decimalSumFinalize(sqlite3_context *context){ ** SQL Function: decimal_mul(X, Y) ** ** Return the product of X and Y. +*/ +static void decimalMulFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Decimal *pA = decimal_new(context, argv[0], 1); + Decimal *pB = decimal_new(context, argv[1], 1); + UNUSED_PARAMETER(argc); + if( pA==0 || pA->oom || pA->isNull + || pB==0 || pB->oom || pB->isNull + ){ + goto mul_end; + } + decimalMul(pA, pB); + if( pA->oom ){ + goto mul_end; + } + decimal_result(context, pA); + +mul_end: + decimal_free(pA); + decimal_free(pB); +} + +/* +** SQL Function: decimal_pow2(N) ** -** All significant digits after the decimal point are retained. -** Trailing zeros after the decimal point are omitted as long as -** the number of digits after the decimal point is no less than -** either the number of digits in either input. +** Return the N-th power of 2. N must be an integer. +*/ +static void decimalPow2Func( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + UNUSED_PARAMETER(argc); + if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){ + Decimal *pA = decimalPow2(sqlite3_value_int(argv[0])); + decimal_result_sci(context, pA); + decimal_free(pA); + } +} + +#ifdef _WIN32 + +#endif +int sqlite3_decimal_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + static const struct { + const char *zFuncName; + int nArg; + int iArg; + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); + } aFunc[] = { + { "decimal", 1, 0, decimalFunc }, + { "decimal_exp", 1, 1, decimalFunc }, + { "decimal_cmp", 2, 0, decimalCmpFunc }, + { "decimal_add", 2, 0, decimalAddFunc }, + { "decimal_sub", 2, 0, decimalSubFunc }, + { "decimal_mul", 2, 0, decimalMulFunc }, + { "decimal_pow2", 1, 0, decimalPow2Func }, + }; + unsigned int i; + (void)pzErrMsg; /* Unused parameter */ + + SQLITE_EXTENSION_INIT2(pApi); + + for(i=0; i<(int)(sizeof(aFunc)/sizeof(aFunc[0])) && rc==SQLITE_OK; i++){ + rc = sqlite3_create_function(db, aFunc[i].zFuncName, aFunc[i].nArg, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, + aFunc[i].iArg ? db : 0, aFunc[i].xFunc, 0, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_window_function(db, "decimal_sum", 1, + SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC, 0, + decimalSumStep, decimalSumFinalize, + decimalSumValue, decimalSumInverse, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8, + 0, decimalCollFunc); + } + return rc; +} + +/************************* End ../ext/misc/decimal.c ********************/ +#undef sqlite3_base_init +#define sqlite3_base_init sqlite3_base64_init +/************************* Begin ../ext/misc/base64.c ******************/ +/* +** 2022-11-18 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This is a SQLite extension for converting in either direction +** between a (binary) blob and base64 text. Base64 can transit a +** sane USASCII channel unmolested. It also plays nicely in CSV or +** written as TCL brace-enclosed literals or SQL string literals, +** and can be used unmodified in XML-like documents. +** +** This is an independent implementation of conversions specified in +** RFC 4648, done on the above date by the author (Larry Brasfield) +** who thereby has the right to put this into the public domain. +** +** The conversions meet RFC 4648 requirements, provided that this +** C source specifies that line-feeds are included in the encoded +** data to limit visible line lengths to 72 characters and to +** terminate any encoded blob having non-zero length. +** +** Length limitations are not imposed except that the runtime +** SQLite string or blob length limits are respected. Otherwise, +** any length binary sequence can be represented and recovered. +** Generated base64 sequences, with their line-feeds included, +** can be concatenated; the result converted back to binary will +** be the concatenation of the represented binary sequences. +** +** This SQLite3 extension creates a function, base64(x), which +** either: converts text x containing base64 to a returned blob; +** or converts a blob x to returned text containing base64. An +** error will be thrown for other input argument types. +** +** This code relies on UTF-8 encoding only with respect to the +** meaning of the first 128 (7-bit) codes matching that of USASCII. +** It will fail miserably if somehow made to try to convert EBCDIC. +** Because it is table-driven, it could be enhanced to handle that, +** but the world and SQLite have moved on from that anachronism. +** +** To build the extension: +** Set shell variable SQDIR= +** *Nix: gcc -O2 -shared -I$SQDIR -fPIC -o base64.so base64.c +** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR -o base64.dylib base64.c +** Win32: gcc -O2 -shared -I%SQDIR% -o base64.dll base64.c +** Win32: cl /Os -I%SQDIR% base64.c -link -dll -out:base64.dll +*/ + +#include + +/* #include "sqlite3ext.h" */ + +#ifndef deliberate_fall_through +/* Quiet some compilers about some of our intentional code. */ +# if GCC_VERSION>=7000000 +# define deliberate_fall_through __attribute__((fallthrough)); +# else +# define deliberate_fall_through +# endif +#endif + +SQLITE_EXTENSION_INIT1; + +#define PC 0x80 /* pad character */ +#define WS 0x81 /* whitespace */ +#define ND 0x82 /* Not above or digit-value */ +#define PAD_CHAR '=' + +#ifndef U8_TYPEDEF +/* typedef unsigned char u8; */ +#define U8_TYPEDEF +#endif + +/* Decoding table, ASCII (7-bit) value to base 64 digit value or other */ +static const u8 b64DigitValues[128] = { + /* HT LF VT FF CR */ + ND,ND,ND,ND, ND,ND,ND,ND, ND,WS,WS,WS, WS,WS,ND,ND, + /* US */ + ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,ND, + /*sp + / */ + WS,ND,ND,ND, ND,ND,ND,ND, ND,ND,ND,62, ND,ND,ND,63, + /* 0 1 5 9 = */ + 52,53,54,55, 56,57,58,59, 60,61,ND,ND, ND,PC,ND,ND, + /* A O */ + ND, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + /* P Z */ + 15,16,17,18, 19,20,21,22, 23,24,25,ND, ND,ND,ND,ND, + /* a o */ + ND,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + /* p z */ + 41,42,43,44, 45,46,47,48, 49,50,51,ND, ND,ND,ND,ND +}; + +static const char b64Numerals[64+1] += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define BX_DV_PROTO(c) \ + ((((u8)(c))<0x80)? (u8)(b64DigitValues[(u8)(c)]) : 0x80) +#define IS_BX_DIGIT(bdp) (((u8)(bdp))<0x80) +#define IS_BX_WS(bdp) ((bdp)==WS) +#define IS_BX_PAD(bdp) ((bdp)==PC) +#define BX_NUMERAL(dv) (b64Numerals[(u8)(dv)]) +/* Width of base64 lines. Should be an integer multiple of 4. */ +#define B64_DARK_MAX 72 + +/* Encode a byte buffer into base64 text with linefeeds appended to limit +** encoded group lengths to B64_DARK_MAX or to terminate the last group. +*/ +static char* toBase64( u8 *pIn, int nbIn, char *pOut ){ + int nCol = 0; + while( nbIn >= 3 ){ + /* Do the bit-shuffle, exploiting unsigned input to avoid masking. */ + pOut[0] = BX_NUMERAL(pIn[0]>>2); + pOut[1] = BX_NUMERAL(((pIn[0]<<4)|(pIn[1]>>4))&0x3f); + pOut[2] = BX_NUMERAL(((pIn[1]&0xf)<<2)|(pIn[2]>>6)); + pOut[3] = BX_NUMERAL(pIn[2]&0x3f); + pOut += 4; + nbIn -= 3; + pIn += 3; + if( (nCol += 4)>=B64_DARK_MAX || nbIn<=0 ){ + *pOut++ = '\n'; + nCol = 0; + } + } + if( nbIn > 0 ){ + signed char nco = nbIn+1; + int nbe; + unsigned long qv = *pIn++; + for( nbe=1; nbe<3; ++nbe ){ + qv <<= 8; + if( nbe =0; --nbe ){ + char ce = (nbe >= 6; + pOut[nbe] = ce; + } + pOut += 4; + *pOut++ = '\n'; + } + *pOut = 0; + return pOut; +} + +/* Skip over text which is not base64 numeral(s). */ +static char * skipNonB64( char *s, int nc ){ + char c; + while( nc-- > 0 && (c = *s) && !IS_BX_DIGIT(BX_DV_PROTO(c)) ) ++s; + return s; +} + +/* Decode base64 text into a byte buffer. */ +static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){ + if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; + while( ncIn>0 && *pIn!=PAD_CHAR ){ + static signed char nboi[] = { 0, 0, 1, 2, 3 }; + char *pUse = skipNonB64(pIn, ncIn); + unsigned long qv = 0L; + int nti, nbo, nac; + ncIn -= (pUse - pIn); + pIn = pUse; + nti = (ncIn>4)? 4 : ncIn; + ncIn -= nti; + nbo = nboi[nti]; + if( nbo==0 ) break; + for( nac=0; nac<4; ++nac ){ + char c = (nac >8) & 0xff; + case 1: + pOut[0] = (qv>>16) & 0xff; + } + pOut += nbo; + } + return pOut; +} + +/* This function does the work for the SQLite base64(x) UDF. */ +static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){ + int nb, nc, nv = sqlite3_value_bytes(av[0]); + int nvMax = sqlite3_limit(sqlite3_context_db_handle(context), + SQLITE_LIMIT_LENGTH, -1); + char *cBuf; + u8 *bBuf; + assert(na==1); + switch( sqlite3_value_type(av[0]) ){ + case SQLITE_BLOB: + nb = nv; + nc = 4*(nv+2/3); /* quads needed */ + nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */ + if( nvMax < nc ){ + sqlite3_result_error(context, "blob expanded to base64 too big", -1); + return; + } + bBuf = (u8*)sqlite3_value_blob(av[0]); + if( !bBuf ){ + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ + goto memFail; + } + sqlite3_result_text(context,"",-1,SQLITE_STATIC); + break; + } + cBuf = sqlite3_malloc(nc); + if( !cBuf ) goto memFail; + nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf); + sqlite3_result_text(context, cBuf, nc, sqlite3_free); + break; + case SQLITE_TEXT: + nc = nv; + nb = 3*((nv+3)/4); /* may overestimate due to LF and padding */ + if( nvMax < nb ){ + sqlite3_result_error(context, "blob from base64 may be too big", -1); + return; + }else if( nb<1 ){ + nb = 1; + } + cBuf = (char *)sqlite3_value_text(av[0]); + if( !cBuf ){ + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ + goto memFail; + } + sqlite3_result_zeroblob(context, 0); + break; + } + bBuf = sqlite3_malloc(nb); + if( !bBuf ) goto memFail; + nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf); + sqlite3_result_blob(context, bBuf, nb, sqlite3_free); + break; + default: + sqlite3_result_error(context, "base64 accepts only blob or text", -1); + return; + } + return; + memFail: + sqlite3_result_error(context, "base64 OOM", -1); +} + +/* +** Establish linkage to running SQLite library. +*/ +#ifndef SQLITE_SHELL_EXTFUNCS +#ifdef _WIN32 + +#endif +int sqlite3_base_init +#else +static int sqlite3_base64_init +#endif +(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){ + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErr; + return sqlite3_create_function + (db, "base64", 1, + SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8, + 0, base64, 0, 0); +} + +/* +** Define some macros to allow this extension to be built into the shell +** conveniently, in conjunction with use of SQLITE_SHELL_EXTFUNCS. This +** allows shell.c, as distributed, to have this extension built in. +*/ +#define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0) +#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */ + +/************************* End ../ext/misc/base64.c ********************/ +#undef sqlite3_base_init +#define sqlite3_base_init sqlite3_base85_init +#define OMIT_BASE85_CHECKER +/************************* Begin ../ext/misc/base85.c ******************/ +/* +** 2022-11-16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This is a utility for converting binary to base85 or vice-versa. +** It can be built as a standalone program or an SQLite3 extension. +** +** Much like base64 representations, base85 can be sent through a +** sane USASCII channel unmolested. It also plays nicely in CSV or +** written as TCL brace-enclosed literals or SQL string literals. +** It is not suited for unmodified use in XML-like documents. +** +** The encoding used resembles Ascii85, but was devised by the author +** (Larry Brasfield) before Mozilla, Adobe, ZMODEM or other Ascii85 +** variant sources existed, in the 1984 timeframe on a VAX mainframe. +** Further, this is an independent implementation of a base85 system. +** Hence, the author has rightfully put this into the public domain. +** +** Base85 numerals are taken from the set of 7-bit USASCII codes, +** excluding control characters and Space ! " ' ( ) { | } ~ Del +** in code order representing digit values 0 to 84 (base 10.) +** +** Groups of 4 bytes, interpreted as big-endian 32-bit values, +** are represented as 5-digit base85 numbers with MS to LS digit +** order. Groups of 1-3 bytes are represented with 2-4 digits, +** still big-endian but 8-24 bit values. (Using big-endian yields +** the simplest transition to byte groups smaller than 4 bytes. +** These byte groups can also be considered base-256 numbers.) +** Groups of 0 bytes are represented with 0 digits and vice-versa. +** No pad characters are used; Encoded base85 numeral sequence +** (aka "group") length maps 1-to-1 to the decoded binary length. +** +** Any character not in the base85 numeral set delimits groups. +** When base85 is streamed or stored in containers of indefinite +** size, newline is used to separate it into sub-sequences of no +** more than 80 digits so that fgets() can be used to read it. +** +** Length limitations are not imposed except that the runtime +** SQLite string or blob length limits are respected. Otherwise, +** any length binary sequence can be represented and recovered. +** Base85 sequences can be concatenated by separating them with +** a non-base85 character; the conversion to binary will then +** be the concatenation of the represented binary sequences. + +** The standalone program either converts base85 on stdin to create +** a binary file or converts a binary file to base85 on stdout. +** Read or make it blurt its help for invocation details. +** +** The SQLite3 extension creates a function, base85(x), which will +** either convert text base85 to a blob or a blob to text base85 +** and return the result (or throw an error for other types.) +** Unless built with OMIT_BASE85_CHECKER defined, it also creates a +** function, is_base85(t), which returns 1 iff the text t contains +** nothing other than base85 numerals and whitespace, or 0 otherwise. +** +** To build the extension: +** Set shell variable SQDIR= +** and variable OPTS to -DOMIT_BASE85_CHECKER if is_base85() unwanted. +** *Nix: gcc -O2 -shared -I$SQDIR $OPTS -fPIC -o base85.so base85.c +** OSX: gcc -O2 -dynamiclib -fPIC -I$SQDIR $OPTS -o base85.dylib base85.c +** Win32: gcc -O2 -shared -I%SQDIR% %OPTS% -o base85.dll base85.c +** Win32: cl /Os -I%SQDIR% %OPTS% base85.c -link -dll -out:base85.dll +** +** To build the standalone program, define PP symbol BASE85_STANDALONE. Eg. +** *Nix or OSX: gcc -O2 -DBASE85_STANDALONE base85.c -o base85 +** Win32: gcc -O2 -DBASE85_STANDALONE -o base85.exe base85.c +** Win32: cl /Os /MD -DBASE85_STANDALONE base85.c +*/ + +#include +#include +#include +#include +#ifndef OMIT_BASE85_CHECKER +# include +#endif + +#ifndef BASE85_STANDALONE + +/* # include "sqlite3ext.h" */ + +SQLITE_EXTENSION_INIT1; + +#else + +# ifdef _WIN32 +# include +# include +# else +# define setmode(fd,m) +# endif + +static char *zHelp = + "Usage: base85 \n" + " is either -r to read or -w to write ,\n" + " content to be converted to/from base85 on stdout/stdin.\n" + " names a binary file to be rendered or created.\n" + " Or, the name '-' refers to the stdin or stdout stream.\n" + ; + +static void sayHelp(){ + printf("%s", zHelp); +} +#endif + +#ifndef U8_TYPEDEF +/* typedef unsigned char u8; */ +#define U8_TYPEDEF +#endif + +/* Classify c according to interval within USASCII set w.r.t. base85 + * Values of 1 and 3 are base85 numerals. Values of 0, 2, or 4 are not. + */ +#define B85_CLASS( c ) (((c)>='#')+((c)>'&')+((c)>='*')+((c)>'z')) + +/* Provide digitValue to b85Numeral offset as a function of above class. */ +static u8 b85_cOffset[] = { 0, '#', 0, '*'-4, 0 }; +#define B85_DNOS( c ) b85_cOffset[B85_CLASS(c)] + +/* Say whether c is a base85 numeral. */ +#define IS_B85( c ) (B85_CLASS(c) & 1) + +#if 0 /* Not used, */ +static u8 base85DigitValue( char c ){ + u8 dv = (u8)(c - '#'); + if( dv>87 ) return 0xff; + return (dv > 3)? dv-3 : dv; +} +#endif + +/* Width of base64 lines. Should be an integer multiple of 5. */ +#define B85_DARK_MAX 80 + + +static char * skipNonB85( char *s, int nc ){ + char c; + while( nc-- > 0 && (c = *s) && !IS_B85(c) ) ++s; + return s; +} + +/* Convert small integer, known to be in 0..84 inclusive, to base85 numeral. + * Do not use the macro form with argument expression having a side-effect.*/ +#if 0 +static char base85Numeral( u8 b ){ + return (b < 4)? (char)(b + '#') : (char)(b - 4 + '*'); +} +#else +# define base85Numeral( dn )\ + ((char)(((dn) < 4)? (char)((dn) + '#') : (char)((dn) - 4 + '*'))) +#endif + +static char *putcs(char *pc, char *s){ + char c; + while( (c = *s++)!=0 ) *pc++ = c; + return pc; +} + +/* Encode a byte buffer into base85 text. If pSep!=0, it's a C string +** to be appended to encoded groups to limit their length to B85_DARK_MAX +** or to terminate the last group (to aid concatenation.) */ -static void decimalMulFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - Decimal *pA = decimal_new(context, argv[0], 0, 0); - Decimal *pB = decimal_new(context, argv[1], 0, 0); - signed char *acc = 0; - int i, j, k; - int minFrac; - UNUSED_PARAMETER(argc); - if( pA==0 || pA->oom || pA->isNull - || pB==0 || pB->oom || pB->isNull - ){ - goto mul_end; +static char* toBase85( u8 *pIn, int nbIn, char *pOut, char *pSep ){ + int nCol = 0; + while( nbIn >= 4 ){ + int nco = 5; + unsigned long qbv = (((unsigned long)pIn[0])<<24) | + (pIn[1]<<16) | (pIn[2]<<8) | pIn[3]; + while( nco > 0 ){ + unsigned nqv = (unsigned)(qbv/85UL); + unsigned char dv = qbv - 85UL*nqv; + qbv = nqv; + pOut[--nco] = base85Numeral(dv); + } + nbIn -= 4; + pIn += 4; + pOut += 5; + if( pSep && (nCol += 5)>=B85_DARK_MAX ){ + pOut = putcs(pOut, pSep); + nCol = 0; + } + } + if( nbIn > 0 ){ + int nco = nbIn + 1; + unsigned long qv = *pIn++; + int nbe = 1; + while( nbe++ < nbIn ){ + qv = (qv<<8) | *pIn++; + } + nCol += nco; + while( nco > 0 ){ + u8 dv = (u8)(qv % 85); + qv /= 85; + pOut[--nco] = base85Numeral(dv); + } + pOut += (nbIn+1); + } + if( pSep && nCol>0 ) pOut = putcs(pOut, pSep); + *pOut = 0; + return pOut; +} + +/* Decode base85 text into a byte buffer. */ +static u8* fromBase85( char *pIn, int ncIn, u8 *pOut ){ + if( ncIn>0 && pIn[ncIn-1]=='\n' ) --ncIn; + while( ncIn>0 ){ + static signed char nboi[] = { 0, 0, 1, 2, 3, 4 }; + char *pUse = skipNonB85(pIn, ncIn); + unsigned long qv = 0L; + int nti, nbo; + ncIn -= (pUse - pIn); + pIn = pUse; + nti = (ncIn>5)? 5 : ncIn; + nbo = nboi[nti]; + if( nbo==0 ) break; + while( nti>0 ){ + char c = *pIn++; + u8 cdo = B85_DNOS(c); + --ncIn; + if( cdo==0 ) break; + qv = 85 * qv + (c - cdo); + --nti; + } + nbo -= nti; /* Adjust for early (non-digit) end of group. */ + switch( nbo ){ + case 4: + *pOut++ = (qv >> 24)&0xff; + case 3: + *pOut++ = (qv >> 16)&0xff; + case 2: + *pOut++ = (qv >> 8)&0xff; + case 1: + *pOut++ = qv&0xff; + case 0: + break; + } } - acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 ); - if( acc==0 ){ - sqlite3_result_error_nomem(context); - goto mul_end; + return pOut; +} + +#ifndef OMIT_BASE85_CHECKER +/* Say whether input char sequence is all (base85 and/or whitespace).*/ +static int allBase85( char *p, int len ){ + char c; + while( len-- > 0 && (c = *p++) != 0 ){ + if( !IS_B85(c) && !isspace(c) ) return 0; } - memset(acc, 0, pA->nDigit + pB->nDigit + 2); - minFrac = pA->nFrac; - if( pB->nFrac nFrac; - for(i=pA->nDigit-1; i>=0; i--){ - signed char f = pA->a[i]; - int carry = 0, x; - for(j=pB->nDigit-1, k=i+j+3; j>=0; j--, k--){ - x = acc[k] + f*pB->a[j] + carry; - acc[k] = x%10; - carry = x/10; + return 1; +} +#endif + +#ifndef BASE85_STANDALONE + +# ifndef OMIT_BASE85_CHECKER +/* This function does the work for the SQLite is_base85(t) UDF. */ +static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){ + assert(na==1); + switch( sqlite3_value_type(av[0]) ){ + case SQLITE_TEXT: + { + int rv = allBase85( (char *)sqlite3_value_text(av[0]), + sqlite3_value_bytes(av[0]) ); + sqlite3_result_int(context, rv); } - x = acc[k] + carry; - acc[k] = x%10; - acc[k-1] += x/10; - } - sqlite3_free(pA->a); - pA->a = acc; - acc = 0; - pA->nDigit += pB->nDigit + 2; - pA->nFrac += pB->nFrac; - pA->sign ^= pB->sign; - while( pA->nFrac>minFrac && pA->a[pA->nDigit-1]==0 ){ - pA->nFrac--; - pA->nDigit--; + break; + case SQLITE_NULL: + sqlite3_result_null(context); + break; + default: + sqlite3_result_error(context, "is_base85 accepts only text or NULL", -1); + return; } - decimal_result(context, pA); +} +# endif -mul_end: - sqlite3_free(acc); - decimal_free(pA); - decimal_free(pB); +/* This function does the work for the SQLite base85(x) UDF. */ +static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){ + int nb, nc, nv = sqlite3_value_bytes(av[0]); + int nvMax = sqlite3_limit(sqlite3_context_db_handle(context), + SQLITE_LIMIT_LENGTH, -1); + char *cBuf; + u8 *bBuf; + assert(na==1); + switch( sqlite3_value_type(av[0]) ){ + case SQLITE_BLOB: + nb = nv; + /* ulongs tail newlines tailenc+nul*/ + nc = 5*(nv/4) + nv%4 + nv/64+1 + 2; + if( nvMax < nc ){ + sqlite3_result_error(context, "blob expanded to base85 too big", -1); + return; + } + bBuf = (u8*)sqlite3_value_blob(av[0]); + if( !bBuf ){ + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ + goto memFail; + } + sqlite3_result_text(context,"",-1,SQLITE_STATIC); + break; + } + cBuf = sqlite3_malloc(nc); + if( !cBuf ) goto memFail; + nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf); + sqlite3_result_text(context, cBuf, nc, sqlite3_free); + break; + case SQLITE_TEXT: + nc = nv; + nb = 4*(nv/5) + nv%5; /* may overestimate */ + if( nvMax < nb ){ + sqlite3_result_error(context, "blob from base85 may be too big", -1); + return; + }else if( nb<1 ){ + nb = 1; + } + cBuf = (char *)sqlite3_value_text(av[0]); + if( !cBuf ){ + if( SQLITE_NOMEM==sqlite3_errcode(sqlite3_context_db_handle(context)) ){ + goto memFail; + } + sqlite3_result_zeroblob(context, 0); + break; + } + bBuf = sqlite3_malloc(nb); + if( !bBuf ) goto memFail; + nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf); + sqlite3_result_blob(context, bBuf, nb, sqlite3_free); + break; + default: + sqlite3_result_error(context, "base85 accepts only blob or text.", -1); + return; + } + return; + memFail: + sqlite3_result_error(context, "base85 OOM", -1); } +/* +** Establish linkage to running SQLite library. +*/ +#ifndef SQLITE_SHELL_EXTFUNCS #ifdef _WIN32 #endif -int sqlite3_decimal_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - int rc = SQLITE_OK; - static const struct { - const char *zFuncName; - int nArg; - void (*xFunc)(sqlite3_context*,int,sqlite3_value**); - } aFunc[] = { - { "decimal", 1, decimalFunc }, - { "decimal_cmp", 2, decimalCmpFunc }, - { "decimal_add", 2, decimalAddFunc }, - { "decimal_sub", 2, decimalSubFunc }, - { "decimal_mul", 2, decimalMulFunc }, - }; - unsigned int i; - (void)pzErrMsg; /* Unused parameter */ - +int sqlite3_base_init +#else +static int sqlite3_base85_init +#endif +(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){ SQLITE_EXTENSION_INIT2(pApi); + (void)pzErr; +# ifndef OMIT_BASE85_CHECKER + { + int rc = sqlite3_create_function + (db, "is_base85", 1, + SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_UTF8, + 0, is_base85, 0, 0); + if( rc!=SQLITE_OK ) return rc; + } +# endif + return sqlite3_create_function + (db, "base85", 1, + SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8, + 0, base85, 0, 0); +} - for(i=0; i 0 ){ + toBase85( bBuf, (int)nio, cBuf, 0 ); + fprintf(stdout, "%s\n", cBuf); + } + break; + case 'w': + while( 0 != fgets(cBuf, sizeof(cBuf), stdin) ){ + int nc = strlen(cBuf); + size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf; + if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1; +# ifndef OMIT_BASE85_CHECKER + b85Clean &= allBase85( cBuf, nc ); +# endif + } + break; + default: + sayHelp(); + rc = 1; + } + if( foc ) fclose(foc); + } +# ifndef OMIT_BASE85_CHECKER + if( !b85Clean ){ + fprintf(stderr, "Base85 input had non-base85 dark or control content.\n"); } +# endif return rc; } -/************************* End ../ext/misc/decimal.c ********************/ +#endif + +/************************* End ../ext/misc/base85.c ********************/ /************************* Begin ../ext/misc/ieee754.c ******************/ /* ** 2013-04-17 @@ -3243,6 +5534,37 @@ static void ieee754func_to_blob( } } +/* +** SQL Function: ieee754_inc(r,N) +** +** Move the floating point value r by N quantums and return the new +** values. +** +** Behind the scenes: this routine merely casts r into a 64-bit unsigned +** integer, adds N, then casts the value back into float. +** +** Example: To find the smallest positive number: +** +** SELECT ieee754_inc(0.0,+1); +*/ +static void ieee754inc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + double r; + sqlite3_int64 N; + sqlite3_uint64 m1, m2; + double r2; + UNUSED_PARAMETER(argc); + r = sqlite3_value_double(argv[0]); + N = sqlite3_value_int64(argv[1]); + memcpy(&m1, &r, 8); + m2 = m1 + N; + memcpy(&r2, &m2, 8); + sqlite3_result_double(context, r2); +} + #ifdef _WIN32 @@ -3264,7 +5586,7 @@ int sqlite3_ieee_init( { "ieee754_exponent", 1, 2, ieee754func }, { "ieee754_to_blob", 1, 0, ieee754func_to_blob }, { "ieee754_from_blob", 1, 0, ieee754func_from_blob }, - + { "ieee754_inc", 2, 0, ieee754inc }, }; unsigned int i; int rc = SQLITE_OK; @@ -3282,7 +5604,7 @@ int sqlite3_ieee_init( /************************* End ../ext/misc/ieee754.c ********************/ /************************* Begin ../ext/misc/series.c ******************/ /* -** 2015-08-18 +** 2015-08-18, 2023-04-28 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -3295,7 +5617,19 @@ int sqlite3_ieee_init( ** ** This file demonstrates how to create a table-valued-function using ** a virtual table. This demo implements the generate_series() function -** which gives similar results to the eponymous function in PostgreSQL. +** which gives the same results as the eponymous function in PostgreSQL, +** within the limitation that its arguments are signed 64-bit integers. +** +** Considering its equivalents to generate_series(start,stop,step): A +** value V[n] sequence is produced for integer n ascending from 0 where +** ( V[n] == start + n * step && sgn(V[n] - stop) * sgn(step) >= 0 ) +** for each produced value (independent of production time ordering.) +** +** All parameters must be either integer or convertable to integer. +** The start parameter is required. +** The stop parameter defaults to (1<<32)-1 (aka 4294967295 or 0xffffffff) +** The step parameter defaults to 1 and 0 is treated as 1. +** ** Examples: ** ** SELECT * FROM generate_series(0,100,5); @@ -3311,6 +5645,14 @@ int sqlite3_ieee_init( ** ** Integers 20 through 29. ** +** SELECT * FROM generate_series(0,-100,-5); +** +** Integers 0 -5 -10 ... -100. +** +** SELECT * FROM generate_series(0,-1); +** +** Empty sequence. +** ** HOW IT WORKS ** ** The generate_series "function" is really a virtual table with the @@ -3323,6 +5665,9 @@ int sqlite3_ieee_init( ** step HIDDEN ** ); ** +** The virtual table also has a rowid, logically equivalent to n+1 where +** "n" is the ascending integer in the aforesaid production definition. +** ** Function arguments in queries against this virtual table are translated ** into equality constraints against successive hidden columns. In other ** words, the following pairs of queries are equivalent to each other: @@ -3355,9 +5700,126 @@ int sqlite3_ieee_init( SQLITE_EXTENSION_INIT1 #include #include +#include #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Return that member of a generate_series(...) sequence whose 0-based +** index is ix. The 0th member is given by smBase. The sequence members +** progress per ix increment by smStep. +*/ +static sqlite3_int64 genSeqMember(sqlite3_int64 smBase, + sqlite3_int64 smStep, + sqlite3_uint64 ix){ + if( ix>=(sqlite3_uint64)LLONG_MAX ){ + /* Get ix into signed i64 range. */ + ix -= (sqlite3_uint64)LLONG_MAX; + /* With 2's complement ALU, this next can be 1 step, but is split into + * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ + smBase += (LLONG_MAX/2) * smStep; + smBase += (LLONG_MAX - LLONG_MAX/2) * smStep; + } + /* Under UBSAN (or on 1's complement machines), must do this last term + * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ + if( ix>=2 ){ + sqlite3_int64 ix2 = (sqlite3_int64)ix/2; + smBase += ix2*smStep; + ix -= ix2; + } + return smBase + ((sqlite3_int64)ix)*smStep; +} + +/* typedef unsigned char u8; */ +typedef struct SequenceSpec { + sqlite3_int64 iBase; /* Starting value ("start") */ + sqlite3_int64 iTerm; /* Given terminal value ("stop") */ + sqlite3_int64 iStep; /* Increment ("step") */ + sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */ + sqlite3_uint64 uSeqIndexNow; /* Current index during generation */ + sqlite3_int64 iValueNow; /* Current value during generation */ + u8 isNotEOF; /* Sequence generation not exhausted */ + u8 isReversing; /* Sequence is being reverse generated */ +} SequenceSpec; + +/* +** Prepare a SequenceSpec for use in generating an integer series +** given initialized iBase, iTerm and iStep values. Sequence is +** initialized per given isReversing. Other members are computed. +*/ +static void setupSequence( SequenceSpec *pss ){ + int bSameSigns; + pss->uSeqIndexMax = 0; + pss->isNotEOF = 0; + bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0); + if( pss->iTerm < pss->iBase ){ + sqlite3_uint64 nuspan = 0; + if( bSameSigns ){ + nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm); + }else{ + /* Under UBSAN (or on 1's complement machines), must do this in steps. + * In this clause, iBase>=0 and iTerm<0 . */ + nuspan = 1; + nuspan += pss->iBase; + nuspan += -(pss->iTerm+1); + } + if( pss->iStep<0 ){ + pss->isNotEOF = 1; + if( nuspan==ULONG_MAX ){ + pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1; + }else if( pss->iStep>LLONG_MIN ){ + pss->uSeqIndexMax = nuspan/-pss->iStep; + } + } + }else if( pss->iTerm > pss->iBase ){ + sqlite3_uint64 puspan = 0; + if( bSameSigns ){ + puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase); + }else{ + /* Under UBSAN (or on 1's complement machines), must do this in steps. + * In this clause, iTerm>=0 and iBase<0 . */ + puspan = 1; + puspan += pss->iTerm; + puspan += -(pss->iBase+1); + } + if( pss->iStep>0 ){ + pss->isNotEOF = 1; + pss->uSeqIndexMax = puspan/pss->iStep; + } + }else if( pss->iTerm == pss->iBase ){ + pss->isNotEOF = 1; + pss->uSeqIndexMax = 0; + } + pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0; + pss->iValueNow = (pss->isReversing) + ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax) + : pss->iBase; +} + +/* +** Progress sequence generator to yield next value, if any. +** Leave its state to either yield next value or be at EOF. +** Return whether there is a next value, or 0 at EOF. +*/ +static int progressSequence( SequenceSpec *pss ){ + if( !pss->isNotEOF ) return 0; + if( pss->isReversing ){ + if( pss->uSeqIndexNow > 0 ){ + pss->uSeqIndexNow--; + pss->iValueNow -= pss->iStep; + }else{ + pss->isNotEOF = 0; + } + }else{ + if( pss->uSeqIndexNow < pss->uSeqIndexMax ){ + pss->uSeqIndexNow++; + pss->iValueNow += pss->iStep; + }else{ + pss->isNotEOF = 0; + } + } + return pss->isNotEOF; +} /* series_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans @@ -3366,12 +5828,7 @@ SQLITE_EXTENSION_INIT1 typedef struct series_cursor series_cursor; struct series_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ - int isDesc; /* True to count down rather than up */ - sqlite3_int64 iRowid; /* The rowid */ - sqlite3_int64 iValue; /* Current value ("value") */ - sqlite3_int64 mnValue; /* Mimimum value ("start") */ - sqlite3_int64 mxValue; /* Maximum value ("stop") */ - sqlite3_int64 iStep; /* Increment ("step") */ + SequenceSpec ss; /* (this) Derived class data */ }; /* @@ -3453,12 +5910,7 @@ static int seriesClose(sqlite3_vtab_cursor *cur){ */ static int seriesNext(sqlite3_vtab_cursor *cur){ series_cursor *pCur = (series_cursor*)cur; - if( pCur->isDesc ){ - pCur->iValue -= pCur->iStep; - }else{ - pCur->iValue += pCur->iStep; - } - pCur->iRowid++; + progressSequence( & pCur->ss ); return SQLITE_OK; } @@ -3474,23 +5926,27 @@ static int seriesColumn( series_cursor *pCur = (series_cursor*)cur; sqlite3_int64 x = 0; switch( i ){ - case SERIES_COLUMN_START: x = pCur->mnValue; break; - case SERIES_COLUMN_STOP: x = pCur->mxValue; break; - case SERIES_COLUMN_STEP: x = pCur->iStep; break; - default: x = pCur->iValue; break; + case SERIES_COLUMN_START: x = pCur->ss.iBase; break; + case SERIES_COLUMN_STOP: x = pCur->ss.iTerm; break; + case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break; + default: x = pCur->ss.iValueNow; break; } sqlite3_result_int64(ctx, x); return SQLITE_OK; } +#ifndef LARGEST_UINT64 +#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32)) +#endif + /* -** Return the rowid for the current row. In this implementation, the -** first row returned is assigned rowid value 1, and each subsequent -** row a value 1 more than that of the previous. +** Return the rowid for the current row, logically equivalent to n+1 where +** "n" is the ascending integer in the aforesaid production definition. */ static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ series_cursor *pCur = (series_cursor*)cur; - *pRowid = pCur->iRowid; + sqlite3_uint64 n = pCur->ss.uSeqIndexNow; + *pRowid = (sqlite3_int64)((n isDesc ){ - return pCur->iValue < pCur->mnValue; - }else{ - return pCur->iValue > pCur->mxValue; - } + return !pCur->ss.isNotEOF; } -/* True to cause run-time checking of the start=, stop=, and/or step= +/* True to cause run-time checking of the start=, stop=, and/or step= ** parameters. The only reason to do this is for testing the ** constraint checking logic for virtual tables in the SQLite core. */ @@ -3518,7 +5970,7 @@ static int seriesEof(sqlite3_vtab_cursor *cur){ /* ** This method is called to "rewind" the series_cursor object back ** to the first row of output. This method is always called at least -** once prior to any call to seriesColumn() or seriesRowid() or +** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** The query plan selected by seriesBestIndex is passed in the idxNum @@ -3538,7 +5990,7 @@ static int seriesEof(sqlite3_vtab_cursor *cur){ ** (so that seriesEof() will return true) if the table is empty. */ static int seriesFilter( - sqlite3_vtab_cursor *pVtabCursor, + sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStrUnused, int argc, sqlite3_value **argv ){ @@ -3546,46 +5998,41 @@ static int seriesFilter( int i = 0; (void)idxStrUnused; if( idxNum & 1 ){ - pCur->mnValue = sqlite3_value_int64(argv[i++]); + pCur->ss.iBase = sqlite3_value_int64(argv[i++]); }else{ - pCur->mnValue = 0; + pCur->ss.iBase = 0; } if( idxNum & 2 ){ - pCur->mxValue = sqlite3_value_int64(argv[i++]); + pCur->ss.iTerm = sqlite3_value_int64(argv[i++]); }else{ - pCur->mxValue = 0xffffffff; + pCur->ss.iTerm = 0xffffffff; } if( idxNum & 4 ){ - pCur->iStep = sqlite3_value_int64(argv[i++]); - if( pCur->iStep==0 ){ - pCur->iStep = 1; - }else if( pCur->iStep<0 ){ - pCur->iStep = -pCur->iStep; + pCur->ss.iStep = sqlite3_value_int64(argv[i++]); + if( pCur->ss.iStep==0 ){ + pCur->ss.iStep = 1; + }else if( pCur->ss.iStep<0 ){ if( (idxNum & 16)==0 ) idxNum |= 8; } }else{ - pCur->iStep = 1; + pCur->ss.iStep = 1; } for(i=0; i mnValue = 1; - pCur->mxValue = 0; + pCur->ss.iBase = 1; + pCur->ss.iTerm = 0; + pCur->ss.iStep = 1; break; } } if( idxNum & 8 ){ - pCur->isDesc = 1; - pCur->iValue = pCur->mxValue; - if( pCur->iStep>0 ){ - pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep; - } + pCur->ss.isReversing = pCur->ss.iStep > 0; }else{ - pCur->isDesc = 0; - pCur->iValue = pCur->mnValue; + pCur->ss.isReversing = pCur->ss.iStep < 0; } - pCur->iRowid = 1; + setupSequence( &pCur->ss ); return SQLITE_OK; } @@ -3715,7 +6162,8 @@ static sqlite3_module seriesModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -4340,7 +6788,7 @@ static const char *re_subcompile_string(ReCompiled *p){ break; } case '[': { - int iFirst = p->nState; + unsigned int iFirst = p->nState; if( rePeek(p)=='^' ){ re_append(p, RE_OP_CC_EXC, 0); p->sIn.i++; @@ -4364,7 +6812,7 @@ static const char *re_subcompile_string(ReCompiled *p){ if( rePeek(p)==']' ){ p->sIn.i++; break; } } if( c==0 ) return "unclosed '['"; - p->aArg[iFirst] = p->nState - iFirst; + if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst; break; } case '\\': { @@ -4548,6 +6996,7 @@ static void re_bytecode_func( int i; int n; char *z; + (void)argc; zPattern = (const char*)sqlite3_value_text(argv[0]); if( zPattern==0 ) return; @@ -5612,6 +8061,7 @@ static int fsdirRegister(sqlite3 *db){ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ + 0 /* xIntegrity */ }; int rc = sqlite3_create_module(db, "fsdir", &fsdirModule, 0); @@ -6132,7 +8582,8 @@ static sqlite3_module completionModule = { 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollbackTo */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -6872,6 +9323,7 @@ SQLITE_EXTENSION_INIT1 #include #include #include +#include #include @@ -7195,6 +9647,7 @@ static int zipfileConnect( const char *zFile = 0; ZipfileTab *pNew = 0; int rc; + (void)pAux; /* If the table name is not "zipfile", require that the argument be ** specified. This stops zipfile tables from being created as: @@ -7651,6 +10104,7 @@ static int zipfileGetEntry( u8 *aRead; char **pzErr = &pTab->base.zErrMsg; int rc = SQLITE_OK; + (void)nBlob; if( aBlob==0 ){ aRead = pTab->aBuffer; @@ -7938,7 +10392,10 @@ static int zipfileColumn( ** it to be a directory either if the mode suggests so, or if ** the final character in the name is '/'. */ u32 mode = pCDS->iExternalAttr >> 16; - if( !(mode & S_IFDIR) && pCDS->zFile[pCDS->nFile-1]!='/' ){ + if( !(mode & S_IFDIR) + && pCDS->nFile>=1 + && pCDS->zFile[pCDS->nFile-1]!='/' + ){ sqlite3_result_blob(ctx, "", 0, SQLITE_STATIC); } } @@ -8097,6 +10554,9 @@ static int zipfileFilter( int rc = SQLITE_OK; /* Return Code */ int bInMemory = 0; /* True for an in-memory zipfile */ + (void)idxStr; + (void)argc; + zipfileResetCursor(pCsr); if( pTab->zFile ){ @@ -8123,7 +10583,7 @@ static int zipfileFilter( } if( 0==pTab->pWriteFd && 0==bInMemory ){ - pCsr->pFile = fopen(zFile, "rb"); + pCsr->pFile = zFile ? fopen(zFile, "rb") : 0; if( pCsr->pFile==0 ){ zipfileCursorErr(pCsr, "cannot open file: %s", zFile); rc = SQLITE_ERROR; @@ -8157,6 +10617,7 @@ static int zipfileBestIndex( int i; int idx = -1; int unusable = 0; + (void)tab; for(i=0; i nConstraint; i++){ const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; @@ -8371,9 +10832,19 @@ static u32 zipfileGetTime(sqlite3_value *pVal){ */ static void zipfileRemoveEntryFromList(ZipfileTab *pTab, ZipfileEntry *pOld){ if( pOld ){ - ZipfileEntry **pp; - for(pp=&pTab->pFirstEntry; (*pp)!=pOld; pp=&((*pp)->pNext)); - *pp = (*pp)->pNext; + if( pTab->pFirstEntry==pOld ){ + pTab->pFirstEntry = pOld->pNext; + if( pTab->pLastEntry==pOld ) pTab->pLastEntry = 0; + }else{ + ZipfileEntry *p; + for(p=pTab->pFirstEntry; p; p=p->pNext){ + if( p->pNext==pOld ){ + p->pNext = pOld->pNext; + if( pTab->pLastEntry==pOld ) pTab->pLastEntry = p; + break; + } + } + } zipfileEntryFree(pOld); } } @@ -8407,6 +10878,8 @@ static int zipfileUpdate( int bIsDir = 0; u32 iCrc32 = 0; + (void)pRowid; + if( pTab->pWriteFd==0 ){ rc = zipfileBegin(pVtab); if( rc!=SQLITE_OK ) return rc; @@ -8741,6 +11214,7 @@ static int zipfileFindFunction( void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ ){ + (void)nArg; if( sqlite3_stricmp("zipfile_cds", zName)==0 ){ *pxFunc = zipfileFunctionCds; *ppArg = (void*)pVtab; @@ -9021,7 +11495,8 @@ static int zipfileRegister(sqlite3 *db){ 0, /* xSavepoint */ 0, /* xRelease */ 0, /* xRollback */ - 0 /* xShadowName */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; int rc = sqlite3_create_module(db, "zipfile" , &zipfileModule, 0); @@ -9149,7 +11624,9 @@ static void sqlarUncompressFunc( }else{ const Bytef *pData= sqlite3_value_blob(argv[0]); Bytef *pOut = sqlite3_malloc(sz); - if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){ + if( pOut==0 ){ + sqlite3_result_error_nomem(context); + }else if( Z_OK!=uncompress(pOut, &sz, pData, nData) ){ sqlite3_result_error(context, "error in uncompress()", -1); }else{ sqlite3_result_blob(context, pOut, sz, SQLITE_TRANSIENT); @@ -9158,7 +11635,6 @@ static void sqlarUncompressFunc( } } - #ifdef _WIN32 #endif @@ -9389,7 +11865,7 @@ void sqlite3_expert_destroy(sqlite3expert*); #endif /* !defined(SQLITE_AMALGAMATION) */ -#ifndef SQLITE_OMIT_VIRTUALTABLE +#ifndef SQLITE_OMIT_VIRTUALTABLE /* typedef sqlite3_int64 i64; */ /* typedef sqlite3_uint64 u64; */ @@ -10019,6 +12495,7 @@ static int idxRegisterVtab(sqlite3expert *p){ 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ + 0, /* xIntegrity */ }; return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p); @@ -11170,10 +13647,92 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){ if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0); } - - sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0); + + sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0); + return rc; +} + +/* +** Define and possibly pretend to use a useless collation sequence. +** This pretense allows expert to accept SQL using custom collations. +*/ +int dummyCompare(void *up1, int up2, const void *up3, int up4, const void *up5){ + (void)up1; + (void)up2; + (void)up3; + (void)up4; + (void)up5; + assert(0); /* VDBE should never be run. */ + return 0; +} +/* And a callback to register above upon actual need */ +void useDummyCS(void *up1, sqlite3 *db, int etr, const char *zName){ + (void)up1; + sqlite3_create_collation_v2(db, zName, etr, 0, dummyCompare, 0); +} + +#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \ + && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) +/* +** dummy functions for no-op implementation of UDFs during expert's work +*/ +void dummyUDF(sqlite3_context *up1, int up2, sqlite3_value **up3){ + (void)up1; + (void)up2; + (void)up3; + assert(0); /* VDBE should never be run. */ +} +void dummyUDFvalue(sqlite3_context *up1){ + (void)up1; + assert(0); /* VDBE should never be run. */ +} + +/* +** Register UDFs from user database with another. +*/ +int registerUDFs(sqlite3 *dbSrc, sqlite3 *dbDst){ + sqlite3_stmt *pStmt; + int rc = sqlite3_prepare_v2(dbSrc, + "SELECT name,type,enc,narg,flags " + "FROM pragma_function_list() " + "WHERE builtin==0", -1, &pStmt, 0); + if( rc==SQLITE_OK ){ + while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ + int nargs = sqlite3_column_int(pStmt,3); + int flags = sqlite3_column_int(pStmt,4); + const char *name = (char*)sqlite3_column_text(pStmt,0); + const char *type = (char*)sqlite3_column_text(pStmt,1); + const char *enc = (char*)sqlite3_column_text(pStmt,2); + if( name==0 || type==0 || enc==0 ){ + /* no-op. Only happens on OOM */ + }else{ + int ienc = SQLITE_UTF8; + int rcf = SQLITE_ERROR; + if( strcmp(enc,"utf16le")==0 ) ienc = SQLITE_UTF16LE; + else if( strcmp(enc,"utf16be")==0 ) ienc = SQLITE_UTF16BE; + ienc |= (flags & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY)); + if( strcmp(type,"w")==0 ){ + rcf = sqlite3_create_window_function(dbDst,name,nargs,ienc,0, + dummyUDF,dummyUDFvalue,0,0,0); + }else if( strcmp(type,"a")==0 ){ + rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0, + 0,dummyUDF,dummyUDFvalue); + }else if( strcmp(type,"s")==0 ){ + rcf = sqlite3_create_function(dbDst,name,nargs,ienc,0, + dummyUDF,0,0); + } + if( rcf!=SQLITE_OK ){ + rc = rcf; + break; + } + } + } + sqlite3_finalize(pStmt); + if( rc==SQLITE_DONE ) rc = SQLITE_OK; + } return rc; } +#endif /* ** Allocate a new sqlite3expert object. @@ -11201,7 +13760,21 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){ sqlite3_db_config(pNew->dbm, SQLITE_DBCONFIG_TRIGGER_EQP, 1, (int*)0); } } - + + /* Allow custom collations to be dealt with through prepare. */ + if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbm,0,useDummyCS); + if( rc==SQLITE_OK ) rc = sqlite3_collation_needed(pNew->dbv,0,useDummyCS); + +#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) \ + && !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) + /* Register UDFs from database [db] with [dbm] and [dbv]. */ + if( rc==SQLITE_OK ){ + rc = registerUDFs(pNew->db, pNew->dbm); + } + if( rc==SQLITE_OK ){ + rc = registerUDFs(pNew->db, pNew->dbv); + } +#endif /* Copy the entire schema of database [db] into [dbm]. */ if( rc==SQLITE_OK ){ @@ -11277,6 +13850,10 @@ int sqlite3_expert_sql( while( rc==SQLITE_OK && zStmt && zStmt[0] ){ sqlite3_stmt *pStmt = 0; + /* Ensure that the provided statement compiles against user's DB. */ + rc = idxPrepareStmt(p->db, &pStmt, pzErr, zStmt); + if( rc!=SQLITE_OK ) break; + sqlite3_finalize(pStmt); rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt); if( rc==SQLITE_OK ){ if( pStmt ){ @@ -11386,32 +13963,285 @@ const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){ } /* -** Free an sqlite3expert object. +** Free an sqlite3expert object. +*/ +void sqlite3_expert_destroy(sqlite3expert *p){ + if( p ){ + sqlite3_close(p->dbm); + sqlite3_close(p->dbv); + idxScanFree(p->pScan, 0); + idxStatementFree(p->pStatement, 0); + idxTableFree(p->pTable); + idxWriteFree(p->pWrite); + idxHashClear(&p->hIdx); + sqlite3_free(p->zCandidates); + sqlite3_free(p); + } +} + +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ + +/************************* End ../ext/expert/sqlite3expert.c ********************/ + +#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) +#define SQLITE_SHELL_HAVE_RECOVER 1 +#else +#define SQLITE_SHELL_HAVE_RECOVER 0 +#endif +#if SQLITE_SHELL_HAVE_RECOVER +/************************* Begin ../ext/recover/sqlite3recover.h ******************/ +/* +** 2022-08-27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains the public interface to the "recover" extension - +** an SQLite extension designed to recover data from corrupted database +** files. +*/ + +/* +** OVERVIEW: +** +** To use the API to recover data from a corrupted database, an +** application: +** +** 1) Creates an sqlite3_recover handle by calling either +** sqlite3_recover_init() or sqlite3_recover_init_sql(). +** +** 2) Configures the new handle using one or more calls to +** sqlite3_recover_config(). +** +** 3) Executes the recovery by repeatedly calling sqlite3_recover_step() on +** the handle until it returns something other than SQLITE_OK. If it +** returns SQLITE_DONE, then the recovery operation completed without +** error. If it returns some other non-SQLITE_OK value, then an error +** has occurred. +** +** 4) Retrieves any error code and English language error message using the +** sqlite3_recover_errcode() and sqlite3_recover_errmsg() APIs, +** respectively. +** +** 5) Destroys the sqlite3_recover handle and frees all resources +** using sqlite3_recover_finish(). +** +** The application may abandon the recovery operation at any point +** before it is finished by passing the sqlite3_recover handle to +** sqlite3_recover_finish(). This is not an error, but the final state +** of the output database, or the results of running the partial script +** delivered to the SQL callback, are undefined. +*/ + +#ifndef _SQLITE_RECOVER_H +#define _SQLITE_RECOVER_H + +/* #include "sqlite3.h" */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** An instance of the sqlite3_recover object represents a recovery +** operation in progress. +** +** Constructors: +** +** sqlite3_recover_init() +** sqlite3_recover_init_sql() +** +** Destructor: +** +** sqlite3_recover_finish() +** +** Methods: +** +** sqlite3_recover_config() +** sqlite3_recover_errcode() +** sqlite3_recover_errmsg() +** sqlite3_recover_run() +** sqlite3_recover_step() +*/ +typedef struct sqlite3_recover sqlite3_recover; + +/* +** These two APIs attempt to create and return a new sqlite3_recover object. +** In both cases the first two arguments identify the (possibly +** corrupt) database to recover data from. The first argument is an open +** database handle and the second the name of a database attached to that +** handle (i.e. "main", "temp" or the name of an attached database). +** +** If sqlite3_recover_init() is used to create the new sqlite3_recover +** handle, then data is recovered into a new database, identified by +** string parameter zUri. zUri may be an absolute or relative file path, +** or may be an SQLite URI. If the identified database file already exists, +** it is overwritten. +** +** If sqlite3_recover_init_sql() is invoked, then any recovered data will +** be returned to the user as a series of SQL statements. Executing these +** SQL statements results in the same database as would have been created +** had sqlite3_recover_init() been used. For each SQL statement in the +** output, the callback function passed as the third argument (xSql) is +** invoked once. The first parameter is a passed a copy of the fourth argument +** to this function (pCtx) as its first parameter, and a pointer to a +** nul-terminated buffer containing the SQL statement formated as UTF-8 as +** the second. If the xSql callback returns any value other than SQLITE_OK, +** then processing is immediately abandoned and the value returned used as +** the recover handle error code (see below). +** +** If an out-of-memory error occurs, NULL may be returned instead of +** a valid handle. In all other cases, it is the responsibility of the +** application to avoid resource leaks by ensuring that +** sqlite3_recover_finish() is called on all allocated handles. +*/ +sqlite3_recover *sqlite3_recover_init( + sqlite3* db, + const char *zDb, + const char *zUri +); +sqlite3_recover *sqlite3_recover_init_sql( + sqlite3* db, + const char *zDb, + int (*xSql)(void*, const char*), + void *pCtx +); + +/* +** Configure an sqlite3_recover object that has just been created using +** sqlite3_recover_init() or sqlite3_recover_init_sql(). This function +** may only be called before the first call to sqlite3_recover_step() +** or sqlite3_recover_run() on the object. +** +** The second argument passed to this function must be one of the +** SQLITE_RECOVER_* symbols defined below. Valid values for the third argument +** depend on the specific SQLITE_RECOVER_* symbol in use. +** +** SQLITE_OK is returned if the configuration operation was successful, +** or an SQLite error code otherwise. +*/ +int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); + +/* +** SQLITE_RECOVER_LOST_AND_FOUND: +** The pArg argument points to a string buffer containing the name +** of a "lost-and-found" table in the output database, or NULL. If +** the argument is non-NULL and the database contains seemingly +** valid pages that cannot be associated with any table in the +** recovered part of the schema, data is extracted from these +** pages to add to the lost-and-found table. +** +** SQLITE_RECOVER_FREELIST_CORRUPT: +** The pArg value must actually be a pointer to a value of type +** int containing value 0 or 1 cast as a (void*). If this option is set +** (argument is 1) and a lost-and-found table has been configured using +** SQLITE_RECOVER_LOST_AND_FOUND, then is assumed that the freelist is +** corrupt and an attempt is made to recover records from pages that +** appear to be linked into the freelist. Otherwise, pages on the freelist +** are ignored. Setting this option can recover more data from the +** database, but often ends up "recovering" deleted records. The default +** value is 0 (clear). +** +** SQLITE_RECOVER_ROWIDS: +** The pArg value must actually be a pointer to a value of type +** int containing value 0 or 1 cast as a (void*). If this option is set +** (argument is 1), then an attempt is made to recover rowid values +** that are not also INTEGER PRIMARY KEY values. If this option is +** clear, then new rowids are assigned to all recovered rows. The +** default value is 1 (set). +** +** SQLITE_RECOVER_SLOWINDEXES: +** The pArg value must actually be a pointer to a value of type +** int containing value 0 or 1 cast as a (void*). If this option is clear +** (argument is 0), then when creating an output database, the recover +** module creates and populates non-UNIQUE indexes right at the end of the +** recovery operation - after all recoverable data has been inserted +** into the new database. This is faster overall, but means that the +** final call to sqlite3_recover_step() for a recovery operation may +** be need to create a large number of indexes, which may be very slow. +** +** Or, if this option is set (argument is 1), then non-UNIQUE indexes +** are created in the output database before it is populated with +** recovered data. This is slower overall, but avoids the slow call +** to sqlite3_recover_step() at the end of the recovery operation. +** +** The default option value is 0. +*/ +#define SQLITE_RECOVER_LOST_AND_FOUND 1 +#define SQLITE_RECOVER_FREELIST_CORRUPT 2 +#define SQLITE_RECOVER_ROWIDS 3 +#define SQLITE_RECOVER_SLOWINDEXES 4 + +/* +** Perform a unit of work towards the recovery operation. This function +** must normally be called multiple times to complete database recovery. +** +** If no error occurs but the recovery operation is not completed, this +** function returns SQLITE_OK. If recovery has been completed successfully +** then SQLITE_DONE is returned. If an error has occurred, then an SQLite +** error code (e.g. SQLITE_IOERR or SQLITE_NOMEM) is returned. It is not +** considered an error if some or all of the data cannot be recovered +** due to database corruption. +** +** Once sqlite3_recover_step() has returned a value other than SQLITE_OK, +** all further such calls on the same recover handle are no-ops that return +** the same non-SQLITE_OK value. +*/ +int sqlite3_recover_step(sqlite3_recover*); + +/* +** Run the recovery operation to completion. Return SQLITE_OK if successful, +** or an SQLite error code otherwise. Calling this function is the same +** as executing: +** +** while( SQLITE_OK==sqlite3_recover_step(p) ); +** return sqlite3_recover_errcode(p); +*/ +int sqlite3_recover_run(sqlite3_recover*); + +/* +** If an error has been encountered during a prior call to +** sqlite3_recover_step(), then this function attempts to return a +** pointer to a buffer containing an English language explanation of +** the error. If no error message is available, or if an out-of memory +** error occurs while attempting to allocate a buffer in which to format +** the error message, NULL is returned. +** +** The returned buffer remains valid until the sqlite3_recover handle is +** destroyed using sqlite3_recover_finish(). */ -void sqlite3_expert_destroy(sqlite3expert *p){ - if( p ){ - sqlite3_close(p->dbm); - sqlite3_close(p->dbv); - idxScanFree(p->pScan, 0); - idxStatementFree(p->pStatement, 0); - idxTableFree(p->pTable); - idxWriteFree(p->pWrite); - idxHashClear(&p->hIdx); - sqlite3_free(p->zCandidates); - sqlite3_free(p); - } -} +const char *sqlite3_recover_errmsg(sqlite3_recover*); -#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ +/* +** If this function is called on an sqlite3_recover handle after +** an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK. +*/ +int sqlite3_recover_errcode(sqlite3_recover*); -/************************* End ../ext/expert/sqlite3expert.c ********************/ +/* +** Clean up a recovery object created by a call to sqlite3_recover_init(). +** The results of using a recovery object with any API after it has been +** passed to this function are undefined. +** +** This function returns the same value as sqlite3_recover_errcode(). +*/ +int sqlite3_recover_finish(sqlite3_recover*); -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) -#define SQLITE_SHELL_HAVE_RECOVER 1 -#else -#define SQLITE_SHELL_HAVE_RECOVER 0 + +#ifdef __cplusplus +} /* end of the 'extern "C"' block */ #endif -#if SQLITE_SHELL_HAVE_RECOVER + +#endif /* ifndef _SQLITE_RECOVER_H */ + +/************************* End ../ext/recover/sqlite3recover.h ********************/ +# ifndef SQLITE_HAVE_SQLITE3R /************************* Begin ../ext/recover/dbdata.c ******************/ /* ** 2019-04-17 @@ -11488,13 +14318,12 @@ void sqlite3_expert_destroy(sqlite3expert *p){ */ #if !defined(SQLITEINT_H) -/* #include "sqlite3ext.h" */ +/* #include "sqlite3.h" */ /* typedef unsigned char u8; */ /* typedef unsigned int u32; */ #endif -SQLITE_EXTENSION_INIT1 #include #include @@ -11579,6 +14408,10 @@ static int dbdataConnect( DbdataTable *pTab = 0; int rc = sqlite3_declare_vtab(db, pAux ? DBPTR_SCHEMA : DBDATA_SCHEMA); + (void)argc; + (void)argv; + (void)pzErr; + sqlite3_vtab_config(db, SQLITE_VTAB_USES_ALL_SCHEMAS); if( rc==SQLITE_OK ){ pTab = (DbdataTable*)sqlite3_malloc64(sizeof(DbdataTable)); if( pTab==0 ){ @@ -11924,10 +14757,14 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK; rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage); if( rc!=SQLITE_OK ) return rc; - if( pCsr->aPage ) break; + if( pCsr->aPage && pCsr->nPage>=256 ) break; + sqlite3_free(pCsr->aPage); + pCsr->aPage = 0; if( pCsr->bOnePage ) return SQLITE_OK; pCsr->iPgno++; } + + assert( iOff+3+2<=pCsr->nPage ); pCsr->iCell = pTab->bPtr ? -2 : 0; pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]); } @@ -12059,556 +14896,311 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){ iHdr = dbdataGetVarintU32(pCsr->pRec, &nHdr); if( nHdr>nPayload ) nHdr = 0; pCsr->nHdr = nHdr; - pCsr->pHdrPtr = &pCsr->pRec[iHdr]; - pCsr->pPtr = &pCsr->pRec[pCsr->nHdr]; - pCsr->iField = (bHasRowid ? -1 : 0); - } - } - }else{ - pCsr->iField++; - if( pCsr->iField>0 ){ - sqlite3_int64 iType; - if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){ - bNextPage = 1; - }else{ - pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); - pCsr->pPtr += dbdataValueBytes(iType); - } - } - } - - if( bNextPage ){ - sqlite3_free(pCsr->aPage); - sqlite3_free(pCsr->pRec); - pCsr->aPage = 0; - pCsr->pRec = 0; - if( pCsr->bOnePage ) return SQLITE_OK; - pCsr->iPgno++; - }else{ - if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){ - return SQLITE_OK; - } - - /* Advance to the next cell. The next iteration of the loop will load - ** the record and so on. */ - sqlite3_free(pCsr->pRec); - pCsr->pRec = 0; - pCsr->iCell++; - } - } - } - - assert( !"can't get here" ); - return SQLITE_OK; -} - -/* -** Return true if the cursor is at EOF. -*/ -static int dbdataEof(sqlite3_vtab_cursor *pCursor){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - return pCsr->aPage==0; -} - -/* -** Return true if nul-terminated string zSchema ends in "()". Or false -** otherwise. -*/ -static int dbdataIsFunction(const char *zSchema){ - size_t n = strlen(zSchema); - if( n>2 && zSchema[n-2]=='(' && zSchema[n-1]==')' ){ - return (int)n-2; - } - return 0; -} - -/* -** Determine the size in pages of database zSchema (where zSchema is -** "main", "temp" or the name of an attached database) and set -** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise, -** an SQLite error code. -*/ -static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ - DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab; - char *zSql = 0; - int rc, rc2; - int nFunc = 0; - sqlite3_stmt *pStmt = 0; - - if( (nFunc = dbdataIsFunction(zSchema))>0 ){ - zSql = sqlite3_mprintf("SELECT %.*s(0)", nFunc, zSchema); - }else{ - zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); - } - if( zSql==0 ) return SQLITE_NOMEM; - - rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - pCsr->szDb = sqlite3_column_int(pStmt, 0); - } - rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - return rc; -} - -/* -** Attempt to figure out the encoding of the database by retrieving page 1 -** and inspecting the header field. If successful, set the pCsr->enc variable -** and return SQLITE_OK. Otherwise, return an SQLite error code. -*/ -static int dbdataGetEncoding(DbdataCursor *pCsr){ - int rc = SQLITE_OK; - int nPg1 = 0; - u8 *aPg1 = 0; - rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1); - assert( rc!=SQLITE_OK || nPg1==0 || nPg1>=512 ); - if( rc==SQLITE_OK && nPg1>0 ){ - pCsr->enc = get_uint32(&aPg1[56]); - } - sqlite3_free(aPg1); - return rc; -} - - -/* -** xFilter method for sqlite_dbdata and sqlite_dbptr. -*/ -static int dbdataFilter( - sqlite3_vtab_cursor *pCursor, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; - int rc = SQLITE_OK; - const char *zSchema = "main"; - - dbdataResetCursor(pCsr); - assert( pCsr->iPgno==1 ); - if( idxNum & 0x01 ){ - zSchema = (const char*)sqlite3_value_text(argv[0]); - if( zSchema==0 ) zSchema = ""; - } - if( idxNum & 0x02 ){ - pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]); - pCsr->bOnePage = 1; - }else{ - rc = dbdataDbsize(pCsr, zSchema); - } - - if( rc==SQLITE_OK ){ - int nFunc = 0; - if( pTab->pStmt ){ - pCsr->pStmt = pTab->pStmt; - pTab->pStmt = 0; - }else if( (nFunc = dbdataIsFunction(zSchema))>0 ){ - char *zSql = sqlite3_mprintf("SELECT %.*s(?2)", nFunc, zSchema); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); - sqlite3_free(zSql); - } - }else{ - rc = sqlite3_prepare_v2(pTab->db, - "SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1, - &pCsr->pStmt, 0 - ); - } - } - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT); - }else{ - pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); - } - - /* Try to determine the encoding of the db by inspecting the header - ** field on page 1. */ - if( rc==SQLITE_OK ){ - rc = dbdataGetEncoding(pCsr); - } - - if( rc==SQLITE_OK ){ - rc = dbdataNext(pCursor); - } - return rc; -} - -/* -** Return a column for the sqlite_dbdata or sqlite_dbptr table. -*/ -static int dbdataColumn( - sqlite3_vtab_cursor *pCursor, - sqlite3_context *ctx, - int i -){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; - if( pTab->bPtr ){ - switch( i ){ - case DBPTR_COLUMN_PGNO: - sqlite3_result_int64(ctx, pCsr->iPgno); - break; - case DBPTR_COLUMN_CHILD: { - int iOff = pCsr->iPgno==1 ? 100 : 0; - if( pCsr->iCell<0 ){ - iOff += 8; - }else{ - iOff += 12 + pCsr->iCell*2; - if( iOff>pCsr->nPage ) return SQLITE_OK; - iOff = get_uint16(&pCsr->aPage[iOff]); + pCsr->pHdrPtr = &pCsr->pRec[iHdr]; + pCsr->pPtr = &pCsr->pRec[pCsr->nHdr]; + pCsr->iField = (bHasRowid ? -1 : 0); + } } - if( iOff<=pCsr->nPage ){ - sqlite3_result_int64(ctx, get_uint32(&pCsr->aPage[iOff])); + }else{ + pCsr->iField++; + if( pCsr->iField>0 ){ + sqlite3_int64 iType; + if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){ + bNextPage = 1; + }else{ + int szField = 0; + pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); + szField = dbdataValueBytes(iType); + if( (pCsr->nRec - (pCsr->pPtr - pCsr->pRec)) pPtr = &pCsr->pRec[pCsr->nRec]; + }else{ + pCsr->pPtr += szField; + } + } } - break; } - } - }else{ - switch( i ){ - case DBDATA_COLUMN_PGNO: - sqlite3_result_int64(ctx, pCsr->iPgno); - break; - case DBDATA_COLUMN_CELL: - sqlite3_result_int(ctx, pCsr->iCell); - break; - case DBDATA_COLUMN_FIELD: - sqlite3_result_int(ctx, pCsr->iField); - break; - case DBDATA_COLUMN_VALUE: { - if( pCsr->iField<0 ){ - sqlite3_result_int64(ctx, pCsr->iIntkey); - }else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){ - sqlite3_int64 iType; - dbdataGetVarintU32(pCsr->pHdrPtr, &iType); - dbdataValue( - ctx, pCsr->enc, iType, pCsr->pPtr, - &pCsr->pRec[pCsr->nRec] - pCsr->pPtr - ); + + if( bNextPage ){ + sqlite3_free(pCsr->aPage); + sqlite3_free(pCsr->pRec); + pCsr->aPage = 0; + pCsr->pRec = 0; + if( pCsr->bOnePage ) return SQLITE_OK; + pCsr->iPgno++; + }else{ + if( pCsr->iField<0 || pCsr->pHdrPtr<&pCsr->pRec[pCsr->nHdr] ){ + return SQLITE_OK; } - break; + + /* Advance to the next cell. The next iteration of the loop will load + ** the record and so on. */ + sqlite3_free(pCsr->pRec); + pCsr->pRec = 0; + pCsr->iCell++; } } } - return SQLITE_OK; -} -/* -** Return the rowid for an sqlite_dbdata or sqlite_dptr table. -*/ -static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - DbdataCursor *pCsr = (DbdataCursor*)pCursor; - *pRowid = pCsr->iRowid; + assert( !"can't get here" ); return SQLITE_OK; } - -/* -** Invoke this routine to register the "sqlite_dbdata" virtual table module -*/ -static int sqlite3DbdataRegister(sqlite3 *db){ - static sqlite3_module dbdata_module = { - 0, /* iVersion */ - 0, /* xCreate */ - dbdataConnect, /* xConnect */ - dbdataBestIndex, /* xBestIndex */ - dbdataDisconnect, /* xDisconnect */ - 0, /* xDestroy */ - dbdataOpen, /* xOpen - open a cursor */ - dbdataClose, /* xClose - close a cursor */ - dbdataFilter, /* xFilter - configure scan constraints */ - dbdataNext, /* xNext - advance a cursor */ - dbdataEof, /* xEof - check for end of scan */ - dbdataColumn, /* xColumn - read data */ - dbdataRowid, /* xRowid - read data */ - 0, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - 0, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0 /* xShadowName */ - }; - - int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1); - } - return rc; -} - -#ifdef _WIN32 - -#endif -int sqlite3_dbdata_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi); - return sqlite3DbdataRegister(db); -} - -#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ - -/************************* End ../ext/recover/dbdata.c ********************/ -/************************* Begin ../ext/recover/sqlite3recover.h ******************/ -/* -** 2022-08-27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** -** This file contains the public interface to the "recover" extension - -** an SQLite extension designed to recover data from corrupted database -** files. -*/ - -/* -** OVERVIEW: -** -** To use the API to recover data from a corrupted database, an -** application: -** -** 1) Creates an sqlite3_recover handle by calling either -** sqlite3_recover_init() or sqlite3_recover_init_sql(). -** -** 2) Configures the new handle using one or more calls to -** sqlite3_recover_config(). -** -** 3) Executes the recovery by repeatedly calling sqlite3_recover_step() on -** the handle until it returns something other than SQLITE_OK. If it -** returns SQLITE_DONE, then the recovery operation completed without -** error. If it returns some other non-SQLITE_OK value, then an error -** has occurred. -** -** 4) Retrieves any error code and English language error message using the -** sqlite3_recover_errcode() and sqlite3_recover_errmsg() APIs, -** respectively. -** -** 5) Destroys the sqlite3_recover handle and frees all resources -** using sqlite3_recover_finish(). -** -** The application may abandon the recovery operation at any point -** before it is finished by passing the sqlite3_recover handle to -** sqlite3_recover_finish(). This is not an error, but the final state -** of the output database, or the results of running the partial script -** delivered to the SQL callback, are undefined. -*/ - -#ifndef _SQLITE_RECOVER_H -#define _SQLITE_RECOVER_H - -/* #include "sqlite3.h" */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* -** An instance of the sqlite3_recover object represents a recovery -** operation in progress. -** -** Constructors: -** -** sqlite3_recover_init() -** sqlite3_recover_init_sql() -** -** Destructor: -** -** sqlite3_recover_finish() -** -** Methods: -** -** sqlite3_recover_config() -** sqlite3_recover_errcode() -** sqlite3_recover_errmsg() -** sqlite3_recover_run() -** sqlite3_recover_step() -*/ -typedef struct sqlite3_recover sqlite3_recover; - /* -** These two APIs attempt to create and return a new sqlite3_recover object. -** In both cases the first two arguments identify the (possibly -** corrupt) database to recover data from. The first argument is an open -** database handle and the second the name of a database attached to that -** handle (i.e. "main", "temp" or the name of an attached database). -** -** If sqlite3_recover_init() is used to create the new sqlite3_recover -** handle, then data is recovered into a new database, identified by -** string parameter zUri. zUri may be an absolute or relative file path, -** or may be an SQLite URI. If the identified database file already exists, -** it is overwritten. -** -** If sqlite3_recover_init_sql() is invoked, then any recovered data will -** be returned to the user as a series of SQL statements. Executing these -** SQL statements results in the same database as would have been created -** had sqlite3_recover_init() been used. For each SQL statement in the -** output, the callback function passed as the third argument (xSql) is -** invoked once. The first parameter is a passed a copy of the fourth argument -** to this function (pCtx) as its first parameter, and a pointer to a -** nul-terminated buffer containing the SQL statement formated as UTF-8 as -** the second. If the xSql callback returns any value other than SQLITE_OK, -** then processing is immediately abandoned and the value returned used as -** the recover handle error code (see below). -** -** If an out-of-memory error occurs, NULL may be returned instead of -** a valid handle. In all other cases, it is the responsibility of the -** application to avoid resource leaks by ensuring that -** sqlite3_recover_finish() is called on all allocated handles. -*/ -sqlite3_recover *sqlite3_recover_init( - sqlite3* db, - const char *zDb, - const char *zUri -); -sqlite3_recover *sqlite3_recover_init_sql( - sqlite3* db, - const char *zDb, - int (*xSql)(void*, const char*), - void *pCtx -); - -/* -** Configure an sqlite3_recover object that has just been created using -** sqlite3_recover_init() or sqlite3_recover_init_sql(). This function -** may only be called before the first call to sqlite3_recover_step() -** or sqlite3_recover_run() on the object. -** -** The second argument passed to this function must be one of the -** SQLITE_RECOVER_* symbols defined below. Valid values for the third argument -** depend on the specific SQLITE_RECOVER_* symbol in use. -** -** SQLITE_OK is returned if the configuration operation was successful, -** or an SQLite error code otherwise. +** Return true if the cursor is at EOF. */ -int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); +static int dbdataEof(sqlite3_vtab_cursor *pCursor){ + DbdataCursor *pCsr = (DbdataCursor*)pCursor; + return pCsr->aPage==0; +} /* -** SQLITE_RECOVER_LOST_AND_FOUND: -** The pArg argument points to a string buffer containing the name -** of a "lost-and-found" table in the output database, or NULL. If -** the argument is non-NULL and the database contains seemingly -** valid pages that cannot be associated with any table in the -** recovered part of the schema, data is extracted from these -** pages to add to the lost-and-found table. -** -** SQLITE_RECOVER_FREELIST_CORRUPT: -** The pArg value must actually be a pointer to a value of type -** int containing value 0 or 1 cast as a (void*). If this option is set -** (argument is 1) and a lost-and-found table has been configured using -** SQLITE_RECOVER_LOST_AND_FOUND, then is assumed that the freelist is -** corrupt and an attempt is made to recover records from pages that -** appear to be linked into the freelist. Otherwise, pages on the freelist -** are ignored. Setting this option can recover more data from the -** database, but often ends up "recovering" deleted records. The default -** value is 0 (clear). -** -** SQLITE_RECOVER_ROWIDS: -** The pArg value must actually be a pointer to a value of type -** int containing value 0 or 1 cast as a (void*). If this option is set -** (argument is 1), then an attempt is made to recover rowid values -** that are not also INTEGER PRIMARY KEY values. If this option is -** clear, then new rowids are assigned to all recovered rows. The -** default value is 1 (set). -** -** SQLITE_RECOVER_SLOWINDEXES: -** The pArg value must actually be a pointer to a value of type -** int containing value 0 or 1 cast as a (void*). If this option is clear -** (argument is 0), then when creating an output database, the recover -** module creates and populates non-UNIQUE indexes right at the end of the -** recovery operation - after all recoverable data has been inserted -** into the new database. This is faster overall, but means that the -** final call to sqlite3_recover_step() for a recovery operation may -** be need to create a large number of indexes, which may be very slow. -** -** Or, if this option is set (argument is 1), then non-UNIQUE indexes -** are created in the output database before it is populated with -** recovered data. This is slower overall, but avoids the slow call -** to sqlite3_recover_step() at the end of the recovery operation. -** -** The default option value is 0. +** Return true if nul-terminated string zSchema ends in "()". Or false +** otherwise. */ -#define SQLITE_RECOVER_LOST_AND_FOUND 1 -#define SQLITE_RECOVER_FREELIST_CORRUPT 2 -#define SQLITE_RECOVER_ROWIDS 3 -#define SQLITE_RECOVER_SLOWINDEXES 4 +static int dbdataIsFunction(const char *zSchema){ + size_t n = strlen(zSchema); + if( n>2 && zSchema[n-2]=='(' && zSchema[n-1]==')' ){ + return (int)n-2; + } + return 0; +} + +/* +** Determine the size in pages of database zSchema (where zSchema is +** "main", "temp" or the name of an attached database) and set +** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise, +** an SQLite error code. +*/ +static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ + DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab; + char *zSql = 0; + int rc, rc2; + int nFunc = 0; + sqlite3_stmt *pStmt = 0; + + if( (nFunc = dbdataIsFunction(zSchema))>0 ){ + zSql = sqlite3_mprintf("SELECT %.*s(0)", nFunc, zSchema); + }else{ + zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); + } + if( zSql==0 ) return SQLITE_NOMEM; + + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ + pCsr->szDb = sqlite3_column_int(pStmt, 0); + } + rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) rc = rc2; + return rc; +} /* -** Perform a unit of work towards the recovery operation. This function -** must normally be called multiple times to complete database recovery. -** -** If no error occurs but the recovery operation is not completed, this -** function returns SQLITE_OK. If recovery has been completed successfully -** then SQLITE_DONE is returned. If an error has occurred, then an SQLite -** error code (e.g. SQLITE_IOERR or SQLITE_NOMEM) is returned. It is not -** considered an error if some or all of the data cannot be recovered -** due to database corruption. -** -** Once sqlite3_recover_step() has returned a value other than SQLITE_OK, -** all further such calls on the same recover handle are no-ops that return -** the same non-SQLITE_OK value. +** Attempt to figure out the encoding of the database by retrieving page 1 +** and inspecting the header field. If successful, set the pCsr->enc variable +** and return SQLITE_OK. Otherwise, return an SQLite error code. */ -int sqlite3_recover_step(sqlite3_recover*); +static int dbdataGetEncoding(DbdataCursor *pCsr){ + int rc = SQLITE_OK; + int nPg1 = 0; + u8 *aPg1 = 0; + rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1); + if( rc==SQLITE_OK && nPg1>=(56+4) ){ + pCsr->enc = get_uint32(&aPg1[56]); + } + sqlite3_free(aPg1); + return rc; +} + /* -** Run the recovery operation to completion. Return SQLITE_OK if successful, -** or an SQLite error code otherwise. Calling this function is the same -** as executing: -** -** while( SQLITE_OK==sqlite3_recover_step(p) ); -** return sqlite3_recover_errcode(p); +** xFilter method for sqlite_dbdata and sqlite_dbptr. */ -int sqlite3_recover_run(sqlite3_recover*); +static int dbdataFilter( + sqlite3_vtab_cursor *pCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + DbdataCursor *pCsr = (DbdataCursor*)pCursor; + DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; + int rc = SQLITE_OK; + const char *zSchema = "main"; + (void)idxStr; + (void)argc; + + dbdataResetCursor(pCsr); + assert( pCsr->iPgno==1 ); + if( idxNum & 0x01 ){ + zSchema = (const char*)sqlite3_value_text(argv[0]); + if( zSchema==0 ) zSchema = ""; + } + if( idxNum & 0x02 ){ + pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]); + pCsr->bOnePage = 1; + }else{ + rc = dbdataDbsize(pCsr, zSchema); + } + + if( rc==SQLITE_OK ){ + int nFunc = 0; + if( pTab->pStmt ){ + pCsr->pStmt = pTab->pStmt; + pTab->pStmt = 0; + }else if( (nFunc = dbdataIsFunction(zSchema))>0 ){ + char *zSql = sqlite3_mprintf("SELECT %.*s(?2)", nFunc, zSchema); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + } + }else{ + rc = sqlite3_prepare_v2(pTab->db, + "SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1, + &pCsr->pStmt, 0 + ); + } + } + if( rc==SQLITE_OK ){ + rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT); + } + + /* Try to determine the encoding of the db by inspecting the header + ** field on page 1. */ + if( rc==SQLITE_OK ){ + rc = dbdataGetEncoding(pCsr); + } + + if( rc!=SQLITE_OK ){ + pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); + } + + if( rc==SQLITE_OK ){ + rc = dbdataNext(pCursor); + } + return rc; +} /* -** If an error has been encountered during a prior call to -** sqlite3_recover_step(), then this function attempts to return a -** pointer to a buffer containing an English language explanation of -** the error. If no error message is available, or if an out-of memory -** error occurs while attempting to allocate a buffer in which to format -** the error message, NULL is returned. -** -** The returned buffer remains valid until the sqlite3_recover handle is -** destroyed using sqlite3_recover_finish(). +** Return a column for the sqlite_dbdata or sqlite_dbptr table. */ -const char *sqlite3_recover_errmsg(sqlite3_recover*); +static int dbdataColumn( + sqlite3_vtab_cursor *pCursor, + sqlite3_context *ctx, + int i +){ + DbdataCursor *pCsr = (DbdataCursor*)pCursor; + DbdataTable *pTab = (DbdataTable*)pCursor->pVtab; + if( pTab->bPtr ){ + switch( i ){ + case DBPTR_COLUMN_PGNO: + sqlite3_result_int64(ctx, pCsr->iPgno); + break; + case DBPTR_COLUMN_CHILD: { + int iOff = pCsr->iPgno==1 ? 100 : 0; + if( pCsr->iCell<0 ){ + iOff += 8; + }else{ + iOff += 12 + pCsr->iCell*2; + if( iOff>pCsr->nPage ) return SQLITE_OK; + iOff = get_uint16(&pCsr->aPage[iOff]); + } + if( iOff<=pCsr->nPage ){ + sqlite3_result_int64(ctx, get_uint32(&pCsr->aPage[iOff])); + } + break; + } + } + }else{ + switch( i ){ + case DBDATA_COLUMN_PGNO: + sqlite3_result_int64(ctx, pCsr->iPgno); + break; + case DBDATA_COLUMN_CELL: + sqlite3_result_int(ctx, pCsr->iCell); + break; + case DBDATA_COLUMN_FIELD: + sqlite3_result_int(ctx, pCsr->iField); + break; + case DBDATA_COLUMN_VALUE: { + if( pCsr->iField<0 ){ + sqlite3_result_int64(ctx, pCsr->iIntkey); + }else if( &pCsr->pRec[pCsr->nRec] >= pCsr->pPtr ){ + sqlite3_int64 iType; + dbdataGetVarintU32(pCsr->pHdrPtr, &iType); + dbdataValue( + ctx, pCsr->enc, iType, pCsr->pPtr, + &pCsr->pRec[pCsr->nRec] - pCsr->pPtr + ); + } + break; + } + } + } + return SQLITE_OK; +} -/* -** If this function is called on an sqlite3_recover handle after -** an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK. +/* +** Return the rowid for an sqlite_dbdata or sqlite_dptr table. */ -int sqlite3_recover_errcode(sqlite3_recover*); +static int dbdataRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + DbdataCursor *pCsr = (DbdataCursor*)pCursor; + *pRowid = pCsr->iRowid; + return SQLITE_OK; +} -/* -** Clean up a recovery object created by a call to sqlite3_recover_init(). -** The results of using a recovery object with any API after it has been -** passed to this function are undefined. -** -** This function returns the same value as sqlite3_recover_errcode(). + +/* +** Invoke this routine to register the "sqlite_dbdata" virtual table module */ -int sqlite3_recover_finish(sqlite3_recover*); +static int sqlite3DbdataRegister(sqlite3 *db){ + static sqlite3_module dbdata_module = { + 0, /* iVersion */ + 0, /* xCreate */ + dbdataConnect, /* xConnect */ + dbdataBestIndex, /* xBestIndex */ + dbdataDisconnect, /* xDisconnect */ + 0, /* xDestroy */ + dbdataOpen, /* xOpen - open a cursor */ + dbdataClose, /* xClose - close a cursor */ + dbdataFilter, /* xFilter - configure scan constraints */ + dbdataNext, /* xNext - advance a cursor */ + dbdataEof, /* xEof - check for end of scan */ + dbdataColumn, /* xColumn - read data */ + dbdataRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ + 0 /* xIntegrity */ + }; + int rc = sqlite3_create_module(db, "sqlite_dbdata", &dbdata_module, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1); + } + return rc; +} -#ifdef __cplusplus -} /* end of the 'extern "C"' block */ -#endif +int sqlite3_dbdata_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + (void)pzErrMsg; + return sqlite3DbdataRegister(db); +} -#endif /* ifndef _SQLITE_RECOVER_H */ +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ -/************************* End ../ext/recover/sqlite3recover.h ********************/ +/************************* End ../ext/recover/dbdata.c ********************/ /************************* Begin ../ext/recover/sqlite3recover.c ******************/ /* ** 2022-08-27 @@ -13373,6 +15965,7 @@ static void recoverEscapeCrnl( sqlite3_value **argv ){ const char *zText = (const char*)sqlite3_value_text(argv[0]); + (void)argc; if( zText && zText[0]=='\'' ){ int nText = sqlite3_value_bytes(argv[0]); int i; @@ -13525,7 +16118,7 @@ static void recoverTransferSettings(sqlite3_recover *p){ return; } - for(ii=0; ii dbIn, "PRAGMA %Q.%s", p->zDb, zPrag); @@ -13603,7 +16196,9 @@ static int recoverOpenOutput(sqlite3_recover *p){ } /* Register the custom user-functions with the output handle. */ - for(ii=0; p->errCode==SQLITE_OK && ii errCode==SQLITE_OK && ii<(int)(sizeof(aFunc)/sizeof(aFunc[0])); + ii++){ p->errCode = sqlite3_create_function(db, aFunc[ii].zName, aFunc[ii].nArg, SQLITE_UTF8, (void*)p, aFunc[ii].xFunc, 0, 0 ); @@ -13715,7 +16310,7 @@ static void recoverAddTable( int iField = sqlite3_column_int(pStmt, 0); int iCol = sqlite3_column_int(pStmt, 1); - assert( iField nCol && iCol nCol ); + assert( iCol nCol ); pNew->aCol[iCol].iField = iField; pNew->bIntkey = 0; @@ -14712,7 +17307,7 @@ static int recoverIsValidPage(u8 *aTmp, const u8 *a, int n){ if( iFree>(n-4) ) return 0; iNext = recoverGetU16(&a[iFree]); nByte = recoverGetU16(&a[iFree+2]); - if( iFree+nByte>n ) return 0; + if( iFree+nByte>n || nByte<4 ) return 0; if( iNext && iNext pLog==0 ) return; - utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); + sputf(p->pLog, "(%d) %s\n", iErrCode, zMsg); fflush(p->pLog); } @@ -15744,9 +18351,9 @@ static void shellPutsFunc( int nVal, sqlite3_value **apVal ){ - ShellState *p = (ShellState*)sqlite3_user_data(pCtx); + /* Unused: (ShellState*)sqlite3_user_data(pCtx); */ (void)nVal; - utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); + oputf("%s\n", sqlite3_value_text(apVal[0])); sqlite3_result_value(pCtx, apVal[0]); } @@ -15765,8 +18372,7 @@ static void failIfSafeMode( va_start(ap, zErrMsg); zMsg = sqlite3_vmprintf(zErrMsg, ap); va_end(ap); - raw_printf(stderr, "line %d: ", p->lineno); - utf8_printf(stderr, "%s\n", zMsg); + eputf("line %d: %s\n", p->lineno, zMsg); exit(1); } } @@ -15894,13 +18500,14 @@ static void editFunc( }else{ /* If the file did not originally contain \r\n then convert any new ** \r\n back into \n */ + p[sz] = 0; for(i=j=0; i z ) oputb(z, (int)(pcEnd-z)); + if( (c = *pcEnd)==0 ) break; + ++pcEnd; + switch( c ){ + case '\\': case '"': + cbsSay = (char)c; + break; + case '\t': cbsSay = 't'; break; + case '\n': cbsSay = 'n'; break; + case '\r': cbsSay = 'r'; break; + case '\f': cbsSay = 'f'; break; + default: cbsSay = 0; break; + } + if( cbsSay ){ + ace[1] = cbsSay; + oputz(ace); }else if( !isprint(c&0xff) ){ - raw_printf(out, "\\%03o", c&0xff); + oputf("\\%03o", c&0xff); }else{ - fputc(c, out); + ace[1] = (char)c; + oputz(ace+1); } + z = pcEnd; } - fputc('"', out); + oputz(zq); } /* ** Output the given string as a quoted according to JSON quoting rules. */ -static void output_json_string(FILE *out, const char *z, i64 n){ - unsigned int c; - if( n<0 ) n = strlen(z); - fputc('"', out); - while( n-- ){ +static void output_json_string(const char *z, i64 n){ + char c; + static const char *zq = "\""; + static long ctrlMask = ~0L; + static const char *zDQBS = "\"\\"; + const char *pcLimit; + char ace[3] = "\\?"; + char cbsSay; + + if( z==0 ) z = ""; + pcLimit = z + ((n<0)? strlen(z) : (size_t)n); + oputz(zq); + while( z < pcLimit ){ + const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); + const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); + const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; + if( pcEnd > z ){ + oputb(z, (int)(pcEnd-z)); + z = pcEnd; + } + if( z >= pcLimit ) break; c = *(z++); - if( c=='\\' || c=='"' ){ - fputc('\\', out); - fputc(c, out); + switch( c ){ + case '"': case '\\': + cbsSay = (char)c; + break; + case '\b': cbsSay = 'b'; break; + case '\f': cbsSay = 'f'; break; + case '\n': cbsSay = 'n'; break; + case '\r': cbsSay = 'r'; break; + case '\t': cbsSay = 't'; break; + default: cbsSay = 0; break; + } + if( cbsSay ){ + ace[1] = cbsSay; + oputz(ace); }else if( c<=0x1f ){ - fputc('\\', out); - if( c=='\b' ){ - fputc('b', out); - }else if( c=='\f' ){ - fputc('f', out); - }else if( c=='\n' ){ - fputc('n', out); - }else if( c=='\r' ){ - fputc('r', out); - }else if( c=='\t' ){ - fputc('t', out); - }else{ - raw_printf(out, "u%04x",c); - } + oputf("u%04x", c); }else{ - fputc(c, out); + ace[1] = (char)c; + oputz(ace+1); } } - fputc('"', out); + oputz(zq); } /* ** Output the given string with characters that are special to ** HTML escaped. */ -static void output_html_string(FILE *out, const char *z){ +static void output_html_string(const char *z){ int i; if( z==0 ) z = ""; while( *z ){ @@ -16158,18 +18823,18 @@ static void output_html_string(FILE *out, const char *z){ && z[i]!='\''; i++){} if( i>0 ){ - utf8_printf(out,"%.*s",i,z); + oputf("%.*s",i,z); } if( z[i]=='<' ){ - raw_printf(out,"<"); + oputz("<"); }else if( z[i]=='&' ){ - raw_printf(out,"&"); + oputz("&"); }else if( z[i]=='>' ){ - raw_printf(out,">"); + oputz(">"); }else if( z[i]=='\"' ){ - raw_printf(out,"""); + oputz("""); }else if( z[i]=='\'' ){ - raw_printf(out,"'"); + oputz("'"); }else{ break; } @@ -16207,9 +18872,8 @@ static const char needCsvQuote[] = { ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ - FILE *out = p->out; if( z==0 ){ - utf8_printf(out,"%s",p->nullValue); + oputf("%s",p->nullValue); }else{ unsigned i; for(i=0; z[i]; i++){ @@ -16221,14 +18885,14 @@ static void output_csv(ShellState *p, const char *z, int bSep){ if( i==0 || strstr(z, p->colSeparator)!=0 ){ char *zQuoted = sqlite3_mprintf("\"%w\"", z); shell_check_oom(zQuoted); - utf8_printf(out, "%s", zQuoted); + oputz(zQuoted); sqlite3_free(zQuoted); }else{ - utf8_printf(out, "%s", z); + oputz(z); } } if( bSep ){ - utf8_printf(p->out, "%s", p->colSeparator); + oputz(p->colSeparator); } } @@ -16237,8 +18901,7 @@ static void output_csv(ShellState *p, const char *z, int bSep){ */ static void interrupt_handler(int NotUsed){ UNUSED_PARAMETER(NotUsed); - seenInterrupt++; - if( seenInterrupt>2 ) exit(1); + if( ++seenInterrupt>1 ) exit(1); if( globalDb ) sqlite3_interrupt(globalDb); } @@ -16337,16 +19000,16 @@ static int shellAuth( az[1] = zA2; az[2] = zA3; az[3] = zA4; - utf8_printf(p->out, "authorizer: %s", azAction[op]); + oputf("authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - raw_printf(p->out, " "); + oputz(" "); if( az[i] ){ - output_c_string(p->out, az[i]); + output_c_string(az[i]); }else{ - raw_printf(p->out, "NULL"); + oputz("NULL"); } } - raw_printf(p->out, "\n"); + oputz("\n"); if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } @@ -16362,7 +19025,7 @@ static int shellAuth( ** sqlite3_complete() returns false, try to terminate the comment before ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c */ -static void printSchemaLine(FILE *out, const char *z, const char *zTail){ +static void printSchemaLine(const char *z, const char *zTail){ char *zToFree = 0; if( z==0 ) return; if( zTail==0 ) return; @@ -16372,6 +19035,7 @@ static void printSchemaLine(FILE *out, const char *z, const char *zTail){ int i; for(i=0; i autoEQPtest ){ - utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); + oputf("%d,%d,%s\n", iEqpId, p2, zText); } pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); shell_check_oom(pNew); @@ -16468,8 +19132,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){ for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ pNext = eqp_next_row(p, iEqpId, pRow); z = pRow->zText; - utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, - pNext ? "|--" : "`--", z); + oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); eqp_render_level(p, pRow->iEqpId); @@ -16481,7 +19144,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){ /* ** Display and reset the EXPLAIN QUERY PLAN data */ -static void eqp_render(ShellState *p){ +static void eqp_render(ShellState *p, i64 nCycle){ EQPGraphRow *pRow = p->sGraph.pRow; if( pRow ){ if( pRow->zText[0]=='-' ){ @@ -16489,11 +19152,13 @@ static void eqp_render(ShellState *p){ eqp_reset(p); return; } - utf8_printf(p->out, "%s\n", pRow->zText+3); + oputf("%s\n", pRow->zText+3); p->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); + }else if( nCycle>0 ){ + oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle); }else{ - utf8_printf(p->out, "QUERY PLAN\n"); + oputz("QUERY PLAN\n"); } p->sGraph.zPrefix[0] = 0; eqp_render_level(p, 0); @@ -16509,13 +19174,13 @@ static int progress_handler(void *pClientData) { ShellState *p = (ShellState*)pClientData; p->nProgress++; if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); + oputf("Progress limit reached (%u)\n", p->nProgress); if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; return 1; } if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - raw_printf(p->out, "Progress %u\n", p->nProgress); + oputf("Progress %u\n", p->nProgress); } return 0; } @@ -16524,14 +19189,14 @@ static int progress_handler(void *pClientData) { /* ** Print N dashes */ -static void print_dashes(FILE *out, int N){ +static void print_dashes(int N){ const char zDash[] = "--------------------------------------------------"; const int nDash = sizeof(zDash) - 1; while( N>nDash ){ - fputs(zDash, out); + oputz(zDash); N -= nDash; } - raw_printf(out, "%.*s", N, zDash); + oputf("%.*s", N, zDash); } /* @@ -16544,15 +19209,15 @@ static void print_row_separator( ){ int i; if( nArg>0 ){ - fputs(zSep, p->out); - print_dashes(p->out, p->actualWidth[0]+2); + oputz(zSep); + print_dashes(p->actualWidth[0]+2); for(i=1; i out); - print_dashes(p->out, p->actualWidth[i]+2); + oputz(zSep); + print_dashes(p->actualWidth[i]+2); } - fputs(zSep, p->out); + oputz(zSep); } - fputs("\n", p->out); + oputz("\n"); } /* @@ -16582,50 +19247,70 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); + if( p->cnt++>0 ) oputz(p->rowSeparator); for(i=0; i out,"%*s = %s%s", w, azCol[i], - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); + oputf("%*s = %s%s", w, azCol[i], + azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } + case MODE_ScanExp: case MODE_Explain: { - static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; - if( nArg>ArraySize(aExplainWidth) ){ - nArg = ArraySize(aExplainWidth); + static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; + static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 }; + static const int aScanExpWidth[] = {4, 6, 6, 13, 4, 4, 4, 13, 2, 13}; + static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 }; + + const int *aWidth = aExplainWidth; + const int *aMap = aExplainMap; + int nWidth = ArraySize(aExplainWidth); + int iIndent = 1; + + if( p->cMode==MODE_ScanExp ){ + aWidth = aScanExpWidth; + aMap = aScanExpMap; + nWidth = ArraySize(aScanExpWidth); + iIndent = 3; } + if( nArg>nWidth ) nArg = nWidth; + + /* If this is the first row seen, print out the headers */ if( p->cnt++==0 ){ for(i=0; i out, w, azCol[i]); - fputs(i==nArg-1 ? "\n" : " ", p->out); + utf8_width_print(aWidth[i], azCol[ aMap[i] ]); + oputz(i==nArg-1 ? "\n" : " "); } for(i=0; i out, w); - fputs(i==nArg-1 ? "\n" : " ", p->out); + print_dashes(aWidth[i]); + oputz(i==nArg-1 ? "\n" : " "); } } + + /* If there is no data, exit early. */ if( azArg==0 ) break; + for(i=0; i w ){ - w = strlenChar(azArg[i]); + if( zVal && strlenChar(zVal)>w ){ + w = strlenChar(zVal); + zSep = " "; } - if( i==1 && p->aiIndent && p->pStmt ){ + if( i==iIndent && p->aiIndent && p->pStmt ){ if( p->iIndent nIndent ){ - utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); + oputf("%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } - utf8_width_print(p->out, w, azArg[i] ? azArg[i] : p->nullValue); - fputs(i==nArg-1 ? "\n" : " ", p->out); + utf8_width_print(w, zVal ? zVal : p->nullValue); + oputz(i==nArg-1 ? "\n" : zSep); } break; } case MODE_Semi: { /* .schema and .fullschema output */ - printSchemaLine(p->out, azArg[0], ";\n"); + printSchemaLine(azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ @@ -16640,7 +19325,7 @@ static int shell_callback( if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 ){ - utf8_printf(p->out, "%s;\n", azArg[0]); + oputf("%s;\n", azArg[0]); break; } z = sqlite3_mprintf("%s", azArg[0]); @@ -16673,7 +19358,7 @@ static int shell_callback( }else if( c==')' ){ nParen--; if( nLine>0 && nParen==0 && j>0 ){ - printSchemaLineN(p->out, z, j, "\n"); + printSchemaLineN(z, j, "\n"); j = 0; } } @@ -16682,7 +19367,7 @@ static int shell_callback( && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) ){ if( c=='\n' ) j--; - printSchemaLineN(p->out, z, j, "\n "); + printSchemaLineN(z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } @@ -16690,64 +19375,59 @@ static int shell_callback( } z[j] = 0; } - printSchemaLine(p->out, z, ";\n"); + printSchemaLine(z, ";\n"); sqlite3_free(z); break; } case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i out,"%s%s",azCol[i], - i==nArg-1 ? p->rowSeparator : p->colSeparator); + oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; i nullValue; - utf8_printf(p->out, "%s", z); - if( i out, "%s", p->colSeparator); - }else{ - utf8_printf(p->out, "%s", p->rowSeparator); - } + oputz(z); + oputz((i colSeparator : p->rowSeparator); } break; } case MODE_Html: { if( p->cnt++==0 && p->showHeader ){ - raw_printf(p->out," "); + oputz(" "); for(i=0; i \n"); + oputz("\n"); } if( azArg==0 ) break; - raw_printf(p->out,"out," "); - output_html_string(p->out, azCol[i]); - raw_printf(p->out," \n"); + oputz(""); + output_html_string(azCol[i]); + oputz(" \n"); } - raw_printf(p->out,""); + oputz(" "); for(i=0; i \n"); + oputz("\n"); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout," "); - output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); - raw_printf(p->out," \n"); + oputz(""); + output_html_string(azArg[i] ? azArg[i] : p->nullValue); + oputz(" \n"); } - raw_printf(p->out,"out,azCol[i] ? azCol[i] : ""); - if(i out, "%s", p->colSeparator); + output_c_string(azCol[i] ? azCol[i] : ""); + if(i colSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + oputz(p->rowSeparator); } if( azArg==0 ) break; for(i=0; i out, azArg[i] ? azArg[i] : p->nullValue); - if(i out, "%s", p->colSeparator); + output_c_string(azArg[i] ? azArg[i] : p->nullValue); + if(i colSeparator); } - utf8_printf(p->out, "%s", p->rowSeparator); + oputz(p->rowSeparator); break; } case MODE_Csv: { @@ -16756,57 +19436,57 @@ static int shell_callback( for(i=0; i out, "%s", p->rowSeparator); + oputz(p->rowSeparator); } if( nArg>0 ){ for(i=0; i out, "%s", p->rowSeparator); + oputz(p->rowSeparator); } setTextMode(p->out, 1); break; } case MODE_Insert: { if( azArg==0 ) break; - utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); + oputf("INSERT INTO %s",p->zDestTable); if( p->showHeader ){ - raw_printf(p->out,"("); + oputz("("); for(i=0; i 0 ) raw_printf(p->out, ","); + if( i>0 ) oputz(","); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); - utf8_printf(p->out, "%s", z); + oputz(z); sqlite3_free(z); }else{ - raw_printf(p->out, "%s", azCol[i]); + oputf("%s", azCol[i]); } } - raw_printf(p->out,")"); + oputz(")"); } p->cnt++; for(i=0; i out, i>0 ? "," : " VALUES("); + oputz(i>0 ? "," : " VALUES("); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + oputz("NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + oputz(azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); + oputz("9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); + oputz("-9.0e+999"); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -16814,21 +19494,21 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - raw_printf(p->out, "%s", z); + oputz(z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + output_hex_blob(pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); + oputz(azArg[i]); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else{ - output_quoted_escaped_string(p->out, azArg[i]); + output_quoted_escaped_string(azArg[i]); } } - raw_printf(p->out,");\n"); + oputz(");\n"); break; } case MODE_Json: { @@ -16840,37 +19520,37 @@ static int shell_callback( } p->cnt++; for(i=0; i out, azCol[i], -1); - putc(':', p->out); + output_json_string(azCol[i], -1); + oputz(":"); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - fputs("null",p->out); + oputz("null"); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - raw_printf(p->out, "1e999"); + oputz("9.0e+999"); }else if( ur==0xfff0000000000000LL ){ - raw_printf(p->out, "-1e999"); + oputz("-9.0e+999"); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); + oputz(z); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_json_string(p->out, pBlob, nBlob); + output_json_string(pBlob, nBlob); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_json_string(p->out, azArg[i], -1); + output_json_string(azArg[i], -1); }else{ - utf8_printf(p->out,"%s", azArg[i]); + oputz(azArg[i]); } if( i out); + oputz(","); } } - putc('}', p->out); + oputz("}"); break; } case MODE_Quote: { @@ -16878,7 +19558,7 @@ static int shell_callback( if( p->cnt==0 && p->showHeader ){ for(i=0; i 0 ) fputs(p->colSeparator, p->out); - output_quoted_string(p->out, azCol[i]); + output_quoted_string(azCol[i]); } fputs(p->rowSeparator, p->out); } @@ -16886,24 +19566,24 @@ static int shell_callback( for(i=0; i 0 ) fputs(p->colSeparator, p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - utf8_printf(p->out,"NULL"); + oputz("NULL"); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - utf8_printf(p->out,"%s", azArg[i]); + oputz(azArg[i]); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - raw_printf(p->out, "%s", z); + oputz(z); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(p->out, pBlob, nBlob); + output_hex_blob(pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - utf8_printf(p->out,"%s", azArg[i]); + oputz(azArg[i]); }else{ - output_quoted_string(p->out, azArg[i]); + output_quoted_string(azArg[i]); } } fputs(p->rowSeparator, p->out); @@ -16912,17 +19592,17 @@ static int shell_callback( case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i 0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); + if( i>0 ) oputz(p->colSeparator); + oputz(azCol[i] ? azCol[i] : ""); } - utf8_printf(p->out, "%s", p->rowSeparator); + oputz(p->rowSeparator); } if( azArg==0 ) break; for(i=0; i 0 ) utf8_printf(p->out, "%s", p->colSeparator); - utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) oputz(p->colSeparator); + oputz(azArg[i] ? azArg[i] : p->nullValue); } - utf8_printf(p->out, "%s", p->rowSeparator); + oputz(p->rowSeparator); break; } case MODE_EQP: { @@ -17001,7 +19681,7 @@ static void createSelftestTable(ShellState *p){ "DROP TABLE [_shell$self];" ,0,0,&zErrMsg); if( zErrMsg ){ - utf8_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); + eputf("SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); @@ -17056,6 +19736,7 @@ static char *shell_error_context(const char *zSql, sqlite3 *db){ if( db==0 || zSql==0 || (iOffset = sqlite3_error_offset(db))<0 + || iOffset>=(int)strlen(zSql) ){ return sqlite3_mprintf(""); } @@ -17067,15 +19748,15 @@ static char *shell_error_context(const char *zSql, sqlite3 *db){ len = strlen(zSql); if( len>78 ){ len = 78; - while( (zSql[len]&0xc0)==0x80 ) len--; + while( len>0 && (zSql[len]&0xc0)==0x80 ) len--; } zCode = sqlite3_mprintf("%.*s", len, zSql); shell_check_oom(zCode); for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; } if( iOffset<25 ){ - zMsg = sqlite3_mprintf("\n %z\n %*s^--- error here", zCode, iOffset, ""); + zMsg = sqlite3_mprintf("\n %z\n %*s^--- error here", zCode,iOffset,""); }else{ - zMsg = sqlite3_mprintf("\n %z\n %*serror here ---^", zCode, iOffset-14, ""); + zMsg = sqlite3_mprintf("\n %z\n %*serror here ---^", zCode,iOffset-14,""); } return zMsg; } @@ -17103,8 +19784,8 @@ static int run_table_dump_query( rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ char *zContext = shell_error_context(zSelect, p->db); - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc, - sqlite3_errmsg(p->db), zContext); + oputf("/**** ERROR: (%d) %s *****/\n%s", + rc, sqlite3_errmsg(p->db), zContext); sqlite3_free(zContext); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; return rc; @@ -17113,23 +19794,22 @@ static int run_table_dump_query( nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ z = (const char*)sqlite3_column_text(pSelect, 0); - utf8_printf(p->out, "%s", z); + oputf("%s", z); for(i=1; i out, ",%s", sqlite3_column_text(pSelect, i)); + oputf(",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - raw_printf(p->out, "\n;\n"); + oputz("\n;\n"); }else{ - raw_printf(p->out, ";\n"); + oputz(";\n"); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ - utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, - sqlite3_errmsg(p->db)); + oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } return rc; @@ -17140,7 +19820,7 @@ static int run_table_dump_query( */ static char *save_err_msg( sqlite3 *db, /* Database to query */ - const char *zPhase, /* When the error occcurs */ + const char *zPhase, /* When the error occurs */ int rc, /* Error code returned from API */ const char *zSql /* SQL string, or NULL */ ){ @@ -17165,7 +19845,7 @@ static char *save_err_msg( /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ -static void displayLinuxIoStats(FILE *out){ +static void displayLinuxIoStats(void){ FILE *in; char z[200]; sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); @@ -17188,7 +19868,7 @@ static void displayLinuxIoStats(FILE *out){ for(i=0; i out, "%-36s %s\n", zLabel, zLine); + oputf("%-36s %s\n", zLabel, zLine); } /* @@ -17233,58 +19912,56 @@ static int display_stats( ){ int iCur; int iHiwtr; - FILE *out; if( pArg==0 || pArg->out==0 ) return 0; - out = pArg->out; if( pArg->pStmt && pArg->statsOn==2 ){ int nCol, i, x; sqlite3_stmt *pStmt = pArg->pStmt; char z[100]; nCol = sqlite3_column_count(pStmt); - raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol); + oputf("%-36s %d\n", "Number of output columns:", nCol); for(i=0; i statsOn==3 ){ if( pArg->pStmt ){ - iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "VM-steps: %d\n", iCur); + iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); + oputf("VM-steps: %d\n", iCur); } return 0; } - displayStatLine(pArg, "Memory Used:", + displayStatLine("Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine(pArg, "Number of Outstanding Allocations:", + displayStatLine("Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine(pArg, "Number of Pcache Pages Used:", + displayStatLine("Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } - displayStatLine(pArg, "Number of Pcache Overflow Bytes:", + displayStatLine("Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine(pArg, "Largest Allocation:", + displayStatLine("Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine(pArg, "Largest Pcache Allocation:", + displayStatLine("Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH - displayStatLine(pArg, "Deepest Parser Stack:", + displayStatLine("Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif @@ -17293,75 +19970,68 @@ static int display_stats( iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, - "Lookaside Slots Used: %d (max %d)\n", - iCur, iHiwtr); + oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Successful lookaside attempts: %d\n", - iHiwtr); + oputf("Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to size: %d\n", - iHiwtr); + oputf("Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n", - iHiwtr); + oputf("Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n", - iCur); + oputf("Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache hits: %d\n", iCur); + oputf("Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache misses: %d\n", iCur); + oputf("Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache writes: %d\n", iCur); + oputf("Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - raw_printf(pArg->out, "Page cache spills: %d\n", iCur); + oputf("Page cache spills: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n", - iCur); + oputf("Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n", - iCur); + oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg->pStmt ){ int iHit, iMiss; iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); - raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur); + oputf("Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - raw_printf(pArg->out, "Sort Operations: %d\n", iCur); + oputf("Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur); - iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); - iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); + oputf("Autoindex Inserts: %d\n", iCur); + iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, + bReset); + iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, + bReset); if( iHit || iMiss ){ - raw_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n", - iHit, iHit+iMiss); + oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); } iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur); + oputf("Virtual Machine Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - raw_printf(pArg->out, "Reprepare operations: %d\n", iCur); + oputf("Reprepare operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - raw_printf(pArg->out, "Number of times run: %d\n", iCur); + oputf("Number of times run: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur); + oputf("Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ - displayLinuxIoStats(pArg->out); + displayLinuxIoStats(); #endif /* Do not remove this machine readable comment: extra-stats-output-here */ @@ -17369,53 +20039,114 @@ static int display_stats( return 0; } -/* -** Display scan stats. -*/ -static void display_scanstats( + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +static int scanStatsHeight(sqlite3_stmt *p, int iEntry){ + int iPid = 0; + int ret = 1; + sqlite3_stmt_scanstatus_v2(p, iEntry, + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid + ); + while( iPid!=0 ){ + int ii; + for(ii=0; 1; ii++){ + int iId; + int res; + res = sqlite3_stmt_scanstatus_v2(p, ii, + SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId + ); + if( res ) break; + if( iId==iPid ){ + sqlite3_stmt_scanstatus_v2(p, ii, + SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid + ); + } + } + ret++; + } + return ret; +} +#endif + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +static void display_explain_scanstats( sqlite3 *db, /* Database to query */ ShellState *pArg /* Pointer to ShellState */ ){ -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS - UNUSED_PARAMETER(db); - UNUSED_PARAMETER(pArg); -#else - int i, k, n, mx; - raw_printf(pArg->out, "-------- scanstats --------\n"); - mx = 0; - for(k=0; k<=mx; k++){ - double rEstLoop = 1.0; - for(i=n=0; 1; i++){ - sqlite3_stmt *p = pArg->pStmt; - sqlite3_int64 nLoop, nVisit; - double rEst; - int iSid; - const char *zExplain; - if( sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NLOOP, (void*)&nLoop) ){ - break; + static const int f = SQLITE_SCANSTAT_COMPLEX; + sqlite3_stmt *p = pArg->pStmt; + int ii = 0; + i64 nTotal = 0; + int nWidth = 0; + eqp_reset(pArg); + + for(ii=0; 1; ii++){ + const char *z = 0; + int n = 0; + if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ + break; + } + n = (int)strlen(z) + scanStatsHeight(p, ii)*3; + if( n>nWidth ) nWidth = n; + } + nWidth += 4; + + sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); + for(ii=0; 1; ii++){ + i64 nLoop = 0; + i64 nRow = 0; + i64 nCycle = 0; + int iId = 0; + int iPid = 0; + const char *zo = 0; + const char *zName = 0; + char *zText = 0; + double rEst = 0.0; + + if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ + break; + } + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); + sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName); + + zText = sqlite3_mprintf("%s", zo); + if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ + char *z = 0; + if( nCycle>=0 && nTotal>0 ){ + z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z, + nCycle, ((nCycle*100)+nTotal/2) / nTotal + ); } - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_SELECTID, (void*)&iSid); - if( iSid>mx ) mx = iSid; - if( iSid!=k ) continue; - if( n==0 ){ - rEstLoop = (double)nLoop; - if( k>0 ) raw_printf(pArg->out, "-------- subquery %d -------\n", k); + if( nLoop>=0 ){ + z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop); } - n++; - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); - sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); - rEstLoop *= rEst; - raw_printf(pArg->out, - " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", - nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst + if( nRow>=0 ){ + z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow); + } + + if( zName && pArg->scanstatsOn>1 ){ + double rpl = (double)nRow / (double)nLoop; + z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst); + } + + zText = sqlite3_mprintf( + "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z ); } + + eqp_append(pArg, iId, iPid, zText); + sqlite3_free(zText); } - raw_printf(pArg->out, "---------------------------\n"); -#endif + + eqp_render(pArg, nTotal); } +#endif + /* ** Parameter azArray points to a zero-terminated array of strings. zStr @@ -17453,8 +20184,6 @@ static int str_in_array(const char *zStr, const char **azArray){ ** and "Goto" by 2 spaces. */ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ - const char *zSql; /* The text of the SQL statement */ - const char *z; /* Used to check if this is an EXPLAIN */ int *abYield = 0; /* True if op is an OP_Yield */ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ int iOp; /* Index of operation in p->aiIndent[] */ @@ -17465,65 +20194,45 @@ static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){ "Rewind", 0 }; const char *azGoto[] = { "Goto", 0 }; - /* Try to figure out if this is really an EXPLAIN statement. If this - ** cannot be verified, return early. */ - if( sqlite3_column_count(pSql)!=8 ){ - p->cMode = p->mode; - return; - } - zSql = sqlite3_sql(pSql); - if( zSql==0 ) return; - for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); - if( sqlite3_strnicmp(z, "explain", 7) ){ - p->cMode = p->mode; - return; - } + /* The caller guarantees that the leftmost 4 columns of the statement + ** passed to this function are equivalent to the leftmost 4 columns + ** of EXPLAIN statement output. In practice the statement may be + ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */ + assert( sqlite3_column_count(pSql)>=4 ); + assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 0), "addr" ) ); + assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 1), "opcode" ) ); + assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 2), "p1" ) ); + assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 3), "p2" ) ); for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ int i; int iAddr = sqlite3_column_int(pSql, 0); const char *zOp = (const char*)sqlite3_column_text(pSql, 1); - - /* Set p2 to the P2 field of the current opcode. Then, assuming that - ** p2 is an instruction address, set variable p2op to the index of that - ** instruction in the aiIndent[] array. p2 and p2op may be different if - ** the current instruction is part of a sub-program generated by an - ** SQL trigger or foreign key. */ + int p1 = sqlite3_column_int(pSql, 2); int p2 = sqlite3_column_int(pSql, 3); + + /* Assuming that p2 is an instruction address, set variable p2op to the + ** index of that instruction in the aiIndent[] array. p2 and p2op may be + ** different if the current instruction is part of a sub-program generated + ** by an SQL trigger or foreign key. */ int p2op = (p2 + (iOp-iAddr)); /* Grow the p->aiIndent array as required */ if( iOp>=nAlloc ){ - if( iOp==0 ){ - /* Do further verfication that this is explain output. Abort if - ** it is not */ - static const char *explainCols[] = { - "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment" }; - int jj; - for(jj=0; jj cMode = p->mode; - sqlite3_reset(pSql); - return; - } - } - } nAlloc += 100; p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int)); shell_check_oom(p->aiIndent); abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); shell_check_oom(abYield); } + abYield[iOp] = str_in_array(zOp, azYield); p->aiIndent[iOp] = 0; p->nIndent = iOp+1; - if( str_in_array(zOp, azNext) && p2op>0 ){ for(i=p2op; i aiIndent[i] += 2; } - if( str_in_array(zOp, azGoto) && p2op nIndent - && (abYield[p2op] || sqlite3_column_int(pSql, 2)) - ){ + if( str_in_array(zOp, azGoto) && p2op aiIndent[i] += 2; } } @@ -17543,6 +20252,48 @@ static void explain_data_delete(ShellState *p){ p->iIndent = 0; } +static void exec_prepared_stmt(ShellState*, sqlite3_stmt*); + +/* +** Display scan stats. +*/ +static void display_scanstats( + sqlite3 *db, /* Database to query */ + ShellState *pArg /* Pointer to ShellState */ +){ +#ifndef SQLITE_ENABLE_STMT_SCANSTATUS + UNUSED_PARAMETER(db); + UNUSED_PARAMETER(pArg); +#else + if( pArg->scanstatsOn==3 ){ + const char *zSql = + " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec," + " round(ncycle*100.0 / (sum(ncycle) OVER ()), 2)||'%' AS cycles" + " FROM bytecode(?)"; + + int rc = SQLITE_OK; + sqlite3_stmt *pStmt = 0; + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_stmt *pSave = pArg->pStmt; + pArg->pStmt = pStmt; + sqlite3_bind_pointer(pStmt, 1, pSave, "stmt-pointer", 0); + + pArg->cnt = 0; + pArg->cMode = MODE_ScanExp; + explain_data_prepare(pArg, pStmt); + exec_prepared_stmt(pArg, pStmt); + explain_data_delete(pArg); + + sqlite3_finalize(pStmt); + pArg->pStmt = pSave; + } + }else{ + display_explain_scanstats(db, pArg); + } +#endif +} + /* ** Disable and restore .wheretrace and .treetrace/.selecttrace settings. */ @@ -17600,12 +20351,13 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ if( nVar==0 ) return; /* Nothing to do */ if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters", "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ - return; /* Parameter table does not exist */ + rc = SQLITE_NOTFOUND; + pQ = 0; + }else{ + rc = sqlite3_prepare_v2(pArg->db, + "SELECT value FROM temp.sqlite_parameters" + " WHERE key=?1", -1, &pQ, 0); } - rc = sqlite3_prepare_v2(pArg->db, - "SELECT value FROM temp.sqlite_parameters" - " WHERE key=?1", -1, &pQ, 0); - if( rc || pQ==0 ) return; for(i=1; i<=nVar; i++){ char zNum[30]; const char *zVar = sqlite3_bind_parameter_name(pStmt, i); @@ -17614,8 +20366,16 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ zVar = zNum; } sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC); - if( sqlite3_step(pQ)==SQLITE_ROW ){ + if( rc==SQLITE_OK && pQ && sqlite3_step(pQ)==SQLITE_ROW ){ sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0)); +#ifdef NAN + }else if( sqlite3_strlike("_NAN", zVar, 0)==0 ){ + sqlite3_bind_double(pStmt, i, NAN); +#endif +#ifdef INFINITY + }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ + sqlite3_bind_double(pStmt, i, INFINITY); +#endif }else{ sqlite3_bind_null(pStmt, i); } @@ -17652,17 +20412,17 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ /* Draw horizontal line N characters long using unicode box ** characters */ -static void print_box_line(FILE *out, int N){ - const char zDash[] = +static void print_box_line(int N){ + const char zDash[] = BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; const int nDash = sizeof(zDash) - 1; N *= 3; while( N>nDash ){ - utf8_printf(out, zDash); + oputz(zDash); N -= nDash; } - utf8_printf(out, "%.*s", N, zDash); + oputf("%.*s", N, zDash); } /* @@ -17677,15 +20437,15 @@ static void print_box_row_separator( ){ int i; if( nArg>0 ){ - utf8_printf(p->out, "%s", zSep1); - print_box_line(p->out, p->actualWidth[0]+2); + oputz(zSep1); + print_box_line(p->actualWidth[0]+2); for(i=1; i out, "%s", zSep2); - print_box_line(p->out, p->actualWidth[i]+2); + oputz(zSep2); + print_box_line(p->actualWidth[i]+2); } - utf8_printf(p->out, "%s", zSep3); + oputz(zSep3); } - fputs("\n", p->out); + oputz("\n"); } /* @@ -17782,7 +20542,7 @@ static char *translateForDisplayAndDup( break; } zOut[j] = 0; - return (char*)zOut; + return (char*)zOut; } /* Extract the value of the i-th current column for pStmt as an SQL literal @@ -17829,7 +20589,7 @@ static char *quoted_column(sqlite3_stmt *pStmt, int i){ */ static void exec_prepared_stmt_columnar( ShellState *p, /* Pointer to ShellState */ - sqlite3_stmt *pStmt /* Statment to run */ + sqlite3_stmt *pStmt /* Statement to run */ ){ sqlite3_int64 nRow = 0; int nColumn = 0; @@ -17859,7 +20619,7 @@ static void exec_prepared_stmt_columnar( azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); shell_check_oom(azData); azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) ); - shell_check_oom((void*)azNextLine); + shell_check_oom(azNextLine); memset((void*)azNextLine, 0, nColumn*sizeof(char*) ); if( p->cmOpts.bQuote ){ azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) ); @@ -17889,6 +20649,7 @@ static void exec_prepared_stmt_columnar( } if( wx<0 ) wx = -wx; uz = (const unsigned char*)sqlite3_column_name(pStmt,i); + if( uz==0 ) uz = (u8*)""; azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); } do{ @@ -17947,11 +20708,11 @@ static void exec_prepared_stmt_columnar( for(i=0; i actualWidth[i]; if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(p->out, w, azData[i]); + utf8_width_print(w, azData[i]); fputs(i==nColumn-1?"\n":" ", p->out); } for(i=0; i out, p->actualWidth[i]); + print_dashes(p->actualWidth[i]); fputs(i==nColumn-1?"\n":" ", p->out); } } @@ -17965,8 +20726,8 @@ static void exec_prepared_stmt_columnar( for(i=0; i actualWidth[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + oputz(i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "+"); break; @@ -17978,8 +20739,8 @@ static void exec_prepared_stmt_columnar( for(i=0; i actualWidth[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - fputs(i==nColumn-1?" |\n":" | ", p->out); + oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); + oputz(i==nColumn-1?" |\n":" | "); } print_row_separator(p, nColumn, "|"); break; @@ -17988,13 +20749,13 @@ static void exec_prepared_stmt_columnar( colSep = " " BOX_13 " "; rowSep = " " BOX_13 "\n"; print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - utf8_printf(p->out, BOX_13 " "); + oputz(BOX_13 " "); for(i=0; i actualWidth[i]; n = strlenChar(azData[i]); - utf8_printf(p->out, "%*s%s%*s%s", - (w-n)/2, "", azData[i], (w-n+1)/2, "", - i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); + oputf("%*s%s%*s%s", + (w-n)/2, "", azData[i], (w-n+1)/2, "", + i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); } print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); break; @@ -18002,28 +20763,28 @@ static void exec_prepared_stmt_columnar( } for(i=nColumn, j=0; i cMode!=MODE_Column ){ - utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| "); + oputz(p->cMode==MODE_Box?BOX_13" ":"| "); } z = azData[i]; if( z==0 ) z = p->nullValue; w = p->actualWidth[j]; if( p->colWidth[j]<0 ) w = -w; - utf8_width_print(p->out, w, z); + utf8_width_print(w, z); if( j==nColumn-1 ){ - utf8_printf(p->out, "%s", rowSep); + oputz(rowSep); if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1 cMode==MODE_Table ){ print_row_separator(p, nColumn, "+"); }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); }else if( p->cMode==MODE_Column ){ - raw_printf(p->out, "\n"); + oputz("\n"); } } j = -1; if( seenInterrupt ) goto columnar_end; }else{ - utf8_printf(p->out, "%s", colSep); + oputz(colSep); } } if( p->cMode==MODE_Table ){ @@ -18033,7 +20794,7 @@ static void exec_prepared_stmt_columnar( } columnar_end: if( seenInterrupt ){ - utf8_printf(p->out, "Interrupt\n"); + oputz("Interrupt\n"); } nData = (nRow+1)*nColumn; for(i=0; i expert.pExpert ); @@ -18154,7 +20915,7 @@ static int expertHandleSQL( /* ** This function is called either to silently clean up the object -** created by the ".expert" command (if bCancel==1), or to generate a +** created by the ".expert" command (if bCancel==1), or to generate a ** report from it and then clean it up (if bCancel==0). ** ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error @@ -18172,7 +20933,6 @@ static int expertFinish( assert( p ); assert( bCancel || pzErr==0 || *pzErr==0 ); if( bCancel==0 ){ - FILE *out = pState->out; int bVerbose = pState->expert.bVerbose; rc = sqlite3_expert_analyze(p, pzErr); @@ -18182,8 +20942,8 @@ static int expertFinish( if( bVerbose ){ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); - raw_printf(out, "-- Candidates -----------------------------\n"); - raw_printf(out, "%s\n", zCand); + oputz("-- Candidates -----------------------------\n"); + oputf("%s\n", zCand); } for(i=0; i =2 && 0==cli_strncmp(z, "-sample", n) ){ if( i==(nArg-1) ){ - raw_printf(stderr, "option requires an argument: %s\n", z); + eputf("option requires an argument: %s\n", z); rc = SQLITE_ERROR; }else{ iSample = (int)integerValue(azArg[++i]); if( iSample<0 || iSample>100 ){ - raw_printf(stderr, "value out of range: %s\n", azArg[i]); + eputf("value out of range: %s\n", azArg[i]); rc = SQLITE_ERROR; } } } else{ - raw_printf(stderr, "unknown option: %s\n", z); + eputf("unknown option: %s\n", z); rc = SQLITE_ERROR; } } @@ -18249,7 +21009,7 @@ static int expertDotCommand( if( rc==SQLITE_OK ){ pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); if( pState->expert.pExpert==0 ){ - raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); + eputf("sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); rc = SQLITE_ERROR; }else{ sqlite3_expert_config( @@ -18312,7 +21072,7 @@ static int shell_exec( if( zStmtSql==0 ) zStmtSql = ""; while( IsSpace(zStmtSql[0]) ) zStmtSql++; - /* save off the prepared statment handle and reset row count */ + /* save off the prepared statement handle and reset row count */ if( pArg ){ pArg->pStmt = pStmt; pArg->cnt = 0; @@ -18321,57 +21081,51 @@ static int shell_exec( /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){ sqlite3_stmt *pExplain; - char *zEQP; int triggerEQP = 0; disable_debug_trace_modes(); sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP); if( pArg->autoEQP>=AUTOEQP_trigger ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0); } - zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql); - shell_check_oom(zEQP); - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); + pExplain = pStmt; + sqlite3_reset(pExplain); + rc = sqlite3_stmt_explain(pExplain, 2); if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3); int iEqpId = sqlite3_column_int(pExplain, 0); int iParentId = sqlite3_column_int(pExplain, 1); if( zEQPLine==0 ) zEQPLine = ""; - if( zEQPLine[0]=='-' ) eqp_render(pArg); + if( zEQPLine[0]=='-' ) eqp_render(pArg, 0); eqp_append(pArg, iEqpId, iParentId, zEQPLine); } - eqp_render(pArg); + eqp_render(pArg, 0); } - sqlite3_finalize(pExplain); - sqlite3_free(zEQP); if( pArg->autoEQP>=AUTOEQP_full ){ /* Also do an EXPLAIN for ".eqp full" mode */ - zEQP = sqlite3_mprintf("EXPLAIN %s", zStmtSql); - shell_check_oom(zEQP); - rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0); + sqlite3_reset(pExplain); + rc = sqlite3_stmt_explain(pExplain, 1); if( rc==SQLITE_OK ){ pArg->cMode = MODE_Explain; + assert( sqlite3_stmt_isexplain(pExplain)==1 ); explain_data_prepare(pArg, pExplain); exec_prepared_stmt(pArg, pExplain); explain_data_delete(pArg); } - sqlite3_finalize(pExplain); - sqlite3_free(zEQP); } if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0); - /* Reprepare pStmt before reactiving trace modes */ - sqlite3_finalize(pStmt); - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( pArg ) pArg->pStmt = pStmt; } + sqlite3_reset(pStmt); + sqlite3_stmt_explain(pStmt, 0); restore_debug_trace_modes(); } if( pArg ){ + int bIsExplain = (sqlite3_stmt_isexplain(pStmt)==1); pArg->cMode = pArg->mode; if( pArg->autoExplain ){ - if( sqlite3_stmt_isexplain(pStmt)==1 ){ + if( bIsExplain ){ pArg->cMode = MODE_Explain; } if( sqlite3_stmt_isexplain(pStmt)==2 ){ @@ -18381,7 +21135,7 @@ static int shell_exec( /* If the shell is currently in ".explain" mode, gather the extra ** data required to add indents to the output.*/ - if( pArg->cMode==MODE_Explain ){ + if( pArg->cMode==MODE_Explain && bIsExplain ){ explain_data_prepare(pArg, pStmt); } } @@ -18389,7 +21143,7 @@ static int shell_exec( bind_prepared_stmt(pArg, pStmt); exec_prepared_stmt(pArg, pStmt); explain_data_delete(pArg); - eqp_render(pArg); + eqp_render(pArg, 0); /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ @@ -18497,7 +21251,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){ */ if( preserveRowid && isIPK ){ /* If a single PRIMARY KEY column with type INTEGER was seen, then it - ** might be an alise for the ROWID. But it might also be a WITHOUT ROWID + ** might be an alias for the ROWID. But it might also be a WITHOUT ROWID ** table or a INTEGER PRIMARY KEY DESC column, neither of which are ** ROWID aliases. To distinguish these cases, check to see if ** there is a "pk" entry in "PRAGMA index_list". There will be @@ -18582,9 +21336,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "DELETE FROM sqlite_sequence;\n"); + if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n"); }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n"); }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ @@ -18592,7 +21346,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=ON;\n"); + oputz("PRAGMA writable_schema=ON;\n"); p->writableSchema = 1; } zIns = sqlite3_mprintf( @@ -18600,11 +21354,11 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); shell_check_oom(zIns); - utf8_printf(p->out, "%s\n", zIns); + oputf("%s\n", zIns); sqlite3_free(zIns); return 0; }else{ - printSchemaLine(p->out, zSql, ";\n"); + printSchemaLine(zSql, ";\n"); } if( cli_strcmp(zType, "table")==0 ){ @@ -18662,7 +21416,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->mode = p->cMode = MODE_Insert; rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + oputz("/****** CORRUPTION ERROR *******/\n"); toggleSelectOrder(p->db); shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); @@ -18693,9 +21447,9 @@ static int run_schema_dump_query( if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n"); + oputz("/****** CORRUPTION ERROR *******/\n"); if( zErr ){ - utf8_printf(p->out, "/****** %s ******/\n", zErr); + oputf("/****** %s ******/\n", zErr); sqlite3_free(zErr); zErr = 0; } @@ -18704,7 +21458,7 @@ static int run_schema_dump_query( sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ - utf8_printf(p->out, "/****** ERROR: %s ******/\n", zErr); + oputf("/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } @@ -18758,7 +21512,6 @@ static const char *(azHelp[]) = { " --async Write to FILE without journal and fsync()", #endif ".bail on|off Stop after hitting an error. Default OFF", - ".binary on|off Turn binary output on or off. Default OFF", #ifndef SQLITE_SHELL_FIDDLE ".cd DIRECTORY Change the working directory to DIRECTORY", #endif @@ -18768,6 +21521,9 @@ static const char *(azHelp[]) = { ".clone NEWDB Clone data into NEWDB from the existing database", #endif ".connection [close] [#] Open or close an auxiliary database connection", +#if defined(_WIN32) || defined(WIN32) + ".crnl on|off Translate \\n to \\r\\n. Default ON", +#endif ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", #if SQLITE_SHELL_HAVE_RECOVER @@ -18821,13 +21577,13 @@ static const char *(azHelp[]) = { " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL - ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", + ",imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", #ifdef SQLITE_ENABLE_IOTRACE - ".iotrace FILE Enable I/O diagnostic logging to FILE", + ",iotrace FILE Enable I/O diagnostic logging to FILE", #endif ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT", ".lint OPTIONS Report potential schema issues.", @@ -18836,8 +21592,10 @@ static const char *(azHelp[]) = { #if !defined(SQLITE_OMIT_LOAD_EXTENSION) && !defined(SQLITE_SHELL_FIDDLE) ".load FILE ?ENTRY? Load an extension library", #endif -#ifndef SQLITE_SHELL_FIDDLE - ".log FILE|off Turn logging on or off. FILE can be stderr/stdout", +#if !defined(SQLITE_SHELL_FIDDLE) + ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", +#else + ".log on|off Turn logging on or off.", #endif ".mode MODE ?OPTIONS? Set output mode", " MODE is one of:", @@ -18914,7 +21672,7 @@ static const char *(azHelp[]) = { #endif ".prompt MAIN CONTINUE Replace the standard prompts", #ifndef SQLITE_SHELL_FIDDLE - ".quit Exit this program", + ".quit Stop interpreting input stream, exit if primary.", ".read FILE Read input from FILE or command output", " If FILE begins with \"|\", it is a command that generates the input.", #endif @@ -18929,12 +21687,12 @@ static const char *(azHelp[]) = { ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", ".save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)", #endif - ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", + ".scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", " Options:", " --indent Try to pretty-print the schema", " --nosys Omit objects whose names start with \"sqlite_\"", - ".selftest ?OPTIONS? Run tests defined in the SELFTEST table", + ",selftest ?OPTIONS? Run tests defined in the SELFTEST table", " Options:", " --init Create a new SELFTEST table", " -v Verbose output", @@ -18976,9 +21734,9 @@ static const char *(azHelp[]) = { #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", #ifndef SQLITE_SHELL_FIDDLE - ".testcase NAME Begin redirecting output to 'testcase-out.txt'", + ",testcase NAME Begin redirecting output to 'testcase-out.txt'", #endif - ".testctrl CMD ... Run various sqlite3_test_control() operations", + ",testctrl CMD ... Run various sqlite3_test_control() operations", " Run \".testctrl\" with no arguments for details", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", @@ -19002,6 +21760,7 @@ static const char *(azHelp[]) = { ".unmodule NAME ... Unregister virtual table modules", " --allexcept Unregister everything except those named", #endif + ".version Show source, library and compiler versions", ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", @@ -19029,21 +21788,46 @@ static int showHelp(FILE *out, const char *zPattern){ || cli_strcmp(zPattern,"-all")==0 || cli_strcmp(zPattern,"--all")==0 ){ - /* Show all commands, but only one line per command */ - if( zPattern==0 ) zPattern = ""; + enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 }; + enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 }; + /* Show all or most commands + ** *zPattern==0 => summary of documented commands only + ** *zPattern=='0' => whole help for undocumented commands + ** Otherwise => whole help for documented commands + */ + enum HelpWanted hw = HW_SummaryOnly; + enum HelpHave hh = HH_More; + if( zPattern!=0 ){ + hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull; + } for(i=0; i 65536 || (pgsz & (pgsz-1))!=0 ){ - utf8_printf(stderr, "invalid pagesize\n"); + eputz("invalid pagesize\n"); goto readHexDb_error; } for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ @@ -19297,64 +22096,17 @@ readHexDb_error: p->lineno = nLine; } sqlite3_free(a); - utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine); + eputf("Error on line %d of --hexdb input\n", nLine); return 0; } #endif /* SQLITE_OMIT_DESERIALIZE */ -/* -** Scalar function "shell_int32". The first argument to this function -** must be a blob. The second a non-negative integer. This function -** reads and returns a 32-bit big-endian integer from byte -** offset (4* ) of the blob. -*/ -static void shellInt32( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *pBlob; - int nBlob; - int iInt; - - UNUSED_PARAMETER(argc); - nBlob = sqlite3_value_bytes(argv[0]); - pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); - iInt = sqlite3_value_int(argv[1]); - - if( iInt>=0 && (iInt+1)*4<=nBlob ){ - const unsigned char *a = &pBlob[iInt*4]; - sqlite3_int64 iVal = ((sqlite3_int64)a[0]<<24) - + ((sqlite3_int64)a[1]<<16) - + ((sqlite3_int64)a[2]<< 8) - + ((sqlite3_int64)a[3]<< 0); - sqlite3_result_int64(context, iVal); - } -} - -/* -** Scalar function "shell_idquote(X)" returns string X quoted as an identifier, -** using "..." with internal double-quote characters doubled. -*/ -static void shellIdQuote( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zName = (const char*)sqlite3_value_text(argv[0]); - UNUSED_PARAMETER(argc); - if( zName ){ - char *z = sqlite3_mprintf("\"%w\"", zName); - sqlite3_result_text(context, z, -1, sqlite3_free); - } -} - /* ** Scalar function "usleep(X)" invokes sqlite3_sleep(X) and returns X. */ static void shellUSleepFunc( - sqlite3_context *context, - int argcUnused, + sqlite3_context *context, + int argcUnused, sqlite3_value **argv ){ int sleep = sqlite3_value_int(argv[0]); @@ -19363,97 +22115,6 @@ static void shellUSleepFunc( sqlite3_result_int(context, sleep); } -/* -** Scalar function "shell_escape_crnl" used by the .recover command. -** The argument passed to this function is the output of built-in -** function quote(). If the first character of the input is "'", -** indicating that the value passed to quote() was a text value, -** then this function searches the input for "\n" and "\r" characters -** and adds a wrapper similar to the following: -** -** replace(replace(, '\n', char(10), '\r', char(13)); -** -** Or, if the first character of the input is not "'", then a copy -** of the input is returned. -*/ -static void shellEscapeCrnl( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const char *zText = (const char*)sqlite3_value_text(argv[0]); - UNUSED_PARAMETER(argc); - if( zText && zText[0]=='\'' ){ - i64 nText = sqlite3_value_bytes(argv[0]); - i64 i; - char zBuf1[20]; - char zBuf2[20]; - const char *zNL = 0; - const char *zCR = 0; - i64 nCR = 0; - i64 nNL = 0; - - for(i=0; zText[i]; i++){ - if( zNL==0 && zText[i]=='\n' ){ - zNL = unused_string(zText, "\\n", "\\012", zBuf1); - nNL = strlen(zNL); - } - if( zCR==0 && zText[i]=='\r' ){ - zCR = unused_string(zText, "\\r", "\\015", zBuf2); - nCR = strlen(zCR); - } - } - - if( zNL || zCR ){ - i64 iOut = 0; - i64 nMax = (nNL > nCR) ? nNL : nCR; - i64 nAlloc = nMax * nText + (nMax+64)*2; - char *zOut = (char*)sqlite3_malloc64(nAlloc); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - return; - } - - if( zNL && zCR ){ - memcpy(&zOut[iOut], "replace(replace(", 16); - iOut += 16; - }else{ - memcpy(&zOut[iOut], "replace(", 8); - iOut += 8; - } - for(i=0; zText[i]; i++){ - if( zText[i]=='\n' ){ - memcpy(&zOut[iOut], zNL, nNL); - iOut += nNL; - }else if( zText[i]=='\r' ){ - memcpy(&zOut[iOut], zCR, nCR); - iOut += nCR; - }else{ - zOut[iOut] = zText[i]; - iOut++; - } - } - - if( zNL ){ - memcpy(&zOut[iOut], ",'", 2); iOut += 2; - memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; - memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12; - } - if( zCR ){ - memcpy(&zOut[iOut], ",'", 2); iOut += 2; - memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; - memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12; - } - - sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT); - sqlite3_free(zOut); - return; - } - } - - sqlite3_result_value(context, argv[0]); -} - /* Flags for open_db(). ** ** The default behavior of open_db() is to exit(1) if the database fails to @@ -19478,13 +22139,13 @@ static void open_db(ShellState *p, int openFlags){ if( zDbFilename==0 || zDbFilename[0]==0 ){ p->openMode = SHELL_OPEN_NORMAL; }else{ - p->openMode = (u8)deduceDatabaseType(zDbFilename, + p->openMode = (u8)deduceDatabaseType(zDbFilename, (openFlags & OPEN_DB_ZIPFILE)!=0); } } switch( p->openMode ){ case SHELL_OPEN_APPENDVFS: { - sqlite3_open_v2(zDbFilename, &p->db, + sqlite3_open_v2(zDbFilename, &p->db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs"); break; } @@ -19511,20 +22172,38 @@ static void open_db(ShellState *p, int openFlags){ } globalDb = p->db; if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ - utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", - zDbFilename, sqlite3_errmsg(p->db)); - if( openFlags & OPEN_DB_KEEPALIVE ){ - sqlite3_open(":memory:", &p->db); - return; + eputf("Error: unable to open database \"%s\": %s\n", + zDbFilename, sqlite3_errmsg(p->db)); + if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ + exit(1); + } + sqlite3_close(p->db); + sqlite3_open(":memory:", &p->db); + if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ + eputz("Also: unable to open substitute in-memory database.\n"); + exit(1); + }else{ + eputf("Notice: using substitute in-memory database instead of \"%s\"\n", + zDbFilename); } - exit(1); } + sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0); + + /* Reflect the use or absence of --unsafe-testing invocation. */ + { + int testmode_on = ShellHasFlag(p,SHFLG_TestingMode); + sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0); + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0); + } + #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif sqlite3_shathree_init(p->db, 0, 0); sqlite3_uint_init(p->db, 0, 0); sqlite3_decimal_init(p->db, 0, 0); + sqlite3_base64_init(p->db, 0, 0); + sqlite3_base85_init(p->db, 0, 0); sqlite3_regexp_init(p->db, 0, 0); sqlite3_ieee_init(p->db, 0, 0); sqlite3_series_init(p->db, 0, 0); @@ -19532,27 +22211,52 @@ static void open_db(ShellState *p, int openFlags){ sqlite3_fileio_init(p->db, 0, 0); sqlite3_completion_init(p->db, 0, 0); #endif -#if SQLITE_SHELL_HAVE_RECOVER - sqlite3_dbdata_init(p->db, 0, 0); -#endif #ifdef SQLITE_HAVE_ZLIB if( !p->bSafeModePersist ){ sqlite3_zipfile_init(p->db, 0, 0); sqlite3_sqlar_init(p->db, 0, 0); } #endif +#ifdef SQLITE_SHELL_EXTFUNCS + /* Create a preprocessing mechanism for extensions to make + * their own provisions for being built into the shell. + * This is a short-span macro. See further below for usage. + */ +#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant +#define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant) + /* Let custom-included extensions get their ..._init() called. + * The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause + * the extension's sqlite3_*_init( db, pzErrorMsg, pApi ) + * initialization routine to be called. + */ + { + int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db); + /* Let custom-included extensions expose their functionality. + * The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause + * the SQL functions, virtual tables, collating sequences or + * VFS's implemented by the extension to be registered. + */ + if( irc==SQLITE_OK + || irc==SQLITE_OK_LOAD_PERMANENTLY ){ + SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0); + } +#undef SHELL_SUB_MACRO +#undef SHELL_SUBMACRO + } +#endif + + sqlite3_create_function(p->db, "strtod", 1, SQLITE_UTF8, 0, + shellStrtod, 0, 0); + sqlite3_create_function(p->db, "dtostr", 1, SQLITE_UTF8, 0, + shellDtostr, 0, 0); + sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0, + shellDtostr, 0, 0); sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, shellAddSchemaName, 0, 0); sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0, shellModuleSchema, 0, 0); sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, shellPutsFunc, 0, 0); - sqlite3_create_function(p->db, "shell_escape_crnl", 1, SQLITE_UTF8, 0, - shellEscapeCrnl, 0, 0); - sqlite3_create_function(p->db, "shell_int32", 2, SQLITE_UTF8, 0, - shellInt32, 0, 0); - sqlite3_create_function(p->db, "shell_idquote", 1, SQLITE_UTF8, 0, - shellIdQuote, 0, 0); sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0, shellUSleepFunc, 0, 0); #ifndef SQLITE_NOHAVE_SYSTEM @@ -19561,6 +22265,7 @@ static void open_db(ShellState *p, int openFlags){ sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, editFunc, 0, 0); #endif + if( p->openMode==SHELL_OPEN_ZIPFILE ){ char *zSql = sqlite3_mprintf( "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", zDbFilename); @@ -19578,15 +22283,15 @@ static void open_db(ShellState *p, int openFlags){ aData = (unsigned char*)readFile(zDbFilename, &nData); }else{ aData = readHexDb(p, &nData); - if( aData==0 ){ - return; - } + } + if( aData==0 ){ + return; } rc = sqlite3_deserialize(p->db, "main", aData, nData, nData, SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); if( rc ){ - utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc); + eputf("Error: sqlite3_deserialize() returns %d\n", rc); } if( p->szMax>0 ){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); @@ -19594,20 +22299,24 @@ static void open_db(ShellState *p, int openFlags){ } #endif } - if( p->bSafeModePersist && p->db!=0 ){ - sqlite3_set_authorizer(p->db, safeModeAuth, p); + if( p->db!=0 ){ + if( p->bSafeModePersist ){ + sqlite3_set_authorizer(p->db, safeModeAuth, p); + } + sqlite3_db_config( + p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 + ); } } /* -** Attempt to close the databaes connection. Report errors. +** Attempt to close the database connection. Report errors. */ void close_db(sqlite3 *db){ int rc = sqlite3_close(db); if( rc ){ - utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n", - rc, sqlite3_errmsg(db)); - } + eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); + } } #if HAVE_READLINE || HAVE_EDITLINE @@ -19637,6 +22346,8 @@ static char *readline_completion_generator(const char *text, int state){ return zRet; } static char **readline_completion(const char *zText, int iStart, int iEnd){ + (void)iStart; + (void)iEnd; rl_attempted_completion_over = 1; return rl_completion_matches(zText, readline_completion_generator); } @@ -19652,7 +22363,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ char *zSql; char zBuf[1000]; - if( nLine>sizeof(zBuf)-30 ) return; + if( nLine>(i64)sizeof(zBuf)-30 ) return; if( zLine[0]=='.' || zLine[0]=='#') return; for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} if( i==nLine-1 ) return; @@ -19668,7 +22379,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ while( sqlite3_step(pStmt)==SQLITE_ROW ){ const char *zCompletion = (const char*)sqlite3_column_text(pStmt, 0); int nCompletion = sqlite3_column_bytes(pStmt, 0); - if( iStart+nCompletion < sizeof(zBuf)-1 && zCompletion ){ + if( iStart+nCompletion < (i64)sizeof(zBuf)-1 && zCompletion ){ memcpy(zBuf+iStart, zCompletion, nCompletion+1); linenoiseAddCompletion(lc, zBuf); } @@ -19692,6 +22403,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ ** \' -> ' ** \\ -> backslash ** \NNN -> ascii character NNN in octal +** \xHH -> ascii character HH in hexadecimal */ static void resolve_backslashes(char *z){ int i, j; @@ -19720,6 +22432,15 @@ static void resolve_backslashes(char *z){ c = '\''; }else if( c=='\\' ){ c = '\\'; + }else if( c=='x' ){ + int nhd = 0, hdv; + u8 hv = 0; + while( nhd<2 && (c=z[i+1+nhd])!=0 && (hdv=hexDigitValue(c))>=0 ){ + hv = (u8)((hv<<4)|hdv); + ++nhd; + } + i += nhd; + c = (u8)hv; }else if( c>='0' && c<='7' ){ c -= '0'; if( z[i+1]>='0' && z[i+1]<='7' ){ @@ -19755,8 +22476,7 @@ static int booleanValue(const char *zArg){ if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ return 0; } - utf8_printf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", - zArg); + eputf("ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); return 0; } @@ -19794,7 +22514,7 @@ static FILE *output_file_open(const char *zFile, int bTextMode){ }else{ f = fopen(zFile, bTextMode ? "w" : "wb"); if( f==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); + eputf("Error: cannot open \"%s\"\n", zFile); } } return f; @@ -19816,10 +22536,10 @@ static int sql_trace_callback( i64 nSql; if( p->traceOut==0 ) return 0; if( mType==SQLITE_TRACE_CLOSE ){ - utf8_printf(p->traceOut, "-- closing database connection\n"); + sputz(p->traceOut, "-- closing database connection\n"); return 0; } - if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){ + if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){ zSql = (const char*)pX; }else{ pStmt = (sqlite3_stmt*)pP; @@ -19847,12 +22567,12 @@ static int sql_trace_callback( switch( mType ){ case SQLITE_TRACE_ROW: case SQLITE_TRACE_STMT: { - utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); + sputf(p->traceOut, "%.*s;\n", (int)nSql, zSql); break; } case SQLITE_TRACE_PROFILE: { - sqlite3_int64 nNanosec = *(sqlite3_int64*)pX; - utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); + sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; + sputf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); break; } } @@ -19863,10 +22583,13 @@ static int sql_trace_callback( /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is ** a useful spot to set a debugger breakpoint. +** +** This routine does not do anything practical. The code are there simply +** to prevent the compiler from optimizing this routine out. */ static void test_breakpoint(void){ - static int nCall = 0; - nCall++; + static unsigned int nCall = 0; + if( (nCall++)==0xffffffff ) printf("Many .breakpoints have run\n"); } /* @@ -19924,8 +22647,8 @@ static void import_append_char(ImportCtx *p, int c){ */ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ int c; - int cSep = p->cColSep; - int rSep = p->cRowSep; + int cSep = (u8)p->cColSep; + int rSep = (u8)p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ @@ -19956,12 +22679,11 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ break; } if( pc==cQuote && c!='\r' ){ - utf8_printf(stderr, "%s:%d: unescaped %c character\n", - p->zFile, p->nLine, cQuote); + eputf("%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); } if( c==EOF ){ - utf8_printf(stderr, "%s:%d: unterminated %c-quoted field\n", - p->zFile, startLine, cQuote); + eputf("%s:%d: unterminated %c-quoted field\n", + p->zFile, startLine, cQuote); p->cTerm = c; break; } @@ -20014,8 +22736,8 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ */ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ int c; - int cSep = p->cColSep; - int rSep = p->cRowSep; + int cSep = (u8)p->cColSep; + int rSep = (u8)p->cRowSep; p->n = 0; c = fgetc(p->in); if( c==EOF || seenInterrupt ){ @@ -20059,9 +22781,8 @@ static void tryToCloneData( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - utf8_printf(stderr, "Error %d: %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); + eputf("Error %d: %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } n = sqlite3_column_count(pQuery); @@ -20077,9 +22798,8 @@ static void tryToCloneData( memcpy(zInsert+i, ");", 3); rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); if( rc ){ - utf8_printf(stderr, "Error %d: %s on [%s]\n", - sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), - zQuery); + eputf("Error %d: %s on [%s]\n", + sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); goto end_data_xfer; } for(k=0; k<2; k++){ @@ -20114,8 +22834,8 @@ static void tryToCloneData( } /* End for */ rc = sqlite3_step(pInsert); if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ - utf8_printf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb), - sqlite3_errmsg(newDb)); + eputf("Error %d: %s\n", + sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); } sqlite3_reset(pInsert); cnt++; @@ -20132,7 +22852,7 @@ static void tryToCloneData( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - utf8_printf(stderr, "Warning: cannot step \"%s\" backwards", zTable); + eputf("Warning: cannot step \"%s\" backwards", zTable); break; } } /* End for(k=0...) */ @@ -20165,30 +22885,31 @@ static void tryToCloneSchema( char *zErrMsg = 0; zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" - " WHERE %s", zWhere); + " WHERE %s ORDER BY rowid ASC", zWhere); shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - utf8_printf(stderr, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); + eputf("Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), + sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; - printf("%s... ", zName); fflush(stdout); - sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); - if( zErrMsg ){ - utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); - sqlite3_free(zErrMsg); - zErrMsg = 0; + if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ + sputf(stdout, "%s... ", zName); fflush(stdout); + sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); + if( zErrMsg ){ + eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); + sqlite3_free(zErrMsg); + zErrMsg = 0; + } } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } - printf("done\n"); + sputz(stdout, "done\n"); } if( rc!=SQLITE_DONE ){ sqlite3_finalize(pQuery); @@ -20198,26 +22919,26 @@ static void tryToCloneSchema( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - utf8_printf(stderr, "Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), - zQuery); + eputf("Error: (%d) %s on [%s]\n", + sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } while( sqlite3_step(pQuery)==SQLITE_ROW ){ zName = sqlite3_column_text(pQuery, 0); zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; - printf("%s... ", zName); fflush(stdout); + if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; + sputf(stdout, "%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ - utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql); + eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } if( xForEach ){ xForEach(p, newDb, (const char*)zName); } - printf("done\n"); + sputz(stdout, "done\n"); } } end_schema_xfer: @@ -20234,13 +22955,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){ int rc; sqlite3 *newDb = 0; if( access(zNewDb,0)==0 ){ - utf8_printf(stderr, "File \"%s\" already exists.\n", zNewDb); + eputf("File \"%s\" already exists.\n", zNewDb); return; } rc = sqlite3_open(zNewDb, &newDb); if( rc ){ - utf8_printf(stderr, "Cannot create output database: %s\n", - sqlite3_errmsg(newDb)); + eputf("Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); @@ -20252,6 +22972,18 @@ static void tryToClone(ShellState *p, const char *zNewDb){ close_db(newDb); } +#ifndef SQLITE_SHELL_FIDDLE +/* +** Change the output stream (file or pipe or console) to something else. +*/ +static void output_redir(ShellState *p, FILE *pfNew){ + if( p->out != stdout ) eputz("Output already redirected.\n"); + else{ + p->out = pfNew; + setOutputStream(pfNew); + } +} + /* ** Change the output file back to stdout. ** @@ -20279,7 +23011,7 @@ static void output_reset(ShellState *p){ char *zCmd; zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); if( system(zCmd) ){ - utf8_printf(stderr, "Failed: [%s]\n", zCmd); + eputf("Failed: [%s]\n", zCmd); }else{ /* Give the start/open/xdg-open command some time to get ** going before we continue, and potential delete the @@ -20294,7 +23026,12 @@ static void output_reset(ShellState *p){ } p->outfile[0] = 0; p->out = stdout; + setOutputStream(stdout); } +#else +# define output_redir(SS,pfO) +# define output_reset(SS) +#endif /* ** Run an SQL command and return the single integer result. @@ -20310,7 +23047,7 @@ static int db_int(sqlite3 *db, const char *zSql){ return res; } -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) +#if SQLITE_SHELL_HAVE_RECOVER /* ** Convert a 2-byte or 4-byte big-endian integer into a native integer */ @@ -20365,7 +23102,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", -1, &pStmt, 0); if( rc ){ - utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db)); + eputf("error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); return 1; } @@ -20373,31 +23110,33 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ if( sqlite3_step(pStmt)==SQLITE_ROW && sqlite3_column_bytes(pStmt,0)>100 ){ - memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100); + const u8 *pb = sqlite3_column_blob(pStmt,0); + shell_check_oom(pb); + memcpy(aHdr, pb, 100); sqlite3_finalize(pStmt); }else{ - raw_printf(stderr, "unable to read database header\n"); + eputz("unable to read database header\n"); sqlite3_finalize(pStmt); return 1; } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; - utf8_printf(p->out, "%-20s %d\n", "database page size:", i); - utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); - utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); - utf8_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); + oputf("%-20s %d\n", "database page size:", i); + oputf("%-20s %d\n", "write format:", aHdr[18]); + oputf("%-20s %d\n", "read format:", aHdr[19]); + oputf("%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; i out, "%-20s %u", aField[i].zName, val); + oputf("%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) raw_printf(p->out, " (utf8)"); - if( val==2 ) raw_printf(p->out, " (utf16le)"); - if( val==3 ) raw_printf(p->out, " (utf16be)"); + if( val==1 ) oputz(" (utf8)"); + if( val==2 ) oputz(" (utf16le)"); + if( val==3 ) oputz(" (utf16be)"); } } - raw_printf(p->out, "\n"); + oputz("\n"); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -20410,11 +23149,11 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); int val = db_int(p->db, zSql); sqlite3_free(zSql); - utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); + oputf("%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); - utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); + oputf("%-20s %u\n", "data version", iDataVersion); return 0; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ @@ -20424,7 +23163,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ */ static int shellDatabaseError(sqlite3 *db){ const char *zErr = sqlite3_errmsg(db); - utf8_printf(stderr, "Error: %s\n", zErr); + eputf("Error: %s\n", zErr); return 1; } @@ -20659,7 +23398,6 @@ static int lintFkeyIndexes( int nArg /* Number of entries in azArg[] */ ){ sqlite3 *db = pState->db; /* Database handle to query "main" db of */ - FILE *out = pState->out; /* Stream to write non-error output to */ int bVerbose = 0; /* If -verbose is present */ int bGroupByParent = 0; /* If -groupbyparent is present */ int i; /* To iterate through azArg[] */ @@ -20741,9 +23479,7 @@ static int lintFkeyIndexes( zIndent = " "; } else{ - raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n", - azArg[0], azArg[1] - ); + eputf("Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); return SQLITE_ERROR; } } @@ -20787,23 +23523,23 @@ static int lintFkeyIndexes( if( rc!=SQLITE_OK ) break; if( res<0 ){ - raw_printf(stderr, "Error: internal error"); + eputz("Error: internal error"); break; }else{ if( bGroupByParent && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ - raw_printf(out, "-- Parent table %s\n", zParent); + oputf("-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ - raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); + oputf("%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ - raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n", - zIndent, zFrom, zTarget + oputf("%s/* no extra indexes required for %s -> %s */\n", + zIndent, zFrom, zTarget ); } } @@ -20811,16 +23547,16 @@ static int lintFkeyIndexes( sqlite3_free(zPrev); if( rc!=SQLITE_OK ){ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + eputf("%s\n", sqlite3_errmsg(db)); } rc2 = sqlite3_finalize(pSql); if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ rc = rc2; - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + eputf("%s\n", sqlite3_errmsg(db)); } }else{ - raw_printf(stderr, "%s\n", sqlite3_errmsg(db)); + eputf("%s\n", sqlite3_errmsg(db)); } return rc; @@ -20840,26 +23576,24 @@ static int lintDotCommand( return lintFkeyIndexes(pState, azArg, nArg); usage: - raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]); - raw_printf(stderr, "Where sub-commands are:\n"); - raw_printf(stderr, " fkey-indexes\n"); + eputf("Usage %s sub-command ?switches...?\n", azArg[0]); + eputz("Where sub-commands are:\n"); + eputz(" fkey-indexes\n"); return SQLITE_ERROR; } #if !defined SQLITE_OMIT_VIRTUALTABLE static void shellPrepare( - sqlite3 *db, - int *pRc, - const char *zSql, + sqlite3 *db, + int *pRc, + const char *zSql, sqlite3_stmt **ppStmt ){ *ppStmt = 0; if( *pRc==SQLITE_OK ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ - raw_printf(stderr, "sql error: %s (%d)\n", - sqlite3_errmsg(db), sqlite3_errcode(db) - ); + eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); *pRc = rc; } } @@ -20873,10 +23607,10 @@ static void shellPrepare( ** nuisance compiler warnings about "defined but not used". */ void shellPreparePrintf( - sqlite3 *db, - int *pRc, + sqlite3 *db, + int *pRc, sqlite3_stmt **ppStmt, - const char *zFmt, + const char *zFmt, ... ){ *ppStmt = 0; @@ -20902,7 +23636,7 @@ void shellPreparePrintf( ** nuisance compiler warnings about "defined but not used". */ void shellFinalize( - int *pRc, + int *pRc, sqlite3_stmt *pStmt ){ if( pStmt ){ @@ -20910,7 +23644,7 @@ void shellFinalize( int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + eputf("SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -20924,14 +23658,14 @@ void shellFinalize( ** nuisance compiler warnings about "defined but not used". */ void shellReset( - int *pRc, + int *pRc, sqlite3_stmt *pStmt ){ int rc = sqlite3_reset(pStmt); if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ sqlite3 *db = sqlite3_db_handle(pStmt); - raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + eputf("SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -20972,7 +23706,7 @@ static int arUsage(FILE *f){ } /* -** Print an error message for the .ar command to stderr and return +** Print an error message for the .ar command to stderr and return ** SQLITE_ERROR. */ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ @@ -20981,11 +23715,11 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); - utf8_printf(stderr, "Error: %s\n", z); + eputf("Error: %s\n", z); if( pAr->fromCmdLine ){ - utf8_printf(stderr, "Use \"-A\" for more help\n"); + eputz("Use \"-A\" for more help\n"); }else{ - utf8_printf(stderr, "Use \".archive --help\" for more help\n"); + eputz("Use \".archive --help\" for more help\n"); } sqlite3_free(z); return SQLITE_ERROR; @@ -21038,7 +23772,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ break; case AR_SWITCH_APPEND: pAr->bAppend = 1; - /* Fall thru into --file */ + deliberate_fall_through; case AR_SWITCH_FILE: pAr->zFile = zArg; break; @@ -21053,7 +23787,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ /* ** Parse the command line for an ".ar" command. The results are written into ** structure (*pAr). SQLITE_OK is returned if the command line is parsed -** successfully, otherwise an error message is written to stderr and +** successfully, otherwise an error message is written to stderr and ** SQLITE_ERROR returned. */ static int arParseCommand( @@ -21085,7 +23819,7 @@ static int arParseCommand( struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ - utf8_printf(stderr, "Wrong number of arguments. Usage:\n"); + eputz("Wrong number of arguments. Usage:\n"); return arUsage(stderr); }else{ char *z = azArg[1]; @@ -21190,7 +23924,10 @@ static int arParseCommand( } } } - + if( pAr->eCmd==0 ){ + eputz("Required argument missing. Usage:\n"); + return arUsage(stderr); + } return SQLITE_OK; } @@ -21231,7 +23968,7 @@ static int arCheckEntries(ArCommand *pAr){ } shellReset(&rc, pTest); if( rc==SQLITE_OK && bOk==0 ){ - utf8_printf(stderr, "not found in archive: %s\n", z); + eputf("not found in archive: %s\n", z); rc = SQLITE_ERROR; } } @@ -21249,7 +23986,7 @@ static int arCheckEntries(ArCommand *pAr){ ** when pAr->bGlob is false and GLOB match when pAr->bGlob is true. */ static void arWhereClause( - int *pRc, + int *pRc, ArCommand *pAr, char **pzWhere /* OUT: New WHERE clause */ ){ @@ -21264,7 +24001,7 @@ static void arWhereClause( for(i=0; i nArg; i++){ const char *z = pAr->azArg[i]; zWhere = sqlite3_mprintf( - "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'", + "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'", zWhere, zSep, zSameOp, z, strlen30(z)+1, zSameOp, z ); if( zWhere==0 ){ @@ -21279,10 +24016,10 @@ static void arWhereClause( } /* -** Implementation of .ar "lisT" command. +** Implementation of .ar "lisT" command. */ static int arListCommand(ArCommand *pAr){ - const char *zSql = "SELECT %s FROM %s WHERE %s"; + const char *zSql = "SELECT %s FROM %s WHERE %s"; const char *azCols[] = { "name", "lsmode(mode), sz, datetime(mtime, 'unixepoch'), name" @@ -21298,18 +24035,15 @@ static int arListCommand(ArCommand *pAr){ shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + oputf("%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s % 10d %s %s\n", - sqlite3_column_text(pSql, 0), - sqlite3_column_int(pSql, 1), - sqlite3_column_text(pSql, 2), - sqlite3_column_text(pSql, 3) - ); + oputf("%s % 10d %s %s\n", + sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), + sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); }else{ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + oputf("%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -21318,7 +24052,6 @@ static int arListCommand(ArCommand *pAr){ return rc; } - /* ** Implementation of .ar "Remove" command. */ @@ -21337,7 +24070,7 @@ static int arRemoveCommand(ArCommand *pAr){ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); @@ -21350,7 +24083,7 @@ static int arRemoveCommand(ArCommand *pAr){ } } if( zErr ){ - utf8_printf(stdout, "ERROR: %s\n", zErr); + sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */ sqlite3_free(zErr); } } @@ -21361,17 +24094,17 @@ static int arRemoveCommand(ArCommand *pAr){ } /* -** Implementation of .ar "eXtract" command. +** Implementation of .ar "eXtract" command. */ static int arExtractCommand(ArCommand *pAr){ - const char *zSql1 = + const char *zSql1 = "SELECT " " ($dir || name)," " writefile(($dir || name), %s, mode, mtime) " "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)" " AND name NOT GLOB '*..[/\\]*'"; - const char *azExtraArg[] = { + const char *azExtraArg[] = { "sqlar_uncompress(data, sz)", "data" }; @@ -21397,7 +24130,7 @@ static int arExtractCommand(ArCommand *pAr){ if( zDir==0 ) rc = SQLITE_NOMEM; } - shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, + shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere ); @@ -21414,11 +24147,11 @@ static int arExtractCommand(ArCommand *pAr){ j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); sqlite3_bind_int(pSql, j, i); if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); + oputf("%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( i==0 && pAr->bVerbose ){ - utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); + oputf("%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -21438,13 +24171,13 @@ static int arExtractCommand(ArCommand *pAr){ static int arExecSql(ArCommand *pAr, const char *zSql){ int rc; if( pAr->bDryRun ){ - utf8_printf(pAr->p->out, "%s\n", zSql); + oputf("%s\n", zSql); rc = SQLITE_OK; }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); if( zErr ){ - utf8_printf(stdout, "ERROR: %s\n", zErr); + sputf(stdout, "ERROR: %s\n", zErr); sqlite3_free(zErr); } } @@ -21475,7 +24208,7 @@ static int arCreateOrUpdateCommand( int bUpdate, /* true for a --create. */ int bOnlyIfChanged /* Only update if file has changed */ ){ - const char *zCreate = + const char *zCreate = "CREATE TABLE IF NOT EXISTS sqlar(\n" " name TEXT PRIMARY KEY, -- name of the file\n" " mode INT, -- access permissions\n" @@ -21517,7 +24250,7 @@ static int arCreateOrUpdateCommand( arExecSql(pAr, "PRAGMA page_size=512"); rc = arExecSql(pAr, "SAVEPOINT ar;"); if( rc!=SQLITE_OK ) return rc; - zTemp[0] = 0; + zTemp[0] = 0; if( pAr->bZip ){ /* Initialize the zipfile virtual table, if necessary */ if( pAr->zFile ){ @@ -21611,7 +24344,7 @@ static int arDotCommand( }else if( cmd.zFile ){ int flags; if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS; - if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT + if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT || cmd.eCmd==AR_CMD_REMOVE || cmd.eCmd==AR_CMD_UPDATE ){ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; }else{ @@ -21619,15 +24352,13 @@ static int arDotCommand( } cmd.db = 0; if( cmd.bDryRun ){ - utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile, - eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); + oputf("-- open database '%s'%s\n", cmd.zFile, + eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); } - rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, + rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "cannot open file: %s (%s)\n", - cmd.zFile, sqlite3_errmsg(cmd.db) - ); + eputf("cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db)); goto end_ar_command; } sqlite3_fileio_init(cmd.db, 0, 0); @@ -21640,7 +24371,7 @@ static int arDotCommand( if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ - utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); + eputz("database does not contain an 'sqlar' table\n"); rc = SQLITE_ERROR; goto end_ar_command; } @@ -21698,7 +24429,7 @@ end_ar_command: */ static int recoverSqlCb(void *pCtx, const char *zSql){ ShellState *pState = (ShellState*)pCtx; - utf8_printf(pState->out, "%s;\n", zSql); + sputf(pState->out, "%s;\n", zSql); return SQLITE_OK; } @@ -21741,7 +24472,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ bRowids = 0; } else{ - utf8_printf(stderr, "unexpected option: %s\n", azArg[i]); + eputf("unexpected option: %s\n", azArg[i]); showHelp(pState->out, azArg[0]); return 1; } @@ -21760,7 +24491,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ const char *zErr = sqlite3_recover_errmsg(p); int errCode = sqlite3_recover_errcode(p); - raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode); + eputf("sql error: %s (%d)\n", zErr, errCode); } rc = sqlite3_recover_finish(p); return rc; @@ -21785,7 +24516,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ #define rc_err_oom_die(rc) \ if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ - fprintf(stderr,"E:%d\n",rc), assert(0) + eputf("E:%d\n",rc), assert(0) #else static void rc_err_oom_die(int rc){ if( rc==SQLITE_NOMEM ) shell_check_oom(0); @@ -21925,6 +24656,7 @@ FROM (\ sqlite3_exec(*pDb,"drop table if exists ColNames;" "drop view if exists RepeatedNames;",0,0,0); #endif +#undef SHELL_COLFIX_DB rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0); rc_err_oom_die(rc); } @@ -21956,7 +24688,7 @@ FROM (\ sqlite3_bind_int(pStmt, 1, nDigits); rc = sqlite3_step(pStmt); sqlite3_finalize(pStmt); - assert(rc==SQLITE_DONE); + if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); } assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); @@ -22024,7 +24756,6 @@ static int do_meta_command(char *zLine, ShellState *p){ azArg[nArg++] = &zLine[h]; while( zLine[h] && !IsSpace(zLine[h]) ){ h++; } if( zLine[h] ) zLine[h++] = 0; - resolve_backslashes(azArg[nArg-1]); } } azArg[nArg] = 0; @@ -22039,7 +24770,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_AUTHORIZATION if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ if( nArg!=2 ){ - raw_printf(stderr, "Usage: .auth ON|OFF\n"); + eputz("Usage: .auth ON|OFF\n"); rc = 1; goto meta_command_exit; } @@ -22086,7 +24817,7 @@ static int do_meta_command(char *zLine, ShellState *p){ bAsync = 1; }else { - utf8_printf(stderr, "unknown option: %s\n", azArg[j]); + eputf("unknown option: %s\n", azArg[j]); return 1; } }else if( zDestFile==0 ){ @@ -22095,19 +24826,19 @@ static int do_meta_command(char *zLine, ShellState *p){ zDb = zDestFile; zDestFile = azArg[j]; }else{ - raw_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); + eputz("Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); return 1; } } if( zDestFile==0 ){ - raw_printf(stderr, "missing FILENAME argument on .backup\n"); + eputz("missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; - rc = sqlite3_open_v2(zDestFile, &pDest, + rc = sqlite3_open_v2(zDestFile, &pDest, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile); + eputf("Error: cannot open \"%s\"\n", zDestFile); close_db(pDest); return 1; } @@ -22118,7 +24849,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); + eputf("Error: %s\n", sqlite3_errmsg(pDest)); close_db(pDest); return 1; } @@ -22127,7 +24858,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_DONE ){ rc = 0; }else{ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest)); + eputf("Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } close_db(pDest); @@ -22138,11 +24869,12 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ bail_on_error = booleanValue(azArg[1]); }else{ - raw_printf(stderr, "Usage: .bail on|off\n"); + eputz("Usage: .bail on|off\n"); rc = 1; } }else + /* Undocumented. Legacy only. See "crnl" below */ if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){ if( nArg==2 ){ if( booleanValue(azArg[1]) ){ @@ -22151,7 +24883,8 @@ static int do_meta_command(char *zLine, ShellState *p){ setTextMode(p->out, 1); } }else{ - raw_printf(stderr, "Usage: .binary on|off\n"); + eputz("The \".binary\" command is deprecated. Use \".crnl\" instead.\n" + "Usage: .binary on|off\n"); rc = 1; } }else @@ -22175,11 +24908,11 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = chdir(azArg[1]); #endif if( rc ){ - utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]); + eputf("Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ - raw_printf(stderr, "Usage: .cd DIRECTORY\n"); + eputz("Usage: .cd DIRECTORY\n"); rc = 1; } }else @@ -22189,7 +24922,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_CountChanges, azArg[1]); }else{ - raw_printf(stderr, "Usage: .changes on|off\n"); + eputz("Usage: .changes on|off\n"); rc = 1; } }else @@ -22203,18 +24936,16 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zRes = 0; output_reset(p); if( nArg!=2 ){ - raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); + eputz("Usage: .check GLOB-PATTERN\n"); rc = 2; }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ - raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); rc = 2; }else if( testcase_glob(azArg[1],zRes)==0 ){ - utf8_printf(stderr, - "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", - p->zTestcase, azArg[1], zRes); + eputf("testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", + p->zTestcase, azArg[1], zRes); rc = 1; }else{ - utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase); + oputf("testcase-%s ok\n", p->zTestcase); p->nCheck++; } sqlite3_free(zRes); @@ -22227,7 +24958,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ tryToClone(p, azArg[1]); }else{ - raw_printf(stderr, "Usage: .clone FILENAME\n"); + eputz("Usage: .clone FILENAME\n"); rc = 1; } }else @@ -22247,9 +24978,9 @@ static int do_meta_command(char *zLine, ShellState *p){ zFile = "(temporary-file)"; } if( p->pAuxDb == &p->aAuxDb[i] ){ - utf8_printf(stdout, "ACTIVE %d: %s\n", i, zFile); + sputf(stdout, "ACTIVE %d: %s\n", i, zFile); }else if( p->aAuxDb[i].db!=0 ){ - utf8_printf(stdout, " %d: %s\n", i, zFile); + sputf(stdout, " %d: %s\n", i, zFile); } } }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ @@ -22266,7 +24997,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( i<0 || i>=ArraySize(p->aAuxDb) ){ /* No-op */ }else if( p->pAuxDb == &p->aAuxDb[i] ){ - raw_printf(stderr, "cannot close the active database connection\n"); + eputz("cannot close the active database connection\n"); rc = 1; }else if( p->aAuxDb[i].db ){ session_close_all(p, i); @@ -22274,7 +25005,23 @@ static int do_meta_command(char *zLine, ShellState *p){ p->aAuxDb[i].db = 0; } }else{ - raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n"); + eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n"); + rc = 1; + } + }else + + if( c=='c' && n==4 && cli_strncmp(azArg[0], "crnl", n)==0 ){ + if( nArg==2 ){ + if( booleanValue(azArg[1]) ){ + setTextMode(p->out, 1); + }else{ + setBinaryMode(p->out, 1); + } + }else{ +#if !defined(_WIN32) && !defined(WIN32) + eputz("The \".crnl\" is a no-op on non-Windows machines.\n"); +#endif + eputz("Usage: .crnl on|off\n"); rc = 1; } }else @@ -22287,7 +25034,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; }else{ while( sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -22306,11 +25053,9 @@ static int do_meta_command(char *zLine, ShellState *p){ int eTxn = sqlite3_txn_state(p->db, azName[i*2]); int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); const char *z = azName[i*2+1]; - utf8_printf(p->out, "%s: %s %s%s\n", - azName[i*2], - z && z[0] ? z : "\"\"", - bRdonly ? "r/o" : "r/w", - eTxn==SQLITE_TXN_NONE ? "" : + oputf("%s: %s %s%s\n", + azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", + eTxn==SQLITE_TXN_NONE ? "" : eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); free(azName[i*2]); free(azName[i*2+1]); @@ -22336,6 +25081,8 @@ static int do_meta_command(char *zLine, ShellState *p){ { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION }, { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE }, { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE }, + { "reverse_scanorder", SQLITE_DBCONFIG_REVERSE_SCANORDER }, + { "stmt_scanstatus", SQLITE_DBCONFIG_STMT_SCANSTATUS }, { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP }, { "trusted_schema", SQLITE_DBCONFIG_TRUSTED_SCHEMA }, { "writable_schema", SQLITE_DBCONFIG_WRITABLE_SCHEMA }, @@ -22348,13 +25095,13 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); } sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ - utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); - utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); - } + eputf("Error: unknown dbconfig \"%s\"\n", azArg[1]); + eputz("Enter \".dbconfig\" with no arguments for a list\n"); + } }else #if SQLITE_SHELL_HAVE_RECOVER @@ -22383,8 +25130,8 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[0]=='-' ) z++; if( cli_strcmp(z,"preserve-rowids")==0 ){ #ifdef SQLITE_OMIT_VIRTUALTABLE - raw_printf(stderr, "The --preserve-rowids option is not compatible" - " with SQLITE_OMIT_VIRTUALTABLE\n"); + eputz("The --preserve-rowids option is not compatible" + " with SQLITE_OMIT_VIRTUALTABLE\n"); rc = 1; sqlite3_free(zLike); goto meta_command_exit; @@ -22402,7 +25149,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ShellSetFlag(p, SHFLG_DumpNoSys); }else { - raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]); + eputf("Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; sqlite3_free(zLike); goto meta_command_exit; @@ -22421,7 +25168,7 @@ static int do_meta_command(char *zLine, ShellState *p){ " substr(o.name, 1, length(name)+1) == (name||'_')" ")", azArg[i], azArg[i] ); - + if( zLike ){ zLike = sqlite3_mprintf("%z OR %z", zLike, zExpr); }else{ @@ -22436,8 +25183,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); - raw_printf(p->out, "BEGIN TRANSACTION;\n"); + oputz("PRAGMA foreign_keys=OFF;\n"); + oputz("BEGIN TRANSACTION;\n"); } p->writableSchema = 0; p->showHeader = 0; @@ -22468,13 +25215,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_free(zLike); if( p->writableSchema ){ - raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); + oputz("PRAGMA writable_schema=OFF;\n"); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); } p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; @@ -22484,7 +25231,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ setOrClearFlag(p, SHFLG_Echo, azArg[1]); }else{ - raw_printf(stderr, "Usage: .echo on|off\n"); + eputz("Usage: .echo on|off\n"); rc = 1; } }else @@ -22515,7 +25262,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->autoEQP = (u8)booleanValue(azArg[1]); } }else{ - raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n"); + eputz("Usage: .eqp off|on|trace|trigger|full\n"); rc = 1; } }else @@ -22554,9 +25301,8 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ if( p->bSafeMode ){ - raw_printf(stderr, - "Cannot run experimental commands such as \"%s\" in safe mode\n", - azArg[0]); + eputf("Cannot run experimental commands such as \"%s\" in safe mode\n", + azArg[0]); rc = 1; }else{ open_db(p, 0); @@ -22573,7 +25319,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } aCtrl[] = { { "chunk_size", SQLITE_FCNTL_CHUNK_SIZE, "SIZE" }, { "data_version", SQLITE_FCNTL_DATA_VERSION, "" }, - { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, + { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" }, { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" }, { "persist_wal", SQLITE_FCNTL_PERSIST_WAL, "[BOOLEAN]" }, /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/ @@ -22594,7 +25340,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); zCmd = nArg>=2 ? azArg[1] : "help"; - if( zCmd[0]=='-' + if( zCmd[0]=='-' && (cli_strcmp(zCmd,"--schema")==0 || cli_strcmp(zCmd,"-schema")==0) && nArg>=4 ){ @@ -22612,10 +25358,9 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all file-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available file-controls:\n"); + oputz("Available file-controls:\n"); for(i=0; i out, " .filectrl %s %s\n", - aCtrl[i].zCtrlName, aCtrl[i].zUsage); + oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; goto meta_command_exit; @@ -22630,16 +25375,16 @@ static int do_meta_command(char *zLine, ShellState *p){ filectrl = aCtrl[i].ctrlCode; iCtrl = i; }else{ - utf8_printf(stderr, "Error: ambiguous file-control: \"%s\"\n" - "Use \".filectrl --help\" for help\n", zCmd); + eputf("Error: ambiguous file-control: \"%s\"\n" + "Use \".filectrl --help\" for help\n", zCmd); rc = 1; goto meta_command_exit; } } } if( filectrl<0 ){ - utf8_printf(stderr,"Error: unknown file-control: %s\n" - "Use \".filectrl --help\" for help\n", zCmd); + eputf("Error: unknown file-control: %s\n" + "Use \".filectrl --help\" for help\n", zCmd); }else{ switch(filectrl){ case SQLITE_FCNTL_SIZE_LIMIT: { @@ -22682,7 +25427,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg!=2 ) break; sqlite3_file_control(p->db, zSchema, filectrl, &z); if( z ){ - utf8_printf(p->out, "%s\n", z); + oputf("%s\n", z); sqlite3_free(z); } isOk = 2; @@ -22696,19 +25441,19 @@ static int do_meta_command(char *zLine, ShellState *p){ } x = -1; sqlite3_file_control(p->db, zSchema, filectrl, &x); - utf8_printf(p->out,"%d\n", x); + oputf("%d\n", x); isOk = 2; break; } } } if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ char zBuf[100]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - raw_printf(p->out, "%s\n", zBuf); + oputf("%s\n", zBuf); } }else @@ -22723,7 +25468,7 @@ static int do_meta_command(char *zLine, ShellState *p){ nArg = 1; } if( nArg!=1 ){ - raw_printf(stderr, "Usage: .fullschema ?--indent?\n"); + eputz("Usage: .fullschema ?--indent?\n"); rc = 1; goto meta_command_exit; } @@ -22743,19 +25488,21 @@ static int do_meta_command(char *zLine, ShellState *p){ "SELECT rowid FROM sqlite_schema" " WHERE name GLOB 'sqlite_stat[134]'", -1, &pStmt, 0); - doStats = sqlite3_step(pStmt)==SQLITE_ROW; - sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ){ + doStats = sqlite3_step(pStmt)==SQLITE_ROW; + sqlite3_finalize(pStmt); + } } if( doStats==0 ){ - raw_printf(p->out, "/* No STAT tables available */\n"); + oputz("/* No STAT tables available */\n"); }else{ - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + oputz("ANALYZE sqlite_schema;\n"); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - raw_printf(p->out, "ANALYZE sqlite_schema;\n"); + oputz("ANALYZE sqlite_schema;\n"); } }else @@ -22764,7 +25511,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->showHeader = booleanValue(azArg[1]); p->shellFlgs |= SHFLG_HeaderSet; }else{ - raw_printf(stderr, "Usage: .headers on|off\n"); + eputz("Usage: .headers on|off\n"); rc = 1; } }else @@ -22773,7 +25520,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg>=2 ){ n = showHelp(p->out, azArg[1]); if( n==0 ){ - utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]); + oputf("Nothing matches '%s'\n", azArg[1]); } }else{ showHelp(p->out, 0); @@ -22817,7 +25564,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zTable==0 ){ zTable = z; }else{ - utf8_printf(p->out, "ERROR: extra argument: \"%s\". Usage:\n", z); + oputf("ERROR: extra argument: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } @@ -22838,14 +25585,14 @@ static int do_meta_command(char *zLine, ShellState *p){ xRead = csv_read_one_field; useOutputMode = 0; }else{ - utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); + oputf("ERROR: unknown option: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } } if( zTable==0 ){ - utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n", - zFile==0 ? "FILE" : "TABLE"); + oputf("ERROR: missing %s argument. Usage:\n", + zFile==0 ? "FILE" : "TABLE"); showHelp(p->out, "import"); goto meta_command_exit; } @@ -22856,20 +25603,17 @@ static int do_meta_command(char *zLine, ShellState *p){ ** the column and row separator characters from the output mode. */ nSep = strlen30(p->colSeparator); if( nSep==0 ){ - raw_printf(stderr, - "Error: non-null column separator required for import\n"); + eputz("Error: non-null column separator required for import\n"); goto meta_command_exit; } if( nSep>1 ){ - raw_printf(stderr, - "Error: multi-character column separators not allowed" + eputz("Error: multi-character column separators not allowed" " for import\n"); goto meta_command_exit; } nSep = strlen30(p->rowSeparator); if( nSep==0 ){ - raw_printf(stderr, - "Error: non-null row separator required for import\n"); + eputz("Error: non-null row separator required for import\n"); goto meta_command_exit; } if( nSep==2 && p->mode==MODE_Csv @@ -22883,18 +25627,18 @@ static int do_meta_command(char *zLine, ShellState *p){ nSep = strlen30(p->rowSeparator); } if( nSep>1 ){ - raw_printf(stderr, "Error: multi-character row separators not allowed" - " for import\n"); + eputz("Error: multi-character row separators not allowed" + " for import\n"); goto meta_command_exit; } - sCtx.cColSep = p->colSeparator[0]; - sCtx.cRowSep = p->rowSeparator[0]; + sCtx.cColSep = (u8)p->colSeparator[0]; + sCtx.cRowSep = (u8)p->rowSeparator[0]; } sCtx.zFile = zFile; sCtx.nLine = 1; if( sCtx.zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + eputz("Error: pipes are not supported in this OS\n"); goto meta_command_exit; #else sCtx.in = popen(sCtx.zFile+1, "r"); @@ -22906,19 +25650,19 @@ static int do_meta_command(char *zLine, ShellState *p){ sCtx.xCloser = fclose; } if( sCtx.in==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); + eputf("Error: cannot open \"%s\"\n", zFile); goto meta_command_exit; } if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; - utf8_printf(p->out, "Column separator "); - output_c_string(p->out, zSep); - utf8_printf(p->out, ", row separator "); + oputz("Column separator "); + output_c_string(zSep); + oputz(", row separator "); zSep[0] = sCtx.cRowSep; - output_c_string(p->out, zSep); - utf8_printf(p->out, "\n"); + output_c_string(zSep); + oputz("\n"); } sCtx.z = sqlite3_malloc64(120); if( sCtx.z==0 ){ @@ -22953,14 +25697,14 @@ static int do_meta_command(char *zLine, ShellState *p){ } zColDefs = zAutoColumn(0, &dbCols, &zRenames); if( zRenames!=0 ){ - utf8_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr, - "Columns renamed during .import %s due to duplicates:\n" - "%s\n", sCtx.zFile, zRenames); + sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr, + "Columns renamed during .import %s due to duplicates:\n" + "%s\n", sCtx.zFile, zRenames); sqlite3_free(zRenames); } assert(dbCols==0); if( zColDefs==0 ){ - utf8_printf(stderr,"%s: empty file\n", sCtx.zFile); + eputf("%s: empty file\n", sCtx.zFile); import_fail: sqlite3_free(zCreate); sqlite3_free(zSql); @@ -22971,11 +25715,11 @@ static int do_meta_command(char *zLine, ShellState *p){ } zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); if( eVerbose>=1 ){ - utf8_printf(p->out, "%s\n", zCreate); + oputf("%s\n", zCreate); } rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); if( rc ){ - utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); + eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); goto import_fail; } sqlite3_free(zCreate); @@ -22984,7 +25728,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( rc ){ if (pStmt) sqlite3_finalize(pStmt); - utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); goto import_fail; } sqlite3_free(zSql); @@ -23006,11 +25750,11 @@ static int do_meta_command(char *zLine, ShellState *p){ zSql[j++] = ')'; zSql[j] = 0; if( eVerbose>=2 ){ - utf8_printf(p->out, "Insert using: %s\n", zSql); + oputf("Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); if( rc ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); if (pStmt) sqlite3_finalize(pStmt); goto import_fail; } @@ -23033,11 +25777,19 @@ static int do_meta_command(char *zLine, ShellState *p){ ** the remaining columns. */ if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; + /* + ** For CSV mode, per RFC 4180, accept EOF in lieu of final + ** record terminator but only for last field of multi-field row. + ** (If there are too few fields, it's not valid CSV anyway.) + */ + if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ + z = ""; + } sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); if( i =nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile, - startLine, sqlite3_errmsg(p->db)); + eputf("%s:%d: INSERT failed: %s\n", + sCtx.zFile, startLine, sqlite3_errmsg(p->db)); sCtx.nErr++; }else{ sCtx.nRow++; @@ -23068,9 +25819,8 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( eVerbose>0 ){ - utf8_printf(p->out, - "Added %d rows with %d errors using %d lines of input\n", - sCtx.nRow, sCtx.nErr, sCtx.nLine-1); + oputf("Added %d rows with %d errors using %d lines of input\n", + sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ @@ -23084,9 +25834,15 @@ static int do_meta_command(char *zLine, ShellState *p){ int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */ int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ int i; + if( !ShellHasFlag(p,SHFLG_TestingMode) ){ + eputf(".%s unavailable without --unsafe-testing\n", + "imposter"); + rc = 1; + goto meta_command_exit; + } if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){ - utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n" - " .imposter off\n"); + eputz("Usage: .imposter INDEX IMPOSTER\n" + " .imposter off\n"); /* Also allowed, but not documented: ** ** .imposter TABLE IMPOSTER @@ -23145,7 +25901,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_finalize(pStmt); if( i==0 || tnum==0 ){ - utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]); + eputf("no such index: \"%s\"\n", azArg[1]); rc = 1; sqlite3_free(zCollist); goto meta_command_exit; @@ -23160,16 +25916,14 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); if( rc ){ - utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); + eputf("Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); }else{ - utf8_printf(stdout, "%s;\n", zSql); - raw_printf(stdout, - "WARNING: writing to an imposter table will corrupt the \"%s\" %s!\n", - azArg[1], isWO ? "table" : "index" - ); + sputf(stdout, "%s;\n", zSql); + sputf(stdout, "WARNING: writing to an imposter table will corrupt" + " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index"); } }else{ - raw_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); + eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); rc = 1; } sqlite3_free(zSql); @@ -23189,7 +25943,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else{ iotrace = fopen(azArg[1], "w"); if( iotrace==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); + eputf("Error: cannot open \"%s\"\n", azArg[1]); sqlite3IoTrace = 0; rc = 1; }else{ @@ -23221,11 +25975,11 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); if( nArg==1 ){ for(i=0; i db, aLimit[i].limitCode, -1)); + sputf(stdout, "%20s %d\n", aLimit[i].zLimitName, + sqlite3_limit(p->db, aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ - raw_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n"); + eputz("Usage: .limit NAME ?NEW-VALUE?\n"); rc = 1; goto meta_command_exit; }else{ @@ -23236,16 +25990,16 @@ static int do_meta_command(char *zLine, ShellState *p){ if( iLimit<0 ){ iLimit = i; }else{ - utf8_printf(stderr, "ambiguous limit: \"%s\"\n", azArg[1]); + eputf("ambiguous limit: \"%s\"\n", azArg[1]); rc = 1; goto meta_command_exit; } } } if( iLimit<0 ){ - utf8_printf(stderr, "unknown limit: \"%s\"\n" - "enter \".limits\" with no arguments for a list.\n", - azArg[1]); + eputf("unknown limit: \"%s\"\n" + "enter \".limits\" with no arguments for a list.\n", + azArg[1]); rc = 1; goto meta_command_exit; } @@ -23253,8 +26007,8 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_limit(p->db, aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } - printf("%20s %d\n", aLimit[iLimit].zLimitName, - sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); + sputf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, + sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); } }else @@ -23268,8 +26022,9 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zFile, *zProc; char *zErrMsg = 0; failIfSafeMode(p, "cannot run .load in safe mode"); - if( nArg<2 ){ - raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n"); + if( nArg<2 || azArg[1][0]==0 ){ + /* Must have a non-empty FILE. (Will not load self.) */ + eputz("Usage: .load FILE ?ENTRYPOINT?\n"); rc = 1; goto meta_command_exit; } @@ -23278,26 +26033,32 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "Error: %s\n", zErrMsg); + eputf("Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif -#ifndef SQLITE_SHELL_FIDDLE if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){ - failIfSafeMode(p, "cannot run .log in safe mode"); if( nArg!=2 ){ - raw_printf(stderr, "Usage: .log FILENAME\n"); + eputz("Usage: .log FILENAME\n"); rc = 1; }else{ const char *zFile = azArg[1]; + if( p->bSafeMode + && cli_strcmp(zFile,"on")!=0 + && cli_strcmp(zFile,"off")!=0 + ){ + sputz(stdout, "cannot set .log to anything other" + " than \"on\" or \"off\"\n"); + zFile = "off"; + } output_file_close(p->pLog); + if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; p->pLog = output_file_open(zFile, 0); } }else -#endif if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ const char *zMode = 0; @@ -23329,17 +26090,17 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zTabname==0 ){ zTabname = z; }else if( z[0]=='-' ){ - utf8_printf(stderr, "unknown option: %s\n", z); - utf8_printf(stderr, "options:\n" - " --noquote\n" - " --quote\n" - " --wordwrap on/off\n" - " --wrap N\n" - " --ww\n"); + eputf("unknown option: %s\n", z); + eputz("options:\n" + " --noquote\n" + " --quote\n" + " --wordwrap on/off\n" + " --wrap N\n" + " --ww\n"); rc = 1; goto meta_command_exit; }else{ - utf8_printf(stderr, "extra argument: \"%s\"\n", z); + eputf("extra argument: \"%s\"\n", z); rc = 1; goto meta_command_exit; } @@ -23348,14 +26109,12 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - raw_printf - (p->out, - "current output mode: %s --wrap %d --wordwrap %s --%squote\n", - modeDescr[p->mode], p->cmOpts.iWrap, - p->cmOpts.bWordWrap ? "on" : "off", - p->cmOpts.bQuote ? "" : "no"); + oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n", + modeDescr[p->mode], p->cmOpts.iWrap, + p->cmOpts.bWordWrap ? "on" : "off", + p->cmOpts.bQuote ? "" : "no"); }else{ - raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]); + oputf("current output mode: %s\n", modeDescr[p->mode]); } zMode = modeDescr[p->mode]; } @@ -23414,9 +26173,9 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( cli_strncmp(zMode,"json",n2)==0 ){ p->mode = MODE_Json; }else{ - raw_printf(stderr, "Error: mode should be one of: " - "ascii box column csv html insert json line list markdown " - "qbox quote table tabs tcl\n"); + eputz("Error: mode should be one of: " + "ascii box column csv html insert json line list markdown " + "qbox quote table tabs tcl\n"); rc = 1; } p->cMode = p->mode; @@ -23425,11 +26184,11 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_SHELL_FIDDLE if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){ if( nArg!=2 ){ - raw_printf(stderr, "Usage: .nonce NONCE\n"); + eputz("Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ - raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n", - p->lineno, azArg[1]); + eputf("line %d: incorrect nonce: \"%s\"\n", + p->lineno, azArg[1]); exit(1); }else{ p->bSafeMode = 0; @@ -23444,7 +26203,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_snprintf(sizeof(p->nullValue), p->nullValue, "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]); }else{ - raw_printf(stderr, "Usage: .nullvalue STRING\n"); + eputz("Usage: .nullvalue STRING\n"); rc = 1; } }else @@ -23483,11 +26242,11 @@ static int do_meta_command(char *zLine, ShellState *p){ }else #endif /* !SQLITE_SHELL_FIDDLE */ if( z[0]=='-' ){ - utf8_printf(stderr, "unknown option: %s\n", z); + eputf("unknown option: %s\n", z); rc = 1; goto meta_command_exit; }else if( zFN ){ - utf8_printf(stderr, "extra argument: \"%s\"\n", z); + eputf("extra argument: \"%s\"\n", z); rc = 1; goto meta_command_exit; }else{ @@ -23529,7 +26288,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->pAuxDb->zDbFilename = zNewFilename; open_db(p, OPEN_DB_KEEPALIVE); if( p->db==0 ){ - utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename); + eputf("Error: cannot open '%s'\n", zNewFilename); sqlite3_free(zNewFilename); }else{ p->pAuxDb->zFreeOnClose = zNewFilename; @@ -23553,9 +26312,9 @@ static int do_meta_command(char *zLine, ShellState *p){ int i; int eMode = 0; int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ - unsigned char zBOM[4]; /* Byte-order mark to using if --bom is present */ + static const char *zBomUtf8 = "\xef\xbb\xbf"; + const char *zBom = 0; - zBOM[0] = 0; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( c=='e' ){ eMode = 'x'; @@ -23568,17 +26327,13 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[0]=='-' ){ if( z[1]=='-' ) z++; if( cli_strcmp(z,"-bom")==0 ){ - zBOM[0] = 0xef; - zBOM[1] = 0xbb; - zBOM[2] = 0xbf; - zBOM[3] = 0; + zBom = zBomUtf8; }else if( c!='e' && cli_strcmp(z,"-x")==0 ){ eMode = 'x'; /* spreadsheet */ }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ eMode = 'e'; /* text editor */ }else{ - utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", - azArg[i]); + oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; @@ -23590,8 +26345,7 @@ static int do_meta_command(char *zLine, ShellState *p){ break; } }else{ - utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n", - azArg[i]); + oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; sqlite3_free(zFile); @@ -23630,30 +26384,30 @@ static int do_meta_command(char *zLine, ShellState *p){ shell_check_oom(zFile); if( zFile[0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + eputz("Error: pipes are not supported in this OS\n"); rc = 1; - p->out = stdout; + output_redir(p, stdout); #else - p->out = popen(zFile + 1, "w"); - if( p->out==0 ){ - utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); - p->out = stdout; + FILE *pfPipe = popen(zFile + 1, "w"); + if( pfPipe==0 ){ + eputf("Error: cannot open pipe \"%s\"\n", zFile + 1); rc = 1; }else{ - if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); + output_redir(p, pfPipe); + if( zBom ) oputz(zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif }else{ - p->out = output_file_open(zFile, bTxtMode); - if( p->out==0 ){ + FILE *pfFile = output_file_open(zFile, bTxtMode); + if( pfFile==0 ){ if( cli_strcmp(zFile,"off")!=0 ){ - utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); + eputf("Error: cannot write to \"%s\"\n", zFile); } - p->out = stdout; rc = 1; } else { - if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out); + output_redir(p, pfFile); + if( zBom ) oputz(zBom); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } @@ -23694,8 +26448,8 @@ static int do_meta_command(char *zLine, ShellState *p){ "SELECT key, quote(value) " "FROM temp.sqlite_parameters;", -1, &pStmt, 0); while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), - sqlite3_column_text(pStmt,1)); + oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), + sqlite3_column_text(pStmt,1)); } sqlite3_finalize(pStmt); } @@ -23739,7 +26493,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rx!=SQLITE_OK ){ - utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); + oputf("Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); pStmt = 0; rc = 1; @@ -23768,10 +26522,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i 1 ) raw_printf(p->out, " "); - utf8_printf(p->out, "%s", azArg[i]); + if( i>1 ) oputz(" "); + oputz(azArg[i]); } - raw_printf(p->out, "\n"); + oputz("\n"); }else #ifndef SQLITE_OMIT_PROGRESS_CALLBACK @@ -23800,7 +26554,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } if( cli_strcmp(z,"limit")==0 ){ if( i+1>=nArg ){ - utf8_printf(stderr, "Error: missing argument on --limit\n"); + eputz("Error: missing argument on --limit\n"); rc = 1; goto meta_command_exit; }else{ @@ -23808,7 +26562,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } continue; } - utf8_printf(stderr, "Error: unknown option: \"%s\"\n", azArg[i]); + eputf("Error: unknown option: \"%s\"\n", azArg[i]); rc = 1; goto meta_command_exit; }else{ @@ -23822,10 +26576,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && cli_strncmp(azArg[0], "prompt", n)==0 ){ if( nArg >= 2) { - strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); + shell_strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); } if( nArg >= 3) { - strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); + shell_strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1); } }else @@ -23841,19 +26595,19 @@ static int do_meta_command(char *zLine, ShellState *p){ int savedLineno = p->lineno; failIfSafeMode(p, "cannot run .read in safe mode"); if( nArg!=2 ){ - raw_printf(stderr, "Usage: .read FILE\n"); + eputz("Usage: .read FILE\n"); rc = 1; goto meta_command_exit; } if( azArg[1][0]=='|' ){ #ifdef SQLITE_OMIT_POPEN - raw_printf(stderr, "Error: pipes are not supported in this OS\n"); + eputz("Error: pipes are not supported in this OS\n"); rc = 1; p->out = stdout; #else p->in = popen(azArg[1]+1, "r"); if( p->in==0 ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); + eputf("Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p); @@ -23861,7 +26615,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } #endif }else if( (p->in = openChrSource(azArg[1]))==0 ){ - utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); + eputf("Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p); @@ -23888,20 +26642,20 @@ static int do_meta_command(char *zLine, ShellState *p){ zSrcFile = azArg[2]; zDb = azArg[1]; }else{ - raw_printf(stderr, "Usage: .restore ?DB? FILE\n"); + eputz("Usage: .restore ?DB? FILE\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ - utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); + eputf("Error: cannot open \"%s\"\n", zSrcFile); close_db(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); close_db(pSrc); return 1; } @@ -23916,10 +26670,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_DONE ){ rc = 0; }else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){ - raw_printf(stderr, "Error: source database is busy\n"); + eputz("Error: source database is busy\n"); rc = 1; }else{ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } close_db(pSrc); @@ -23928,12 +26682,27 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){ if( nArg==2 ){ - p->scanstatsOn = (u8)booleanValue(azArg[1]); -#ifndef SQLITE_ENABLE_STMT_SCANSTATUS - raw_printf(stderr, "Warning: .scanstats not available in this build.\n"); + if( cli_strcmp(azArg[1], "vm")==0 ){ + p->scanstatsOn = 3; + }else + if( cli_strcmp(azArg[1], "est")==0 ){ + p->scanstatsOn = 2; + }else{ + p->scanstatsOn = (u8)booleanValue(azArg[1]); + } + open_db(p, 0); + sqlite3_db_config( + p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0 + ); +#if !defined(SQLITE_ENABLE_STMT_SCANSTATUS) + eputz("Warning: .scanstats not available in this build.\n"); +#elif !defined(SQLITE_ENABLE_BYTECODE_VTAB) + if( p->scanstatsOn==3 ){ + eputz("Warning: \".scanstats vm\" not available in this build.\n"); + } #endif }else{ - raw_printf(stderr, "Usage: .scanstats on|off\n"); + eputz("Usage: .scanstats on|off|est\n"); rc = 1; } }else @@ -23962,13 +26731,13 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( optionMatch(azArg[ii],"nosys") ){ bNoSystemTabs = 1; }else if( azArg[ii][0]=='-' ){ - utf8_printf(stderr, "Unknown option: \"%s\"\n", azArg[ii]); + eputf("Unknown option: \"%s\"\n", azArg[ii]); rc = 1; goto meta_command_exit; }else if( zName==0 ){ zName = azArg[ii]; }else{ - raw_printf(stderr, "Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n"); + eputz("Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } @@ -24001,7 +26770,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ - utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db)); + eputf("Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); rc = 1; goto meta_command_exit; @@ -24063,18 +26832,18 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&sSelect, "sql IS NOT NULL" " ORDER BY snum, rowid", 0); if( bDebug ){ - utf8_printf(p->out, "SQL: %s;\n", sSelect.z); + oputf("SQL: %s;\n", sSelect.z); }else{ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); } freeText(&sSelect); } if( zErrMsg ){ - utf8_printf(stderr,"Error: %s\n", zErrMsg); + eputf("Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ - raw_printf(stderr,"Error: querying schema information\n"); + eputz("Error: querying schema information\n"); rc = 1; }else{ rc = 0; @@ -24084,7 +26853,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( (c=='s' && n==11 && cli_strncmp(azArg[0], "selecttrace", n)==0) || (c=='t' && n==9 && cli_strncmp(azArg[0], "treetrace", n)==0) ){ - unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff; + unsigned int x = nArg>=2? (unsigned int)integerValue(azArg[1]) : 0xffffffff; sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x); }else @@ -24120,11 +26889,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ){ session_not_open: - raw_printf(stderr, "ERROR: No sessions are open\n"); + eputz("ERROR: No sessions are open\n"); }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ - raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc); + eputf("ERROR: sqlite3session_attach() returns %d\n",rc); rc = 0; } } @@ -24143,8 +26912,8 @@ static int do_meta_command(char *zLine, ShellState *p){ if( pSession->p==0 ) goto session_not_open; out = fopen(azCmd[1], "wb"); if( out==0 ){ - utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n", - azCmd[1]); + eputf("ERROR: cannot open \"%s\" for writing\n", + azCmd[1]); }else{ int szChng; void *pChng; @@ -24154,13 +26923,12 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); } if( rc ){ - printf("Error: error code %d\n", rc); + sputf(stdout, "Error: error code %d\n", rc); rc = 0; } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ - raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", - szChng); + eputf("ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); fclose(out); @@ -24187,8 +26955,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_enable(pSession->p, ii); - utf8_printf(p->out, "session %s enable flag = %d\n", - pSession->zName, ii); + oputf("session %s enable flag = %d\n", pSession->zName, ii); } }else @@ -24205,10 +26972,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_free(pSession->azFilter); nByte = sizeof(pSession->azFilter[0])*(nCmd-1); pSession->azFilter = sqlite3_malloc( nByte ); - if( pSession->azFilter==0 ){ - raw_printf(stderr, "Error: out or memory\n"); - exit(1); - } + shell_check_oom( pSession->azFilter ); for(ii=1; ii azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]); shell_check_oom(x); @@ -24226,8 +26990,7 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_indirect(pSession->p, ii); - utf8_printf(p->out, "session %s indirect flag = %d\n", - pSession->zName, ii); + oputf("session %s indirect flag = %d\n", pSession->zName, ii); } }else @@ -24239,8 +27002,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=1 ) goto session_syntax_error; if( pAuxDb->nSession ){ ii = sqlite3session_isempty(pSession->p); - utf8_printf(p->out, "session %s isempty flag = %d\n", - pSession->zName, ii); + oputf("session %s isempty flag = %d\n", pSession->zName, ii); } }else @@ -24249,7 +27011,7 @@ static int do_meta_command(char *zLine, ShellState *p){ */ if( cli_strcmp(azCmd[0],"list")==0 ){ for(i=0; i nSession; i++){ - utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); + oputf("%d %s\n", i, pAuxDb->aSession[i].zName); } }else @@ -24264,18 +27026,18 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zName[0]==0 ) goto session_syntax_error; for(i=0; i nSession; i++){ if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ - utf8_printf(stderr, "Session \"%s\" already exists\n", zName); + eputf("Session \"%s\" already exists\n", zName); goto meta_command_exit; } } if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ - raw_printf(stderr, "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); + eputf("Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); goto meta_command_exit; } pSession = &pAuxDb->aSession[pAuxDb->nSession]; rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); if( rc ){ - raw_printf(stderr, "Cannot open session: error code=%d\n", rc); + eputf("Cannot open session: error code=%d\n", rc); rc = 0; goto meta_command_exit; } @@ -24299,7 +27061,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, v; for(i=1; i out, "%s: %d 0x%x\n", azArg[i], v, v); + oputf("%s: %d 0x%x\n", azArg[i], v, v); } } if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ @@ -24308,7 +27070,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); - utf8_printf(p->out, "%s", zBuf); + oputz(zBuf); } } }else @@ -24335,9 +27097,8 @@ static int do_meta_command(char *zLine, ShellState *p){ bVerbose++; }else { - utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", - azArg[i], azArg[0]); - raw_printf(stderr, "Should be one of: --init -v\n"); + eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); + eputz("Should be one of: --init -v\n"); rc = 1; goto meta_command_exit; } @@ -24366,7 +27127,7 @@ static int do_meta_command(char *zLine, ShellState *p){ -1, &pStmt, 0); } if( rc ){ - raw_printf(stderr, "Error querying the selftest table\n"); + eputz("Error querying the selftest table\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -24382,10 +27143,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zAns==0 ) continue; k = 0; if( bVerbose>0 ){ - printf("%d: %s %s\n", tno, zOp, zSql); + sputf(stdout, "%d: %s %s\n", tno, zOp, zSql); } if( cli_strcmp(zOp,"memo")==0 ){ - utf8_printf(p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else if( cli_strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; @@ -24394,23 +27155,22 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ - utf8_printf(p->out, "Result: %s\n", str.z); + oputf("Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; - utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); + oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); sqlite3_free(zErrMsg); }else if( cli_strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; - utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); - utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); + oputf("%d: Expected: [%s]\n", tno, zAns); + oputf("%d: Got: [%s]\n", tno, str.z); } - }else - { - utf8_printf(stderr, - "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); + } + else{ + eputf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); rc = 1; break; } @@ -24418,12 +27178,12 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); - utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); + oputf("%d errors out of %d tests\n", nErr, nTest); }else if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ if( nArg<2 || nArg>3 ){ - raw_printf(stderr, "Usage: .separator COL ?ROW?\n"); + eputz("Usage: .separator COL ?ROW?\n"); rc = 1; } if( nArg>=2 ){ @@ -24466,14 +27226,13 @@ static int do_meta_command(char *zLine, ShellState *p){ bDebug = 1; }else { - utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n", - azArg[i], azArg[0]); + eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; } }else if( zLike ){ - raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); + eputz("Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; }else{ @@ -24483,12 +27242,12 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( bSchema ){ - zSql = "SELECT lower(name) FROM sqlite_schema" + zSql = "SELECT lower(name) as tname FROM sqlite_schema" " WHERE type='table' AND coalesce(rootpage,0)>1" " UNION ALL SELECT 'sqlite_schema'" " ORDER BY 1 collate nocase"; }else{ - zSql = "SELECT lower(name) FROM sqlite_schema" + zSql = "SELECT lower(name) as tname FROM sqlite_schema" " WHERE type='table' AND coalesce(rootpage,0)>1" " AND name NOT LIKE 'sqlite_%'" " ORDER BY 1 collate nocase"; @@ -24545,10 +27304,70 @@ static int do_meta_command(char *zLine, ShellState *p){ freeText(&sQuery); freeText(&sSql); if( bDebug ){ - utf8_printf(p->out, "%s\n", zSql); + oputf("%s\n", zSql); }else{ shell_exec(p, zSql, 0); } +#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE) + { + int lrc; + char *zRevText = /* Query for reversible to-blob-to-text check */ + "SELECT lower(name) as tname FROM sqlite_schema\n" + "WHERE type='table' AND coalesce(rootpage,0)>1\n" + "AND name NOT LIKE 'sqlite_%%'%s\n" + "ORDER BY 1 collate nocase"; + zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : ""); + zRevText = sqlite3_mprintf( + /* lower-case query is first run, producing upper-case query. */ + "with tabcols as materialized(\n" + "select tname, cname\n" + "from (" + " select printf('\"%%w\"',ss.tname) as tname," + " printf('\"%%w\"',ti.name) as cname\n" + " from (%z) ss\n inner join pragma_table_info(tname) ti))\n" + "select 'SELECT total(bad_text_count) AS bad_text_count\n" + "FROM ('||group_concat(query, ' UNION ALL ')||')' as btc_query\n" + " from (select 'SELECT COUNT(*) AS bad_text_count\n" + "FROM '||tname||' WHERE '\n" + "||group_concat('CAST(CAST('||cname||' AS BLOB) AS TEXT)<>'||cname\n" + "|| ' AND typeof('||cname||')=''text'' ',\n" + "' OR ') as query, tname from tabcols group by tname)" + , zRevText); + shell_check_oom(zRevText); + if( bDebug ) oputf("%s\n", zRevText); + lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); + if( lrc!=SQLITE_OK ){ + /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the + ** user does cruel and unnatural things like ".limit expr_depth 0". */ + rc = 1; + }else{ + if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC); + lrc = SQLITE_ROW==sqlite3_step(pStmt); + if( lrc ){ + const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); + sqlite3_stmt *pCheckStmt; + lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); + if( bDebug ) oputf("%s\n", zGenQuery); + if( lrc!=SQLITE_OK ){ + rc = 1; + }else{ + if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){ + double countIrreversible = sqlite3_column_double(pCheckStmt, 0); + if( countIrreversible>0 ){ + int sz = (int)(countIrreversible + 0.5); + eputf("Digest includes %d invalidly encoded text field%s.\n", + sz, (sz>1)? "s": ""); + } + } + sqlite3_finalize(pCheckStmt); + } + sqlite3_finalize(pStmt); + } + } + if( rc ) eputz(".sha3sum failed.\n"); + sqlite3_free(zRevText); + } +#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */ sqlite3_free(zSql); }else @@ -24561,7 +27380,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, x; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( nArg<2 ){ - raw_printf(stderr, "Usage: .system COMMAND\n"); + eputz("Usage: .system COMMAND\n"); rc = 1; goto meta_command_exit; } @@ -24570,9 +27389,11 @@ static int do_meta_command(char *zLine, ShellState *p){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } + consoleRestore(); x = zCmd!=0 ? system(zCmd) : 1; + consoleRenewSetup(); sqlite3_free(zCmd); - if( x ) raw_printf(stderr, "System command returns %d\n", x); + if( x ) eputf("System command returns %d\n", x); }else #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ @@ -24581,52 +27402,51 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zOut; int i; if( nArg!=1 ){ - raw_printf(stderr, "Usage: .show\n"); + eputz("Usage: .show\n"); rc = 1; goto meta_command_exit; } - utf8_printf(p->out, "%12.12s: %s\n","echo", - azBool[ShellHasFlag(p, SHFLG_Echo)]); - utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); - utf8_printf(p->out, "%12.12s: %s\n","explain", - p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); - utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]); + oputf("%12.12s: %s\n","echo", + azBool[ShellHasFlag(p, SHFLG_Echo)]); + oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); + oputf("%12.12s: %s\n","explain", + p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); + oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]); if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - utf8_printf - (p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", - modeDescr[p->mode], p->cmOpts.iWrap, - p->cmOpts.bWordWrap ? "on" : "off", - p->cmOpts.bQuote ? "" : "no"); + oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", + modeDescr[p->mode], p->cmOpts.iWrap, + p->cmOpts.bWordWrap ? "on" : "off", + p->cmOpts.bQuote ? "" : "no"); }else{ - utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); - } - utf8_printf(p->out, "%12.12s: ", "nullvalue"); - output_c_string(p->out, p->nullValue); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: %s\n","output", - strlen30(p->outfile) ? p->outfile : "stdout"); - utf8_printf(p->out,"%12.12s: ", "colseparator"); - output_c_string(p->out, p->colSeparator); - raw_printf(p->out, "\n"); - utf8_printf(p->out,"%12.12s: ", "rowseparator"); - output_c_string(p->out, p->rowSeparator); - raw_printf(p->out, "\n"); + oputf("%12.12s: %s\n","mode", modeDescr[p->mode]); + } + oputf("%12.12s: ", "nullvalue"); + output_c_string(p->nullValue); + oputz("\n"); + oputf("%12.12s: %s\n","output", + strlen30(p->outfile) ? p->outfile : "stdout"); + oputf("%12.12s: ", "colseparator"); + output_c_string(p->colSeparator); + oputz("\n"); + oputf("%12.12s: ", "rowseparator"); + output_c_string(p->rowSeparator); + oputz("\n"); switch( p->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; case 2: zOut = "stmt"; break; case 3: zOut = "vmstep"; break; } - utf8_printf(p->out, "%12.12s: %s\n","stats", zOut); - utf8_printf(p->out, "%12.12s: ", "width"); + oputf("%12.12s: %s\n","stats", zOut); + oputf("%12.12s: ", "width"); for (i=0;i nWidth;i++) { - raw_printf(p->out, "%d ", p->colWidth[i]); + oputf("%d ", p->colWidth[i]); } - raw_printf(p->out, "\n"); - utf8_printf(p->out, "%12.12s: %s\n", "filename", - p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); + oputz("\n"); + oputf("%12.12s: %s\n", "filename", + p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); }else if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){ @@ -24641,7 +27461,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( nArg==1 ){ display_stats(p->db, p, 0); }else{ - raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n"); + eputz("Usage: .stats ?on|off|stmt|vmstep?\n"); rc = 1; } }else @@ -24667,7 +27487,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ - raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); + eputz("Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; sqlite3_finalize(pStmt); goto meta_command_exit; @@ -24743,10 +27563,9 @@ static int do_meta_command(char *zLine, ShellState *p){ for(i=0; i out, "%s%-*s", zSp, maxlen, - azResult[j] ? azResult[j]:""); + oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - raw_printf(p->out, "\n"); + oputz("\n"); } } @@ -24760,7 +27579,7 @@ static int do_meta_command(char *zLine, ShellState *p){ output_reset(p); p->out = output_file_open("testcase-out.txt", 0); if( p->out==0 ){ - raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); + eputz("Error: cannot open 'testcase-out.txt'\n"); } if( nArg>=2 ){ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); @@ -24775,31 +27594,33 @@ static int do_meta_command(char *zLine, ShellState *p){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ - int unSafe; /* Not valid for --safe mode */ + int unSafe; /* Not valid unless --unsafe-testing */ const char *zUsage; /* Usage notes */ } aCtrl[] = { - { "always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, - { "assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, - /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ - /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ - { "byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, - { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, - /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/ - { "imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, - { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, - { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, - { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, - { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, + {"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, + {"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, + /*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ + /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ + {"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, + {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, + /*{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/ + {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" }, + {"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, + {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, + {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, + {"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, + {"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, #ifdef YYCOVERAGE - { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, -#endif - { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " }, - { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, - { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, - { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, - { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, - { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, - { "tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, + {"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, +#endif + {"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " }, + {"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, + {"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, + {"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, + {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, + {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, + {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, + {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, }; int testctrl = -1; int iCtrl = -1; @@ -24819,10 +27640,11 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all test-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - utf8_printf(p->out, "Available test-controls:\n"); + oputz("Available test-controls:\n"); for(i=0; i out, " .testctrl %s %s\n", - aCtrl[i].zCtrlName, aCtrl[i].zUsage); + if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; + oputf(" .testctrl %s %s\n", + aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; goto meta_command_exit; @@ -24832,31 +27654,28 @@ static int do_meta_command(char *zLine, ShellState *p){ ** of the option name, or a numerical value. */ n2 = strlen30(zCmd); for(i=0; i bSafeMode ){ - utf8_printf(stderr, - "line %d: \".testctrl %s\" may not be used in safe mode\n", - p->lineno, aCtrl[iCtrl].zCtrlName); - exit(1); + eputf("Error: unknown test-control: %s\n" + "Use \".testctrl --help\" for help\n", zCmd); }else{ switch(testctrl){ /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_OPTIMIZATIONS: + case SQLITE_TESTCTRL_FK_NO_ACTION: if( nArg==3 ){ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0); rc2 = sqlite3_test_control(testctrl, p->db, opt); @@ -24890,7 +27709,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3 *db; if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ sqlite3_randomness(sizeof(ii),&ii); - printf("-- random seed: %d\n", ii); + sputf(stdout, "-- random seed: %d\n", ii); } if( nArg==3 ){ db = 0; @@ -24924,6 +27743,21 @@ static int do_meta_command(char *zLine, ShellState *p){ } break; + /* sqlite3_test_control(int, int) */ + case SQLITE_TESTCTRL_USELONGDOUBLE: { + int opt = -1; + if( nArg==3 ){ + if( cli_strcmp(azArg[2],"default")==0 ){ + opt = 2; + }else{ + opt = booleanValue(azArg[2]); + } + } + rc2 = sqlite3_test_control(testctrl, opt); + isOk = 1; + break; + } + /* sqlite3_test_control(sqlite3*) */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: rc2 = sqlite3_test_control(testctrl, p->db); @@ -24943,7 +27777,7 @@ static int do_meta_command(char *zLine, ShellState *p){ case SQLITE_TESTCTRL_SEEK_COUNT: { u64 x = 0; rc2 = sqlite3_test_control(testctrl, p->db, &x); - utf8_printf(p->out, "%llu\n", x); + oputf("%llu\n", x); isOk = 3; break; } @@ -24974,11 +27808,11 @@ static int do_meta_command(char *zLine, ShellState *p){ int val = 0; rc2 = sqlite3_test_control(testctrl, -id, &val); if( rc2!=SQLITE_OK ) break; - if( id>1 ) utf8_printf(p->out, " "); - utf8_printf(p->out, "%d: %d", id, val); + if( id>1 ) oputz(" "); + oputf("%d: %d", id, val); id++; } - if( id>1 ) utf8_printf(p->out, "\n"); + if( id>1 ) oputz("\n"); isOk = 3; } break; @@ -24994,12 +27828,12 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( isOk==0 && iCtrl>=0 ){ - utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ - raw_printf(p->out, "%d\n", rc2); + oputf("%d\n", rc2); }else if( isOk==2 ){ - raw_printf(p->out, "0x%08x\n", rc2); + oputf("0x%08x\n", rc2); } }else #endif /* !defined(SQLITE_UNTESTABLE) */ @@ -25013,11 +27847,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg==2 ){ enableTimer = booleanValue(azArg[1]); if( enableTimer && !HAS_TIMER ){ - raw_printf(stderr, "Error: timer not available on this system.\n"); + eputz("Error: timer not available on this system.\n"); enableTimer = 0; } }else{ - raw_printf(stderr, "Usage: .timer on|off\n"); + eputz("Usage: .timer on|off\n"); rc = 1; } }else @@ -25054,13 +27888,13 @@ static int do_meta_command(char *zLine, ShellState *p){ mType |= SQLITE_TRACE_CLOSE; } else { - raw_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z); + eputf("Unknown option \"%s\" on \".trace\"\n", z); rc = 1; goto meta_command_exit; } }else{ output_file_close(p->traceOut); - p->traceOut = output_file_open(azArg[1], 0); + p->traceOut = output_file_open(z, 0); } } if( p->traceOut==0 ){ @@ -25078,7 +27912,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int lenOpt; char *zOpt; if( nArg<2 ){ - raw_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n"); + eputz("Usage: .unmodule [--allexcept] NAME ...\n"); rc = 1; goto meta_command_exit; } @@ -25100,60 +27934,60 @@ static int do_meta_command(char *zLine, ShellState *p){ #if SQLITE_USER_AUTHENTICATION if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ if( nArg<2 ){ - raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n"); + eputz("Usage: .user SUBCOMMAND ...\n"); rc = 1; goto meta_command_exit; } open_db(p, 0); if( cli_strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ - raw_printf(stderr, "Usage: .user login USER PASSWORD\n"); + eputz("Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], strlen30(azArg[3])); if( rc ){ - utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]); + eputf("Authentication failed for user %s\n", azArg[2]); rc = 1; } }else if( cli_strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); + eputz("Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ - raw_printf(stderr, "User-Add failed: %d\n", rc); + eputf("User-Add failed: %d\n", rc); rc = 1; } }else if( cli_strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ - raw_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); + eputz("Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), booleanValue(azArg[4])); if( rc ){ - raw_printf(stderr, "User-Edit failed: %d\n", rc); + eputf("User-Edit failed: %d\n", rc); rc = 1; } }else if( cli_strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ - raw_printf(stderr, "Usage: .user delete USER\n"); + eputz("Usage: .user delete USER\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_delete(p->db, azArg[2]); if( rc ){ - raw_printf(stderr, "User-Delete failed: %d\n", rc); + eputf("User-Delete failed: %d\n", rc); rc = 1; } }else{ - raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n"); + eputz("Usage: .user login|add|edit|delete ...\n"); rc = 1; goto meta_command_exit; } @@ -25161,21 +27995,22 @@ static int do_meta_command(char *zLine, ShellState *p){ #endif /* SQLITE_USER_AUTHENTICATION */ if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ - utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, - sqlite3_libversion(), sqlite3_sourceid()); + char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; + oputf("SQLite %s %s\n" /*extra-version-info*/, + sqlite3_libversion(), sqlite3_sourceid()); #if SQLITE_HAVE_ZLIB - utf8_printf(p->out, "zlib version %s\n", zlibVersion()); + oputf("zlib version %s\n", zlibVersion()); #endif #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) #if defined(__clang__) && defined(__clang_major__) - utf8_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." - CTIMEOPT_VAL(__clang_minor__) "." - CTIMEOPT_VAL(__clang_patchlevel__) "\n"); + oputf("clang-" CTIMEOPT_VAL(__clang_major__) "." + CTIMEOPT_VAL(__clang_minor__) "." + CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); #elif defined(_MSC_VER) - utf8_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) "\n"); + oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); #elif defined(__GNUC__) && defined(__VERSION__) - utf8_printf(p->out, "gcc-" __VERSION__ "\n"); + oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz); #endif }else @@ -25185,10 +28020,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ - utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + oputf("vfs.zName = \"%s\"\n", pVfs->zName); + oputf("vfs.iVersion = %d\n", pVfs->iVersion); + oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); + oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); } } }else @@ -25200,13 +28035,13 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); } for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ - utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, - pVfs==pCurrent ? " <--- CURRENT" : ""); - raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); - raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); - raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); + oputf("vfs.zName = \"%s\"%s\n", pVfs->zName, + pVfs==pCurrent ? " <--- CURRENT" : ""); + oputf("vfs.iVersion = %d\n", pVfs->iVersion); + oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); + oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - raw_printf(p->out, "-----------------------------------\n"); + oputz("-----------------------------------\n"); } } }else @@ -25217,14 +28052,14 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ - utf8_printf(p->out, "%s\n", zVfsName); + oputf("%s\n", zVfsName); sqlite3_free(zVfsName); } } }else if( c=='w' && cli_strncmp(azArg[0], "wheretrace", n)==0 ){ - unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff; + unsigned int x = nArg>=2? (unsigned int)integerValue(azArg[1]) : 0xffffffff; sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x); }else @@ -25241,8 +28076,8 @@ static int do_meta_command(char *zLine, ShellState *p){ }else { - utf8_printf(stderr, "Error: unknown command or invalid arguments: " - " \"%s\". Enter \".help\" for help\n", azArg[0]); + eputf("Error: unknown command or invalid arguments: " + " \"%s\". Enter \".help\" for help\n", azArg[0]); rc = 1; } @@ -25276,7 +28111,8 @@ typedef enum { ** The scan is resumable for subsequent lines when prior ** return values are passed as the 2nd argument. */ -static QuickScanState quickscan(char *zLine, QuickScanState qss){ +static QuickScanState quickscan(char *zLine, QuickScanState qss, + SCAN_TRACKER_REFTYPE pst){ char cin; char cWait = (char)qss; /* intentional narrowing loss */ if( cWait==0 ){ @@ -25300,17 +28136,25 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss){ if( *zLine=='*' ){ ++zLine; cWait = '*'; + CONTINUE_PROMPT_AWAITS(pst, "/*"); qss = QSS_SETV(qss, cWait); goto TermScan; } break; case '[': cin = ']'; - /* fall thru */ + deliberate_fall_through; case '`': case '\'': case '"': cWait = cin; qss = QSS_HasDark | cWait; + CONTINUE_PROMPT_AWAITC(pst, cin); goto TermScan; + case '(': + CONTINUE_PAREN_INCR(pst, 1); + break; + case ')': + CONTINUE_PAREN_INCR(pst, -1); + break; default: break; } @@ -25326,16 +28170,19 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss){ continue; ++zLine; cWait = 0; + CONTINUE_PROMPT_AWAITC(pst, 0); qss = QSS_SETV(qss, 0); goto PlainScan; case '`': case '\'': case '"': if(*zLine==cWait){ + /* Swallow doubled end-delimiter.*/ ++zLine; continue; } - /* fall thru */ + deliberate_fall_through; case ']': cWait = 0; + CONTINUE_PROMPT_AWAITC(pst, 0); qss = QSS_SETV(qss, 0); goto PlainScan; default: assert(0); @@ -25359,17 +28206,15 @@ static int line_is_command_terminator(char *zLine){ zLine += 2; /* SQL Server */ else return 0; - return quickscan(zLine, QSS_Start)==QSS_Start; + return quickscan(zLine, QSS_Start, 0)==QSS_Start; } /* -** We need a default sqlite3_complete() implementation to use in case -** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes -** any arbitrary text is a complete SQL statement. This is not very -** user-friendly, but it does seem to work. +** The CLI needs a working sqlite3_complete() to work properly. So error +** out of the build if compiling with SQLITE_OMIT_COMPLETE. */ #ifdef SQLITE_OMIT_COMPLETE -#define sqlite3_complete(x) 1 +# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE. #endif /* @@ -25422,7 +28267,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); } - utf8_printf(stderr, "%s %s\n", zPrefix, zErrorTail); + eputf("%s %s\n", zPrefix, zErrorTail); sqlite3_free(zErrMsg); zErrMsg = 0; return 1; @@ -25431,20 +28276,20 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); - raw_printf(p->out, "%s\n", zLineBuf); + oputf("%s\n", zLineBuf); } return 0; } static void echo_group_input(ShellState *p, const char *zDo){ - if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo); + if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); } #ifdef SQLITE_SHELL_FIDDLE /* -** Alternate one_input_line() impl for wasm mode. This is not in the primary impl -** because we need the global shellState and cannot access it from that function -** without moving lots of code around (creating a larger/messier diff). +** Alternate one_input_line() impl for wasm mode. This is not in the primary +** impl because we need the global shellState and cannot access it from that +** function without moving lots of code around (creating a larger/messier diff). */ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ /* Parse the next line from shellState.wasm.zInput. */ @@ -25495,18 +28340,19 @@ static int process_input(ShellState *p){ if( p->inputNesting==MAX_INPUT_NESTING ){ /* This will be more informative in a later version. */ - utf8_printf(stderr,"Input nesting limit (%d) reached at line %d." - " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); + eputf("Input nesting limit (%d) reached at line %d." + " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); return 1; } ++p->inputNesting; p->lineno = 0; + CONTINUE_PROMPT_RESET; while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ fflush(p->out); zLine = one_input_line(p->in, zLine, nSql>0); if( zLine==0 ){ /* End of input */ - if( p->in==0 && stdin_is_interactive ) printf("\n"); + if( p->in==0 && stdin_is_interactive ) oputz("\n"); break; } if( seenInterrupt ){ @@ -25519,7 +28365,7 @@ static int process_input(ShellState *p){ && line_is_complete(zSql, nSql) ){ memcpy(zLine,";",2); } - qss = quickscan(zLine, qss); + qss = quickscan(zLine, qss, CONTINUE_PROMPT_PSTATE); if( QSS_PLAINWHITE(qss) && nSql==0 ){ /* Just swallow single-line whitespace */ echo_group_input(p, zLine); @@ -25527,6 +28373,7 @@ static int process_input(ShellState *p){ continue; } if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){ + CONTINUE_PROMPT_RESET; echo_group_input(p, zLine); if( zLine[0]=='.' ){ rc = do_meta_command(zLine, p); @@ -25562,6 +28409,7 @@ static int process_input(ShellState *p){ if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ echo_group_input(p, zSql); errCnt += runOneSqlLine(p, zSql, p->in, startline); + CONTINUE_PROMPT_RESET; nSql = 0; if( p->outCount ){ output_reset(p); @@ -25581,6 +28429,7 @@ static int process_input(ShellState *p){ /* This may be incomplete. Let the SQL parser deal with that. */ echo_group_input(p, zSql); errCnt += runOneSqlLine(p, zSql, p->in, startline); + CONTINUE_PROMPT_RESET; } free(zSql); free(zLine); @@ -25602,7 +28451,7 @@ static char *find_home_dir(int clearFlag){ if( home_dir ) return home_dir; #if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) \ - && !defined(__RTP__) && !defined(_WRS_KERNEL) + && !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) { struct passwd *pwent; uid_t uid = getuid(); @@ -25657,9 +28506,43 @@ static char *find_home_dir(int clearFlag){ return home_dir; } +/* +** On non-Windows platforms, look for $XDG_CONFIG_HOME. +** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return +** the path to it, else return 0. The result is cached for +** subsequent calls. +*/ +static const char *find_xdg_config(void){ +#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \ + || defined(__RTP__) || defined(_WRS_KERNEL) + return 0; +#else + static int alreadyTried = 0; + static char *zConfig = 0; + const char *zXdgHome; + + if( alreadyTried!=0 ){ + return zConfig; + } + alreadyTried = 1; + zXdgHome = getenv("XDG_CONFIG_HOME"); + if( zXdgHome==0 ){ + return 0; + } + zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome); + shell_check_oom(zConfig); + if( access(zConfig,0)!=0 ){ + sqlite3_free(zConfig); + zConfig = 0; + } + return zConfig; +#endif +} + /* ** Read input from the file given by sqliterc_override. Or if that -** parameter is NULL, take input from ~/.sqliterc +** parameter is NULL, take input from the first of find_xdg_config() +** or ~/.sqliterc which is found. ** ** Returns the number of errors. */ @@ -25673,11 +28556,14 @@ static void process_sqliterc( FILE *inSaved = p->in; int savedLineno = p->lineno; - if (sqliterc == NULL) { + if( sqliterc == NULL ){ + sqliterc = find_xdg_config(); + } + if( sqliterc == NULL ){ home_dir = find_home_dir(0); if( home_dir==0 ){ - raw_printf(stderr, "-- warning: cannot find home directory;" - " cannot read ~/.sqliterc\n"); + eputz("-- warning: cannot find home directory;" + " cannot read ~/.sqliterc\n"); return; } zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); @@ -25687,12 +28573,12 @@ static void process_sqliterc( p->in = fopen(sqliterc,"rb"); if( p->in ){ if( stdin_is_interactive ){ - utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); + eputf("-- Loading resources from %s\n", sqliterc); } if( process_input(p) && bail_on_error ) exit(1); fclose(p->in); }else if( sqliterc_override!=0 ){ - utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc); + eputf("cannot open: \"%s\"\n", sqliterc); if( bail_on_error ) exit(1); } p->in = inSaved; @@ -25704,6 +28590,7 @@ static void process_sqliterc( ** Show available command line options */ static const char zOptions[] = + " -- treat no subsequent arguments as options\n" #if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE) " -A ARGS... run \".archive ARGS\" and exit\n" #endif @@ -25743,8 +28630,10 @@ static const char zOptions[] = " -newline SEP set output row separator. Default: '\\n'\n" " -nofollow refuse to open symbolic links to database files\n" " -nonce STRING set the safe-mode escape nonce\n" + " -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n" " -nullvalue TEXT set text string for NULL values. Default ''\n" " -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n" + " -pcachetrace trace all page cache operations\n" " -quote set output mode to 'quote'\n" " -readonly open the database read-only\n" " -safe enable safe-mode\n" @@ -25755,6 +28644,7 @@ static const char zOptions[] = " -stats print memory stats before each finalize\n" " -table set output mode to 'table'\n" " -tabs set output mode to 'tabs'\n" + " -unsafe-testing allow unsafe commands and modes for testing\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE @@ -25765,14 +28655,13 @@ static const char zOptions[] = #endif ; static void usage(int showDetail){ - utf8_printf(stderr, - "Usage: %s [OPTIONS] FILENAME [SQL]\n" - "FILENAME is the name of an SQLite database. A new database is created\n" - "if the file does not previously exist.\n", Argv0); + eputf("Usage: %s [OPTIONS] [FILENAME [SQL]]\n" + "FILENAME is the name of an SQLite database. A new database is created\n" + "if the file does not previously exist. Defaults to :memory:.\n", Argv0); if( showDetail ){ - utf8_printf(stderr, "OPTIONS include:\n%s", zOptions); + eputf("OPTIONS include:\n%s", zOptions); }else{ - raw_printf(stderr, "Use the -help option for additional information\n"); + eputz("Use the -help option for additional information\n"); } exit(1); } @@ -25783,8 +28672,8 @@ static void usage(int showDetail){ */ static void verify_uninitialized(void){ if( sqlite3_config(-1)==SQLITE_MISUSE ){ - utf8_printf(stdout, "WARNING: attempt to configure SQLite after" - " initialization.\n"); + sputz(stdout, "WARNING: attempt to configure SQLite after" + " initialization.\n"); } } @@ -25800,9 +28689,11 @@ static void main_init(ShellState *data) { memcpy(data->rowSeparator,SEP_Row, 2); data->showHeader = 0; data->shellFlgs = SHFLG_Lookaside; + sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); +#if !defined(SQLITE_SHELL_FIDDLE) verify_uninitialized(); +#endif sqlite3_config(SQLITE_CONFIG_URI, 1); - sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); sqlite3_snprintf(sizeof(mainPrompt), mainPrompt,"sqlite> "); sqlite3_snprintf(sizeof(continuePrompt), continuePrompt," ...> "); @@ -25811,7 +28702,7 @@ static void main_init(ShellState *data) { /* ** Output text to the console in a font that attracts extra attention. */ -#ifdef _WIN32 +#if defined(_WIN32) || defined(WIN32) static void printBold(const char *zText){ #if !SQLITE_OS_WINRT HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); @@ -25821,14 +28712,14 @@ static void printBold(const char *zText){ FOREGROUND_RED|FOREGROUND_INTENSITY ); #endif - printf("%s", zText); + oputz(zText); #if !SQLITE_OS_WINRT SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); #endif } #else static void printBold(const char *zText){ - printf("\033[1m%s\033[0m", zText); + oputf("\033[1m%s\033[0m", zText); } #endif @@ -25838,13 +28729,16 @@ static void printBold(const char *zText){ */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ - utf8_printf(stderr, "%s: Error: missing argument to %s\n", - argv[0], argv[argc-1]); + eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; } +static void sayAbnormalExit(void){ + if( seenInterrupt ) eputz("Program interrupted.\n"); +} + #ifndef SQLITE_SHELL_IS_UTF8 # if (defined(_WIN32) || defined(WIN32)) \ && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) @@ -25865,13 +28759,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char **argv; #endif #ifdef SQLITE_DEBUG - sqlite3_int64 mem_main_enter = sqlite3_memory_used(); + sqlite3_int64 mem_main_enter = 0; #endif char *zErrMsg = 0; #ifdef SQLITE_SHELL_FIDDLE # define data shellState #else ShellState data; + StreamsAreConsole consStreams = SAC_NoConsole; #endif const char *zInitFile = 0; int i; @@ -25879,30 +28774,34 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ int warnInmemoryDb = 0; int readStdin = 1; int nCmd = 0; + int nOptsEnd = argc; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 char **argvToFree = 0; int argcToFree = 0; #endif - - setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ + #ifdef SQLITE_SHELL_FIDDLE stdin_is_interactive = 0; stdout_is_console = 1; data.wasm.zDefaultDbName = "/fiddle.sqlite3"; #else - stdin_is_interactive = isatty(0); - stdout_is_console = isatty(1); + consStreams = consoleClassifySetup(stdin, stdout, stderr); + stdin_is_interactive = (consStreams & SAC_InConsole)!=0; + stdout_is_console = (consStreams & SAC_OutConsole)!=0; + atexit(consoleRestore); +#endif + atexit(sayAbnormalExit); +#ifdef SQLITE_DEBUG + mem_main_enter = sqlite3_memory_used(); #endif - #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ - fprintf(stderr, - "attach debugger to process %d and press any key to continue.\n", - GETPID()); + eputf("attach debugger to process %d and press any key to continue.\n", + GETPID()); fgetc(stdin); }else{ #if defined(_WIN32) || defined(WIN32) @@ -25917,11 +28816,19 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } } #endif + /* Register a valid signal handler early, before much else is done. */ +#ifdef SIGINT + signal(SIGINT, interrupt_handler); +#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) + if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ + eputz("No ^C handler.\n"); + } +#endif #if USE_SYSTEM_SQLITE+0!=1 if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ - utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", - sqlite3_sourceid(), SQLITE_SOURCE_ID); + eputf("SQLite header and source version mismatch\n%s\n%s\n", + sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } #endif @@ -25956,15 +28863,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; - /* Make sure we have a valid signal handler early, before anything - ** else is done. - */ -#ifdef SIGINT - signal(SIGINT, interrupt_handler); -#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) - SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE); -#endif - #ifdef SQLITE_SHELL_DBNAME_PROC { /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name @@ -25979,18 +28877,20 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, - ** the size of the alternative malloc heap, - ** and the first command to execute. + ** the size of the alternative malloc heap, options affecting commands + ** or SQL run from the command line, and the first command to execute. */ +#ifndef SQLITE_SHELL_FIDDLE verify_uninitialized(); +#endif for(i=1; i nOptsEnd ){ if( data.aAuxDb->zDbFilename==0 ){ data.aAuxDb->zDbFilename = z; }else{ - /* Excesss arguments are interpreted as SQL (or dot-commands) and + /* Excess arguments are interpreted as SQL (or dot-commands) and ** mean that nothing is read from stdin */ readStdin = 0; nCmd++; @@ -25998,9 +28898,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ shell_check_oom(azCmd); azCmd[nCmd-1] = z; } + continue; } if( z[1]=='-' ) z++; - if( cli_strcmp(z,"-separator")==0 + if( cli_strcmp(z, "-")==0 ){ + nOptsEnd = i; + continue; + }else if( cli_strcmp(z,"-separator")==0 || cli_strcmp(z,"-nullvalue")==0 || cli_strcmp(z,"-newline")==0 || cli_strcmp(z,"-cmd")==0 @@ -26008,12 +28912,23 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ (void)cmdline_option_value(argc, argv, ++i); }else if( cli_strcmp(z,"-init")==0 ){ zInitFile = cmdline_option_value(argc, argv, ++i); + }else if( cli_strcmp(z,"-interactive")==0 ){ + /* Need to check for interactive override here to so that it can + ** affect console setup (for Windows only) and testing thereof. + */ + stdin_is_interactive = 1; }else if( cli_strcmp(z,"-batch")==0 ){ /* Need to check for batch mode here to so we can avoid printing ** informational messages (like from process_sqliterc) before ** we do the actual processing of arguments later in a second pass. */ stdin_is_interactive = 0; + }else if( cli_strcmp(z,"-utf8")==0 ){ + }else if( cli_strcmp(z,"-no-utf8")==0 ){ + }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ + int val = 0; + sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW, &val); + assert( val==0 ); }else if( cli_strcmp(z,"-heap")==0 ){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) const char *zSize; @@ -26022,6 +28937,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ zSize = cmdline_option_value(argc, argv, ++i); szHeap = integerValue(zSize); if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; + verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #else (void)cmdline_option_value(argc, argv, ++i); @@ -26035,6 +28951,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ if( sz>0 && n>0 && 0xffffffffffffLL/sz 0 && sz>0) ? malloc(n*sz) : 0, sz, n); data.shellFlgs |= SHFLG_Pagecache; @@ -26044,11 +28961,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ if( sz<0 ) sz = 0; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); if( n<0 ) n = 0; + verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n); if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside; }else if( cli_strcmp(z,"-threadsafe")==0 ){ int n; n = (int)integerValue(cmdline_option_value(argc,argv,++i)); + verify_uninitialized(); switch( n ){ case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break; case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break; @@ -26067,15 +28986,17 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( cli_strcmp(z,"-multiplex")==0 ){ - extern int sqlite3_multiple_initialize(const char*,int); + extern int sqlite3_multiplex_initialize(const char*,int); sqlite3_multiplex_initialize(0, 1); #endif }else if( cli_strcmp(z,"-mmap")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); + verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz); -#ifdef SQLITE_ENABLE_SORTER_REFERENCES +#if defined(SQLITE_ENABLE_SORTER_REFERENCES) }else if( cli_strcmp(z,"-sorterref")==0 ){ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i)); + verify_uninitialized(); sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz); #endif }else if( cli_strcmp(z,"-vfs")==0 ){ @@ -26104,16 +29025,22 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif }else if( cli_strcmp(z, "-memtrace")==0 ){ sqlite3MemTraceActivate(stderr); + }else if( cli_strcmp(z, "-pcachetrace")==0 ){ + sqlite3PcacheTraceActivate(stderr); }else if( cli_strcmp(z,"-bail")==0 ){ bail_on_error = 1; }else if( cli_strcmp(z,"-nonce")==0 ){ free(data.zNonce); data.zNonce = strdup(cmdline_option_value(argc, argv, ++i)); + }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ + ShellSetFlag(&data,SHFLG_TestingMode); }else if( cli_strcmp(z,"-safe")==0 ){ /* no-op - catch this on the second pass */ } } +#ifndef SQLITE_SHELL_FIDDLE verify_uninitialized(); +#endif #ifdef SQLITE_SHELL_INIT_PROC @@ -26136,7 +29063,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ - utf8_printf(stderr, "no such VFS: \"%s\"\n", zVfs); + eputf("no such VFS: \"%s\"\n", zVfs); exit(1); } } @@ -26146,7 +29073,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ data.pAuxDb->zDbFilename = ":memory:"; warnInmemoryDb = argc==1; #else - utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0); + eputf("%s: Error: no database filename specified\n", Argv0); return 1; #endif } @@ -26177,7 +29104,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ */ for(i=1; i =nOptsEnd ) continue; if( z[1]=='-' ){ z++; } if( cli_strcmp(z,"-init")==0 ){ i++; @@ -26222,12 +29149,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ data.openFlags |= SQLITE_OPEN_NOFOLLOW; }else if( cli_strcmp(z,"-ascii")==0 ){ data.mode = MODE_Ascii; - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Unit); - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Record); + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit); + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record); }else if( cli_strcmp(z,"-tabs")==0 ){ data.mode = MODE_List; - sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Tab); - sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row); + sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Tab); + sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Row); }else if( cli_strcmp(z,"-separator")==0 ){ sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, "%s",cmdline_option_value(argc,argv,++i)); @@ -26263,12 +29190,19 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ }else if( cli_strcmp(z,"-version")==0 ){ - printf("%s %s\n", sqlite3_libversion(), sqlite3_sourceid()); + oputf("%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(), + 8*(int)sizeof(char*)); return 0; }else if( cli_strcmp(z,"-interactive")==0 ){ - stdin_is_interactive = 1; + /* already handled */ }else if( cli_strcmp(z,"-batch")==0 ){ - stdin_is_interactive = 0; + /* already handled */ + }else if( cli_strcmp(z,"-utf8")==0 ){ + /* already handled */ + }else if( cli_strcmp(z,"-no-utf8")==0 ){ + /* already handled */ + }else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){ + /* already handled */ }else if( cli_strcmp(z,"-heap")==0 ){ i++; }else if( cli_strcmp(z,"-pagecache")==0 ){ @@ -26283,6 +29217,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ i++; }else if( cli_strcmp(z,"-memtrace")==0 ){ i++; + }else if( cli_strcmp(z,"-pcachetrace")==0 ){ + i++; #ifdef SQLITE_ENABLE_SORTER_REFERENCES }else if( cli_strcmp(z,"-sorterref")==0 ){ i++; @@ -26313,18 +29249,18 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ open_db(&data, 0); rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ - utf8_printf(stderr,"Error: %s\n", zErrMsg); + eputf("Error: %s\n", zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; }else if( rc!=0 ){ - utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z); + eputf("Error: unable to process SQL \"%s\"\n", z); if( bail_on_error ) return rc; } } #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) }else if( cli_strncmp(z, "-A", 2)==0 ){ if( nCmd>0 ){ - utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands" - " with \"%s\"\n", z); + eputf("Error: cannot mix regular SQL or dot-commands" + " with \"%s\"\n", z); return 1; } open_db(&data, OPEN_DB_ZIPFILE); @@ -26339,9 +29275,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif }else if( cli_strcmp(z,"-safe")==0 ){ data.bSafeMode = data.bSafeModePersist = 1; + }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ + /* Acted upon in first pass. */ }else{ - utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); - raw_printf(stderr,"Use -help for a list of options.\n"); + eputf("%s: Error: unknown option: %s\n", Argv0, z); + eputz("Use -help for a list of options.\n"); return 1; } data.cMode = data.mode; @@ -26361,12 +29299,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } }else{ open_db(&data, 0); + echo_group_input(&data, azCmd[i]); rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg || rc ){ if( zErrMsg!=0 ){ - utf8_printf(stderr,"Error: %s\n", zErrMsg); + eputf("Error: %s\n", zErrMsg); }else{ - utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]); + eputf("Error: unable to process SQL: %s\n", azCmd[i]); } sqlite3_free(zErrMsg); free(azCmd); @@ -26381,16 +29320,19 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char *zHome; char *zHistory; int nHistory; - printf( - "SQLite version %s %.19s\n" /*extra-version-info*/ - "Enter \".help\" for usage hints.\n", - sqlite3_libversion(), sqlite3_sourceid() - ); +#if CIO_WIN_WC_XLATE +# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "") +#else +# define SHELL_CIO_CHAR_SET "" +#endif + oputf("SQLite version %s %.19s%s\n" /*extra-version-info*/ + "Enter \".help\" for usage hints.\n", + sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); if( warnInmemoryDb ){ - printf("Connected to a "); + oputz("Connected to a "); printBold("transient in-memory database"); - printf(".\nUse \".open FILENAME\" to reopen on a " - "persistent database.\n"); + oputz(".\nUse \".open FILENAME\" to reopen on a" + " persistent database.\n"); } zHistory = getenv("SQLITE_HISTORY"); if( zHistory ){ @@ -26450,8 +29392,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ memset(&data, 0, sizeof(data)); #ifdef SQLITE_DEBUG if( sqlite3_memory_used()>mem_main_enter ){ - utf8_printf(stderr, "Memory leaked: %u bytes\n", - (unsigned int)(sqlite3_memory_used()-mem_main_enter)); + eputf("Memory leaked: %u bytes\n", + (unsigned int)(sqlite3_memory_used()-mem_main_enter)); } #endif #endif /* !SQLITE_SHELL_FIDDLE */ diff --git a/src/sqlite3.c b/src/sqlite3.c index 2fb86bc7c442c8964d1059f688e05d8465a18d06..7fb290f841431817db3b44f1d83fd443b0b1c96d 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.40.1. By combining all the individual C code files into this +** version 3.44.4. 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 @@ -16,6 +16,9 @@ ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. +** +** The content in this amalgamation comes from Fossil check-in +** f1e31fd9961ac82535a5d0702b127d84de8c. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -50,11 +53,11 @@ ** used on lines of code that actually ** implement parts of coverage testing. ** -** OPTIMIZATION-IF-TRUE - This branch is allowed to alway be false +** OPTIMIZATION-IF-TRUE - This branch is allowed to always be false ** and the correct answer is still obtained, ** though perhaps more slowly. ** -** OPTIMIZATION-IF-FALSE - This branch is allowed to alway be true +** OPTIMIZATION-IF-FALSE - This branch is allowed to always be true ** and the correct answer is still obtained, ** though perhaps more slowly. ** @@ -123,6 +126,10 @@ #define SQLITE_4_BYTE_ALIGNED_MALLOC #endif /* defined(_MSC_VER) && !defined(_WIN64) */ +#if !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 +#define HAVE_LOG2 0 +#endif /* !defined(HAVE_LOG2) && defined(_MSC_VER) && _MSC_VER<1800 */ + #endif /* SQLITE_MSVC_H */ /************** End of msvc.h ************************************************/ @@ -452,9 +459,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.40.1" -#define SQLITE_VERSION_NUMBER 3040001 -#define SQLITE_SOURCE_ID "2022-12-28 14:03:47 df5c253c0b3dd24916e4ec7cf77d3db5294cc9fd45ae7b9c5e82ad8197f38a24" +#define SQLITE_VERSION "3.44.4" +#define SQLITE_VERSION_NUMBER 3044004 +#define SQLITE_SOURCE_ID "2025-02-19 00:18:53 f1e31fd9961ac82535a5d0702b127d84de8ca21d4df1c51c73e078ea0ad4afa8" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -834,6 +841,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) +#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) @@ -869,6 +877,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT |(12<<8)) #define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8)) #define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8)) +#define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) @@ -1481,7 +1490,6 @@ struct sqlite3_io_methods { ** in wal mode after the client has finished copying pages from the wal ** file to the database file, but before the *-shm file is updated to ** record the fact that the pages have been checkpointed. -** ** ** [[SQLITE_FCNTL_EXTERNAL_READER]] ** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect @@ -1494,16 +1502,16 @@ struct sqlite3_io_methods { ** the database is not a wal-mode db, or if there is no such connection in any ** other process. This opcode cannot be used to detect transactions opened ** by clients within the current process, only within other processes. -** ** ** [[SQLITE_FCNTL_CKSM_FILE]] -** Used by the cksmvfs VFS module only. +** The [SQLITE_FCNTL_CKSM_FILE] opcode is for use internally by the +** [checksum VFS shim] only. ** ** [[SQLITE_FCNTL_RESET_CACHE]] ** If there is currently no transaction open on the database, and the -** database is not a temp db, then this file-control purges the contents -** of the in-memory page cache. If there is an open transaction, or if -** the db is a temp-db, it is a no-op, not an error. +** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control +** purges the contents of the in-memory page cache. If there is an open +** transaction, or if the db is a temp-db, this opcode is a no-op, not an error. ** */ #define SQLITE_FCNTL_LOCKSTATE 1 @@ -1961,20 +1969,23 @@ SQLITE_API int sqlite3_os_end(void); ** must ensure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running. ** -** The sqlite3_config() interface -** may only be invoked prior to library initialization using -** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** ^If sqlite3_config() is called after [sqlite3_initialize()] and before -** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. -** Note, however, that ^sqlite3_config() can be called as part of the -** implementation of an application-defined [sqlite3_os_init()]. -** ** The first argument to sqlite3_config() is an integer ** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [configuration option] ** in the first argument. ** +** For most configuration options, the sqlite3_config() interface +** may only be invoked prior to library initialization using +** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. +** The exceptional configuration options that may be invoked at any time +** are called "anytime configuration options". +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] with a first argument that is not an anytime +** configuration option, then the sqlite3_config() call will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the +** implementation of an application-defined [sqlite3_os_init()]. +** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. @@ -2082,6 +2093,23 @@ struct sqlite3_mem_methods { ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. ** +** Most of the configuration options for sqlite3_config() +** will only work if invoked prior to [sqlite3_initialize()] or after +** [sqlite3_shutdown()]. The few exceptions to this rule are called +** "anytime configuration options". +** ^Calling [sqlite3_config()] with a first argument that is not an +** anytime configuration option in between calls to [sqlite3_initialize()] and +** [sqlite3_shutdown()] is a no-op that returns SQLITE_MISUSE. +** +** The set of anytime configuration options can change (by insertions +** and/or deletions) from one release of SQLite to the next. +** As of SQLite version 3.42.0, the complete set of anytime configuration +** options is: +** +**
+** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_config()] to make sure that @@ -2412,7 +2440,7 @@ struct sqlite3_mem_methods { ** is stored in each sorted record and the required column values loaded ** from the database as records are returned in sorted order. The default ** value for this option is to never use this optimization. Specifying a -** negative value for this option restores the default behaviour. +** negative value for this option restores the default behavior. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** @@ -2426,30 +2454,46 @@ struct sqlite3_mem_methods { ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. +** +** [[SQLITE_CONFIG_ROWID_IN_VIEW]] +**- SQLITE_CONFIG_LOG +**
- SQLITE_CONFIG_PCACHE_HDRSZ +**
SQLITE_CONFIG_ROWID_IN_VIEW +** The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability +** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is +** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability +** defaults to on. This configuration option queries the current setting or +** changes the setting to off or on. The argument is a pointer to an integer. +** If that integer initially holds a value of 1, then the ability for VIEWs to +** have ROWIDs is activated. If the integer initially holds zero, then the +** ability is deactivated. Any other initial value for the integer leaves the +** setting unchanged. After changes, if any, the integer is written with +** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite +** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and +** recommended case) then the integer is always filled with zero, regardless +** if its initial value. ** */ -#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ -#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ -#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ -#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ -#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ -#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ -#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ -#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ -#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ -#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ -/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ -#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* no-op */ -#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ -#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ -#define SQLITE_CONFIG_URI 17 /* int */ -#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ -#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ +#define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ +#define SQLITE_CONFIG_SERIALIZED 3 /* nil */ +#define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ +#define SQLITE_CONFIG_SCRATCH 6 /* No longer used */ +#define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ +#define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ +#define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ +#define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ +#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ +/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ +#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ #define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ -#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ -#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ +#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ @@ -2457,6 +2501,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ +#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2490,7 +2535,7 @@ struct sqlite3_mem_methods { ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by -** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^ @@ -2587,7 +2632,7 @@ struct sqlite3_mem_methods { ** database handle, SQLite checks if this will mean that there are now no ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to -** override this behaviour. The first parameter passed to this operation +** override this behavior. The first parameter passed to this operation ** is an integer - positive to disable checkpoints-on-close, or zero (the ** default) to enable them, and negative to leave the setting unchanged. ** The second parameter is a pointer to an integer @@ -2640,8 +2685,12 @@ struct sqlite3_mem_methods { **sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0); ** ** Because resetting a database is destructive and irreversible, the -** process requires the use of this obscure API and multiple steps to help -** ensure that it does not happen by accident. +** process requires the use of this obscure API and multiple steps to +** help ensure that it does not happen by accident. Because this +** feature must be capable of resetting corrupt databases, and +** shutting down virtual tables may require access to that corrupt +** storage, the library must abandon any installed virtual tables +** without calling their xDestroy() methods. ** ** [[SQLITE_DBCONFIG_DEFENSIVE]] SQLITE_DBCONFIG_DEFENSIVE **The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the @@ -2680,7 +2729,7 @@ struct sqlite3_mem_methods { ** ** ** [[SQLITE_DBCONFIG_DQS_DML]] -**SQLITE_DBCONFIG_DQS_DML +** SQLITE_DBCONFIG_DQS_DML **The SQLITE_DBCONFIG_DQS_DML option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DML statements ** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The @@ -2689,7 +2738,7 @@ struct sqlite3_mem_methods { ** ** ** [[SQLITE_DBCONFIG_DQS_DDL]] -**SQLITE_DBCONFIG_DQS_DDL +** SQLITE_DBCONFIG_DQS_DDL **The SQLITE_DBCONFIG_DQS option activates or deactivates ** the legacy [double-quoted string literal] misfeature for DDL statements, ** such as CREATE TABLE and CREATE INDEX. The @@ -2698,7 +2747,7 @@ struct sqlite3_mem_methods { ** ** ** [[SQLITE_DBCONFIG_TRUSTED_SCHEMA]] -**SQLITE_DBCONFIG_TRUSTED_SCHEMA +** SQLITE_DBCONFIG_TRUSTED_SCHEMA **The SQLITE_DBCONFIG_TRUSTED_SCHEMA option tells SQLite to ** assume that database schemas are untainted by malicious content. ** When the SQLITE_DBCONFIG_TRUSTED_SCHEMA option is disabled, SQLite @@ -2718,7 +2767,7 @@ struct sqlite3_mem_methods { ** ** ** [[SQLITE_DBCONFIG_LEGACY_FILE_FORMAT]] -**SQLITE_DBCONFIG_LEGACY_FILE_FORMAT +** SQLITE_DBCONFIG_LEGACY_FILE_FORMAT **The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates ** the legacy file format flag. When activated, this flag causes all newly ** created database file to have a schema format version number (the 4-byte @@ -2727,7 +2776,7 @@ struct sqlite3_mem_methods { ** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting, ** newly created databases are generally not understandable by SQLite versions ** prior to 3.3.0 ([dateof:3.3.0]). As these words are written, there -** is now scarcely any need to generated database files that are compatible +** is now scarcely any need to generate database files that are compatible ** all the way back to version 3.0.0, and so this setting is of little ** practical use, but is provided so that SQLite can continue to claim the ** ability to generate new database files that are compatible with version @@ -2736,8 +2785,40 @@ struct sqlite3_mem_methods { ** the [VACUUM] command will fail with an obscure error when attempting to ** process a table with generated columns and a descending index. This is ** not considered a bug since SQLite versions 3.3.0 and earlier do not support -** either generated columns or decending indexes. +** either generated columns or descending indexes. ** +** +** [[SQLITE_DBCONFIG_STMT_SCANSTATUS]] +**SQLITE_DBCONFIG_STMT_SCANSTATUS +**The SQLITE_DBCONFIG_STMT_SCANSTATUS option is only useful in +** SQLITE_ENABLE_STMT_SCANSTATUS builds. In this case, it sets or clears +** a flag that enables collection of the sqlite3_stmt_scanstatus_v2() +** statistics. For statistics to be collected, the flag must be set on +** the database handle both when the SQL statement is prepared and when it +** is stepped. The flag is set (collection of statistics is enabled) +** by default. This option takes two arguments: an integer and a pointer to +** an integer.. The first argument is 1, 0, or -1 to enable, disable, or +** leave unchanged the statement scanstatus option. If the second argument +** is not NULL, then the value of the statement scanstatus setting after +** processing the first argument is written into the integer that the second +** argument points to. +** +** +** [[SQLITE_DBCONFIG_REVERSE_SCANORDER]] +**SQLITE_DBCONFIG_REVERSE_SCANORDER +**The SQLITE_DBCONFIG_REVERSE_SCANORDER option changes the default order +** in which tables and indexes are scanned so that the scans start at the end +** and work toward the beginning rather than starting at the beginning and +** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the +** same as setting [PRAGMA reverse_unordered_selects]. This option takes +** two arguments which are an integer and a pointer to an integer. The first +** argument is 1, 0, or -1 to enable, disable, or leave unchanged the +** reverse scan order flag, respectively. If the second argument is not NULL, +** then 0 or 1 is written into the integer that the second argument points to +** depending on if the reverse scan order flag is set after processing the +** first argument. +** +** ** */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -2758,7 +2839,9 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */ #define SQLITE_DBCONFIG_LEGACY_FILE_FORMAT 1016 /* int int* */ #define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */ -#define SQLITE_DBCONFIG_MAX 1017 /* Largest DBCONFIG */ +#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */ +#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */ +#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes @@ -2980,8 +3063,13 @@ SQLITE_API sqlite3_int64 sqlite3_total_changes64(sqlite3*); ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. +** +** ^The [sqlite3_is_interrupted(D)] interface can be used to determine whether +** or not an interrupt is currently in effect for [database connection] D. +** It returns 1 if an interrupt is currently in effect, or 0 otherwise. */ SQLITE_API void sqlite3_interrupt(sqlite3*); +SQLITE_API int sqlite3_is_interrupted(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete @@ -3599,8 +3687,8 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, **^An SQLITE_TRACE_PROFILE callback provides approximately the same ** information as is provided by the [sqlite3_profile()] callback. ** ^The P argument is a pointer to the [prepared statement] and the -** X argument points to a 64-bit integer which is the estimated of -** the number of nanosecond that the prepared statement took to run. +** X argument points to a 64-bit integer which is approximately +** the number of nanoseconds that the prepared statement took to run. ** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes. ** ** [[SQLITE_TRACE_ROW]] SQLITE_TRACE_ROW @@ -3632,8 +3720,10 @@ SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, ** M argument should be the bitwise OR-ed combination of ** zero or more [SQLITE_TRACE] constants. ** -** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides -** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2(). +** ^Each call to either sqlite3_trace(D,X,P) or sqlite3_trace_v2(D,M,X,P) +** overrides (cancels) all prior calls to sqlite3_trace(D,X,P) or +** sqlite3_trace_v2(D,M,X,P) for the [database connection] D. Each +** database connection may have at most one trace callback. ** ** ^The X callback is invoked whenever any of the events identified by ** mask M occur. ^The integer return value from the callback is currently @@ -3663,7 +3753,7 @@ SQLITE_API int sqlite3_trace_v2( ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to -** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for +** [sqlite3_step()] and [sqlite3_prepare()] and similar for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** @@ -3688,6 +3778,13 @@ SQLITE_API int sqlite3_trace_v2( ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** +** The progress handler callback would originally only be invoked from the +** bytecode engine. It still might be invoked during [sqlite3_prepare()] +** and similar because those routines might force a reparse of the schema +** which involves running the bytecode engine. However, beginning with +** SQLite version 3.41.0, the progress handler callback might also be +** invoked directly from [sqlite3_prepare()] while analyzing and generating +** code for complex queries. */ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); @@ -3724,13 +3821,18 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** **** ^(
*/ @@ -5755,6 +5931,7 @@ SQLITE_API int sqlite3_create_window_function( #define SQLITE_DIRECTONLY 0x000080000 #define SQLITE_SUBTYPE 0x000100000 #define SQLITE_INNOCUOUS 0x000200000 +#define SQLITE_RESULT_SUBTYPE 0x001000000 /* ** CAPI3REF: Deprecated Functions @@ -5855,16 +6032,6 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** -** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], -** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current encoding -** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) -** returns something other than SQLITE_TEXT, then the return value from -** sqlite3_value_encoding(X) is meaningless. ^Calls to -** sqlite3_value_text(X), sqlite3_value_text16(X), sqlite3_value_text16be(X), -** sqlite3_value_text16le(X), sqlite3_value_bytes(X), or -** sqlite3_value_bytes16(X) might change the encoding of the value X and -** thus change the return from subsequent calls to sqlite3_value_encoding(X). -** ** ^Within the [xUpdate] method of a [virtual table], the ** sqlite3_value_nochange(X) interface returns true if and only if ** the column corresponding to X is unchanged by the UPDATE operation @@ -5929,6 +6096,27 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); SQLITE_API int sqlite3_value_frombind(sqlite3_value*); + +/* +** CAPI3REF: Report the internal text encoding state of an sqlite3_value object +** METHOD: sqlite3_value +** +** ^(The sqlite3_value_encoding(X) interface returns one of [SQLITE_UTF8], +** [SQLITE_UTF16BE], or [SQLITE_UTF16LE] according to the current text encoding +** of the value X, assuming that X has type TEXT.)^ If sqlite3_value_type(X) +** returns something other than SQLITE_TEXT, then the return value from +** sqlite3_value_encoding(X) is meaningless. ^Calls to +** [sqlite3_value_text(X)], [sqlite3_value_text16(X)], [sqlite3_value_text16be(X)], +** [sqlite3_value_text16le(X)], [sqlite3_value_bytes(X)], or +** [sqlite3_value_bytes16(X)] might change the encoding of the value X and +** thus change the return from subsequent calls to sqlite3_value_encoding(X). +** +** This routine is intended for used by applications that test and validate +** the SQLite implementation. This routine is inquiring about the opaque +** internal state of an [sqlite3_value] object. Ordinary applications should +** not need to know what the internal state of an sqlite3_value object is and +** hence should not need to use this interface. +*/ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); /* @@ -5940,6 +6128,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*); ** information can be used to pass a limited amount of context from ** one SQL function to another. Use the [sqlite3_result_subtype()] ** routine to set the subtype for the return value of an SQL function. +** +** Every [application-defined SQL function] that invoke this interface +** should include the [SQLITE_SUBTYPE] property in the text +** encoding argument when the function is [sqlite3_create_function|registered]. +** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype() +** might return zero instead of the upstream subtype in some corner cases. */ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*); @@ -6038,48 +6232,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** METHOD: sqlite3_context ** ** These functions may be used by (non-aggregate) SQL functions to -** associate metadata with argument values. If the same value is passed to -** multiple invocations of the same SQL function during query execution, under -** some circumstances the associated metadata may be preserved. An example -** of where this might be useful is in a regular-expression matching -** function. The compiled version of the regular expression can be stored as -** metadata associated with the pattern string. +** associate auxiliary data with argument values. If the same argument +** value is passed to multiple invocations of the same SQL function during +** query execution, under some circumstances the associated auxiliary data +** might be preserved. An example of where this might be useful is in a +** regular-expression matching function. The compiled version of the regular +** expression can be stored as auxiliary data associated with the pattern string. ** Then as long as the pattern string remains the same, ** the compiled regular expression can be reused on multiple ** invocations of the same function. ** -** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata +** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data ** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument ** value to the application-defined function. ^N is zero for the left-most -** function argument. ^If there is no metadata +** function argument. ^If there is no auxiliary data ** associated with the function argument, the sqlite3_get_auxdata(C,N) interface ** returns a NULL pointer. ** -** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th -** argument of the application-defined function. ^Subsequent +** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the +** N-th argument of the application-defined function. ^Subsequent ** calls to sqlite3_get_auxdata(C,N) return P from the most recent -** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or -** NULL if the metadata has been discarded. +** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or +** NULL if the auxiliary data has been discarded. ** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL, ** SQLite will invoke the destructor function X with parameter P exactly -** once, when the metadata is discarded. -** SQLite is free to discard the metadata at any time, including:- [SQLITE_OPEN_READONLY]
-**- The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
)^ +**- The database is opened in read-only mode. If the database does +** not already exist, an error is returned.
)^ ** ** ^(- [SQLITE_OPEN_READWRITE]
-**- The database is opened for reading and writing if possible, or reading -** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
)^ +**- The database is opened for reading and writing if possible, or +** reading only if the file is write protected by the operating +** system. In either case the database must already exist, otherwise +** an error is returned. For historical reasons, if opening in +** read-write mode fails due to OS-level permissions, an attempt is +** made to open it in read-only mode. [sqlite3_db_readonly()] can be +** used to determine whether the database is actually +** read-write.
)^ ** ** ^(- [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
**- The database is opened for reading and writing, and is created if @@ -3990,7 +4092,7 @@ SQLITE_API int sqlite3_open_v2( ** as F) must be one of: **
** ** [[SQLITE_INNOCUOUS]]**
@@ -4103,7 +4205,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*); /* ** CAPI3REF: Create and Destroy VFS Filenames ** -** These interfces are provided for use by [VFS shim] implementations and +** These interfaces are provided for use by [VFS shim] implementations and ** are not useful outside of that context. ** ** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of @@ -4183,6 +4285,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename); ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. +** (See how SQLite handles [invalid UTF] for exceptions to this rule.) ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by @@ -4650,6 +4753,41 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); */ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Change The EXPLAIN Setting For A Prepared Statement +** METHOD: sqlite3_stmt +** +** The sqlite3_stmt_explain(S,E) interface changes the EXPLAIN +** setting for [prepared statement] S. If E is zero, then S becomes +** a normal prepared statement. If E is 1, then S behaves as if +** its SQL text began with "[EXPLAIN]". If E is 2, then S behaves as if +** its SQL text began with "[EXPLAIN QUERY PLAN]". +** +** Calling sqlite3_stmt_explain(S,E) might cause S to be reprepared. +** SQLite tries to avoid a reprepare, but a reprepare might be necessary +** on the first transition into EXPLAIN or EXPLAIN QUERY PLAN mode. +** +** Because of the potential need to reprepare, a call to +** sqlite3_stmt_explain(S,E) will fail with SQLITE_ERROR if S cannot be +** reprepared because it was created using [sqlite3_prepare()] instead of +** the newer [sqlite3_prepare_v2()] or [sqlite3_prepare_v3()] interfaces and +** hence has no saved SQL text with which to reprepare. +** +** Changing the explain setting for a prepared statement does not change +** the original SQL text for the statement. Hence, if the SQL text originally +** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) +** is called to convert the statement into an ordinary statement, the EXPLAIN +** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) +** output, even though the statement now acts like a normal SQL statement. +** +** This routine returns SQLITE_OK if the explain mode is successfully +** changed, or an error code if the explain mode could not be changed. +** The explain mode cannot be changed while a statement is active. +** Hence, it is good practice to call [sqlite3_reset(S)] +** immediately prior to calling sqlite3_stmt_explain(S,E). +*/ +SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode); + /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt @@ -4813,7 +4951,7 @@ typedef struct sqlite3_context sqlite3_context; ** with it may be passed. ^It is called to dispose of the BLOB or string even ** if the call to the bind API fails, except the destructor is not called if ** the third parameter is a NULL pointer or the fourth parameter is negative. -** ^ (2) The special constant, [SQLITE_STATIC], may be passsed to indicate that +** ^ (2) The special constant, [SQLITE_STATIC], may be passed to indicate that ** the application remains responsible for disposing of the object. ^In this ** case, the object and the provided pointer to it must remain valid until ** either the prepared statement is finalized or the same SQL parameter is @@ -5492,20 +5630,33 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S ** back to the beginning of its program. ** -** ^If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** ^The return code from [sqlite3_reset(S)] indicates whether or not +** the previous evaluation of prepared statement S completed successfully. +** ^If [sqlite3_step(S)] has never before been called on S or if +** [sqlite3_step(S)] has not been called since the previous call +** to [sqlite3_reset(S)], then [sqlite3_reset(S)] will return +** [SQLITE_OK]. ** ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. +** ^The [sqlite3_reset(S)] interface might also return an [error code] +** if there were no prior errors but the process of resetting +** the prepared statement caused a new error. ^For example, if an +** [INSERT] statement with a [RETURNING] clause is only stepped one time, +** that one call to [sqlite3_step(S)] might return SQLITE_ROW but +** the overall statement might still fail and the [sqlite3_reset(S)] call +** might return SQLITE_BUSY if locking constraints prevent the +** database change from committing. Therefore, it is important that +** applications check the return code from [sqlite3_reset(S)] even if +** no prior call to [sqlite3_step(S)] indicated a problem. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); + /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} @@ -5711,10 +5862,21 @@ SQLITE_API int sqlite3_create_window_function( ** from top-level SQL, and cannot be used in VIEWs or TRIGGERs nor in ** schema structures such as [CHECK constraints], [DEFAULT clauses], ** [expression indexes], [partial indexes], or [generated columns]. -** The SQLITE_DIRECTONLY flags is a security feature which is recommended -** for all [application-defined SQL functions], and especially for functions -** that have side-effects or that could potentially leak sensitive -** information. +**- A database filename pointer created by the SQLite core and -** passed into the xOpen() method of a VFS implemention, or +** passed into the xOpen() method of a VFS implementation, or **
- A filename obtained from [sqlite3_db_filename()], or **
- A new filename constructed using [sqlite3_create_filename()]. **
+** The SQLITE_DIRECTONLY flag is recommended for any +** [application-defined SQL function] +** that has side-effects or that could potentially leak sensitive information. +** This will prevent attacks in which an application is tricked +** into using a database file that has had its schema surreptitiously +** modified to invoke the application-defined function in ways that are +** harmful. +**
+** Some people say it is good practice to set SQLITE_DIRECTONLY on all +** [application-defined SQL functions], regardless of whether or not they +** are security sensitive, as doing so prevents those functions from being used +** inside of the database schema, and thus ensures that the database +** can be inspected and modified using generic tools (such as the [CLI]) +** that do not have access to the application-defined functions. **
- SQLITE_INNOCUOUS
- @@ -5741,13 +5903,27 @@ SQLITE_API int sqlite3_create_window_function( **
** ** [[SQLITE_SUBTYPE]]- SQLITE_SUBTYPE
- -** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call +** The SQLITE_SUBTYPE flag indicates to SQLite that a function might 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). +** This flag instructs SQLite to omit some corner-case optimizations that +** might disrupt the operation of the [sqlite3_value_subtype()] function, +** causing it to return zero rather than the correct subtype(). +** SQL functions that invokes [sqlite3_value_subtype()] should have this +** property. If the SQLITE_SUBTYPE property is omitted, then the return +** value from [sqlite3_value_subtype()] might sometimes be zero even though +** a non-zero subtype was specified by the function argument expression. +** +** [[SQLITE_RESULT_SUBTYPE]]
- SQLITE_RESULT_SUBTYPE
- +** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call +** [sqlite3_result_subtype()] to cause a sub-type to be associated with its +** result. +** Every function that invokes [sqlite3_result_subtype()] should have this +** property. If it does not, then the call to [sqlite3_result_subtype()] +** might become a no-op if the function is used as term in an +** [expression index]. On the other hand, SQL functions that never invoke +** [sqlite3_result_subtype()] should avoid setting this property, as the +** purpose of this property is to disable certain optimizations that are +** incompatible with subtypes. **
**+** once, when the auxiliary data is discarded. +** SQLite is free to discard the auxiliary data at any time, including:
** -** Note the last bullet in particular. The destructor X in +** Note the last two bullets in particular. The destructor X in ** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the ** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata() ** should be called near the end of the function implementation and the ** function implementation should not make any use of P after -** sqlite3_set_auxdata() has been called. -** -** ^(In practice, metadata is preserved between function calls for +** sqlite3_set_auxdata() has been called. Furthermore, a call to +** sqlite3_get_auxdata() that occurs immediately after a corresponding call +** to sqlite3_set_auxdata() might still return NULL if an out-of-memory +** condition occurred during the sqlite3_set_auxdata() call or if the +** function is being evaluated during query planning rather than during +** query execution. +** +** ^(In practice, auxiliary data is preserved between function calls for ** function parameters that are compile-time constants, including literal ** values and [parameters] and expressions composed from the same.)^ ** @@ -6089,10 +6291,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); ** ** These routines must be called from the same thread in which ** the SQL function is running. +** +** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()]. */ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +/* +** CAPI3REF: Database Connection Client Data +** METHOD: sqlite3 +** +** These functions are used to associate one or more named pointers +** with a [database connection]. +** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P +** to be attached to [database connection] D using name N. Subsequent +** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P +** or a NULL pointer if there were no prior calls to +** sqlite3_set_clientdata() with the same values of D and N. +** Names are compared using strcmp() and are thus case sensitive. +** +** If P and X are both non-NULL, then the destructor X is invoked with +** argument P on the first of the following occurrences: +****
+** allocation error occurs.)^ +**- ^(when the corresponding function parameter changes)^, or **
- ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the ** SQL statement)^, or **
- ^(when sqlite3_set_auxdata() is invoked again on the same ** parameter)^, or **
- ^(during the original sqlite3_set_auxdata() call when a memory -** allocation error occurs.)^
- ^(during the original sqlite3_set_auxdata() call if the function +** is evaluated during query planning instead of during query execution, +** as sometimes happens with [SQLITE_ENABLE_STAT4].)^
+**
+** +** SQLite does not do anything with client data other than invoke +** destructors on the client data at the appropriate time. The intended +** use for client data is to provide a mechanism for wrapper libraries +** to store additional information about an SQLite database connection. +** +** There is no limit (other than available memory) on the number of different +** client data pointers (with different names) that can be attached to a +** single database connection. However, the implementation is optimized +** for the case of having only one or two different client data names. +** Applications and wrapper libraries are discouraged from using more than +** one client data name each. +** +** There is no way to enumerate the client data pointers +** associated with a database connection. The N parameter can be thought +** of as a secret key such that only code that knows the secret key is able +** to access the associated data. +** +** Security Warning: These interfaces should not be exposed in scripting +** languages or in other circumstances where it might be possible for an +** an attacker to invoke them. Any agent that can invoke these interfaces +** can probably also take control of the process. +** +** Database connection client data is only available for SQLite +** version 3.44.0 ([dateof:3.44.0]) and later. +** +** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()]. +*/ +SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*); +SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*)); /* ** CAPI3REF: Constants Defining Special Destructor Behavior @@ -6294,6 +6553,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n); ** higher order bits are discarded. ** The number of subtype bytes preserved by SQLite might increase ** in future releases of SQLite. +** +** Every [application-defined SQL function] that invokes this interface +** should include the [SQLITE_RESULT_SUBTYPE] property in its +** text encoding argument when the SQL function is +** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE] +** property is omitted from the function that invokes sqlite3_result_subtype(), +** then in some cases the sqlite3_result_subtype() might fail to set +** the result subtype. +** +** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any +** SQL function that invokes the sqlite3_result_subtype() interface +** and that does not have the SQLITE_RESULT_SUBTYPE property will raise +** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1 +** by default. */ SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int); @@ -6465,6 +6738,13 @@ SQLITE_API void sqlite3_activate_cerod( ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. +** +** If a negative argument is passed to sqlite3_sleep() the results vary by +** VFS and operating system. Some system treat a negative argument as an +** instruction to sleep forever. Others understand it to mean do not sleep +** at all. ^In SQLite version 3.42.0 and later, a negative +** argument passed into sqlite3_sleep() is changed to zero before it is relayed +** down into the xSleep method of the VFS. */ SQLITE_API int sqlite3_sleep(int); @@ -6718,7 +6998,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema); /* -** CAPI3REF: Allowed return values from [sqlite3_txn_state()] +** CAPI3REF: Allowed return values from sqlite3_txn_state() ** KEYWORDS: {transaction state} ** ** These constants define the current transaction state of a database file. @@ -6850,7 +7130,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); ** ^Each call to the sqlite3_autovacuum_pages() interface overrides all ** previous invocations for that database connection. ^If the callback ** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer, -** then the autovacuum steps callback is cancelled. The return value +** then the autovacuum steps callback is canceled. The return value ** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might ** be some other error code if something goes wrong. The current ** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other @@ -7309,15 +7589,6 @@ SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void)); */ SQLITE_API void sqlite3_reset_auto_extension(void); -/* -** The interface to the virtual-table mechanism is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** Structures used by the virtual table interface */ @@ -7378,6 +7649,10 @@ struct sqlite3_module { /* The methods above are in versions 1 and 2 of the sqlite_module object. ** Those below are for version 3 and greater. */ int (*xShadowName)(const char*); + /* The methods above are in versions 1 through 3 of the sqlite_module object. + ** Those below are for version 4 and greater. */ + int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema, + const char *zTabName, int mFlags, char **pzErr); }; /* @@ -7436,10 +7711,10 @@ struct sqlite3_module { ** when the omit flag is true there is no guarantee that the constraint will ** not be checked again using byte code.)^ ** -** ^The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxStr values are recorded and passed into the ** [xFilter] method. -** ^[sqlite3_free()] is used to free idxPtr if and only if -** needToFreeIdxPtr is true. +** ^[sqlite3_free()] is used to free idxStr if and only if +** needToFreeIdxStr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate @@ -7559,7 +7834,7 @@ struct sqlite3_index_info { ** the [sqlite3_vtab_collation()] interface. For most real-world virtual ** tables, the collating sequence of constraints does not matter (for example ** because the constraints are numeric) and so the sqlite3_vtab_collation() -** interface is no commonly needed. +** interface is not commonly needed. */ #define SQLITE_INDEX_CONSTRAINT_EQ 2 #define SQLITE_INDEX_CONSTRAINT_GT 4 @@ -7718,16 +7993,6 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); */ SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); -/* -** The interface to the virtual-table mechanism defined above (back up -** to a comment remarkably similar to this one) is currently considered -** to be experimental. The interface might change in incompatible ways. -** If this is a problem for you, do not use the interface at this time. -** -** When the virtual-table mechanism stabilizes, we will declare the -** interface fixed, support it indefinitely, and remove this comment. -*/ - /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} @@ -7875,7 +8140,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); ** code is returned and the transaction rolled back. ** ** Calling this function with an argument that is not a NULL pointer or an -** open blob handle results in undefined behaviour. ^Calling this routine +** open blob handle results in undefined behavior. ^Calling this routine ** with a null pointer (such as would be returned by a failed call to ** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function ** is passed a valid open blob handle, the values returned by the @@ -8111,9 +8376,9 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** -** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or -** sqlite3_mutex_leave() is a NULL pointer, then all three routines -** behave as no-ops. +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), +** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, +** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ @@ -8355,6 +8620,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */ +#define SQLITE_TESTCTRL_FK_NO_ACTION 7 #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 @@ -8383,7 +8649,8 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_TRACEFLAGS 31 #define SQLITE_TESTCTRL_TUNE 32 #define SQLITE_TESTCTRL_LOGEST 33 -#define SQLITE_TESTCTRL_LAST 33 /* Largest TESTCTRL */ +#define SQLITE_TESTCTRL_USELONGDOUBLE 34 +#define SQLITE_TESTCTRL_LAST 34 /* Largest TESTCTRL */ /* ** CAPI3REF: SQL Keyword Checking @@ -9839,7 +10106,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_DIRECTONLY]]- An out-of-memory error occurs during the call to +** sqlite3_set_clientdata() which attempts to register pointer P. +**
- A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made +** with the same D and N parameters. +**
- The database connection closes. SQLite does not make any guarantees +** about the order in which destructors are called, only that all +** destructors will be called exactly once at some point during the +** database connection closing process. +**
SQLITE_VTAB_DIRECTONLY **Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_DIRECTONLY) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** prohibits that virtual table from being used from within triggers and ** views. ** @@ -9847,18 +10114,28 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); ** [[SQLITE_VTAB_INNOCUOUS]]SQLITE_VTAB_INNOCUOUS **Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_INNOCUOUS) from within the -** the [xConnect] or [xCreate] methods of a [virtual table] implmentation +** the [xConnect] or [xCreate] methods of a [virtual table] implementation ** identify that virtual table as being safe to use from within triggers ** and views. Conceptually, the SQLITE_VTAB_INNOCUOUS tag means that the ** virtual table can do no serious harm even if it is controlled by a ** malicious hacker. Developers should avoid setting the SQLITE_VTAB_INNOCUOUS ** flag unless absolutely necessary. ** +** +** [[SQLITE_VTAB_USES_ALL_SCHEMAS]]SQLITE_VTAB_USES_ALL_SCHEMAS +**Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_USES_ALL_SCHEMA) from within the +** the [xConnect] or [xCreate] methods of a [virtual table] implementation +** instruct the query planner to begin at least a read transaction on +** all schemas ("main", "temp", and any ATTACH-ed databases) whenever the +** virtual table is used. +** ** */ #define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 #define SQLITE_VTAB_INNOCUOUS 2 #define SQLITE_VTAB_DIRECTONLY 3 +#define SQLITE_VTAB_USES_ALL_SCHEMAS 4 /* ** CAPI3REF: Determine The Virtual Table Conflict Policy @@ -9931,7 +10208,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context*); **Otherwise, "BINARY" is returned. ** */ -SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); +SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int); /* ** CAPI3REF: Determine if a virtual table query is DISTINCT @@ -10019,7 +10296,7 @@ SQLITE_API int sqlite3_vtab_distinct(sqlite3_index_info*); ** communicated to the xBestIndex method as a ** [SQLITE_INDEX_CONSTRAINT_EQ] constraint.)^ If xBestIndex wants to use ** this constraint, it must set the corresponding -** aConstraintUsage[].argvIndex to a postive integer. ^(Then, under +** aConstraintUsage[].argvIndex to a positive integer. ^(Then, under ** the usual mode of handling IN operators, SQLite generates [bytecode] ** that invokes the [xFilter|xFilter() method] once for each value ** on the right-hand side of the IN operator.)^ Thus the virtual table @@ -10088,21 +10365,20 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle); ** is undefined and probably harmful. ** ** The X parameter in a call to sqlite3_vtab_in_first(X,P) or -** sqlite3_vtab_in_next(X,P) must be one of the parameters to the +** sqlite3_vtab_in_next(X,P) should be one of the parameters to the ** xFilter method which invokes these routines, and specifically ** a parameter that was previously selected for all-at-once IN constraint ** processing use the [sqlite3_vtab_in()] interface in the ** [xBestIndex|xBestIndex method]. ^(If the X parameter is not ** an xFilter argument that was selected for all-at-once IN constraint -** processing, then these routines return [SQLITE_MISUSE])^ or perhaps -** exhibit some other undefined or harmful behavior. +** processing, then these routines return [SQLITE_ERROR].)^ ** ** ^(Use these routines to access all values on the right-hand side ** of the IN constraint using code like the following: ** **
** for(rc=sqlite3_vtab_in_first(pList, &pVal); -** rc==SQLITE_OK && pVal +** rc==SQLITE_OK && pVal; ** rc=sqlite3_vtab_in_next(pList, &pVal) ** ){ ** // do something with pVal @@ -10200,6 +10476,10 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** managed by the prepared statement S and will be automatically freed when ** S is finalized. ** +** Not all values are available for all query elements. When a value is +** not available, the output variable is set to -1 if the value is numeric, +** or to NULL if it is a string (SQLITE_SCANSTAT_NAME). +** **** [[SQLITE_SCANSTAT_NLOOP]]
*/ #define SQLITE_SCANSTAT_NLOOP 0 @@ -10241,12 +10533,14 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** #define SQLITE_SCANSTAT_NAME 3 #define SQLITE_SCANSTAT_EXPLAIN 4 #define SQLITE_SCANSTAT_SELECTID 5 +#define SQLITE_SCANSTAT_PARENTID 6 +#define SQLITE_SCANSTAT_NCYCLE 7 /* ** CAPI3REF: Prepared Statement Scan Status ** METHOD: sqlite3_stmt ** -** This interface returns information about the predicted and measured +** These interfaces return information about the predicted and measured ** performance for pStmt. Advanced applications can use this ** interface to compare the predicted and the measured performance and ** issue warnings and/or rerun [ANALYZE] if discrepancies are found. @@ -10257,19 +10551,25 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** ** The "iScanStatusOp" parameter determines which status information to return. ** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior -** of this interface is undefined. -** ^The requested measurement is written into a variable pointed to by -** the "pOut" parameter. -** Parameter "idx" identifies the specific loop to retrieve statistics for. -** Loops are numbered starting from zero. ^If idx is out of range - less than -** zero or greater than or equal to the total number of loops used to implement -** the statement - a non-zero value is returned and the variable that pOut -** points to is unchanged. -** -** ^Statistics might not be available for all loops in all statements. ^In cases -** where there exist loops with no available statistics, this function behaves -** as if the loop did not exist - it returns non-zero and leave the variable -** that pOut points to unchanged. +** of this interface is undefined. ^The requested measurement is written into +** a variable pointed to by the "pOut" parameter. +** +** The "flags" parameter must be passed a mask of flags. At present only +** one flag is defined - SQLITE_SCANSTAT_COMPLEX. If SQLITE_SCANSTAT_COMPLEX +** is specified, then status information is available for all elements +** of a query plan that are reported by "EXPLAIN QUERY PLAN" output. If +** SQLITE_SCANSTAT_COMPLEX is not specified, then only query plan elements +** that correspond to query loops (the "SCAN..." and "SEARCH..." elements of +** the EXPLAIN QUERY PLAN output) are available. Invoking API +** sqlite3_stmt_scanstatus() is equivalent to calling +** sqlite3_stmt_scanstatus_v2() with a zeroed flags parameter. +** +** Parameter "idx" identifies the specific query element to retrieve statistics +** for. Query elements are numbered starting from zero. A value of -1 may be +** to query for statistics regarding the entire query. ^If idx is out of range +** - less than -1 or greater than or equal to the total number of query +** elements used to implement the statement - a non-zero value is returned and +** the variable that pOut points to is unchanged. ** ** See also: [sqlite3_stmt_scanstatus_reset()] */ @@ -10279,6 +10579,19 @@ SQLITE_API int sqlite3_stmt_scanstatus( int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ void *pOut /* Result written here */ ); +SQLITE_API int sqlite3_stmt_scanstatus_v2( + sqlite3_stmt *pStmt, /* Prepared statement for which info desired */ + int idx, /* Index of loop to report on */ + int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */ + int flags, /* Mask of flags defined below */ + void *pOut /* Result written here */ +); + +/* +** CAPI3REF: Prepared Statement Scan Status +** KEYWORDS: {scan status flags} +*/ +#define SQLITE_SCANSTAT_COMPLEX 0x0001 /* ** CAPI3REF: Zero Scan-Status Counters @@ -10369,6 +10682,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** function is not defined for operations on WITHOUT ROWID tables, or for ** DELETE operations on rowid tables. ** +** ^The sqlite3_preupdate_hook(D,C,P) function returns the P argument from +** the previous call on the same [database connection] D, or NULL for +** the first call on D. +** ** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()], ** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces ** provide additional information about a preupdate event. These routines @@ -10408,7 +10725,7 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*); ** When the [sqlite3_blob_write()] API is used to update a blob column, ** the pre-update hook is invoked with SQLITE_DELETE. This is because the ** in this case the new values are not available. In this case, when a -** callback made with op==SQLITE_DELETE is actuall a write using the +** callback made with op==SQLITE_DELETE is actually a write using the ** sqlite3_blob_write() API, the [sqlite3_preupdate_blobwrite()] returns ** the index of the column being written. In other cases, where the ** pre-update hook is being invoked for some other reason, including a @@ -10669,6 +10986,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c ** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy ** of the database exists. ** +** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set, +** the returned buffer content will remain accessible and unchanged +** until either the next write operation on the connection or when +** the connection is closed, and applications must not modify the +** buffer. If the bit had been clear, the returned buffer will not +** be accessed by SQLite after the call. +** ** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the ** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory ** allocation error occurs. @@ -10717,6 +11041,9 @@ SQLITE_API unsigned char *sqlite3_serialize( ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** +** Applications must not modify the buffer P or invalidate it before +** the database connection D is closed. +** ** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the ** database is currently in a read transaction or is involved in a backup ** operation. @@ -10725,6 +11052,13 @@ SQLITE_API unsigned char *sqlite3_serialize( ** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the ** function returns SQLITE_ERROR. ** +** The deserialized database should not be in [WAL mode]. If the database +** is in WAL mode, then any attempt to use the database file will result +** in an [SQLITE_CANTOPEN] error. The application can set the +** [file format version numbers] (bytes 18 and 19) of the input database P +** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the +** database file into rollback mode and work around this limitation. +** ** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the ** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then ** [sqlite3_free()] is invoked on argument P prior to returning. @@ -10774,6 +11108,19 @@ SQLITE_API int sqlite3_deserialize( # undef double #endif +#if defined(__wasi__) +# undef SQLITE_WASI +# define SQLITE_WASI 1 +# undef SQLITE_OMIT_WAL +# define SQLITE_OMIT_WAL 1/* because it requires shared memory APIs */ +# ifndef SQLITE_OMIT_LOAD_EXTENSION +# define SQLITE_OMIT_LOAD_EXTENSION +# endif +# ifndef SQLITE_THREADSAFE +# define SQLITE_THREADSAFE 0 +# endif +#endif + #if 0 } /* End of the 'extern "C"' block */ #endif @@ -10980,16 +11327,20 @@ SQLITE_API int sqlite3session_create( SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); /* -** CAPIREF: Conigure a Session Object +** CAPI3REF: Configure a Session Object ** METHOD: sqlite3_session ** ** This method is used to configure a session object after it has been -** created. At present the only valid value for the second parameter is -** [SQLITE_SESSION_OBJCONFIG_SIZE]. +** created. At present the only valid values for the second parameter are +** [SQLITE_SESSION_OBJCONFIG_SIZE] and [SQLITE_SESSION_OBJCONFIG_ROWID]. ** -** Arguments for sqlite3session_object_config() +*/ +SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); + +/* +** CAPI3REF: Options for sqlite3session_object_config ** -** The following values may passed as the the 4th parameter to +** The following values may passed as the the 2nd parameter to ** sqlite3session_object_config(). ** **- SQLITE_SCANSTAT_NLOOP
**- ^The [sqlite3_int64] variable pointed to by the V parameter will be @@ -10227,12 +10507,24 @@ SQLITE_API int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value ** ** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN] ** description for the X-th loop. ** -** [[SQLITE_SCANSTAT_SELECTID]]
- SQLITE_SCANSTAT_SELECT
+** [[SQLITE_SCANSTAT_SELECTID]]- SQLITE_SCANSTAT_SELECTID
**- ^The "int" variable pointed to by the V parameter will be set to the -** "select-id" for the X-th loop. The select-id identifies which query or -** subquery the loop is part of. The main query has a select-id of zero. -** The select-id is the same value as is output in the first column -** of an [EXPLAIN QUERY PLAN] query. +** id for the X-th query plan element. The id value is unique within the +** statement. The select-id is the same value as is output in the first +** column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_PARENTID]]
- SQLITE_SCANSTAT_PARENTID
+**- The "int" variable pointed to by the V parameter will be set to the +** the id of the parent of the current query element, if applicable, or +** to zero if the query element has no parent. This is the same value as +** returned in the second column of an [EXPLAIN QUERY PLAN] query. +** +** [[SQLITE_SCANSTAT_NCYCLE]]
- SQLITE_SCANSTAT_NCYCLE
+**- The sqlite3_int64 output value is set to the number of cycles, +** according to the processor time-stamp counter, that elapsed while the +** query element was being processed. This value is not available for +** all query elements - if it is unavailable the output variable is +** set to -1. **
SQLITE_SESSION_OBJCONFIG_SIZE @@ -11005,12 +11356,21 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession); ** ** It is an error (SQLITE_MISUSE) to attempt to modify this setting after ** the first table has been attached to the session object. +** +** SQLITE_SESSION_OBJCONFIG_ROWID +** This option is used to set, clear or query the flag that enables +** collection of data for tables with no explicit PRIMARY KEY. +** +** Normally, tables with no explicit PRIMARY KEY are simply ignored +** by the sessions module. However, if this flag is set, it behaves +** as if such tables have a column "_rowid_ INTEGER PRIMARY KEY" inserted +** as their leftmost columns. +** +** It is an error (SQLITE_MISUSE) to attempt to modify this setting after +** the first table has been attached to the session object. */ -SQLITE_API int sqlite3session_object_config(sqlite3_session*, int op, void *pArg); - -/* -*/ -#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_SIZE 1 +#define SQLITE_SESSION_OBJCONFIG_ROWID 2 /* ** CAPI3REF: Enable Or Disable A Session Object @@ -11771,6 +12131,18 @@ SQLITE_API int sqlite3changeset_concat( ); +/* +** CAPI3REF: Upgrade the Schema of a Changeset/Patchset +*/ +SQLITE_API int sqlite3changeset_upgrade( + sqlite3 *db, + const char *zDb, + int nIn, const void *pIn, /* Input changeset */ + int *pnOut, void **ppOut /* OUT: Inverse of input */ +); + + + /* ** CAPI3REF: Changegroup Handle ** @@ -11817,6 +12189,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup; */ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); +/* +** CAPI3REF: Add a Schema to a Changegroup +** METHOD: sqlite3_changegroup_schema +** +** This method may be used to optionally enforce the rule that the changesets +** added to the changegroup handle must match the schema of database zDb +** ("main", "temp", or the name of an attached database). If +** sqlite3changegroup_add() is called to add a changeset that is not compatible +** with the configured schema, SQLITE_SCHEMA is returned and the changegroup +** object is left in an undefined state. +** +** A changeset schema is considered compatible with the database schema in +** the same way as for sqlite3changeset_apply(). Specifically, for each +** table in the changeset, there exists a database table with: +** +** +**
+** +** The output of the changegroup object always has the same schema as the +** database nominated using this function. In cases where changesets passed +** to sqlite3changegroup_add() have fewer columns than the corresponding table +** in the database schema, these are filled in using the default column +** values from the database schema. This makes it possible to combined +** changesets that have different numbers of columns for a single table +** within a changegroup, provided that they are otherwise compatible. +*/ +SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb); + /* ** CAPI3REF: Add A Changeset To A Changegroup ** METHOD: sqlite3_changegroup @@ -11885,13 +12289,18 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp); ** If the new changeset contains changes to a table that is already present ** in the changegroup, then the number of columns and the position of the ** primary key columns for the table must be consistent. If this is not the -** case, this function fails with SQLITE_SCHEMA. If the input changeset -** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is -** returned. Or, if an out-of-memory condition occurs during processing, this -** function returns SQLITE_NOMEM. In all cases, if an error occurs the state -** of the final contents of the changegroup is undefined. +** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup +** object has been configured with a database schema using the +** sqlite3changegroup_schema() API, then it is possible to combine changesets +** with different numbers of columns for a single table, provided that +** they are otherwise compatible. +** +** If the input changeset appears to be corrupt and the corruption is +** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition +** occurs during processing, this function returns SQLITE_NOMEM. ** -** If no error occurs, SQLITE_OK is returned. +** In all cases, if an error occurs the state of the final contents of the +** changegroup is undefined. If no error occurs, SQLITE_OK is returned. */ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData); @@ -12143,9 +12552,30 @@ SQLITE_API int sqlite3changeset_apply_v2( ** Invert the changeset before applying it. This is equivalent to inverting ** a changeset using sqlite3changeset_invert() before applying it. It is ** an error to specify this flag with a patchset. +** +**- The name identified by the changeset, and +**
- at least as many columns as recorded in the changeset, and +**
- the primary key columns in the same position as recorded in +** the changeset. +**
SQLITE_CHANGESETAPPLY_IGNORENOOP +** Do not invoke the conflict handler callback for any changes that +** would not actually modify the database even if they were applied. +** Specifically, this means that the conflict handler is not invoked +** for: +** +**
+** +**- a delete change if the row being deleted cannot be found, +**
- an update change if the modified fields are already set to +** their new values in the conflicting row, or +**
- an insert change if all fields of the conflicting row match +** the row being inserted. +**
SQLITE_CHANGESETAPPLY_FKNOACTION +** If this flag it set, then all foreign key constraints in the target +** database behave as if they were declared with "ON UPDATE NO ACTION ON +** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL +** or SET DEFAULT. */ #define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001 #define SQLITE_CHANGESETAPPLY_INVERT 0x0002 +#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004 +#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008 /* ** CAPI3REF: Constants Passed To The Conflict Handler @@ -12886,7 +13316,7 @@ struct Fts5PhraseIter { ** See xPhraseFirstColumn above. */ struct Fts5ExtensionApi { - int iVersion; /* Currently always set to 3 */ + int iVersion; /* Currently always set to 2 */ void *(*xUserData)(Fts5Context*); @@ -13115,8 +13545,8 @@ struct Fts5ExtensionApi { ** as separate queries of the FTS index are required for each synonym. ** ** When using methods (2) or (3), it is important that the tokenizer only -** provide synonyms when tokenizing document text (method (2)) or query -** text (method (3)), not both. Doing so will not cause any errors, but is +** provide synonyms when tokenizing document text (method (3)) or query +** text (method (2)), not both. Doing so will not cause any errors, but is ** inefficient. */ typedef struct Fts5Tokenizer Fts5Tokenizer; @@ -13164,7 +13594,7 @@ struct fts5_api { int (*xCreateTokenizer)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_tokenizer *pTokenizer, void (*xDestroy)(void*) ); @@ -13173,7 +13603,7 @@ struct fts5_api { int (*xFindTokenizer)( fts5_api *pApi, const char *zName, - void **ppContext, + void **ppUserData, fts5_tokenizer *pTokenizer ); @@ -13181,7 +13611,7 @@ struct fts5_api { int (*xCreateFunction)( fts5_api *pApi, const char *zName, - void *pContext, + void *pUserData, fts5_extension_function xFunction, void (*xDestroy)(void*) ); @@ -13292,7 +13722,7 @@ struct fts5_api { ** level of recursion for each term. A stack overflow can result ** if the number of terms is too large. In practice, most SQL ** never has more than 3 or 4 terms. Use a value of 0 to disable -** any limit on the number of terms in a compount SELECT. +** any limit on the number of terms in a compound SELECT. */ #ifndef SQLITE_MAX_COMPOUND_SELECT # define SQLITE_MAX_COMPOUND_SELECT 500 @@ -13442,8 +13872,8 @@ struct fts5_api { #endif /* -** WAL mode depends on atomic aligned 32-bit loads and stores in a few -** places. The following macros try to make this explicit. +** A few places in the code require atomic load/store of aligned +** integer values. */ #ifndef __has_extension # define __has_extension(x) 0 /* compatibility with non-clang compilers */ @@ -13499,15 +13929,22 @@ struct fts5_api { #endif /* -** A macro to hint to the compiler that a function should not be +** Macros to hint to the compiler that a function should or should not be ** inlined. */ #if defined(__GNUC__) # define SQLITE_NOINLINE __attribute__((noinline)) +# define SQLITE_INLINE __attribute__((always_inline)) inline #elif defined(_MSC_VER) && _MSC_VER>=1310 # define SQLITE_NOINLINE __declspec(noinline) +# define SQLITE_INLINE __forceinline #else # define SQLITE_NOINLINE +# define SQLITE_INLINE +#endif +#if defined(SQLITE_COVERAGE_TEST) || defined(__STRICT_ANSI__) +# undef SQLITE_INLINE +# define SQLITE_INLINE #endif /* @@ -13529,6 +13966,16 @@ struct fts5_api { # endif #endif +/* +** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit +** SEH support if the -DSQLITE_OMIT_SEH option is given. +*/ +#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH) +# define SQLITE_USE_SEH 1 +#else +# undef SQLITE_USE_SEH +#endif + /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never @@ -14325,15 +14772,9 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ /* ** The datatype used to store estimates of the number of rows in a -** table or index. This is an unsigned integer type. For 99.9% of -** the world, a 32-bit integer is sufficient. But a 64-bit integer -** can be used at compile-time if desired. +** table or index. */ -#ifdef SQLITE_64BIT_STATS - typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ -#else - typedef u32 tRowcnt; /* 32-bit is the default */ -#endif +typedef u64 tRowcnt; /* ** Estimated quantities used for query planning are stored as 16-bit @@ -14394,8 +14835,31 @@ typedef INT16_TYPE LogEst; ** the end of buffer S. This macro returns true if P points to something ** contained within the buffer S. */ -#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) +#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E))) +/* +** P is one byte past the end of a large buffer. Return true if a span of bytes +** between S..E crosses the end of that buffer. In other words, return true +** if the sub-buffer S..E-1 overflows the buffer whose last byte is P-1. +** +** S is the start of the span. E is one byte past the end of end of span. +** +** P +** |-----------------| FALSE +** |-------| +** S E +** +** P +** |-----------------| +** |-------| TRUE +** S E +** +** P +** |-----------------| +** |-------| FALSE +** S E +*/ +#define SQLITE_OVERFLOW(P,S,E) (((uptr)(S)<(uptr)(P))&&((uptr)(E)>(uptr)(P))) /* ** Macros to determine whether the machine is big or little endian, @@ -14405,16 +14869,33 @@ typedef INT16_TYPE LogEst; ** using C-preprocessor macros. If that is unsuccessful, or if ** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined ** at run-time. +** +** If you are building SQLite on some obscure platform for which the +** following ifdef magic does not work, you can always include either: +** +** -DSQLITE_BYTEORDER=1234 +** +** or +** +** -DSQLITE_BYTEORDER=4321 +** +** to cause the build to work for little-endian or big-endian processors, +** respectively. */ -#ifndef SQLITE_BYTEORDER -# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \ +#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */ +# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ +# define SQLITE_BYTEORDER 4321 +# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ +# define SQLITE_BYTEORDER 1234 +# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1 +# define SQLITE_BYTEORDER 4321 +# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \ defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64) -# define SQLITE_BYTEORDER 1234 -# elif defined(sparc) || defined(__ppc__) || \ - defined(__ARMEB__) || defined(__AARCH64EB__) -# define SQLITE_BYTEORDER 4321 +# define SQLITE_BYTEORDER 1234 +# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__) +# define SQLITE_BYTEORDER 4321 # else # define SQLITE_BYTEORDER 0 # endif @@ -14479,9 +14960,9 @@ typedef INT16_TYPE LogEst; ** pointers. In that case, only verify 4-byte alignment. */ #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) #else -# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) #endif /* @@ -14535,15 +15016,38 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace; && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE) \ || defined(SQLITE_ENABLE_TREETRACE)) # define TREETRACE_ENABLED 1 -# define SELECTTRACE(K,P,S,X) \ +# define TREETRACE(K,P,S,X) \ if(sqlite3TreeTrace&(K)) \ sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\ sqlite3DebugPrintf X #else -# define SELECTTRACE(K,P,S,X) +# define TREETRACE(K,P,S,X) # define TREETRACE_ENABLED 0 #endif +/* TREETRACE flag meanings: +** +** 0x00000001 Beginning and end of SELECT processing +** 0x00000002 WHERE clause processing +** 0x00000004 Query flattener +** 0x00000008 Result-set wildcard expansion +** 0x00000010 Query name resolution +** 0x00000020 Aggregate analysis +** 0x00000040 Window functions +** 0x00000080 Generated column names +** 0x00000100 Move HAVING terms into WHERE +** 0x00000200 Count-of-view optimization +** 0x00000400 Compound SELECT processing +** 0x00000800 Drop superfluous ORDER BY +** 0x00001000 LEFT JOIN simplifies to JOIN +** 0x00002000 Constant propagation +** 0x00004000 Push-down optimization +** 0x00008000 After all FROM-clause analysis +** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing +** 0x00020000 Transform DISTINCT into GROUP BY +** 0x00040000 SELECT tree dump after all code has been generated +*/ + /* ** Macros for "wheretrace" */ @@ -14556,6 +15060,36 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace; # define WHERETRACE(K,X) #endif +/* +** Bits for the sqlite3WhereTrace mask: +** +** (---any--) Top-level block structure +** 0x-------F High-level debug messages +** 0x----FFF- More detail +** 0xFFFF---- Low-level debug messages +** +** 0x00000001 Code generation +** 0x00000002 Solver +** 0x00000004 Solver costs +** 0x00000008 WhereLoop inserts +** +** 0x00000010 Display sqlite3_index_info xBestIndex calls +** 0x00000020 Range an equality scan metrics +** 0x00000040 IN operator decisions +** 0x00000080 WhereLoop cost adjustements +** 0x00000100 +** 0x00000200 Covering index decisions +** 0x00000400 OR optimization +** 0x00000800 Index scanner +** 0x00001000 More details associated with code generation +** 0x00002000 +** 0x00004000 Show all WHERE terms at key points +** 0x00008000 Show the full SELECT statement at key places +** +** 0x00010000 Show more detail when printing WHERE terms +** 0x00020000 Show WHERE terms returned from whereScanNext() +*/ + /* ** An instance of the following structure is used to store the busy-handler @@ -14576,7 +15110,7 @@ struct BusyHandler { /* ** Name of table that holds the database schema. ** -** The PREFERRED names are used whereever possible. But LEGACY is also +** The PREFERRED names are used wherever possible. But LEGACY is also ** used for backwards compatibility. ** ** 1. Queries can use either the PREFERRED or the LEGACY names @@ -14685,11 +15219,13 @@ typedef struct Column Column; typedef struct Cte Cte; typedef struct CteUse CteUse; typedef struct Db Db; +typedef struct DbClientData DbClientData; typedef struct DbFixer DbFixer; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct FKey FKey; +typedef struct FpDecode FpDecode; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; @@ -14708,6 +15244,7 @@ typedef struct Parse Parse; typedef struct ParseCleanup ParseCleanup; typedef struct PreUpdate PreUpdate; typedef struct PrintfArguments PrintfArguments; +typedef struct RCStr RCStr; typedef struct RenameToken RenameToken; typedef struct Returning Returning; typedef struct RowSet RowSet; @@ -15345,6 +15882,10 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); # define enable_simulated_io_errors() #endif +#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) +SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager*); +#endif + #endif /* SQLITE_PAGER_H */ /************** End of pager.h ***********************************************/ @@ -15536,7 +16077,7 @@ SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); ** reduce network bandwidth. ** ** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by -** standard SQLite. The other hints are provided for extentions that use +** standard SQLite. The other hints are provided for extensions that use ** the SQLite parser and code generator but substitute their own storage ** engine. */ @@ -15674,15 +16215,21 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int flags); SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorPin(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeCursorUnpin(BtCursor*); -#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); -#endif SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); -SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(sqlite3*,Btree*,Pgno*aRoot,int nRoot,int,int*); +SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck( + sqlite3 *db, /* Database connection that is running the check */ + Btree *p, /* The btree to be checked */ + Pgno *aRoot, /* An array of root pages numbers for individual trees */ + int nRoot, /* Number of entries in aRoot[] */ + int mxErr, /* Stop reporting errors after this many */ + int *pnErr, /* OUT: Write number of errors seen to this variable */ + char **pzOut /* OUT: Write the error message string here */ +); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor*); @@ -15839,14 +16386,14 @@ struct VdbeOp { #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS char *zComment; /* Comment to improve readability */ #endif -#ifdef VDBE_PROFILE - u32 cnt; /* Number of times this instruction was executed */ - u64 cycles; /* Total time spent executing this instruction */ -#endif #ifdef SQLITE_VDBE_COVERAGE u32 iSrcLine; /* Source-code line that generated this opcode ** with flags in the upper 8 bits */ #endif +#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || defined(VDBE_PROFILE) + u64 nExec; + u64 nCycle; +#endif }; typedef struct VdbeOp VdbeOp; @@ -15898,6 +16445,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */ #define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */ +#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */ /* Error message codes for OP_Halt */ #define P5_ConstraintNotNull 1 @@ -16113,19 +16661,20 @@ typedef struct VdbeOpList VdbeOpList; #define OP_VCreate 171 #define OP_VDestroy 172 #define OP_VOpen 173 -#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */ -#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 176 -#define OP_Pagecount 177 -#define OP_MaxPgcnt 178 -#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */ -#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */ -#define OP_Trace 181 -#define OP_CursorHint 182 -#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */ -#define OP_Noop 184 -#define OP_Explain 185 -#define OP_Abortable 186 +#define OP_VCheck 174 +#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */ +#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 177 +#define OP_Pagecount 178 +#define OP_MaxPgcnt 179 +#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */ +#define OP_FilterAdd 181 /* synopsis: filter(P1) += key(P3@P4) */ +#define OP_Trace 182 +#define OP_CursorHint 183 +#define OP_ReleaseReg 184 /* synopsis: release r[P1@P2] mask P3 */ +#define OP_Noop 185 +#define OP_Explain 186 +#define OP_Abortable 187 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -16137,31 +16686,32 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_IN3 0x08 /* in3: P3 is an input */ #define OPFLG_OUT2 0x10 /* out2: P2 is an output */ #define OPFLG_OUT3 0x20 /* out3: P3 is an output */ +#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,\ +/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\ /* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\ -/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x09, 0x09, 0x09,\ -/* 24 */ 0x09, 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\ -/* 32 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\ -/* 40 */ 0x01, 0x01, 0x01, 0x26, 0x26, 0x01, 0x23, 0x0b,\ +/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\ +/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\ +/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\ +/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\ /* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\ -/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01,\ +/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\ /* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\ /* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\ /* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02,\ -/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x00, 0x00,\ -/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x26, 0x26,\ +/* 88 */ 0x02, 0x00, 0x00, 0x12, 0x1e, 0x20, 0x40, 0x00,\ +/* 96 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x40, 0x26, 0x26,\ /* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\ -/* 112 */ 0x00, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00,\ -/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\ -/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ -/* 136 */ 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10, 0x00,\ +/* 112 */ 0x40, 0x00, 0x12, 0x40, 0x40, 0x10, 0x40, 0x00,\ +/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\ +/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\ +/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\ /* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\ /* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ /* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,\ -/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\ -/* 184 */ 0x00, 0x00, 0x00,} +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\ +/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00,\ +/* 184 */ 0x00, 0x00, 0x00, 0x00,} /* The resolve3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -16214,14 +16764,20 @@ SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(Vdbe*,int,int,int); #endif SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno); #ifndef SQLITE_OMIT_EXPLAIN -SQLITE_PRIVATE void sqlite3VdbeExplain(Parse*,u8,const char*,...); +SQLITE_PRIVATE int sqlite3VdbeExplain(Parse*,u8,const char*,...); SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse*); SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*); # define ExplainQueryPlan(P) sqlite3VdbeExplain P +# ifdef SQLITE_ENABLE_STMT_SCANSTATUS +# define ExplainQueryPlan2(V,P) (V = sqlite3VdbeExplain P) +# else +# define ExplainQueryPlan2(V,P) ExplainQueryPlan(P) +# endif # define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P) # define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P) #else # define ExplainQueryPlan(P) +# define ExplainQueryPlan2(V,P) # define ExplainQueryPlanPop(P) # define ExplainQueryPlanParent(P) 0 # define sqlite3ExplainBreakpoint(A,B) /*no-op*/ @@ -16330,7 +16886,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** The VdbeCoverage macros are used to set a coverage testing point ** for VDBE branch instructions. The coverage testing points are line ** numbers in the sqlite3.c source file. VDBE branch coverage testing -** only works with an amalagmation build. That's ok since a VDBE branch +** only works with an amalgamation build. That's ok since a VDBE branch ** coverage build designed for testing the test suite only. No application ** should ever ship with VDBE branch coverage measuring turned on. ** @@ -16348,7 +16904,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** // NULL option is not possible ** ** VdbeCoverageEqNe(v) // Previous OP_Jump is only interested -** // in distingishing equal and not-equal. +** // in distinguishing equal and not-equal. ** ** Every VDBE branch operation must be tagged with one of the macros above. ** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and @@ -16358,7 +16914,7 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); ** During testing, the test application will invoke ** sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE,...) to set a callback ** routine that is invoked as each bytecode branch is taken. The callback -** contains the sqlite3.c source line number ov the VdbeCoverage macro and +** contains the sqlite3.c source line number of the VdbeCoverage macro and ** flags to indicate whether or not the branch was taken. The test application ** is responsible for keeping track of this and reporting byte-code branches ** that are never taken. @@ -16394,14 +16950,22 @@ SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*); +SQLITE_PRIVATE void sqlite3VdbeScanStatusRange(Vdbe*, int, int, int); +SQLITE_PRIVATE void sqlite3VdbeScanStatusCounters(Vdbe*, int, int, int); #else -# define sqlite3VdbeScanStatus(a,b,c,d,e) +# define sqlite3VdbeScanStatus(a,b,c,d,e,f) +# define sqlite3VdbeScanStatusRange(a,b,c,d) +# define sqlite3VdbeScanStatusCounters(a,b,c,d) #endif #if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, VdbeOp*); #endif +#if defined(SQLITE_ENABLE_CURSOR_HINTS) && defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3CursorRangeHintExprCheck(Walker *pWalker, Expr *pExpr); +#endif + #endif /* SQLITE_VDBE_H */ /************** End of vdbe.h ************************************************/ @@ -16450,7 +17014,7 @@ struct PgHdr { ** private to pcache.c and should not be accessed by other modules. ** pCache is grouped with the public elements for efficiency. */ - i16 nRef; /* Number of users of this page */ + i64 nRef; /* Number of users of this page */ PgHdr *pDirtyNext; /* Next element in list of dirty pages */ PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ /* NB: pDirtyNext and pDirtyPrev are undefined if the @@ -16531,12 +17095,12 @@ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ -SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); +SQLITE_PRIVATE i64 sqlite3PcacheRefCount(PCache*); /* Increment the reference count of an existing page */ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); -SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); +SQLITE_PRIVATE i64 sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); @@ -16689,7 +17253,7 @@ SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); /* ** Default synchronous levels. ** -** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ +** Note that (for historical reasons) the PAGER_SYNCHRONOUS_* macros differ ** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1. ** ** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS @@ -16728,7 +17292,7 @@ struct Db { ** An instance of the following structure stores a database schema. ** ** Most Schema objects are associated with a Btree. The exception is -** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** the Schema for the TEMP database (sqlite3.aDb[1]) which is free-standing. ** In shared cache mode, a single Schema object can be shared by multiple ** Btrees that refer to the same underlying BtShared object. ** @@ -16839,7 +17403,7 @@ struct Lookaside { LookasideSlot *pInit; /* List of buffers not previously used */ LookasideSlot *pFree; /* List of available buffers */ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE - LookasideSlot *pSmallInit; /* List of small buffers not prediously used */ + LookasideSlot *pSmallInit; /* List of small buffers not previously used */ LookasideSlot *pSmallFree; /* List of available small buffers */ void *pMiddle; /* First byte past end of full-size buffers and ** the first byte of LOOKASIDE_SMALL buffers */ @@ -16856,7 +17420,7 @@ struct LookasideSlot { #define EnableLookaside db->lookaside.bDisable--;\ db->lookaside.sz=db->lookaside.bDisable?0:db->lookaside.szTrue -/* Size of the smaller allocations in two-size lookside */ +/* Size of the smaller allocations in two-size lookaside */ #ifdef SQLITE_OMIT_TWOSIZE_LOOKASIDE # define LOOKASIDE_SMALL 0 #else @@ -17056,6 +17620,7 @@ struct sqlite3 { i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ + DbClientData *pDbData; /* sqlite3_set_clientdata() content */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MAIN ** mutex, not by sqlite3.mutex. They are used by code in notify.c. @@ -17111,7 +17676,7 @@ struct sqlite3 { #define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ /* result set is empty */ #define SQLITE_IgnoreChecks 0x00000200 /* Do not enforce check constraints */ -#define SQLITE_ReadUncommit 0x00000400 /* READ UNCOMMITTED in shared-cache */ +#define SQLITE_StmtScanStatus 0x00000400 /* Enable stmt_scanstats() counters */ #define SQLITE_NoCkptOnClose 0x00000800 /* No checkpoint on close()/DETACH */ #define SQLITE_ReverseOrder 0x00001000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x00002000 /* Enable recursive triggers */ @@ -17137,6 +17702,8 @@ struct sqlite3 { /* DELETE, or UPDATE and return */ /* the count using a callback. */ #define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */ +#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */ +#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */ /* Flags used only if debugging */ #ifdef SQLITE_DEBUG @@ -17192,6 +17759,9 @@ struct sqlite3 { #define SQLITE_FlttnUnionAll 0x00800000 /* Disable the UNION ALL flattener */ /* TH3 expects this value ^^^^^^^^^^ See flatten04.test */ #define SQLITE_IndexedExpr 0x01000000 /* Pull exprs from index when able */ +#define SQLITE_Coroutines 0x02000000 /* Co-routines for subqueries */ +#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */ +#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */ #define SQLITE_AllOpts 0xffffffff /* All optimizations */ /* @@ -17274,10 +17844,17 @@ struct FuncDestructor { ** SQLITE_FUNC_ANYORDER == NC_OrderAgg == SF_OrderByReqd ** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG ** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG +** SQLITE_FUNC_BYTELEN == OPFLAG_BYTELENARG ** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API ** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API -** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS +** SQLITE_FUNC_UNSAFE == SQLITE_INNOCUOUS -- opposite meanings!!! ** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API +** +** Note that even though SQLITE_FUNC_UNSAFE and SQLITE_INNOCUOUS have the +** same bit value, their meanings are inverted. SQLITE_FUNC_UNSAFE is +** used internally and if set means that the function has side effects. +** SQLITE_INNOCUOUS is used by application code and means "not unsafe". +** See multiple instances of tag-20230109-1. */ #define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */ #define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */ @@ -17286,6 +17863,7 @@ struct FuncDestructor { #define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/ #define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */ #define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */ +#define SQLITE_FUNC_BYTELEN 0x00c0 /* Built-in octet_length() function */ #define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */ /* 0x0200 -- available for reuse */ #define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */ @@ -17294,14 +17872,15 @@ struct FuncDestructor { #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */ -/* 0x8000 -- available for reuse */ +#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */ #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 */ +/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */ #define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */ #define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */ #define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */ +/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */ #define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */ /* Identifier numbers for each in-line function */ @@ -17393,9 +17972,10 @@ struct FuncDestructor { #define MFUNCTION(zName, nArg, xPtr, xFunc) \ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ xPtr, 0, xFunc, 0, 0, 0, #zName, {0} } -#define JFUNCTION(zName, nArg, iArg, xFunc) \ - {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|\ - SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \ +#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, iArg, xFunc) \ + {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\ + SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\ + ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} } #define INLINE_FUNC(zName, nArg, iArg, mFlags) \ {nArg, SQLITE_FUNC_BUILTIN|\ @@ -17586,6 +18166,7 @@ struct CollSeq { #define SQLITE_AFF_NUMERIC 0x43 /* 'C' */ #define SQLITE_AFF_INTEGER 0x44 /* 'D' */ #define SQLITE_AFF_REAL 0x45 /* 'E' */ +#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */ #define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) @@ -17656,6 +18237,7 @@ struct VTable { sqlite3_vtab *pVtab; /* Pointer to vtab instance */ int nRef; /* Number of pointers to this structure */ u8 bConstraint; /* True if constraints are supported */ + u8 bAllSchemas; /* True if might use any attached schema */ u8 eVtabRisk; /* Riskiness of allowing hacker access */ int iSavepoint; /* Depth of the SAVEPOINT stack */ VTable *pNext; /* Next in linked list (see above) */ @@ -17792,6 +18374,15 @@ struct Table { #define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0) #define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0) +/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is +** available. By default, this macro is false +*/ +#ifndef SQLITE_ALLOW_ROWID_IN_VIEW +# define ViewCanHaveRowid 0 +#else +# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0) +#endif + /* ** Each foreign key constraint is an instance of the following structure. ** @@ -17863,7 +18454,7 @@ struct FKey { ** foreign key. ** ** The OE_Default value is a place holder that means to use whatever -** conflict resolution algorthm is required from context. +** conflict resolution algorithm is required from context. ** ** The following symbolic values are used to record which type ** of conflict resolution action to take. @@ -18036,6 +18627,7 @@ struct Index { ** expression, or a reference to a VIRTUAL column */ #ifdef SQLITE_ENABLE_STAT4 int nSample; /* Number of elements in aSample[] */ + int mxSample; /* Number of slots allocated to aSample[] */ int nSampleCol; /* Size of IndexSample.anEq[] and so on */ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */ IndexSample *aSample; /* Samples of the left-most key */ @@ -18117,16 +18709,15 @@ struct AggInfo { ** from source tables rather than from accumulators */ u8 useSortingIdx; /* In direct mode, reference the sorting index rather ** than the source table */ + u16 nSortingColumn; /* Number of columns in the sorting index */ int sortingIdx; /* Cursor number of the sorting index */ int sortingIdxPTab; /* Cursor number of pseudo-table */ - int nSortingColumn; /* Number of columns in the sorting index */ - int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */ + int iFirstReg; /* First register in range for aCol[] and aFunc[] */ ExprList *pGroupBy; /* The group by clause */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ Expr *pCExpr; /* The original expression */ int iTable; /* Cursor number of the source table */ - int iMem; /* Memory location that acts as accumulator */ i16 iColumn; /* Column number within the source table */ i16 iSorterColumn; /* Column number in the sorting index */ } *aCol; @@ -18137,14 +18728,30 @@ struct AggInfo { struct AggInfo_func { /* For each aggregate function */ Expr *pFExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ - int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ int iDistAddr; /* Address of OP_OpenEphemeral */ + int iOBTab; /* Ephemeral table to implement ORDER BY */ + u8 bOBPayload; /* iOBTab has payload columns separate from key */ + u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ u32 selId; /* Select to which this AggInfo belongs */ +#ifdef SQLITE_DEBUG + Select *pSelect; /* SELECT statement that this AggInfo supports */ +#endif }; +/* +** Macros to compute aCol[] and aFunc[] register numbers. +** +** These macros should not be used prior to the call to +** assignAggregateRegisters() that computes the value of pAggInfo->iFirstReg. +** The assert()s that are part of this macro verify that constraint. +*/ +#define AggInfoColumnReg(A,I) (assert((A)->iFirstReg),(A)->iFirstReg+(I)) +#define AggInfoFuncReg(A,I) \ + (assert((A)->iFirstReg),(A)->iFirstReg+(A)->nColumn+(I)) + /* ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater @@ -18264,7 +18871,7 @@ struct Expr { ** TK_REGISTER: register number ** TK_TRIGGER: 1 -> new, 0 -> old ** EP_Unlikely: 134217728 times likelihood - ** TK_IN: ephemerial table holding RHS + ** TK_IN: ephemeral 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. @@ -18310,7 +18917,7 @@ struct Expr { #define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_Win 0x008000 /* Contains window functions */ #define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ - /* 0x020000 // Available for reuse */ +#define EP_FullSize 0x020000 /* Expr structure must remain full sized */ #define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */ #define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */ #define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ @@ -18340,12 +18947,15 @@ struct Expr { #define ExprClearProperty(E,P) (E)->flags&=~(P) #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) +#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) /* Macros used to ensure that the correct members of unions are accessed ** in Expr. */ #define ExprUseUToken(E) (((E)->flags&EP_IntValue)==0) #define ExprUseUValue(E) (((E)->flags&EP_IntValue)!=0) +#define ExprUseWOfst(E) (((E)->flags&(EP_InnerON|EP_OuterON))==0) +#define ExprUseWJoin(E) (((E)->flags&(EP_InnerON|EP_OuterON))!=0) #define ExprUseXList(E) (((E)->flags&EP_xIsSelect)==0) #define ExprUseXSelect(E) (((E)->flags&EP_xIsSelect)!=0) #define ExprUseYTab(E) (((E)->flags&(EP_WinFunc|EP_Subrtn))==0) @@ -18455,6 +19065,7 @@ struct ExprList { #define ENAME_NAME 0 /* The AS clause of a result set */ #define ENAME_SPAN 1 /* Complete text of the result set expression */ #define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */ +#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */ /* ** An instance of this structure can hold a simple list of identifiers, @@ -18534,7 +19145,7 @@ struct SrcItem { unsigned notCte :1; /* This item may not match a CTE */ unsigned isUsing :1; /* u3.pUsing is valid */ unsigned isOn :1; /* u3.pOn was once valid and non-NULL */ - unsigned isSynthUsing :1; /* u3.pUsing is synthensized from NATURAL */ + unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */ unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */ } fg; int iCursor; /* The VDBE cursor number used to access this table */ @@ -18675,7 +19286,7 @@ struct NameContext { #define NC_HasAgg 0x000010 /* One or more aggregate functions seen */ #define NC_IdxExpr 0x000020 /* True if resolving columns of CREATE INDEX */ #define NC_SelfRef 0x00002e /* Combo: PartIdx, isCheck, GenCol, and IdxExpr */ -#define NC_VarSelect 0x000040 /* A correlated subquery has been seen */ +#define NC_Subquery 0x000040 /* A subquery has been seen */ #define NC_UEList 0x000080 /* True if uNC.pEList is used */ #define NC_UAggInfo 0x000100 /* True if uNC.pAggInfo is used */ #define NC_UUpsert 0x000200 /* True if uNC.pUpsert is used */ @@ -18711,6 +19322,7 @@ struct Upsert { Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */ Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */ u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */ + u8 isDup; /* True if 2nd or later with same pUpsertIdx */ /* Above this point is the parse tree for the ON CONFLICT clauses. ** The next group of fields stores intermediate data. */ void *pToFree; /* Free memory when deleting the Upsert object */ @@ -18804,6 +19416,7 @@ struct Select { #define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */ #define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */ #define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */ +#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */ /* True if S exists and has SF_NestedFrom */ #define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0) @@ -18912,7 +19525,7 @@ struct SelectDest { int iSDParm2; /* A second parameter for the eDest disposal method */ int iSdst; /* Base register where results are written */ int nSdst; /* Number of registers allocated */ - char *zAffSdst; /* Affinity used for SRT_Set, SRT_Table, and similar */ + char *zAffSdst; /* Affinity used for SRT_Set */ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */ }; @@ -18971,10 +19584,10 @@ struct TriggerPrg { #else typedef unsigned int yDbMask; # define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0) -# define DbMaskZero(M) (M)=0 -# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I)) -# define DbMaskAllZero(M) (M)==0 -# define DbMaskNonZero(M) (M)!=0 +# define DbMaskZero(M) ((M)=0) +# define DbMaskSet(M,I) ((M)|=(((yDbMask)1)<<(I))) +# define DbMaskAllZero(M) ((M)==0) +# define DbMaskNonZero(M) ((M)!=0) #endif /* @@ -18993,6 +19606,7 @@ struct IndexedExpr { int iIdxCur; /* The index cursor */ int iIdxCol; /* The index column that contains value of pExpr */ u8 bMaybeNullRow; /* True if we need an OP_IfNullRow check */ + u8 aff; /* Affinity of the pExpr expression */ IndexedExpr *pIENext; /* Next in a list of all indexed expressions */ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS const char *zIdxName; /* Name of index, used only for bytecode comments */ @@ -19044,6 +19658,9 @@ struct Parse { u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */ +#endif +#ifdef SQLITE_DEBUG + u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */ #endif int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ @@ -19057,7 +19674,8 @@ struct Parse { int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ - IndexedExpr *pIdxExpr;/* List of expressions used by active indexes */ + IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */ + IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */ Token constraintName;/* Name of the constraint currently being parsed */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ @@ -19065,6 +19683,9 @@ struct Parse { int regRoot; /* Register holding root page number for new objects */ int nMaxArg; /* Max args passed to user function by sub-program */ int nSelect; /* Number of SELECT stmts. Counter for Select.selId */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */ +#endif #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ @@ -19078,9 +19699,9 @@ struct Parse { int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */ Returning *pReturning; /* The RETURNING clause */ } u1; - u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u32 oldmask; /* Mask of old.* columns referenced */ u32 newmask; /* Mask of new.* columns referenced */ + LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 bReturning; /* Coding a RETURNING trigger */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ @@ -19204,6 +19825,7 @@ struct AuthContext { #define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ +#define OPFLAG_BYTELENARG 0xc0 /* OP_Column only for octet_length() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */ #define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */ @@ -19325,6 +19947,7 @@ struct Returning { int iRetCur; /* Transient table holding RETURNING results */ int nRetCol; /* Number of in pReturnEL after expansion */ int iRetReg; /* Register array for holding a row of RETURNING */ + char zName[40]; /* Name of trigger: "sqlite_returning_%p" */ }; /* @@ -19346,6 +19969,25 @@ struct sqlite3_str { #define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0) +/* +** The following object is the header for an "RCStr" or "reference-counted +** string". An RCStr is passed around and used like any other char* +** that has been dynamically allocated. The important interface +** differences: +** +** 1. RCStr strings are reference counted. They are deallocated +** when the reference count reaches zero. +** +** 2. Use sqlite3RCStrUnref() to free an RCStr string rather than +** sqlite3_free() +** +** 3. Make a (read-only) copy of a read-only RCStr string using +** sqlite3RCStrRef(). +*/ +struct RCStr { + u64 nRCRef; /* Number of references */ + /* Total structure size should be a multiple of 8 bytes for alignment */ +}; /* ** A pointer to this structure is used to communicate information @@ -19372,7 +20014,7 @@ typedef struct { /* Tuning parameters are set using SQLITE_TESTCTRL_TUNE and are controlled ** on debug-builds of the CLI using ".testctrl tune ID VALUE". Tuning ** parameters are for temporary use during development, to help find -** optimial values for parameters in the query planner. The should not +** optimal values for parameters in the query planner. The should not ** be used on trunk check-ins. They are a temporary mechanism available ** for transient development builds only. ** @@ -19398,6 +20040,7 @@ struct Sqlite3Config { 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 */ + u8 bUseLongDouble; /* Make use of long double */ int mxStrlen; /* Maximum string length */ int neverCorrupt; /* Database is always well-formed */ int szLookaside; /* Default lookaside buffer size */ @@ -19444,6 +20087,11 @@ struct Sqlite3Config { #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ +#endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW + ** feature is disabled. 0 if rowids can + ** occur in views. */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */ @@ -19484,6 +20132,7 @@ struct Walker { void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ int walkerDepth; /* Number of subqueries */ u16 eCode; /* A small processing code */ + u16 mWFlags; /* Use-dependent flags */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int n; /* A counter */ @@ -19502,6 +20151,7 @@ struct Walker { struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */ SrcItem *pSrcItem; /* A single FROM clause item */ DbFixer *pFix; /* See sqlite3FixSelect() */ + Mem *aMem; /* See sqlite3BtreeCursorHint() */ } u; }; @@ -19522,6 +20172,7 @@ struct DbFixer { /* Forward declarations */ SQLITE_PRIVATE int sqlite3WalkExpr(Walker*, Expr*); +SQLITE_PRIVATE int sqlite3WalkExprNN(Walker*, Expr*); SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*); SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*); SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*); @@ -19602,6 +20253,16 @@ struct CteUse { }; +/* Client data associated with sqlite3_set_clientdata() and +** sqlite3_get_clientdata(). +*/ +struct DbClientData { + DbClientData *pNext; /* Next in a linked list */ + void *pData; /* The data */ + void (*xDestructor)(void*); /* Destructor. Might be NULL */ + char zName[1]; /* Name of this client data. MUST BE LAST */ +}; + #ifdef SQLITE_DEBUG /* ** An instance of the TreeView object is used for printing the content of @@ -19771,6 +20432,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08) # define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)]) # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) +# define sqlite3JsonId1(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x42) +# define sqlite3JsonId2(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x46) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) # define sqlite3Isspace(x) isspace((unsigned char)(x)) @@ -19780,6 +20443,8 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define sqlite3Isxdigit(x) isxdigit((unsigned char)(x)) # define sqlite3Tolower(x) tolower((unsigned char)(x)) # define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`') +# define sqlite3JsonId1(x) (sqlite3IsIdChar(x)&&(x)<'0') +# define sqlite3JsonId2(x) sqlite3IsIdChar(x) #endif SQLITE_PRIVATE int sqlite3IsIdChar(u8); @@ -19829,13 +20494,11 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void); #ifdef SQLITE_USE_ALLOCA # define sqlite3StackAllocRaw(D,N) alloca(N) # define sqlite3StackAllocRawNN(D,N) alloca(N) -# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N) # define sqlite3StackFree(D,P) # define sqlite3StackFreeNN(D,P) #else # define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N) # define sqlite3StackAllocRawNN(D,N) sqlite3DbMallocRawNN(D,N) -# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N) # define sqlite3StackFree(D,P) sqlite3DbFree(D,P) # define sqlite3StackFreeNN(D,P) sqlite3DbFreeNN(D,P) #endif @@ -19885,10 +20548,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*); # define EXP754 (((u64)0x7ff)<<52) # define MAN754 ((((u64)1)<<52)-1) # define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0) +# define IsOvfl(X) (((X)&EXP754)==EXP754) SQLITE_PRIVATE int sqlite3IsNaN(double); +SQLITE_PRIVATE int sqlite3IsOverflow(double); #else -# define IsNaN(X) 0 -# define sqlite3IsNaN(X) 0 +# define IsNaN(X) 0 +# define sqlite3IsNaN(X) 0 +# define sqlite3IsOVerflow(X) 0 #endif /* @@ -19901,6 +20567,20 @@ struct PrintfArguments { sqlite3_value **apArg; /* The argument values */ }; +/* +** An instance of this object receives the decoding of a floating point +** value into an approximate decimal representation. +*/ +struct FpDecode { + char sign; /* '+' or '-' */ + char isSpecial; /* 1: Infinity 2: NaN */ + int n; /* Significant digits in the decode */ + int iDP; /* Location of the decimal point */ + char *z; /* Start of significant digits */ + char zBuf[24]; /* Storage for significant digits */ +}; + +SQLITE_PRIVATE void sqlite3FpDecode(FpDecode*,double,int,int); SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) @@ -19960,6 +20640,7 @@ SQLITE_PRIVATE void sqlite3ShowWinFunc(const Window*); #endif SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); +SQLITE_PRIVATE void sqlite3ProgressCheck(Parse*); SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int); SQLITE_PRIVATE void sqlite3Dequote(char*); @@ -19974,6 +20655,10 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int); SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int); SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int); SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*); +SQLITE_PRIVATE void sqlite3TouchRegister(Parse*,int); +#if defined(SQLITE_ENABLE_STAT4) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3FirstAvailableRegister(Parse*,int); +#endif #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int); #endif @@ -19985,6 +20670,8 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*); SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int); +SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*); +SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*); SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); @@ -20017,7 +20704,7 @@ SQLITE_PRIVATE const char *sqlite3ColumnColl(Column*); SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*); SQLITE_PRIVATE void sqlite3GenerateColumnNames(Parse *pParse, Select *pSelect); SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); -SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char); +SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); @@ -20124,7 +20811,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList Expr*,ExprList*,u32,Expr*); SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*); SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*); -SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int); +SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*); SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int); #if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*); @@ -20186,7 +20873,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int) SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*,Expr*,int); SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); SQLITE_PRIVATE int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); -SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int); +SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr*,int,int); SQLITE_PRIVATE void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); @@ -20213,7 +20900,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8); SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*); SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int); -SQLITE_PRIVATE int sqlite3ExprIsTableConstraint(Expr*,const SrcItem*); +SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int); #ifdef SQLITE_ENABLE_CURSOR_HINTS SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*); #endif @@ -20221,6 +20908,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); +SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab); SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); @@ -20335,9 +21023,10 @@ SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*); SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*); SQLITE_PRIVATE int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); + SQLITE_PRIVATE int sqlite3RealSameAsInt(double,sqlite3_int64); SQLITE_PRIVATE i64 sqlite3RealToI64(double); -SQLITE_PRIVATE void sqlite3Int64ToText(i64,char*); +SQLITE_PRIVATE int sqlite3Int64ToText(i64,char*); SQLITE_PRIVATE int sqlite3AtoF(const char *z, double*, int, u8); SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*); SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*); @@ -20388,6 +21077,7 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(const Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity); SQLITE_PRIVATE char sqlite3TableColumnAffinity(const Table*,int); SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr); +SQLITE_PRIVATE int sqlite3ExprDataType(const Expr *pExpr); SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8); SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*); SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...); @@ -20404,6 +21094,9 @@ SQLITE_PRIVATE const char *sqlite3ErrName(int); #ifndef SQLITE_OMIT_DESERIALIZE SQLITE_PRIVATE int sqlite3MemdbInit(void); +SQLITE_PRIVATE int sqlite3IsMemdb(const sqlite3_vfs*); +#else +# define sqlite3IsMemdb(X) 0 #endif SQLITE_PRIVATE const char *sqlite3ErrStr(int); @@ -20435,6 +21128,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*); SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8); SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8); +SQLITE_PRIVATE int sqlite3ValueIsOfClass(const sqlite3_value*, void(*)(void*)); SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8); SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); @@ -20486,7 +21180,8 @@ SQLITE_PRIVATE int sqlite3MatchEName( const struct ExprList_item*, const char*, const char*, - const char* + const char*, + int* ); SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*); SQLITE_PRIVATE u8 sqlite3StrIHash(const char*); @@ -20542,8 +21237,13 @@ SQLITE_PRIVATE void sqlite3OomClear(sqlite3*); SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int); SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *); +SQLITE_PRIVATE char *sqlite3RCStrRef(char*); +SQLITE_PRIVATE void sqlite3RCStrUnref(void*); +SQLITE_PRIVATE char *sqlite3RCStrNew(u64); +SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64); + SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int); -SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, int); +SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum*, i64); SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumSetError(StrAccum*, u8); SQLITE_PRIVATE void sqlite3ResultStrAccum(sqlite3_context*,StrAccum*); @@ -20657,10 +21357,7 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); -#if (defined(SQLITE_ENABLE_DBPAGE_VTAB) || defined(SQLITE_TEST)) \ - && !defined(SQLITE_OMIT_VIRTUALTABLE) -SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(sqlite3_index_info*); -#endif +SQLITE_PRIVATE void sqlite3VtabUsesAllSchemas(Parse*); SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); @@ -20697,7 +21394,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8); SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*); SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*); SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*); -SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*); +SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*); SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int); SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*); SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*); @@ -20796,6 +21493,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int); #define sqlite3SelectExprHeight(x) 0 #define sqlite3ExprCheckHeight(x,y) #endif +SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr*,int); SQLITE_PRIVATE u32 sqlite3Get4byte(const u8*); SQLITE_PRIVATE void sqlite3Put4byte(u8*, u32); @@ -20901,6 +21599,18 @@ SQLITE_PRIVATE const char **sqlite3CompileOptions(int *pnOpt); SQLITE_PRIVATE int sqlite3KvvfsInit(void); #endif +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +SQLITE_PRIVATE sqlite3_uint64 sqlite3Hwtime(void); +#endif + +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS +# define IS_STMT_SCANSTATUS(db) (db->flags & SQLITE_StmtScanStatus) +#else +# define IS_STMT_SCANSTATUS(db) 0 +#endif + #endif /* SQLITEINT_H */ /************** End of sqliteInt.h *******************************************/ @@ -20942,101 +21652,6 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void); */ #ifdef SQLITE_PERFORMANCE_TRACE -/* -** hwtime.h contains inline assembler code for implementing -** high-performance timing routines. -*/ -/************** Include hwtime.h in the middle of os_common.h ****************/ -/************** Begin file hwtime.h ******************************************/ -/* -** 2008 May 27 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This file contains inline asm code for retrieving "high-performance" -** counters for x86 and x86_64 class CPUs. -*/ -#ifndef SQLITE_HWTIME_H -#define SQLITE_HWTIME_H - -/* -** The following routine only works on pentium-class (or newer) processors. -** It uses the RDTSC opcode to read the cycle count value out of the -** processor and returns that value. This can be used for high-res -** profiling. -*/ -#if !defined(__STRICT_ANSI__) && \ - (defined(__GNUC__) || defined(_MSC_VER)) && \ - (defined(i386) || defined(__i386__) || defined(_M_IX86)) - - #if defined(__GNUC__) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned int lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return (sqlite_uint64)hi << 32 | lo; - } - - #elif defined(_MSC_VER) - - __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ - __asm { - rdtsc - ret ; return value at EDX:EAX - } - } - - #endif - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long val; - __asm__ __volatile__ ("rdtsc" : "=A" (val)); - return val; - } - -#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) - - __inline__ sqlite_uint64 sqlite3Hwtime(void){ - unsigned long long retval; - unsigned long junk; - __asm__ __volatile__ ("\n\ - 1: mftbu %1\n\ - mftb %L0\n\ - mftbu %0\n\ - cmpw %0,%1\n\ - bne 1b" - : "=r" (retval), "=r" (junk)); - return retval; - } - -#else - - /* - ** asm() is needed for hardware timing support. Without asm(), - ** disable the sqlite3Hwtime() routine. - ** - ** sqlite3Hwtime() is only used for some obscure debugging - ** and analysis configurations, not in any deliverable, so this - ** should not be a great loss. - */ -SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } - -#endif - -#endif /* !defined(SQLITE_HWTIME_H) */ - -/************** End of hwtime.h **********************************************/ -/************** Continuing where we left off in os_common.h ******************/ - static sqlite_uint64 g_start; static sqlite_uint64 g_elapsed; #define TIMER_START g_start=sqlite3Hwtime() @@ -21164,9 +21779,6 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC "4_BYTE_ALIGNED_MALLOC", #endif -#ifdef SQLITE_64BIT_STATS - "64BIT_STATS", -#endif #ifdef SQLITE_ALLOW_COVERING_INDEX_SCAN # if SQLITE_ALLOW_COVERING_INDEX_SCAN != 1 "ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN), @@ -21462,6 +22074,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS "EXPLAIN_ESTIMATED_ROWS", #endif +#ifdef SQLITE_EXTRA_AUTOEXT + "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT), +#endif #ifdef SQLITE_EXTRA_IFNULLROW "EXTRA_IFNULLROW", #endif @@ -21503,6 +22118,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_INTEGRITY_CHECK_ERROR_MAX "INTEGRITY_CHECK_ERROR_MAX=" CTIMEOPT_VAL(SQLITE_INTEGRITY_CHECK_ERROR_MAX), #endif +#ifdef SQLITE_LEGACY_JSON_VALID + "LEGACY_JSON_VALID", +#endif #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS "LIKE_DOESNT_MATCH_BLOBS", #endif @@ -21740,6 +22358,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS "OMIT_SCHEMA_VERSION_PRAGMAS", #endif +#ifdef SQLITE_OMIT_SEH + "OMIT_SEH", +#endif #ifdef SQLITE_OMIT_SHARED_CACHE "OMIT_SHARED_CACHE", #endif @@ -21991,7 +22612,7 @@ SQLITE_PRIVATE const unsigned char *sqlite3aGTb = &sqlite3UpperToLower[256+12-OP ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 -** SQLite identifier character 0x40 +** SQLite identifier character 0x40 $, _, or non-ascii ** Quote character 0x80 ** ** Bit 0x20 is set if the mapped character requires translation to upper @@ -22137,6 +22758,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */ 0, /* bSmallMalloc */ 1, /* bExtraSchemaChecks */ + sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */ 0x7ffffffe, /* mxStrlen */ 0, /* neverCorrupt */ SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */ @@ -22178,6 +22800,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ +#endif +#ifdef SQLITE_ALLOW_ROWID_IN_VIEW + 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */ #endif 0, /* bLocaltimeFault */ 0, /* xAltLocaltime */ @@ -22185,7 +22810,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ 0, /* iPrngSeed */ #ifdef SQLITE_DEBUG - {0,0,0,0,0,0} /* aTune */ + {0,0,0,0,0,0}, /* aTune */ #endif }; @@ -22366,6 +22991,9 @@ typedef struct VdbeSorter VdbeSorter; /* Elements of the linked list at Vdbe.pAuxData */ typedef struct AuxData AuxData; +/* A cache of large TEXT or BLOB values in a VdbeCursor */ +typedef struct VdbeTxtBlbCache VdbeTxtBlbCache; + /* Types of VDBE cursors */ #define CURTYPE_BTREE 0 #define CURTYPE_SORTER 1 @@ -22397,6 +23025,7 @@ struct VdbeCursor { Bool useRandomRowid:1; /* Generate new record numbers semi-randomly */ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */ Bool noReuse:1; /* OpenEphemeral may not reuse this cursor */ + Bool colCache:1; /* pCache pointer is initialized and non-NULL */ u16 seekHit; /* See the OP_SeekHit and OP_IfNoHope opcodes */ union { /* pBtx for isEphermeral. pAltMap otherwise */ Btree *pBtx; /* Separate file holding temporary table */ @@ -22437,6 +23066,7 @@ struct VdbeCursor { #ifdef SQLITE_ENABLE_COLUMN_USED_MASK u64 maskUsed; /* Mask of columns used by this cursor */ #endif + VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ /* 2*nField extra array elements allocated for aType[], beyond the one ** static element declared in the structure. nField total array slots for @@ -22449,12 +23079,25 @@ struct VdbeCursor { #define IsNullCursor(P) \ ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) - /* ** A value for VdbeCursor.cacheStatus that means the cache is always invalid. */ #define CACHE_STALE 0 +/* +** Large TEXT or BLOB values can be slow to load, so we want to avoid +** loading them more than once. For that reason, large TEXT and BLOB values +** can be stored in a cache defined by this object, and attached to the +** VdbeCursor using the pCache field. +*/ +struct VdbeTxtBlbCache { + char *pCValue; /* A RCStr buffer to hold the value */ + i64 iOffset; /* File offset of the row being cached */ + int iCol; /* Column for which the cache is valid */ + u32 cacheStatus; /* Vdbe.cacheCtr value */ + u32 colCacheCtr; /* Column cache counter */ +}; + /* ** When a sub-program is executed (OP_Program), a structure of this type ** is allocated to store the current value of the program counter, as @@ -22481,7 +23124,6 @@ struct VdbeFrame { Vdbe *v; /* VM this frame belongs to */ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */ Op *aOp; /* Program instructions for parent frame */ - i64 *anExec; /* Event counters from parent frame */ Mem *aMem; /* Array of memory cells for parent frame */ VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */ u8 *aOnce; /* Bitmask used by OP_Once */ @@ -22697,10 +23339,19 @@ typedef unsigned bft; /* Bit Field Type */ /* The ScanStatus object holds a single value for the ** sqlite3_stmt_scanstatus() interface. +** +** aAddrRange[]: +** This array is used by ScanStatus elements associated with EQP +** notes that make an SQLITE_SCANSTAT_NCYCLE value available. It is +** an array of up to 3 ranges of VM addresses for which the Vdbe.anCycle[] +** values should be summed to calculate the NCYCLE value. Each pair of +** integer addresses is a start and end address (both inclusive) for a range +** instructions. A start value of 0 indicates an empty range. */ typedef struct ScanStatus ScanStatus; struct ScanStatus { int addrExplain; /* OP_Explain for loop */ + int aAddrRange[6]; int addrLoop; /* Address of "loops" counter */ int addrVisit; /* Address of "rows visited" counter */ int iSelectID; /* The "Select-ID" for this loop */ @@ -22756,7 +23407,7 @@ struct Vdbe { int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Slots allocated for aOp[] */ Mem *aColName; /* Column names to return */ - Mem *pResultSet; /* Pointer to an array of results */ + Mem *pResultRow; /* Current output row */ char *zErrMsg; /* Error message written here */ VList *pVList; /* Name of variables */ #ifndef SQLITE_OMIT_TRACE @@ -22767,16 +23418,18 @@ struct Vdbe { u32 nWrite; /* Number of write operations that have occurred */ #endif u16 nResColumn; /* Number of columns in one row of the result set */ + u16 nResAlloc; /* Column slots allocated to aColName[] */ u8 errorAction; /* Recovery action to do in case of an error */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 eVdbeState; /* On of the VDBE_*_STATE values */ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ - bft explain:2; /* True if EXPLAIN present on SQL command */ + bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */ bft changeCntOn:1; /* True to update the change-counter */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */ @@ -22793,7 +23446,6 @@ struct Vdbe { SubProgram *pProgram; /* Linked list of all sub-programs used by VM */ AuxData *pAuxData; /* Linked list of auxdata allocations */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - i64 *anExec; /* Number of times each op has been executed */ int nScan; /* Entries in aScan[] */ ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */ #endif @@ -22824,7 +23476,7 @@ struct PreUpdate { i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ - Table *pTab; /* Schema object being upated */ + Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ }; @@ -22914,6 +23566,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetZeroBlob(Mem*,int); SQLITE_PRIVATE int sqlite3VdbeMemIsRowSet(const Mem*); #endif SQLITE_PRIVATE int sqlite3VdbeMemSetRowSet(Mem*); +SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*); SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8); SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double); @@ -22960,6 +23613,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *); SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *); SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *); +SQLITE_PRIVATE void sqlite3VdbeValueListFree(void*); + #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*); SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe*); @@ -23475,6 +24130,7 @@ struct DateTime { char validTZ; /* True (1) if tz is valid */ char tzSet; /* Timezone was set explicitly */ char isError; /* An overflow has occurred */ + char useSubsec; /* Display subsecond precision */ }; @@ -23507,8 +24163,8 @@ struct DateTime { */ static int getDigits(const char *zDate, const char *zFormat, ...){ /* The aMx[] array translates the 3rd character of each format - ** spec into a max size: a b c d e f */ - static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 }; + ** spec into a max size: a b c d e f */ + static const u16 aMx[] = { 12, 14, 24, 31, 59, 14712 }; va_list ap; int cnt = 0; char nextC; @@ -23789,6 +24445,11 @@ static int parseDateOrTime( }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8)>0 ){ setRawDateNumber(p, r); return 0; + }else if( (sqlite3StrICmp(zDate,"subsec")==0 + || sqlite3StrICmp(zDate,"subsecond")==0) + && sqlite3NotPureFunc(context) ){ + p->useSubsec = 1; + return setDateTimeToCurrent(context, p); } return 1; } @@ -23844,17 +24505,14 @@ static void computeYMD(DateTime *p){ ** Compute the Hour, Minute, and Seconds from the julian day number. */ static void computeHMS(DateTime *p){ - int s; + int day_ms, day_min; /* milliseconds, minutes into the day */ if( p->validHMS ) return; computeJD(p); - s = (int)((p->iJD + 43200000) % 86400000); - p->s = s/1000.0; - s = (int)p->s; - p->s -= s; - p->h = s/3600; - s -= p->h*3600; - p->m = s/60; - p->s += s - p->m*60; + day_ms = (int)((p->iJD + 43200000) % 86400000); + p->s = (day_ms % 60000)/1000.0; + day_min = day_ms/60000; + p->m = day_min % 60; + p->h = day_min / 60; p->rawS = 0; p->validHMS = 1; } @@ -24033,6 +24691,25 @@ static const struct { { 4, "year", 14713.0, 31536000.0 }, }; +/* +** If the DateTime p is raw number, try to figure out if it is +** a julian day number of a unix timestamp. Set the p value +** appropriately. +*/ +static void autoAdjustDate(DateTime *p){ + if( !p->rawS || p->validJD ){ + p->rawS = 0; + }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ + && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ + ){ + double r = p->s*1000.0 + 210866760000000.0; + clearYMD_HMS_TZ(p); + p->iJD = (sqlite3_int64)(r + 0.5); + p->validJD = 1; + p->rawS = 0; + } +} + /* ** Process a modifier to a date-time stamp. The modifiers are ** as follows: @@ -24076,19 +24753,8 @@ static int parseModifier( */ if( sqlite3_stricmp(z, "auto")==0 ){ if( idx>1 ) return 1; /* IMP: R-33611-57934 */ - if( !p->rawS || p->validJD ){ - rc = 0; - p->rawS = 0; - }else if( p->s>=-21086676*(i64)10000 /* -4713-11-24 12:00:00 */ - && p->s<=(25340230*(i64)10000)+799 /* 9999-12-31 23:59:59 */ - ){ - r = p->s*1000.0 + 210866760000000.0; - clearYMD_HMS_TZ(p); - p->iJD = (sqlite3_int64)(r + 0.5); - p->validJD = 1; - p->rawS = 0; - rc = 0; - } + autoAdjustDate(p); + rc = 0; } break; } @@ -24147,7 +24813,7 @@ static int parseModifier( i64 iOrigJD; /* Original localtime */ i64 iGuess; /* Guess at the corresponding utc time */ int cnt = 0; /* Safety to prevent infinite loop */ - int iErr; /* Guess is off by this much */ + i64 iErr; /* Guess is off by this much */ computeJD(p); iGuess = iOrigJD = p->iJD; @@ -24203,8 +24869,22 @@ static int parseModifier( ** ** Move the date backwards to the beginning of the current day, ** or month or year. + ** + ** subsecond + ** subsec + ** + ** Show subsecond precision in the output of datetime() and + ** unixepoch() and strftime('%s'). */ - if( sqlite3_strnicmp(z, "start of ", 9)!=0 ) break; + if( sqlite3_strnicmp(z, "start of ", 9)!=0 ){ + if( sqlite3_stricmp(z, "subsec")==0 + || sqlite3_stricmp(z, "subsecond")==0 + ){ + p->useSubsec = 1; + rc = 0; + } + break; + } if( !p->validJD && !p->validYMD && !p->validHMS ) break; z += 9; computeYMD(p); @@ -24240,18 +24920,73 @@ static int parseModifier( case '9': { double rRounder; int i; - for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} + int Y,M,D,h,m,x; + const char *z2 = z; + char z0 = z[0]; + for(n=1; z[n]; n++){ + if( z[n]==':' ) break; + if( sqlite3Isspace(z[n]) ) break; + if( z[n]=='-' ){ + if( n==5 && getDigits(&z[1], "40f", &Y)==1 ) break; + if( n==6 && getDigits(&z[1], "50f", &Y)==1 ) break; + } + } if( sqlite3AtoF(z, &r, n, SQLITE_UTF8)<=0 ){ - rc = 1; + assert( rc==1 ); break; } - if( z[n]==':' ){ + if( z[n]=='-' ){ + /* A modifier of the form (+|-)YYYY-MM-DD adds or subtracts the + ** specified number of years, months, and days. MM is limited to + ** the range 0-11 and DD is limited to 0-30. + */ + if( z0!='+' && z0!='-' ) break; /* Must start with +/- */ + if( n==5 ){ + if( getDigits(&z[1], "40f-20a-20d", &Y, &M, &D)!=3 ) break; + }else{ + assert( n==6 ); + if( getDigits(&z[1], "50f-20a-20d", &Y, &M, &D)!=3 ) break; + z++; + } + if( M>=12 ) break; /* M range 0..11 */ + if( D>=31 ) break; /* D range 0..30 */ + computeYMD_HMS(p); + p->validJD = 0; + if( z0=='-' ){ + p->Y -= Y; + p->M -= M; + D = -D; + }else{ + p->Y += Y; + p->M += M; + } + x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12; + p->Y += x; + p->M -= x*12; + computeJD(p); + p->validHMS = 0; + p->validYMD = 0; + p->iJD += (i64)D*86400000; + if( z[11]==0 ){ + rc = 0; + break; + } + if( sqlite3Isspace(z[11]) + && getDigits(&z[12], "20c:20e", &h, &m)==2 + ){ + z2 = &z[12]; + n = 2; + }else{ + break; + } + } + if( z2[n]==':' ){ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the ** specified number of hours, minutes, seconds, and fractional seconds ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be ** omitted. */ - const char *z2 = z; + DateTime tx; sqlite3_int64 day; if( !sqlite3Isdigit(*z2) ) z2++; @@ -24261,7 +24996,7 @@ static int parseModifier( tx.iJD -= 43200000; day = tx.iJD/86400000; tx.iJD -= day*86400000; - if( z[0]=='-' ) tx.iJD = -tx.iJD; + if( z0=='-' ) tx.iJD = -tx.iJD; computeJD(p); clearYMD_HMS_TZ(p); p->iJD += tx.iJD; @@ -24277,7 +25012,7 @@ static int parseModifier( if( n>10 || n<3 ) break; if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--; computeJD(p); - rc = 1; + assert( rc==1 ); rRounder = r<0 ? -0.5 : +0.5; for(i=0; i M += (int)r; @@ -24402,7 +25136,11 @@ static void unixepochFunc( DateTime x; if( isDate(context, argc, argv, &x)==0 ){ computeJD(&x); - sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + if( x.useSubsec ){ + sqlite3_result_double(context, (x.iJD - 21086676*(i64)10000000)/1000.0); + }else{ + sqlite3_result_int64(context, x.iJD/1000 - 21086676*(i64)10000); + } } } @@ -24418,8 +25156,8 @@ static void datetimeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - int Y, s; - char zBuf[24]; + int Y, s, n; + char zBuf[32]; computeYMD_HMS(&x); Y = x.Y; if( Y<0 ) Y = -Y; @@ -24440,15 +25178,28 @@ static void datetimeFunc( zBuf[15] = '0' + (x.m/10)%10; zBuf[16] = '0' + (x.m)%10; zBuf[17] = ':'; - s = (int)x.s; - zBuf[18] = '0' + (s/10)%10; - zBuf[19] = '0' + (s)%10; - zBuf[20] = 0; + if( x.useSubsec ){ + s = (int)(1000.0*x.s + 0.5); + zBuf[18] = '0' + (s/10000)%10; + zBuf[19] = '0' + (s/1000)%10; + zBuf[20] = '.'; + zBuf[21] = '0' + (s/100)%10; + zBuf[22] = '0' + (s/10)%10; + zBuf[23] = '0' + (s)%10; + zBuf[24] = 0; + n = 24; + }else{ + s = (int)x.s; + zBuf[18] = '0' + (s/10)%10; + zBuf[19] = '0' + (s)%10; + zBuf[20] = 0; + n = 20; + } if( x.Y<0 ){ zBuf[0] = '-'; - sqlite3_result_text(context, zBuf, 20, SQLITE_TRANSIENT); + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); }else{ - sqlite3_result_text(context, &zBuf[1], 19, SQLITE_TRANSIENT); + sqlite3_result_text(context, &zBuf[1], n-1, SQLITE_TRANSIENT); } } } @@ -24465,7 +25216,7 @@ static void timeFunc( ){ DateTime x; if( isDate(context, argc, argv, &x)==0 ){ - int s; + int s, n; char zBuf[16]; computeHMS(&x); zBuf[0] = '0' + (x.h/10)%10; @@ -24474,11 +25225,24 @@ static void timeFunc( zBuf[3] = '0' + (x.m/10)%10; zBuf[4] = '0' + (x.m)%10; zBuf[5] = ':'; - s = (int)x.s; - zBuf[6] = '0' + (s/10)%10; - zBuf[7] = '0' + (s)%10; - zBuf[8] = 0; - sqlite3_result_text(context, zBuf, 8, SQLITE_TRANSIENT); + if( x.useSubsec ){ + s = (int)(1000.0*x.s + 0.5); + zBuf[6] = '0' + (s/10000)%10; + zBuf[7] = '0' + (s/1000)%10; + zBuf[8] = '.'; + zBuf[9] = '0' + (s/100)%10; + zBuf[10] = '0' + (s/10)%10; + zBuf[11] = '0' + (s)%10; + zBuf[12] = 0; + n = 12; + }else{ + s = (int)x.s; + zBuf[6] = '0' + (s/10)%10; + zBuf[7] = '0' + (s)%10; + zBuf[8] = 0; + n = 8; + } + sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT); } } @@ -24533,7 +25297,7 @@ static void dateFunc( ** %M minute 00-59 ** %s seconds since 1970-01-01 ** %S seconds 00-59 -** %w day of week 0-6 sunday==0 +** %w day of week 0-6 Sunday==0 ** %W week of year 00-53 ** %Y year 0000-9999 ** %% % @@ -24559,13 +25323,16 @@ static void strftimeFunc( computeJD(&x); computeYMD_HMS(&x); for(i=j=0; zFmt[i]; i++){ + char cf; if( zFmt[i]!='%' ) continue; if( j12 ) h -= 12; + if( h==0 ) h = 12; + sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } case 'W': /* Fall thru */ @@ -24587,7 +25367,7 @@ static void strftimeFunc( y.D = 1; computeJD(&y); nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ + if( cf=='W' ){ int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ wd = (int)(((x.iJD+43200000)/86400000)%7); sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); @@ -24608,18 +25388,42 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d",x.m); break; } + case 'p': /* Fall thru */ + case 'P': { + if( x.h>=12 ){ + sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2); + }else{ + sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2); + } + break; + } + case 'R': { + sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m); + break; + } case 's': { - i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); - sqlite3_str_appendf(&sRes,"%lld",iS); + if( x.useSubsec ){ + sqlite3_str_appendf(&sRes,"%.3f", + (x.iJD - 21086676*(i64)10000000)/1000.0); + }else{ + i64 iS = (i64)(x.iJD/1000 - 21086676*(i64)10000); + sqlite3_str_appendf(&sRes,"%lld",iS); + } break; } case 'S': { sqlite3_str_appendf(&sRes,"%02d",(int)x.s); break; } + case 'T': { + sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); + break; + } + case 'u': /* Fall thru */ case 'w': { - sqlite3_str_appendchar(&sRes, 1, - (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; + if( c=='0' && cf=='u' ) c = '7'; + sqlite3_str_appendchar(&sRes, 1, c); break; } case 'Y': { @@ -24668,6 +25472,117 @@ static void cdateFunc( dateFunc(context, 0, 0); } +/* +** timediff(DATE1, DATE2) +** +** Return the amount of time that must be added to DATE2 in order to +** convert it into DATE2. The time difference format is: +** +** +YYYY-MM-DD HH:MM:SS.SSS +** +** The initial "+" becomes "-" if DATE1 occurs before DATE2. For +** date/time values A and B, the following invariant should hold: +** +** datetime(A) == (datetime(B, timediff(A,B)) +** +** Both DATE arguments must be either a julian day number, or an +** ISO-8601 string. The unix timestamps are not supported by this +** routine. +*/ +static void timediffFunc( + sqlite3_context *context, + int NotUsed1, + sqlite3_value **argv +){ + char sign; + int Y, M; + DateTime d1, d2; + sqlite3_str sRes; + UNUSED_PARAMETER(NotUsed1); + if( isDate(context, 1, &argv[0], &d1) ) return; + if( isDate(context, 1, &argv[1], &d2) ) return; + computeYMD_HMS(&d1); + computeYMD_HMS(&d2); + if( d1.iJD>=d2.iJD ){ + sign = '+'; + Y = d1.Y - d2.Y; + if( Y ){ + d2.Y = d1.Y; + d2.validJD = 0; + computeJD(&d2); + } + M = d1.M - d2.M; + if( M<0 ){ + Y--; + M += 12; + } + if( M!=0 ){ + d2.M = d1.M; + d2.validJD = 0; + computeJD(&d2); + } + while( d1.iJD d2.iJD ){ + M--; + if( M<0 ){ + M = 11; + Y--; + } + d2.M++; + if( d2.M>12 ){ + d2.M = 1; + d2.Y++; + } + d2.validJD = 0; + computeJD(&d2); + } + d1.iJD = d2.iJD - d1.iJD; + d1.iJD += (u64)1486995408 * (u64)100000; + } + d1.validYMD = 0; + d1.validHMS = 0; + d1.validTZ = 0; + computeYMD_HMS(&d1); + sqlite3StrAccumInit(&sRes, 0, 0, 0, 100); + sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f", + sign, Y, M, d1.D-1, d1.h, d1.m, d1.s); + sqlite3ResultStrAccum(context, &sRes); +} + + /* ** current_timestamp() ** @@ -24742,6 +25657,7 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){ PURE_DATE(time, -1, 0, 0, timeFunc ), PURE_DATE(datetime, -1, 0, 0, datetimeFunc ), PURE_DATE(strftime, -1, 0, 0, strftimeFunc ), + PURE_DATE(timediff, 2, 0, 0, timediffFunc ), DFUNCTION(current_time, 0, 0, 0, ctimeFunc ), DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc), DFUNCTION(current_date, 0, 0, 0, cdateFunc ), @@ -24895,7 +25811,7 @@ SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite ** is using a regular VFS, it is called after the corresponding ** transaction has been committed. Injecting a fault at this point - ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM + ** confuses the test scripts - the COMMIT command returns SQLITE_NOMEM ** but the transaction is committed anyway. ** ** The core must call OsFileControl() though, not OsFileControlHint(), @@ -25516,7 +26432,7 @@ static void *sqlite3MemMalloc(int nByte){ ** or sqlite3MemRealloc(). ** ** For this low-level routine, we already know that pPrior!=0 since -** cases where pPrior==0 will have been intecepted and dealt with +** cases where pPrior==0 will have been intercepted and dealt with ** by higher-level routines. */ static void sqlite3MemFree(void *pPrior){ @@ -25604,7 +26520,7 @@ static int sqlite3MemInit(void *NotUsed){ return SQLITE_OK; } len = sizeof(cpuCount); - /* One usually wants to use hw.acctivecpu for MT decisions, but not here */ + /* One usually wants to use hw.activecpu for MT decisions, but not here */ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0); if( cpuCount>1 ){ /* defer MT decisions to system malloc */ @@ -27596,7 +28512,7 @@ static void checkMutexFree(sqlite3_mutex *p){ assert( SQLITE_MUTEX_FAST<2 ); assert( SQLITE_MUTEX_WARNONCONTENTION<2 ); -#if SQLITE_ENABLE_API_ARMOR +#ifdef SQLITE_ENABLE_API_ARMOR if( ((CheckMutex*)p)->iType<2 ) #endif { @@ -28071,7 +28987,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ /* ** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields -** are necessary under two condidtions: (1) Debug builds and (2) using +** are necessary under two conditions: (1) Debug builds and (2) using ** home-grown mutexes. Encapsulate these conditions into a single #define. */ #if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) @@ -28268,7 +29184,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){ */ static void pthreadMutexFree(sqlite3_mutex *p){ assert( p->nRef==0 ); -#if SQLITE_ENABLE_API_ARMOR +#ifdef SQLITE_ENABLE_API_ARMOR if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) #endif { @@ -28572,7 +29488,7 @@ struct sqlite3_mutex { CRITICAL_SECTION mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ #ifdef SQLITE_DEBUG - volatile int nRef; /* Number of enterances */ + volatile int nRef; /* Number of entrances */ volatile DWORD owner; /* Thread holding this mutex */ volatile LONG trace; /* True to trace changes */ #endif @@ -28621,7 +29537,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ SQLITE_MEMORY_BARRIER; #elif defined(__GNUC__) __sync_synchronize(); -#elif MSVC_VERSION>=1300 +#elif MSVC_VERSION>=1400 _ReadWriteBarrier(); #elif defined(MemoryBarrier) MemoryBarrier(); @@ -29215,7 +30131,7 @@ static void mallocWithAlarm(int n, void **pp){ ** The upper bound is slightly less than 2GiB: 0x7ffffeff == 2,147,483,391 ** This provides a 256-byte safety margin for defense against 32-bit ** signed integer overflow bugs when computing memory allocation sizes. -** Parnoid applications might want to reduce the maximum allocation size +** Paranoid applications might want to reduce the maximum allocation size ** further for an even larger safety margin. 0x3fffffff or 0x0fffffff ** or even smaller would be reasonable upper bounds on the size of a memory ** allocations for most applications. @@ -29729,9 +30645,14 @@ SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){ */ SQLITE_PRIVATE char *sqlite3DbSpanDup(sqlite3 *db, const char *zStart, const char *zEnd){ int n; +#ifdef SQLITE_DEBUG + /* Because of the way the parser works, the span is guaranteed to contain + ** at least one non-space character */ + for(n=0; sqlite3Isspace(zStart[n]); n++){ assert( &zStart[n] 0) && sqlite3Isspace(zStart[n-1]) ) n--; + while( sqlite3Isspace(zStart[n-1]) ) n--; return sqlite3DbStrNDup(db, zStart, n); } @@ -29827,7 +30748,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ if( db->mallocFailed || rc ){ return apiHandleError(db, rc); } - return rc & db->errMask; + return 0; } /************** End of malloc.c **********************************************/ @@ -29939,43 +30860,6 @@ static const et_info fmtinfo[] = { ** %!S Like %S but prefer the zName over the zAlias */ -/* Floating point constants used for rounding */ -static const double arRound[] = { - 5.0e-01, 5.0e-02, 5.0e-03, 5.0e-04, 5.0e-05, - 5.0e-06, 5.0e-07, 5.0e-08, 5.0e-09, 5.0e-10, -}; - -/* -** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point -** conversions will work. -*/ -#ifndef SQLITE_OMIT_FLOATING_POINT -/* -** "*val" is a double such that 0.1 <= *val < 10.0 -** Return the ascii code for the leading digit of *val, then -** multiply "*val" by 10.0 to renormalize. -** -** Example: -** input: *val = 3.14159 -** output: *val = 1.4159 function return = '3' -** -** The counter *cnt is incremented each time. After counter exceeds -** 16 (the number of significant digits in a 64-bit float) '0' is -** always returned. -*/ -static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ - int digit; - LONGDOUBLE_TYPE d; - if( (*cnt)<=0 ) return '0'; - (*cnt)--; - digit = (int)*val; - d = digit; - digit += '0'; - *val = (*val - d)*10.0; - return (char)digit; -} -#endif /* SQLITE_OMIT_FLOATING_POINT */ - /* ** Set the StrAccum object to an error mode. */ @@ -30067,18 +30951,15 @@ SQLITE_API void sqlite3_str_vappendf( u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ sqlite_uint64 longvalue; /* Value for integer types */ - LONGDOUBLE_TYPE realvalue; /* Value for real types */ + double realvalue; /* Value for real types */ const et_info *infop; /* Pointer to the appropriate info structure */ char *zOut; /* Rendering buffer */ int nOut; /* Size of the rendering buffer */ char *zExtra = 0; /* Malloced memory used by some conversion */ -#ifndef SQLITE_OMIT_FLOATING_POINT - int exp, e2; /* exponent of real numbers */ - int nsd; /* Number of significant digits returned */ - double rounder; /* Used for rounding floating point values */ + int exp, e2; /* exponent of real numbers */ etByte flag_dp; /* True if decimal point should be shown */ etByte flag_rtz; /* True if trailing zeros should be removed */ -#endif + PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ char buf[etBUFSIZE]; /* Conversion buffer */ @@ -30353,73 +31234,66 @@ SQLITE_API void sqlite3_str_vappendf( break; case etFLOAT: case etEXP: - case etGENERIC: + case etGENERIC: { + FpDecode s; + int iRound; + int j; + if( bArgList ){ realvalue = getDoubleArg(pArgList); }else{ realvalue = va_arg(ap,double); } -#ifdef SQLITE_OMIT_FLOATING_POINT - length = 0; -#else if( precision<0 ) precision = 6; /* Set default precision */ #ifdef SQLITE_FP_PRECISION_LIMIT if( precision>SQLITE_FP_PRECISION_LIMIT ){ precision = SQLITE_FP_PRECISION_LIMIT; } #endif - if( realvalue<0.0 ){ - realvalue = -realvalue; - prefix = '-'; - }else{ - prefix = flag_prefix; - } - if( xtype==etGENERIC && precision>0 ) precision--; - testcase( precision>0xfff ); - idx = precision & 0xfff; - rounder = arRound[idx%10]; - while( idx>=10 ){ rounder *= 1.0e-10; idx -= 10; } if( xtype==etFLOAT ){ - double rx = (double)realvalue; - sqlite3_uint64 u; - int ex; - memcpy(&u, &rx, sizeof(u)); - ex = -1023 + (int)((u>>52)&0x7ff); - if( precision+(ex/3) < 15 ) rounder += realvalue*3e-16; - realvalue += rounder; - } - /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ - exp = 0; - if( sqlite3IsNaN((double)realvalue) ){ - bufpt = "NaN"; - length = 3; - break; + iRound = -precision; + }else if( xtype==etGENERIC ){ + iRound = precision; + }else{ + iRound = precision+1; } - if( realvalue>0.0 ){ - LONGDOUBLE_TYPE scale = 1.0; - while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} - while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } - while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } - realvalue /= scale; - while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } - while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } - if( exp>350 ){ + sqlite3FpDecode(&s, realvalue, iRound, flag_altform2 ? 26 : 16); + if( s.isSpecial ){ + if( s.isSpecial==2 ){ + bufpt = flag_zeropad ? "null" : "NaN"; + length = sqlite3Strlen30(bufpt); + break; + }else if( flag_zeropad ){ + s.z[0] = '9'; + s.iDP = 1000; + s.n = 1; + }else{ + memcpy(buf, "-Inf", 5); bufpt = buf; - buf[0] = prefix; - memcpy(buf+(prefix!=0),"Inf",4); - length = 3+(prefix!=0); + if( s.sign=='-' ){ + /* no-op */ + }else if( flag_prefix ){ + buf[0] = flag_prefix; + }else{ + bufpt++; + } + length = sqlite3Strlen30(bufpt); break; } } - bufpt = buf; + if( s.sign=='-' ){ + prefix = '-'; + }else{ + prefix = flag_prefix; + } + + exp = s.iDP-1; + if( xtype==etGENERIC && precision>0 ) precision--; + /* ** If the field type is etGENERIC, then convert to either etEXP ** or etFLOAT, as appropriate. */ - if( xtype!=etFLOAT ){ - realvalue += rounder; - if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } - } if( xtype==etGENERIC ){ flag_rtz = !flag_alternateform; if( exp<-4 || exp>precision ){ @@ -30434,29 +31308,32 @@ SQLITE_API void sqlite3_str_vappendf( if( xtype==etEXP ){ e2 = 0; }else{ - e2 = exp; + e2 = s.iDP - 1; } + bufpt = buf; { i64 szBufNeeded; /* Size of a temporary buffer needed */ szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; + if( cThousand && e2>0 ) szBufNeeded += (e2+2)/3; if( szBufNeeded > etBUFSIZE ){ bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); if( bufpt==0 ) return; } } zOut = bufpt; - nsd = 16 + flag_altform2*10; flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; /* The sign in front of the number */ if( prefix ){ *(bufpt++) = prefix; } /* Digits prior to the decimal point */ + j = 0; if( e2<0 ){ *(bufpt++) = '0'; }else{ for(; e2>=0; e2--){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); + *(bufpt++) = j 1 ) *(bufpt++) = ','; } } /* The decimal point */ @@ -30465,13 +31342,12 @@ SQLITE_API void sqlite3_str_vappendf( } /* "0" digits after the decimal point but before the first ** significant digit of the number */ - for(e2++; e2<0; precision--, e2++){ - assert( precision>0 ); + for(e2++; e2<0 && precision>0; precision--, e2++){ *(bufpt++) = '0'; } /* Significant digits after the decimal point */ while( (precision--)>0 ){ - *(bufpt++) = et_getdigit(&realvalue,&nsd); + *(bufpt++) = j charset]; if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; @@ -30520,8 +31397,8 @@ SQLITE_API void sqlite3_str_vappendf( while( nPad-- ) bufpt[i++] = '0'; length = width; } -#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ break; + } case etSIZE: if( !bArgList ){ *(va_arg(ap,int*)) = pAccum->nChar; @@ -30570,13 +31447,26 @@ SQLITE_API void sqlite3_str_vappendf( } } if( precision>1 ){ + i64 nPrior = 1; width -= precision-1; if( width>1 && !flag_leftjustify ){ sqlite3_str_appendchar(pAccum, width-1, ' '); width = 0; } - while( precision-- > 1 ){ - sqlite3_str_append(pAccum, buf, length); + sqlite3_str_append(pAccum, buf, length); + precision--; + while( precision > 1 ){ + i64 nCopyBytes; + if( nPrior > precision-1 ) nPrior = precision - 1; + nCopyBytes = length*nPrior; + if( nCopyBytes + pAccum->nChar >= pAccum->nAlloc ){ + sqlite3StrAccumEnlarge(pAccum, nCopyBytes); + } + if( pAccum->accError ) break; + sqlite3_str_append(pAccum, + &pAccum->zText[pAccum->nChar-nCopyBytes], nCopyBytes); + precision -= nPrior; + nPrior *= 2; } } bufpt = buf; @@ -30804,9 +31694,9 @@ SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExp ** Return the number of bytes of text that StrAccum is able to accept ** after the attempted enlargement. The value returned might be zero. */ -SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){ +SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, i64 N){ char *zNew; - assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ + assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */ if( p->accError ){ testcase(p->accError==SQLITE_TOOBIG); testcase(p->accError==SQLITE_NOMEM); @@ -30817,8 +31707,7 @@ SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){ return p->nAlloc - p->nChar - 1; }else{ char *zOld = isMalloced(p) ? p->zText : 0; - i64 szNew = p->nChar; - szNew += (sqlite3_int64)N + 1; + i64 szNew = p->nChar + N + 1; if( szNew+p->nChar<=p->mxAlloc ){ /* Force exponential buffer size growth as long as it does not overflow, ** to avoid having to call this routine too often */ @@ -30848,7 +31737,8 @@ SQLITE_PRIVATE int sqlite3StrAccumEnlarge(StrAccum *p, int N){ return 0; } } - return N; + assert( N>=0 && N<=0x7fffffff ); + return (int)N; } /* @@ -31139,12 +32029,22 @@ SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_li return zBuf; } SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ - char *z; + StrAccum acc; va_list ap; + if( n<=0 ) return zBuf; +#ifdef SQLITE_ENABLE_API_ARMOR + if( zBuf==0 || zFormat==0 ) { + (void)SQLITE_MISUSE_BKPT; + if( zBuf ) zBuf[0] = 0; + return zBuf; + } +#endif + sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); va_start(ap,zFormat); - z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); + sqlite3_str_vappendf(&acc, zFormat, ap); va_end(ap); - return z; + zBuf[acc.nChar] = 0; + return zBuf; } /* @@ -31222,6 +32122,75 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){ va_end(ap); } + +/***************************************************************************** +** Reference counted string storage +*****************************************************************************/ + +/* +** Increase the reference count of the string by one. +** +** The input parameter is returned. +*/ +SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + p->nRCRef++; + return z; +} + +/* +** Decrease the reference count by one. Free the string when the +** reference count reaches zero. +*/ +SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){ + RCStr *p = (RCStr*)z; + assert( p!=0 ); + p--; + assert( p->nRCRef>0 ); + if( p->nRCRef>=2 ){ + p->nRCRef--; + }else{ + sqlite3_free(p); + } +} + +/* +** Create a new string that is capable of holding N bytes of text, not counting +** the zero byte at the end. The string is uninitialized. +** +** The reference count is initially 1. Call sqlite3RCStrUnref() to free the +** newly allocated string. +** +** This routine returns 0 on an OOM. +*/ +SQLITE_PRIVATE char *sqlite3RCStrNew(u64 N){ + RCStr *p = sqlite3_malloc64( N + sizeof(*p) + 1 ); + if( p==0 ) return 0; + p->nRCRef = 1; + return (char*)&p[1]; +} + +/* +** Change the size of the string so that it is able to hold N bytes. +** The string might be reallocated, so return the new allocation. +*/ +SQLITE_PRIVATE char *sqlite3RCStrResize(char *z, u64 N){ + RCStr *p = (RCStr*)z; + RCStr *pNew; + assert( p!=0 ); + p--; + assert( p->nRCRef==1 ); + pNew = sqlite3_realloc64(p, N+sizeof(RCStr)+1); + if( pNew==0 ){ + sqlite3_free(p); + return 0; + }else{ + return (char*)&pNew[1]; + } +} + /************** End of printf.c **********************************************/ /************** Begin file treeview.c ****************************************/ /* @@ -31444,6 +32413,13 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){ sqlite3_str_appendf(&x, " ON"); } + if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc"); + if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated"); + if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized"); + if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine"); + if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte"); + if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom"); + sqlite3StrAccumFinish(&x); sqlite3TreeViewItem(pView, zLine, i nSrc-1); n = 0; @@ -31631,6 +32607,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u sqlite3TreeViewItem(pView, "FILTER", 1); sqlite3TreeViewExpr(pView, pWin->pFilter, 0); sqlite3TreeViewPop(&pView); + if( pWin->eFrmType==TK_FILTER ) return; } sqlite3TreeViewPush(&pView, more); if( pWin->zName ){ @@ -31640,7 +32617,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u } if( pWin->zBase ) nElement++; if( pWin->pOrderBy ) nElement++; - if( pWin->eFrmType ) nElement++; + if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++; if( pWin->eExclude ) nElement++; if( pWin->zBase ){ sqlite3TreeViewPush(&pView, (--nElement)>0); @@ -31653,7 +32630,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u if( pWin->pOrderBy ){ sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); } - if( pWin->eFrmType ){ + if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){ char zBuf[30]; const char *zFrmType = "ROWS"; if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; @@ -31713,7 +32690,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m sqlite3TreeViewPop(&pView); return; } - if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags ){ + if( pExpr->flags || pExpr->affExpr || pExpr->vvaFlags || pExpr->pAggInfo ){ StrAccum x; sqlite3StrAccumInit(&x, 0, zFlgs, sizeof(zFlgs), 0); sqlite3_str_appendf(&x, " fg.af=%x.%c", @@ -31730,6 +32707,9 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m if( ExprHasVVAProperty(pExpr, EP_Immutable) ){ sqlite3_str_appendf(&x, " IMMUTABLE"); } + if( pExpr->pAggInfo!=0 ){ + sqlite3_str_appendf(&x, " agg-column[%d]", pExpr->iAgg); + } sqlite3StrAccumFinish(&x); }else{ zFlgs[0] = 0; @@ -31859,7 +32839,8 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m }; assert( pExpr->op2==TK_IS || pExpr->op2==TK_ISNOT ); assert( pExpr->pRight ); - assert( sqlite3ExprSkipCollate(pExpr->pRight)->op==TK_TRUEFALSE ); + assert( sqlite3ExprSkipCollateAndLikely(pExpr->pRight)->op + == TK_TRUEFALSE ); x = (pExpr->op2==TK_ISNOT)*2 + sqlite3ExprTruthValue(pExpr->pRight); zUniOp = azOp[x]; break; @@ -31897,7 +32878,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m assert( ExprUseXList(pExpr) ); pFarg = pExpr->x.pList; #ifndef SQLITE_OMIT_WINDOWFUNC - pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0; + pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0; #else pWin = 0; #endif @@ -31923,7 +32904,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs); } if( pFarg ){ - sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0); + sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0); + if( pExpr->pLeft ){ + Expr *pOB = pExpr->pLeft; + assert( pOB->op==TK_ORDER ); + assert( ExprUseXList(pOB) ); + sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY"); + } } #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ @@ -31932,6 +32919,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m #endif break; } + case TK_ORDER: { + sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY"); + break; + } #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: { assert( ExprUseXSelect(pExpr) ); @@ -33518,7 +34509,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ /* ** Calls to sqlite3FaultSim() are used to simulate a failure during testing, ** or to bypass normal error detection during testing in order to let -** execute proceed futher downstream. +** execute proceed further downstream. ** ** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The ** sqlite3FaultSim() function only returns non-zero during testing. @@ -33562,6 +34553,19 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){ } #endif /* SQLITE_OMIT_FLOATING_POINT */ +#ifndef SQLITE_OMIT_FLOATING_POINT +/* +** Return true if the floating point value is NaN or +Inf or -Inf. +*/ +SQLITE_PRIVATE int sqlite3IsOverflow(double x){ + int rc; /* The value return */ + u64 y; + memcpy(&y,&x,sizeof(y)); + rc = IsOvfl(y); + return rc; +} +#endif /* SQLITE_OMIT_FLOATING_POINT */ + /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. @@ -33635,6 +34639,23 @@ SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){ */ SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){ if( rc==SQLITE_IOERR_NOMEM ) return; +#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL) + if( rc==SQLITE_IOERR_IN_PAGE ){ + int ii; + int iErr; + sqlite3BtreeEnterAll(db); + for(ii=0; ii nDb; ii++){ + if( db->aDb[ii].pBt ){ + iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt)); + if( iErr ){ + db->iSysErrno = iErr; + } + } + } + sqlite3BtreeLeaveAll(db); + return; + } +#endif rc &= 0xff; if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){ db->iSysErrno = sqlite3OsGetLastError(db->pVfs); @@ -33669,6 +34690,30 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *z } } +/* +** Check for interrupts and invoke progress callback. +*/ +SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){ + sqlite3 *db = p->db; + if( AtomicLoad(&db->u1.isInterrupted) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + if( db->xProgress ){ + if( p->rc==SQLITE_INTERRUPT ){ + p->nProgressSteps = 0; + }else if( (++p->nProgressSteps)>=db->nProgressOps ){ + if( db->xProgress(db->pProgressArg) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } + p->nProgressSteps = 0; + } + } +#endif +} + /* ** Add an error message to pParse->zErrMsg and increment pParse->nErr. ** @@ -33860,43 +34905,40 @@ SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){ return h; } -/* -** Compute 10 to the E-th power. Examples: E==1 results in 10. -** E==2 results in 100. E==50 results in 1.0e50. +/* Double-Double multiplication. (x[0],x[1]) *= (y,yy) ** -** This routine only works for values of E between 1 and 341. +** Reference: +** T. J. Dekker, "A Floating-Point Technique for Extending the +** Available Precision". 1971-07-26. */ -static LONGDOUBLE_TYPE sqlite3Pow10(int E){ -#if defined(_MSC_VER) - static const LONGDOUBLE_TYPE x[] = { - 1.0e+001L, - 1.0e+002L, - 1.0e+004L, - 1.0e+008L, - 1.0e+016L, - 1.0e+032L, - 1.0e+064L, - 1.0e+128L, - 1.0e+256L - }; - LONGDOUBLE_TYPE r = 1.0; - int i; - assert( E>=0 && E<=307 ); - for(i=0; E!=0; i++, E >>=1){ - if( E & 1 ) r *= x[i]; - } - return r; -#else - LONGDOUBLE_TYPE x = 10.0; - LONGDOUBLE_TYPE r = 1.0; - while(1){ - if( E & 1 ) r *= x; - E >>= 1; - if( E==0 ) break; - x *= x; - } - return r; -#endif +static void dekkerMul2(volatile double *x, double y, double yy){ + /* + ** The "volatile" keywords on parameter x[] and on local variables + ** below are needed force intermediate results to be truncated to + ** binary64 rather than be carried around in an extended-precision + ** format. The truncation is necessary for the Dekker algorithm to + ** work. Intel x86 floating point might omit the truncation without + ** the use of volatile. + */ + volatile double tx, ty, p, q, c, cc; + double hx, hy; + u64 m; + memcpy(&m, (void*)&x[0], 8); + m &= 0xfffffffffc000000LL; + memcpy(&hx, &m, 8); + tx = x[0] - hx; + memcpy(&m, &y, 8); + m &= 0xfffffffffc000000LL; + memcpy(&hy, &m, 8); + ty = y - hy; + p = hx*hy; + q = hx*ty + tx*hy; + c = p+q; + cc = p - c + q + tx*ty; + cc = x[0]*yy + x[1]*y + cc; + x[0] = c + cc; + x[1] = c - x[0]; + x[1] += cc; } /* @@ -33937,12 +34979,11 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en const char *zEnd; /* sign * significand * (10 ^ (esign * exponent)) */ int sign = 1; /* sign of significand */ - i64 s = 0; /* significand */ + u64 s = 0; /* significand */ int d = 0; /* adjust exponent for shifting decimal point */ int esign = 1; /* sign of exponent */ int e = 0; /* exponent */ int eValid = 1; /* True exponent is either not used or is well-formed */ - double result; int nDigit = 0; /* Number of digits processed */ int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ @@ -33982,7 +35023,7 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en while( z =((LARGEST_INT64-9)/10) ){ + if( s>=((LARGEST_UINT64-9)/10) ){ /* skip non-significant significand digits ** (increase exponent by d to shift decimal left) */ while( z 0 ){ /*OPTIMIZATION-IF-TRUE*/ - if( esign>0 ){ - if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/ - s *= 10; - }else{ - if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/ - s /= 10; - } - e--; - } + /* adjust exponent by d, and update sign */ + e = (e*esign) + d; - /* adjust the sign of significand */ - s = sign<0 ? -s : s; + /* Try to adjust the exponent to make it smaller */ + while( e>0 && s<(LARGEST_UINT64/10) ){ + s *= 10; + e--; + } + while( e<0 && (s%10)==0 ){ + s /= 10; + e++; + } - if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/ - result = (double)s; + if( e==0 ){ + *pResult = s; + }else if( sqlite3Config.bUseLongDouble ){ + LONGDOUBLE_TYPE r = (LONGDOUBLE_TYPE)s; + if( e>0 ){ + while( e>=100 ){ e-=100; r *= 1.0e+100L; } + while( e>=10 ){ e-=10; r *= 1.0e+10L; } + while( e>=1 ){ e-=1; r *= 1.0e+01L; } }else{ - /* attempt to handle extremely small/large numbers better */ - if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/ - if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/ - LONGDOUBLE_TYPE scale = sqlite3Pow10(e-308); - if( esign<0 ){ - result = s / scale; - result /= 1.0e+308; - }else{ - result = s * scale; - result *= 1.0e+308; - } - }else{ assert( e>=342 ); - if( esign<0 ){ - result = 0.0*s; - }else{ + while( e<=-100 ){ e+=100; r *= 1.0e-100L; } + while( e<=-10 ){ e+=10; r *= 1.0e-10L; } + while( e<=-1 ){ e+=1; r *= 1.0e-01L; } + } + assert( r>=0.0 ); + if( r>+1.7976931348623157081452742373e+308L ){ #ifdef INFINITY - result = INFINITY*s; + *pResult = +INFINITY; #else - result = 1e308*1e308*s; /* Infinity */ + *pResult = 1.0e308*10.0; #endif - } - } - }else{ - LONGDOUBLE_TYPE scale = sqlite3Pow10(e); - if( esign<0 ){ - result = s / scale; - }else{ - result = s * scale; - } + }else{ + *pResult = (double)r; + } + }else{ + double rr[2]; + u64 s2; + rr[0] = (double)s; + s2 = (u64)rr[0]; + rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s); + if( e>0 ){ + while( e>=100 ){ + e -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( e>=10 ){ + e -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( e>=1 ){ + e -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); + } + }else{ + while( e<=-100 ){ + e += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( e<=-10 ){ + e += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( e<=-1 ){ + e += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); } } + *pResult = rr[0]+rr[1]; + if( sqlite3IsNaN(*pResult) ) *pResult = 1e300*1e300; } + if( sign<0 ) *pResult = -*pResult; + assert( !sqlite3IsNaN(*pResult) ); - /* store the result */ - *pResult = result; - - /* return true if number and no extra non-whitespace chracters after */ +atof_return: + /* return true if number and no extra non-whitespace characters after */ if( z==zEnd && nDigit>0 && eValid && eType>0 ){ return eType; }else if( eType>=2 && (eType==3 || eValid) && nDigit>0 ){ @@ -34126,11 +35177,14 @@ do_atof_calc: #endif /* -** Render an signed 64-bit integer as text. Store the result in zOut[]. +** Render an signed 64-bit integer as text. Store the result in zOut[] and +** return the length of the string that was stored, in bytes. The value +** returned does not include the zero terminator at the end of the output +** string. ** ** The caller must ensure that zOut[] is at least 21 bytes in size. */ -SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ +SQLITE_PRIVATE int sqlite3Int64ToText(i64 v, char *zOut){ int i; u64 x; char zTemp[22]; @@ -34141,12 +35195,15 @@ SQLITE_PRIVATE void sqlite3Int64ToText(i64 v, char *zOut){ } i = sizeof(zTemp)-2; zTemp[sizeof(zTemp)-1] = 0; - do{ - zTemp[i--] = (x%10) + '0'; + while( 1 /*exit-by-break*/ ){ + zTemp[i] = (x%10) + '0'; x = x/10; - }while( x ); - if( v<0 ) zTemp[i--] = '-'; - memcpy(zOut, &zTemp[i+1], sizeof(zTemp)-1-i); + if( x==0 ) break; + i--; + }; + if( v<0 ) zTemp[--i] = '-'; + memcpy(zOut, &zTemp[i], sizeof(zTemp)-i); + return sizeof(zTemp)-1-i; } /* @@ -34239,7 +35296,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc /* This test and assignment is needed only to suppress UB warnings ** from clang and -fsanitize=undefined. This test and assignment make ** the code a little larger and slower, and no harm comes from omitting - ** them, but we must appaise the undefined-behavior pharisees. */ + ** them, but we must appease the undefined-behavior pharisees. */ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; }else if( neg ){ *pNum = -(i64)u; @@ -34311,11 +35368,15 @@ SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ u = u*16 + sqlite3HexToInt(z[k]); } memcpy(pOut, &u, 8); - return (z[k]==0 && k-i<=16) ? 0 : 2; + if( k-i>16 ) return 2; + if( z[k]!=0 ) return 1; + return 0; }else #endif /* SQLITE_OMIT_HEX_INTEGER */ { - return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); + int n = (int)(0x3fffffff&strspn(z,"+- \n\t0123456789")); + if( z[n] ) n++; + return sqlite3Atoi64(z, pOut, n, SQLITE_UTF8); } } @@ -34347,7 +35408,7 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ u32 u = 0; zNum += 2; while( zNum[0]=='0' ) zNum++; - for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ + for(i=0; i<8 && sqlite3Isxdigit(zNum[i]); i++){ u = u*16 + sqlite3HexToInt(zNum[i]); } if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ @@ -34394,6 +35455,153 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){ return x; } +/* +** Decode a floating-point value into an approximate decimal +** representation. +** +** Round the decimal representation to n significant digits if +** n is positive. Or round to -n signficant digits after the +** decimal point if n is negative. No rounding is performed if +** n is zero. +** +** The significant digits of the decimal representation are +** stored in p->z[] which is a often (but not always) a pointer +** into the middle of p->zBuf[]. There are p->n significant digits. +** The p->z[] array is *not* zero-terminated. +*/ +SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){ + int i; + u64 v; + int e, exp = 0; + p->isSpecial = 0; + p->z = p->zBuf; + + /* Convert negative numbers to positive. Deal with Infinity, 0.0, and + ** NaN. */ + if( r<0.0 ){ + p->sign = '-'; + r = -r; + }else if( r==0.0 ){ + p->sign = '+'; + p->n = 1; + p->iDP = 1; + p->z = "0"; + return; + }else{ + p->sign = '+'; + } + memcpy(&v,&r,8); + e = v>>52; + if( (e&0x7ff)==0x7ff ){ + p->isSpecial = 1 + (v!=0x7ff0000000000000LL); + p->n = 0; + p->iDP = 0; + return; + } + + /* Multiply r by powers of ten until it lands somewhere in between + ** 1.0e+19 and 1.0e+17. + */ + if( sqlite3Config.bUseLongDouble ){ + LONGDOUBLE_TYPE rr = r; + if( rr>=1.0e+19 ){ + while( rr>=1.0e+119L ){ exp+=100; rr *= 1.0e-100L; } + while( rr>=1.0e+29L ){ exp+=10; rr *= 1.0e-10L; } + while( rr>=1.0e+19L ){ exp++; rr *= 1.0e-1L; } + }else{ + while( rr<1.0e-97L ){ exp-=100; rr *= 1.0e+100L; } + while( rr<1.0e+07L ){ exp-=10; rr *= 1.0e+10L; } + while( rr<1.0e+17L ){ exp--; rr *= 1.0e+1L; } + } + v = (u64)rr; + }else{ + /* If high-precision floating point is not available using "long double", + ** then use Dekker-style double-double computation to increase the + ** precision. + ** + ** The error terms on constants like 1.0e+100 computed using the + ** decimal extension, for example as follows: + ** + ** SELECT decimal_exp(decimal_sub('1.0e+100',decimal(1.0e+100))); + */ + double rr[2]; + rr[0] = r; + rr[1] = 0.0; + if( rr[0]>9.223372036854774784e+18 ){ + while( rr[0]>9.223372036854774784e+118 ){ + exp += 100; + dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117); + } + while( rr[0]>9.223372036854774784e+28 ){ + exp += 10; + dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27); + } + while( rr[0]>9.223372036854774784e+18 ){ + exp += 1; + dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18); + } + }else{ + while( rr[0]<9.223372036854774784e-83 ){ + exp -= 100; + dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83); + } + while( rr[0]<9.223372036854774784e+07 ){ + exp -= 10; + dekkerMul2(rr, 1.0e+10, 0.0); + } + while( rr[0]<9.22337203685477478e+17 ){ + exp -= 1; + dekkerMul2(rr, 1.0e+01, 0.0); + } + } + v = rr[1]<0.0 ? (u64)rr[0]-(u64)(-rr[1]) : (u64)rr[0]+(u64)rr[1]; + } + + + /* Extract significant digits. */ + i = sizeof(p->zBuf)-1; + assert( v>0 ); + while( v ){ p->zBuf[i--] = (v%10) + '0'; v /= 10; } + assert( i>=0 && i zBuf)-1 ); + p->n = sizeof(p->zBuf) - 1 - i; + assert( p->n>0 ); + assert( p->n zBuf) ); + p->iDP = p->n + exp; + if( iRound<0 ){ + iRound = p->iDP - iRound; + if( iRound==0 && p->zBuf[i+1]>='5' ){ + iRound = 1; + p->zBuf[i--] = '0'; + p->n++; + p->iDP++; + } + } + if( iRound>0 && (iRound n || p->n>mxRound) ){ + char *z = &p->zBuf[i+1]; + if( iRound>mxRound ) iRound = mxRound; + p->n = iRound; + if( z[iRound]>='5' ){ + int j = iRound-1; + while( 1 /*exit-by-break*/ ){ + z[j]++; + if( z[j]<='9' ) break; + z[j] = '0'; + if( j==0 ){ + p->z[i--] = '1'; + p->n++; + p->iDP++; + break; + }else{ + j--; + } + } + } + } + p->z = &p->zBuf[i+1]; + assert( i+p->n < sizeof(p->zBuf) ); + while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; } +} + /* ** Try to convert z into an unsigned 32-bit integer. Return true on ** success and false if there is an error. @@ -34657,121 +35865,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ ** this function assumes the single-byte case has already been handled. */ SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ - u32 a,b; + u64 v64; + u8 n; - /* The 1-byte case. Overwhelmingly the most common. Handled inline - ** by the getVarin32() macro */ - a = *p; - /* a: p0 (unmasked) */ -#ifndef getVarint32 - if (!(a&0x80)) - { - /* Values between 0 and 127 */ - *v = a; - return 1; - } -#endif + /* Assume that the single-byte case has already been handled by + ** the getVarint32() macro */ + assert( (p[0] & 0x80)!=0 ); - /* The 2-byte case */ - p++; - b = *p; - /* b: p1 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 128 and 16383 */ - a &= 0x7f; - a = a<<7; - *v = a | b; + if( (p[1] & 0x80)==0 ){ + /* This is the two-byte case */ + *v = ((p[0]&0x7f)<<7) | p[1]; return 2; } - - /* The 3-byte case */ - p++; - a = a<<14; - a |= *p; - /* a: p0<<14 | p2 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 16384 and 2097151 */ - a &= (0x7f<<14)|(0x7f); - b &= 0x7f; - b = b<<7; - *v = a | b; + if( (p[2] & 0x80)==0 ){ + /* This is the three-byte case */ + *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2]; return 3; } - - /* A 32-bit varint is used to store size information in btrees. - ** Objects are rarely larger than 2MiB limit of a 3-byte varint. - ** A 3-byte varint is sufficient, for example, to record the size - ** of a 1048569-byte BLOB or string. - ** - ** We only unroll the first 1-, 2-, and 3- byte cases. The very - ** rare larger cases can be handled by the slower 64-bit varint - ** routine. - */ -#if 1 - { - u64 v64; - u8 n; - - n = sqlite3GetVarint(p-2, &v64); - assert( n>3 && n<=9 ); - if( (v64 & SQLITE_MAX_U32)!=v64 ){ - *v = 0xffffffff; - }else{ - *v = (u32)v64; - } - return n; - } - -#else - /* For following code (kept for historical record only) shows an - ** unrolling for the 3- and 4-byte varint cases. This code is - ** slightly faster, but it is also larger and much harder to test. - */ - p++; - b = b<<14; - b |= *p; - /* b: p1<<14 | p3 (unmasked) */ - if (!(b&0x80)) - { - /* Values between 2097152 and 268435455 */ - b &= (0x7f<<14)|(0x7f); - a &= (0x7f<<14)|(0x7f); - a = a<<7; - *v = a | b; - return 4; - } - - p++; - a = a<<14; - a |= *p; - /* a: p0<<28 | p2<<14 | p4 (unmasked) */ - if (!(a&0x80)) - { - /* Values between 268435456 and 34359738367 */ - a &= SLOT_4_2_0; - b &= SLOT_4_2_0; - b = b<<7; - *v = a | b; - return 5; - } - - /* We can only reach this point when reading a corrupt database - ** file. In that case we are not in any hurry. Use the (relatively - ** slow) general-purpose sqlite3GetVarint() routine to extract the - ** value. */ - { - u64 v64; - u8 n; - - p -= 4; - n = sqlite3GetVarint(p, &v64); - assert( n>5 && n<=9 ); + /* four or more bytes */ + n = sqlite3GetVarint(p, &v64); + assert( n>3 && n<=9 ); + if( (v64 & SQLITE_MAX_U32)!=v64 ){ + *v = 0xffffffff; + }else{ *v = (u32)v64; - return n; } -#endif + return n; } /* @@ -34922,7 +36041,7 @@ SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ } /* -** Attempt to add, substract, or multiply the 64-bit signed value iB against +** Attempt to add, subtract, or multiply the 64-bit signed value iB against ** the other 64-bit signed integer at *pA and store the result in *pA. ** Return 0 on success. Or if the operation would have resulted in an ** overflow, leave *pA unchanged and return 1. @@ -35208,6 +36327,104 @@ SQLITE_PRIVATE int sqlite3VListNameToNum(VList *pIn, const char *zName, int nNam return 0; } +/* +** High-resolution hardware timer used for debugging and testing only. +*/ +#if defined(VDBE_PROFILE) \ + || defined(SQLITE_PERFORMANCE_TRACE) \ + || defined(SQLITE_ENABLE_STMT_SCANSTATUS) +/************** Include hwtime.h in the middle of util.c *********************/ +/************** Begin file hwtime.h ******************************************/ +/* +** 2008 May 27 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains inline asm code for retrieving "high-performance" +** counters for x86 and x86_64 class CPUs. +*/ +#ifndef SQLITE_HWTIME_H +#define SQLITE_HWTIME_H + +/* +** The following routine only works on Pentium-class (or newer) processors. +** It uses the RDTSC opcode to read the cycle count value out of the +** processor and returns that value. This can be used for high-res +** profiling. +*/ +#if !defined(__STRICT_ANSI__) && \ + (defined(__GNUC__) || defined(_MSC_VER)) && \ + (defined(i386) || defined(__i386__) || defined(_M_IX86)) + + #if defined(__GNUC__) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + + #elif defined(_MSC_VER) + + __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ + __asm { + rdtsc + ret ; return value at EDX:EAX + } + } + + #endif + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__x86_64__)) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned int lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return (sqlite_uint64)hi << 32 | lo; + } + +#elif !defined(__STRICT_ANSI__) && (defined(__GNUC__) && defined(__ppc__)) + + __inline__ sqlite_uint64 sqlite3Hwtime(void){ + unsigned long long retval; + unsigned long junk; + __asm__ __volatile__ ("\n\ + 1: mftbu %1\n\ + mftb %L0\n\ + mftbu %0\n\ + cmpw %0,%1\n\ + bne 1b" + : "=r" (retval), "=r" (junk)); + return retval; + } + +#else + + /* + ** asm() is needed for hardware timing support. Without asm(), + ** disable the sqlite3Hwtime() routine. + ** + ** sqlite3Hwtime() is only used for some obscure debugging + ** and analysis configurations, not in any deliverable, so this + ** should not be a great loss. + */ +SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } + +#endif + +#endif /* !defined(SQLITE_HWTIME_H) */ + +/************** End of hwtime.h **********************************************/ +/************** Continuing where we left off in util.c ***********************/ +#endif + /************** End of util.c ************************************************/ /************** Begin file hash.c ********************************************/ /* @@ -35309,7 +36526,7 @@ static void insertElement( } -/* Resize the hash table so that it cantains "new_size" buckets. +/* Resize the hash table so that it contains "new_size" buckets. ** ** The hash table might fail to resize if sqlite3_malloc() fails or ** if the new size is the same as the prior size. @@ -35378,12 +36595,13 @@ static HashElem *findElementWithHash( count = pH->count; } if( pHash ) *pHash = h; - while( count-- ){ + while( count ){ assert( elem!=0 ); if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ return elem; } elem = elem->next; + count--; } return &nullElement; } @@ -35668,19 +36886,20 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 171 */ "VCreate" OpHelp(""), /* 172 */ "VDestroy" OpHelp(""), /* 173 */ "VOpen" OpHelp(""), - /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), - /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 176 */ "VRename" OpHelp(""), - /* 177 */ "Pagecount" OpHelp(""), - /* 178 */ "MaxPgcnt" OpHelp(""), - /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), - /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), - /* 181 */ "Trace" OpHelp(""), - /* 182 */ "CursorHint" OpHelp(""), - /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), - /* 184 */ "Noop" OpHelp(""), - /* 185 */ "Explain" OpHelp(""), - /* 186 */ "Abortable" OpHelp(""), + /* 174 */ "VCheck" OpHelp(""), + /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"), + /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 177 */ "VRename" OpHelp(""), + /* 178 */ "Pagecount" OpHelp(""), + /* 179 */ "MaxPgcnt" OpHelp(""), + /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"), + /* 181 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"), + /* 182 */ "Trace" OpHelp(""), + /* 183 */ "CursorHint" OpHelp(""), + /* 184 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"), + /* 185 */ "Noop" OpHelp(""), + /* 186 */ "Explain" OpHelp(""), + /* 187 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -35742,7 +36961,9 @@ struct KVVfsFile { char *aJrnl; /* Journal content */ int szPage; /* Last known page size */ sqlite3_int64 szDb; /* Database file size. -1 means unknown */ + char *aData; /* Buffer to hold page data */ }; +#define SQLITE_KVOS_SZ 133073 /* ** Methods for KVVfsFile @@ -36105,8 +37326,7 @@ static int kvvfsDecode(const char *a, char *aOut, int nOut){ if( j+n>nOut ) return -1; memset(&aOut[j], 0, n); j += n; - c = aIn[i]; - if( c==0 ) break; + if( c==0 || mult==1 ) break; /* progress stalled if mult==1 */ }else{ aOut[j] = c<<4; c = kvvfsHexValue[aIn[++i]]; @@ -36183,6 +37403,7 @@ static int kvvfsClose(sqlite3_file *pProtoFile){ SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, pFile->isJournal ? "journal" : "db")); sqlite3_free(pFile->aJrnl); + sqlite3_free(pFile->aData); return SQLITE_OK; } @@ -36231,7 +37452,7 @@ static int kvvfsReadDb( unsigned int pgno; int got, n; char zKey[30]; - char aData[133073]; + char *aData = pFile->aData; assert( iOfst>=0 ); assert( iAmt>=0 ); SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); @@ -36248,7 +37469,8 @@ static int kvvfsReadDb( pgno = 1; } sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); - got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, aData, sizeof(aData)-1); + got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, + aData, SQLITE_KVOS_SZ-1); if( got<0 ){ n = 0; }else{ @@ -36256,7 +37478,7 @@ static int kvvfsReadDb( if( iOfst+iAmt<512 ){ int k = iOfst+iAmt; aData[k*2] = 0; - n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000); + n = kvvfsDecode(aData, &aData[2000], SQLITE_KVOS_SZ-2000); if( n>=iOfst+iAmt ){ memcpy(zBuf, &aData[2000+iOfst], iAmt); n = iAmt; @@ -36315,7 +37537,7 @@ static int kvvfsWriteDb( KVVfsFile *pFile = (KVVfsFile*)pProtoFile; unsigned int pgno; char zKey[30]; - char aData[131073]; + char *aData = pFile->aData; SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); assert( iAmt>=512 && iAmt<=65536 ); assert( (iAmt & (iAmt-1))==0 ); @@ -36524,6 +37746,10 @@ static int kvvfsOpen( }else{ pFile->zClass = "local"; } + pFile->aData = sqlite3_malloc64(SQLITE_KVOS_SZ); + if( pFile->aData==0 ){ + return SQLITE_NOMEM; + } pFile->aJrnl = 0; pFile->nJrnl = 0; pFile->szPage = -1; @@ -36687,7 +37913,7 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ ** This source file is organized into divisions where the logic for various ** subfunctions is contained within the appropriate division. PLEASE ** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed -** in the correct division and should be clearly labeled. +** in the correct division and should be clearly labelled. ** ** The layout of divisions is as follows: ** @@ -36737,7 +37963,7 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ #endif /* Use pread() and pwrite() if they are available */ -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__linux__) # define HAVE_PREAD 1 # define HAVE_PWRITE 1 #endif @@ -36760,7 +37986,8 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ /* #include */ #include /* amalgamator: keep */ #include -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) # include #endif @@ -36848,9 +38075,46 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){ */ #define SQLITE_MAX_SYMLINKS 100 +/* +** Remove and stub certain info for WASI (WebAssembly System +** Interface) builds. +*/ +#ifdef SQLITE_WASI +# undef HAVE_FCHMOD +# undef HAVE_FCHOWN +# undef HAVE_MREMAP +# define HAVE_MREMAP 0 +# ifndef SQLITE_DEFAULT_UNIX_VFS +# define SQLITE_DEFAULT_UNIX_VFS "unix-dotfile" + /* ^^^ should SQLITE_DEFAULT_UNIX_VFS be "unix-none"? */ +# endif +# ifndef F_RDLCK +# define F_RDLCK 0 +# define F_WRLCK 1 +# define F_UNLCK 2 +# if __LONG_MAX == 0x7fffffffL +# define F_GETLK 12 +# define F_SETLK 13 +# define F_SETLKW 14 +# else +# define F_GETLK 5 +# define F_SETLK 6 +# define F_SETLKW 7 +# endif +# endif +#else /* !SQLITE_WASI */ +# ifndef HAVE_FCHMOD +# define HAVE_FCHMOD +# endif +#endif /* SQLITE_WASI */ + +#ifdef SQLITE_WASI +# define osGetpid(X) (pid_t)1 +#else /* Always cast the getpid() return type for compatibility with ** kernel modules in VxWorks. */ -#define osGetpid(X) (pid_t)getpid() +# define osGetpid(X) (pid_t)getpid() +#endif /* ** Only set the lastErrno if the error code is a real error and not @@ -37122,7 +38386,11 @@ static struct unix_syscall { #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\ aSyscall[13].pCurrent) +#if defined(HAVE_FCHMOD) { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, +#else + { "fchmod", (sqlite3_syscall_ptr)0, 0 }, +#endif #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE @@ -37158,14 +38426,16 @@ static struct unix_syscall { #endif #define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, #else { "mmap", (sqlite3_syscall_ptr)0, 0 }, #endif #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) -#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 +#if (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) \ + && !defined(SQLITE_WASI) { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, #else { "munmap", (sqlite3_syscall_ptr)0, 0 }, @@ -37230,7 +38500,7 @@ static int robustFchown(int fd, uid_t uid, gid_t gid){ /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the -** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** "unix" VFSes. Return SQLITE_OK upon successfully updating the ** system call pointer, or SQLITE_NOTFOUND if there is no configurable ** system call named zName. */ @@ -37752,7 +39022,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ ** If you close a file descriptor that points to a file that has locks, ** all locks on that file that are owned by the current process are ** released. To work around this problem, each unixInodeInfo object -** maintains a count of the number of pending locks on tha inode. +** maintains a count of the number of pending locks on the inode. ** When an attempt is made to close an unixFile, if there are ** other unixFile open on the same inode that are holding locks, the call ** to close() the file descriptor is deferred until all of the locks clear. @@ -37766,7 +39036,7 @@ static void vxworksReleaseFileId(struct vxworksFileId *pId){ ** not posix compliant. Under LinuxThreads, a lock created by thread ** A cannot be modified or overridden by a different thread B. ** Only thread A can modify the lock. Locking behavior is correct -** if the appliation uses the newer Native Posix Thread Library (NPTL) +** if the application uses the newer Native Posix Thread Library (NPTL) ** on linux - with NPTL a lock created by thread A can override locks ** in thread B. But there is no way to know at compile-time which ** threading library is being used. So there is no way to know at @@ -37968,7 +39238,7 @@ static void storeLastErrno(unixFile *pFile, int error){ } /* -** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. +** Close all file descriptors accumulated in the unixInodeInfo->pUnused list. */ static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; @@ -38316,7 +39586,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){ ** ** UNLOCKED -> SHARED ** SHARED -> RESERVED -** SHARED -> (PENDING) -> EXCLUSIVE +** SHARED -> EXCLUSIVE ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** @@ -38331,7 +39601,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** slightly in order to be compatible with Windows95 systems simultaneously ** accessing the same database file, in case that is ever required. ** - ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved + ** Symbols defined in os.h identify the 'pending byte' and the 'reserved ** byte', each single bytes at well known offsets, and the 'shared byte ** range', a range of 510 bytes at a well known offset. ** @@ -38339,7 +39609,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** byte'. If this is successful, 'shared byte range' is read-locked ** and the lock on the 'pending byte' released. (Legacy note: When ** SQLite was first developed, Windows95 systems were still very common, - ** and Widnows95 lacks a shared-lock capability. So on Windows95, a + ** and Windows95 lacks a shared-lock capability. So on Windows95, a ** single randomly selected by from the 'shared byte range' is locked. ** Windows95 is now pretty much extinct, but this work-around for the ** lack of shared-locks on Windows95 lives on, for backwards @@ -38349,19 +39619,20 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ** A RESERVED lock is implemented by grabbing a write-lock on the ** 'reserved byte'. ** - ** A process may only obtain a PENDING lock after it has obtained a - ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock - ** on the 'pending byte'. This ensures that no new SHARED locks can be - ** obtained, but existing SHARED locks are allowed to persist. A process - ** does not have to obtain a RESERVED lock on the way to a PENDING lock. - ** This property is used by the algorithm for rolling back a journal file - ** after a crash. + ** An EXCLUSIVE lock may only be requested after either a SHARED or + ** RESERVED lock is held. An EXCLUSIVE lock is implemented by obtaining + ** a write-lock on the entire 'shared byte range'. Since all other locks + ** require a read-lock on one of the bytes within this range, this ensures + ** that no other locks are held on the database. ** - ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is - ** implemented by obtaining a write-lock on the entire 'shared byte - ** range'. Since all other locks require a read-lock on one of the bytes - ** within this range, this ensures that no other locks are held on the - ** database. + ** If a process that holds a RESERVED lock requests an EXCLUSIVE, then + ** a PENDING lock is obtained first. A PENDING lock is implemented by + ** obtaining a write-lock on the 'pending byte'. This ensures that no new + ** SHARED locks can be obtained, but existing SHARED locks are allowed to + ** persist. If the call to this function fails to obtain the EXCLUSIVE + ** lock in this case, it holds the PENDING lock instead. The client may + ** then re-attempt the EXCLUSIVE lock later on, after existing SHARED + ** locks have cleared. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; @@ -38387,7 +39658,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ /* Make sure the locking sequence is correct. ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. + ** (2) SQLite never explicitly requests a pending lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); @@ -38432,7 +39703,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK - || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock eFileLock==RESERVED_LOCK) ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; @@ -38443,6 +39714,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ storeLastErrno(pFile, tErrno); } goto end_lock; + }else if( eFileLock==EXCLUSIVE_LOCK ){ + pFile->eFileLock = PENDING_LOCK; + pInode->eFileLock = PENDING_LOCK; } } @@ -38530,13 +39804,9 @@ static int unixLock(sqlite3_file *id, int eFileLock){ } #endif - if( rc==SQLITE_OK ){ pFile->eFileLock = eFileLock; pInode->eFileLock = eFileLock; - }else if( eFileLock==EXCLUSIVE_LOCK ){ - pFile->eFileLock = PENDING_LOCK; - pInode->eFileLock = PENDING_LOCK; } end_lock: @@ -39606,7 +40876,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ /* Make sure the locking sequence is correct ** (1) We never move from unlocked to anything higher than shared lock. - ** (2) SQLite never explicitly requests a pendig lock. + ** (2) SQLite never explicitly requests a pending lock. ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); @@ -39722,7 +40992,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){ if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + pInode->sharedByte, 1, 0)) ){ int failed2 = SQLITE_OK; - /* now attemmpt to get the exclusive lock range */ + /* now attempt to get the exclusive lock range */ failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 1); if( failed && (failed2 = afpSetLock(context->dbPath, pFile, @@ -39771,9 +41041,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { unixInodeInfo *pInode; afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; int skipShared = 0; -#ifdef SQLITE_TEST - int h = pFile->h; -#endif assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, @@ -39789,9 +41056,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); #ifdef SQLITE_DEBUG /* When reducing a lock such that other processes can start @@ -39840,9 +41104,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; pInode->nShared--; if( pInode->nShared==0 ){ - SimulateIOErrorBenign(1); - SimulateIOError( h=(-1) ) - SimulateIOErrorBenign(0); if( !skipShared ){ rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); } @@ -39943,12 +41204,6 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){ ** Seek to the offset passed as the second argument, then read cnt ** bytes into pBuf. Return the number of bytes actually read. ** -** NB: If you define USE_PREAD or USE_PREAD64, then it might also -** be necessary to define _XOPEN_SOURCE to be 500. This varies from -** one system to another. Since SQLite does not define USE_PREAD -** in any form by default, we will not attempt to define _XOPEN_SOURCE. -** See tickets #2741 and #2681. -** ** To avoid stomping the errno value on a failed read the lastErrno value ** is set before returning. */ @@ -40023,7 +41278,7 @@ static int unixRead( #endif #if SQLITE_MAX_MMAP_SIZE>0 - /* Deal with as much of this read request as possible by transfering + /* Deal with as much of this read request as possible by transferring ** data from the memory mapping using memcpy(). */ if( offset