]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Experimental (untested, non-working) changes that try reduce the sqlite3-stmt-explain-opt1
authordrh <>
Tue, 18 Jul 2023 15:06:58 +0000 (15:06 +0000)
committerdrh <>
Tue, 18 Jul 2023 15:06:58 +0000 (15:06 +0000)
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
manifest.uuid
src/delete.c
src/pragma.c
src/select.c
src/sqlite.h.in
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c

index 560436430f7135abe4ee31b36eceee6be768902c..73162081e605ab2821bb3108f0e5a56c8ff80f1d 100644 (file)
--- 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.
index 8363f6b5937ef67082a1704a080511fe864d5385..d86083b1289480000f3d401117f298c5c926856a 100644 (file)
@@ -1 +1 @@
-5683743ddf0bb051f2fe5d137cc18407e000e77e9faa9010a22e3134b575638b
\ No newline at end of file
+c2fba6a6322bff1037c05bcc184b292faf1e9e7d778edc176b0bf9597a36d8f8
\ No newline at end of file
index 641d1ae5893f763071f627f9ce6217ce81ef4747..b7dae30cb9fd302ec2e9fb20782fc5dc089ca5f6 100644 (file)
@@ -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.
index f15c1be279570d0ae2c72d3145501dd7902fcac9..afbca6a60bf8517fdba9a7e852364926cdee3007 100644 (file)
@@ -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; i<n; i++, j++){
-      sqlite3VdbeSetColName(v, i, COLNAME_NAME, pragCName[j], SQLITE_STATIC);
+      sqlite3VdbeSetColName(v, i, COLNAME_NAME, pragCName[j], COLNAME_STATIC);
     }
   }
 }
@@ -451,7 +451,7 @@ void sqlite3Pragma(
   rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
   if( rc==SQLITE_OK ){
     sqlite3VdbeSetNumCols(v, 1);
-    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, aFcntl[0], SQLITE_TRANSIENT);
+    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, aFcntl[0], COLNAME_TRANSIENT);
     returnSingleText(v, aFcntl[0]);
     sqlite3_free(aFcntl[0]);
     goto pragma_out;
index 4af812fa06df3ec348f03d316c851c28f3b84e46..7546d374ec3b7e656b51d50c549edf29524adc37 100644 (file)
@@ -2046,13 +2046,13 @@ static void generateColumnTypes(
     ** column specific strings, in case the schema is reset before this
     ** virtual machine is deleted.
     */
-    sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT);
-    sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
-    sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
+    sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, COLNAME_TRANSIENT);
+    sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, COLNAME_TRANSIENT);
+    sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, COLNAME_TRANSIENT);
 #else
     zType = columnType(&sNC, p, 0, 0, 0);
 #endif
-    sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
+    sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, COLNAME_TRANSIENT);
   }
 #endif /* !defined(SQLITE_OMIT_DECLTYPE) */
 }
@@ -2130,7 +2130,7 @@ void sqlite3GenerateColumnNames(
     if( pEList->a[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);
index 9e165c1e55ade89908e27eae1bd9620adfb2bd7a..7aca51b8968ac9d76ea34f6c316c3f0445883ff3 100644 (file)
@@ -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.
index f44f24f93e0d608fb2b8e47d7766f5d653fd7fd9..ad038e5e40a19fa150fcc89b1b85cfc902d0541a 100644 (file)
@@ -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*);
index 4c3394716bfda6136c084253e9628145b68b29be..0309b81cad97e2581027703fd034e187c18340f6 100644 (file)
@@ -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() */
index 885b17f7624c94206aa0508fc29129029314a994..2b1db78cd067f1a7c8598127c0cb0a82446bd14e 100644 (file)
@@ -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<n && 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;
 }
index a0eff155dd9506ff51717b770d76ee5927378d41..a5039f0acf686d49f8e01e1d358633cb39521952 100644 (file)
@@ -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( idx<p->nResColumn );
   assert( var<COLNAME_N );
+  assert( eNmType>=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;