Use fewer cycles to generate the "next key" value used by sqlite3_intck_suspend() function in the intck extension.

FossilOrigin-Name: 95f01426f948cf435d0b400dc7ae06fa699eee32cff498fe77e74a1257a4e09b
This commit is contained in:
dan 2024-02-20 18:17:06 +00:00
parent 626d619218
commit cfcb3b9208
3 changed files with 190 additions and 44 deletions

View File

@ -16,12 +16,25 @@
#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,20 +95,119 @@ static void intckFinalize(sqlite3_intck *p, sqlite3_stmt *pStmt){
}
}
static char *intckStrdup(sqlite3_intck *p, const char *zIn){
char *zOut = 0;
/*
** 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 ){
int nIn = strlen(zIn);
zOut = sqlite3_malloc(nIn+1);
if( zOut==0 ){
pRet = sqlite3_malloc64(nByte);
if( pRet==0 ){
p->rc = SQLITE_NOMEM;
}else{
memcpy(zOut, zIn, nIn+1);
}
}
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 ){
if( zRet==0 ){
p->rc = SQLITE_NOMEM;
}
}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;
}
}
/*
** 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){
sqlite3_stmt *pStmt = 0;
char *zPrev = p->zObj;
@ -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;

View File

@ -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.

View File

@ -1 +1 @@
43cbbea82132db2d0ddb4f34cc2b6910b3a1243ae6d4e837b1b27bfe91b84834
95f01426f948cf435d0b400dc7ae06fa699eee32cff498fe77e74a1257a4e09b