1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-12-24 14:17:58 +03:00

Merge various enhancements that were being held on a branch until after the

3.46.0 release.  (1) Allow arbitrary expressions in the second argument to
the RAISE() function. (2) Performance optimization to the OP_OpenRead and
OP_OpenWrite bytecode operators. (3) Allow FTS5 tables to be dropped even if
the associated tokenizer is not available.  (4) Performance optimizations in
FTS5.  (5) Generate better bytecode to improve performance of ONEROW scans.

FossilOrigin-Name: 1e5385ffc30743412ade1536583504eec36790cac5355042aa1e12d8d2271d1f
This commit is contained in:
drh
2024-05-23 15:05:57 +00:00
18 changed files with 262 additions and 121 deletions

View File

@@ -142,6 +142,15 @@ struct Fts5Colset {
*/
typedef struct Fts5Config Fts5Config;
typedef struct Fts5TokenizerConfig Fts5TokenizerConfig;
struct Fts5TokenizerConfig {
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
const char **azArg;
int nArg;
int ePattern; /* FTS_PATTERN_XXX constant */
};
/*
** An instance of the following structure encodes all information that can
@@ -184,6 +193,7 @@ typedef struct Fts5Config Fts5Config;
*/
struct Fts5Config {
sqlite3 *db; /* Database handle */
Fts5Global *pGlobal; /* Global fts5 object for handle db */
char *zDb; /* Database holding FTS index (e.g. "main") */
char *zName; /* Name of FTS index */
int nCol; /* Number of columns */
@@ -199,10 +209,8 @@ struct Fts5Config {
int bTokendata; /* "tokendata=" option value (dflt==0) */
int eDetail; /* FTS5_DETAIL_XXX value */
char *zContentExprlist;
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
Fts5TokenizerConfig t;
int bLock; /* True when table is preparing statement */
int ePattern; /* FTS_PATTERN_XXX constant */
/* Values loaded from the %_config table */
int iVersion; /* fts5 file format 'version' */
@@ -597,13 +605,7 @@ struct Fts5Table {
Fts5Index *pIndex; /* Full-text index */
};
int sqlite3Fts5GetTokenizer(
Fts5Global*,
const char **azArg,
int nArg,
Fts5Config*,
char **pzErr
);
int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig);
Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64);
@@ -866,6 +868,7 @@ int sqlite3Fts5TokenizerPattern(
int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
Fts5Tokenizer *pTok
);
int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig*);
/*
** End of interface to code in fts5_tokenizer.c.
**************************************************************************/

View File

@@ -298,12 +298,11 @@ static int fts5ConfigParseSpecial(
if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
const char *p = (const char*)zArg;
sqlite3_int64 nArg = strlen(zArg) + 1;
char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
char *pSpace = pDel;
char **azArg = sqlite3Fts5MallocZero(&rc, (sizeof(char*) + 2) * nArg);
if( azArg && pSpace ){
if( pConfig->pTok ){
if( azArg ){
char *pSpace = (char*)&azArg[nArg];
if( pConfig->t.azArg ){
*pzErr = sqlite3_mprintf("multiple tokenize=... directives");
rc = SQLITE_ERROR;
}else{
@@ -326,16 +325,14 @@ static int fts5ConfigParseSpecial(
*pzErr = sqlite3_mprintf("parse error in tokenize directive");
rc = SQLITE_ERROR;
}else{
rc = sqlite3Fts5GetTokenizer(pGlobal,
(const char**)azArg, (int)nArg, pConfig,
pzErr
);
pConfig->t.azArg = (const char**)azArg;
pConfig->t.nArg = nArg;
azArg = 0;
}
}
}
sqlite3_free(azArg);
sqlite3_free(pDel);
return rc;
}
@@ -412,16 +409,6 @@ static int fts5ConfigParseSpecial(
return SQLITE_ERROR;
}
/*
** Allocate an instance of the default tokenizer ("simple") at
** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
** code if an error occurs.
*/
static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
return sqlite3Fts5GetTokenizer(pGlobal, 0, 0, pConfig, 0);
}
/*
** Gobble up the first bareword or quoted word from the input buffer zIn.
** Return a pointer to the character immediately following the last in
@@ -554,6 +541,7 @@ int sqlite3Fts5ConfigParse(
*ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
if( pRet==0 ) return SQLITE_NOMEM;
memset(pRet, 0, sizeof(Fts5Config));
pRet->pGlobal = pGlobal;
pRet->db = db;
pRet->iCookie = -1;
@@ -640,13 +628,6 @@ int sqlite3Fts5ConfigParse(
rc = SQLITE_ERROR;
}
/* If a tokenizer= option was successfully parsed, the tokenizer has
** already been allocated. Otherwise, allocate an instance of the default
** tokenizer (unicode61) now. */
if( rc==SQLITE_OK && pRet->pTok==0 ){
rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
}
/* If no zContent option was specified, fill in the default values. */
if( rc==SQLITE_OK && pRet->zContent==0 ){
const char *zTail = 0;
@@ -688,9 +669,10 @@ int sqlite3Fts5ConfigParse(
void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
if( pConfig ){
int i;
if( pConfig->pTok ){
pConfig->pTokApi->xDelete(pConfig->pTok);
if( pConfig->t.pTok ){
pConfig->t.pTokApi->xDelete(pConfig->t.pTok);
}
sqlite3_free(pConfig->t.azArg);
sqlite3_free(pConfig->zDb);
sqlite3_free(pConfig->zName);
for(i=0; i<pConfig->nCol; i++){
@@ -765,10 +747,18 @@ int sqlite3Fts5Tokenize(
void *pCtx, /* Context passed to xToken() */
int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
){
if( pText==0 ) return SQLITE_OK;
return pConfig->pTokApi->xTokenize(
pConfig->pTok, pCtx, flags, pText, nText, xToken
);
int rc = SQLITE_OK;
if( pText ){
if( pConfig->t.pTok==0 ){
rc = sqlite3Fts5LoadTokenizer(pConfig);
}
if( rc==SQLITE_OK ){
rc = pConfig->t.pTokApi->xTokenize(
pConfig->t.pTok, pCtx, flags, pText, nText, xToken
);
}
}
return rc;
}
/*

View File

@@ -324,7 +324,7 @@ int sqlite3Fts5ExprNew(
}
sqlite3_free(sParse.apPhrase);
*pzErr = sParse.zErr;
if( 0==*pzErr ) *pzErr = sParse.zErr;
return sParse.rc;
}

View File

@@ -377,8 +377,12 @@ static int fts5InitVtab(
assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
}
if( rc==SQLITE_OK ){
pConfig->pzErrmsg = pzErr;
pTab->p.pConfig = pConfig;
pTab->pGlobal = pGlobal;
if( bCreate || sqlite3Fts5TokenizerPreload(&pConfig->t) ){
rc = sqlite3Fts5LoadTokenizer(pConfig);
}
}
/* Open the index sub-system */
@@ -400,11 +404,8 @@ static int fts5InitVtab(
/* Load the initial configuration */
if( rc==SQLITE_OK ){
assert( pConfig->pzErrmsg==0 );
pConfig->pzErrmsg = pzErr;
rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
sqlite3Fts5IndexRollback(pTab->p.pIndex);
pConfig->pzErrmsg = 0;
}
if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -414,6 +415,7 @@ static int fts5InitVtab(
rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
if( pConfig ) pConfig->pzErrmsg = 0;
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -481,10 +483,10 @@ static int fts5UsePatternMatch(
){
assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
if( pConfig->ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
if( pConfig->t.ePattern==FTS5_PATTERN_GLOB && p->op==FTS5_PATTERN_GLOB ){
return 1;
}
if( pConfig->ePattern==FTS5_PATTERN_LIKE
if( pConfig->t.ePattern==FTS5_PATTERN_LIKE
&& (p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB)
){
return 1;
@@ -626,6 +628,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
idxStr += strlen(&idxStr[iIdxStr]);
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
assert( idxStr[iIdxStr]=='\0' );
bSeenMatch = 1;
}else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
idxStr[iIdxStr++] = '=';
bSeenEq = 1;
@@ -2860,7 +2863,7 @@ static int fts5FindTokenizer(
return rc;
}
int sqlite3Fts5GetTokenizer(
int fts5GetTokenizer(
Fts5Global *pGlobal,
const char **azArg,
int nArg,
@@ -2877,26 +2880,37 @@ int sqlite3Fts5GetTokenizer(
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
}else{
rc = pMod->x.xCreate(
pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->t.pTok
);
pConfig->pTokApi = &pMod->x;
pConfig->t.pTokApi = &pMod->x;
if( rc!=SQLITE_OK ){
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
}else{
pConfig->ePattern = sqlite3Fts5TokenizerPattern(
pMod->x.xCreate, pConfig->pTok
pConfig->t.ePattern = sqlite3Fts5TokenizerPattern(
pMod->x.xCreate, pConfig->t.pTok
);
}
}
if( rc!=SQLITE_OK ){
pConfig->pTokApi = 0;
pConfig->pTok = 0;
pConfig->t.pTokApi = 0;
pConfig->t.pTok = 0;
}
return rc;
}
/*
** Attempt to instantiate the tokenizer.
*/
int sqlite3Fts5LoadTokenizer(Fts5Config *pConfig){
return fts5GetTokenizer(
pConfig->pGlobal, pConfig->t.azArg, pConfig->t.nArg,
pConfig, pConfig->pzErrmsg
);
}
static void fts5ModuleDestroy(void *pCtx){
Fts5TokenizerModule *pTok, *pNextTok;
Fts5Auxiliary *pAux, *pNextAux;

View File

@@ -1428,6 +1428,16 @@ int sqlite3Fts5TokenizerPattern(
return FTS5_PATTERN_NONE;
}
/*
** Return true if the tokenizer described by p->azArg[] is the trigram
** tokenizer. This tokenizer needs to be loaded before xBestIndex is
** called for the first time in order to correctly handle LIKE/GLOB.
*/
int sqlite3Fts5TokenizerPreload(Fts5TokenizerConfig *p){
return (p->nArg>=1 && 0==sqlite3_stricmp(p->azArg[0], "trigram"));
}
/*
** Register all built-in tokenizers with FTS5.
*/

View File

@@ -64,6 +64,7 @@ struct Fts5VocabCursor {
int nLeTerm; /* Size of zLeTerm in bytes */
char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */
int colUsed; /* Copy of sqlite3_index_info.colUsed */
/* These are used by 'col' tables only */
int iCol;
@@ -90,9 +91,11 @@ struct Fts5VocabCursor {
/*
** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
*/
#define FTS5_VOCAB_TERM_EQ 0x01
#define FTS5_VOCAB_TERM_GE 0x02
#define FTS5_VOCAB_TERM_LE 0x04
#define FTS5_VOCAB_TERM_EQ 0x0100
#define FTS5_VOCAB_TERM_GE 0x0200
#define FTS5_VOCAB_TERM_LE 0x0400
#define FTS5_VOCAB_COLUSED_MASK 0xFF
/*
@@ -269,11 +272,13 @@ static int fts5VocabBestIndexMethod(
int iTermEq = -1;
int iTermGe = -1;
int iTermLe = -1;
int idxNum = 0;
int idxNum = (int)pInfo->colUsed;
int nArg = 0;
UNUSED_PARAM(pUnused);
assert( (pInfo->colUsed & FTS5_VOCAB_COLUSED_MASK)==pInfo->colUsed );
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
if( p->usable==0 ) continue;
@@ -365,7 +370,7 @@ static int fts5VocabOpenMethod(
if( rc==SQLITE_OK ){
pVTab->zErrMsg = sqlite3_mprintf(
"no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
);
);
rc = SQLITE_ERROR;
}
}else{
@@ -525,9 +530,19 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
switch( pTab->eType ){
case FTS5_VOCAB_ROW:
if( eDetail==FTS5_DETAIL_FULL ){
while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
pCsr->aCnt[0]++;
/* Do not bother counting the number of instances if the "cnt"
** column is not being read (according to colUsed). */
if( eDetail==FTS5_DETAIL_FULL && (pCsr->colUsed & 0x04) ){
while( iPos<nPos ){
u32 ii;
fts5FastGetVarint32(pPos, iPos, ii);
if( ii==1 ){
/* New column in the position list */
fts5FastGetVarint32(pPos, iPos, ii);
}else{
/* An instance - increment pCsr->aCnt[] */
pCsr->aCnt[0]++;
}
}
}
pCsr->aDoc[0]++;
@@ -625,6 +640,7 @@ static int fts5VocabFilterMethod(
if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
pCsr->colUsed = (idxNum & FTS5_VOCAB_COLUSED_MASK);
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);

View File

@@ -300,5 +300,75 @@ set ::flags [list]
do_execsql_test 9.5.1 { SELECT * FROM t1('"abc xyz*"'); } {}
do_test 9.5.2 { set ::flags } {query}
#-------------------------------------------------------------------------
#
reset_db
do_execsql_test 10.1 {
CREATE VIRTUAL TABLE x1 USING fts5(x, tokenize=unicode61);
PRAGMA writable_schema = 1;
UPDATE sqlite_schema
SET sql = 'CREATE VIRTUAL TABLE x1 USING fts5(x, tokenize="unicode61 error");'
WHERE name = 'x1';
}
db close
sqlite3 db test.db
do_catchsql_test 10.2 {
SELECT * FROM x1('abc');
} {1 {error in tokenizer constructor}}
do_catchsql_test 10.3 {
INSERT INTO x1 VALUES('abc');
} {1 {error in tokenizer constructor}}
do_execsql_test 10.4 {
PRAGMA writable_schema = 1;
UPDATE sqlite_schema
SET sql = 'CREATE VIRTUAL TABLE x1 USING fts5(x, tokenize="nosuch error");'
WHERE name = 'x1';
}
db close
sqlite3 db test.db
do_catchsql_test 10.5 {
SELECT * FROM x1('abc');
} {1 {no such tokenizer: nosuch}}
do_catchsql_test 10.6 {
INSERT INTO x1 VALUES('abc');
} {1 {no such tokenizer: nosuch}}
do_execsql_test 10.7 {
DROP TABLE x1;
SELECT * FROM sqlite_schema;
}
reset_db
do_execsql_test 10.8 {
CREATE VIRTUAL TABLE x1 USING fts5(x, tokenize=unicode61);
INSERT INTO x1 VALUES('a b c'), ('d e f'), ('a b c');
CREATE VIRTUAL TABLE x1v USING fts5vocab(x1, row);
PRAGMA writable_schema = 1;
UPDATE sqlite_schema
SET sql = 'CREATE VIRTUAL TABLE x1 USING fts5(x, tokenize=simplify);'
WHERE name = 'x1';
}
do_execsql_test 10.9 {
SELECT * FROM x1v
} {
a 2 2 b 2 2 c 2 2 d 1 1 e 1 1 f 1 1
}
db close
sqlite3 db test.db
do_execsql_test 10.10 {
SELECT * FROM x1v
} {
a 2 2 b 2 2 c 2 2 d 1 1 e 1 1 f 1 1
}
finish_test

View File

@@ -101,9 +101,20 @@ do_execsql_test 4.0 {
CREATE VIRTUAL TABLE t4 USING fts5(z, tokenize=trigram);
} {}
breakpoint
do_execsql_test 4.1 {
INSERT INTO t4 VALUES('ABCD');
INSERT INTO t4 VALUES('DEFG');
} {}
db close
sqlite3 db test.db
do_eqp_test 4.1 {
SELECT rowid FROM t4 WHERE z LIKE '%abc%'
} {VIRTUAL TABLE INDEX 0:L0}
do_execsql_test 4.2 {
SELECT rowid FROM t4 WHERE z LIKE '%abc%'
} {1}
finish_test

View File

@@ -1,5 +1,5 @@
C Increase\sthe\sversion\snumber\sto\s3.47.0\sto\sbegin\sthe\snext\sdevelopment\scycle.
D 2024-05-23T14:58:31.543
C Merge\svarious\senhancements\sthat\swere\sbeing\sheld\son\sa\sbranch\suntil\safter\sthe\n3.46.0\srelease.\s\s(1)\sAllow\sarbitrary\sexpressions\sin\sthe\ssecond\sargument\sto\nthe\sRAISE()\sfunction.\s(2)\sPerformance\soptimization\sto\sthe\sOP_OpenRead\sand\nOP_OpenWrite\sbytecode\soperators.\s(3)\sAllow\sFTS5\stables\sto\sbe\sdropped\seven\sif\nthe\sassociated\stokenizer\sis\snot\savailable.\s\s(4)\sPerformance\soptimizations\sin\nFTS5.\s\s(5)\sGenerate\sbetter\sbytecode\sto\simprove\sperformance\sof\sONEROW\sscans.
D 2024-05-23T15:05:57.348
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -93,22 +93,22 @@ F ext/fts3/unicode/mkunicode.tcl d5aebf022fa4577ee8cdf27468f0d847879993959101f6d
F ext/fts3/unicode/parseunicode.tcl a981bd6466d12dd17967515801c3ff23f74a281be1a03cf1e6f52a6959fc77eb
F ext/fts5/extract_api_docs.tcl bc3a0ca78be7d3df08e7602c00ca48021ebae40682d75eb001bfdf6e54ffb44e
F ext/fts5/fts5.h 8856e11a5f0269cd346754cea0765efe8089635b80cad3222e8bfdb08cd5348a
F ext/fts5/fts5Int.h defa43c0932265138ee910ca416e6baccf8b774e0f3d610e74be1ab2880e9834
F ext/fts5/fts5Int.h 407ee36addad0ae6df5d37a811f0bd509ab6708b29640884ed5c7509e5f75143
F ext/fts5/fts5_aux.c 4584e88878e54828bf7d4d0d83deedd232ec60628b7731be02bad6adb62304b1
F ext/fts5/fts5_buffer.c 0eec58bff585f1a44ea9147eae5da2447292080ea435957f7488c70673cb6f09
F ext/fts5/fts5_config.c 8072a207034b51ae9b7694121d1b5715c794e94b275e088f70ae532378ca5cdf
F ext/fts5/fts5_expr.c e91156ebdcc08d837f4f324168f69f3c0d7fdef0e521fd561efb48ef3297b696
F ext/fts5/fts5_config.c 74093394dc26750becc302922e5fe308a8597a70e6e83e66c892242952468163
F ext/fts5/fts5_expr.c a84df51a1d178fe38a89c0feb9ee6da3e5b98d204dde4d241fc8115464987d7a
F ext/fts5/fts5_hash.c adda4272be401566a6e0ba1acbe70ee5cb97fce944bc2e04dc707152a0ec91b1
F ext/fts5/fts5_index.c ee0f4d50bc0c58a7c5ef7d645e7e38e1e59315b8ea9d722ae00c5f949ee65379
F ext/fts5/fts5_main.c d68bd9533d5a638b7f6fae61c3cb0a15257dcdcccedaf3d0b3c9f55940c85048
F ext/fts5/fts5_main.c dc6938b7233e9d88b3612fe2e88ce0a62130c43083be59beab8e6f01aa496689
F ext/fts5/fts5_storage.c f9e31b0d155e9b2c92d5d3a09ad7a56b937fbf1c7f962e10f4ca6281349f3934
F ext/fts5/fts5_tcl.c fdf7e2bb9a9186cfcaf2d2ce11d338309342b7a7593c2812bc54455db53da5d2
F ext/fts5/fts5_test_mi.c 08c11ec968148d4cb4119d96d819f8c1f329812c568bac3684f5464be177d3ee
F ext/fts5/fts5_test_tok.c 3cb0a9b508b30d17ef025ccddd26ae3dc8ddffbe76c057616e59a9aa85d36f3b
F ext/fts5/fts5_tokenize.c 83cfcede3898001cab84432a36ce1503e3080cf9b1c682b022ec82e267ea4c13
F ext/fts5/fts5_tokenize.c fa5493075101540270f572038fc1723d44fcc97bfbf237c8530013b8a27860be
F ext/fts5/fts5_unicode2.c eca63dbc797f8ff0572e97caf4631389c0ab900d6364861b915bdd4735973f00
F ext/fts5/fts5_varint.c e64d2113f6e1bfee0032972cffc1207b77af63319746951bf1d09885d1dadf80
F ext/fts5/fts5_vocab.c 209e0c151e108d5f3621fa24b91e9b02f3750ee6c3f9ccec312df39481b68a09
F ext/fts5/fts5_vocab.c e4830b00809e5da53bc10f93adc59e321407b0f801c7f4167c0e47f5552267e0
F ext/fts5/fts5parse.y eb526940f892ade5693f22ffd6c4f2702543a9059942772526eac1fde256bb05
F ext/fts5/mkportersteps.tcl 5acf962d2e0074f701620bb5308155fa1e4a63ba
F ext/fts5/test/fts5_common.tcl 3378732aae2a7d9a4b9b5c40bde678d4259ca16bd490883325aecc4747bcb384
@@ -227,10 +227,10 @@ F ext/fts5/test/fts5synonym.test 1651815b8008de170e8e600dcacc17521d765482ea8f074
F ext/fts5/test/fts5synonym2.test e2f6ff68c4fbe12a866a3a87510f553d9dac99bcb74c10b56487c4c0a562fcf5
F ext/fts5/test/fts5tok1.test 1f7817499f5971450d8c4a652114b3d833393c8134e32422d0af27884ffe9cef
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
F ext/fts5/test/fts5tokenizer.test ac3c9112b263a639fb0508ae73a3ee886bf4866d2153771a8e8a20c721305a43
F ext/fts5/test/fts5tokenizer.test abeb8b2d00415e5c8eae48d2dcb91357b7f7e1fddbb2b650f00b589a7bea9659
F ext/fts5/test/fts5tokenizer2.test cb5428c7cfb3b6a74b7adfcde65506e329112003e8dffa7501d01c2d18d02569
F ext/fts5/test/fts5trigram.test 6c4e37864f3e7d90673db5563d9736d7e40080ab94d10ebdffa94c1b77941da0
F ext/fts5/test/fts5trigram2.test 9fe4207f8a4241747aff1005258b564958588d21bfd240d6cd4c2e955d31c156
F ext/fts5/test/fts5trigram2.test c91f0a94f7e1ff859682228646abeab4c0eba2effc46af2cbc8f0f48b05a0566
F ext/fts5/test/fts5ubsan.test 783d5a8d13ebfa169e634940228db54540780e3ba7a87ad1e4510e61440bf64b
F ext/fts5/test/fts5umlaut.test a42fe2fe6387c40c49ab27ccbd070e1ae38e07f38d05926482cc0bccac9ad602
F ext/fts5/test/fts5unicode.test 17056f4efe6b0a5d4f41fdf7a7dc9af2873004562eaa899d40633b93dc95f5a9
@@ -704,9 +704,9 @@ F src/date.c 126ba2ab10aeb2e7ba6e089b5f07b747c0625b8287f78b60da346eda8d23c875
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
F src/expr.c f7bad20d2f74005f1f876e7fbb627222ea28250e44b296b047403720c5c21818
F src/expr.c 50e71ed518f1b1ba8401006922a309e200d205b2ca5b93d6fd8a006a336dbf67
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00
F src/fkey.c 852f93c0ef995e0c2b8983059a2b97151c194cc8259e21f5bc2b7ac508348c2a
F src/func.c 283d4f3b2751a1d9339fd93a8a013d1948fd5f4474a3cab0955eb4fafd445d0f
F src/global.c 61a419dd9e993b9be0f91de4c4ccf322b053eb829868e089f0321dd669be3b90
F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
@@ -743,7 +743,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 9beb80f6e330dd63c5d8ba0f7a7f3a55fff22067a68d424949c389bfc6fa0c56
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y 50516253433303673ff6b009983bb246d1527415e5a9af22acc51b0eedb9a10d
F src/parse.y de2da0ffd33f6532c1640e7c6a6c6d8ac5ae85b0dbb0b1d7c3194d243a4c1e6a
F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
@@ -817,14 +817,14 @@ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c 3f703cacdab728d7741e5a6ac242006d74fe1c2754d4f03ed889d7253259bd68
F src/treeview.c 38eefdc85d2793c4059ae651a611b30eb034389fb428f69e572bbea565da6c78
F src/treeview.c 4b0e9131c33a08905bb56a424cd07d6146497f3277301962b4d3d997c2fd7d5f
F src/trigger.c 0858f75818ed1580332db274f1032bcc5effe567cb132df5c5be8b1d800ca97f
F src/update.c 732404a04d1737ef14bb6ec6b84f74edf28b3c102a92ae46b4855438a710efe7
F src/upsert.c 2e60567a0e9e8520c18671b30712a88dc73534474304af94f32bb5f3ef65ac65
F src/utf.c f23165685a67b4caf8ec08fb274cb3f319103decfb2a980b7cfd55d18dfa855e
F src/util.c 4d6d7ebfe6772a1b950c97bbb1d1a72ad4874617ec498ab8aa73b7f5a43e44bb
F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
F src/vdbe.c 3b1793c5d2235ae89b01ef051a33d7d2ad3704c71799653b112686735ad401ff
F src/vdbe.c 2448b34ff095efa5a8d9dd525c4c0842ef7226e44b68fe8df1053ee9894aa5fd
F src/vdbe.h c2d78d15112c3fc5ab87f5e8e0b75d2db1c624409de2e858c3d1aafb1650bb4f
F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
F src/vdbeapi.c 80235ac380e9467fec1cb0883354d841f2a771976e766995f7e0c77f845406df
@@ -839,7 +839,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
F src/where.c 6f02c3936d1f9a637d8d7b5ad7362371af3e4434b0ec1eb950189a83de560d59
F src/where.c 9b2ab23fcc2198ebcee1e6e840a9ff22350668bd3a99195a5dc7299d5908370a
F src/whereInt.h 82a13766f13d1a53b05387c2e60726289ef26404bc7b9b1f7770204d97357fb8
F src/wherecode.c f5255f49d1f42b6e7e6b0362ff3522fa88cbcaa7213e52f9374744027ecdebca
F src/whereexpr.c 67d15caf88a1a9528283d68ff578e024cf9fe810b517bb0343e5aaf695ad97dd
@@ -998,7 +998,7 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e
F test/colname.test 387e880eeac0889900f7b3e9703c375515f5d323f71fd4f2bb5428a4ac8e2023
F test/columncount.test 6fe99c2f35738b0129357a1cf3fa483f76140f4cd8a89014c88c33c876d2638f
F test/conflict.test b705cddf025a675d3c13d62fa78ab1e2696fb8e07a3d7cccce1596ff8b301492
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
@@ -1868,7 +1868,7 @@ F test/trans.test 45f6f9ab6f66a7b5744f1caac06b558f95da62501916906cf55586a896f9f4
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
F test/transitive1.test f8ee983600b33d167da1885657f064aec404e1c0d0bc8765fdf163f4c749237a
F test/trigger1.test 02cc64dc98278816c1c1ed8e472e18db8edbad88f37018bf46223e9614831963
F test/trigger1.test 2834f8830a1ae338d95c2e3ea0c2a7bc4cda126cdeb715004cf0fd071892e44f
F test/trigger2.test 30fcb3a6aa6782020d47968735ee6086ed795f73a7affa9406c8d5a36e7b5265
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
@@ -2191,8 +2191,9 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e
R c841fa8f021d8f0e7db7ccbf58585698
P 20e228a22e4c5a8d62fae003ac176de62126d8e121871c5ca14a5480c4d4b6a2 6dc6472175bccbed15ebf6811c209d1a0b5fad60158fb32040210f2cdae916a6
R 8bddc71c9aad2b206ce1df483c24253e
T +closed 6dc6472175bccbed15ebf6811c209d1a0b5fad60158fb32040210f2cdae916a6
U drh
Z 19bbf589edce5b1123a18efb65256692
Z bb39af0ce41ef3b8a3492c4ff2e0362a
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
20e228a22e4c5a8d62fae003ac176de62126d8e121871c5ca14a5480c4d4b6a2
1e5385ffc30743412ade1536583504eec36790cac5355042aa1e12d8d2271d1f

View File

@@ -5306,15 +5306,14 @@ expr_code_doover:
}
assert( !ExprHasProperty(pExpr, EP_IntValue) );
if( pExpr->affExpr==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, OE_Ignore);
VdbeCoverage(v);
}else{
sqlite3HaltConstraint(pParse,
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
sqlite3VdbeAddOp3(v, OP_Halt,
pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
pExpr->affExpr, pExpr->u.zToken, 0, 0);
pExpr->affExpr, r1);
}
break;
}
#endif

View File

@@ -1328,7 +1328,8 @@ static Trigger *fkActionTrigger(
SrcList *pSrc;
Expr *pRaise;
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
pRaise = sqlite3Expr(db, TK_STRING, "FOREIGN KEY constraint failed"),
pRaise = sqlite3PExpr(pParse, TK_RAISE, pRaise, 0);
if( pRaise ){
pRaise->affExpr = OE_Abort;
}

View File

@@ -1658,8 +1658,8 @@ expr(A) ::= RAISE LP IGNORE RP. {
A->affExpr = OE_Ignore;
}
}
expr(A) ::= RAISE LP raisetype(T) COMMA nm(Z) RP. {
A = sqlite3ExprAlloc(pParse->db, TK_RAISE, &Z, 1);
expr(A) ::= RAISE LP raisetype(T) COMMA expr(Z) RP. {
A = sqlite3PExpr(pParse, TK_RAISE, Z, 0);
if( A ) {
A->affExpr = (char)T;
}

View File

@@ -820,7 +820,8 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
case OE_Ignore: zType = "ignore"; break;
}
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
sqlite3TreeViewLine(pView, "RAISE %s", zType);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
#endif

View File

@@ -1214,7 +1214,7 @@ case OP_HaltIfNull: { /* in3 */
/* no break */ deliberate_fall_through
}
/* Opcode: Halt P1 P2 * P4 P5
/* Opcode: Halt P1 P2 P3 P4 P5
**
** Exit immediately. All open cursors, etc are closed
** automatically.
@@ -1227,18 +1227,22 @@ case OP_HaltIfNull: { /* in3 */
** then back out all changes that have occurred during this execution of the
** VDBE, but do not rollback the transaction.
**
** If P4 is not null then it is an error message string.
** If P3 is not zero and P4 is NULL, then P3 is a register that holds the
** text of an error message.
**
** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
** If P3 is zero and P4 is not null then the error message string is held
** in P4.
**
** P5 is a value between 1 and 4, inclusive, then the P4 error message
** string is modified as follows:
**
** 0: (no change)
** 1: NOT NULL constraint failed: P4
** 2: UNIQUE constraint failed: P4
** 3: CHECK constraint failed: P4
** 4: FOREIGN KEY constraint failed: P4
**
** If P5 is not zero and P4 is NULL, then everything after the ":" is
** omitted.
** If P3 is zero and P5 is not zero and P4 is NULL, then everything after
** the ":" is omitted.
**
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
** every program. So a jump past the last instruction of the program
@@ -1251,6 +1255,9 @@ case OP_Halt: {
#ifdef SQLITE_DEBUG
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
#endif
assert( pOp->p4type==P4_NOTUSED
|| pOp->p4type==P4_STATIC
|| pOp->p4type==P4_DYNAMIC );
/* A deliberately coded "OP_Halt SQLITE_INTERNAL * * * *" opcode indicates
** something is wrong with the code generator. Raise an assertion in order
@@ -1281,7 +1288,12 @@ case OP_Halt: {
p->errorAction = (u8)pOp->p2;
assert( pOp->p5<=4 );
if( p->rc ){
if( pOp->p5 ){
if( pOp->p3>0 && pOp->p4type==P4_NOTUSED ){
const char *zErr;
assert( pOp->p3<=(p->nMem + 1 - p->nCursor) );
zErr = sqlite3ValueText(&aMem[pOp->p3], SQLITE_UTF8);
sqlite3VdbeError(p, "%s", zErr);
}else if( pOp->p5 ){
static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
"FOREIGN KEY" };
testcase( pOp->p5==1 );
@@ -4324,23 +4336,23 @@ case OP_OpenWrite:
if( pDb->pSchema->file_format < p->minWriteFileFormat ){
p->minWriteFileFormat = pDb->pSchema->file_format;
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
assert( p2<=(u32)(p->nMem+1 - p->nCursor) );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
p2 = (int)pIn2->u.i;
/* The p2 value always comes from a prior OP_CreateBtree opcode and
** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure, the prepared statement would have halted
** before reaching this instruction. */
assert( p2>=2 );
}
}else{
wrFlag = 0;
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
assert( p2<=(u32)(p->nMem+1 - p->nCursor) );
assert( pOp->opcode==OP_OpenWrite );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
p2 = (int)pIn2->u.i;
/* The p2 value always comes from a prior OP_CreateBtree opcode and
** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure, the prepared statement would have halted
** before reaching this instruction. */
assert( p2>=2 );
assert( (pOp->p5 & OPFLAG_P2ISREG)==0 );
}
if( pOp->p4type==P4_KEYINFO ){
pKeyInfo = pOp->p4.pKeyInfo;

View File

@@ -3170,7 +3170,7 @@ static int whereLoopAddBtreeIndex(
|| (iCol>=0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
){
if( iCol==XN_ROWID || pProbe->uniqNotNull
|| (pProbe->nKeyCol==1 && pProbe->onError && eOp==WO_EQ)
|| (pProbe->nKeyCol==1 && pProbe->onError && (eOp & WO_EQ))
){
pNew->wsFlags |= WHERE_ONEROW;
}else{

View File

@@ -424,7 +424,7 @@ do_catchsql_test colname-9.400 {
#
do_catchsql_test colname-9.410 {
CREATE TABLE t5 AS SELECT RAISE(abort,a);
} {1 {RAISE() may only be used within a trigger-program}}
} {1 {no such column: a}}
# Make sure the quotation marks get removed from the column names
# when constructing a new table from an aggregate SELECT.

View File

@@ -838,4 +838,17 @@ do_catchsql_test trigger1-23.1 {
END;
} {1 {near "#1": syntax error}}
# 2024-05-08 Allow arbitrary expressions as the 2nd argument to RAISE().
#
do_catchsql_test trigger1-24.1 {
CREATE TRIGGER r1 AFTER INSERT ON t1 BEGIN
SELECT raise(abort,format('attempt to insert %d where is not a power of 2',new.a))
WHERE (new.a & (new.a-1))!=0;
END;
INSERT INTO t1 VALUES(0),(1),(2),(4),(8),(65536);
} {0 {}}
do_catchsql_test trigger1-24.2 {
INSERT INTO t1 VALUES(9876);
} {1 {attempt to insert 9876 where is not a power of 2}}
finish_test