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:
parent
626d619218
commit
cfcb3b9208
@ -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;
|
||||
|
12
manifest
12
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.
|
||||
|
@ -1 +1 @@
|
||||
43cbbea82132db2d0ddb4f34cc2b6910b3a1243ae6d4e837b1b27bfe91b84834
|
||||
95f01426f948cf435d0b400dc7ae06fa699eee32cff498fe77e74a1257a4e09b
|
Loading…
Reference in New Issue
Block a user