Compare commits

...

53 Commits

Author SHA1 Message Date
drh 5b92691f85 This is an idea for new APIs to improve upon sqlite3_set_authorizer().
After implementing it, however, I'm having second throughts.  Saved here
in case I changed my mind.

FossilOrigin-Name: c3c3d9731535650ba1bcac8709ead27ce037736b31b7efef37422b502e10d332
2026-05-01 23:56:16 +00:00
drh 7509c6351e Fix an SQLITE_MISUSE memory error in check-ini [1f940357f7bb160b].
FossilOrigin-Name: fc64113e8f9aee0161e682db7c244a7849f2db86551a557b276deb1785941476
2026-05-01 17:42:15 +00:00
drh 57e46429fe Harden the recovery extension against SQL injections coming from the
sqlite_schema table of the database being recovered.

FossilOrigin-Name: 9190f1b9b8889e4d80c370db36b916744d5779de0ec51da0369bbe74adb7b931
2026-05-01 16:06:21 +00:00
drh a9c2e08087 Check-in [1786fcd5b4ee6cd9] works great and generates correct code, but it
upset UBSAN.  This check-in fixes the UBSAN complaint.

FossilOrigin-Name: 7cd76847e8c9b683e39c1063a343288f11b4aa5e9302394fe0c4244d361ee4f1
2026-05-01 16:03:51 +00:00
drh 7f1f4c803d The value for /f and other filename substitutions in SQLITE_PS1 is now
"memory" if open on an in-memory database.

FossilOrigin-Name: 4aac1057eeaf6c29a4893e9c080497c780b0963e810c501532d79eba1b457f27
2026-04-30 18:23:58 +00:00
drh bd13573a96 Remove lots of code used to support legacy Windows version (WindowsXP and
before).  Use Slim Reader/Writer Locks on Windows for non-recursive mutexes,
since SRWLs are now available on all supported Windows platforms.

FossilOrigin-Name: 6f7a5ff22db10f889596239aae5f6a1130cbdfe72612ceee68cbeb3b86e40227
2026-04-30 16:52:56 +00:00
drh 0cadeecf6f Fix a harmless compiler warning in src/mutex_w32.c.
FossilOrigin-Name: 8de796ca459f2c996937fe1107838b217572636110c8c09e358343385bf17a1b
2026-04-30 15:41:01 +00:00
drh ad52288e0d Fix the memdb1.test test module so that it runs correctly on Windows.
FossilOrigin-Name: 9012f8ea7454bbb792b4361ed74d778e02b98ef44a443f9a23a27a4222fda747
2026-04-30 15:33:44 +00:00
drh d5f0e01593 Change the CLI warning that appears in the header about opening a
in-memory database, to be more readable.

FossilOrigin-Name: cc64cafdfe0b0965fabaf9d561d9a108c20a3643f71b36e6a6c51fc8f30b82cb
2026-04-30 15:19:09 +00:00
drh 3e663bd8c7 Add the /m escape to the CLI prompt string. Use it to show "memory"
in italic for the default prompt for in-memory databases.

FossilOrigin-Name: 1e3c95393044279997833145cb610b98926fb0c8f53ae4fb35e9fcc6c1ded1a2
2026-04-30 15:07:25 +00:00
dan 97901bbd69 Fix a crash that could occur if the destination database of a backup is
replaced using sqlite3_deserialize() between the call to sqlite3_backup_init()
and the first call to sqlite3_backup_step(). Forum post [forum:15d82885e2 | 15d82885e2].

FossilOrigin-Name: 1f940357f7bb160b583ac5b08ff4e32a9fef353255d032c5a18bcb04416c0f0b
2026-04-29 19:14:54 +00:00
drh c549e28c67 Reduce the number of system calls in the os_win.c thunk from 81 down
to 58, as well as other simplifications.

FossilOrigin-Name: c49521739c961ed5e38424bf26b2e34d8017a85122622ceac5af7398233fe343
2026-04-29 16:09:47 +00:00
drh 90dae0d01d Further simplifications to the interface to the Windows family of OSes.
An incremental check-in.

FossilOrigin-Name: f2aa6b66d2a1b5bb2dc8a44788a061a549a846244354df19b44398839578461e
2026-04-29 15:03:01 +00:00
drh 0a0a52ae93 Simplify the Windows interface by removing support for various WindowsXP
and WindowsCE.  This is a work-in-progress.  More to follow.

FossilOrigin-Name: 4dbc4d19c4f3b58d0bedd766cfba65d9624903a5ab0a340eb83ddeec058607ea
2026-04-29 14:02:23 +00:00
drh e1d4680fdd Updates and simplifications to the mutex implementation on Windows. Uses
Slim Reader/Writer Locks for performance, for non-reentrant mutexes.
Requires WindowsVista or later.

FossilOrigin-Name: a75aa5400f94531b8ffd00be4580b0578d8d81ba9681fd06b1d9d96a6180994c
2026-04-28 20:01:03 +00:00
drh 532e506d18 Attempt to align instances of the sqlite3_mutex object at 128-byte boundaries
to prevent false-sharing in multi-core machines. See the discussion at and
around [forum:/forumpost/2026-03-25T23:15:03Z|forum post 2026-03-25T23:15:03Z].

FossilOrigin-Name: 1786fcd5b4ee6cd9b4780f3687dfaec5b90ef0476e0da266a94e069b98e70514
2026-04-28 15:12:40 +00:00
drh c9bed48c3c Fix comment typo
FossilOrigin-Name: d64a1dbe0fb2d9286806d833a3146b21d5bf1636a650d5a64cf163c7f2356e98
2026-04-28 12:49:10 +00:00
drh 6b4eb6725f Improved detection of when the alternative windows-compatible main() is
needed in the sqldiff.exe utility.

FossilOrigin-Name: e25b849a603cb08ab888641ff324e9aa129391c4792de86a62c55e9caf84c16e
2026-04-27 14:01:23 +00:00
drh 6176034151 Relax arbitrary length restrictions on some error messages in the CLI.
FossilOrigin-Name: 52f5531ded8e0d3da3dc4d5c9b3d14573041b9446e2c45b5093c958b6c74e0de
2026-04-27 12:43:09 +00:00
stephan 7471b5d6c0 Increase a stack-local buffer size to resolve a test regression introduced in [ff084ae341eab5c], in which some tests fail a pattern match check if their absolute file names are too long. Resolves [forum:205f73db1b2806f5|forum post 205f73db1b2806f5].
FossilOrigin-Name: 286fc22c7ca882b0b09de2f1b2a7e0b382af6c498f2f8ad237fe064feaa70d7f
2026-04-27 12:05:56 +00:00
dan 1fc7341ad2 Fix a crash that could occur if sqlite3_deserialize() was used to overwrite a database with an open transaction on it. Bug introduced by [fc42d31d6fca21ab] on 2018-03-07 and first released in version 3.23.0. This is not a vulnerability as an attacker that can exploit this already has the ability to execute arbitrary code. Forum post [forum:39134ba029 | 39134ba029].
FossilOrigin-Name: 77662cce9aa452ba5820e5f1a864a802763bd83b937cdb16e7025219eff7bd08
2026-04-27 11:32:43 +00:00
stephan 6af10b0efd Refactor the uint128 type check to consolidate duplicated rules.
FossilOrigin-Name: 362ef7bc00eea180fb3458bd8887021aed54e321901483edc00ad6b23a33d8e5
2026-04-27 06:48:07 +00:00
stephan 70d1e3e75e Feature detection fix for uint128 on RISCV 32-bit, reported in [forum:f8d1417ce8eb2f22|forum post 2026-04-26T11:41:01Z] and validated by the OP.
FossilOrigin-Name: c4a2c20839ef75534d71d928a03ad276bdc488b0baa76c1505f48dc1ff0bcdd1
2026-04-27 05:55:38 +00:00
drh 74006fdcdb Improvements to temp-file cleanup on close in the CLI.
FossilOrigin-Name: 048c969c34eaaf8c203b996e999a7dbc94c47b4959719af9e6625052520f7135
2026-04-26 18:35:26 +00:00
drh 4b9f9d6794 In the CLI, eliminate the 2-second delay before getting the prompt back
after running a ".www" or ".excel".  Instead, remember the name of the
temporary file that is to be deleted and delete it later.

FossilOrigin-Name: 4e9ee211ca661fe8978b7f676f33ef962ff5c8177bc7c5ad42b19a2fa4482d90
2026-04-25 16:17:23 +00:00
drh 39b2fe7e5b Simplify the modePush()/modePop() implementation in the CLI.
FossilOrigin-Name: 92cad2cd7284d3a969b713336865007da2b0b2a8eef367d4185802174cbd027a
2026-04-25 15:05:46 +00:00
dan 01ec60bb87 Do not run expridx2.test as part of the "journaltest" permutation, as it requires wal mode.
FossilOrigin-Name: 11cfbae735669776c967522294b1606af90b4bd5379e2e1ed722520887b57757
2026-04-25 14:32:05 +00:00
dan e2dbd0a578 Fix a single-byte OOB read that could occur in the session module when concatenating patchsets.
FossilOrigin-Name: 60d7cd625a6160ba1bc60fd00fab2e91e0deff42034c6864107c19330b35ea7a
2026-04-25 14:00:29 +00:00
drh df7b22790e Minor code simplification in the CLI.
FossilOrigin-Name: 59795c71e5745e8a27dc596b9f1fc8f137df58a6ec8d8d0dab2b31cf8562796d
2026-04-25 10:45:17 +00:00
drh 50811f74e0 Fix a problem in the CLI in which the ".www" command followed by
".dbstat" does not restore the mode to its original setting afterwards.

FossilOrigin-Name: 4a5cac1d00e1fa287ab8ce3437c0152a9f362d72bdb9976889c93f6368b3fd66
2026-04-25 00:29:40 +00:00
drh d7bf8233ff Fix test cases due to recent CLI default prompt changes. Make sure ANSI
escape codes are recognized on Windows terminals.

FossilOrigin-Name: 22b791e404f0895b0f343b2ce0e169a7ada8578e46a39a9f4bd3a31f81c9853c
2026-04-24 20:27:42 +00:00
drh 455486bbc4 Improvements to the default prompt for the CLI.
FossilOrigin-Name: 9ab0bb487f78ba5a51cd3b4efcee23b11096e90ebf3add4da18aea657e8de0b6
2026-04-24 09:21:25 +00:00
drh ba9092e228 Improved default prompt in the CLI.
FossilOrigin-Name: 78193b60346dba304485241dd65f0eb6e971714397b76191b3bf7a5fdb44916b
2026-04-23 22:15:18 +00:00
drh 67d4e0226f Fix harmless compiler warnings. Enhance the CLI prompt mechanism to make
it more easily reusable by other projects, and to add the /h, /H, and /u
escape sequences.

FossilOrigin-Name: 354ce4f5e2a86b621b2058a6372495a5919cf3dc88ce1ed3637642f720c48002
2026-04-23 20:13:14 +00:00
drh 323b3fc121 Fix a bug in [dbc1d6f0a3452607] that disrupts the display when running
on a Windows console (not redirecting to a file).

FossilOrigin-Name: 75b5df2b0046827e985712b5725488b59667e2cfd171cde9f1194f2917165205
2026-04-23 20:06:16 +00:00
drh 78279e2550 Improvements to the #define mechanism in the CLI that allows other programs
that incorporate the shell.c source file to customize some of the
prompt expansions.

FossilOrigin-Name: e5f8840d11c1863cfc5c0b4a3d16c9ec9ab6ca6ecb7ff1a3a9a237715eed5d30
2026-04-23 16:40:34 +00:00
dan 4e4962f6b3 Have testrunner.tcl return a non-zero exit status if one or more tests have failed.
FossilOrigin-Name: 50ddc36d32540b6d98990b5c9a465beb5b68cc31ae3f0ed162f6fe07e7c74e2d
2026-04-23 15:54:00 +00:00
drh ed95a9d103 New CLI prompt escape sequences: /r, /A, /v, /V, and /D../D.
FossilOrigin-Name: e7c7acd1a4e5cb0f8e68f119352480c1aa9dd3c2b289c92f88e9e697a2e440e0
2026-04-23 15:05:46 +00:00
drh e791e7c7a0 Fix harmless compiler warning.
FossilOrigin-Name: 706373dcbe43752cf21604f278f96fb3015a36089ad64f069bb3a64bc526d137
2026-04-23 14:49:21 +00:00
stephan 2545d9c201 CLI shell: handle the special case of .prompt "" to not incorrectly report that as an OOM. Reported [https://fossil-scm.org/forum/forumpost/ca277be73a|in the Fossil SCM forum].
FossilOrigin-Name: 3fa0500b964869c45f7b49717680b791049f7cd8c47a06a337dd22351d2002f9
2026-04-23 14:05:36 +00:00
dan 2a07c6de2c When updating indexes on expressions, omit the update if the new indexed values are the same as the old.
FossilOrigin-Name: f0fc4e5ec334da3a1db8b0334e23d1911874c96675690da94210b2afb45f7307
2026-04-22 19:38:04 +00:00
dan 68b07ddd41 Add OOM test for the new code on this branch.
FossilOrigin-Name: 685be705a2a69e79de0d581e5bbe9d942133817342121786a551d95d59a4af6c
2026-04-22 19:25:12 +00:00
drh 4b69e7704b Mark branches that are believed to be unreachable.
FossilOrigin-Name: 18ed9736fbbea762e04b8ad156e83e577e60c5234a5e233540ad8664fe3b379c
2026-04-22 19:12:48 +00:00
dan e17ab401f0 Instead of adding a branch to OP_IdxInsert, use an extra OP_IfNull when updating indexes on expressions.
FossilOrigin-Name: 5112f96cb1428693614b91bcca78326fccf7289e395a920339676ceeba871e3f
2026-04-22 18:19:24 +00:00
dan 57ee077c5e Enable the optimization on this branch for indexes on expressions only.
FossilOrigin-Name: 3576b6930436b3149c5033476f70518e9b4adeaeec2538a68b7dc599eb854c47
2026-04-22 17:50:50 +00:00
dan c7edd22456 Fix P3/P5 confusion in the comment above OP_IdxDelete.
FossilOrigin-Name: 0fb6ca8e22f355838a02223dd24b34c0669a6cc0b7ac80931c721db72cf5c1c8
2026-04-22 17:31:19 +00:00
dan 8606a52934 Detect and omit cases where an index key is being replaced with an identical key.
FossilOrigin-Name: 7a01caf0d0864a462eb81b7643dcf89048f4ae56a29d762f0cfc38066218504f
2026-04-22 16:58:36 +00:00
drh 5c6404aa77 Fix a integer type bug in the previous check-in.
FossilOrigin-Name: 5e1b5ff1c170e94956bf77bd38afcad6369cb9f9333864711212af045281ad0e
2026-04-22 13:41:20 +00:00
drh 429369f4aa QRF and the CLI preserve zero bytes when outputing unformatted BLOB values.
FossilOrigin-Name: dbc1d6f0a3452607e4e92155df0fdb1c5ea4ea5ad1654664f86383faf6c105ef
2026-04-22 12:22:32 +00:00
drh 9529894a29 Fix harmless compiler warning in QRF.
FossilOrigin-Name: dcfb505ac7762f7a3102e1c3853a83dca1d1ef436cc8981949b63c7d7992cc5b
2026-04-21 22:19:38 +00:00
dan c4c47a49a8 Disable optimization of nested EXIST loops. Possible fix for [forum:/forumpost/077c02e8ed | forum post 77c02e8ed].
FossilOrigin-Name: d0d95a39bc211034071746395ee6dbcfbea9d573d27480655391ba9698f7e214
2026-04-21 20:40:22 +00:00
drh ee7f6f29fc Fix the BETWEEN operator so that it propagates the EP_Subquery,
EP_Collate, and EP_HasFunc flags in the expression tree, as it ought.
Fix for the problem reported at
[forum:/forumpost/2026-04-21T19:10:55Z|forum post 2026-04-21T19:10:55Z]
and probably a bunch of other problems too.  This has been incorrect
for a long time.

FossilOrigin-Name: 2d1d2947b28f5dcbf4c66b594e176038c4e71e61948a88db565c1cb1212657ce
2026-04-21 20:28:38 +00:00
dan c3b52dff53 Correctly dequote the constraint name passed to ALTER TABLE DROP CONSTRAINT. This improves upon the partial fix in [7f5afb12f4]. Forum post [forum:/forumpost/a746b08b87 | a746b08b87].
FossilOrigin-Name: 9387238cb0bb2651c64ecb229072df039577d71fd91546a2f0c94ca9755bf25d
2026-04-21 18:07:04 +00:00
33 changed files with 1540 additions and 2043 deletions
+24 -23
View File
@@ -419,7 +419,7 @@ static void analyzeFunc(
int rc;
sqlite3_stmt *pStmt;
int n;
sqlite3_int64 i64;
sqlite3_int64 ii;
sqlite3_int64 pgsz;
sqlite3_int64 nPage;
sqlite3_int64 nPageInUse;
@@ -429,6 +429,7 @@ static void analyzeFunc(
Analysis s;
sqlite3_uint64 r[2];
(void)argc;
memset(&s, 0, sizeof(s));
s.db = sqlite3_context_db_handle(context);
s.context = context;
@@ -445,10 +446,10 @@ static void analyzeFunc(
sqlite3_result_text(context, "cannot analyze \"temp\"",-1,SQLITE_STATIC);
return;
}
i64 = 0;
rc = analysisSqlInt(&s,&i64,"SELECT 1 FROM pragma_database_list"
ii = 0;
rc = analysisSqlInt(&s,&ii,"SELECT 1 FROM pragma_database_list"
" WHERE name=%Q COLLATE nocase",s.zSchema);
if( rc || i64==0 ){
if( rc || ii==0 ){
analysisReset(&s);
sqlite3_result_text(context,"no such database",-1,SQLITE_STATIC);
return;
@@ -570,25 +571,25 @@ static void analyzeFunc(
analysisLine(&s, "Pages on the freelist", "%-11lld ", nFreeList);
analysisPercent(&s, (nFreeList*100.0)/(double)nPage);
i64 = 0;
rc = analysisSqlInt(&s, &i64, "PRAGMA \"%w\".auto_vacuum", s.zSchema);
ii = 0;
rc = analysisSqlInt(&s, &ii, "PRAGMA \"%w\".auto_vacuum", s.zSchema);
if( rc ) return;
if( i64==0 || nPage<=1 ){
i64 = 0;
if( ii==0 || nPage<=1 ){
ii = 0;
}else{
double rPtrsPerPage = pgsz/5;
double rAvPage = (nPage-1.0)/(rPtrsPerPage+1.0);
i64 = (sqlite3_int64)ceil(rAvPage);
ii = (sqlite3_int64)ceil(rAvPage);
}
analysisLine(&s, "Pages of auto-vacuum overhead", "%-11lld ", i64);
analysisPercent(&s, (i64*100.0)/(double)nPage);
analysisLine(&s, "Pages of auto-vacuum overhead", "%-11lld ", ii);
analysisPercent(&s, (ii*100.0)/(double)nPage);
i64 = 0;
rc = analysisSqlInt(&s, &i64,
ii = 0;
rc = analysisSqlInt(&s, &ii,
"SELECT count(*)+1 FROM \"%w\".sqlite_schema WHERE type='table'",
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of tables", "%lld\n", i64);
analysisLine(&s, "Number of tables", "%lld\n", ii);
nWORowid = 0;
rc = analysisSqlInt(&s, &nWORowid,
"SELECT count(*) FROM \"%w\".pragma_table_list WHERE wr",
@@ -596,7 +597,7 @@ static void analyzeFunc(
if( rc ) return;
if( nWORowid>0 ){
analysisLine(&s, "Number of WITHOUT ROWID tables", "%lld\n", nWORowid);
analysisLine(&s, "Number of rowid tables", "%lld\n", i64 - nWORowid);
analysisLine(&s, "Number of rowid tables", "%lld\n", ii - nWORowid);
}
nIndex = 0;
rc = analysisSqlInt(&s, &nIndex,
@@ -604,23 +605,23 @@ static void analyzeFunc(
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of indexes", "%lld\n", nIndex);
i64 = 0;
rc = analysisSqlInt(&s, &i64,
ii = 0;
rc = analysisSqlInt(&s, &ii,
"SELECT count(*) FROM \"%w\".sqlite_schema"
" WHERE name GLOB 'sqlite_autoindex_*' AND type='index'",
s.zSchema);
if( rc ) return;
analysisLine(&s, "Number of defined indexes", "%lld\n", nIndex - i64);
analysisLine(&s, "Number of implied indexes", "%lld\n", i64);
analysisLine(&s, "Number of defined indexes", "%lld\n", nIndex - ii);
analysisLine(&s, "Number of implied indexes", "%lld\n", ii);
analysisLine(&s, "Size of the database in bytes", "%lld\n", pgsz*nPage);
i64 = 0;
rc = analysisSqlInt(&s, &i64,
ii = 0;
rc = analysisSqlInt(&s, &ii,
"SELECT sum(payload) FROM temp.%s"
" WHERE NOT is_index AND name NOT LIKE 'sqlite_schema'",
s.zSU);
if( rc ) return;
analysisLine(&s, "Bytes of payload", "%-11lld ", i64);
analysisPercent(&s, i64*100.0/(double)(pgsz*nPage));
analysisLine(&s, "Bytes of payload", "%-11lld ", ii);
analysisPercent(&s, ii*100.0/(double)(pgsz*nPage));
analysisTitle(&s, "Page counts for all tables with their indexes");
pStmt = analysisPrepare(&s,
+31 -19
View File
@@ -428,7 +428,6 @@ static void qrfEqpStats(Qrf *p){
if( nRow>=0 ){
if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' ');
qrfApproxInt64(pStats, nRow);
nSp = 2;
if( p->spec.eStyle==QRF_STYLE_StatsEst ){
sqlite3_str_appendf(pStats, " ");
qrfApproxInt64(pStats, (i64)rEstCum);
@@ -718,19 +717,16 @@ static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){
}
/*
** Escape the input string if it is needed and in accordance with
** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol.
** Escape the text starting at byte iStart of pStr, if needed, using the
** escape encoding of eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol.
** The pStr string is modified appropriately.
**
** Escaping is needed if the string contains any control characters
** other than \t, \n, and \r\n
**
** If no escaping is needed (the common case) then set *ppOut to NULL
** and return 0. If escaping is needed, write the escaped string into
** memory obtained from sqlite3_malloc64() and make *ppOut point to that
** memory and return 0. If an error occurs, return non-zero.
**
** The caller is responsible for freeing *ppFree if it is non-NULL in order
** to reclaim memory.
** If no escaping is needed (the common case) then pStr is unchanged.
** If escaping is required, then pStr is expanded and modified to hold
** an escaped representation of the text.
*/
static void qrfEscape(
int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */
@@ -738,20 +734,22 @@ static void qrfEscape(
int iStart /* Begin escapding on this byte of pStr */
){
sqlite3_int64 i, j; /* Loop counters */
sqlite3_int64 sz; /* Size of the string prior to escaping */
sqlite3_int64 nCtrl = 0;/* Number of control characters to escape */
unsigned char *zIn; /* Text to be escaped */
unsigned int nIn; /* Bytes of text to be escaped */
unsigned char c; /* A single character of the text */
unsigned char *zOut; /* Where to write the results */
/* Find the text to be escaped */
zIn = (unsigned char*)sqlite3_str_value(pStr);
nIn = sqlite3_str_length(pStr);
if( zIn==0 ) return;
zIn += iStart;
nIn -= iStart;
/* Count the control characters */
for(i=0; (c = zIn[i])!=0; i++){
if( c<=0x1f
for(i=0; i<nIn; i++){
if( (c = zIn[i])<=0x1f
&& c!='\t'
&& c!='\n'
&& (c!='\r' || zIn[i+1]!='\n')
@@ -763,18 +761,17 @@ static void qrfEscape(
/* Make space to hold the escapes. Copy the original text to the end
** of the available space. */
sz = sqlite3_str_length(pStr) - iStart;
if( eEsc==QRF_ESC_Symbol ) nCtrl *= 2;
sqlite3_str_appendchar(pStr, nCtrl, ' ');
zOut = (unsigned char*)sqlite3_str_value(pStr);
if( zOut==0 ) return;
zOut += iStart;
zIn = zOut + nCtrl;
memmove(zIn,zOut,sz);
memmove(zIn,zOut,nIn);
/* Convert the control characters */
for(i=j=0; (c = zIn[i])!=0; i++){
if( c>0x1f
for(i=j=0; i<nIn; i++){
if( (c = zIn[i])>0x1f
|| c=='\t'
|| c=='\n'
|| (c=='\r' && zIn[i+1]=='\n')
@@ -786,6 +783,7 @@ static void qrfEscape(
j += i;
}
zIn += i+1;
nIn -= i+1;
i = -1;
if( eEsc==QRF_ESC_Symbol ){
zOut[j++] = 0xe2;
@@ -1188,8 +1186,22 @@ static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
break;
}
default: {
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
qrfEncodeText(p, pOut, zTxt);
const void *pBlob = sqlite3_column_blob(p->pStmt,iCol);
int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
int rc;
qrfWrite(p);
if( nBlob==0 ){
/* no-op */
}else if( p->spec.eEsc==QRF_ESC_Off ){
rc = p->spec.xWrite(p->spec.pWriteArg,pBlob,nBlob);
if( rc ){
qrfError(p, rc, "Failed to write %d bytes of BLOB output", nBlob);
}
}else{
sqlite3_str_append(pOut, pBlob, nBlob);
qrfEscape(p->spec.eEsc, pOut, 0);
qrfWrite(p);
}
}
}
break;
+40 -16
View File
@@ -532,18 +532,39 @@ static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){
}
}
/*
** Run a single SQL statement in zSql. If zSql contains two or more
** SQL statements separated by ';', only the first is run.
**
** Return the sqlite3_finalizer() or sqlite3_prepare() result code
** from running the zSql statement.
*/
static int recoverOneStmt(sqlite3 *db, const char *zSql){
sqlite3_stmt *pStmt = 0;
int rc;
if( zSql==0 ) return SQLITE_OK;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc ){
sqlite3_finalize(pStmt);
return rc;
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){}
return sqlite3_finalize(pStmt);
}
/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). A copy of p->errCode is returned in this
** case.
**
** Otherwise, execute SQL script zSql. If successful, return SQLITE_OK.
** Or, if an error occurs, leave an error code and message in the recover
** handle and return a copy of the error code.
** Otherwise, execute a single SQL statment in zSql. Even if zSql contains
** two or more SQL statements separated by ';', only execute the first one.
** If successful, return SQLITE_OK. Or, if an error occurs, leave an error
** code and message in the recover handle and return a copy of the error code.
*/
static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){
if( p->errCode==SQLITE_OK ){
int rc = sqlite3_exec(db, zSql, 0, 0, 0);
int rc = recoverOneStmt(db, zSql);
if( rc ){
recoverDbError(p, db);
}
@@ -943,7 +964,8 @@ static void recoverTransferSettings(sqlite3_recover *p){
}
recoverFinalize(p, p1);
}
recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;");
recoverExec(p, db2, "CREATE TABLE t1(a)");
recoverExec(p, db2, "DROP TABLE t1");
if( p->errCode==SQLITE_OK ){
sqlite3 *db = p->dbOut;
@@ -1025,12 +1047,12 @@ static int recoverOpenOutput(sqlite3_recover *p){
static void recoverOpenRecovery(sqlite3_recover *p){
char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb);
recoverExec(p, p->dbOut, zSql);
recoverExec(p, p->dbOut,
"PRAGMA writable_schema = 1;"
"CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);"
"CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);"
);
sqlite3_free(zSql);
recoverExec(p, p->dbOut, "PRAGMA writable_schema = 1");
recoverExec(p, p->dbOut,
"CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT)");
recoverExec(p, p->dbOut,
"CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql)");
}
@@ -1170,7 +1192,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){
")"
"SELECT rootpage, tbl, isVirtual, name, sql"
" FROM dbschema "
" WHERE tbl OR isIndex"
" WHERE (tbl OR isIndex) AND sql GLOB 'CREATE *'"
" ORDER BY tbl DESC, name=='sqlite_sequence' DESC"
);
@@ -1196,7 +1218,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){
zName, zName, zSql
));
}
rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
rc = recoverOneStmt(p->dbOut, zSql);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
if( bTable && !bVirtual ){
@@ -1238,15 +1260,17 @@ static int recoverWriteSchema2(sqlite3_recover *p){
p->bSlowIndexes ?
"SELECT rootpage, sql FROM recovery.schema "
" WHERE type!='table' AND type!='index'"
" AND sql GLOB 'CREATE *'"
:
"SELECT rootpage, sql FROM recovery.schema "
" WHERE type!='table' AND (type!='index' OR sql NOT LIKE '%unique%')"
" AND sql GLOB 'CREATE *'"
);
if( pSelect ){
while( sqlite3_step(pSelect)==SQLITE_ROW ){
const char *zSql = (const char*)sqlite3_column_text(pSelect, 1);
int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
int rc = recoverOneStmt(p->dbOut, zSql);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
}else if( rc!=SQLITE_ERROR ){
@@ -2624,7 +2648,7 @@ static void recoverStep(sqlite3_recover *p){
if( bUseWrapper ) recoverUninstallWrapper(p);
}while( p->errCode==SQLITE_NOTADB
&& (bUseWrapper--)
&& SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0)
&& SQLITE_OK==recoverOneStmt(p->dbIn, "ROLLBACK")
);
}
@@ -2689,7 +2713,7 @@ static void recoverStep(sqlite3_recover *p){
** database. Regardless of whether or not an error has occurred, make
** an attempt to end the read transaction on the input database. */
recoverExec(p, p->dbOut, "COMMIT");
rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0);
rc = recoverOneStmt(p->dbIn, "END");
if( p->errCode==SQLITE_OK ) p->errCode = rc;
recoverSqlCallback(p, "PRAGMA writable_schema = off");
@@ -2885,7 +2909,7 @@ int sqlite3_recover_finish(sqlite3_recover *p){
}else{
recoverFinalCleanup(p);
if( p->bCloseTransaction && sqlite3_get_autocommit(p->dbIn)==0 ){
rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0);
rc = recoverOneStmt(p->dbIn, "END");
if( p->errCode==SQLITE_OK ) p->errCode = rc;
}
rc = p->errCode;
+7 -7
View File
@@ -665,17 +665,17 @@ static unsigned int sessionChangeHash(
u8 *a = aRecord; /* Used to iterate through change record */
for(i=0; i<pTab->nCol; i++){
int eType = *a;
int isPK = pTab->abPK[i];
if( bPkOnly && isPK==0 ) continue;
assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
|| eType==SQLITE_TEXT || eType==SQLITE_BLOB
|| eType==SQLITE_NULL || eType==0
);
if( isPK ){
a++;
int eType = *a++;
assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
|| eType==SQLITE_TEXT || eType==SQLITE_BLOB
|| eType==SQLITE_NULL || eType==0
);
h = sessionHashAppendType(h, eType);
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
h = sessionHashAppendI64(h, sessionGetI64(a));
+38 -37
View File
@@ -1,5 +1,5 @@
C Disable\soptimization\sof\snested\sEXIST\sloops.\sPossible\sfix\sfor\s[forum:/forumpost/077c02e8ed\s|\sforum\spost\s77c02e8ed].
D 2026-04-21T16:40:08.614
C This\sis\san\sidea\sfor\snew\sAPIs\sto\simprove\supon\ssqlite3_set_authorizer().\nAfter\simplementing\sit,\showever,\sI'm\shaving\ssecond\sthroughts.\s\sSaved\shere\nin\scase\sI\schanged\smy\smind.
D 2026-05-01T23:56:16.497
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -358,7 +358,7 @@ F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b6
F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0
F ext/misc/README.md 6243cdc4d7eb791c41ef0716f3980b8b5f6aa8c61ff76a3958cbf0031c6ebfa7
F ext/misc/amatch.c 8d237cc014b3736922c26a76a451050d244aa4980c47c531f368f817b1e77b49
F ext/misc/analyze.c 13caf932d591d7c0ce3f0470023a5c835aa24caddec673c41d296ac89de98220
F ext/misc/analyze.c 77e0f45a44c13d66f30922db553072305521c0fb3a448b920e641fccd2498527
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
F ext/misc/appendvfs.c 9642c7a194a2a25dca7ad3e36af24a0a46d7702168c4ad7e59c9f9b0e16a3824
F ext/misc/base64.c 1445761667c16356e827fc6418294c869468be934429aaa8315035e76dd58acf
@@ -422,7 +422,7 @@ F ext/misc/zipfile.c 5a583b5e72b4d777dc9f845529e6bd185d58024b633aafc93588679c787
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
F ext/qrf/README.md 9e644615d7d7b77ef7e9db798765679e50c5ed12eda48bce21c9ef9eb4715e9d
F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430
F ext/qrf/qrf.c 64203184d9fc6ee75439fb6da70fd51dc7aaf3243600e52aa87d18ea1e4d6e73
F ext/qrf/qrf.c 9bd4ad396e75a7905b1eb956ac53fd93f77942963c5ffac176ae39880368553b
F ext/qrf/qrf.h fbb223ff5789b324b3e9c22e787e4c1f53e217cff7cc5a243164d4b2e8410f4b
F ext/rbu/rbu.c 801450b24eaf14440d8fd20385aacc751d5c9d6123398df41b1b5aa804bf4ce8
F ext/rbu/rbu1.test 25870dd7db7eb5597e2b4d6e29e7a7e095abf332660f67d89959552ce8f8f255
@@ -487,7 +487,7 @@ F ext/recover/recoverpgsz.test 88766fcb810e52ee05335c456d4e5fb06d02b73d3ccb48c52
F ext/recover/recoverrowid.test f948bf4024a5f41b0e21b8af80c60564c5b5d78c05a8d64fc00787715ff9f45f
F ext/recover/recoverslowidx.test c90d59c46bb8924a973ac6fbc38f3163cee38cc240256addcab1cf1a322c37dc
F ext/recover/recoversql.test e66d01f95302a223bcd3fd42b5ee58dc2b53d70afa90b0d00e41e4b8eab20486
F ext/recover/sqlite3recover.c 56c216332ea91233d6d820d429f3384adbec9ecedda67aa98186b691d427cc57
F ext/recover/sqlite3recover.c dfbfca5a8e4909333e5aa3749bcffb5dd7e396de9541c338f8cc843b2e20217e
F ext/recover/sqlite3recover.h 011c799f02deb70ab685916f6f538e6bb32c4e0025e79bfd0e24ff9c74820959
F ext/recover/test_recover.c 3d0fb1df7823f5bc22a0b93955034d16a2dfa2eb1e443e9a0123a77f120599a3
F ext/rtree/README 734aa36238bcd2dee91db5dba107d5fcbdb02396612811377a8ad50f1272b1c1
@@ -572,7 +572,7 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
F ext/session/sqlite3session.c 871d8a4574bfc682ca0816efb55c85c5fea048e0becf9367a4b271d6a4474b2f
F ext/session/sqlite3session.c 48b5585ea444c9646294d86f16ad3efa28dd19632dd3e295557c1ab40c447a4c
F ext/session/sqlite3session.h 063e7bf7be2fff874456f452a224b5b3013b25682d108933b0351c93a1279b9c
F ext/session/test_session.c 2a02a68b522e2f3d4a64b2a4733af54b0f3e500769aeccd5bcbdd440103db069
F ext/wasm/GNUmakefile 68c750f173106d9d63f12c1edf1256c6f4bad9894b155da5db64322f4912de4b
@@ -668,11 +668,11 @@ F mptest/multiwrite01.test dab5c5f8f9534971efce679152c5146da265222d
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 1b9c24374a85dfc7eb8fa7c4266ee0db4f9609cceecfc5481cd8307e5af04366
F sqlite3.pc.in e6dee284fba59ef500092fdc1843df3be8433323a3733c91da96690a50a5b398
F src/alter.c e257c51ac9301b3d72be4e1ac39c3c78528aad83194d963a075351344fbc14c7
F src/alter.c 7d7ddbdc189f0e0c686e32ee170abdddc95c11f2089e40df4ffcee88f5334826
F src/analyze.c 03bcfc083fc0cccaa9ded93604e1d4244ea245c17285d463ef6a60425fcb247d
F src/attach.c 7cf07d4fa42b9fc8662237c60c40b730326c30aa90ae5fffc0b18b2d726ebf61
F src/auth.c ebec42df26b34a62b6750d30d9c2c03554a1c522020182476f7729a439fef04f
F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
F src/attach.c c58278c7d2d954785591c4fde81669ec3e4d52f348c453b028a19ae8adf4f338
F src/auth.c ca42996e133f1b7b50447b54291db6a5e2b7c8fbca015235c101dcc97e719917
F src/backup.c 6ebe22ccbedfcb92423833992130e8d65824be4e6599c3a03f540ab38fc7d13c
F src/bitvec.c e242d4496774dfc88fa278177dd23b607dce369ccafb3f61b41638eea2c9b399
F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/btree.c 216ffbe197e330118a2999adc7d3f09b0e2eeb163df8746ba9a2b27fed3d4335
@@ -685,7 +685,7 @@ F src/complete.c f216b970ce99c5a657556cf1f17e7ddd494515d3beb63df426bf59ff43bd3d9
F src/date.c 61e92f1f7e2e88e1cd91e91dc69eb2b2854e7877254470f9fabd776bfac922b8
F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 1f2268d6fe3c78fc1bf794ba65d7026498b78e2342ffaf85825dedae546e6fde
F src/delete.c 59eeca3fb88c29329afc41bb803ee568b120d9dd7470b5f38ab55cc38390b451
F src/expr.c 68400681c5f6e41231d2c85abf6bb432aeeb2e36c4abdf90eb7b78551a5ce0f3
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
@@ -695,25 +695,25 @@ F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
F src/hwtime.h 21c2cf1f736e7b97502c3674d0c386db3f06870d6f10d0cf8174e2a4b8cb726e
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c dfd311b0ac2d4f6359e62013db67799757f4d2cc56cca5c10f4888acfbbfa3fd
F src/insert.c 77aa90e7ddbebf131c5de504854b5a61c25b11d53580649fb375b23752793651
F src/json.c 5027b856cd9b621dc9ba66b211e21a440ccdc63cefdefb44c51e7d3ac550d1a4
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 78d5b06f18996ffa1203129b28fea043f63a87a4117539678f1d761c30b4ff65
F src/main.c 6180079f53ccdd784df2eddc3751f49ea7153c5959bee792b19ad9f4bdbcf437
F src/main.c 022f66a3adb94db148c4ad9ff4357a4d4586fea0c0546f8b91f745bd54a3529c
F src/malloc.c 422f7e0498e1c9ef967f06283b6f2c0b16db6b905d8e06f6dbc8baaa3e4e6c5a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
F src/mem5.c b7da5c10a726aacacc9ad7cdcb0667deec643e117591cc69cf9b4b9e7f3e96ff
F src/memdb.c a3feb427cdd4036ea2db0ba56d152f14c8212ca760ccb05fb7aa49ff6b897df3
F src/memdb.c 0a0e827311cec57fdea2ed3142d6684704a1b90e7995b286f74fd7d1a6c5a62c
F src/memjournal.c c283c6c95d940eb9dc70f1863eef3ee40382dbd35e5a1108026e7817c206e8a0
F src/msvc.h 80b35f95d93bf996ccb3e498535255f2ef1118c78764719a7cd15ab4106ccac9
F src/mutex.c 00b8cee206a67fd764d001f3a148494331d8d0b3b9c3974ecd69ff29bb444462
F src/mutex.h a7b2293c48db5f27007c3bdb21d438873637d12658f5a0bf8ad025bb96803c4a
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c f7ee5a2061a4c11815a2bf4fc0e2bfa6fb8d9dc89390eb613ca0cec32fc9a3d1
F src/mutex_w32.c e1d317d29cb623667d43de94714264d1e1871cc4bb39fa67dd17048e8138c739
F src/mutex_unix.c caec9862aeebad01f6c8997cf709a239abeb0715fcce1895720b87fe7e2ef42f
F src/mutex_w32.c d44e94c064f8ab39e0318fb6fa00171cf7e5645bd4241129b07d655a8ddbd44b
F src/notify.c 57c2d1a2805d6dee32acd5d250d928ab94e02d76369ae057dee7d445fd64e878
F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d
F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
@@ -721,11 +721,11 @@ F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e
F src/os_kv.c e7d96727db5b67e39d590a68cc61c86daf4c093c36c011a09ebfb521182ec28d
F src/os_setup.h 8efc64eda6a6c2f221387eefc2e7e45fd5a3d5c8337a7a83519ba4fbd2957ae2
F src/os_unix.c a07dce662f6c4e18098f6faa9f7ec7cf311f56ee9151bed2aad4dcd55852c9e2
F src/os_win.c 0d553b6e8b92c8eb85e7f1b4a8036fe8638c8b32c9ad8d9d72a861c10f81b4c5
F src/os_win.h 5e168adf482484327195d10f9c3bce3520f598e04e07ffe62c9c5a8067c1037b
F src/os_win.c 4786b1123beea22a9cd8f8e9d781cc1b4bf4679c56103edbf60a519ce23a2781
F src/os_win.h c06ccc3a090cf54202ea58981c298817f3309d4c9e4d52ad0a02927346493721
F src/pager.c fbec9063ea139dfa5d94ce540671752b89f8e8dc38f8a1f614bab1aa04a2dd40
F src/pager.h 6137149346e6c8a3ddc1eeb40aee46381e9bc8b0fcc6dda8a1efde993c2275b8
F src/parse.y 3b784d6083380a950e3b1b32ce5ddd303e8c7c209d8ab788df2c62aaf9ee8eb3
F src/parse.y 8c8003a932f6f98ac612fd239d3cdc7ca64480f61ca5f4f680c4fe69b1bc68c9
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F src/pcache.h 092b758d2c5e4dabb30eae46d8dfad77c0f70b16bf3ff1943f7a232b0fe0d4ba
F src/pcache1.c 131ca0daf4e66b4608d2945ae76d6ed90de3f60539afbd5ef9ec65667a5f2fcd
@@ -736,11 +736,11 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 928ff887f2a7c64275182060d94d06fdddbe32226c569781cf7e7edc6f58d7fd
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49
F src/shell.c.in 4e68371fec5a8ab31038d51e7179d9ef3539d693e05a5fdabcd59483e445b27d
F src/sqlite.h.in 39d2e09114d2bdb7afd998f4a469c8f8cd065f8093835a7d0422f260fc78fb4f
F src/shell.c.in 3ed0902de2a82c92ee535c1bc627fb7cfd64cc3b81762f1c10b59c634597da10
F src/sqlite.h.in fa079952ae30eb0921e3b5eb11e4c73741d7dee86d202b2fa7e4c688d6f9b0cf
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e
F src/sqliteInt.h 1e9df4f7f0a754cebbc5e1494ff74b54bf510046b800db1d5382393972f53499
F src/sqliteInt.h 5071d64ad787d9922e524d834c3fe7b7a11989d29dc6ea25f7944892a43266f4
F src/sqliteLimit.h c70656b67ab5b96741a8f1c812bdd80c81f2b1c1e443d0cc3ea8c33bb1f1a092
F src/status.c 7565d63a79aa2f326339a24a0461a60096d0bd2bce711fefb50b5c89335f3592
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -800,9 +800,9 @@ F src/trigger.c 4bf3bfb3851d165e4404a9f9e69357345f3f7103378c07e07139fdd8aeb7bd20
F src/update.c 3e5e7ff66fa19ebe4d1b113d480639a24cc1175adbefabbd1a948a07f28e37cf
F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 7267c3fb9e2467020507601af3354c2446c61f444387e094c779dccd5ca62165
F src/util.c 377af5da226519a0f374dc3c6d408c9d303a92943e3ae5986432c7d52e6679a2
F src/util.c 7c85ba0cdf97f91b57ec9d45f9852aaa4f61e3b7e7cc36d197ee354ad44cdba2
F src/vacuum.c d3d35d8ae893d419ade5fa196d761a83bddcbb62137a1a157ae751ef38b26e82
F src/vdbe.c 6c57525d7db0232d52687d30da1093db0c152f14206c2ef1adf0c19a09d863e3
F src/vdbe.c 1cd55e8171e4e87054de196c3c4747d44b69041b5ec910cf547ae82a85beb3e4
F src/vdbe.h 70e862ac8a11b590f8c1eaac17a0078429d42bc4ea3f757a9af0f451dd966a71
F src/vdbeInt.h c31ba4dc8d280c2b1dc89c6fcee68f2555e3813ab34279552c20b964c0e338b1
F src/vdbeapi.c 6cdcbe5c7afa754c998e73d2d5d2805556268362914b952811bdfb9c78a37cf1
@@ -839,7 +839,7 @@ F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256b
F test/alterauth2.test 4b74fa8f184f4736497317feb587b65759eb87d87acfe3a8ef433d4d18bb002b
F test/altercol.test 3661c432aacb42bc2198dd4611bbb9c3b09fc73251b59edda046109103b8ac00
F test/altercons.test ea18def4a0f26b9066da56095c9c480df705df4d02e4ae151708fae76f7e3884
F test/altercons2.test ade124ce5c733d7d939a5b602946eaf4a1825c7e355319a2b00bcc0b34aafe3f
F test/altercons2.test 4933eadb035fc0ccfc34ed879f69a09051c53907f9dae9c3d36d25f93519ccc7
F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12
F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e
F test/alterdropcol2.test 527fce683b200d620f560f666c44ae33e22728e990a10a48a543280dfd4b4d41
@@ -1105,9 +1105,10 @@ F test/existsexpr2.test dc23e76389eff3d29f6488ff733012a3560cd67ec8cfaecbecd52cce
F test/existsfault.test ff41c11f3052c1bbd4f8dd557802310026253d67d7c4e3a180c16d2f0862973e
F test/expr.test db981f8a85520e99ae20aab7ad2e9b5b0437ed09159b57ced434c672075d2e61
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
F test/exprfault.test da33606d799718e2f8e34efd0e5858884a1ad87f608774c552a7f5517cc27181
F test/exprfault.test 7475e363c7221847855722e9dd2459cf60088f6b172c0c808dba75f89e1704b8
F test/exprfault2.test c49e84273898969af5dbc4fe6a3f4335f14639799f343590336c9ddf84425965
F test/expridx1.test b464520126e1d781a7800f8540621c82e8bbc526f089ff8b0b57ffc51feea6b7
F test/expridx2.test 13348ed208a5d18d9cef7d0a70f8e17948d3c0b5b46508c0573b7e983456165f
F test/extension01.test 5de412c66276105901c370770175003381fdcb0c4da7054fa43cf4a31e0bfa3a
F test/external_reader.test 6fdec43eeca23eb32faad1e95a4d1abc402bc8b3db70df12d6fc08a637f4a2b5
F test/extraquick.test cb254400bd42bfb777ff675356aabf3287978f79
@@ -1423,7 +1424,7 @@ F test/malloctraceviewer.tcl 3e3ddf11e30d2b20f53aa16aa6615082fb24a100bea61cca721
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
F test/memdb.test c1f2a343ad14398d5d6debda6ea33e80d0dafcc7
F test/memdb1.test c737ac9aa5895092332b1dde24fae7ae494b7fcbcd346d22d600891096a3836d
F test/memdb1.test 413f845c0c2d20dc9808d4084de9e57d55759070eed2a4cf23cd30335a96c295
F test/memdb2.test 4ba1fc09e2f51df80d148a540e4a3fa66d0462e91167b27497084de4d1f6b5b4
F test/memjournal.test 70f3a00c7f84ee2978ad14e831231caa1e7f23915a2c54b4f775a021d5740c6c
F test/memjournal2.test dbc2c5cb5f7b38950f4f6dc3e73fcecf0fcbed3fc32c7ce913bba164d288da1e
@@ -1623,7 +1624,7 @@ F test/sharedA.test 64bdd21216dda2c6a3bd3475348ccdc108160f34682c97f2f51c19fc0e21
F test/sharedB.test 1a84863d7a2204e0d42f2e1606577c5e92e4473fa37ea0f5bdf829e4bf8ee707
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell-prompt.sql 43ed98433bfde83171b5d06c300525154f4cab9230570a326ca606aaef351db6
F test/shell-prompt.sql 868ca1711677ada45720532fe23dbbbe881f3612dc29692dac45b19129648b3f
F test/shell1.test c84eff209f93ad17ccdf7e1634969fc8231684254edeb21d9b13d67c3179cdb5
F test/shell2.test dc541d2681503e55466a24d35a4cbf8ca5b90b8fcdef37fc4db07373a67d31d3
F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012db2
@@ -1719,7 +1720,7 @@ F test/temptrigfault.tes fc5918e64f3867156fefe7cfca9d8e1f495134a5229b2b511b0dc11
F test/temptrigger.test a00f258ed8d21a0e8fd4f322f15e8cfb5cef2e43655670e07a753e3fb4769d61
F test/tester.tcl 2d943f60200e0a36bcd3f1f0baf181a751cd3604ef6b6bd4c8dc39b4e8a53116
F test/testloadext.c 862b848783eaed9985fbce46c65cd214664376b549fae252b364d5d1ef350a27
F test/testrunner.tcl 1ad787b744277532521a2a9898ac66ee6a8363fbcc1211027db0dadad4ee3214 x
F test/testrunner.tcl 6d13732185c13aef87540ebf9daafb8d9657e66f0ac00eb082d93f4749311d4e x
F test/testrunner_data.tcl dfcf192d274e965845189cc014ac89fff91dde92b6e2ac9e1262897fc21ee2e0
F test/testrunner_estwork.tcl 81e2ae10238f50540f42fbf2d94913052a99bfb494b69e546506323f195dcff9
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
@@ -1927,7 +1928,7 @@ F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
F test/unixexcl.test d2366ef2d3d95249314307861d748924d9ab4f24305541159a08be61ccd4a9ee
F test/unordered.test 0edaf3411d300693bca595897c5201421c6c5ec787990a1dfe2f7f60ae93f1e2
F test/update.test 258dcf26d401177d3cb7fdf0beab14d671dbe72e8ff6e0435fd85a08fcd57bd9
F test/update.test 93fba5f7505f7a7b25d66135d98d9b055b7f1bd627a7e514dbba1ea6213f0c41
F test/update2.test 67455bc61fcbcf96923c45b3bc4f87bc72be7d67575ad35f134906148c7b06d3
F test/upfrom1.tcl 8859d9d437f03b44174c4524a7a734a391fd4526fcff65be08285dafc9dc9041
F test/upfrom1.test 8cb06689e99cd707d884faa16da0e8eb26ff658bb01c47ddf72fadade666e6e1
@@ -2181,7 +2182,7 @@ F tool/soak1.tcl a3892082ed1079671565c044e93b55c3c7f38829aedf53cc597c65d23ffdadd
F tool/spaceanal.tcl 1f83962090a6b60e1d7bf92495d643e622bef9fe82ea3f2d22350dcbce9a12d0
F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d40618 x
F tool/split-sqlite3c.tcl 4969fd642dad0ea483e4e104163021d92baf98f6a8eac981fe48525f9b873430
F tool/sqldiff.c 847edc1e0d1e1feb652d3d6128e504456deaf254ab9ad3e7cebd4317d2037182
F tool/sqldiff.c de59a4eecbc802db8ad053889c10e6f8b8427b6f349199ee79938c92c20860dd
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
F tool/sqlite3_rsync.c f510a8b230e1c5b0f62842acd0e94ff15d2f77a00ae782f7d20f9e39919fa19b
F tool/sqltclsh.c.in c103c6fc7d42bce611f9d4596774d60b7ef3d0b291a1f58c9e6184e458b89296
@@ -2202,11 +2203,11 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
P 24df42104d5fd1cf974f2bf539fa7515808e21e68d64899c39f5fcf3bee49a48
R 9a3da3dbb0fb1d6e93abd196b6a44c9b
T *branch * disable-nested-exist-opt
T *sym-disable-nested-exist-opt *
P fc64113e8f9aee0161e682db7c244a7849f2db86551a557b276deb1785941476
R 5c538d610a39b9c009307badcddf6e4d
T *branch * push-pop-authorizer
T *sym-push-pop-authorizer *
T -sym-trunk *
U dan
Z 5d80e9245f4b6dbd6635f95d676dfd7d
U drh
Z a0686f1cab4b032dd5f99fc3c9d92233
# Remove this line to create a well-formed Fossil manifest.
+2 -2
View File
@@ -1,2 +1,2 @@
branch disable-nested-exist-opt
tag disable-nested-exist-opt
branch push-pop-authorizer
tag push-pop-authorizer
+1 -1
View File
@@ -1 +1 @@
64f0d9eb63a351de12bb9643f1f6390a89f6d95dc81c07f1dc409ee31bae6f83
c3c3d9731535650ba1bcac8709ead27ce037736b31b7efef37422b502e10d332
+3 -2
View File
@@ -2798,8 +2798,9 @@ void sqlite3AlterDropConstraint(
if( !pTab ) return;
if( pCons ){
sqlite3DequoteToken(pCons);
zArg = sqlite3MPrintf(db, "%.*Q", pCons->n, pCons->z);
char *z = sqlite3NameFromToken(db, pCons);
zArg = sqlite3MPrintf(db, "%Q", z);
sqlite3DbFree(db, z);
}else{
int iCol;
if( alterFindCol(pParse, pTab, pCol, &iCol) ) return;
+11 -2
View File
@@ -106,6 +106,16 @@ static void attachFunc(
** from sqlite3_deserialize() to close database db->init.iDb and
** reopen it as a MemDB */
Btree *pNewBt = 0;
pNew = &db->aDb[db->init.iDb];
assert( pNew->pBt!=0 );
if( sqlite3BtreeTxnState(pNew->pBt)!=SQLITE_TXN_NONE
|| sqlite3BtreeIsInBackup(pNew->pBt)
){
rc = SQLITE_BUSY;
goto attach_error;
}
pVfs = sqlite3_vfs_find("memdb");
if( pVfs==0 ) return;
rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNewBt, 0, SQLITE_OPEN_MAIN_DB);
@@ -115,8 +125,7 @@ static void attachFunc(
/* Both the Btree and the new Schema were allocated successfully.
** Close the old db and update the aDb[] slot with the new memdb
** values. */
pNew = &db->aDb[db->init.iDb];
if( ALWAYS(pNew->pBt) ) sqlite3BtreeClose(pNew->pBt);
sqlite3BtreeClose(pNew->pBt);
pNew->pBt = pNewBt;
pNew->pSchema = pNewSchema;
}else{
+88
View File
@@ -257,4 +257,92 @@ void sqlite3AuthContextPop(AuthContext *pContext){
}
}
/*
** There is a single instance of this object for each nested
** authorizer.
*/
typedef struct NestedAuth {
int (*xPriorAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pPriorArg;
int (*xThisAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pThisArg;
int bOvrd;
} NestedAuth;
/*
** This is an authorizer implementation that invokes two separate
** authorizers and returns the most restrictive answer.
**
** The pArg value is a pointer to an sqlite3_auth object that contains
** the function pointer and augument for two other (real) authorizers.
** This routine runs both of the nested authorizers. If either returns
** SQLITE_DENY, this routine returns SQLITE_DENY. If neither returns
** SQLITE_DENY but either of them return SQLITE_IGNORE, then this
** routine returns SQLITE_IGNORE. Only if both authorizers return
** SQLITE_OK does this routine return SQLITE_OK.
*/
static int sqlite3NestedAuthorizer(
void *pArg, /* Must be an sqlite3_auth object */
int eOp, /* Operation to be authorized */
const char *z1, /* First argument */
const char *z2, /* Second argument */
const char *z3, /* Third argument */
const char *z4 /* Fourth argument */
){
NestedAuth *p = (NestedAuth*)pArg;
int rc1, rc2;
if( p->xThisAuth==0 ){
rc1 = SQLITE_OK;
/* If the top-most authorizer is a NULL, then do no perform
** any other authorizations. */
}else{
rc1 = p->xThisAuth(p->pThisArg,eOp,z1,z2,z3,z4);
if( rc1==SQLITE_DENY || p->bOvrd ) return rc1;
}
assert( p->xPriorAuth!=0 );
rc2 = p->xPriorAuth(p->pPriorArg,eOp,z1,z2,z3,z4);
if( rc2 ) return rc2;
return rc1;
}
/*
** Push a new authorizer function xAuth with pArg. This new authorizer
** runs first before any prior authorizer.
*/
int sqlite3_push_authorizer(
sqlite3 *db,
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pArg,
int bOvrd
){
NestedAuth *p;
if( db==0 ) return SQLITE_MISUSE;
if( db->xAuth==0 ){
return sqlite3_set_authorizer(db, xAuth, pArg);
}
p = sqlite3DbMallocZero(db, sizeof(NestedAuth));
if( p==0 ) return SQLITE_NOMEM;
p->xPriorAuth = db->xAuth;
p->pPriorArg = db->pAuthArg;
p->xThisAuth = xAuth;
p->pThisArg = pArg;
p->bOvrd = bOvrd;
return sqlite3_set_authorizer(db, sqlite3NestedAuthorizer, p);
}
/*
** Pop the top-most authorizer off of the authorizer stack.
*/
int sqlite3_pop_authorizer(sqlite3 *db){
if( db==0 ) return SQLITE_MISUSE;
if( db->xAuth==sqlite3NestedAuthorizer ){
NestedAuth *p = (NestedAuth*)db->pAuthArg;
(void)sqlite3_set_authorizer(db, p->xPriorAuth, p->pPriorArg);
sqlite3DbFree(db, p);
}else{
(void)sqlite3_set_authorizer(db, 0, 0);
}
return SQLITE_OK;
}
#endif /* SQLITE_OMIT_AUTHORIZATION */
+66 -54
View File
@@ -20,13 +20,13 @@
*/
struct sqlite3_backup {
sqlite3* pDestDb; /* Destination database handle */
Btree *pDest; /* Destination b-tree file */
Db *pDest; /* Destination db file */
u32 iDestSchema; /* Original schema cookie in destination */
int bDestLocked; /* True once a write-transaction is open on pDest */
Pgno iNext; /* Page number of the next source page to copy */
sqlite3* pSrcDb; /* Source database handle */
Btree *pSrc; /* Source b-tree file */
Db *pSrc; /* Source db file */
int rc; /* Backup process error code */
@@ -79,7 +79,7 @@ struct sqlite3_backup {
** function. If an error occurs while doing so, return 0 and write an
** error message to pErrorDb.
*/
static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
static Db *findDatabase(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
int i = sqlite3FindDbName(pDb, zDb);
if( i==1 ){
@@ -102,7 +102,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
return 0;
}
return pDb->aDb[i].pBt;
return &pDb->aDb[i];
}
/*
@@ -110,9 +110,9 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
** of the source.
*/
static int setDestPgsz(sqlite3_backup *p){
int rc;
rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
return rc;
return sqlite3BtreeSetPageSize(p->pDest->pBt,
sqlite3BtreeGetPageSize(p->pSrc->pBt), 0, 0
);
}
/*
@@ -181,15 +181,15 @@ sqlite3_backup *sqlite3_backup_init(
/* If the allocation succeeded, populate the new object. */
if( p ){
p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
p->pSrc = findDatabase(pDestDb, pSrcDb, zSrcDb);
p->pDest = findDatabase(pDestDb, pDestDb, zDestDb);
p->pDestDb = pDestDb;
p->pSrcDb = pSrcDb;
p->iNext = 1;
p->isAttached = 0;
if( 0==p->pSrc || 0==p->pDest
|| checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
|| checkReadTransaction(pDestDb, p->pDest->pBt)!=SQLITE_OK
){
/* One (or both) of the named databases did not exist or an OOM
** error was hit. Or there is a transaction open on the destination
@@ -201,7 +201,7 @@ sqlite3_backup *sqlite3_backup_init(
}
}
if( p ){
p->pSrc->nBackup++;
p->pSrc->pBt->nBackup++;
}
sqlite3_mutex_leave(pDestDb->mutex);
@@ -229,18 +229,18 @@ static int backupOnePage(
const u8 *zSrcData, /* Source database page data */
int bUpdate /* True for an update, false otherwise */
){
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
Pager * const pDestPager = sqlite3BtreePager(p->pDest->pBt);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc->pBt);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest->pBt);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
int rc = SQLITE_OK;
i64 iOff;
assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( sqlite3BtreeGetReserveNoMutex(p->pSrc->pBt)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt->pBt) );
assert( zSrcData );
assert( nSrcPgsz==nDestPgsz || sqlite3PagerIsMemdb(pDestPager)==0 );
@@ -251,7 +251,7 @@ static int backupOnePage(
for(iOff=iEnd-(i64)nSrcPgsz; rc==SQLITE_OK && iOff<iEnd; iOff+=nDestPgsz){
DbPage *pDestPg = 0;
Pgno iDest = (Pgno)(iOff/nDestPgsz)+1;
if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue;
if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt->pBt) ) continue;
if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0))
&& SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg))
){
@@ -269,7 +269,7 @@ static int backupOnePage(
memcpy(zOut, zIn, nCopy);
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
if( iOff==0 && bUpdate==0 ){
sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc->pBt));
}
}
sqlite3PagerUnref(pDestPg);
@@ -301,8 +301,8 @@ static int backupTruncateFile(sqlite3_file *pFile, i64 iSize){
*/
static void attachBackupObject(sqlite3_backup *p){
sqlite3_backup **pp;
assert( sqlite3BtreeHoldsMutex(p->pSrc) );
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
assert( sqlite3BtreeHoldsMutex(p->pSrc->pBt) );
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc->pBt));
p->pNext = *pp;
*pp = p;
p->isAttached = 1;
@@ -316,20 +316,26 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
int destMode; /* Destination journal mode */
int pgszSrc = 0; /* Source page size */
int pgszDest = 0; /* Destination page size */
Btree *pDest;
Btree *pSrc;
#ifdef SQLITE_ENABLE_API_ARMOR
if( p==0 ) return SQLITE_MISUSE_BKPT;
#endif
assert( p->pDest );
assert( p->pSrc );
pDest = p->pDest->pBt;
pSrc = p->pSrc->pBt;
sqlite3_mutex_enter(p->pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
sqlite3BtreeEnter(pSrc);
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
rc = p->rc;
if( !isFatalError(rc) ){
Pager * const pSrcPager = sqlite3BtreePager(p->pSrc); /* Source pager */
Pager * const pDestPager = sqlite3BtreePager(p->pDest); /* Dest pager */
Pager * const pSrcPager = sqlite3BtreePager(pSrc); /* Source pager */
Pager * const pDestPager = sqlite3BtreePager(pDest); /* Dest pager */
int ii; /* Iterator variable */
int nSrcPage = -1; /* Size of source db in pages */
int bCloseTrans = 0; /* True if src db requires unlocking */
@@ -337,7 +343,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* If the source pager is currently in a write-transaction, return
** SQLITE_BUSY immediately.
*/
if( p->pDestDb && p->pSrc->pBt->inTransaction==TRANS_WRITE ){
if( p->pDestDb && pSrc->pBt->inTransaction==TRANS_WRITE ){
rc = SQLITE_BUSY;
}else{
rc = SQLITE_OK;
@@ -347,8 +353,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** one now. If a transaction is opened here, then it will be closed
** before this function exits.
*/
if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(p->pSrc) ){
rc = sqlite3BtreeBeginTrans(p->pSrc, 0, 0);
if( rc==SQLITE_OK && SQLITE_TXN_NONE==sqlite3BtreeTxnState(pSrc) ){
rc = sqlite3BtreeBeginTrans(pSrc, 0, 0);
bCloseTrans = 1;
}
@@ -364,7 +370,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Lock the destination database, if it is not locked already. */
if( SQLITE_OK==rc && p->bDestLocked==0
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2,
&& SQLITE_OK==(rc = sqlite3BtreeBeginTrans(pDest, 2,
(int*)&p->iDestSchema))
){
p->bDestLocked = 1;
@@ -372,9 +378,9 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Do not allow backup if the destination database is in WAL mode
** and the page sizes are different between source and destination */
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
pgszDest = sqlite3BtreeGetPageSize(p->pDest);
destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(p->pDest));
pgszSrc = sqlite3BtreeGetPageSize(pSrc);
pgszDest = sqlite3BtreeGetPageSize(pDest);
destMode = sqlite3PagerGetJournalMode(sqlite3BtreePager(pDest));
if( SQLITE_OK==rc
&& (destMode==PAGER_JOURNALMODE_WAL || sqlite3PagerIsMemdb(pDestPager))
&& pgszSrc!=pgszDest
@@ -385,11 +391,11 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Now that there is a read-lock on the source database, query the
** source pager for the number of pages in the database.
*/
nSrcPage = (int)sqlite3BtreeLastPage(p->pSrc);
nSrcPage = (int)sqlite3BtreeLastPage(pSrc);
assert( nSrcPage>=0 );
for(ii=0; (nPage<0 || ii<nPage) && p->iNext<=(Pgno)nSrcPage && !rc; ii++){
const Pgno iSrcPg = p->iNext; /* Source page number */
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
if( iSrcPg!=PENDING_BYTE_PAGE(pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
@@ -416,18 +422,18 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
if( rc==SQLITE_DONE ){
if( nSrcPage==0 ){
rc = sqlite3BtreeNewDb(p->pDest);
rc = sqlite3BtreeNewDb(pDest);
nSrcPage = 1;
}
if( rc==SQLITE_OK || rc==SQLITE_DONE ){
rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
rc = sqlite3BtreeUpdateMeta(pDest,1,p->iDestSchema+1);
}
if( rc==SQLITE_OK ){
if( p->pDestDb ){
sqlite3ResetAllSchemasOfConnection(p->pDestDb);
}
if( destMode==PAGER_JOURNALMODE_WAL ){
rc = sqlite3BtreeSetVersion(p->pDest, 2);
rc = sqlite3BtreeSetVersion(pDest, 2);
}
}
if( rc==SQLITE_OK ){
@@ -444,12 +450,12 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** journalled by PagerCommitPhaseOne() before they are destroyed
** by the file truncation.
*/
assert( pgszSrc==sqlite3BtreeGetPageSize(p->pSrc) );
assert( pgszDest==sqlite3BtreeGetPageSize(p->pDest) );
assert( pgszSrc==sqlite3BtreeGetPageSize(pSrc) );
assert( pgszDest==sqlite3BtreeGetPageSize(pDest) );
if( pgszSrc<pgszDest ){
int ratio = pgszDest/pgszSrc;
nDestTruncate = (nSrcPage+ratio-1)/ratio;
if( nDestTruncate==(int)PENDING_BYTE_PAGE(p->pDest->pBt) ){
if( nDestTruncate==(int)PENDING_BYTE_PAGE(pDest->pBt) ){
nDestTruncate--;
}
}else{
@@ -477,7 +483,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
assert( pFile );
assert( nDestTruncate==0
|| (i64)nDestTruncate*(i64)pgszDest >= iSize || (
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
nDestTruncate==(int)(PENDING_BYTE_PAGE(pDest->pBt)-1)
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
));
@@ -489,7 +495,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** journal file. */
sqlite3PagerPagecount(pDestPager, &nDstPage);
for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
if( iPg!=PENDING_BYTE_PAGE(pDest->pBt) ){
DbPage *pPg;
rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0);
if( rc==SQLITE_OK ){
@@ -533,7 +539,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(pDest, 0))
){
rc = SQLITE_DONE;
}
@@ -547,8 +553,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
if( bCloseTrans ){
TESTONLY( int rc2 );
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(pSrc, 0);
TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(pSrc, 0);
assert( rc2==SQLITE_OK );
}
@@ -560,7 +566,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( p->pDestDb ){
sqlite3_mutex_leave(p->pDestDb->mutex);
}
sqlite3BtreeLeave(p->pSrc);
sqlite3BtreeLeave(pSrc);
sqlite3_mutex_leave(p->pSrcDb->mutex);
return rc;
}
@@ -577,17 +583,17 @@ int sqlite3_backup_finish(sqlite3_backup *p){
if( p==0 ) return SQLITE_OK;
pSrcDb = p->pSrcDb;
sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
sqlite3BtreeEnter(p->pSrc->pBt);
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
/* Detach this backup from the source pager. */
if( p->pDestDb ){
p->pSrc->nBackup--;
p->pSrc->pBt->nBackup--;
}
if( p->isAttached ){
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc->pBt));
assert( pp!=0 );
while( *pp!=p ){
pp = &(*pp)->pNext;
@@ -597,7 +603,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
}
/* If a transaction is still open on the Btree, roll it back. */
sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
sqlite3BtreeRollback(p->pDest->pBt, SQLITE_OK, 0);
/* Set the error code of the destination database handle. */
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
@@ -607,7 +613,7 @@ int sqlite3_backup_finish(sqlite3_backup *p){
/* Exit the mutexes and free the backup context structure. */
sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
sqlite3BtreeLeave(p->pSrc->pBt);
if( p->pDestDb ){
/* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
@@ -665,7 +671,7 @@ static SQLITE_NOINLINE void backupUpdate(
){
assert( p!=0 );
do{
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
assert( sqlite3_mutex_held(p->pSrc->pBt->pBt->mutex) );
if( !isFatalError(p->rc) && iPage<p->iNext ){
/* The backup process p has already copied page iPage. But now it
** has been modified by a transaction on the source pager. Copy
@@ -701,7 +707,7 @@ void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
void sqlite3BackupRestart(sqlite3_backup *pBackup){
sqlite3_backup *p; /* Iterator variable */
for(p=pBackup; p; p=p->pNext){
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
assert( sqlite3_mutex_held(p->pSrc->pBt->pBt->mutex) );
p->iNext = 1;
}
}
@@ -719,6 +725,8 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
int rc;
sqlite3_file *pFd; /* File descriptor for database pTo */
sqlite3_backup b;
Db dbDest;
Db dbSrc;
sqlite3BtreeEnter(pTo);
sqlite3BtreeEnter(pFrom);
@@ -737,9 +745,13 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
** from this function, not directly by the user.
*/
memset(&b, 0, sizeof(b));
memset(&dbDest, 0, sizeof(dbDest));
memset(&dbSrc, 0, sizeof(dbSrc));
dbDest.pBt = pTo;
dbSrc.pBt = pFrom;
b.pSrcDb = pFrom->db;
b.pSrc = pFrom;
b.pDest = pTo;
b.pSrc = &dbSrc;
b.pDest = &dbDest;
b.iNext = 1;
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
@@ -755,7 +767,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
if( rc==SQLITE_OK ){
pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
}else{
sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
sqlite3PagerClearCache(sqlite3BtreePager(pTo));
}
assert( sqlite3BtreeTxnState(pTo)!=SQLITE_TXN_WRITE );
+6 -3
View File
@@ -915,6 +915,7 @@ void sqlite3GenerateRowIndexDelete(
v = pParse->pVdbe;
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
int p3 = 0;
assert( iIdxCur+i!=iDataCur || pPk==pIdx );
if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
if( pIdx==pPk ) continue;
@@ -922,10 +923,12 @@ void sqlite3GenerateRowIndexDelete(
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
&iPartIdxLabel, pPrior, r1);
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn
);
if( pIdx->bHasExpr && aRegIdx ){
p3 = aRegIdx[i];
}
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1, p3);
sqlite3VdbeChangeP4(v, -1, (const char*)pIdx, P4_INDEX);
sqlite3VdbeChangeP5(v, pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
pPrior = pIdx;
}
+4 -1
View File
@@ -2809,7 +2809,10 @@ void sqlite3CompleteInsertion(
|| pIdx->pNext==0
|| pIdx->pNext->onError==OE_Replace );
if( aRegIdx[i]==0 ) continue;
if( pIdx->pPartIdxWhere ){
if( pIdx->pPartIdxWhere || (update_flags && pIdx->bHasExpr) ){
/* If this is a partial index, or an UPDATE of an index on an
** expression, then the record register may be set to NULL to indicate
** that no record should be inserted into this index. */
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
}
+3
View File
@@ -1302,6 +1302,9 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
if( p->xDestructor ) p->xDestructor(p->pData);
sqlite3_free(p);
}
while( db->xAuth ){
sqlite3_pop_authorizer(db);
}
/* Convert the connection into a zombie and then close it.
*/
+3 -4
View File
@@ -876,10 +876,10 @@ int sqlite3_deserialize(
if( rc ) goto end_deserialize;
db->init.iDb = (u8)iDb;
db->init.reopenMemdb = 1;
rc = sqlite3_step(pStmt);
sqlite3_step(pStmt);
db->init.reopenMemdb = 0;
if( rc!=SQLITE_DONE ){
rc = SQLITE_ERROR;
rc = sqlite3_finalize(pStmt);
if( rc!=SQLITE_OK ){
goto end_deserialize;
}
p = memdbFromDbSchema(db, zSchema);
@@ -900,7 +900,6 @@ int sqlite3_deserialize(
}
end_deserialize:
sqlite3_finalize(pStmt);
if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){
sqlite3_free(pData);
}
+35 -16
View File
@@ -35,8 +35,15 @@
# define SQLITE_MUTEX_NREF 0
#endif
#if GCC_VERSION>0
# define ALIGN128 __attribute__((aligned(128)))
#else
# define ALIGN128
#endif
/*
** Each recursive mutex is an instance of the following structure.
** Each SQLite mutex is an instance of the following structure.
**
*/
struct sqlite3_mutex {
pthread_mutex_t mutex; /* Mutex controlling the lock */
@@ -151,19 +158,31 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** the same type number.
*/
static sqlite3_mutex *pthreadMutexAlloc(int iType){
static sqlite3_mutex staticMutexes[] = {
SQLITE3_MUTEX_INITIALIZER(2),
SQLITE3_MUTEX_INITIALIZER(3),
SQLITE3_MUTEX_INITIALIZER(4),
SQLITE3_MUTEX_INITIALIZER(5),
SQLITE3_MUTEX_INITIALIZER(6),
SQLITE3_MUTEX_INITIALIZER(7),
SQLITE3_MUTEX_INITIALIZER(8),
SQLITE3_MUTEX_INITIALIZER(9),
SQLITE3_MUTEX_INITIALIZER(10),
SQLITE3_MUTEX_INITIALIZER(11),
SQLITE3_MUTEX_INITIALIZER(12),
SQLITE3_MUTEX_INITIALIZER(13)
/* Static mutexes - those with IDs of 2 or more...
**
** The ALIGN128 macro attempts to force 128-byte alignment on mutexes,
** so that adjacent mutex objects are always on different cache lines
** in the CPU. This is CPU-dependent, of course, but 128-byte alignment
** seems to work well for all contemporary processors. Experiments show
** that 64 works just as well most of the time, but the internet says
** that 128-byte alignment works better.
*/
static ALIGN128 union staticMutex {
sqlite3_mutex m;
char aSpacer[128];
} aMutex[] = {
{ .m = SQLITE3_MUTEX_INITIALIZER(2) },
{ .m = SQLITE3_MUTEX_INITIALIZER(3) },
{ .m = SQLITE3_MUTEX_INITIALIZER(4) },
{ .m = SQLITE3_MUTEX_INITIALIZER(5) },
{ .m = SQLITE3_MUTEX_INITIALIZER(6) },
{ .m = SQLITE3_MUTEX_INITIALIZER(7) },
{ .m = SQLITE3_MUTEX_INITIALIZER(8) },
{ .m = SQLITE3_MUTEX_INITIALIZER(9) },
{ .m = SQLITE3_MUTEX_INITIALIZER(10) },
{ .m = SQLITE3_MUTEX_INITIALIZER(11) },
{ .m = SQLITE3_MUTEX_INITIALIZER(12) },
{ .m = SQLITE3_MUTEX_INITIALIZER(13) },
};
sqlite3_mutex *p;
switch( iType ){
@@ -200,12 +219,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
}
default: {
#ifdef SQLITE_ENABLE_API_ARMOR
if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){
if( iType-2<0 || iType-2>=ArraySize(aMutex) ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
p = &staticMutexes[iType-2];
p = &aMutex[iType-2].m;
break;
}
}
+87 -94
View File
@@ -31,11 +31,29 @@
*/
#ifdef SQLITE_MUTEX_W32
#if MSVC_VERSION>0
# define ALIGN128 __declspec(align(128))
#else
# define ALIGN128
#endif
#pragma warning(push)
#pragma warning(disable: 4324)
/*
** Each recursive mutex is an instance of the following structure.
** Each SQLite mutex is an instance of the following structure.
**
** The ALIGN128 macro attempts to force 128-byte alignment on mutexes,
** so that adjacent mutex objects are always on different cache lines
** in the CPU. This is CPU-dependent, of course, but 128-byte alignment
** seems to work well for all contemporary processors. Experiments show
** that 64 works just as well most of the time, but the internet says
** that 128-byte alignment works better.
*/
struct sqlite3_mutex {
CRITICAL_SECTION mutex; /* Mutex controlling the lock */
struct sqlite3_mutex {
union {
CRITICAL_SECTION cs; /* The CRITICAL_SECTION mutex. id==1 */
SRWLOCK srwl; /* The Slim reader/writer lock. id!=1 */
} u;
int id; /* Mutex type */
#ifdef SQLITE_DEBUG
volatile int nRef; /* Number of entrances */
@@ -43,20 +61,7 @@ struct sqlite3_mutex {
volatile LONG trace; /* True to trace changes */
#endif
};
/*
** These are the initializer values used when declaring a "static" mutex
** on Win32. It should be noted that all mutexes require initialization
** on the Win32 platform.
*/
#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
#ifdef SQLITE_DEBUG
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id, \
0L, (DWORD)0, 0 }
#else
#define SQLITE3_MUTEX_INITIALIZER(id) { SQLITE_W32_MUTEX_INITIALIZER, id }
#endif
#pragma warning(pop)
#ifdef SQLITE_DEBUG
/*
@@ -67,13 +72,8 @@ static int winMutexHeld(sqlite3_mutex *p){
return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
return p->nRef==0 || p->owner!=tid;
}
static int winMutexNotheld(sqlite3_mutex *p){
DWORD tid = GetCurrentThreadId();
return winMutexNotheld2(p, tid);
return p->nRef==0 || p->owner!=GetCurrentThreadId();
}
#endif
@@ -95,41 +95,42 @@ void sqlite3MemoryBarrier(void){
}
/*
** Initialize and deinitialize the mutex subsystem.
** Static mutexes.
**
** The ALIGN128 macro on the static mutex array forces 128-byte alignment
** on the mutexes so that adjacent mutex objects are always on different
** cache lines in the CPU. This is CPU-dependent, of course, but 128-byte
** alignment seems to work well for all contemporary processors.
** Experiments show that 64 works just as well most of the time, but
** the internet says that 128-byte alignment works better.
*/
static sqlite3_mutex winMutex_staticMutexes[] = {
SQLITE3_MUTEX_INITIALIZER(2),
SQLITE3_MUTEX_INITIALIZER(3),
SQLITE3_MUTEX_INITIALIZER(4),
SQLITE3_MUTEX_INITIALIZER(5),
SQLITE3_MUTEX_INITIALIZER(6),
SQLITE3_MUTEX_INITIALIZER(7),
SQLITE3_MUTEX_INITIALIZER(8),
SQLITE3_MUTEX_INITIALIZER(9),
SQLITE3_MUTEX_INITIALIZER(10),
SQLITE3_MUTEX_INITIALIZER(11),
SQLITE3_MUTEX_INITIALIZER(12),
SQLITE3_MUTEX_INITIALIZER(13)
};
static ALIGN128 union staticMutexes {
sqlite3_mutex m;
char spacer[128];
} aWindowsMutex[12];
static int winMutex_isInit = 0;
static int winMutex_isNt = -1; /* <0 means "need to query" */
/* As the winMutexInit() and winMutexEnd() functions are called as part
** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
** "interlocked" magic used here is probably not strictly necessary.
*/
static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
static LONG volatile winMutex_lock = 0;
int sqlite3_win32_is_nt(void); /* os_win.c */
void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
static int winMutexInit(void){
/* The first to increment to 1 does actual initialization */
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
int i;
for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
for(i=0; i<ArraySize(aWindowsMutex); i++){
sqlite3_mutex *p = &aWindowsMutex[i].m;
p->id = i+2;
InitializeSRWLock(&p->u.srwl);
#ifdef SQLITE_DEBUG
p->nRef = 0;
p->owner = 0;
p->trace = 0;
#endif
}
winMutex_isInit = 1;
}else{
@@ -147,10 +148,6 @@ static int winMutexEnd(void){
** (which should be the last to shutdown.) */
if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
if( winMutex_isInit==1 ){
int i;
for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
DeleteCriticalSection(&winMutex_staticMutexes[i].mutex);
}
winMutex_isInit = 0;
}
}
@@ -219,18 +216,22 @@ static sqlite3_mutex *winMutexAlloc(int iType){
p->trace = 1;
#endif
#endif
InitializeCriticalSection(&p->mutex);
if( iType==SQLITE_MUTEX_RECURSIVE ){
InitializeCriticalSection(&p->u.cs);
}else{
InitializeSRWLock(&p->u.srwl);
}
}
break;
}
default: {
#ifdef SQLITE_ENABLE_API_ARMOR
if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){
if( iType-2<0 || iType-2>=ArraySize(aWindowsMutex) ){
(void)SQLITE_MISUSE_BKPT;
return 0;
}
#endif
p = &winMutex_staticMutexes[iType-2];
p = &aWindowsMutex[iType-2].m;
#ifdef SQLITE_DEBUG
#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
InterlockedCompareExchange(&p->trace, 1, 0);
@@ -252,8 +253,10 @@ static sqlite3_mutex *winMutexAlloc(int iType){
static void winMutexFree(sqlite3_mutex *p){
assert( p );
assert( p->nRef==0 && p->owner==0 );
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){
DeleteCriticalSection(&p->mutex);
if( p->id==SQLITE_MUTEX_FAST ){
sqlite3_free(p);
}else if( p->id==SQLITE_MUTEX_RECURSIVE ){
DeleteCriticalSection(&p->u.cs);
sqlite3_free(p);
}else{
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -274,67 +277,56 @@ static void winMutexFree(sqlite3_mutex *p){
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
#endif
assert( p );
#ifdef SQLITE_DEBUG
assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
#else
assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );
#endif
assert( winMutex_isInit==1 );
EnterCriticalSection(&p->mutex);
if( p->id==SQLITE_MUTEX_RECURSIVE ){
EnterCriticalSection(&p->u.cs);
}else{
AcquireSRWLockExclusive(&p->u.srwl);
}
#ifdef SQLITE_DEBUG
assert( p->nRef>0 || p->owner==0 );
p->owner = tid;
p->owner = GetCurrentThreadId();
p->nRef++;
if( p->trace ){
OSTRACE(("ENTER-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n",
tid, p->id, p, p->trace, p->nRef));
GetCurrentThreadId(), p->id, p, p->trace, p->nRef));
}
#endif
}
static int winMutexTry(sqlite3_mutex *p){
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
#endif
int rc = SQLITE_BUSY;
assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld(p) );
/*
** The sqlite3_mutex_try() routine is very rarely used, and when it
** is used it is merely an optimization. So it is OK for it to always
** The sqlite3_mutex_try() routine is seldom used, and when it is
** used it is merely an optimization. So it is OK for it to always
** fail.
**
** The TryEnterCriticalSection() interface is only available on WinNT.
** And some windows compilers complain if you try to use it without
** first doing some #defines that prevent SQLite from building on Win98.
** For that reason, we will omit this optimization for now. See
** ticket #2685.
*/
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
assert( winMutex_isInit==1 );
assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
if( winMutex_isNt<0 ){
winMutex_isNt = sqlite3_win32_is_nt();
if( p->id==SQLITE_MUTEX_RECURSIVE ){
rc = TryEnterCriticalSection(&p->u.cs);
}else{
rc = TryAcquireSRWLockExclusive(&p->u.srwl);
}
assert( winMutex_isNt==0 || winMutex_isNt==1 );
if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
if( rc ){
#ifdef SQLITE_DEBUG
p->owner = tid;
p->owner = GetCurrentThreadId();
p->nRef++;
#endif
rc = SQLITE_OK;
}else{
rc = SQLITE_BUSY;
}
#else
UNUSED_PARAMETER(p);
#endif
#ifdef SQLITE_DEBUG
if( p->trace ){
OSTRACE(("TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
tid, p->id, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
OSTRACE((
"TRY-MUTEX tid=%lu, mutex(%d)=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
GetCurrentThreadId(), p->id, p, p->trace, p->owner, p->nRef,
sqlite3ErrName(rc)));
}
#endif
return rc;
@@ -347,23 +339,24 @@ static int winMutexTry(sqlite3_mutex *p){
** is not currently allocated. SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
#endif
assert( p );
#ifdef SQLITE_DEBUG
assert( p->nRef>0 );
assert( p->owner==tid );
assert( p->owner==GetCurrentThreadId() );
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
assert( winMutex_isInit==1 );
LeaveCriticalSection(&p->mutex);
if( p->id==SQLITE_MUTEX_RECURSIVE ){
LeaveCriticalSection(&p->u.cs);
}else{
ReleaseSRWLockExclusive(&p->u.srwl);
}
#ifdef SQLITE_DEBUG
if( p->trace ){
OSTRACE(("LEAVE-MUTEX tid=%lu, mutex(%d)=%p (%d), nRef=%d\n",
tid, p->id, p, p->trace, p->nRef));
GetCurrentThreadId(), p->id, p, p->trace, p->nRef));
}
#endif
}
+317 -1553
View File
File diff suppressed because it is too large Load Diff
+1 -43
View File
@@ -27,53 +27,11 @@
# include <errno.h> /* amalgamator: dontcache */
#endif
/*
** Determine if we are dealing with Windows NT.
**
** We ought to be able to determine if we are compiling for Windows 9x or
** Windows NT using the _WIN32_WINNT macro as follows:
**
** #if defined(_WIN32_WINNT)
** # define SQLITE_OS_WINNT 1
** #else
** # define SQLITE_OS_WINNT 0
** #endif
**
** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
** it ought to, so the above test does not work. We'll just assume that
** everything is Windows NT unless the programmer explicitly says otherwise
** by setting SQLITE_OS_WINNT to 0.
*/
#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
# define SQLITE_OS_WINNT 1
#endif
/*
** Determine if we are dealing with Windows CE - which has a much reduced
** API.
*/
#if defined(_WIN32_WCE)
# define SQLITE_OS_WINCE 1
#else
# define SQLITE_OS_WINCE 0
#endif
/*
** For WinCE, some API function parameters do not appear to be declared as
** volatile.
*/
#if SQLITE_OS_WINCE
# define SQLITE_WIN32_VOLATILE
#else
# define SQLITE_WIN32_VOLATILE volatile
#endif
/*
** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
** functions are not available (e.g. those not using MSVC, Cygwin, etc).
*/
#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && \
SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
#if SQLITE_OS_WIN && SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
# define SQLITE_OS_WIN_THREADS 1
#else
# define SQLITE_OS_WIN_THREADS 0
+1
View File
@@ -1476,6 +1476,7 @@ expr(A) ::= expr(A) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
A = sqlite3PExpr(pParse, TK_BETWEEN, A, 0);
if( A ){
A->x.pList = pList;
sqlite3ExprSetHeightAndFlags(pParse, A);
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
+423 -96
View File
@@ -356,6 +356,13 @@ typedef struct Mode {
#define MFLG_CRLF 0x02 /* Use CR/LF output line endings */
#define MFLG_HDR 0x04 /* .header used to change headers on/off */
/* A file that needs to be deleted, but only after a delay.
*/
typedef struct Unlink {
sqlite3_int64 tm; /* Unlink after this time */
char *zFN; /* Name of file. Space from sqlite3_malloc() */
} Unlink;
/*
** State information about the database connection is contained in an
** instance of the following structure.
@@ -401,7 +408,10 @@ struct ShellState {
FILE *pLog; /* Write log output here */
char *azPrompt[2]; /* Main and continuation prompt strings */
Mode mode; /* Current display mode */
Mode modePrior; /* Backup */
Mode *aModeStack; /* Backups */
unsigned nModeStack; /* Number of entries in aModeStack[] */
unsigned nUnlink; /* Number of entries in aDelayUnlink[] */
Unlink *aUnlink; /* Temp files to unlink after a delay */
struct SavedMode { /* Ability to define custom mode configurations */
char *zTag; /* Name of this saved mode */
Mode mode; /* The saved mode */
@@ -596,7 +606,7 @@ static const ModeInfo aModeInfo[] = {
** text encoding |/ | show | \
** v-------------------' | hdrs? | The QRF style
** 0: n/a blob | v-----'
** 1: plain v_---------' 0: n/a
** 1: plain v----------' 0: n/a
** 2: sql 0: auto 1: no
** 3: csv 1: as-text 2: yes
** 4: html 2: sql
@@ -605,18 +615,6 @@ static const ModeInfo aModeInfo[] = {
** 5: json
** 6: size
******************************************************************/
/*
** These are the column/row/line separators used by the various
** import/export modes.
*/
#define SEP_Column "|"
#define SEP_Row "\n"
#define SEP_Tab "\t"
#define SEP_Space " "
#define SEP_Comma ","
#define SEP_CrLf "\r\n"
#define SEP_Unit "\x1F"
#define SEP_Record "\x1E"
/*
** Default values for the various QRF limits
@@ -665,6 +663,13 @@ static int stdout_tty_width = -1;
*/
static sqlite3 *globalDb = 0;
/*
** This is a global pointer to the main ShellState variable. This
** exists so that the atexit() callback can access the ShellState
** object to do some cleanup.
*/
static struct ShellState *globalShellState = 0;
/*
** True if an interrupt (Control-C) has been received.
*/
@@ -689,6 +694,7 @@ static FILE *iotrace = 0;
** Works like.
** --------------
** cli_printf(FILE*, const char*, ...); fprintf()
** cli_write(FILE*, const char*, int); write()
** cli_puts(const char*, FILE*); fputs()
** cli_vprintf(FILE*, const char*, va_list); vfprintf()
**
@@ -715,6 +721,18 @@ static int cli_printf(FILE *out, const char *zFormat, ...){
va_end(ap);
return rc;
}
static int cli_write(FILE *out, const char *zData, int nData){
if( cli_output_capture && (out==stdout || out==stderr) ){
sqlite3_str_append(cli_output_capture, zData, nData);
#ifdef _WIN32
}else if( out==stdout || out==stderr ){
nData = sqlite3_fprintf(out, "%.*s", nData, zData);
#endif
}else{
nData = (int)fwrite(zData, 1, nData, out);
}
return nData;
}
static int cli_puts(const char *zText, FILE *out){
if( cli_output_capture && (out==stdout || out==stderr) ){
sqlite3_str_appendall(cli_output_capture, zText);
@@ -909,15 +927,77 @@ static char *local_getline(char *zLine, FILE *in){
}
/*
** The default prompts.
** The SQLITE_PS_APPDEF macro should be set to the name of a function
** that accepts a single "int" argument and returns a "const char *"
** that is guaranteed to be non-NULL. The value returned depends on the
** argument:
**
** 1 Default main prompt.
** 2 Default continuation prompt.
** 3 Environment variable to override main prompt ("SQLITE_PS1")
** 4 Environment variable to override continuatio ("SQLITE_PS2")
** 'A' The name of the application. (Nominally "SQLite")
** 'V' Version number including patch. (ex: "3.54.1")
** 'v' Version number without patch. (ex: "3.54")
**
*/
#ifndef SQLITE_PS1
# define SQLITE_PS1 "SQLite /f> "
#ifdef SQLITE_PS_APPDEF
extern const char *SQLITE_PS_APPDEF(int);
#else
# define SQLITE_PS_APPDEF shellPromptAppDef
static const char *shellPromptAppDef(int c){
switch( c ){
/* The default main prompt string */
case 1:
#if defined(SQLITE_PS1)
return SQLITE_PS1;
#elif defined(SQLITE_PS_NOANSI)
return "/A-/v /~> ";
#else
return "/e[1;32m/A-/v /e[1;/x33/:36/;m/m/e[3m/;/f/;/e[0m-> ";
#endif
#ifndef SQLITE_PS2
# define SQLITE_PS2 "/B.../H> "
/* The default continuation prompt string */
case 2:
#if defined(SQLITE_PS2)
return SQLITE_PS2;
#elif defined(SQLITE_PS_NOANSI)
return "/B/C> ";
#else
return "/B/e[1;/x33/:36/;m/C/e[0m-> ";
#endif
/* Name of environment variables that override the prompt strings
** of cases 1 and 2 */
case 3: return "SQLITE_PS1";
case 4: return "SQLITE_PS2";
/* Name of the application */
case 'A': return "SQLite";
/* Full version number of the application, including patch level */
case 'V': return sqlite3_libversion();
/* Version number without the patch level */
case 'v': {
static char zRel[16];
const char *zF = sqlite3_libversion();
const char *zD = strrchr(zF,'.');
if( zD && (size_t)(zD-zF)<sizeof(zRel)-1 ){
memcpy(zRel,zF,(size_t)(zD-zF));
zRel[(size_t)(zD-zF)] = 0;
return zRel;
}else{
return zF;
}
}
}
return "";
}
#endif /* !defined(SQLITE_PS_APPDEF) */
/*
** Return the raw (unexpanded) prompt string. This will be the
** first of the following that exist:
@@ -936,21 +1016,17 @@ static const char *prompt_string(ShellState *p, int bContinue){
return p->azPrompt[bContinue];
}
#ifndef SQLITE_SHELL_FIDDLE
zPS = getenv(bContinue ? "SQLITE_PS2" : "SQLITE_PS1");
zPS = getenv(SQLITE_PS_APPDEF(3+bContinue));
if( zPS ) return zPS;
#endif
if( bContinue ){
return SQLITE_PS2;
}else{
return SQLITE_PS1;
}
return SQLITE_PS_APPDEF(1+bContinue);
}
/*
** Return the name of the open database file, to be used for prompt
** expansion purposes.
*/
static const char *prompt_filename(ShellState *p){
static const char *prompt_filename(ShellState *p, const char *zMemoryName){
sqlite3_filename pFN;
const char *zFN = 0;
if( p->pAuxDb->mFlgs & 0x01 ){
@@ -961,12 +1037,51 @@ static const char *prompt_filename(ShellState *p){
if( zFN==0 || zFN[0]==0 ){
zFN = p->pAuxDb->zDbFilename;
if( zFN==0 || zFN[0]==0 || cli_strcmp(zFN,":memory:")==0 ){
zFN = "in-memory";
zFN = zMemoryName;
}
}
return zFN;
}
/*
** Return the name of the computer on which we are running.
*/
static const char *prompt_hostname(int bFull){
#ifdef _WIN32
const char *z = getenv("COMPUTERNAME");
if( z==0 || z[0]==0 ) z = "?";
return z;
#else
static char zHost[256];
if( gethostname(zHost, sizeof(zHost)) ){
zHost[0] = '?';
zHost[1] = 0;
}
if( !bFull ){
char *p = strchr(zHost,'.');
if( p ) p[0] = 0;
}
return zHost;
#endif
}
/*
** Return the username. This is taken from an environment variable
** and can thus be forged. Do not depend on it.
*/
static const char *prompt_user(void){
const char *z;
#ifdef _WIN32
z = getenv("USERNAME");
if( z==0 || z[0]==0 ) z = "?";
#else
z = getenv("USER");
if( z==0 || z[0]==0 ) z = getenv("LOGNAME");
if( z==0 || z[0]==0 ) z = "?";
#endif
return z;
}
/*
** Expand escapes in the given input prompt string. Return the
** expanded prompt in memory obtained from sqlite3_malloc(). The
@@ -988,12 +1103,13 @@ static char *expand_prompt(
sqlite3_str *pOut = sqlite3_str_new(0);
int i;
char c;
int onoff = 1;
unsigned int mOff = 0; /* Bitmask of FALSE for if/then/else */
int idxSpace = -1;
int iDate = -1;
for(i=0; zPrompt[i]; i++){
if( zPrompt[i]!='/' ) continue;
if( i>0 ){
if( onoff ) sqlite3_str_append(pOut, zPrompt, i);
if( !mOff ) sqlite3_str_append(pOut, zPrompt, i);
zPrompt += i;
i = 0;
}
@@ -1016,14 +1132,23 @@ static char *expand_prompt(
while( i<=2 && zPrompt[i+1]>='0' && zPrompt[i+1]<='7' ){
v = v*8 + zPrompt[++i] - '0';
}
if( onoff ) sqlite3_str_appendchar(pOut, 1, v);
if( !mOff ) sqlite3_str_appendchar(pOut, 1, v);
zPrompt += i+1;
i = -1;
continue;
}
if( c=='e' ){
/* /e is shorthand for /033 which is U+001B "Escape" */
if( onoff ) sqlite3_str_append(pOut, "\033", 1);
if( !mOff ) sqlite3_str_append(pOut, "\033", 1);
zPrompt += 2;
i = -1;
continue;
}
if( c=='A' || c=='V' || c=='v' ){
/* /A expands to the application name */
/* /V expands to the version number with patch level */
/* /v expands to the version number without the patch level */
if( !mOff ) sqlite3_str_appendall(pOut, SQLITE_PS_APPDEF(c));
zPrompt += 2;
i = -1;
continue;
@@ -1043,33 +1168,47 @@ static char *expand_prompt(
** .prompt '/e[1;/x31/:34/;m~f>/e[0m '
*/
if( c==':' ){
/* toggle display on/off */
onoff = !onoff;
/* ELSE: toggle display on/off */
mOff ^= 1;
zPrompt += 2;
i = -1;
continue;
}
if( c==';' ){
/* Turn display on */
onoff = 1;
/* ENDIF: Turn display on */
mOff >>= 1;
zPrompt += 2;
i = -1;
continue;
}
if( c=='x' ){
/* /x turns display off not in a transaction, on if in txn */
onoff = p->db && !sqlite3_get_autocommit(p->db);
mOff = (mOff<<1) | (p->db==0 || sqlite3_get_autocommit(p->db)!=0);
zPrompt += 2;
i = -1;
continue;
}
if( c=='r' ){
/* /r turns display off if database is read/write, on if read-only */
mOff = (mOff<<1) | (p->db==0 || sqlite3_db_readonly(p->db,0)==0);
zPrompt += 2;
i = -1;
continue;
}
if( c=='m' ){
/* /m turns display on for an in-memory database and off for persistent */
mOff = (mOff<<1) | (p->db && prompt_filename(p,0)!=0);
zPrompt += 2;
i = -1;
continue;
}
if( c=='f' || c=='F' || c=='~' ){
/* /f becomes the tail of the database filename */
/* /F becomes the full pathname */
/* /~ becomes the full pathname relative to $HOME */
if( onoff ){
const char *zFN = prompt_filename(p);
if( !mOff ){
const char *zFN = prompt_filename(p,"memory");
if( c=='f' ){
#ifdef _WIN32
const char *zTail = strrchr(zFN,'\\');
@@ -1095,9 +1234,29 @@ static char *expand_prompt(
continue;
}
if( c=='H' ){
/* /H becomes text needed to terminate current input */
if( onoff ){
if( c=='h' || c=='H' ){
/* /h becomes hostname up to the first '.' */
/* /H is the full hostname */
if( !mOff ){
sqlite3_str_appendall(pOut, prompt_hostname(c=='H'));
}
zPrompt += 2;
i = -1;
continue;
}
if( c=='u' ){
/* /u becomes the username (taken from environment variables) */
if( !mOff ){
sqlite3_str_appendall(pOut, prompt_user());
}
zPrompt += 2;
i = -1;
continue;
}
if( c=='C' ){
/* /C becomes text needed to terminate current input */
if( !mOff ){
sqlite3_int64 R = zPrior ? sqlite3_incomplete(zPrior) : 0;
int cc = (R>>16)&0xff;
int nParen = R>>32;
@@ -1131,18 +1290,44 @@ static char *expand_prompt(
/* /B is a no-op for the main prompt. For the continuation prompt,
** /B expands to zero or more spaces to make the continuation prompt
** at least as wide as the main prompt. */
if( onoff ) idxSpace = sqlite3_str_length(pOut);
if( !mOff ) idxSpace = sqlite3_str_length(pOut);
zPrompt += 2;
i = -1;
continue;
}
/* No match to a known escape. Generate an error. */
if( onoff ) sqlite3_str_appendf(pOut,"UNKNOWN(\"/%c\")",c);
if( c=='D' ){
/* /D.../D replaces all of the text between the two /D escapes with
** the result from strftime(). */
if( iDate<0 ){
iDate = sqlite3_str_length(pOut);
}else{
if( !mOff ){
time_t now;
char zBuf[200];
zBuf[0] = 0;
time(&now);
strftime(zBuf, sizeof(zBuf)-1, sqlite3_str_value(pOut)+iDate,
localtime(&now));
zBuf[199] = 0;
sqlite3_str_truncate(pOut, iDate);
sqlite3_str_appendall(pOut, zBuf);
}
iDate = -1;
}
zPrompt += 2;
i = -1;
continue;
}
/* No match to a known escape. Generate an error. The mOff flag
** is ignored for this output, so that errors appear even if they
** are in an unused branch. */
sqlite3_str_appendf(pOut,"UNKNOWN(\"/%c\")",c);
zPrompt += 2;
i = -1;
}
if( i>0 && onoff ){
if( i>0 && !mOff ){
sqlite3_str_append(pOut, zPrompt, i);
}
@@ -1166,7 +1351,11 @@ static char *expand_prompt(
}
}
}
if( 0==sqlite3_str_length(pOut) ){
/* Avoid a bogus OOM */
sqlite3_str_appendchar(pOut, 1, '\0');
}
return sqlite3_str_finish(pOut);
}
@@ -1589,6 +1778,7 @@ static void shellExpandPrompt(
sqlite3_free(zRes);
}
/************************* BEGIN PERFORMANCE TIMER *****************************/
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
#include <sys/time.h>
@@ -1932,20 +2122,20 @@ static int modeFind(ShellState *p, const char *zName){
** Save or restore the current output mode
*/
static void modePush(ShellState *p){
if( p->nPopMode==0 ){
modeFree(&p->modePrior);
modeDup(&p->modePrior,&p->mode);
}
p->aModeStack = realloc(p->aModeStack, sizeof(Mode)*(1+p->nModeStack));
shell_check_oom(p->aModeStack);
modeDup(&p->aModeStack[p->nModeStack], &p->mode);
p->nModeStack++;
}
static void modePop(ShellState *p){
if( p->modePrior.spec.iVersion>0 ){
if( p->nModeStack ){
modeFree(&p->mode);
p->mode = p->modePrior;
memset(&p->modePrior, 0, sizeof(p->modePrior));
p->nModeStack--;
memcpy(&p->mode, &p->aModeStack[p->nModeStack], sizeof(Mode));
memset(&p->aModeStack[p->nModeStack], 0, sizeof(Mode));
}
}
/*
** A callback for the sqlite3_log() interface.
*/
@@ -2355,6 +2545,11 @@ static void interrupt_handler(int NotUsed){
if( ++seenInterrupt>1 ) cli_exit(1);
if( globalDb ) sqlite3_interrupt(globalDb);
}
/* No-masking interrupts (SIGTERM or SIGHUP) */
static void sigterm_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
cli_exit(1);
}
/* Try to determine the screen width. Use the default if unable.
*/
@@ -3414,7 +3609,7 @@ static int expertDotCommand(
*/
static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){
ShellState *pArg = (ShellState*)pX;
cli_printf(pArg->out, "%.*s", (int)n, z);
cli_write(pArg->out, z, (int)n);
return SQLITE_OK;
}
@@ -4675,6 +4870,9 @@ static void shellModuleSchema(
#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */
#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */
/* Forward reference */
static void shellTempFilenameFunc(sqlite3_context*,int,sqlite3_value**);
/*
** Make sure the database is open. If it is not, then open it. If
** the database fails to open, print an error message and exit.
@@ -4847,6 +5045,8 @@ static void open_db(ShellState *p, int openFlags){
p, shellExpandPrompt, 0, 0);
sqlite3_create_function(p->db, "shell_prompt_test", 3, SQLITE_UTF8,
p, shellExpandPrompt, 0, 0);
sqlite3_create_function(p->db, "shell_temp_filename", 1, SQLITE_UTF8,
p, shellTempFilenameFunc, 0, 0);
if( p->openMode==SHELL_OPEN_ZIPFILE ){
@@ -5662,7 +5862,12 @@ static void output_reset(ShellState *p){
/* Give the start/open/xdg-open command some time to get
** going before we continue and potentially delete the
** p->zTempFile data file out from under it */
sqlite3_sleep(2000);
p->aUnlink = realloc(p->aUnlink, sizeof(Unlink)*(1+p->nUnlink));
shell_check_oom(p->aUnlink);
p->aUnlink[p->nUnlink].tm = timeOfDay()+10000;
p->aUnlink[p->nUnlink].zFN = p->zTempFile;
p->nUnlink++;
p->zTempFile = 0;
}
sqlite3_free(zCmd);
modePop(p);
@@ -6075,29 +6280,111 @@ static char *shellFilenameFromUri(const char *zFN){
/*
** Delete a file.
**
** If unsuccessful on the first attempt and if pRetry!=NULL and pRetry[0]
** is positive, then delay for pRetry[0] milliseconds and try again. On
** a retry, pRetry[0] is set to zero. Unsuccessful unlinks usually only
** happen on Windows. It is possible (in theory) for unlink() to fail
** on Unix, but it rarely happens.
**
** Return 0 on success and non-zero if unable.
*/
int shellDeleteFile(const char *zFilename){
int shellDeleteFile(const char *zFilename, int *pRetry){
int rc;
for(;;){
#ifdef _WIN32
wchar_t *z = sqlite3_win32_utf8_to_unicode(zFilename);
rc = _wunlink(z);
sqlite3_free(z);
wchar_t *z = sqlite3_win32_utf8_to_unicode(zFilename);
rc = _wunlink(z);
if( rc && _waccess(z,0)!=0 ) rc = 0;
sqlite3_free(z);
#else
rc = unlink(zFilename);
rc = unlink(zFilename);
if( rc && access(zFilename, 0)!=0 ) rc = 0;
#endif
if( rc && pRetry && pRetry[0] ){
sqlite3_sleep(pRetry[0]);
pRetry[0] = 0;
continue;
}
break;
}
return rc;
}
/*
** Try to delete the temporary file (if there is one) and free the
** memory used to hold the name of the temp file.
** This routine does two things:
**
** (1) Delete (unlink) temporary files that are no longer needed.
** (2) Free memory used to hold the names of those temporary files.
**
** Temp filenames are stored in p->zTempName and in the p->aUnlink[]
** array. The p->zTempFile is always subject to immediate deletion. The
** temp files named in p->aUnlink[] are usually not deleted until after
** the timestamp stored in the p->aUnlink[].tm field, except if
** bForce is true, the p->aUnlink[] files will be deleted even if they
** have not reached their expiration, as long as a delay of at least
** nDelay milliseconds occurs first.
**
** If nDelay is non-zero and a deletion attempt is not successful,
** wait for nDelay milliseconds and try again before giving up. Only
** a single wait occurs, even if problems are encountered with multiple
** temp files. Only the first problem seen takes the delay. Thus
** the total delay never exceeds nDelay milliseconds. When nDelay
** is non-zero, memory used to hold the filename is reclaimed regardless
** of whether or not the temporary files were successfully deleted.
**
** If bForce is true, deletion is attempted on p->aUnlink[] files
** even if their time has not expired. However, there is a pause
** of up to nDelay milliseconds before doing the deletion.
**
** When nDelay is zero and the deletion attempt fails, memory used to
** store temporary filesnames is not reclaimed.
**
** The bForce flag is used only when the process is about to exit. When
** bForce is set, that indicates that this is our last opportunity to
** clean up temporary files.
*/
static void clearTempFile(ShellState *p){
if( p->zTempFile==0 ) return;
if( p->doXdgOpen ) return;
if( shellDeleteFile(p->zTempFile) ) return;
sqlite3_free(p->zTempFile);
p->zTempFile = 0;
static void clearTempFile(ShellState *p, int nDelay, int bForce){
int alwaysFree = (nDelay>0) || bForce;
int rc = 0;
if( p->zTempFile && (!p->doXdgOpen || bForce) ){
rc = shellDeleteFile(p->zTempFile, &nDelay);
if( rc==0 || alwaysFree ){
sqlite3_free(p->zTempFile);
p->zTempFile = 0;
}
}
if( p->nUnlink ){
unsigned int i;
for(i=0; i<p->nUnlink; i++){
sqlite3_int64 tmToGo = p->aUnlink[i].tm - timeOfDay();
int doDelete = tmToGo<=0;
if( !doDelete && alwaysFree ){
int tmSleep = nDelay;
if( tmSleep > tmToGo ) tmSleep = (int)tmToGo;
sqlite3_sleep(tmSleep);
nDelay -= tmSleep;
doDelete = 1;
}
if( doDelete ){
char *zFN = p->aUnlink[i].zFN;
rc = shellDeleteFile(zFN, &nDelay);
if( rc==0 || alwaysFree ){
sqlite3_free(p->aUnlink[i].zFN);
p->nUnlink--;
if( i<p->nUnlink ){
p->aUnlink[i] = p->aUnlink[p->nUnlink];
memset(&p->aUnlink[p->nUnlink], 0, sizeof(Unlink));
}
i--;
}
}
}
if( p->nUnlink==0 ){
free(p->aUnlink);
p->aUnlink = 0;
}
}
}
/* Forward reference */
@@ -6127,7 +6414,7 @@ static void newTempFile(ShellState *p, const char *zSuffix){
r /= 36;
}
zRand[i] = 0;
clearTempFile(p);
clearTempFile(p,10,0);
sqlite3_free(p->zTempFile);
p->zTempFile = 0;
zHome = find_home_dir(0);
@@ -6136,6 +6423,26 @@ static void newTempFile(ShellState *p, const char *zSuffix){
shell_check_oom(p->zTempFile);
}
/*
** SQL function: shell_temp_filename(SUFFIX)
**
** Return a randomly generated temporary filename.
*/
static void shellTempFilenameFunc(
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
const char *zSuffix = (const char*)sqlite3_value_text(apVal[0]);
(void)nVal;
if( zSuffix==0 ) zSuffix = "";
newTempFile(p, zSuffix);
sqlite3_result_text(pCtx, p->zTempFile, -1, SQLITE_TRANSIENT);
sqlite3_free(p->zTempFile);
p->zTempFile = 0;
}
/*
** The implementation of SQL scalar function fkey_collate_clause(), used
** by the ".lint fkey-indexes" command. This scalar function is always
@@ -7765,8 +8072,8 @@ static int dotCmdImport(ShellState *p){
}else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){
nSkip = integerValue(azArg[++i]);
}else if( cli_strcmp(z,"-ascii")==0 ){
if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0];
if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0];
if( sCtx.cColSep==0 ) sCtx.cColSep = '\037';
if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\036';
xRead = ascii_read_one_field;
}else if( cli_strcmp(z,"-csv")==0 ){
if( sCtx.cColSep==0 ) sCtx.cColSep = ',';
@@ -8373,7 +8680,6 @@ static int dotCmdMode(ShellState *p){
chng = 1; /* Not really a change, but we still want to suppress the
** "current mode" output */
}else if( optionMatch(z,"once") ){
p->nPopMode = 0;
modePush(p);
p->nPopMode = 1;
}else if( optionMatch(z,"noquote") ){
@@ -8882,8 +9188,8 @@ static int dotCmdOutput(ShellState *p){
newTempFile(p, "csv");
p->mode.mFlags &= ~MFLG_ECHO;
p->mode.eMode = MODE_Csv;
modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma);
modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf);
modeSetStr(&p->mode.spec.zColumnSep, ",");
modeSetStr(&p->mode.spec.zRowSep, "\r\n");
#ifdef _WIN32
zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does
** not work without it. */
@@ -9240,7 +9546,7 @@ static int do_meta_command(const char *zLine, ShellState *p){
if( nArg==0 ) return 0; /* no tokens, no error */
n = strlen30(azArg[0]);
c = azArg[0][0];
clearTempFile(p);
clearTempFile(p,0,0);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){
@@ -10476,10 +10782,10 @@ static int do_meta_command(const char *zLine, ShellState *p){
if( cli_strncmp(zFN,"file:",5)==0 ){
char *zDel = shellFilenameFromUri(zFN);
shell_check_oom(zDel);
shellDeleteFile(zDel);
shellDeleteFile(zDel, 0);
sqlite3_free(zDel);
}else{
shellDeleteFile(zFN);
shellDeleteFile(zFN, 0);
}
}
#ifndef SQLITE_SHELL_FIDDLE
@@ -12553,7 +12859,7 @@ static int runOneSqlLine(
rc = shell_exec(p, zSql, &zErrMsg);
END_TIMER(p);
if( rc || zErrMsg ){
char zPrefix[100];
sqlite3_str *pPrefix = sqlite3_str_new(p->db);
const char *zErrorTail;
const char *zErrorType;
if( zErrMsg==0 ){
@@ -12571,28 +12877,27 @@ static int runOneSqlLine(
}
if( zFilename || !stdin_is_interactive ){
if( cli_strcmp(zFilename,"cmdline")==0 ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
sqlite3_str_appendf(pPrefix,
"%s in %r command line argument:", zErrorType, startline);
}else if( cli_strcmp(zFilename,"<stdin>")==0 ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
sqlite3_str_appendf(pPrefix,
"%s near line %d:", zErrorType, startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
sqlite3_str_appendf(pPrefix,
"%s near line %d of %s:", zErrorType, startline, zFilename);
}
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
sqlite3_str_appendf(pPrefix, "%s:", zErrorType);
}
cli_printf(stderr,"%s %s\n", zPrefix, zErrorTail);
cli_printf(stderr,"%s %s\n", sqlite3_str_value(pPrefix), zErrorTail);
sqlite3_str_free(pPrefix);
sqlite3_free(zErrMsg);
zErrMsg = 0;
return 1;
}else if( ShellHasFlag(p, SHFLG_CountChanges) ){
char zLineBuf[2000];
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
cli_printf(p->out,
"changes: %lld total_changes: %lld\n",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
cli_printf(p->out, "%s\n", zLineBuf);
}
if( doAutoDetectRestore(p, zSql) ) return 1;
@@ -12748,7 +13053,7 @@ static int process_input(ShellState *p, const char *zSrc){
output_reset(p);
p->nPopOutput = 0;
}else{
clearTempFile(p);
clearTempFile(p,0,0);
}
if( p->nPopMode ){
modePop(p);
@@ -13051,12 +13356,13 @@ static void main_init(ShellState *p) {
#endif
sqlite3_config(SQLITE_CONFIG_URI, 1);
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
globalShellState = p;
}
/*
** Output text to the console in a font that attracts extra attention.
*/
#if defined(_WIN32) || defined(WIN32)
#if 0 /* Windows now handles ANSI escape codes */
static void printBold(const char *zText){
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
@@ -13069,7 +13375,7 @@ static void printBold(const char *zText){
}
#else
static void printBold(const char *zText){
cli_printf(stdout, "\033[1m%s\033[0m", zText);
cli_printf(stdout, "\033[1;36m\033[3m%s\033[0m", zText);
}
#endif
@@ -13086,8 +13392,14 @@ static char *cmdline_option_value(int argc, char **argv, int i){
return argv[i];
}
static void sayAbnormalExit(void){
/*
** The callback from atexit().
*/
static void abnormalExit(void){
if( seenInterrupt ) eputz("Program interrupted.\n");
if( globalShellState ){
clearTempFile(globalShellState, 1, 1);
}
}
/* Routine to output from vfstrace
@@ -13129,6 +13441,8 @@ static int auto_ext_leak_tester(
int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
int rc, i;
DWORD mode;
HANDLE hOut;
char **argv = malloc( sizeof(char*) * (argc+1) );
char **orig = argv;
if( argv==0 ){
@@ -13153,6 +13467,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
}
argv[argc] = 0;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleMode(hOut, &mode);
SetConsoleMode(hOut, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
rc = utf8_main(argc, argv);
for(i=0; i<argc; i++) free(orig[i]);
free(argv);
@@ -13200,7 +13517,7 @@ int SQLITE_CDECL main(int argc, char **argv){
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
#endif
atexit(sayAbnormalExit);
atexit(abnormalExit);
#ifdef SQLITE_DEBUG
mem_main_enter = sqlite3_memory_used();
#endif
@@ -13233,6 +13550,13 @@ int SQLITE_CDECL main(int argc, char **argv){
eputz("No ^C handler.\n");
}
#endif
#ifdef SIGTERM
signal(SIGTERM, sigterm_handler);
#endif
#ifdef SIGHUP
signal(SIGHUP, sigterm_handler);
#endif
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
@@ -13518,7 +13842,7 @@ int SQLITE_CDECL main(int argc, char **argv){
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_appendvfs_init(0,0,0);
#ifdef SQLITE_DEBUG
sqlite3_auto_extension( (void (*)())auto_ext_leak_tester );
sqlite3_auto_extension( (void(*)(void))auto_ext_leak_tester );
#endif
#endif
modeDefault(&data);
@@ -13793,7 +14117,7 @@ int SQLITE_CDECL main(int argc, char **argv){
output_reset(&data);
data.nPopOutput = 0;
}else{
clearTempFile(&data);
clearTempFile(&data,0,0);
}
}
}else{
@@ -13872,7 +14196,10 @@ int SQLITE_CDECL main(int argc, char **argv){
find_home_dir(1);
output_reset(&data);
data.doXdgOpen = 0;
clearTempFile(&data);
clearTempFile(&data,2500,1);
globalShellState = 0;
while( data.nModeStack ) modePop(&data);
free(data.aModeStack);
modeFree(&data.mode);
if( data.nSavedModes ){
int ii;
+64 -35
View File
@@ -3388,58 +3388,57 @@ void sqlite3_randomness(int N, void *P);
** METHOD: sqlite3
** KEYWORDS: {authorizer callback}
**
** ^This routine registers an authorizer callback with a particular
** [database connection], supplied in the first argument.
** ^The authorizer callback is invoked as SQL statements are being compiled
** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
** [sqlite3_prepare_v3()], [sqlite3_prepare16()], [sqlite3_prepare16_v2()],
** and [sqlite3_prepare16_v3()]. ^At various
** The sqlite3_set_authorizer(D,X,P) and sqlite3_push_authorizer(D,X,P,F)
** routines registers an authorizer callback X with a particular
** [database connection] D. The sqlite3_push_authorizer() interface
** is preferred.
**
** The authorizer callback is invoked multiple times as SQL statements
** are being compiled by [sqlite3_prepare()] or its variants
** [sqlite3_prepare_v2()], [sqlite3_prepare_v3()], [sqlite3_prepare16()],
** [sqlite3_prepare16_v2()], and [sqlite3_prepare16_v3()]. At various
** points during the compilation process, as logic is being created
** to perform various actions, the authorizer callback is invoked to
** see if those actions are allowed. ^The authorizer callback should
** see if those actions are allowed. The authorizer callback should
** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
** specific action but allow the SQL statement to continue to be
** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
** rejected with an error. ^If the authorizer callback returns
** rejected with an error. If the authorizer callback returns
** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
** then the [sqlite3_prepare_v2()] or equivalent call that triggered
** the authorizer will fail with an error message.
**
** When the callback returns [SQLITE_OK], that means the operation
** requested is ok. ^When the callback returns [SQLITE_DENY], the
** [sqlite3_prepare_v2()] or equivalent call that triggered the
** authorizer will fail with an error message explaining that
** access is denied.
**
** ^The first parameter to the authorizer callback is a copy of the third
** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
** The first parameter to the authorizer callback is a copy of the third
** parameter (P) to the sqlite3_set_authorizer(D,X,P) or
** sqlite3_push_authorizer(D,X,P,F) interface that created the
** authorizer. The second parameter
** to the callback is an integer [SQLITE_COPY | action code] that specifies
** the particular action to be authorized. ^The third through sixth parameters
** the particular action to be authorized. The third through sixth parameters
** to the callback are either NULL pointers or zero-terminated strings
** that contain additional details about the action to be authorized.
** Applications must always be prepared to encounter a NULL pointer in any
** of the third through the sixth parameters of the authorization callback.
**
** ^If the action code is [SQLITE_READ]
** If the action code is [SQLITE_READ]
** and the callback returns [SQLITE_IGNORE] then the
** [prepared statement] statement is constructed to substitute
** a NULL value in place of the table column that would have
** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE]
** return can be used to deny an untrusted user access to individual
** columns of a table.
** ^When a table is referenced by a [SELECT] but no column values are
** When a table is referenced by a [SELECT] but no column values are
** extracted from that table (for example in a query like
** "SELECT count(*) FROM tab") then the [SQLITE_READ] authorizer callback
** is invoked once for that table with a column name that is an empty string.
** ^If the action code is [SQLITE_DELETE] and the callback returns
** If the action code is [SQLITE_DELETE] and the callback returns
** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the
** [truncate optimization] is disabled and all rows are deleted individually.
**
** An authorizer is used when [sqlite3_prepare | preparing]
** SQL statements from an untrusted source, to ensure that the SQL statements
** do not try to access data they are not allowed to see, or that they do not
** try to execute malicious statements that damage the database. For
** example, an application may allow a user to enter arbitrary
** The authorizer mechanism is designed for use when
** [sqlite3_prepare | preparing] SQL statements from an untrusted source,
** to ensure that the SQL statements do not access data they are
** not allowed to see, or change data that they are not allowed to
** modify. For example, an application may allow a user to enter arbitrary
** SQL queries for evaluation by a database. But the application does
** not want the user to be able to make arbitrary changes to the
** database. An authorizer could then be put in place while the
@@ -3451,22 +3450,45 @@ void sqlite3_randomness(int N, void *P);
** and limiting database size using the [max_page_count] [PRAGMA]
** in addition to using an authorizer.
**
** ^(Only a single authorizer can be in place on a database connection
** at a time. Each call to sqlite3_set_authorizer overrides the
** previous call.)^ ^Disable the authorizer by installing a NULL callback.
** The authorizer is disabled by default.
** The sqlite3_set_authorizer(D,X,P) interface causes X to be the only
** authorizer that runs. Any previously registered authorizers are
** discarded. Older versions of SQLite (prior to version 3.54.0)
** only support sqlite3_set_authorizer().
**
** The authorizer callback must not do anything that will modify
** the database connection that invoked the authorizer callback.
** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
** database connections for the meaning of "modify" in this paragraph.
** The sqlite3_push_authorizer(D,X,P,F) interface pushes a new authorizer
** onto a stack of authorizers. The sqlite3_pop_authorizer(D) pops that
** authorizer back off the stack. If the F flag is 1, then the pushed
** authorizer callback is the only one that is considered - previous
** authorizers are retained so that they can be restored after the
** next sqlite3_pop_authorizer(), but they are not used while the new
** authorizer is still on the stack. If the F flag to
** sqlite3_push_authorizer(D,X,P,F) is zero, that means that
** the new authorizer runs first, but preexisting authorizers are also
** run and the result is the most restrictive result returned by
** any authorizer. SQLITE_DENY is more restrictive than SQLITE_IGNORE,
** and SQLITE_IGNORE is more restrictive than SQLITE_OK.
**
** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
** A call to sqlite3_push_authorizer() will return SQLITE_NOMEM if it
** is unable to allocate memory to hold prior authorizers.
**
** Use sqlite3_push_authorizer(D,0,0,1) to disable all authorizers on
** database connection D temporarily, but remember prior authorizers
** so that they are restored at the next sqlite3_pop_authorizer(D).
**
** It is illegal to invoke sqlite3_set_authorizer() if there are pending
** (unpopped) calls to sqlite3_push_authorizer(). Doing so can lead
** to memory leaks. The use of sqlite3_set_authorizer() is discouraged.
** New applications should use only sqlite3_push_authorizer() and
** sqlite3_pop_authorizer().
**
** <h2>Important Details</h2>
**
** When [sqlite3_prepare_v2()] is used to prepare a statement, the
** statement might be re-prepared during [sqlite3_step()] due to a
** schema change. Hence, the application should ensure that the
** correct authorizer callback remains in place during the [sqlite3_step()].
**
** ^Note that the authorizer callback is invoked only during
** Note that the authorizer callback is invoked only during
** [sqlite3_prepare()] or its variants. Authorization is not
** performed during statement evaluation in [sqlite3_step()], unless
** as stated in the previous paragraph, sqlite3_step() invokes
@@ -3477,6 +3499,13 @@ int sqlite3_set_authorizer(
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pUserData
);
int sqlite3_push_authorizer(
sqlite3*,
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pUserData,
int bOvrd
);
int sqlite3_pop_authorizer(sqlite3*);
/*
** CAPI3REF: Authorizer Return Codes
+1 -1
View File
@@ -202,7 +202,7 @@
#include "sqlite3.h"
/*
** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory.
** Reuse the STATIC_VFS1 for mutex access to sqlite3_temp_directory.
*/
#define SQLITE_MUTEX_STATIC_TEMPDIR SQLITE_MUTEX_STATIC_VFS1
+11 -6
View File
@@ -465,15 +465,20 @@ u8 sqlite3StrIHash(const char *z){
return h;
}
#if !defined(SQLITE_DISABLE_INTRINSIC) \
&& (defined(__GNUC__) || defined(__clang__)) \
&& (defined(__x86_64__) || defined(__aarch64__) || \
(defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen>32)))
#define SQLITE_USE_UINT128
#endif
/*
** Two inputs are multiplied to get a 128-bit result. Write the
** lower 64-bits of the result into *pLo, and return the high-order
** 64 bits.
*/
static u64 sqlite3Multiply128(u64 a, u64 b, u64 *pLo){
#if (defined(__GNUC__) || defined(__clang__)) \
&& (defined(__x86_64__) || defined(__aarch64__) || defined(__riscv)) \
&& !defined(SQLITE_DISABLE_INTRINSIC)
#if defined(SQLITE_USE_UINT128)
__uint128_t r = (__uint128_t)a * b;
*pLo = (u64)r;
return (u64)(r>>64);
@@ -507,9 +512,7 @@ static u64 sqlite3Multiply128(u64 a, u64 b, u64 *pLo){
** The lower 64 bits of A*B are discarded.
*/
static u64 sqlite3Multiply160(u64 a, u32 aLo, u64 b, u32 *pLo){
#if (defined(__GNUC__) || defined(__clang__)) \
&& (defined(__x86_64__) || defined(__aarch64__) || defined(__riscv)) \
&& !defined(SQLITE_DISABLE_INTRINSIC)
#if defined(SQLITE_USE_UINT128)
__uint128_t r = (__uint128_t)a * b;
r += ((__uint128_t)aLo * b) >> 32;
*pLo = (r>>32)&0xffffffff;
@@ -547,6 +550,8 @@ static u64 sqlite3Multiply160(u64 a, u32 aLo, u64 b, u32 *pLo){
#endif
}
#undef SQLITE_USE_UINT128
/*
** Return a u64 with the N-th bit set.
*/
+52 -7
View File
@@ -708,7 +708,6 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){
return h;
}
/*
** For OP_Column, factor out the case where content is loaded from
** overflow pages, so that the code to implement this case is separate
@@ -794,6 +793,40 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
return rc;
}
/*
** Memory cell pMem may contain a blob or a NULL value. Cursor pCsr is
** open on an index. If the current index entry matches the blob value in
** pMem byte-for-byte, set pMem to NULL and return 1. Otherwise, return 0.
**
** If an error occurs, set (*pRc) to an SQLite error code. Return 1 in this
** case as well.
*/
static SQLITE_NOINLINE int vdbeIndexKeyCompare(
BtCursor *pCsr, /* Cursor to compare key to */
Mem *pMem,
int *pRc
){
int ret = 0;
u32 nKey = 0;
assert( pMem->flags & (MEM_Blob|MEM_Null) );
nKey = sqlite3BtreePayloadSize(pCsr);
if( nKey==(u32)pMem->n && ALWAYS((pMem->flags & MEM_Blob)!=0) ){
/* This code could just use sqlite3BtreePayloadFetch(). But calling that
** function here apparently prevents compilers from inlining it in other,
** more performance critical, places. So this code uses
** MemFromBtreeZeroOffset(), which is just as fast in most cases, but also
** handles the case where the index record uses overflow pages. */
Mem m;
memset(&m, 0, sizeof(m));
*pRc = sqlite3VdbeMemFromBtreeZeroOffset(pCsr, nKey, &m);
ret = (*pRc!=SQLITE_OK || 0==memcmp(pMem->z, m.z, nKey));
sqlite3VdbeMemReleaseMalloc(&m);
}
return ret;
}
/*
** Send a "statement aborts" message to the error log.
*/
@@ -6622,15 +6655,20 @@ case OP_SorterInsert: { /* in2 */
break;
}
/* Opcode: IdxDelete P1 P2 P3 P4 *
** Synopsis: key=r[P2@P3]
/* Opcode: IdxDelete P1 P2 P3 P4 P5
** Synopsis: key=r[P2@P5]
**
** The content of P3 registers starting at register P2 form
** The content of P5 registers starting at register P2 form
** an unpacked index key. This opcode removes that entry from the
** index opened by cursor P1.
**
** P4 is a pointer to an Index structure.
**
** If P3 is non-zero, it is the register number of a register holding
** a record that will be inserted into this index. If that record is
** identical to the one that would be deleted by this instruction,
** skip the delete and set register P3 to NULL.
**
** Raise an SQLITE_CORRUPT_INDEX error if no matching index entry is found
** and not in writable_schema mode.
*/
@@ -6640,8 +6678,8 @@ case OP_IdxDelete: {
int res;
UnpackedRecord r;
assert( pOp->p3>0 );
assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 );
assert( pOp->p5>0 );
assert( pOp->p2>0 && pOp->p2+pOp->p5<=(p->nMem+1 - p->nCursor)+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
@@ -6650,7 +6688,7 @@ case OP_IdxDelete: {
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
r.pKeyInfo = pC->pKeyInfo;
r.nField = (u16)pOp->p3;
r.nField = pOp->p5;
r.default_rc = 0;
r.aMem = &aMem[pOp->p2];
rc = sqlite3BtreeIndexMoveto(pCrsr, &r, &res);
@@ -6669,6 +6707,13 @@ case OP_IdxDelete: {
break;
}
}
if( pOp->p3 && vdbeIndexKeyCompare(pCrsr, &aMem[pOp->p3], &rc) ){
if( rc ) goto abort_due_to_error;
sqlite3VdbeMemSetNull(&aMem[pOp->p3]);
break;
}
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
if( rc ) goto abort_due_to_error;
assert( pC->deferredMoveto==0 );
+8
View File
@@ -283,5 +283,13 @@ do_execsql_test 12.5 {
"IsActive" INTEGER)}
}
do_execsql_test 12.6 {
CREATE TABLE t1(a, b CONSTRAINT "a""b" CHECK (b IS NOT NULL));
}
do_execsql_test 12.7 {
ALTER TABLE t1 DROP CONSTRAINT "a""b"
}
finish_test
+21
View File
@@ -41,5 +41,26 @@ do_faultsim_test 2 -faults oom* -prep {
faultsim_test_result {0 ABCDEF}
}
#-------------------------------------------------------------------------
reset_db
do_execsql_test 3.0 {
PRAGMA page_size=1024;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE INDEX i1 ON t1( hex(b) );
INSERT INTO t1 VALUES(10, randomblob(500));
}
faultsim_save_and_close
do_faultsim_test 3 -faults oom* -prep {
faultsim_restore_and_reopen
} -body {
execsql {
UPDATE t1 SET b=randomblob(500);
}
} -test {
faultsim_test_result {0 {}}
}
finish_test
+86
View File
@@ -0,0 +1,86 @@
# 2026-04-23
#
# 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.
#
#***********************************************************************
#
# Tests for omitting the update of an index when the new value is the
# same as the old.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix expridx2
# This test requires wal mode, but the journaltest permutation installs
# a custom VFS that does not support wal mode.
if {[permutation]=="journaltest"} {
finish_test
return
}
do_execsql_test 1.0 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
INSERT INTO t1 VALUES(1, '{a: 10, b: 20, c: 30}');
INSERT INTO t1 VALUES(2, '{a: 11, b: 21, c: 31}');
INSERT INTO t1 VALUES(3, '{a: 12, b: 22, c: 32}');
INSERT INTO t1 VALUES(4, '{a: 13, b: 23, c: 33}');
CREATE INDEX t1a ON t1( (b->>'a') );
CREATE INDEX t1b ON t1( (b->>'b') );
CREATE INDEX t1c ON t1( (b->>'c') );
CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX t2b ON t2( b );
INSERT INTO t2 VALUES(1, 1, 1);
INSERT INTO t2 VALUES(2, 2, 2);
INSERT INTO t2 VALUES(3, 3, 3);
INSERT INTO t2 VALUES(4, 4, 4);
} {wal}
proc nWrite {sql} {
sqlite3_db_status db CACHE_WRITE 1
execsql $sql
lindex [sqlite3_db_status db CACHE_WRITE 0] 1
}
do_test 1.1 {
nWrite { UPDATE t1 SET b = json_set(b, '$.a', b->>'a' + 1) }
} {2}
do_test 1.2 {
nWrite {
UPDATE t1 SET b = json_set(
json_set(b, '$.a', b->>'a' + 1),
'$.b', b->>'b' + 1
);
}
} {3}
do_test 1.3 {
nWrite {
UPDATE t1 SET b = '{a:1, b:2, c:3}' WHERE rowid=1;
}
} {4}
integrity_check 1.4
do_test 1.5 {
nWrite { UPDATE t2 SET b = b+1, c=c+1 }
} {2}
do_test 1.6 {
nWrite { UPDATE t2 SET b = b, c=c+1 }
} {2}
do_test 1.7 {
nWrite { UPDATE t2 SET c=c+1 }
} {1}
finish_test
+61
View File
@@ -280,4 +280,65 @@ do_test 900 {
} 1
dbempty close
#-------------------------------------------------------------------------
# Make sure a database cannot be overwritten by deserialize() if
# there is an open transaction on it.
#
do_execsql_test 1000 {
CREATE TABLE t(x);
INSERT INTO t VALUES(1),(2);
}
set blob [db serialize main]
do_test 1010 {
set seen 0
list [catch {
db eval {SELECT x FROM t} {
incr seen
if {$seen == 1} {
db deserialize main $blob
}
}
} msg] $msg
} {1 {unable to set MEMDB content}}
forcedelete test.db2
sqlite3 db2 test.db2
do_test 1020 {
set seen 0
sqlite3_backup B db2 main db main
B step 2
set res [list [catch {
db deserialize main $blob
} msg] $msg]
B finish
set res
} {1 {unable to set MEMDB content}}
db2 close
#-------------------------------------------------------------------------
# Check that things work if the destination database of a backup is
# overwritten using sqlite3_deserialize() between sqlite3_backup_init()
# and the first call to sqlite3_backup_step(). Forum post 15d82885e2.
#
reset_db
do_execsql_test 1100 {
CREATE TABLE t(x);
INSERT INTO t VALUES(1),(2);
}
set blob [db serialize main]
forcedelete test.db2
sqlite3 db2 test.db2
do_test 1110 {
set seen 0
sqlite3_backup B db2 main db main
db2 deserialize main $blob
B step 2
B finish
} {SQLITE_OK}
finish_test
+27 -19
View File
@@ -28,9 +28,9 @@
.testcase 110
.prompt --show
.check <<END
Main prompt: 'SQLite /f> '
Continuation: '/B.../H> '
.check --glob <<END
Main prompt: '*/A*/f*-> '
Continuation: '/B*/C*-> '
END
.testcase 111
.prompt 'abc> ' '123> ' -show
@@ -47,9 +47,9 @@ Continuation: '--second'
END
.testcase 113
.prompt --reset --show
.check <<END
Main prompt: 'SQLite /f> '
Continuation: '/B.../H> '
.check --glob <<END
Main prompt: '*/A*/f*> '
Continuation: '/B*/C*> '
END
.testcase 120 --error-prefix ERROR:
@@ -64,9 +64,9 @@ END
.check ''
.testcase 122
.prompt --show
.check <<END
.check --glob <<END
Main prompt: 'show'
Continuation: '/B.../H> '
Continuation: '/B*/C*> '
END
.testcase 130
@@ -90,31 +90,31 @@ END
.testcase 1000
SELECT shell_prompt_test(NULL);
.check 'SQLite test.db> ';
.check --glob '*SQLite-3*test.db*-> ';
.testcase 1001
SELECT shell_prompt_test(NULL,'SELECT');
.check ' ...;> ';
.check --glob ' *;*-> ';
.testcase 1002
SELECT shell_prompt_test(NULL,'SELECT ((("');
.check ' ...")));> ';
.check --glob ' *[ m]")));*-> ';
.testcase 1003
SELECT shell_prompt_test(NULL,'SELECT ((()[');
.check ' ...]));> ';
.check --glob ' *[ m]]));*-> ';
.testcase 1004
SELECT shell_prompt_test(NULL,'SELECT ''');
.check " ...';> ";
.check --glob " *[ m]';*-> ";
.testcase 1005
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN');
.check " ...;END;> ";
.check --glob " *[ m];END;*-> ";
.testcase 1006
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((([');
.check " ...])));END;> ";
.check --glob " *[ m]])));END;*-> ";
.testcase 1007
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT ((/*a(((''bc');
.check " ...*/));END;> ";
.check --glob " *[ m][*]/));END;*-> ";
.testcase 1008
SELECT shell_prompt_test(NULL,'CREATE TRIGGER t1 BEGIN SELECT 1;');
.check " ...END;> ";
.check --glob " *[ m]END;*-> ";
.testcase 2000
.prompt 'SQLite/x-txn$/:>/; '
@@ -135,6 +135,14 @@ SELECT shell_prompt_test(NULL);
.testcase 2004
.prompt --reset
SELECT shell_prompt_test(NULL);
.check 'SQLite test.db> ';
.check --glob '*SQLite-3.# *test.db*-> ';
.testcase
.testcase 3000
SELECT shell_prompt_test('(/A-/V)');
.check --glob '(SQLite-3.#.#)'
.testcase 3001
SELECT shell_prompt_test('(/A-/v)');
.check --glob '(SQLite-3.#)'
.testcase 3002
SELECT shell_prompt_test('(/A-/D%Y-%m-%dT%H:%M:%S/D)');
.check --glob '(SQLite-20#-#-#T#:#:#)'
+7 -1
View File
@@ -1969,6 +1969,7 @@ proc run_testset {} {
SELECT DISTINCT substr(svers,1,79) as v1 FROM jobs WHERE svers IS NOT NULL
} {puts $v1}
return [expr {$nErr>0}]
}
# If the argument is "retest", simply rerun all tests from the previous
@@ -2028,6 +2029,7 @@ proc explain_tests {} {
explain_layer "" ""
}
set exit_status 0
sqlite3 trdb $TRG(dbname)
trdb timeout $TRG(timeout)
if {[llength $TRG(patternlist)]==1 && $TRG(patternlist) eq "retest"} {
@@ -2046,6 +2048,10 @@ if {$TRG(explain)} {
puts "built testset in [expr $tm/1000]ms.."
}
handle_buildonly
run_testset
set exit_status [run_testset]
}
trdb close
exit $exit_status
+10
View File
@@ -787,4 +787,14 @@ do_eqp_test update-21.12 {
`--SEARCH t3 USING AUTOMATIC COVERING INDEX (x=?)
}
# /forumpost/2026-04-21T19:10:55Z
do_execsql_test update-22.0 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(x INT, y INT);
INSERT INTO t1(x) VALUES(1),(2),(3),(4),(5);
UPDATE t1 SET x=x+100, y=x<=(SELECT min(x) FROM t1)
WHERE x<3 OR (1 BETWEEN 0 AND x<=(SELECT min(x)+2 FROM t1));
SELECT x FROM t1 WHERE x<100 ORDER BY x;
} {4 5}
finish_test
+1 -1
View File
@@ -1900,7 +1900,7 @@ static void showHelp(void){
}
/* work-around the Microsoft "WorstFit" bug */
#ifdef _WIN32
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(main)
#define main utf8_main
#endif