]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Use fewer cycles to generate the "next key" value used by sqlite3_intck_suspend(...
authordan <Dan Kennedy>
Tue, 20 Feb 2024 18:17:06 +0000 (18:17 +0000)
committerdan <Dan Kennedy>
Tue, 20 Feb 2024 18:17:06 +0000 (18:17 +0000)
FossilOrigin-Name: 95f01426f948cf435d0b400dc7ae06fa699eee32cff498fe77e74a1257a4e09b

ext/intck/sqlite3intck.c
manifest
manifest.uuid

index 05eaa81a9347d18d54a58713e66060cb7d1e2997..6d545269281c12bc697fddb2a823f831afb53a0d 100644 (file)
 #include <assert.h>
 #include <stdio.h>
 
+/*
+** apKeyVal:
+**   If sqlite3_intck_suspend() is called when there is a running pCheck
+**   statement, this array is allocated and populated with the key values 
+**   required to restart the check. If the intck object has not been
+**   suspended, this is set to NULL.
+**
+** nKeyVal:
+**   The size of the apKeyVal[] array, if it is allocated.
+*/
 struct sqlite3_intck {
   sqlite3 *db;
   const char *zDb;                /* Copy of zDb parameter to _open() */
   char *zObj;                     /* Current object. Or NULL. */
-  char *zKey;                     /* Key saved by _intck_suspend() call. */
+
   sqlite3_stmt *pCheck;           /* Current check statement */
+  int nKeyVal;
+  sqlite3_value **apKeyVal;
+
   int rc;                         /* Error code */
   char *zErr;                     /* Error message */
   char *zTestSql;                 /* Returned by sqlite3_intck_test_sql() */
@@ -82,18 +95,117 @@ static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
   }
 }
 
+/*
+** Wrapper around sqlite3_malloc64() that uses the sqlite3_intck error
+** code convention.
+*/
+static void *intckMalloc(sqlite3_intck *p, sqlite3_int64 nByte){
+  void *pRet = 0;
+  assert( nByte>0 );
+  if( p->rc==SQLITE_OK ){
+    pRet = sqlite3_malloc64(nByte);
+    if( pRet==0 ){
+      p->rc = SQLITE_NOMEM;
+    }
+  }
+  return pRet;
+}
+
+/*
+** If p->rc is other than SQLITE_OK when this function is called, it
+** immediately returns NULL. Otherwise, it attempts to create a copy of
+** nul-terminated string zIn in a buffer obtained from sqlite3_malloc().
+** If successful, a pointer to this buffer is returned and it becomes
+** the responsibility of the caller to release it using sqlite3_free()
+** at some point in the future.
+**
+** Or, if an allocation fails within this function, p->rc is set to
+** SQLITE_NOMEM and NULL is returned.
+*/
 static char *intckStrdup(sqlite3_intck *p, const char *zIn){
   char *zOut = 0;
+  int nIn = strlen(zIn);
+  zOut = (char*)intckMalloc(p, nIn+1);
+  if( zOut ){
+    memcpy(zOut, zIn, nIn+1);
+  }
+  return zOut;
+}
+
+/*
+** A wrapper around sqlite3_mprintf() that:
+**
+**   + Always returns 0 if p->rc is other than SQLITE_OK when it is called, and
+**   + Sets p->rc to SQLITE_NOMEM if an allocation fails.
+*/
+static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){
+  va_list ap;
+  char *zRet = 0;
+  va_start(ap, zFmt);
+  zRet = sqlite3_vmprintf(zFmt, ap);
   if( p->rc==SQLITE_OK ){
-    int nIn = strlen(zIn);
-    zOut = sqlite3_malloc(nIn+1);
-    if( zOut==0 ){
+    if( zRet==0 ){
       p->rc = SQLITE_NOMEM;
-    }else{
-      memcpy(zOut, zIn, nIn+1);
     }
+  }else{
+    sqlite3_free(zRet);
+    zRet = 0;
+  }
+  return zRet;
+}
+
+/*
+** Free the sqlite3_intck.apKeyVal, if it is allocated and populated.
+*/
+static void intckSavedKeyClear(sqlite3_intck *p){
+  if( p->apKeyVal ){
+    int ii;
+    for(ii=0; ii<p->nKeyVal; ii++){
+      sqlite3_value_free(p->apKeyVal[ii]);
+    }
+    sqlite3_free(p->apKeyVal);
+    p->apKeyVal = 0;
   }
-  return zOut;
+}
+
+/*
+** If the apKeyVal array is currently allocated and populated, return
+** a pointer to a buffer containing a nul-terminated string representing
+** the values as an SQL vector. e.g.
+**
+**   "('abc', NULL, 2)"
+**
+** If apKeyVal is not allocated, return NULL. Or, if an error (e.g. OOM) 
+** occurs within this function, set sqlite3_intck.rc before returning
+** and return NULL.
+*/
+static char *intckSavedKeyToText(sqlite3_intck *p){
+  char *zRet = 0;
+  if( p->apKeyVal ){
+    int ii;
+    const char *zSep = "SELECT '(' || ";
+    char *zSql = 0;
+    sqlite3_stmt *pStmt = 0;
+
+    for(ii=0; ii<p->nKeyVal; ii++){
+      zSql = intckMprintf(p, "%z%squote(?)", zSql, zSep);
+      zSep = " || ', ' || ";
+    }
+    zSql = intckMprintf(p, "%z || ')'", zSql);
+
+    pStmt = intckPrepare(p, "%s", zSql);
+    if( p->rc==SQLITE_OK ){
+      for(ii=0; ii<p->nKeyVal; ii++){
+        sqlite3_bind_value(pStmt, ii+1, p->apKeyVal[ii]);
+      }
+      if( SQLITE_ROW==sqlite3_step(pStmt) ){
+        zRet = intckStrdup(p, (const char*)sqlite3_column_text(pStmt, 0));
+      }
+      intckFinalize(p, pStmt);
+    }
+    sqlite3_free(zSql);
+  }
+  return zRet;
 }
 
 static void intckFindObject(sqlite3_intck *p){
@@ -112,7 +224,7 @@ static void intckFindObject(sqlite3_intck *p){
     "SELECT table_name FROM tables "
     "WHERE ?1 IS NULL OR table_name%s?1 "
     "ORDER BY 1"
-    , p->zDb, (p->zKey ? ">=" : ">")
+    , p->zDb, (p->apKeyVal ? ">=" : ">")
   );
 
   if( p->rc==SQLITE_OK ){
@@ -125,8 +237,7 @@ static void intckFindObject(sqlite3_intck *p){
 
   /* If this is a new object, ensure the previous key value is cleared. */
   if( sqlite3_stricmp(p->zObj, zPrev) ){
-    sqlite3_free(p->zKey);
-    p->zKey = 0;
+    intckSavedKeyClear(p);
   }
 
   sqlite3_free(zPrev);
@@ -332,7 +443,8 @@ static void intckExec(sqlite3_intck *p, const char *zSql){
 static char *intckCheckObjectSql(
   sqlite3_intck *p, 
   const char *zObj,
-  const char *zPrev
+  const char *zPrev,
+  int *pnKeyVal                   /* OUT: Number of key-values for this scan */
 ){
   char *zRet = 0;
   sqlite3_stmt *pStmt = 0;
@@ -377,7 +489,16 @@ static char *intckCheckObjectSql(
       ")"
       ""
       ""
-      ", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk) AS ("
+      /*
+      ** For a PK declared as "PRIMARY KEY(a, b) ... WITHOUT ROWID", where
+      ** the intck_wrapper aliases of "a" and "b" are "c1" and "c2":
+      **
+      **   o_pk:   "o.c1, o.c2"
+      **   i_pk:   "i.'a', i.'b'"
+      **   ...
+      **   n_pk:   2
+      */ 
+      ", tabpk(db, tab, idx, o_pk, i_pk, q_pk, eq_pk, ps_pk, pk_pk, n_pk) AS ("
       "    WITH pkfields(f, a) AS ("
       "      SELECT i.col_name, i.col_alias FROM idx_cols i WHERE i.idx_ispk"
       "    )"
@@ -390,7 +511,8 @@ static char *intckCheckObjectSql(
       "               group_concat(format('\"%w\"', f), ', ')"
       "           ),"
       "           group_concat('%s', ','),"
-      "           group_concat('quote('||a||')', ', ')  "
+      "           group_concat('quote('||a||')', ', '),  "
+      "           count(*)"
       "    FROM tabname t, pkfields"
       ")"
       ""
@@ -467,11 +589,10 @@ static char *intckCheckObjectSql(
       "  FROM tabname t, tabpk p, idx_cols i WHERE i.idx_name=t.idx"
       ")"
       ""
-      ", thiskey(k) AS ("
-      "    SELECT format('format(''(%%s,%%s)'', %%s, %%s) AS thiskey', "
-      "        group_concat('%%s', ','), p.ps_pk, "
-      "        group_concat('quote('||i.col_alias||')',', '), p.pk_pk"
-      "    ) FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx"
+      ", thiskey(k, n) AS ("
+      "    SELECT group_concat(i.col_alias, ', ') || ', ' || p.o_pk, "
+      "           count(*) + p.n_pk "
+      "    FROM tabpk p, idx_cols i WHERE i.idx_name=p.idx"
       ")"
       ""
       ", whereclause(w_c) AS ("
@@ -483,15 +604,15 @@ static char *intckCheckObjectSql(
       "    FROM tabpk, tabname, idx_cols i WHERE i.idx_name=tabpk.idx"
       ")"
       ""
-      ", main_select(m) AS ("
+      ", main_select(m, n) AS ("
       "  SELECT format("
       "      'WITH %%s\nSELECT %%s,\n%%s\nFROM intck_wrapper AS o%%s',"
       "      ww.s, c, t.k, whereclause.w_c"
-      "  )"
+      "  ), t.n"
       "  FROM case_statement, wrapper_with ww, thiskey t, whereclause"
       ")"
 
-      "SELECT m FROM main_select"
+      "SELECT m, n FROM main_select"
       , p->zDb, p->zDb, zObj, zObj
       , zPrev, zCommon
       );
@@ -549,9 +670,8 @@ static char *intckCheckObjectSql(
       **
       **     format('(%d,%d)', _rowid_, n.ii)
       */
-      ", thiskey(k) AS ("
-      "    SELECT 'format(''(' || ps_pk || ',%%d)'', ' || pk_pk || ', n.ii)'"
-      "    FROM tabpk"
+      ", thiskey(k, n) AS ("
+      "    SELECT o_pk || ', n.ii', n_pk+1 FROM tabpk"
       ")"
       ""
       ", whereclause(w_c) AS ("
@@ -562,17 +682,17 @@ static char *intckCheckObjectSql(
       "    FROM tabpk, tabname"
       ")"
       ""
-      ", main_select(m) AS ("
+      ", main_select(m, n) AS ("
       "  SELECT format("
       "      '%%s, %%s\nSELECT %%s,\n%%s AS thiskey\nFROM intck_wrapper AS o"
                ", intck_counter AS n%%s\nORDER BY %%s', "
       "      w, ww.s, c, thiskey.k, whereclause.w_c, t.o_pk"
-      "  )"
+      "  ), thiskey.n"
       "  FROM case_statement, tabpk t, counter_with, "
       "       wrapper_with ww, thiskey, whereclause"
       ")"
 
-      "SELECT m FROM main_select",
+      "SELECT m, n FROM main_select",
       p->zDb, zObj, zPrev, zCommon
     );
   }
@@ -590,6 +710,9 @@ static char *intckCheckObjectSql(
     fflush(stdout);
 #else
     zRet = intckStrdup(p, (const char*)sqlite3_column_text(pStmt, 0));
+    if( pnKeyVal ){
+      *pnKeyVal = sqlite3_column_int(pStmt, 1);
+    }
 #endif
   }
   intckFinalize(p, pStmt);
@@ -599,11 +722,14 @@ static char *intckCheckObjectSql(
 }
 
 static void intckCheckObject(sqlite3_intck *p){
-  char *zSql = intckCheckObjectSql(p, p->zObj, p->zKey);
+  char *zSql = 0;
+  char *zKey = 0;
+  zKey = intckSavedKeyToText(p);
+  zSql = intckCheckObjectSql(p, p->zObj, zKey, &p->nKeyVal);
   p->pCheck = intckPrepare(p, "%s", zSql);
   sqlite3_free(zSql);
-  sqlite3_free(p->zKey);
-  p->zKey = 0;
+  sqlite3_free(zKey);
+  intckSavedKeyClear(p);
 }
 
 int sqlite3_intck_open(
@@ -644,7 +770,7 @@ int sqlite3_intck_close(sqlite3_intck *p){
       );
     }
     sqlite3_free(p->zObj);
-    sqlite3_free(p->zKey);
+    intckSavedKeyClear(p);
     sqlite3_free(p->zTestSql);
     sqlite3_free(p->zErr);
     sqlite3_free(p);
@@ -668,12 +794,13 @@ int sqlite3_intck_step(sqlite3_intck *p){
     if( p->rc==SQLITE_OK ){
       assert( p->pCheck );
       if( sqlite3_step(p->pCheck)==SQLITE_ROW ){
-        /* Fine, whatever... */
+        /* Normal case, do nothing. */
       }else{
         if( sqlite3_finalize(p->pCheck)!=SQLITE_OK ){
           intckSaveErrmsg(p);
         }
         p->pCheck = 0;
+        p->nKeyVal = 0;
       }
     }
   }
@@ -693,10 +820,27 @@ int sqlite3_intck_error(sqlite3_intck *p, const char **pzErr){
   return (p->rc==SQLITE_DONE ? SQLITE_OK : p->rc);
 }
 
+
+static sqlite3_value *intckValueDup(sqlite3_intck *p, sqlite3_value *pIn){
+  sqlite3_value *pRet = 0;
+  if( p->rc==SQLITE_OK ){
+    pRet = sqlite3_value_dup(pIn);
+    if( pRet==0 ){
+      p->rc = SQLITE_NOMEM;
+    }
+  }
+  return pRet;
+}
+
 int sqlite3_intck_suspend(sqlite3_intck *p){
   if( p->pCheck && p->rc==SQLITE_OK ){
-    assert( p->zKey==0 );
-    p->zKey = intckStrdup(p, (const char*)sqlite3_column_text(p->pCheck, 1));
+    const int nByte = sizeof(sqlite3_value*) * p->nKeyVal;
+    int ii;
+    assert( p->apKeyVal==0 && p->nKeyVal>0 );
+    p->apKeyVal = (sqlite3_value**)intckMalloc(p, nByte);
+    for(ii=0; p->rc==SQLITE_OK && ii<p->nKeyVal; ii++){
+      p->apKeyVal[ii] = intckValueDup(p, sqlite3_column_value(p->pCheck, ii+1));
+    }
     intckFinalize(p, p->pCheck);
     p->pCheck = 0;
   }
@@ -706,10 +850,12 @@ int sqlite3_intck_suspend(sqlite3_intck *p){
 const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){
   sqlite3_free(p->zTestSql);
   if( zObj ){
-    p->zTestSql = intckCheckObjectSql(p, zObj, 0);
+    p->zTestSql = intckCheckObjectSql(p, zObj, 0, 0);
   }else{
     if( p->zObj ){
-      p->zTestSql = intckCheckObjectSql(p, p->zObj, p->zKey);
+      char *zKey = intckSavedKeyToText(p);
+      p->zTestSql = intckCheckObjectSql(p, p->zObj, zKey, 0);
+      sqlite3_free(zKey);
     }else{
       sqlite3_free(p->zTestSql);
       p->zTestSql = 0;
index e254a597a21c0389d8e4142cdee640eb9b213cd3..39fc17cfc5aa003f47ff6a8217cc5bdfcc274832 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Consider\susing\s"="\sand\sIS\soperators\swith\seven\slow-quality\sindexes\sin\scases\swhere\sthey\sare\sselected\sexplicitly\susing\san\sINDEXED\sBY\sclause.
-D 2024-02-20T16:04:27.694
+C Use\sfewer\scycles\sto\sgenerate\sthe\s"next\skey"\svalue\sused\sby\ssqlite3_intck_suspend()\sfunction\sin\sthe\sintck\sextension.
+D 2024-02-20T18:17:06.096
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -251,7 +251,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
 F ext/intck/intck1.test 5b3c9800e119b4dd50a381974f34cee6cfd5b7434286fb8da83b7c8ff1d6bb3c
 F ext/intck/intck2.test b65d7f627342f767e1d2c447d25619666cec36f516786dd56568bd741e5d7e67
 F ext/intck/intck_common.tcl 2895854e7aaf5e199a15f6f82538a00999fd8fc55553bc1f04619af7aa86c0d0
-F ext/intck/sqlite3intck.c 5f319b7c72b0c01cfa28bb5fdd19be5781eb11f5a5216af2a0870dc7e001414d
+F ext/intck/sqlite3intck.c 7a795f23424a29f656f3d4c7b83d23484746b57cdc25d3fb98ec805d017fc935
 F ext/intck/sqlite3intck.h d9501ea480b7c41c0555f39f4f1b7c3e8d54fc1ea6d115de5e1211e0bc11d3e7
 F ext/intck/test_intck.c 06206b35f1428961015c060dd35201246c849625cfdff461e0eeaaf76bda545c
 F ext/jni/GNUmakefile 59eb05f2a363bdfac8d15d66bed624bfe1ff289229184f3861b95f98a19cf4b2
@@ -2168,8 +2168,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P c01e008c28895e50b14531b2a1f3f1110aab3b54df41ebdbd416fbac7b1bba94
-R 8db173421c176ccb487011bd9440e123
+P 43cbbea82132db2d0ddb4f34cc2b6910b3a1243ae6d4e837b1b27bfe91b84834
+R 9dcf98c64d0302d62dce4c3c1b529641
 U dan
-Z d4e70ffeb295fbc1a103b9adca306ecc
+Z c2dbf2f2090bfeaba66446a197f3ad62
 # Remove this line to create a well-formed Fossil manifest.
index 4565b3d5286d80f11a50d97aac14626f7fdbb12d..69d33b69383c483000ef956538eef41ee8097758 100644 (file)
@@ -1 +1 @@
-43cbbea82132db2d0ddb4f34cc2b6910b3a1243ae6d4e837b1b27bfe91b84834
\ No newline at end of file
+95f01426f948cf435d0b400dc7ae06fa699eee32cff498fe77e74a1257a4e09b
\ No newline at end of file