From d74c9cac3541b9400129188cc79ddbbe8f187da7 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 18 Jul 2023 15:06:58 +0000 Subject: [PATCH] Experimental (untested, non-working) changes that try reduce the number of reprepares generated by sqlite3_stmt_explain(). I think I see an easier way to do this now, so I'm parking this experiment on a branch to pursue the new idea. FossilOrigin-Name: c2fba6a6322bff1037c05bcc184b292faf1e9e7d778edc176b0bf9597a36d8f8 --- manifest | 32 ++++++++-------- manifest.uuid | 2 +- src/delete.c | 2 +- src/pragma.c | 6 +-- src/select.c | 24 +++++++----- src/sqlite.h.in | 29 +++++++++------ src/vdbe.h | 9 ++++- src/vdbeInt.h | 7 +++- src/vdbeapi.c | 97 +++++++++++++++++++++++++++++++++++-------------- src/vdbeaux.c | 54 +++++++++++++++++++-------- 10 files changed, 173 insertions(+), 89 deletions(-) diff --git a/manifest b/manifest index 560436430f..73162081e6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sexperimental\ssqlite3_stmt_explain(S,E)\sinterface. -D 2023-07-15T16:48:14.281 +C Experimental\s(untested,\snon-working)\schanges\sthat\stry\sreduce\sthe\nnumber\sof\sreprepares\sgenerated\sby\ssqlite3_stmt_explain().\s\sI\sthink\sI\ssee\nan\seasier\sway\sto\sdo\sthis\snow,\sso\sI'm\sparking\sthis\sexperiment\son\sa\sbranch\nto\spursue\sthe\snew\sidea. +D 2023-07-18T15:06:58.759 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -586,7 +586,7 @@ F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94 F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387 F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef -F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be +F src/delete.c 57079fa54b956a67bc42f246d8436e6b09f49836863442c7a84bb733f4e96b1f F src/expr.c 8d1656b65e26af3e34f78e947ac423f0d20c214ed25a67486e433bf16ca6b543 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 @@ -630,16 +630,16 @@ F src/parse.y aeb7760d41cfa86465e3adba506500c021597049fd55f82a30e5b7045862c28c F src/pcache.c 4cd4a0043167da9ba7e19b4d179a0e6354e7fe32c16f781ecf9bf0a5ff63b40b F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5 F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00 -F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a +F src/pragma.c 76621ce5e91b53d632ec88120e9dba731f857f63b830364158a7494da58bfd13 F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 80548297dc0e1fb3139cdebffb5a1bcac3dfac66d791012dd74838e70445072d F src/printf.c 84b7b4b647f336934a5ab2e7f0c52555833cc0778d2d60e016cca52ee8c6cd8f F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 -F src/select.c 3ab1186290a311a8ceed1286c0e286209f7fe97b2d02c7593258004ce295dd88 +F src/select.c ad0a31b6aab191c2f19dba409d052f1ccc7f2ee5fef1913da73506c9ec747a06 F src/shell.c.in 3f125426a25c717fbf8b84b3b75a8b60fa989bf7a039ed38926d455329f9dd0f -F src/sqlite.h.in 13d5458cd23e7b4759cff4d978ad09591a457b4a2821993579953a9a4257ce0f +F src/sqlite.h.in 974ab81077091f0593db3199f2c4a1fd5335a3ff58af7ba0eabd6aef2e174d35 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4 F src/sqliteInt.h dcb1a885e8b6cb78df618944b89d44361a99d0fe33e1bba2c150a855f7dc5599 @@ -708,10 +708,10 @@ F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c fea6fffdee3cdae917a66b70deec59d4f238057cfd6de623d15cf990c196940d F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 74282a947234513872a83b0bab1b8c644ece64b3e27b053ef17677c8ff9c81e0 -F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 -F src/vdbeInt.h 7bd49eef8f89c1a271fbf12d80a206bf56c876814c5fc6bee340f4e1907095ae -F src/vdbeapi.c c528ef4fafc8be172cbe4f48d8f15c9526bebde9f90185917fcc43fad264bcb6 -F src/vdbeaux.c b5e3f7e158518b4eca6f166ac43900640a3fe9735c710e12bfa119af21059339 +F src/vdbe.h bc8590f094dd93aa577511df86c8be81b32b7483c65a9c168ce53acd52d67cf8 +F src/vdbeInt.h ac7afe3ae89786771774781dbe324d47f29d93d15bf556de798d7c0177a3fa1b +F src/vdbeapi.c 4da107457d0913f93d6c1c0c315eab5b99cc8a864e3f224e4389da82b2c167e5 +F src/vdbeaux.c b4dbf5dd83b442f50991dbb38a2c52641f3f01e0ec0968ad4cc2f5db06a009f1 F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce F src/vdbemem.c cf4a1556dd5b18c071cf7c243373c29ce752eb516022e3ad49ba72f08b785033 F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015 @@ -2043,11 +2043,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 984d491eb3fe06f714bf07d6873321f3992a072812b46508e599bfefd39dff3e -R d7676f37f2e59b0f85cc021dadb3d7bc -T *branch * sqlite3_stmt_explain -T *sym-sqlite3_stmt_explain * -T -sym-trunk * +P 5683743ddf0bb051f2fe5d137cc18407e000e77e9faa9010a22e3134b575638b +R a22ec6c79a7381f26aabf921b683ab91 +T *branch * sqlite3-stmt-explain-opt1 +T *sym-sqlite3-stmt-explain-opt1 * +T -sym-sqlite3_stmt_explain * U drh -Z bc12630434faa3e2cbe8d73f4285c221 +Z 3fd23720e07a20d698e2dc19a24f7360 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8363f6b593..d86083b128 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5683743ddf0bb051f2fe5d137cc18407e000e77e9faa9010a22e3134b575638b \ No newline at end of file +c2fba6a6322bff1037c05bcc184b292faf1e9e7d778edc176b0bf9597a36d8f8 \ No newline at end of file diff --git a/src/delete.c b/src/delete.c index 641d1ae589..b7dae30cb9 100644 --- a/src/delete.c +++ b/src/delete.c @@ -52,7 +52,7 @@ void sqlite3CodeChangeCount(Vdbe *v, int regCounter, const char *zColName){ sqlite3VdbeAddOp0(v, OP_FkCheck); sqlite3VdbeAddOp2(v, OP_ResultRow, regCounter, 1); sqlite3VdbeSetNumCols(v, 1); - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, SQLITE_STATIC); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zColName, COLNAME_STATIC); } /* Return true if table pTab is read-only. diff --git a/src/pragma.c b/src/pragma.c index f15c1be279..afbca6a60b 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -174,11 +174,11 @@ static void setPragmaResultColumnNames( u8 n = pPragma->nPragCName; sqlite3VdbeSetNumCols(v, n==0 ? 1 : n); if( n==0 ){ - sqlite3VdbeSetColName(v, 0, COLNAME_NAME, pPragma->zName, SQLITE_STATIC); + sqlite3VdbeSetColName(v, 0, COLNAME_NAME, pPragma->zName, COLNAME_STATIC); }else{ int i, j; for(i=0, j=pPragma->iPragCName; ia[i].zEName && pEList->a[i].fg.eEName==ENAME_NAME ){ /* An AS clause always takes first priority */ char *zName = pEList->a[i].zEName; - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, COLNAME_TRANSIENT); }else if( srcName && p->op==TK_COLUMN ){ char *zCol; int iCol = p->iColumn; @@ -2145,15 +2145,19 @@ void sqlite3GenerateColumnNames( } if( fullName ){ char *zName = 0; - zName = sqlite3MPrintf(db, "%s.%s", pTab->zName, zCol); - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_DYNAMIC); + zName = sqlite3MPrintf(db, "\001%s.%s", pTab->zName, zCol); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, COLNAME_DYNAMIC); }else{ - sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, COLNAME_TRANSIENT); } }else{ const char *z = pEList->a[i].zEName; - z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z); - sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC); + if( z==0 ){ + z = sqlite3MPrintf(db, "\001column%d", i+1); + sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, COLNAME_DYNAMIC); + }else{ + sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, COLNAME_TRANSIENT); + } } } generateColumnTypes(pParse, pTabList, pEList); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 9e165c1e55..7aca51b896 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -4431,18 +4431,23 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); ** its SQL text began with "EXPLAIN". If E is 2, then S behaves as if ** its SQL text began with "EXPLAIN QUERY PLAN". ** -** Calling sqlite3_stmt_explain(S,E) causes prepared statement S -** to be reprepared. A call to sqlite3_stmt_explain(S,E) will fail -** with SQLITE_ERROR if S cannot be re-prepared because it was created -** using sqlite3_prepare() instead of the newer sqlite_prepare_v2() or -** sqlite3_prepare_v3() interfaces and hence has no saved SQL text with -** which to reprepare. Changing the explain setting for a prepared -** statement does not change the original SQL text for the statement. -** Hence, if the SQL text originally began with EXPLAIN or EXPLAIN -** QUERY PLAN, but sqlite3_stmt_explain(S,0) is called to convert the -** statement into an ordinary statement, the EXPLAIN or EXPLAIN QUERY -** PLAN keywords still appear in the sqlite3_sql(S) output, even though -** the statement now acts like a normal SQL statement. +** Calling sqlite3_stmt_explain(S,E) might cause prepared statement S +** to be reprepared. SQLite tries to avoid a reprepare, but a reprepare +** might be necessary on the first transition into EXPLAIN or EXPLAIN QUERY +** PLAN mode. +** +** Because of the potential need to reprepare, a call to +** sqlite3_stmt_explain(S,E) might fail with SQLITE_ERROR if S cannot be +** reprepared because it was created using sqlite3_prepare() instead of +** the newer sqlite_prepare_v2() or sqlite3_prepare_v3() interfaces and +** hence has no saved SQL text with which to reprepare. +** +** Changing the explain setting for a prepared statement does not change +** the original SQL text for the statement. Hence, if the SQL text originally +** began with EXPLAIN or EXPLAIN QUERY PLAN, but sqlite3_stmt_explain(S,0) +** is called to convert the statement into an ordinary statement, the EXPLAIN +** or EXPLAIN QUERY PLAN keywords will still appear in the sqlite3_sql(S) +** output, even though the statement now acts like a normal SQL statement. ** ** This routine returns SQLITE_OK if the explain mode is successfully ** changed, or an error code if the explain mode could not be changed. diff --git a/src/vdbe.h b/src/vdbe.h index f44f24f93e..ad038e5e40 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -152,6 +152,13 @@ typedef struct VdbeOpList VdbeOpList; # endif #endif +/* +** zName argument lifetimes for calls to sqlite3VdbeSetColName(). +*/ +#define COLNAME_DYNAMIC 0 +#define COLNAME_TRANSIENT 1 +#deifne COLNAME_STATIC 2 + /* ** The following macro converts a label returned by sqlite3VdbeMakeLabel() ** into an index into the Parse.aLabel[] array that contains the resolved @@ -265,7 +272,7 @@ void sqlite3VdbeResetStepResult(Vdbe*); void sqlite3VdbeRewind(Vdbe*); int sqlite3VdbeReset(Vdbe*); void sqlite3VdbeSetNumCols(Vdbe*,int); -int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); +int sqlite3VdbeSetColName(Vdbe*, int, int, const char*, int); void sqlite3VdbeCountChanges(Vdbe*); sqlite3 *sqlite3VdbeDb(Vdbe*); u8 sqlite3VdbePrepareFlags(Vdbe*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 4c3394716b..0309b81cad 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -453,7 +453,9 @@ struct Vdbe { Op *aOp; /* Space to hold the virtual machine's program */ int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Slots allocated for aOp[] */ - Mem *aColName; /* Column names to return */ + char **azColName; /* Column names. 1st byte determines format: + ** 1: UTF-8, 2: UTF-16, other: static UTF-8 + ** See tag-20230718-1 */ Mem *pResultRow; /* Current output row */ char *zErrMsg; /* Error message written here */ VList *pVList; /* Name of variables */ @@ -470,11 +472,12 @@ struct Vdbe { u8 prepFlags; /* SQLITE_PREPARE_* flags */ u8 eVdbeState; /* On of the VDBE_*_STATE values */ bft expired:2; /* 1: recompile VM immediately 2: when convenient */ - bft explain:2; /* True if EXPLAIN present on SQL command */ + bft explain:2; /* 0: normal, 1: EXPLAIN, 2: EXPLAIN QUERY PLAN */ bft changeCntOn:1; /* True to update the change-counter */ bft usesStmtJournal:1; /* True if uses a statement journal */ bft readOnly:1; /* True for statements that do not write */ bft bIsReader:1; /* True for statements that read */ + bft haveEqpOps:1; /* Bytecode supports EXPLAIN QUERY PLAN */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ yDbMask lockMask; /* Subset of btreeMask that requires a lock */ u32 aCounter[9]; /* Counters used by sqlite3_stmt_status() */ diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 885b17f762..2b1db78cd0 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -1126,7 +1126,10 @@ int sqlite3_aggregate_count(sqlite3_context *p){ */ int sqlite3_column_count(sqlite3_stmt *pStmt){ Vdbe *pVm = (Vdbe *)pStmt; - return pVm ? pVm->nResColumn : 0; + if( pVm==0 ) return 0; + if( pVM->explain==0 ) return pVm->nResColumn; + if( pVm->explain==1 ) return 8; + return 3; } /* @@ -1299,6 +1302,32 @@ int sqlite3_column_type(sqlite3_stmt *pStmt, int i){ return iType; } +/* +** Column names appropriate for EXPLAIN or EXPLAIN QUERY PLAN. +*/ +static const char * const azExplainColNames8[] = { + "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", /* EXPLAIN */ + "id", "parent", "notused", "detail" /* EQP */ +}; +static const u16 azExplainColNames16data[] = { + /* 0 */ 'a', 'd', 'd', 'r', 0, + /* 5 */ 'o', 'p', 'c', 'o', 'd', 'e', 0, + /* 12 */ 'p', '1', 0, + /* 15 */ 'p', '2', 0, + /* 18 */ 'p', '3', 0, + /* 21 */ 'p', '4', 0, + /* 24 */ 'p', '5', 0, + /* 27 */ 'c', 'o', 'm', 'm', 'e', 'n', 't', 0, + /* 35 */ 'i', 'd', 0, + /* 38 */ 'p', 'a', 'r', 'e', 'n', 't', 0, + /* 45 */ 'n', 'o', 't', 'u', 's', 'e', 'd', 0, + /* 53 */ 'd', 'e', 't', 'a', 'i', 'l', 0 +}; +static const u8 iExplainColNames16[] = { + 0, 5, 12, 15, 18, 21, 24, 27, + 35, 38, 45, 53 +}; + /* ** Convert the N-th element of pStmt->pColName[] into a string using ** xFunc() then return that string. If N is out of range, return 0. @@ -1321,7 +1350,7 @@ static const void *columnName( int useUtf16, /* True to return the name as UTF16 */ int useType /* What type of name */ ){ - const void *ret; + const void *ret = 0; Vdbe *p; int n; sqlite3 *db; @@ -1331,33 +1360,39 @@ static const void *columnName( return 0; } #endif - ret = 0; - p = (Vdbe *)pStmt; - db = p->db; - assert( db!=0 ); - n = sqlite3_column_count(pStmt); - if( N=0 ){ - u8 prior_mallocFailed = db->mallocFailed; - N += useType*n; - sqlite3_mutex_enter(db->mutex); -#ifndef SQLITE_OMIT_UTF16 + + if( N<0 ) goto columnName_end; + p = (Vdbe*)pStmt; + sqlite3_mutex_enter(p->db->mutex); + if( p->explain ){ + if( useType>0 ) goto columnName_end; + n = p->explain==1 ? 8 : 4; + if( N>=n ) goto columnName_end; if( useUtf16 ){ - ret = sqlite3_value_text16((sqlite3_value*)&p->aColName[N]); - }else -#endif - { - ret = sqlite3_value_text((sqlite3_value*)&p->aColName[N]); + int i = iExplainColNames16[N + 8*p->explain - 8]; + ret = (void*)&azExplainColNames16data[i]; + }else{ + ret = (void*)azExplainColNames8[N + 8*p->explain - 8]; } - /* A malloc may have failed inside of the _text() call. If this - ** is the case, clear the mallocFailed flag and return NULL. - */ - assert( db->mallocFailed==0 || db->mallocFailed==1 ); - if( db->mallocFailed > prior_mallocFailed ){ - sqlite3OomClear(db); - ret = 0; + goto columnName_end; + } + n = p->nResColumn; + if( N>=n ) goto columnName_end; + N += useType*n; + if( p->azColName[N]==0 ) goto columnName_end; + if( useUtf16 ){ + if( p->azColName[N][0]!=2 ){ + /* Convert UTF-8 to UTF-16 for p->azColName[N] */ + } + }else{ + if( p->azColName[N][0]==2 ){ + /* Convert UTF-16 to UTF-8 for p->azColName[N] */ } - sqlite3_mutex_leave(db->mutex); } + ret = p->azColName[N]; + +columnName_end: + sqlite3_mutex_leave(p->db->mutex); return ret; } @@ -1824,8 +1859,16 @@ int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ if( v->explain==eMode ) return SQLITE_OK; if( v->zSql==0 || eMode<0 || eMode>2 ) return SQLITE_ERROR; sqlite3_mutex_enter(v->db->mutex); - v->explain = eMode; - rc = sqlite3Reprepare(v); + if( v->nMem>=10 && (eMode!=2 || v->haveEqpOps) ){ + /* No reprepare necessary */ + v->explain = eMode; + rc = SQLITE_OK; + }else{ + int haveEqpOps = v->explain==2 || v->haveEqpOps; + v->explain = eMode; + rc = sqlite3Reprepare(v); + v->haveEqpOps = haveEqpOps!=0; + } sqlite3_mutex_leave(v->db->mutex); return rc; } diff --git a/src/vdbeaux.c b/src/vdbeaux.c index a0eff155dd..a5039f0acf 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2651,13 +2651,15 @@ void sqlite3VdbeMakeReady( resolveP2Values(p, &nArg); p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort); + p->explain = pParse->explain; +#if 0 if( pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", "id", "parent", "notused", "detail" }; int iFirst, mx, i; - if( nMem<10 ) nMem = 10; + if( nMem<10 ) nMem = 10; /******** FIX ME *******/ p->explain = pParse->explain; if( pParse->explain==2 ){ sqlite3VdbeSetNumCols(p, 4); @@ -2673,6 +2675,7 @@ void sqlite3VdbeMakeReady( azColName[i], SQLITE_STATIC); } } +#endif p->expired = 0; /* Memory for registers, parameters, cursor, etc, is allocated in one or two @@ -2825,14 +2828,18 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){ sqlite3 *db = p->db; if( p->nResColumn ){ - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - sqlite3DbFree(db, p->aColName); + int i; + for(i = p->nResColumn*COLNAME_N - 1; i>=0; i--){ + if( p->azColName[i]!=0 && p->azColName[i][0]<=2 ){ + sqlite3DbNNFreeNN(db, p->azColName[i]); + } + } + sqlite3DbNNFreeNN(db, p->azColName); + p->colNameChng = 1; } n = nResColumn*COLNAME_N; p->nResColumn = (u16)nResColumn; - p->aColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n ); - if( p->aColName==0 ) return; - initMemArray(p->aColName, n, db, MEM_Null); + p->azColName = (char**)sqlite3DbMallocZero(db, sizeof(char**)*n ); } /* @@ -2850,20 +2857,30 @@ int sqlite3VdbeSetColName( int idx, /* Index of column zName applies to */ int var, /* One of the COLNAME_* constants */ const char *zName, /* Pointer to buffer containing name */ - void (*xDel)(void*) /* Memory management strategy for zName */ + int eNmType /* COLNAME_DYNAMIC, _TRANSIENT, or _STATIC */ ){ int rc; - Mem *pColName; assert( idxnResColumn ); assert( var=COLNAME_DYNAMIC && eNmType<=COLNAME_STATIC ); if( p->db->mallocFailed ){ - assert( !zName || xDel!=SQLITE_DYNAMIC ); + assert( !zName || eNmType!=COLNAME_DYNAMIC ); return SQLITE_NOMEM_BKPT; } - assert( p->aColName!=0 ); - pColName = &(p->aColName[idx+var*p->nResColumn]); - rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, xDel); - assert( rc!=0 || !zName || (pColName->flags&MEM_Term)!=0 ); + assert( zName!=0 ); + assert( zName[0]!=0 ); + assert( eNmType1=COLNAME_DYNAMIC || zName[0]==1 ); /* tag-20230718-1 */ + assert( p->azColName!=0 ); + if( eNmType==COLNAME_TRANSIENT ){ + int n = sqlite3Strlen30(zName); + char *z = sqlite3DbMalloc(p->db, n+2); + if( z==0 ) return SQLITE_NOMEM_BKPT; + z[0] = 1; /* Dynamic UTF-8. tag-20230718-1 */ + memcpy(&z[1], zName, n+1); + zName = (const char*)z; + } + assert( p->azColName[idx+var*p->nResColumn]==0 ); + p->azColName[idx+var*p->nResColumn] = zName; return rc; } @@ -3684,9 +3701,14 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ SubProgram *pSub, *pNext; assert( db!=0 ); assert( p->db==0 || p->db==db ); - if( p->aColName ){ - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - sqlite3DbNNFreeNN(db, p->aColName); + if( p->azColName ){ + int i; + for(i = p->nResColumn*COLNAME_N - 1; i>=0; i--){ + if( p->azColName[i]!=0 && p->azColName[i][0]<=2 ){ + sqlite3DbNNFreeNN(db, p->azColName[i]); + } + } + sqlite3DbNNFreeNN(db, p->azColName); } for(pSub=p->pProgram; pSub; pSub=pNext){ pNext = pSub->pNext; -- 2.39.5