Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f4a72788a4 | |||
| 60fe2352f5 | |||
| 329cb9eb88 | |||
| 239c84fc62 | |||
| c90fa01dbf | |||
| 0336140cd9 | |||
| dcc29e0d1d | |||
| a7da40f377 | |||
| 678f3b33cc | |||
| 16953469a6 | |||
| 30cdb992dd | |||
| a55a839ab3 | |||
| 6f6e60ddb1 | |||
| 39065807dd | |||
| fadc0e34be | |||
| 6859d3245a | |||
| 7b7a9cf263 | |||
| c7e93f58d5 | |||
| c54246ffdf | |||
| c2a878ed0a | |||
| c16592cd1b | |||
| 6a5a13df97 | |||
| 4b0229ae1f | |||
| 6e6d9833cc | |||
| 6b37b762ed | |||
| 13162ef975 | |||
| 33941691a5 | |||
| f2b32e8915 | |||
| 351ae772a3 | |||
| 863fd4905c | |||
| 329eaaf0b3 | |||
| 8794c68a35 | |||
| e8f1490f55 | |||
| 8b7e93adbc | |||
| 23ed8109b6 | |||
| 40803caa1d | |||
| b9edded0fd | |||
| 513c9a1ff3 | |||
| 8737d46ea1 | |||
| 82ab4f6b1a | |||
| d510197276 | |||
| e1c9a4ebf9 | |||
| 8729642112 | |||
| 5f323787bd | |||
| 8873aea390 | |||
| 29f6a365cc | |||
| 70bd2124ed | |||
| 7dec804d42 | |||
| e1db9343d5 | |||
| 552562c48f | |||
| 381bdacc8f | |||
| 8ab79d6135 | |||
| 02c4aa39e7 | |||
| 8c333cfc0d | |||
| 17b74817c0 | |||
| 7937f63220 | |||
| c5eb176a09 | |||
| cd39cda00c | |||
| 78197e0f8b | |||
| 2aa41c82da | |||
| 47600083f1 | |||
| cb83dc9e95 | |||
| e0d2096afa | |||
| 709dd13927 | |||
| c9be863098 | |||
| d33d3a2ba7 | |||
| 9e673ace5b | |||
| 7baf3d411b | |||
| 9407b6ef29 | |||
| 18e5607211 | |||
| 662fe79647 | |||
| 28828c550f | |||
| 1832f2921d | |||
| a8a64a078f | |||
| 0166df0bda | |||
| cf4108bbc6 | |||
| 658f0a3679 | |||
| 343256b9b0 | |||
| 16d861ffc2 | |||
| ba71a8a01b | |||
| 8f4e2e25c4 | |||
| d086aa0a48 | |||
| dac9a5f7df | |||
| b835247954 | |||
| 02d6f9b295 | |||
| 2c99b84c95 | |||
| f54a80fe5c | |||
| 55d4c84b61 | |||
| 16cd8b96d3 | |||
| df1b52e727 | |||
| e5ceaac446 | |||
| 8ac090f51d | |||
| 2053f313bf |
@@ -4828,22 +4828,24 @@ static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
|
||||
#endif
|
||||
|
||||
assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
|
||||
if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
|
||||
/* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
|
||||
** taking this lock also prevents any checkpoints from occurring.
|
||||
** todo: really, it's not clear why this might occur, as
|
||||
** wal_autocheckpoint ought to be turned off. */
|
||||
if( pRbu && (
|
||||
pRbu->eStage==RBU_STAGE_OAL
|
||||
|| pRbu->eStage==RBU_STAGE_MOVE
|
||||
|| pRbu->eStage==RBU_STAGE_DONE
|
||||
)){
|
||||
/* Prevent SQLite from taking a shm-lock on the target file when it
|
||||
** is supplying heap memory to the upper layer in place of *-shm
|
||||
** segments. */
|
||||
if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
|
||||
}else{
|
||||
int bCapture = 0;
|
||||
if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
|
||||
bCapture = 1;
|
||||
}
|
||||
|
||||
if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
|
||||
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
|
||||
if( bCapture && rc==SQLITE_OK ){
|
||||
pRbu->mLock |= (1 << ofst);
|
||||
pRbu->mLock |= ((1<<n) - 1) << ofst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ proc test_reset {} {
|
||||
|
||||
test_reset
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES('i', 'one');
|
||||
}
|
||||
do_iterator_test 1.1 t1 {
|
||||
@@ -184,7 +184,7 @@ set set_of_tests {
|
||||
|
||||
test_reset
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t1(a int PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
|
||||
CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
|
||||
CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
|
||||
@@ -206,17 +206,17 @@ sqlite3 db3 test.db3
|
||||
do_test 3.0 {
|
||||
execsql {
|
||||
ATTACH 'test.db3' AS 'aux';
|
||||
CREATE TABLE t1(a, b PRIMARY KEY);
|
||||
CREATE TABLE t1(a int, b PRIMARY KEY);
|
||||
CREATE TABLE t2(x, y, z);
|
||||
CREATE TABLE t3(a);
|
||||
|
||||
CREATE TABLE aux.t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE aux.t1(a int PRIMARY KEY, b);
|
||||
CREATE TABLE aux.t2(a, b INTEGER PRIMARY KEY);
|
||||
CREATE TABLE aux.t3(a, b, c, PRIMARY KEY(a, b));
|
||||
CREATE TABLE aux.t4(a, b, PRIMARY KEY(b, a));
|
||||
}
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t1(a int PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
|
||||
CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b));
|
||||
CREATE TABLE t4(a, b, PRIMARY KEY(b, a));
|
||||
@@ -588,4 +588,52 @@ do_execsql_test 10.2 {
|
||||
} {0 0 1 1}
|
||||
S delete
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
test_reset
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e, f);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<32
|
||||
)
|
||||
INSERT INTO t1 SELECT NULL, 0, 0, 0, 0, 0 FROM s
|
||||
}
|
||||
|
||||
do_then_apply_sql {
|
||||
UPDATE t1 SET f=f+1 WHERE a=1;
|
||||
UPDATE t1 SET e=e+1 WHERE a=2;
|
||||
UPDATE t1 SET e=e+1, f=f+1 WHERE a=3;
|
||||
UPDATE t1 SET d=d+1 WHERE a=4;
|
||||
UPDATE t1 SET d=d+1, f=f+1 WHERE a=5;
|
||||
UPDATE t1 SET d=d+1, e=e+1 WHERE a=6;
|
||||
UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=7;
|
||||
UPDATE t1 SET c=c+1 WHERE a=8;
|
||||
UPDATE t1 SET c=c+1, f=f+1 WHERE a=9;
|
||||
UPDATE t1 SET c=c+1, e=e+1 WHERE a=10;
|
||||
UPDATE t1 SET c=c+1, e=e+1, f=f+1 WHERE a=11;
|
||||
UPDATE t1 SET c=c+1, d=d+1 WHERE a=12;
|
||||
UPDATE t1 SET c=c+1, d=d+1, f=f+1 WHERE a=13;
|
||||
UPDATE t1 SET c=c+1, d=d+1, e=e+1 WHERE a=14;
|
||||
UPDATE t1 SET c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=15;
|
||||
UPDATE t1 SET d=d+1 WHERE a=16;
|
||||
UPDATE t1 SET d=d+1, f=f+1 WHERE a=17;
|
||||
UPDATE t1 SET d=d+1, e=e+1 WHERE a=18;
|
||||
UPDATE t1 SET d=d+1, e=e+1, f=f+1 WHERE a=19;
|
||||
UPDATE t1 SET d=d+1, d=d+1 WHERE a=20;
|
||||
UPDATE t1 SET d=d+1, d=d+1, f=f+1 WHERE a=21;
|
||||
UPDATE t1 SET d=d+1, d=d+1, e=e+1 WHERE a=22;
|
||||
UPDATE t1 SET d=d+1, d=d+1, e=e+1, f=f+1 WHERE a=23;
|
||||
UPDATE t1 SET d=d+1, c=c+1 WHERE a=24;
|
||||
UPDATE t1 SET d=d+1, c=c+1, f=f+1 WHERE a=25;
|
||||
UPDATE t1 SET d=d+1, c=c+1, e=e+1 WHERE a=26;
|
||||
UPDATE t1 SET d=d+1, c=c+1, e=e+1, f=f+1 WHERE a=27;
|
||||
UPDATE t1 SET d=d+1, c=c+1, d=d+1 WHERE a=28;
|
||||
UPDATE t1 SET d=d+1, c=c+1, d=d+1, f=f+1 WHERE a=29;
|
||||
UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1 WHERE a=30;
|
||||
UPDATE t1 SET d=d+1, c=c+1, d=d+1, e=e+1, f=f+1 WHERE a=31;
|
||||
}
|
||||
|
||||
do_test 11.0 {
|
||||
compare_db db db2
|
||||
} {}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -69,7 +69,9 @@ foreach {tn wo} {
|
||||
} {
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.$tn.0 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
|
||||
do_execsql_test 2.$tn.0.1 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b) $wo ;"
|
||||
do_execsql_test 2.$tn.0.2 "CREATE TABLE t2(a INTEGER PRIMARY KEY, b) $wo ;"
|
||||
do_execsql_test 2.$tn.0.3 "CREATE TABLE t3(a INTEGER PRIMARY KEY, b) $wo ;"
|
||||
|
||||
do_iterator_test 1.1 t1 {
|
||||
INSERT INTO t1 VALUES(1, 'two');
|
||||
@@ -94,6 +96,27 @@ foreach {tn wo} {
|
||||
} {
|
||||
{DELETE t1 0 X. {i 1 t four} {}}
|
||||
}
|
||||
|
||||
do_execsql_test 2.$tn.5 {
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
}
|
||||
|
||||
do_iterator_test 2.$tn.6 t2 {
|
||||
INSERT INTO t2 SELECT a, b FROM t1
|
||||
} {
|
||||
{INSERT t2 0 X. {} {i 1 t one}}
|
||||
{INSERT t2 0 X. {} {i 2 t two}}
|
||||
{INSERT t2 0 X. {} {i 3 t three}}
|
||||
}
|
||||
do_iterator_test 2.$tn.7 t3 {
|
||||
INSERT INTO t3 SELECT * FROM t1
|
||||
} {
|
||||
{INSERT t3 0 X. {} {i 1 t one}}
|
||||
{INSERT t3 0 X. {} {i 2 t two}}
|
||||
{INSERT t3 0 X. {} {i 3 t three}}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+187
-128
@@ -3510,16 +3510,25 @@ int sqlite3changeset_invert_strm(
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
typedef struct SessionUpdate SessionUpdate;
|
||||
struct SessionUpdate {
|
||||
sqlite3_stmt *pStmt;
|
||||
u32 *aMask;
|
||||
SessionUpdate *pNext;
|
||||
};
|
||||
|
||||
typedef struct SessionApplyCtx SessionApplyCtx;
|
||||
struct SessionApplyCtx {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *pDelete; /* DELETE statement */
|
||||
sqlite3_stmt *pUpdate; /* UPDATE statement */
|
||||
sqlite3_stmt *pInsert; /* INSERT statement */
|
||||
sqlite3_stmt *pSelect; /* SELECT statement */
|
||||
int nCol; /* Size of azCol[] and abPK[] arrays */
|
||||
const char **azCol; /* Array of column names */
|
||||
u8 *abPK; /* Boolean array - true if column is in PK */
|
||||
u32 *aUpdateMask; /* Used by sessionUpdateFind */
|
||||
SessionUpdate *pUp;
|
||||
int bStat1; /* True if table is sqlite_stat1 */
|
||||
int bDeferConstraints; /* True to defer constraints */
|
||||
int bInvertConstraints; /* Invert when iterating constraints buffer */
|
||||
@@ -3529,6 +3538,167 @@ struct SessionApplyCtx {
|
||||
u8 bRebase; /* True to collect rebase information */
|
||||
};
|
||||
|
||||
/* Number of prepared UPDATE statements to cache. */
|
||||
#define SESSION_UPDATE_CACHE_SZ 12
|
||||
|
||||
/*
|
||||
** Find a prepared UPDATE statement suitable for the UPDATE step currently
|
||||
** being visited by the iterator. The UPDATE is of the form:
|
||||
**
|
||||
** UPDATE tbl SET col = ?, col2 = ? WHERE pk1 IS ? AND pk2 IS ?
|
||||
*/
|
||||
static int sessionUpdateFind(
|
||||
sqlite3_changeset_iter *pIter,
|
||||
SessionApplyCtx *p,
|
||||
int bPatchset,
|
||||
sqlite3_stmt **ppStmt
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SessionUpdate *pUp = 0;
|
||||
int nCol = pIter->nCol;
|
||||
int nU32 = (pIter->nCol+33)/32;
|
||||
int ii;
|
||||
|
||||
if( p->aUpdateMask==0 ){
|
||||
p->aUpdateMask = sqlite3_malloc(nU32*sizeof(u32));
|
||||
if( p->aUpdateMask==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(p->aUpdateMask, 0, nU32*sizeof(u32));
|
||||
rc = SQLITE_CORRUPT;
|
||||
for(ii=0; ii<pIter->nCol; ii++){
|
||||
if( sessionChangesetNew(pIter, ii) ){
|
||||
p->aUpdateMask[ii/32] |= (1<<(ii%32));
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( bPatchset ) p->aUpdateMask[nCol/32] |= (1<<(nCol%32));
|
||||
|
||||
if( p->pUp ){
|
||||
int nUp = 0;
|
||||
SessionUpdate **pp = &p->pUp;
|
||||
while( 1 ){
|
||||
nUp++;
|
||||
if( 0==memcmp(p->aUpdateMask, (*pp)->aMask, nU32*sizeof(u32)) ){
|
||||
pUp = *pp;
|
||||
*pp = pUp->pNext;
|
||||
pUp->pNext = p->pUp;
|
||||
p->pUp = pUp;
|
||||
break;
|
||||
}
|
||||
|
||||
if( (*pp)->pNext ){
|
||||
pp = &(*pp)->pNext;
|
||||
}else{
|
||||
if( nUp>=SESSION_UPDATE_CACHE_SZ ){
|
||||
sqlite3_finalize((*pp)->pStmt);
|
||||
sqlite3_free(*pp);
|
||||
*pp = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( pUp==0 ){
|
||||
int nByte = sizeof(SessionUpdate) * nU32*sizeof(u32);
|
||||
int bStat1 = (sqlite3_stricmp(pIter->zTab, "sqlite_stat1")==0);
|
||||
pUp = (SessionUpdate*)sqlite3_malloc(nByte);
|
||||
if( pUp==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
const char *zSep = "";
|
||||
SessionBuffer buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
pUp->aMask = (u32*)&pUp[1];
|
||||
memcpy(pUp->aMask, p->aUpdateMask, nU32*sizeof(u32));
|
||||
|
||||
sessionAppendStr(&buf, "UPDATE main.", &rc);
|
||||
sessionAppendIdent(&buf, pIter->zTab, &rc);
|
||||
sessionAppendStr(&buf, " SET ", &rc);
|
||||
|
||||
/* Create the assignments part of the UPDATE */
|
||||
for(ii=0; ii<pIter->nCol; ii++){
|
||||
if( p->abPK[ii]==0 && sessionChangesetNew(pIter, ii) ){
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[ii], &rc);
|
||||
sessionAppendStr(&buf, " = ?", &rc);
|
||||
sessionAppendInteger(&buf, ii*2+1, &rc);
|
||||
zSep = ", ";
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the WHERE clause part of the UPDATE */
|
||||
zSep = "";
|
||||
sessionAppendStr(&buf, " WHERE ", &rc);
|
||||
for(ii=0; ii<pIter->nCol; ii++){
|
||||
if( p->abPK[ii] || (bPatchset==0 && sessionChangesetOld(pIter, ii)) ){
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
if( bStat1 && ii==1 ){
|
||||
assert( sqlite3_stricmp(p->azCol[ii], "idx")==0 );
|
||||
sessionAppendStr(&buf,
|
||||
"idx IS CASE "
|
||||
"WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL "
|
||||
"ELSE ?4 END ", &rc
|
||||
);
|
||||
}else{
|
||||
sessionAppendIdent(&buf, p->azCol[ii], &rc);
|
||||
sessionAppendStr(&buf, " IS ?", &rc);
|
||||
sessionAppendInteger(&buf, ii*2+2, &rc);
|
||||
}
|
||||
zSep = " AND ";
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
char *zSql = (char*)buf.aBuf;
|
||||
rc = sqlite3_prepare_v2(p->db, zSql, buf.nBuf, &pUp->pStmt, 0);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_free(pUp);
|
||||
pUp = 0;
|
||||
}else{
|
||||
pUp->pNext = p->pUp;
|
||||
p->pUp = pUp;
|
||||
}
|
||||
sqlite3_free(buf.aBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert( (rc==SQLITE_OK)==(pUp!=0) );
|
||||
if( pUp ){
|
||||
*ppStmt = pUp->pStmt;
|
||||
}else{
|
||||
*ppStmt = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all cached UPDATE statements.
|
||||
*/
|
||||
static void sessionUpdateFree(SessionApplyCtx *p){
|
||||
SessionUpdate *pUp;
|
||||
SessionUpdate *pNext;
|
||||
for(pUp=p->pUp; pUp; pUp=pNext){
|
||||
pNext = pUp->pNext;
|
||||
sqlite3_finalize(pUp->pStmt);
|
||||
sqlite3_free(pUp);
|
||||
}
|
||||
p->pUp = 0;
|
||||
sqlite3_free(p->aUpdateMask);
|
||||
p->aUpdateMask = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Formulate a statement to DELETE a row from database db. Assuming a table
|
||||
** structure like this:
|
||||
@@ -3598,103 +3768,6 @@ static int sessionDeleteRow(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Formulate and prepare a statement to UPDATE a row from database db.
|
||||
** Assuming a table structure like this:
|
||||
**
|
||||
** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
|
||||
**
|
||||
** The UPDATE statement looks like this:
|
||||
**
|
||||
** UPDATE x SET
|
||||
** a = CASE WHEN ?2 THEN ?3 ELSE a END,
|
||||
** b = CASE WHEN ?5 THEN ?6 ELSE b END,
|
||||
** c = CASE WHEN ?8 THEN ?9 ELSE c END,
|
||||
** d = CASE WHEN ?11 THEN ?12 ELSE d END
|
||||
** WHERE a = ?1 AND c = ?7 AND (?13 OR
|
||||
** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
|
||||
** )
|
||||
**
|
||||
** For each column in the table, there are three variables to bind:
|
||||
**
|
||||
** ?(i*3+1) The old.* value of the column, if any.
|
||||
** ?(i*3+2) A boolean flag indicating that the value is being modified.
|
||||
** ?(i*3+3) The new.* value of the column, if any.
|
||||
**
|
||||
** Also, a boolean flag that, if set to true, causes the statement to update
|
||||
** a row even if the non-PK values do not match. This is required if the
|
||||
** conflict-handler is invoked with CHANGESET_DATA and returns
|
||||
** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
|
||||
**
|
||||
** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
|
||||
** pointing to the prepared version of the SQL statement.
|
||||
*/
|
||||
static int sessionUpdateRow(
|
||||
sqlite3 *db, /* Database handle */
|
||||
const char *zTab, /* Table name */
|
||||
SessionApplyCtx *p /* Session changeset-apply context */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
const char *zSep = "";
|
||||
SessionBuffer buf = {0, 0, 0};
|
||||
|
||||
/* Append "UPDATE tbl SET " */
|
||||
sessionAppendStr(&buf, "UPDATE main.", &rc);
|
||||
sessionAppendIdent(&buf, zTab, &rc);
|
||||
sessionAppendStr(&buf, " SET ", &rc);
|
||||
|
||||
/* Append the assignments */
|
||||
for(i=0; i<p->nCol; i++){
|
||||
sessionAppendStr(&buf, zSep, &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
|
||||
sessionAppendInteger(&buf, i*3+2, &rc);
|
||||
sessionAppendStr(&buf, " THEN ?", &rc);
|
||||
sessionAppendInteger(&buf, i*3+3, &rc);
|
||||
sessionAppendStr(&buf, " ELSE ", &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " END", &rc);
|
||||
zSep = ", ";
|
||||
}
|
||||
|
||||
/* Append the PK part of the WHERE clause */
|
||||
sessionAppendStr(&buf, " WHERE ", &rc);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( p->abPK[i] ){
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " = ?", &rc);
|
||||
sessionAppendInteger(&buf, i*3+1, &rc);
|
||||
sessionAppendStr(&buf, " AND ", &rc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Append the non-PK part of the WHERE clause */
|
||||
sessionAppendStr(&buf, " (?", &rc);
|
||||
sessionAppendInteger(&buf, p->nCol*3+1, &rc);
|
||||
sessionAppendStr(&buf, " OR 1", &rc);
|
||||
for(i=0; i<p->nCol; i++){
|
||||
if( !p->abPK[i] ){
|
||||
sessionAppendStr(&buf, " AND (?", &rc);
|
||||
sessionAppendInteger(&buf, i*3+2, &rc);
|
||||
sessionAppendStr(&buf, "=0 OR ", &rc);
|
||||
sessionAppendIdent(&buf, p->azCol[i], &rc);
|
||||
sessionAppendStr(&buf, " IS ?", &rc);
|
||||
sessionAppendInteger(&buf, i*3+1, &rc);
|
||||
sessionAppendStr(&buf, ")", &rc);
|
||||
}
|
||||
}
|
||||
sessionAppendStr(&buf, ")", &rc);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
|
||||
}
|
||||
sqlite3_free(buf.aBuf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Formulate and prepare an SQL statement to query table zTab by primary
|
||||
** key. Assuming the following table structure:
|
||||
@@ -3775,17 +3848,6 @@ static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
|
||||
"?3)"
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionPrepare(db, &p->pUpdate,
|
||||
"UPDATE main.sqlite_stat1 SET "
|
||||
"tbl = CASE WHEN ?2 THEN ?3 ELSE tbl END, "
|
||||
"idx = CASE WHEN ?5 THEN ?6 ELSE idx END, "
|
||||
"stat = CASE WHEN ?8 THEN ?9 ELSE stat END "
|
||||
"WHERE tbl=?1 AND idx IS "
|
||||
"CASE WHEN length(?4)=0 AND typeof(?4)='blob' THEN NULL ELSE ?4 END "
|
||||
"AND (?10 OR ?8=0 OR stat IS ?7)"
|
||||
);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionPrepare(db, &p->pDelete,
|
||||
"DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
|
||||
@@ -4102,7 +4164,7 @@ static int sessionApplyOneOp(
|
||||
int nCol;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
|
||||
assert( p->pDelete && p->pInsert && p->pSelect );
|
||||
assert( p->azCol && p->abPK );
|
||||
assert( !pbReplace || *pbReplace==0 );
|
||||
|
||||
@@ -4142,29 +4204,28 @@ static int sessionApplyOneOp(
|
||||
|
||||
}else if( op==SQLITE_UPDATE ){
|
||||
int i;
|
||||
sqlite3_stmt *pUp = 0;
|
||||
int bPatchset = (pbRetry==0 || pIter->bPatchset);
|
||||
|
||||
rc = sessionUpdateFind(pIter, p, bPatchset, &pUp);
|
||||
|
||||
/* Bind values to the UPDATE statement. */
|
||||
for(i=0; rc==SQLITE_OK && i<nCol; i++){
|
||||
sqlite3_value *pOld = sessionChangesetOld(pIter, i);
|
||||
sqlite3_value *pNew = sessionChangesetNew(pIter, i);
|
||||
|
||||
sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
|
||||
if( pOld ){
|
||||
rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
|
||||
if( p->abPK[i] || (bPatchset==0 && pOld) ){
|
||||
rc = sessionBindValue(pUp, i*2+2, pOld);
|
||||
}
|
||||
if( rc==SQLITE_OK && pNew ){
|
||||
rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
|
||||
rc = sessionBindValue(pUp, i*2+1, pNew);
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
|
||||
}
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
|
||||
/* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
|
||||
** the result will be SQLITE_OK with 0 rows modified. */
|
||||
sqlite3_step(p->pUpdate);
|
||||
rc = sqlite3_reset(p->pUpdate);
|
||||
sqlite3_step(pUp);
|
||||
rc = sqlite3_reset(pUp);
|
||||
|
||||
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
|
||||
/* A NOTFOUND or DATA error. Search the table to see if it contains
|
||||
@@ -4387,14 +4448,13 @@ static int sessionChangesetApply(
|
||||
);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
sessionUpdateFree(&sApply);
|
||||
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
|
||||
sqlite3_finalize(sApply.pDelete);
|
||||
sqlite3_finalize(sApply.pUpdate);
|
||||
sqlite3_finalize(sApply.pInsert);
|
||||
sqlite3_finalize(sApply.pSelect);
|
||||
sApply.db = db;
|
||||
sApply.pDelete = 0;
|
||||
sApply.pUpdate = 0;
|
||||
sApply.pInsert = 0;
|
||||
sApply.pSelect = 0;
|
||||
sApply.nCol = 0;
|
||||
@@ -4458,11 +4518,10 @@ static int sessionChangesetApply(
|
||||
}
|
||||
sApply.bStat1 = 1;
|
||||
}else{
|
||||
if((rc = sessionSelectRow(db, zTab, &sApply))
|
||||
|| (rc = sessionUpdateRow(db, zTab, &sApply))
|
||||
|| (rc = sessionDeleteRow(db, zTab, &sApply))
|
||||
|| (rc = sessionInsertRow(db, zTab, &sApply))
|
||||
){
|
||||
if( (rc = sessionSelectRow(db, zTab, &sApply))
|
||||
|| (rc = sessionDeleteRow(db, zTab, &sApply))
|
||||
|| (rc = sessionInsertRow(db, zTab, &sApply))
|
||||
){
|
||||
break;
|
||||
}
|
||||
sApply.bStat1 = 0;
|
||||
@@ -4521,9 +4580,9 @@ static int sessionChangesetApply(
|
||||
*pnRebase = sApply.rebase.nBuf;
|
||||
sApply.rebase.aBuf = 0;
|
||||
}
|
||||
sessionUpdateFree(&sApply);
|
||||
sqlite3_finalize(sApply.pInsert);
|
||||
sqlite3_finalize(sApply.pDelete);
|
||||
sqlite3_finalize(sApply.pUpdate);
|
||||
sqlite3_finalize(sApply.pSelect);
|
||||
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
|
||||
sqlite3_free((char*)sApply.constraints.aBuf);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C Merge\sfixes\sfrom\strunk.\s\sOmit\sa\sconditional\sin\s\nsqlite3IsLikeFunction()\sthat\swas\smade\sunreachable\sby\sthis\senhancement.
|
||||
D 2021-01-22T20:28:30.345
|
||||
C Fix\sa\scrash\sthat\scould\soccur\sin\sALTER\sTABLE\sDROP\sCOLUMN\sif\sthe\ssqlite_schema\stable\swas\scorrupt.
|
||||
D 2021-02-19T14:13:40.890
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -379,7 +379,7 @@ F ext/rbu/rbuvacuum.test 55e101e90168c2b31df6c9638fe73dc7f7cc666b6142266d1563697
|
||||
F ext/rbu/rbuvacuum2.test b8e5b51dc8b2c0153373d024c0936be3f66f9234acbd6d0baab0869d56b14e6b
|
||||
F ext/rbu/rbuvacuum3.test 8addd82e4b83b4c93fa47428eae4fd0dbf410f8512c186f38e348feb49ba03dc
|
||||
F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2f1dbccfd10
|
||||
F ext/rbu/sqlite3rbu.c 05c457c27e9340c944f34e850871a915a6b5ee1d823f7a0bb2b482ac6b1e1464
|
||||
F ext/rbu/sqlite3rbu.c e6531884442b72f9e0ba47036fb5c4641ea817ff659a642b3984c10b8535b0fd
|
||||
F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812
|
||||
F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a
|
||||
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
|
||||
@@ -428,7 +428,7 @@ F ext/session/changeset.c 7a1e6a14c7e92d36ca177e92e88b5281acd709f3b726298dc34ec0
|
||||
F ext/session/changesetfuzz.c 227076ab0ae4447d742c01ee88a564da6478bbf26b65108bf8fac9cd8b0b24aa
|
||||
F ext/session/changesetfuzz1.test 2e1b90d888fbf0eea5e1bd2f1e527a48cc85f8e0ff75df1ec4e320b21f580b3a
|
||||
F ext/session/session1.test 0b2f88995832ea040ae8e83a1ad4afa99c00b85c779d213da73a95ea4113233e
|
||||
F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
|
||||
F ext/session/session2.test 7f53d755d921e0baf815c4258348e0ed460dfd8a772351bca5ad3ccbb1dc786e
|
||||
F ext/session/session3.test ce9ce3dfa489473987f899e9f6a0f2db9bde3479
|
||||
F ext/session/session4.test 6778997065b44d99c51ff9cece047ff9244a32856b328735ae27ddef68979c40
|
||||
F ext/session/session5.test 716bc6fafd625ce60dfa62ae128971628c1a1169
|
||||
@@ -453,8 +453,8 @@ F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2
|
||||
F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
|
||||
F ext/session/sessionrebase.test ccfa716b23bd1d3b03217ee58cfd90c78d4b99f53e6a9a2f05e82363b9142810
|
||||
F ext/session/sessionstat1.test 218d351cf9fcd6648f125a26b607b140310160184723c2666091b54450a68fb5
|
||||
F ext/session/sessionwor.test 67b5ab91d4f93ce65ff1f58240ac5ddf73f8670facc1ffa49cef56293d52818d
|
||||
F ext/session/sqlite3session.c d2aaaf05241ac7d23a1b1eaa8b1f165c90f7ff0fe57ff1932a87c6b89b886117
|
||||
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
||||
F ext/session/sqlite3session.c 1d0553077b55ffcfa69963c354e9bad3bace6ce79bbe7368e650c6ae1e106314
|
||||
F ext/session/sqlite3session.h f53c99731882bf59c7362855cdeba176ce1fe8eeba089e38a8cce0172f8473aa
|
||||
F ext/session/test_session.c 93ca965112d2b4d9d669c9c0be6b1e52942a268796050a145612df1eee175ce0
|
||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
@@ -475,34 +475,34 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 36cae0d6e3e91a1996e1a472f8c7242c31a4e38ba4295e3056da198c04fd2a87
|
||||
F src/alter.c 1addd06a7aae343497ebede71ec355c2011f175075943007341693ebf7daa47d
|
||||
F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c
|
||||
F src/attach.c 87102aba5ddac8ef3a8a033ab657e4cae9cc8e846efe93b14029cfffaaf8831e
|
||||
F src/auth.c 8d1df0e2ef8bafbedd4f1fe4baff03eb27507da4bf6e449df3613d383c4018b2
|
||||
F src/attach.c e80162a47411f296bea550ed8fafd730481f4aa71e89ece23ba9c957eed15d4a
|
||||
F src/auth.c 08954fdc4cc2da5264ba5b75cfd90b67a6fc7d1710a02ccf917c38eadec77853
|
||||
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c 47d9fe97d5c0d74506154e3597f8a23b81a00080751dc4d11fec91ee22796f4c
|
||||
F src/btree.c 694020ad8a3af3d79b09f74c8f1421272a419cdea42a13401e3b0f7dea6e9c3e
|
||||
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
|
||||
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
|
||||
F src/build.c d4c06261b0e532523ede58dc511381a7a9c155132e4b65a6bb2ff76fe657793a
|
||||
F src/build.c 72b868d9c8a0d56aef4237825b0fad38898a2d9310669940383accf20429c5ca
|
||||
F src/callback.c d0b853dd413255d2e337b34545e54d888ea02f20da5ad0e63585b389624c4a6c
|
||||
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 2a322b9a3d75771fb4d99e0702851f4f68dda982507a0f798eefb0712969a410
|
||||
F src/date.c dace306a10d9b02ee553d454c8e1cf8d3c9b932e137738a6b15b90253a9bfc10
|
||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
|
||||
F src/delete.c 927cf8f900583e79aca8f1a321979e0a8f053babd9a690b44b38f79de2cc09fe
|
||||
F src/delete.c 352ea931218c45a3daf17472d4141b9c7fc026d85da3f1ade404ea5bb6d67f77
|
||||
F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
|
||||
F src/func.c 796a7a4a0ff5eee82a04ee3c8265c5ebf9c6a9f5625621c5f97ed94f6224d7d9
|
||||
F src/fkey.c 73adaca988d0dd517d373b432dc9dfa2cd7fa3108b114260132a80832de19037
|
||||
F src/func.c 479f6929be027eb0210cbdde9d3529c012facf082d64a6b854a9415940761e5e
|
||||
F src/global.c ed55af196a9b66e198aaeda3f5454c3aa7d7d050c6c938181fd044b70d180a81
|
||||
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
|
||||
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
|
||||
F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 9b970eff058a858fbd9f2db71425ef195942c2610855daa66ae23024432d52f5
|
||||
F src/insert.c 3959a2e8a6c1e688e7390ef242472817653f4ae9a42bb78b293eaa98645f1a07
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
|
||||
F src/main.c 1c5de7b3fabcdf05f4fe563aab5d81d175b89c67a8678a12ba86629356afa356
|
||||
@@ -530,23 +530,23 @@ F src/os_win.c 77d39873836f1831a9b0b91894fec45ab0e9ca8e067dc8c549e1d1eca1566fe9
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c c49952ac5e9cc536778eff528091d79d38b3e45cbeeed4695dc05e207dc6547d
|
||||
F src/pager.h 4bf9b3213a4b2bebbced5eaa8b219cf25d4a82f385d093cd64b7e93e5285f66f
|
||||
F src/parse.y 6c8aa09a7fa6e0867c3a3d67ef61b911aa392c9b084a61dc632cd93732aef8ad
|
||||
F src/parse.y cdc4e5efaf1618d82f6b241a30f1a586188bf37e9c55f967258ed15fe24c51a2
|
||||
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
||||
F src/pragma.c 6daaaecc26a4b09481d21722525b079ce756751a43a79cc1d8f122d686806193
|
||||
F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
|
||||
F src/prepare.c f288cbc35f79eb32e162de7e80a63ebe00d80e639dcfac071bee11570cbdb16f
|
||||
F src/prepare.c 7a534d100c556f45f10aee131f2e4244cb52547b7cf17e1c393f55d8abb62e97
|
||||
F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c
|
||||
F src/resolve.c 52f81603cc40f78449f5b6aed96dbea9484b194771ecb1937e8c0f6547c186a0
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 738cb746189f721f59972993c13085fa2975c4cbfd04ba26445f3b42c81237dc
|
||||
F src/shell.c.in 9ebc74e4f05cfbd0f4a36060fdaeff1da4e9af4458358722bc08c5a1ab9a0879
|
||||
F src/sqlite.h.in 0af968a1fa3c717261e1df0ed105fa7bddb4d82de7e0adb3eab49e6aa81b4de7
|
||||
F src/select.c 371a5c95d47d4e969337548ed9bd344788f15518a1c75921507355bd93815205
|
||||
F src/shell.c.in 844417f84df1f6c4fce1c815629a888cfdcf219e86513e9c332bbcc38832f477
|
||||
F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||
F src/sqliteInt.h 3e5bc0446611e272b93754265e3265f36249d0458da25e32991fce241d69dbcf
|
||||
F src/sqliteInt.h 4bd449478df0444ccb9f6a8e6f210b2ceacdd59562f52630edb0bb2d8cc25094
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -607,31 +607,31 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c c64c49d7c2ec4490c2fef1f24350167ba16b03b0c6cee58ad1a1d70a4325d4e9
|
||||
F src/treeview.c 4b92992176fb2caefbe06ba5bd06e0e0ebcde3d5564758da672631f17aa51cda
|
||||
F src/trigger.c 29680c54c1aa088fa1b4d50137d75669a40d5ef814394e321fab1e547868e3d3
|
||||
F src/update.c 9f126204a6acb96bbe47391ae48e0fc579105d8e76a6d9c4fab3271367476580
|
||||
F src/trigger.c 861c3ec2c5b0fc830bdf82470454a9324fad70cbaa96d2e208fb54577c9e8d28
|
||||
F src/update.c 0f5a61f0787199983530a33f6fffe4f52742f35fcdf6ccfad1078b1a8bc17723
|
||||
F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
|
||||
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
||||
F src/vdbe.c fd3ae827dea9da118d22c172ad183888120070d4c709bd0a5a97af7749246783
|
||||
F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
|
||||
F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
|
||||
F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
|
||||
F src/vdbeaux.c e91d74e24babcf61969279b193e228cf4f8bc724a9cc59ed287db064326876f8
|
||||
F src/vdbe.c 9766281c0febf25f30a043d98b2417011acc7b04f287d3c671c3e59f656fc208
|
||||
F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
|
||||
F src/vdbeInt.h 3df118924e1711f1bbc8e30c46260d0ab6c3b029b32dd411f789111f76434f3c
|
||||
F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b
|
||||
F src/vdbeaux.c fb51483c2bcaf45c5de63c26cce8649ef37f1332e8e035867033d21ef5e7fc2c
|
||||
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
||||
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
|
||||
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
|
||||
F src/vdbetrace.c 666c6fd9f1b62be6999e072a45b913e3c2c3518bc60dfd4d54fe304130acb724
|
||||
F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c
|
||||
F src/vtab.c 5f5fc793092f53bbdfde296c50f563fb7bda58cf48e9cf6a8bdfbc5abd409845
|
||||
F src/vtab.c 032a0165c147fda16927e6a3230e90c068d4af93f887ce94e26f678fe48e5e4c
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 69e770e96fd56cc21608992bf2c6f1f3dc5cf2572d0495c6a643b06c3a679f14
|
||||
F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
|
||||
F src/walker.c d9c4e454ebb9499e908aa62d55b8994c375cf5355ac78f60d45af17f7890701c
|
||||
F src/where.c 0e6abb22a2323fec80b450825593c26a2ad8f4815d1ee3af9969d8f6144bf681
|
||||
F src/where.c 6efc4a10bfe0ec908c4f3c9112afb7675c952204b4b0b255672f488860141fec
|
||||
F src/whereInt.h ae03b5e3a4cca9bd9cb1b7d3c63faf8f1f177200fc8cecc87d3d0cab6ca338e6
|
||||
F src/wherecode.c a3a1aff30fe99a818d8e7c607980f033f40c68d890e03ed25838b9dbb7908bee
|
||||
F src/whereexpr.c a182038cf7d10aa9a95a96b8563a34f34990323cf307dee6559e995961966d43
|
||||
F src/wherecode.c 43a63441f8662ddf86b15975683a502ec33f08167e9636f4d19e38e265e95fd9
|
||||
F src/whereexpr.c f7b5469e83db3c3b9eb14e4ba44559a2e125523761d12e5ac8d8fb88301af393
|
||||
F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
@@ -645,11 +645,14 @@ F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807
|
||||
F test/alter3.test e487958dec7932453e0b83baf21d6b1e71d5e7d9a55bc20eadfa62a51ddffc29
|
||||
F test/alter4.test dfd6086faf461b27ca2d2999848dcd207edf23352fc1592d0005c0844f3f08cf
|
||||
F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959
|
||||
F test/alterauth2.test c0a1ddf5b93d93cb0d15ba7acaf0c5c6fb515bbe861ede75b2d3fabad33b6499
|
||||
F test/alterauth2.test 794ac5cef251819fe364b4fe20f12f86e9c5d68070513c7fd26c17cb244c89af
|
||||
F test/altercol.test 1d6a6fe698b81e626baea4881f5717f9bc53d7d07f1cd23ee7ad1b931f117ddf
|
||||
F test/alterlegacy.test 82022721ce0de29cedc9a7af63bc9fcc078b0ee000f8283b4b6ea9c3eab2f44b
|
||||
F test/altercorrupt.test cb17a81f655a0d71bc1e48e44741a5e0905a3b3efbbe485b0b6c7648f2af3eed
|
||||
F test/alterdropcol.test baad37ff9b07078ea02dcc33dbfb82bde655f3eee5c453e218f69501c36f02ba
|
||||
F test/alterlegacy.test f38c6d06cda39e1f7b955bbce57f2e3ef5b7cb566d3d1234502093e228c15811
|
||||
F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74a3fb80c9
|
||||
F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
|
||||
F test/altermalloc3.test 2c7bbd8cf3e9c4a91e28675bb62bcc2ef70f227967fa74349f03d9f4642f0615
|
||||
F test/altertab.test 6d7bbac2c4a6ef71b775094a3298fa3a92274d95034ee23157ffba92768e47e6
|
||||
F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b
|
||||
F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479
|
||||
@@ -697,7 +700,7 @@ F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
|
||||
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
|
||||
F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d
|
||||
F test/backup.test dd4a5ff756e3df3931dacb1791db0584d4bad989
|
||||
F test/backup2.test 1fd1ad8c5b3d2d5b9c0cce4143a4fc610d51ddc6ae16a7a122973d43e6b50bbd
|
||||
F test/backup2.test 8facb54df1388419d34b362ab1f7e233310ff3a3af64e8ad5ec47ba3c2bbe5cf
|
||||
F test/backup4.test 8f6fd48e0dfde77b9a3bb26dc471ede3e101df32
|
||||
F test/backup5.test ee5da6d7fe5082f5b9b0bbfa31d016f52412a2e4
|
||||
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
|
||||
@@ -766,6 +769,7 @@ F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
|
||||
F test/collateB.test 1e68906951b846570f29f20102ed91d29e634854ee47454d725f2151ecac0b95
|
||||
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
|
||||
F test/colname.test 87ad5458bb8709312dac0d6755fd30e8e4ca83298d0a9ef6e5c24277a3c3390e
|
||||
F test/columncount.test eff33d402a7b0fde0a52a1920d238af200ca573327021e0ce3b7e5688de41449
|
||||
F test/conflict.test ac0667090f66130ac77d5fb764655558ca6600dd6d88f670ca9123b61c448337
|
||||
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
|
||||
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
|
||||
@@ -1019,7 +1023,7 @@ F test/fts4noti.test 5553d7bb2e20bf4a06b23e849352efc022ce6309
|
||||
F test/fts4onepass.test d69ddc4ee3415e40b0c5d1d0408488a87614d4f63ba9c44f3e52db541d6b7cc7
|
||||
F test/fts4opt.test 0fd0cc84000743ff2a883b9b84b4a5be07249f0ba790c8848a757164cdd46b2a
|
||||
F test/fts4record.test a48508f69a84c9287c8019d3a1ae712f5730d8335ffaf8e2101e691d078950bb
|
||||
F test/fts4rename.test 15fd9985c2bce6dea20da2245b22029ec89bd4710ed317c4c53abbe3cfd0c880
|
||||
F test/fts4rename.test 2e0565ffd92b2c51f1a757df0b8f2ca30537197106fec09e943919801d173692
|
||||
F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f757380429
|
||||
F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9
|
||||
F test/fts4upfrom.test 8df5acb6e10ad73f393d1add082b042ab1db72567888847d098152121e507b34
|
||||
@@ -1030,7 +1034,7 @@ F test/func3.test 2bb0f31ab7baaed690b962a88544d7be6b34fa389364bc36a44e441ed3e3f1
|
||||
F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31
|
||||
F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d82a
|
||||
F test/func6.test 90e42b64c4f9fb6f04f44cb8a1da586c8542502e926b19c76504fe74ff2a9b7c
|
||||
F test/func7.test bb05a77daedf0e3f8764f323a49bc3b8d98f280a0bc6a370387117f4596bde05
|
||||
F test/func7.test b9e2a1a30a8562b00841b4a21a5d2d81754fa3ab99275fd71fd5279287b44b1c
|
||||
F test/fuzz-oss1.test e58330d01cbbd8215ee636b17a03fe220b37dbfa
|
||||
F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
|
||||
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
|
||||
@@ -1046,7 +1050,7 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4
|
||||
F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5
|
||||
F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
|
||||
F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2
|
||||
F test/fuzzdata8.db 7f6c5443d67ba040f760b4d28da54cc9f68174fa212ae34ccb86c645de761ec4
|
||||
F test/fuzzdata8.db 977cb95f4a5d828056dea804a6de416debe3fa0182c77f47fe19a0554aaf4db0
|
||||
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
|
||||
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
|
||||
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
|
||||
@@ -1056,7 +1060,7 @@ F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
F test/having.test a89236dd8d55aa50c4805f82ac9daf64d477a44d712d8209c118978d0ca21ec9
|
||||
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
|
||||
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
|
||||
F test/hook.test e97382e68e4379838e888756d653afd159f5f14780315ff97b70360d3d8485bc
|
||||
F test/hook.test fa54fa8afc842ae375f10c1f9fc0014fa59789052fc30c9eae19811fa3afa009
|
||||
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
|
||||
F test/icu.test 716a6b89fbabe5cc63e0cd4c260befb08fd7b9d761f04d43669233292f0753b1
|
||||
F test/ieee754.test b0945d12be7d255f3dfa18e2511b17ca37e0edd2b803231c52d05b86c04ab26e
|
||||
@@ -1064,7 +1068,7 @@ F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
|
||||
F test/in.test 688ed2011d922d83141a45af431601738674a4c0bdde34b6351f688b82a169b3
|
||||
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
|
||||
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
|
||||
F test/in4.test 65460600d48933adba4283c6ebd089aae173d16136ab9d01f74c89089090c5a5
|
||||
F test/in4.test 64ac9c767ac5af562f066a40163d4202f8fa3be05d264ec65d6258e74606b30c
|
||||
F test/in5.test b32ce7f4a93f44c5dee94af16886d922cc16ebe33c8e1765c73d4049d0f4b40f
|
||||
F test/in6.test 8562d0945195cab3cc4ab3794e9118e72cb44c43f785c2b04d48a9d06ca6b4ec
|
||||
F test/incrblob.test c9b96afc292aeff43d6687bcb09b0280aa599822
|
||||
@@ -1135,7 +1139,7 @@ F test/kvtest.c feb4358fb022da8ebd098c45811f2f6507688bb6c43aa72b3e840df19026317b
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
F test/lemon-test01.y 58b764610fd934e189ffbb0bbfa33d171b9cb06019b55bdc04d090d6767e11d7
|
||||
F test/like.test 47b81d5de2ff19d996d49a65d50ec9754246aacbe0e950b48d186d9d8171eaf0
|
||||
F test/like.test 0b7b4765ca59d95a1f92dfab9e4d810c9fb8280b5edd6332a01340a20db9e0ed
|
||||
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
|
||||
F test/like3.test 03d1bdf848483b78d2cfd1db283d75c4ec2e37c8b8eccc006813f3978d78fbbd
|
||||
F test/limit.test 0c99a27a87b14c646a9d583c7c89fd06c352663e
|
||||
@@ -1283,9 +1287,10 @@ F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
|
||||
F test/reindex.test cd9d6021729910ece82267b4f5e1b5ac2911a7566c43b43c176a6a4732e2118d
|
||||
F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b0049153 x
|
||||
F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
|
||||
F test/releasetest_data.tcl 756514dabf6d19531a173a07853426543a2f3a6026863c1b5ef00cbcbd86ff30
|
||||
F test/resetdb.test 8062cf10a09d8c048f8de7711e94571c38b38168db0e5877ba7561789e5eeb2b
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/returning1.test 684e1c73d961422a7376c932fcdd6dacf02bad21d12f749cfe8c19991ef379f6
|
||||
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
|
||||
F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
|
||||
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
|
||||
@@ -1293,7 +1298,7 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab
|
||||
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
|
||||
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
|
||||
F test/rowid.test bfbd7b97d9267660be3c8f28507c4ed7f205196b8877c0db42df347c2e8845e3
|
||||
F test/rowvalue.test 8964f95b253d3b5cc8dc1cfd0cdb7529bce3ecc6b6259e23c5f829f80f4d51cd
|
||||
F test/rowvalue.test b5436c767394a3f8fa5e7e474b2114ba430fdab34a5c8573c1b6256756534565
|
||||
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
|
||||
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
|
||||
F test/rowvalue4.test 02e35f7762371c2f57ebd856aa056eac56cb27ef7715a0bb31eac1895a745356
|
||||
@@ -1323,7 +1328,7 @@ F test/schema6.test e4bd1f23d368695eb9e7b51ef6e02ca0642ea2ab4a52579959826b5e7dce
|
||||
F test/schemafault.test 1936bceca55ac82c5efbcc9fc91a1933e45c8d1e1d106b9a7e56c972a5a2a51e
|
||||
F test/securedel.test 2f70b2449186a1921bd01ec9da407fbfa98c3a7a5521854c300c194b2ff09384
|
||||
F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
|
||||
F test/select1.test 0ed936740777f52858b6607f39ffac4b2b63b8fc7edf3ab2ebad3c3553ceecee
|
||||
F test/select1.test 3d23f66bf9ba77570acfe2ca5f1540ece17037cc64ab1a00efec9758ac29c268
|
||||
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
|
||||
F test/select3.test c49fbb758903f3718e2de5aa4655eda4838131cbea24a86db908f8b6889aa68c
|
||||
F test/select4.test e8a2502e3623f3058871030599a48abb35789d2244d5b380ecf3696873fdd4a4
|
||||
@@ -1438,7 +1443,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
|
||||
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
|
||||
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
|
||||
F test/tester.tcl e5e4f5707fbf791ff8e06438fd0d4d71fe4c1d48753b7dd415efe72e853ef877
|
||||
F test/tester.tcl 19d2a19a6dd55a2b4e2b943963959a05a2c088495dd5f5274b04e0494ce86d66
|
||||
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1631,7 +1636,7 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776
|
||||
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
|
||||
F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac
|
||||
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
|
||||
F test/unionall.test ca6f612930c79e30f41f804dff4cce949b61347930847642e333ecd5d79f5964
|
||||
F test/unionall.test 369dac51f4e7b94442b054d3d7f2e6755cd6994274718228878e3bd47c425f6d
|
||||
F test/unionall2.test c9a62db63350bcbce3a7bec50dd8c5410f08be33f8af435473756286d4657215
|
||||
F test/unionallfault.test 652bfbb630e6c43135965dc1e8f0a9a791da83aec885d626a632fe1909c56f73
|
||||
F test/unionvtab.test e1704ab1b4c1bb3ffc9da4681f8e85a0b909fd80b937984fc94b27415ac8e5a4
|
||||
@@ -1751,7 +1756,7 @@ F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
|
||||
F test/whereL.test 1afe47227f093dc0547236491fb37529b7be9724b8575925a321001b80e6a23a
|
||||
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
|
||||
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
|
||||
F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
|
||||
F test/wherelimit.test daa0fd9122c5745cc459ec40b8d3c16ce13ce8382b5b847e7cfff4b871260cbf
|
||||
F test/wherelimit2.test 657a3f24aadee62d058c5091ea682dc4af4b95ffe32f137155be49799a58e721
|
||||
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
|
||||
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
|
||||
@@ -1778,7 +1783,7 @@ F test/windowerr.test a8b752402109c15aa1c5efe1b93ccb0ce1ef84fa964ae1cd6684dd0b3c
|
||||
F test/windowfault.test 72375ae71031eabf96bc88d0af128c8628a091ddc99b5a394e848b3df5fc17ad
|
||||
F test/with1.test 780be387f01e290e768bdfd1827280f9e37ba37223eb4736aba386864fac5a94
|
||||
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
|
||||
F test/with3.test a261f0ea225c4af0ce6447f1157bb603959b2a665f14a03951c2883d2ef1c0f0
|
||||
F test/with3.test 85e059bf4c2ef5626411ee59f399b4bb4b4a0f009bcb7db86f254e570ed11831
|
||||
F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205
|
||||
F test/with5.test 6248213c41fab36290b5b73aa3f937309dfba337004d9d8434c3fabc8c7d4be8
|
||||
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
|
||||
@@ -1826,7 +1831,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
|
||||
F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
|
||||
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
|
||||
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
|
||||
F tool/mkkeywordhash.c 24e4396ae665d985fed9e040e8b748129c1a12d77eeeae7ad4609821c41ba7bf
|
||||
F tool/mkkeywordhash.c 750f25aef0e23f8e3367af6d824fbf5ed7d3e285f27cea91aa2dd72c367630eb
|
||||
F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a895ab33
|
||||
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
|
||||
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
|
||||
@@ -1898,7 +1903,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 6d60cf540b8cc231448175f1e16e1f4f7a0aee26898570a5b8a09c89fae53c02 d5ea75a09d4bf61262cead2604e35d8331b727c504807592d09f5d9e01ce794a
|
||||
R dd365517921749a64a3732b6a8560730
|
||||
U drh
|
||||
Z 57540375938636005cdf9e0125af5478
|
||||
P c5719fc5aa04c50bb01533f1cedb73dc80c4bf5315ff6a7206c8a10504afca8b
|
||||
R a9f6b9c36a39e6908ea00fd7298f0619
|
||||
U dan
|
||||
Z 77e2ec90698c4f83ecd6e0d769d5b14d
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
0b42f9eb5dd710991af3cf4b16464b22db9539361d618e50eaa4d43c811577b4
|
||||
126ee1ec4f3565c0cccca98885fa3665a641ea3df251511de16eed2a1c396124
|
||||
+251
-39
@@ -49,15 +49,21 @@ static int isAlterableTable(Parse *pParse, Table *pTab){
|
||||
** statement to ensure that the operation has not rendered any schema
|
||||
** objects unusable.
|
||||
*/
|
||||
static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
static void renameTestSchema(
|
||||
Parse *pParse, /* Parse context */
|
||||
const char *zDb, /* Name of db to verify schema of */
|
||||
int bTemp, /* True if this is the temp db */
|
||||
const char *zWhen /* "when" part of error message */
|
||||
){
|
||||
pParse->colNamesSet = 1;
|
||||
sqlite3NestedParse(pParse,
|
||||
"SELECT 1 "
|
||||
"FROM \"%w\"." DFLT_SCHEMA_TABLE " "
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, %d)=NULL ",
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, %d, %Q)=NULL ",
|
||||
zDb,
|
||||
zDb, bTemp
|
||||
zDb, bTemp, zWhen
|
||||
);
|
||||
|
||||
if( bTemp==0 ){
|
||||
@@ -66,8 +72,8 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
"FROM temp." DFLT_SCHEMA_TABLE " "
|
||||
"WHERE name NOT LIKE 'sqliteX_%%' ESCAPE 'X'"
|
||||
" AND sql NOT LIKE 'create virtual%%'"
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, 1)=NULL ",
|
||||
zDb
|
||||
" AND sqlite_rename_test(%Q, sql, type, name, 1, %Q)=NULL ",
|
||||
zDb, zWhen
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -76,12 +82,12 @@ static void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){
|
||||
** Generate code to reload the schema for database iDb. And, if iDb!=1, for
|
||||
** the temp database as well.
|
||||
*/
|
||||
static void renameReloadSchema(Parse *pParse, int iDb){
|
||||
static void renameReloadSchema(Parse *pParse, int iDb, u16 p5){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
if( v ){
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
|
||||
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
|
||||
sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0, p5);
|
||||
if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0, p5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +236,7 @@ void sqlite3AlterRenameTable(
|
||||
"sql = sqlite_rename_table(%Q, type, name, sql, %Q, %Q, 1), "
|
||||
"tbl_name = "
|
||||
"CASE WHEN tbl_name=%Q COLLATE nocase AND "
|
||||
" sqlite_rename_test(%Q, sql, type, name, 1) "
|
||||
" sqlite_rename_test(%Q, sql, type, name, 1, 'after rename') "
|
||||
"THEN %Q ELSE tbl_name END "
|
||||
"WHERE type IN ('view', 'trigger')"
|
||||
, zDb, zTabName, zName, zTabName, zDb, zName);
|
||||
@@ -249,8 +255,8 @@ void sqlite3AlterRenameTable(
|
||||
}
|
||||
#endif
|
||||
|
||||
renameReloadSchema(pParse, iDb);
|
||||
renameTestSchema(pParse, zDb, iDb==1);
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
|
||||
renameTestSchema(pParse, zDb, iDb==1, "after rename");
|
||||
|
||||
exit_rename_table:
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
@@ -412,7 +418,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
|
||||
}
|
||||
|
||||
/* Reload the table definition */
|
||||
renameReloadSchema(pParse, iDb);
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -512,7 +518,7 @@ exit_begin_add_column:
|
||||
** Or, if pTab is not a view or virtual table, zero is returned.
|
||||
*/
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
|
||||
static int isRealTable(Parse *pParse, Table *pTab){
|
||||
static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
|
||||
const char *zType = 0;
|
||||
#ifndef SQLITE_OMIT_VIEW
|
||||
if( pTab->pSelect ){
|
||||
@@ -525,15 +531,16 @@ static int isRealTable(Parse *pParse, Table *pTab){
|
||||
}
|
||||
#endif
|
||||
if( zType ){
|
||||
sqlite3ErrorMsg(
|
||||
pParse, "cannot rename columns of %s \"%s\"", zType, pTab->zName
|
||||
sqlite3ErrorMsg(pParse, "cannot %s %s \"%s\"",
|
||||
(bDrop ? "drop column from" : "rename columns of"),
|
||||
zType, pTab->zName
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else /* !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) */
|
||||
# define isRealTable(x,y) (0)
|
||||
# define isRealTable(x,y,z) (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -562,7 +569,7 @@ void sqlite3AlterRenameColumn(
|
||||
|
||||
/* Cannot alter a system table */
|
||||
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column;
|
||||
if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column;
|
||||
if( SQLITE_OK!=isRealTable(pParse, pTab, 0) ) goto exit_rename_column;
|
||||
|
||||
/* Which schema holds the table to be altered */
|
||||
iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
@@ -616,8 +623,8 @@ void sqlite3AlterRenameColumn(
|
||||
);
|
||||
|
||||
/* Drop and reload the database schema. */
|
||||
renameReloadSchema(pParse, iSchema);
|
||||
renameTestSchema(pParse, zDb, iSchema==1);
|
||||
renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
|
||||
renameTestSchema(pParse, zDb, iSchema==1, "after rename");
|
||||
|
||||
exit_rename_column:
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
@@ -869,23 +876,33 @@ static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
|
||||
|
||||
/*
|
||||
** Search the Parse object passed as the first argument for a RenameToken
|
||||
** object associated with parse tree element pPtr. If found, remove it
|
||||
** from the Parse object and add it to the list maintained by the
|
||||
** RenameCtx object passed as the second argument.
|
||||
** object associated with parse tree element pPtr. If found, return a pointer
|
||||
** to it. Otherwise, return NULL.
|
||||
**
|
||||
** If the second argument passed to this function is not NULL and a matching
|
||||
** RenameToken object is found, remove it from the Parse object and add it to
|
||||
** the list maintained by the RenameCtx object.
|
||||
*/
|
||||
static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){
|
||||
static RenameToken *renameTokenFind(
|
||||
Parse *pParse,
|
||||
struct RenameCtx *pCtx,
|
||||
void *pPtr
|
||||
){
|
||||
RenameToken **pp;
|
||||
assert( pPtr!=0 );
|
||||
for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
|
||||
if( (*pp)->p==pPtr ){
|
||||
RenameToken *pToken = *pp;
|
||||
*pp = pToken->pNext;
|
||||
pToken->pNext = pCtx->pList;
|
||||
pCtx->pList = pToken;
|
||||
pCtx->nList++;
|
||||
break;
|
||||
if( pCtx ){
|
||||
*pp = pToken->pNext;
|
||||
pToken->pNext = pCtx->pList;
|
||||
pCtx->pList = pToken;
|
||||
pCtx->nList++;
|
||||
}
|
||||
return pToken;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -956,7 +973,7 @@ static RenameToken *renameColumnTokenNext(RenameCtx *pCtx){
|
||||
*/
|
||||
static void renameColumnParseError(
|
||||
sqlite3_context *pCtx,
|
||||
int bPost,
|
||||
const char *zWhen,
|
||||
sqlite3_value *pType,
|
||||
sqlite3_value *pObject,
|
||||
Parse *pParse
|
||||
@@ -965,8 +982,8 @@ static void renameColumnParseError(
|
||||
const char *zN = (const char*)sqlite3_value_text(pObject);
|
||||
char *zErr;
|
||||
|
||||
zErr = sqlite3_mprintf("error in %s %s%s: %s",
|
||||
zT, zN, (bPost ? " after rename" : ""),
|
||||
zErr = sqlite3_mprintf("error in %s %s%s%s: %s",
|
||||
zT, zN, (zWhen[0] ? " " : ""), zWhen,
|
||||
pParse->zErrMsg
|
||||
);
|
||||
sqlite3_result_error(pCtx, zErr, -1);
|
||||
@@ -1045,7 +1062,7 @@ static int renameParseSql(
|
||||
p->eParseMode = PARSE_MODE_RENAME;
|
||||
p->db = db;
|
||||
p->nQueryLoop = 1;
|
||||
rc = sqlite3RunParser(p, zSql, &zErr);
|
||||
rc = zSql ? sqlite3RunParser(p, zSql, &zErr) : SQLITE_NOMEM;
|
||||
assert( p->zErrMsg==0 );
|
||||
assert( rc!=SQLITE_OK || zErr==0 );
|
||||
p->zErrMsg = zErr;
|
||||
@@ -1472,7 +1489,7 @@ static void renameColumnFunc(
|
||||
renameColumnFunc_done:
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( sParse.zErrMsg ){
|
||||
renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
|
||||
renameColumnParseError(context, "", argv[1], argv[2], &sParse);
|
||||
}else{
|
||||
sqlite3_result_error_code(context, rc);
|
||||
}
|
||||
@@ -1661,7 +1678,7 @@ static void renameTableFunc(
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( sParse.zErrMsg ){
|
||||
renameColumnParseError(context, 0, argv[1], argv[2], &sParse);
|
||||
renameColumnParseError(context, "", argv[1], argv[2], &sParse);
|
||||
}else{
|
||||
sqlite3_result_error_code(context, rc);
|
||||
}
|
||||
@@ -1690,6 +1707,7 @@ static void renameTableFunc(
|
||||
** 2: Object type ("view", "table", "trigger" or "index").
|
||||
** 3: Object name.
|
||||
** 4: True if object is from temp schema.
|
||||
** 5: "when" part of error message.
|
||||
**
|
||||
** Unless it finds an error, this function normally returns NULL. However, it
|
||||
** returns integer value 1 if:
|
||||
@@ -1707,6 +1725,7 @@ static void renameTableTest(
|
||||
char const *zInput = (const char*)sqlite3_value_text(argv[1]);
|
||||
int bTemp = sqlite3_value_int(argv[4]);
|
||||
int isLegacy = (db->flags & SQLITE_LegacyAlter);
|
||||
char const *zWhen = (const char*)sqlite3_value_text(argv[5]);
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3_xauth xAuth = db->xAuth;
|
||||
@@ -1739,8 +1758,8 @@ static void renameTableTest(
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
|
||||
if( rc!=SQLITE_OK && zWhen ){
|
||||
renameColumnParseError(context, zWhen, argv[2], argv[3],&sParse);
|
||||
}
|
||||
renameParseCleanup(&sParse);
|
||||
}
|
||||
@@ -1750,14 +1769,207 @@ static void renameTableTest(
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** The implementation of internal UDF sqlite_drop_column().
|
||||
**
|
||||
** Arguments:
|
||||
**
|
||||
** argv[0]: An integer - the index of the schema containing the table
|
||||
** argv[1]: CREATE TABLE statement to modify.
|
||||
** argv[2]: An integer - the index of the column to remove.
|
||||
** argv[3]: Byte offset of first byte after last column definition in argv[1]
|
||||
**
|
||||
** The value returned is a string containing the CREATE TABLE statement
|
||||
** with column argv[2] removed.
|
||||
*/
|
||||
static void dropColumnFunc(
|
||||
sqlite3_context *context,
|
||||
int NotUsed,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
int iSchema = sqlite3_value_int(argv[0]);
|
||||
const char *zSql = (const char*)sqlite3_value_text(argv[1]);
|
||||
int iCol = sqlite3_value_int(argv[2]);
|
||||
int iAddColOffset = sqlite3_value_int(argv[3]);
|
||||
const char *zDb = db->aDb[iSchema].zDbSName;
|
||||
int rc;
|
||||
Parse sParse;
|
||||
RenameToken *pCol;
|
||||
Table *pTab;
|
||||
const char *zEnd;
|
||||
char *zNew = 0;
|
||||
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
sqlite3_xauth xAuth = db->xAuth;
|
||||
db->xAuth = 0;
|
||||
#endif
|
||||
|
||||
rc = renameParseSql(&sParse, zDb, db, zSql, iSchema==1);
|
||||
if( rc!=SQLITE_OK ) goto drop_column_done;
|
||||
pTab = sParse.pNewTable;
|
||||
if( iCol>=pTab->nCol ){
|
||||
/* This can happen if the sqlite_schema table is corrupt */
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
goto drop_column_done;
|
||||
}
|
||||
|
||||
pCol = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol].zName);
|
||||
if( iCol<pTab->nCol-1 ){
|
||||
RenameToken *pEnd;
|
||||
pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zName);
|
||||
zEnd = (const char*)pEnd->t.z;
|
||||
}else{
|
||||
zEnd = (const char*)&zSql[iAddColOffset];
|
||||
while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
|
||||
}
|
||||
|
||||
zNew = sqlite3MPrintf(db, "%.*s%s", pCol->t.z-zSql, zSql, zEnd);
|
||||
sqlite3_result_text(context, zNew, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_free(zNew);
|
||||
|
||||
drop_column_done:
|
||||
renameParseCleanup(&sParse);
|
||||
#ifndef SQLITE_OMIT_AUTHORIZATION
|
||||
db->xAuth = xAuth;
|
||||
#endif
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_result_error_code(context, rc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called by the parser upon parsing an
|
||||
**
|
||||
** ALTER TABLE pSrc DROP COLUMN pName
|
||||
**
|
||||
** statement. Argument pSrc contains the possibly qualified name of the
|
||||
** table being edited, and token pName the name of the column to drop.
|
||||
*/
|
||||
void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, Token *pName){
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
Table *pTab; /* Table to modify */
|
||||
int iDb; /* Index of db containing pTab in aDb[] */
|
||||
const char *zDb; /* Database containing pTab ("main" etc.) */
|
||||
char *zCol = 0; /* Name of column to drop */
|
||||
int iCol; /* Index of column zCol in pTab->aCol[] */
|
||||
|
||||
/* Look up the table being altered. */
|
||||
assert( pParse->pNewTable==0 );
|
||||
assert( sqlite3BtreeHoldsAllMutexes(db) );
|
||||
if( NEVER(db->mallocFailed) ) goto exit_drop_column;
|
||||
pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
|
||||
if( !pTab ) goto exit_drop_column;
|
||||
|
||||
/* Make sure this is not an attempt to ALTER a view, virtual table or
|
||||
** system table. */
|
||||
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_drop_column;
|
||||
if( SQLITE_OK!=isRealTable(pParse, pTab, 1) ) goto exit_drop_column;
|
||||
|
||||
/* Find the index of the column being dropped. */
|
||||
zCol = sqlite3NameFromToken(db, pName);
|
||||
if( zCol==0 ){
|
||||
assert( db->mallocFailed );
|
||||
goto exit_drop_column;
|
||||
}
|
||||
iCol = sqlite3ColumnIndex(pTab, zCol);
|
||||
if( iCol<0 ){
|
||||
sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zCol);
|
||||
goto exit_drop_column;
|
||||
}
|
||||
|
||||
/* Do not allow the user to drop a PRIMARY KEY column or a column
|
||||
** constrained by a UNIQUE constraint. */
|
||||
if( pTab->aCol[iCol].colFlags & (COLFLAG_PRIMKEY|COLFLAG_UNIQUE) ){
|
||||
sqlite3ErrorMsg(pParse, "cannot drop %s column: \"%s\"",
|
||||
(pTab->aCol[iCol].colFlags&COLFLAG_PRIMKEY) ? "PRIMARY KEY" : "UNIQUE",
|
||||
zCol
|
||||
);
|
||||
goto exit_drop_column;
|
||||
}
|
||||
|
||||
/* Do not allow the number of columns to go to zero */
|
||||
if( pTab->nCol<=1 ){
|
||||
sqlite3ErrorMsg(pParse, "cannot drop column \"%s\": no other columns exist",zCol);
|
||||
goto exit_drop_column;
|
||||
}
|
||||
|
||||
/* Edit the sqlite_schema table */
|
||||
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
assert( iDb>=0 );
|
||||
zDb = db->aDb[iDb].zDbSName;
|
||||
renameTestSchema(pParse, zDb, iDb==1, "");
|
||||
sqlite3NestedParse(pParse,
|
||||
"UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
|
||||
"sql = sqlite_drop_column(%d, sql, %d, %d) "
|
||||
"WHERE (type=='table' AND tbl_name=%Q COLLATE nocase)"
|
||||
, zDb, iDb, iCol, pTab->addColOffset, pTab->zName
|
||||
);
|
||||
|
||||
/* Drop and reload the database schema. */
|
||||
renameReloadSchema(pParse, iDb, INITFLAG_AlterDrop);
|
||||
renameTestSchema(pParse, zDb, iDb==1, "after drop column");
|
||||
|
||||
/* Edit rows of table on disk */
|
||||
if( pParse->nErr==0 && (pTab->aCol[iCol].colFlags & COLFLAG_VIRTUAL)==0 ){
|
||||
int i;
|
||||
int addr;
|
||||
int reg;
|
||||
int regRec;
|
||||
Index *pPk = 0;
|
||||
int nField = 0; /* Number of non-virtual columns after drop */
|
||||
int iCur;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
iCur = pParse->nTab++;
|
||||
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
|
||||
addr = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
|
||||
reg = ++pParse->nMem;
|
||||
pParse->nMem += pTab->nCol;
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, reg);
|
||||
}else{
|
||||
pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
}
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( i!=iCol && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ){
|
||||
int regOut;
|
||||
if( pPk ){
|
||||
int iPos = sqlite3TableColumnToIndex(pPk, i);
|
||||
int iColPos = sqlite3TableColumnToIndex(pPk, iCol);
|
||||
regOut = reg+1+iPos-(iPos>iColPos);
|
||||
}else{
|
||||
regOut = reg+1+nField;
|
||||
}
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
|
||||
nField++;
|
||||
}
|
||||
}
|
||||
regRec = reg + pTab->nCol;
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg+1, nField, regRec);
|
||||
if( pPk ){
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iCur, regRec, reg+1, pPk->nKeyCol);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iCur, regRec, reg);
|
||||
}
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
|
||||
exit_drop_column:
|
||||
sqlite3DbFree(db, zCol);
|
||||
sqlite3SrcListDelete(db, pSrc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Register built-in functions used to help implement ALTER TABLE
|
||||
*/
|
||||
void sqlite3AlterFunctions(void){
|
||||
static FuncDef aAlterTableFuncs[] = {
|
||||
INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
|
||||
INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
|
||||
INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest),
|
||||
INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc),
|
||||
INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc),
|
||||
INTERNAL_FUNCTION(sqlite_rename_test, 6, renameTableTest),
|
||||
INTERNAL_FUNCTION(sqlite_drop_column, 4, dropColumnFunc),
|
||||
};
|
||||
sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
|
||||
}
|
||||
|
||||
@@ -480,7 +480,6 @@ static int fixSelectCb(Walker *p, Select *pSelect){
|
||||
#endif
|
||||
}
|
||||
if( pSelect->pWith ){
|
||||
int i;
|
||||
for(i=0; i<pSelect->pWith->nCte; i++){
|
||||
if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){
|
||||
return WRC_Abort;
|
||||
|
||||
+5
-6
@@ -143,7 +143,6 @@ void sqlite3AuthRead(
|
||||
Schema *pSchema, /* The schema of the expression */
|
||||
SrcList *pTabList /* All table that pExpr might refer to */
|
||||
){
|
||||
sqlite3 *db = pParse->db;
|
||||
Table *pTab = 0; /* The table being read */
|
||||
const char *zCol; /* Name of the column of the table */
|
||||
int iSrc; /* Index in pTabList->a[] of table being read */
|
||||
@@ -151,8 +150,8 @@ void sqlite3AuthRead(
|
||||
int iCol; /* Index of column in table */
|
||||
|
||||
assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
|
||||
assert( !IN_RENAME_OBJECT || db->xAuth==0 );
|
||||
if( db->xAuth==0 ) return;
|
||||
assert( !IN_RENAME_OBJECT );
|
||||
assert( pParse->db->xAuth!=0 );
|
||||
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
|
||||
if( iDb<0 ){
|
||||
/* An attempt to read a column out of a subquery or other
|
||||
@@ -164,7 +163,7 @@ void sqlite3AuthRead(
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else{
|
||||
assert( pTabList );
|
||||
for(iSrc=0; ALWAYS(iSrc<pTabList->nSrc); iSrc++){
|
||||
for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
|
||||
if( pExpr->iTable==pTabList->a[iSrc].iCursor ){
|
||||
pTab = pTabList->a[iSrc].pTab;
|
||||
break;
|
||||
@@ -172,7 +171,7 @@ void sqlite3AuthRead(
|
||||
}
|
||||
}
|
||||
iCol = pExpr->iColumn;
|
||||
if( NEVER(pTab==0) ) return;
|
||||
if( pTab==0 ) return;
|
||||
|
||||
if( iCol>=0 ){
|
||||
assert( iCol<pTab->nCol );
|
||||
@@ -183,7 +182,7 @@ void sqlite3AuthRead(
|
||||
}else{
|
||||
zCol = "ROWID";
|
||||
}
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
assert( iDb>=0 && iDb<pParse->db->nDb );
|
||||
if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){
|
||||
pExpr->op = TK_NULL;
|
||||
}
|
||||
|
||||
+4
-1
@@ -7975,6 +7975,9 @@ static int balance_nonroot(
|
||||
apOld[i] = 0;
|
||||
rc = sqlite3PagerWrite(pNew->pDbPage);
|
||||
nNew++;
|
||||
if( sqlite3PagerPageRefcount(pNew->pDbPage)!=1+(i==(iParentIdx-nxDiv)) ){
|
||||
rc = SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( rc ) goto balance_cleanup;
|
||||
}else{
|
||||
assert( i>0 );
|
||||
@@ -8011,7 +8014,7 @@ static int balance_nonroot(
|
||||
aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
|
||||
aPgFlags[i] = apNew[i]->pDbPage->flags;
|
||||
for(j=0; j<i; j++){
|
||||
if( aPgno[j]==aPgno[i] ){
|
||||
if( NEVER(aPgno[j]==aPgno[i]) ){
|
||||
/* This branch is taken if the set of sibling pages somehow contains
|
||||
** duplicate entries. This can happen if the database is corrupt.
|
||||
** It would be simpler to detect this as part of the loop below, but
|
||||
|
||||
+110
-10
@@ -143,14 +143,36 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
/* Begin by generating some termination code at the end of the
|
||||
** vdbe program
|
||||
*/
|
||||
if( pParse->pVdbe==0 && db->init.busy ){
|
||||
pParse->rc = SQLITE_DONE;
|
||||
return;
|
||||
v = pParse->pVdbe;
|
||||
if( v==0 ){
|
||||
if( db->init.busy ){
|
||||
pParse->rc = SQLITE_DONE;
|
||||
return;
|
||||
}
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) pParse->rc = SQLITE_ERROR;
|
||||
}
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( !pParse->isMultiWrite
|
||||
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
|
||||
if( v ){
|
||||
if( pParse->bReturning ){
|
||||
Returning *pReturning = pParse->u1.pReturning;
|
||||
int addrRewind;
|
||||
int i;
|
||||
int reg;
|
||||
|
||||
addrRewind =
|
||||
sqlite3VdbeAddOp1(v, OP_Rewind, pReturning->iRetCur);
|
||||
VdbeCoverage(v);
|
||||
reg = pReturning->iRetReg;
|
||||
for(i=0; i<pReturning->nRetCol; i++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pReturning->iRetCur, i, reg+i);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, reg, i);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeJumpHere(v, addrRewind);
|
||||
}
|
||||
sqlite3VdbeAddOp0(v, OP_Halt);
|
||||
|
||||
#if SQLITE_USER_AUTHENTICATION
|
||||
@@ -228,12 +250,16 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
}
|
||||
}
|
||||
|
||||
if( pParse->bReturning ){
|
||||
Returning *pRet = pParse->u1.pReturning;
|
||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
|
||||
}
|
||||
|
||||
/* Finally, jump back to the beginning of the executable code. */
|
||||
sqlite3VdbeGoto(v, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Get the VDBE program ready for execution
|
||||
*/
|
||||
if( v && pParse->nErr==0 && !db->mallocFailed ){
|
||||
@@ -1210,7 +1236,8 @@ void sqlite3StartTable(
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
pParse->addrCrTab =
|
||||
assert( !pParse->bReturning );
|
||||
pParse->u1.addrCrTab =
|
||||
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
|
||||
}
|
||||
sqlite3OpenSchemaTable(pParse, iDb);
|
||||
@@ -1237,12 +1264,84 @@ begin_table_error:
|
||||
void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
|
||||
if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
|
||||
pCol->colFlags |= COLFLAG_HIDDEN;
|
||||
if( pTab ) pTab->tabFlags |= TF_HasHidden;
|
||||
}else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
|
||||
pTab->tabFlags |= TF_OOOHidden;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Name of the special TEMP trigger used to implement RETURNING. The
|
||||
** name begins with "sqlite_" so that it is guaranteed not to collide
|
||||
** with any application-generated triggers.
|
||||
*/
|
||||
#define RETURNING_TRIGGER_NAME "sqlite_returning"
|
||||
|
||||
/*
|
||||
** Clean up the data structures associated with the RETURNING clause.
|
||||
*/
|
||||
static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
|
||||
Hash *pHash;
|
||||
pHash = &(db->aDb[1].pSchema->trigHash);
|
||||
sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
|
||||
sqlite3ExprListDelete(db, pRet->pReturnEL);
|
||||
sqlite3DbFree(db, pRet);
|
||||
}
|
||||
|
||||
/*
|
||||
** Add the RETURNING clause to the parse currently underway.
|
||||
**
|
||||
** This routine creates a special TEMP trigger that will fire for each row
|
||||
** of the DML statement. That TEMP trigger contains a single SELECT
|
||||
** statement with a result set that is the argument of the RETURNING clause.
|
||||
** The trigger has the Trigger.bReturning flag and an opcode of
|
||||
** TK_RETURNING instead of TK_SELECT, so that the trigger code generator
|
||||
** knows to handle it specially. The TEMP trigger is automatically
|
||||
** removed at the end of the parse.
|
||||
**
|
||||
** When this routine is called, we do not yet know if the RETURNING clause
|
||||
** is attached to a DELETE, INSERT, or UPDATE, so construct it as a
|
||||
** RETURNING trigger instead. It will then be converted into the appropriate
|
||||
** type on the first call to sqlite3TriggersExist().
|
||||
*/
|
||||
void sqlite3AddReturning(Parse *pParse, ExprList *pList){
|
||||
Returning *pRet;
|
||||
Hash *pHash;
|
||||
sqlite3 *db = pParse->db;
|
||||
if( pParse->pNewTrigger ){
|
||||
sqlite3ErrorMsg(pParse, "cannot use RETURNING in a trigger");
|
||||
}else{
|
||||
assert( pParse->bReturning==0 );
|
||||
}
|
||||
pParse->bReturning = 1;
|
||||
pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
|
||||
if( pRet==0 ){
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
return;
|
||||
}
|
||||
pParse->u1.pReturning = pRet;
|
||||
pRet->pParse = pParse;
|
||||
pRet->pReturnEL = pList;
|
||||
sqlite3ParserAddCleanup(pParse,
|
||||
(void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
|
||||
if( db->mallocFailed ) return;
|
||||
pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
|
||||
pRet->retTrig.op = TK_RETURNING;
|
||||
pRet->retTrig.tr_tm = TRIGGER_AFTER;
|
||||
pRet->retTrig.bReturning = 1;
|
||||
pRet->retTrig.pSchema = db->aDb[1].pSchema;
|
||||
pRet->retTrig.step_list = &pRet->retTStep;
|
||||
pRet->retTStep.op = TK_RETURNING;
|
||||
pRet->retTStep.pTrig = &pRet->retTrig;
|
||||
pRet->retTStep.pExprList = pList;
|
||||
pHash = &(db->aDb[1].pSchema->trigHash);
|
||||
assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 || pParse->nErr );
|
||||
if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
|
||||
==&pRet->retTrig ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new column to the table currently being constructed.
|
||||
@@ -2075,9 +2174,10 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
|
||||
/* Convert the P3 operand of the OP_CreateBtree opcode from BTREE_INTKEY
|
||||
** into BTREE_BLOBKEY.
|
||||
*/
|
||||
if( pParse->addrCrTab ){
|
||||
assert( !pParse->bReturning );
|
||||
if( pParse->u1.addrCrTab ){
|
||||
assert( v );
|
||||
sqlite3VdbeChangeP3(v, pParse->addrCrTab, BTREE_BLOBKEY);
|
||||
sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY);
|
||||
}
|
||||
|
||||
/* Locate the PRIMARY KEY index. Or, if this table was originally
|
||||
@@ -2541,7 +2641,7 @@ void sqlite3EndTable(
|
||||
|
||||
/* Reparse everything to update our internal data structures */
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
|
||||
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
|
||||
}
|
||||
|
||||
/* Add the table to the in-memory representation of the database.
|
||||
@@ -4029,7 +4129,7 @@ void sqlite3CreateIndex(
|
||||
sqlite3RefillIndex(pParse, pIndex, iMem);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
|
||||
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -387,6 +387,7 @@ void sqlite3DeleteFrom(
|
||||
if( (db->flags & SQLITE_CountRows)!=0
|
||||
&& !pParse->nested
|
||||
&& !pParse->pTriggerTab
|
||||
&& !pParse->bReturning
|
||||
){
|
||||
memCnt = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
|
||||
@@ -608,7 +609,7 @@ void sqlite3DeleteFrom(
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( memCnt ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_ChngCntRow, memCnt, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
|
||||
}
|
||||
|
||||
+11
-8
@@ -1112,7 +1112,9 @@ u32 sqlite3FkOldmask(
|
||||
**
|
||||
** For an UPDATE, this function returns 2 if:
|
||||
**
|
||||
** * There are any FKs for which pTab is the child and the parent table, or
|
||||
** * There are any FKs for which pTab is the child and the parent table
|
||||
** and any FK processing at all is required (even of a different FK), or
|
||||
**
|
||||
** * the UPDATE modifies one or more parent keys for which the action is
|
||||
** not "NO ACTION" (i.e. is CASCADE, SET DEFAULT or SET NULL).
|
||||
**
|
||||
@@ -1124,13 +1126,14 @@ int sqlite3FkRequired(
|
||||
int *aChange, /* Non-NULL for UPDATE operations */
|
||||
int chngRowid /* True for UPDATE that affects rowid */
|
||||
){
|
||||
int eRet = 0;
|
||||
int eRet = 1; /* Value to return if bHaveFK is true */
|
||||
int bHaveFK = 0; /* If FK processing is required */
|
||||
if( pParse->db->flags&SQLITE_ForeignKeys ){
|
||||
if( !aChange ){
|
||||
/* A DELETE operation. Foreign key processing is required if the
|
||||
** table in question is either the child or parent table for any
|
||||
** foreign key constraint. */
|
||||
eRet = (sqlite3FkReferences(pTab) || pTab->pFKey);
|
||||
bHaveFK = (sqlite3FkReferences(pTab) || pTab->pFKey);
|
||||
}else{
|
||||
/* This is an UPDATE. Foreign key processing is only required if the
|
||||
** operation modifies one or more child or parent key columns. */
|
||||
@@ -1138,9 +1141,9 @@ int sqlite3FkRequired(
|
||||
|
||||
/* Check if any child key columns are being modified. */
|
||||
for(p=pTab->pFKey; p; p=p->pNextFrom){
|
||||
if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) return 2;
|
||||
if( fkChildIsModified(pTab, p, aChange, chngRowid) ){
|
||||
eRet = 1;
|
||||
if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2;
|
||||
bHaveFK = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,12 +1151,12 @@ int sqlite3FkRequired(
|
||||
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
|
||||
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
|
||||
if( p->aAction[1]!=OE_None ) return 2;
|
||||
eRet = 1;
|
||||
bHaveFK = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return eRet;
|
||||
return bHaveFK ? eRet : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1352,7 +1355,7 @@ static Trigger *fkActionTrigger(
|
||||
|
||||
switch( action ){
|
||||
case OE_Restrict:
|
||||
pStep->op = TK_SELECT;
|
||||
pStep->op = TK_SELECT;
|
||||
break;
|
||||
case OE_Cascade:
|
||||
if( !pChanges ){
|
||||
|
||||
+7
-5
@@ -694,7 +694,8 @@ static int patternCompare(
|
||||
/* Skip over multiple "*" characters in the pattern. If there
|
||||
** are also "?" characters, skip those as well, but consume a
|
||||
** single character of the input string for each "?" skipped */
|
||||
while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
|
||||
while( (c=Utf8Read(zPattern)) == matchAll
|
||||
|| (c == matchOne && matchOne!=0) ){
|
||||
if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
|
||||
return SQLITE_NOWILDCARDMATCH;
|
||||
}
|
||||
@@ -1980,7 +1981,7 @@ static void logFunc(
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT:
|
||||
x = sqlite3_value_double(argv[0]);
|
||||
if( x<0.0 ) return;
|
||||
if( x<=0.0 ) return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -1989,14 +1990,15 @@ static void logFunc(
|
||||
switch( sqlite3_value_numeric_type(argv[0]) ){
|
||||
case SQLITE_INTEGER:
|
||||
case SQLITE_FLOAT:
|
||||
b = x;
|
||||
b = log(x);
|
||||
if( b<=0.0 ) return;
|
||||
x = sqlite3_value_double(argv[1]);
|
||||
if( x<0.0 ) return;
|
||||
if( x<=0.0 ) return;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
ans = log(x)/log(b);
|
||||
ans = log(x)/b;
|
||||
}else{
|
||||
ans = log(x);
|
||||
switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
|
||||
|
||||
+57
-30
@@ -930,19 +930,24 @@ void sqlite3Insert(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
|
||||
}
|
||||
if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"table %S has %d columns but %d values were supplied",
|
||||
pTabList, 0, pTab->nCol-nHidden, nColumn);
|
||||
goto insert_cleanup;
|
||||
/* Make sure the number of columns in the source data matches the number
|
||||
** of columns to be inserted into the table.
|
||||
*/
|
||||
assert( TF_HasHidden==COLFLAG_HIDDEN );
|
||||
assert( TF_HasGenerated==COLFLAG_GENERATED );
|
||||
assert( COLFLAG_NOINSERT==(COLFLAG_GENERATED|COLFLAG_HIDDEN) );
|
||||
if( (pTab->tabFlags & (TF_HasGenerated|TF_HasHidden))!=0 ){
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
if( pTab->aCol[i].colFlags & COLFLAG_NOINSERT ) nHidden++;
|
||||
}
|
||||
}
|
||||
if( nColumn!=(pTab->nCol-nHidden) ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"table %S has %d columns but %d values were supplied",
|
||||
pTabList, 0, pTab->nCol-nHidden, nColumn);
|
||||
goto insert_cleanup;
|
||||
}
|
||||
}
|
||||
if( pColumn!=0 && nColumn!=pColumn->nId ){
|
||||
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
|
||||
@@ -954,6 +959,7 @@ void sqlite3Insert(
|
||||
if( (db->flags & SQLITE_CountRows)!=0
|
||||
&& !pParse->nested
|
||||
&& !pParse->pTriggerTab
|
||||
&& !pParse->bReturning
|
||||
){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
@@ -1142,11 +1148,6 @@ void sqlite3Insert(
|
||||
sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
|
||||
}
|
||||
|
||||
/* Cannot have triggers on a virtual table. If it were possible,
|
||||
** this block would have to account for hidden column.
|
||||
*/
|
||||
assert( !IsVirtual(pTab) );
|
||||
|
||||
/* Copy the new data already generated. */
|
||||
assert( pTab->nNVCol>0 );
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regRowid+1, regCols+1, pTab->nNVCol-1);
|
||||
@@ -1318,7 +1319,7 @@ insert_end:
|
||||
** invoke the callback function.
|
||||
*/
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
|
||||
}
|
||||
@@ -2409,6 +2410,32 @@ void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Table pTab is a WITHOUT ROWID table that is being written to. The cursor
|
||||
** number is iCur, and register regData contains the new record for the
|
||||
** PK index. This function adds code to invoke the pre-update hook,
|
||||
** if one is registered.
|
||||
*/
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
static void codeWithoutRowidPreupdate(
|
||||
Parse *pParse, /* Parse context */
|
||||
Table *pTab, /* Table being updated */
|
||||
int iCur, /* Cursor number for table */
|
||||
int regData /* Data containing new record */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int r = sqlite3GetTempReg(pParse);
|
||||
assert( !HasRowid(pTab) );
|
||||
assert( 0==(pParse->db->mDbFlags & DBFLAG_Vacuum) );
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iCur, regData, r, (char*)pTab, P4_TABLE);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
|
||||
sqlite3ReleaseTempReg(pParse, r);
|
||||
}
|
||||
#else
|
||||
# define codeWithoutRowidPreupdate(a,b,c,d)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine generates code to finish the INSERT or UPDATE operation
|
||||
** that was started by a prior call to sqlite3GenerateConstraintChecks.
|
||||
@@ -2457,17 +2484,9 @@ void sqlite3CompleteInsertion(
|
||||
assert( pParse->nested==0 );
|
||||
pik_flags |= OPFLAG_NCHANGE;
|
||||
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( update_flags==0 ){
|
||||
int r = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, r);
|
||||
sqlite3VdbeAddOp4(v, OP_Insert,
|
||||
iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE
|
||||
);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
|
||||
sqlite3ReleaseTempReg(pParse, r);
|
||||
codeWithoutRowidPreupdate(pParse, pTab, iIdxCur+i, aRegIdx[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
|
||||
aRegIdx[i]+1,
|
||||
@@ -2940,7 +2959,7 @@ static int xferOptimization(
|
||||
insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( db->xPreUpdateCallback ){
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
insFlags &= ~OPFLAG_PREFORMAT;
|
||||
}else
|
||||
@@ -2948,8 +2967,10 @@ static int xferOptimization(
|
||||
{
|
||||
sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
|
||||
(char*)pDest, P4_TABLE);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pDest, P4_TABLE);
|
||||
}
|
||||
sqlite3VdbeChangeP5(v, insFlags);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
|
||||
@@ -3002,6 +3023,12 @@ static int xferOptimization(
|
||||
}
|
||||
if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
|
||||
sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
|
||||
if( (db->mDbFlags & DBFLAG_Vacuum)==0
|
||||
&& !HasRowid(pDest)
|
||||
&& IsPrimaryKeyIndex(pDestIdx)
|
||||
){
|
||||
codeWithoutRowidPreupdate(pParse, pDest, iDest, regData);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
|
||||
sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
|
||||
|
||||
+23
-7
@@ -868,7 +868,7 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
|
||||
/////////////////////////// The DELETE statement /////////////////////////////
|
||||
//
|
||||
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
|
||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W)
|
||||
orderby_opt(O) limit_opt(L). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
#ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
@@ -881,7 +881,7 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
|
||||
sqlite3DeleteFrom(pParse,X,W,O,L);
|
||||
}
|
||||
%else
|
||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
|
||||
cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3DeleteFrom(pParse,X,W,0,0);
|
||||
}
|
||||
@@ -889,15 +889,23 @@ cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
|
||||
|
||||
%type where_opt {Expr*}
|
||||
%destructor where_opt {sqlite3ExprDelete(pParse->db, $$);}
|
||||
%type where_opt_ret {Expr*}
|
||||
%destructor where_opt_ret {sqlite3ExprDelete(pParse->db, $$);}
|
||||
|
||||
where_opt(A) ::= . {A = 0;}
|
||||
where_opt(A) ::= WHERE expr(X). {A = X;}
|
||||
where_opt_ret(A) ::= . {A = 0;}
|
||||
where_opt_ret(A) ::= WHERE expr(X). {A = X;}
|
||||
where_opt_ret(A) ::= RETURNING selcollist(X).
|
||||
{sqlite3AddReturning(pParse,X); A = 0;}
|
||||
where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
|
||||
{sqlite3AddReturning(pParse,Y); A = X;}
|
||||
|
||||
////////////////////////// The UPDATE command ////////////////////////////////
|
||||
//
|
||||
%if SQLITE_ENABLE_UPDATE_DELETE_LIMIT || SQLITE_UDL_CAPABLE_PARSER
|
||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||
where_opt(W) orderby_opt(O) limit_opt(L). {
|
||||
where_opt_ret(W) orderby_opt(O) limit_opt(L). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
@@ -912,7 +920,7 @@ cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||
}
|
||||
%else
|
||||
cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
|
||||
where_opt(W). {
|
||||
where_opt_ret(W). {
|
||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||
X = sqlite3SrcListAppendList(pParse, X, F);
|
||||
@@ -946,7 +954,7 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
|
||||
upsert(U). {
|
||||
sqlite3Insert(pParse, X, S, F, R, U);
|
||||
}
|
||||
cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
|
||||
cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning.
|
||||
{
|
||||
sqlite3Insert(pParse, X, 0, F, R, 0);
|
||||
}
|
||||
@@ -959,16 +967,20 @@ cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
|
||||
// avoid unreachable code.
|
||||
//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
|
||||
upsert(A) ::= . { A = 0; }
|
||||
upsert(A) ::= RETURNING selcollist(X). { A = 0; sqlite3AddReturning(pParse,X); }
|
||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
|
||||
DO UPDATE SET setlist(Z) where_opt(W) upsert(N).
|
||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,Z,W,N);}
|
||||
upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING upsert(N).
|
||||
{ A = sqlite3UpsertNew(pParse->db,T,TW,0,0,N); }
|
||||
upsert(A) ::= ON CONFLICT DO NOTHING.
|
||||
upsert(A) ::= ON CONFLICT DO NOTHING returning.
|
||||
{ A = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
|
||||
upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W).
|
||||
upsert(A) ::= ON CONFLICT DO UPDATE SET setlist(Z) where_opt(W) returning.
|
||||
{ A = sqlite3UpsertNew(pParse->db,0,0,Z,W,0);}
|
||||
|
||||
returning ::= RETURNING selcollist(X). {sqlite3AddReturning(pParse,X);}
|
||||
returning ::= .
|
||||
|
||||
%type insert_cmd {int}
|
||||
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
|
||||
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
|
||||
@@ -1609,6 +1621,10 @@ cmd ::= ALTER TABLE add_column_fullname
|
||||
Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
|
||||
sqlite3AlterFinishAddColumn(pParse, &Y);
|
||||
}
|
||||
cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
|
||||
sqlite3AlterDropColumn(pParse, X, &Y);
|
||||
}
|
||||
|
||||
add_column_fullname ::= fullname(X). {
|
||||
disableLookaside(pParse);
|
||||
sqlite3AlterBeginAddColumn(pParse, X);
|
||||
|
||||
+16
-27
@@ -21,7 +21,7 @@
|
||||
*/
|
||||
static void corruptSchema(
|
||||
InitData *pData, /* Initialization context */
|
||||
const char *zObj, /* Object being parsed at the point of error */
|
||||
char **azObj, /* Type and name of object being parsed */
|
||||
const char *zExtra /* Error information */
|
||||
){
|
||||
sqlite3 *db = pData->db;
|
||||
@@ -29,14 +29,18 @@ static void corruptSchema(
|
||||
pData->rc = SQLITE_NOMEM_BKPT;
|
||||
}else if( pData->pzErrMsg[0]!=0 ){
|
||||
/* A error message has already been generated. Do not overwrite it */
|
||||
}else if( pData->mInitFlags & INITFLAG_AlterTable ){
|
||||
*pData->pzErrMsg = sqlite3DbStrDup(db, zExtra);
|
||||
}else if( pData->mInitFlags & (INITFLAG_AlterRename|INITFLAG_AlterDrop) ){
|
||||
*pData->pzErrMsg = sqlite3MPrintf(db,
|
||||
"error in %s %s after %s: %s", azObj[0], azObj[1],
|
||||
(pData->mInitFlags & INITFLAG_AlterRename) ? "rename" : "drop column",
|
||||
zExtra
|
||||
);
|
||||
pData->rc = SQLITE_ERROR;
|
||||
}else if( db->flags & SQLITE_WriteSchema ){
|
||||
pData->rc = SQLITE_CORRUPT_BKPT;
|
||||
}else{
|
||||
char *z;
|
||||
if( zObj==0 ) zObj = "?";
|
||||
const char *zObj = azObj[1] ? azObj[1] : "?";
|
||||
z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
|
||||
if( zExtra && zExtra[0] ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
|
||||
*pData->pzErrMsg = z;
|
||||
@@ -94,14 +98,14 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
db->mDbFlags |= DBFLAG_EncodingFixed;
|
||||
pData->nInitRow++;
|
||||
if( db->mallocFailed ){
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
corruptSchema(pData, argv, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
|
||||
if( argv[3]==0 ){
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
corruptSchema(pData, argv, 0);
|
||||
}else if( argv[4]
|
||||
&& 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]]
|
||||
&& 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){
|
||||
@@ -126,7 +130,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
|| (db->init.newTnum>pData->mxPage && pData->mxPage>0)
|
||||
){
|
||||
if( sqlite3Config.bExtraSchemaChecks ){
|
||||
corruptSchema(pData, argv[1], "invalid rootpage");
|
||||
corruptSchema(pData, argv, "invalid rootpage");
|
||||
}
|
||||
}
|
||||
db->init.orphanTrigger = 0;
|
||||
@@ -145,13 +149,13 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
sqlite3OomFault(db);
|
||||
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
|
||||
corruptSchema(pData, argv[1], sqlite3_errmsg(db));
|
||||
corruptSchema(pData, argv, sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
|
||||
corruptSchema(pData, argv[1], 0);
|
||||
corruptSchema(pData, argv, 0);
|
||||
}else{
|
||||
/* If the SQL column is blank it means this is an index that
|
||||
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
|
||||
@@ -162,7 +166,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
Index *pIndex;
|
||||
pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
|
||||
if( pIndex==0 ){
|
||||
corruptSchema(pData, argv[1], "orphan index");
|
||||
corruptSchema(pData, argv, "orphan index");
|
||||
}else
|
||||
if( sqlite3GetUInt32(argv[3],&pIndex->tnum)==0
|
||||
|| pIndex->tnum<2
|
||||
@@ -170,7 +174,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
|
||||
|| sqlite3IndexHasDuplicateRootPage(pIndex)
|
||||
){
|
||||
if( sqlite3Config.bExtraSchemaChecks ){
|
||||
corruptSchema(pData, argv[1], "invalid rootpage");
|
||||
corruptSchema(pData, argv, "invalid rootpage");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,31 +554,16 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate a single AggInfo object
|
||||
*/
|
||||
static void agginfoFree(sqlite3 *db, AggInfo *p){
|
||||
sqlite3DbFree(db, p->aCol);
|
||||
sqlite3DbFree(db, p->aFunc);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Free all memory allocations in the pParse object
|
||||
*/
|
||||
void sqlite3ParserReset(Parse *pParse){
|
||||
sqlite3 *db = pParse->db;
|
||||
AggInfo *pThis = pParse->pAggList;
|
||||
while( pThis ){
|
||||
AggInfo *pNext = pThis->pNext;
|
||||
agginfoFree(db, pThis);
|
||||
pThis = pNext;
|
||||
}
|
||||
while( pParse->pCleanup ){
|
||||
ParseCleanup *pCleanup = pParse->pCleanup;
|
||||
pParse->pCleanup = pCleanup->pNext;
|
||||
pCleanup->xCleanup(db, pCleanup->pPtr);
|
||||
sqlite3DbFree(db, pCleanup);
|
||||
sqlite3DbFreeNN(db, pCleanup);
|
||||
}
|
||||
sqlite3DbFree(db, pParse->aLabel);
|
||||
if( pParse->pConstExpr ){
|
||||
|
||||
+42
-32
@@ -70,7 +70,6 @@ static void resolveAlias(
|
||||
ExprList *pEList, /* A result set */
|
||||
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
|
||||
Expr *pExpr, /* Transform this into an alias to the result set */
|
||||
const char *zType, /* "GROUP" or "ORDER" or "" */
|
||||
int nSubquery /* Number of subqueries that the label is moving */
|
||||
){
|
||||
Expr *pOrig; /* The iCol-th column of the result set */
|
||||
@@ -83,7 +82,7 @@ static void resolveAlias(
|
||||
db = pParse->db;
|
||||
pDup = sqlite3ExprDup(db, pOrig, 0);
|
||||
if( pDup!=0 ){
|
||||
if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
|
||||
incrAggFunctionDepth(pDup, nSubquery);
|
||||
if( pExpr->op==TK_COLLATE ){
|
||||
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
|
||||
}
|
||||
@@ -112,7 +111,6 @@ static void resolveAlias(
|
||||
}
|
||||
sqlite3DbFree(db, pDup);
|
||||
}
|
||||
ExprSetProperty(pExpr, EP_Alias);
|
||||
}
|
||||
|
||||
|
||||
@@ -369,25 +367,29 @@ static int lookupName(
|
||||
#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
|
||||
/* If we have not already resolved the name, then maybe
|
||||
** it is a new.* or old.* trigger argument reference. Or
|
||||
** maybe it is an excluded.* from an upsert.
|
||||
** maybe it is an excluded.* from an upsert. Or maybe it is
|
||||
** a reference in the RETURNING clause to a table being modified.
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 ){
|
||||
if( cnt==0 && zDb==0 ){
|
||||
pTab = 0;
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( pParse->pTriggerTab!=0 ){
|
||||
int op = pParse->eTriggerOp;
|
||||
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
|
||||
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
if( op!=TK_DELETE && zTab && sqlite3StrICmp("new",zTab) == 0 ){
|
||||
pExpr->iTable = 1;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
|
||||
}else if( op!=TK_INSERT && zTab && sqlite3StrICmp("old",zTab)==0 ){
|
||||
pExpr->iTable = 0;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}else if( pParse->bReturning && (pNC->ncFlags & NC_UBaseReg)!=0 ){
|
||||
pExpr->iTable = op!=TK_DELETE;
|
||||
pTab = pParse->pTriggerTab;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( (pNC->ncFlags & NC_UUpsert)!=0 ){
|
||||
if( (pNC->ncFlags & NC_UUpsert)!=0 && zTab!=0 ){
|
||||
Upsert *pUpsert = pNC->uNC.pUpsert;
|
||||
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
||||
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
||||
@@ -426,27 +428,32 @@ static int lookupName(
|
||||
pExpr->iTable = pNC->uNC.pUpsert->regData +
|
||||
sqlite3TableColumnToStorage(pTab, iCol);
|
||||
eNewExprOp = TK_REGISTER;
|
||||
ExprSetProperty(pExpr, EP_Alias);
|
||||
}
|
||||
}else
|
||||
#endif /* SQLITE_OMIT_UPSERT */
|
||||
{
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( iCol<0 ){
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}else if( pExpr->iTable==0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}else{
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}
|
||||
pExpr->y.pTab = pTab;
|
||||
pExpr->iColumn = (i16)iCol;
|
||||
eNewExprOp = TK_TRIGGER;
|
||||
if( pParse->bReturning ){
|
||||
eNewExprOp = TK_REGISTER;
|
||||
pExpr->iTable = pNC->uNC.iBaseReg + (pTab->nCol+1)*pExpr->iTable
|
||||
+ iCol + 1;
|
||||
}else{
|
||||
pExpr->iColumn = (i16)iCol;
|
||||
eNewExprOp = TK_TRIGGER;
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
if( iCol<0 ){
|
||||
pExpr->affExpr = SQLITE_AFF_INTEGER;
|
||||
}else if( pExpr->iTable==0 ){
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}else{
|
||||
testcase( iCol==31 );
|
||||
testcase( iCol==32 );
|
||||
pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
|
||||
}
|
||||
#endif /* SQLITE_OMIT_TRIGGER */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,7 +523,7 @@ static int lookupName(
|
||||
sqlite3ErrorMsg(pParse, "row value misused");
|
||||
return WRC_Abort;
|
||||
}
|
||||
resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
|
||||
resolveAlias(pParse, pEList, j, pExpr, nSubquery);
|
||||
cnt = 1;
|
||||
pMatch = 0;
|
||||
assert( zTab==0 && zDb==0 );
|
||||
@@ -618,16 +625,20 @@ static int lookupName(
|
||||
|
||||
/* Clean up and return
|
||||
*/
|
||||
sqlite3ExprDelete(db, pExpr->pLeft);
|
||||
pExpr->pLeft = 0;
|
||||
sqlite3ExprDelete(db, pExpr->pRight);
|
||||
pExpr->pRight = 0;
|
||||
if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
|
||||
sqlite3ExprDelete(db, pExpr->pLeft);
|
||||
pExpr->pLeft = 0;
|
||||
sqlite3ExprDelete(db, pExpr->pRight);
|
||||
pExpr->pRight = 0;
|
||||
}
|
||||
pExpr->op = eNewExprOp;
|
||||
ExprSetProperty(pExpr, EP_Leaf);
|
||||
lookupname_end:
|
||||
if( cnt==1 ){
|
||||
assert( pNC!=0 );
|
||||
if( !ExprHasProperty(pExpr, EP_Alias) ){
|
||||
if( pParse->db->xAuth
|
||||
&& (pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER)
|
||||
){
|
||||
sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
|
||||
}
|
||||
/* Increment the nRef value on all name contexts from TopNC up to
|
||||
@@ -1391,8 +1402,7 @@ int sqlite3ResolveOrderGroupBy(
|
||||
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
|
||||
return 1;
|
||||
}
|
||||
resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
|
||||
zType,0);
|
||||
resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@@ -1639,7 +1649,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
|
||||
** Minor point: If this is the case, then the expression will be
|
||||
** re-evaluated for each reference to it.
|
||||
*/
|
||||
assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
|
||||
assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert|NC_UBaseReg))==0 );
|
||||
sNC.uNC.pEList = p->pEList;
|
||||
sNC.ncFlags |= NC_UEList;
|
||||
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
|
||||
|
||||
+32
-10
@@ -262,7 +262,7 @@ int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
|
||||
** Return the index of a column in a table. Return -1 if the column
|
||||
** is not contained in the table.
|
||||
*/
|
||||
static int columnIndex(Table *pTab, const char *zCol){
|
||||
int sqlite3ColumnIndex(Table *pTab, const char *zCol){
|
||||
int i;
|
||||
u8 h = sqlite3StrIHash(zCol);
|
||||
Column *pCol;
|
||||
@@ -294,7 +294,7 @@ static int tableAndColumnIndex(
|
||||
|
||||
assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */
|
||||
for(i=0; i<N; i++){
|
||||
iCol = columnIndex(pSrc->a[i].pTab, zCol);
|
||||
iCol = sqlite3ColumnIndex(pSrc->a[i].pTab, zCol);
|
||||
if( iCol>=0
|
||||
&& (bIgnoreHidden==0 || IsHiddenColumn(&pSrc->a[i].pTab->aCol[iCol])==0)
|
||||
){
|
||||
@@ -504,7 +504,7 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
int iRightCol; /* Column number of matching column on the right */
|
||||
|
||||
zName = pList->a[j].zName;
|
||||
iRightCol = columnIndex(pRightTab, zName);
|
||||
iRightCol = sqlite3ColumnIndex(pRightTab, zName);
|
||||
if( iRightCol<0
|
||||
|| !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol, 0)
|
||||
){
|
||||
@@ -2086,6 +2086,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
|
||||
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
|
||||
const char *zType;
|
||||
int n, m;
|
||||
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
|
||||
p = a[i].pExpr;
|
||||
zType = columnType(&sNC, p, 0, 0, 0);
|
||||
/* pCol->szEst = ... // Column size est for SELECT tables never used */
|
||||
@@ -3688,7 +3689,8 @@ static void srclistRenumberCursors(
|
||||
*/
|
||||
static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
|
||||
int *aCsrMap = pWalker->u.aiCol;
|
||||
if( pExpr->op==TK_COLUMN && aCsrMap[pExpr->iTable] ){
|
||||
int op = pExpr->op;
|
||||
if( (op==TK_COLUMN || op==TK_IF_NULL_ROW) && aCsrMap[pExpr->iTable] ){
|
||||
pExpr->iTable = aCsrMap[pExpr->iTable];
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_FromJoin) && aCsrMap[pExpr->iRightJoinTable] ){
|
||||
@@ -4592,6 +4594,7 @@ static int pushDownWhereTerms(
|
||||
}
|
||||
if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
|
||||
nChng++;
|
||||
pSubq->selFlags |= SF_PushDown;
|
||||
while( pSubq ){
|
||||
SubstContext x;
|
||||
pNew = sqlite3ExprDup(pParse->db, pWhere, 0);
|
||||
@@ -5764,6 +5767,8 @@ static struct SrcList_item *isSelfJoinView(
|
||||
struct SrcList_item *pThis /* Search for prior reference to this subquery */
|
||||
){
|
||||
struct SrcList_item *pItem;
|
||||
assert( pThis->pSelect!=0 );
|
||||
if( pThis->pSelect->selFlags & SF_PushDown ) return 0;
|
||||
for(pItem = pTabList->a; pItem<pThis; pItem++){
|
||||
Select *pS1;
|
||||
if( pItem->pSelect==0 ) continue;
|
||||
@@ -5779,9 +5784,7 @@ static struct SrcList_item *isSelfJoinView(
|
||||
** names in the same FROM clause. */
|
||||
continue;
|
||||
}
|
||||
if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1)
|
||||
|| sqlite3ExprCompare(0, pThis->pSelect->pHaving, pS1->pHaving, -1)
|
||||
){
|
||||
if( pItem->pSelect->selFlags & SF_PushDown ){
|
||||
/* The view was modified by some other optimization such as
|
||||
** pushDownWhereTerms() */
|
||||
continue;
|
||||
@@ -5791,6 +5794,15 @@ static struct SrcList_item *isSelfJoinView(
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Deallocate a single AggInfo object
|
||||
*/
|
||||
static void agginfoFree(sqlite3 *db, AggInfo *p){
|
||||
sqlite3DbFree(db, p->aCol);
|
||||
sqlite3DbFree(db, p->aFunc);
|
||||
sqlite3DbFreeNN(db, p);
|
||||
}
|
||||
|
||||
#ifdef SQLITE_COUNTOFVIEW_OPTIMIZATION
|
||||
/*
|
||||
** Attempt to transform a query of the form
|
||||
@@ -6202,6 +6214,7 @@ int sqlite3Select(
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
|
||||
}else{
|
||||
SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
|
||||
}
|
||||
@@ -6424,6 +6437,7 @@ int sqlite3Select(
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
|
||||
/* If sorting index that was created by a prior OP_OpenEphemeral
|
||||
** instruction ended up not being needed, then change the OP_OpenEphemeral
|
||||
@@ -6462,6 +6476,7 @@ int sqlite3Select(
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
}else{
|
||||
@@ -6532,11 +6547,13 @@ int sqlite3Select(
|
||||
** SELECT statement.
|
||||
*/
|
||||
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
|
||||
if( pAggInfo==0 ){
|
||||
if( pAggInfo ){
|
||||
sqlite3ParserAddCleanup(pParse,
|
||||
(void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
goto select_end;
|
||||
}
|
||||
pAggInfo->pNext = pParse->pAggList;
|
||||
pParse->pAggList = pAggInfo;
|
||||
pAggInfo->selId = p->selId;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
sNC.pParse = pParse;
|
||||
@@ -6655,6 +6672,7 @@ int sqlite3Select(
|
||||
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
|
||||
);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
|
||||
/* The optimizer is able to deliver rows in group by order so
|
||||
** we do not have to sort. The OP_OpenEphemeral table will be
|
||||
@@ -6703,6 +6721,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++;
|
||||
sortOut = sqlite3GetTempReg(pParse);
|
||||
@@ -6780,6 +6799,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
|
||||
VdbeCoverage(v);
|
||||
}else{
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
|
||||
}
|
||||
@@ -6935,11 +6955,13 @@ int sqlite3Select(
|
||||
if( pWInfo==0 ){
|
||||
goto select_end;
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereBegin returns\n"));
|
||||
updateAccumulator(pParse, regAcc, pAggInfo);
|
||||
if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
|
||||
if( minMaxFlag ){
|
||||
sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);
|
||||
}
|
||||
SELECTTRACE(1,pParse,p,("WhereEnd\n"));
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
finalizeAggFunctions(pParse, pAggInfo);
|
||||
}
|
||||
|
||||
+12
-5
@@ -8753,7 +8753,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
|
||||
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
|
||||
){
|
||||
const char *zFile = 0;
|
||||
char *zFile = 0;
|
||||
int bTxtMode = 0;
|
||||
int i;
|
||||
int eMode = 0;
|
||||
@@ -8783,17 +8783,22 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
rc = 1;
|
||||
goto meta_command_exit;
|
||||
}
|
||||
}else if( zFile==0 ){
|
||||
zFile = z;
|
||||
}else if( zFile==0 && eMode!='e' && eMode!='x' ){
|
||||
zFile = sqlite3_mprintf("%s", z);
|
||||
if( zFile[0]=='|' ){
|
||||
while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n",
|
||||
azArg[i]);
|
||||
showHelp(p->out, azArg[0]);
|
||||
rc = 1;
|
||||
sqlite3_free(zFile);
|
||||
goto meta_command_exit;
|
||||
}
|
||||
}
|
||||
if( zFile==0 ) zFile = "stdout";
|
||||
if( zFile==0 ) zFile = sqlite3_mprintf("stdout");
|
||||
if( bOnce ){
|
||||
p->outCount = 2;
|
||||
}else{
|
||||
@@ -8816,7 +8821,8 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
newTempFile(p, "txt");
|
||||
bTxtMode = 1;
|
||||
}
|
||||
zFile = p->zTempFile;
|
||||
sqlite3_free(zFile);
|
||||
zFile = sqlite3_mprintf("%s", p->zTempFile);
|
||||
}
|
||||
#endif /* SQLITE_NOHAVE_SYSTEM */
|
||||
if( zFile[0]=='|' ){
|
||||
@@ -8848,6 +8854,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
|
||||
}
|
||||
}
|
||||
sqlite3_free(zFile);
|
||||
}else
|
||||
|
||||
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
|
||||
|
||||
+7
-1
@@ -2115,7 +2115,13 @@ struct sqlite3_mem_methods {
|
||||
** The second parameter is a pointer to an integer into which
|
||||
** is written 0 or 1 to indicate whether triggers are disabled or enabled
|
||||
** following this call. The second parameter may be a NULL pointer, in
|
||||
** which case the trigger setting is not reported back. </dd>
|
||||
** which case the trigger setting is not reported back.
|
||||
**
|
||||
** <p>Originally this option disabled all triggers. ^(However, since
|
||||
** SQLite version 3.35.0, TEMP triggers are still allowed even if
|
||||
** this option is off. So, in other words, this option now only disables
|
||||
** triggers in the main database schema or in the schemas of ATTACH-ed
|
||||
** databases.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
||||
|
||||
+43
-11
@@ -1159,6 +1159,7 @@ typedef struct ParseCleanup ParseCleanup;
|
||||
typedef struct PreUpdate PreUpdate;
|
||||
typedef struct PrintfArguments PrintfArguments;
|
||||
typedef struct RenameToken RenameToken;
|
||||
typedef struct Returning Returning;
|
||||
typedef struct RowSet RowSet;
|
||||
typedef struct Savepoint Savepoint;
|
||||
typedef struct Select Select;
|
||||
@@ -2014,7 +2015,12 @@ struct Column {
|
||||
u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
|
||||
};
|
||||
|
||||
/* Allowed values for Column.colFlags:
|
||||
/* Allowed values for Column.colFlags.
|
||||
**
|
||||
** Constraints:
|
||||
** TF_HasVirtual == COLFLAG_VIRTUAL
|
||||
** TF_HasStored == COLFLAG_STORED
|
||||
** TF_HasHidden == COLFLAG_HIDDEN
|
||||
*/
|
||||
#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
|
||||
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
|
||||
@@ -2203,11 +2209,12 @@ struct Table {
|
||||
**
|
||||
** Constraints:
|
||||
**
|
||||
** TF_HasVirtual == COLFLAG_Virtual
|
||||
** TF_HasStored == COLFLAG_Stored
|
||||
** TF_HasVirtual == COLFLAG_VIRTUAL
|
||||
** TF_HasStored == COLFLAG_STORED
|
||||
** TF_HasHidden == COLFLAG_HIDDEN
|
||||
*/
|
||||
#define TF_Readonly 0x0001 /* Read-only system table */
|
||||
#define TF_Ephemeral 0x0002 /* An ephemeral table */
|
||||
#define TF_HasHidden 0x0002 /* Has one or more hidden columns */
|
||||
#define TF_HasPrimaryKey 0x0004 /* Table has a primary key */
|
||||
#define TF_Autoincrement 0x0008 /* Integer primary key is autoincrement */
|
||||
#define TF_HasStat1 0x0010 /* nRowLogEst set from sqlite_stat1 */
|
||||
@@ -2222,6 +2229,7 @@ struct Table {
|
||||
#define TF_HasNotNull 0x0800 /* Contains NOT NULL constraints */
|
||||
#define TF_Shadow 0x1000 /* True for a shadow table */
|
||||
#define TF_HasStat4 0x2000 /* STAT4 info available for this table */
|
||||
#define TF_Ephemeral 0x4000 /* An ephemeral table */
|
||||
|
||||
/*
|
||||
** Test to see whether or not a table is a virtual table. This is
|
||||
@@ -2590,7 +2598,6 @@ struct AggInfo {
|
||||
} *aFunc;
|
||||
int nFunc; /* Number of entries in aFunc[] */
|
||||
u32 selId; /* Select to which this AggInfo belongs */
|
||||
AggInfo *pNext; /* Next in list of them all */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -2761,7 +2768,7 @@ struct Expr {
|
||||
#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
|
||||
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
|
||||
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
|
||||
#define EP_Alias 0x400000 /* Is an alias for a result set column */
|
||||
/* 0x400000 // Available */
|
||||
#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
|
||||
#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
|
||||
#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
|
||||
@@ -3036,6 +3043,7 @@ struct NameContext {
|
||||
ExprList *pEList; /* Optional list of result-set columns */
|
||||
AggInfo *pAggInfo; /* Information about aggregates at this level */
|
||||
Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
|
||||
int iBaseReg; /* For TK_REGISTER when parsing RETURNING */
|
||||
} uNC;
|
||||
NameContext *pNext; /* Next outer name context. NULL for outermost */
|
||||
int nRef; /* Number of names resolved by this context */
|
||||
@@ -3064,6 +3072,7 @@ struct NameContext {
|
||||
#define NC_UEList 0x00080 /* True if uNC.pEList is used */
|
||||
#define NC_UAggInfo 0x00100 /* True if uNC.pAggInfo is used */
|
||||
#define NC_UUpsert 0x00200 /* True if uNC.pUpsert is used */
|
||||
#define NC_UBaseReg 0x00400 /* True if uNC.iBaseReg is used */
|
||||
#define NC_MinMaxAgg 0x01000 /* min/max aggregates seen. See note above */
|
||||
#define NC_Complex 0x02000 /* True if a function or subquery seen */
|
||||
#define NC_AllowWin 0x04000 /* Window functions are allowed here */
|
||||
@@ -3181,6 +3190,7 @@ struct Select {
|
||||
#define SF_View 0x0200000 /* SELECT statement is a view */
|
||||
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
|
||||
#define SF_UpdateFrom 0x0800000 /* Statement is an UPDATE...FROM */
|
||||
#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
|
||||
|
||||
/*
|
||||
** The results of a SELECT can be distributed in several ways, as defined
|
||||
@@ -3423,12 +3433,15 @@ struct Parse {
|
||||
Parse *pToplevel; /* Parse structure for main program (or NULL) */
|
||||
Table *pTriggerTab; /* Table triggers are being coded for */
|
||||
Parse *pParentParse; /* Parent parser if this parser is nested */
|
||||
AggInfo *pAggList; /* List of all AggInfo objects */
|
||||
int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */
|
||||
union {
|
||||
int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
|
||||
Returning *pReturning; /* The RETURNING clause */
|
||||
} u1;
|
||||
u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
|
||||
u32 oldmask; /* Mask of old.* columns referenced */
|
||||
u32 newmask; /* Mask of new.* columns referenced */
|
||||
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
|
||||
u8 bReturning; /* Coding a RETURNING trigger */
|
||||
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
|
||||
u8 disableTriggers; /* True to disable triggers */
|
||||
|
||||
@@ -3578,6 +3591,7 @@ struct Trigger {
|
||||
char *table; /* The table or view to which the trigger applies */
|
||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
|
||||
u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */
|
||||
u8 bReturning; /* This trigger implements a RETURNING clause */
|
||||
Expr *pWhen; /* The WHEN clause of the expression (may be NULL) */
|
||||
IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
|
||||
the <column-list> is stored here */
|
||||
@@ -3636,14 +3650,15 @@ struct Trigger {
|
||||
*
|
||||
*/
|
||||
struct TriggerStep {
|
||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
|
||||
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT,
|
||||
** or TK_RETURNING */
|
||||
u8 orconf; /* OE_Rollback etc. */
|
||||
Trigger *pTrig; /* The trigger that this step is a part of */
|
||||
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
|
||||
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
|
||||
SrcList *pFrom; /* FROM clause for UPDATE statement (if any) */
|
||||
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
|
||||
ExprList *pExprList; /* SET clause for UPDATE */
|
||||
ExprList *pExprList; /* SET clause for UPDATE, or RETURNING clause */
|
||||
IdList *pIdList; /* Column names for INSERT */
|
||||
Upsert *pUpsert; /* Upsert clauses on an INSERT */
|
||||
char *zSpan; /* Original SQL text of this command */
|
||||
@@ -3651,6 +3666,19 @@ struct TriggerStep {
|
||||
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
|
||||
};
|
||||
|
||||
/*
|
||||
** Information about a RETURNING clause
|
||||
*/
|
||||
struct Returning {
|
||||
Parse *pParse; /* The parse that includes the RETURNING clause */
|
||||
ExprList *pReturnEL; /* List of expressions to return */
|
||||
Trigger retTrig; /* The transient trigger that implements RETURNING */
|
||||
TriggerStep retTStep; /* The trigger step */
|
||||
int iRetCur; /* Transient table holding RETURNING results */
|
||||
int nRetCol; /* Number of in pReturnEL after expansion */
|
||||
int iRetReg; /* Register array for holding a row of RETURNING */
|
||||
};
|
||||
|
||||
/*
|
||||
** An objected used to accumulate the text of a string where we
|
||||
** do not necessarily know how big the string will be in the end.
|
||||
@@ -3688,7 +3716,8 @@ typedef struct {
|
||||
/*
|
||||
** Allowed values for mInitFlags
|
||||
*/
|
||||
#define INITFLAG_AlterTable 0x0001 /* This is a reparse after ALTER TABLE */
|
||||
#define INITFLAG_AlterRename 0x0001 /* Reparse after a RENAME */
|
||||
#define INITFLAG_AlterDrop 0x0002 /* Reparse after a DROP COLUMN */
|
||||
|
||||
/*
|
||||
** Structure containing global configuration data for the SQLite library.
|
||||
@@ -4252,6 +4281,7 @@ void sqlite3AddDefaultValue(Parse*,Expr*,const char*,const char*);
|
||||
void sqlite3AddCollateType(Parse*, Token*);
|
||||
void sqlite3AddGenerated(Parse*,Expr*,Token*);
|
||||
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
|
||||
void sqlite3AddReturning(Parse*,ExprList*);
|
||||
int sqlite3ParseUri(const char*,const char*,unsigned int*,
|
||||
sqlite3_vfs**,char**,char **);
|
||||
#define sqlite3CodecQueryParameters(A,B,C) 0
|
||||
@@ -4507,6 +4537,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
|
||||
#endif
|
||||
|
||||
int sqlite3JoinType(Parse*, Token*, Token*, Token*);
|
||||
int sqlite3ColumnIndex(Table *pTab, const char *zCol);
|
||||
void sqlite3SetJoinExpr(Expr*,int);
|
||||
void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
|
||||
void sqlite3DeferForeignKey(Parse*, int);
|
||||
@@ -4691,6 +4722,7 @@ int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
|
||||
void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
|
||||
void sqlite3AlterFinishAddColumn(Parse *, Token *);
|
||||
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
|
||||
void sqlite3AlterDropColumn(Parse*, SrcList*, Token*);
|
||||
void *sqlite3RenameTokenMap(Parse*, void*, Token*);
|
||||
void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
|
||||
void sqlite3RenameExprUnmap(Parse*, Expr*);
|
||||
|
||||
+199
-32
@@ -48,28 +48,39 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
|
||||
** pTab as well as the triggers lised in pTab->pTrigger.
|
||||
*/
|
||||
Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
|
||||
Schema * const pTmpSchema = pParse->db->aDb[1].pSchema;
|
||||
Trigger *pList = 0; /* List of triggers to return */
|
||||
Schema *pTmpSchema; /* Schema of the pTab table */
|
||||
Trigger *pList; /* List of triggers to return */
|
||||
HashElem *p; /* Loop variable for TEMP triggers */
|
||||
|
||||
if( pParse->disableTriggers ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
pTmpSchema = pParse->db->aDb[1].pSchema;
|
||||
p = sqliteHashFirst(&pTmpSchema->trigHash);
|
||||
if( p==0 ){
|
||||
return pTab->pTrigger;
|
||||
}
|
||||
pList = pTab->pTrigger;
|
||||
if( pTmpSchema!=pTab->pSchema ){
|
||||
HashElem *p;
|
||||
assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) );
|
||||
for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){
|
||||
while( p ){
|
||||
Trigger *pTrig = (Trigger *)sqliteHashData(p);
|
||||
if( pTrig->pTabSchema==pTab->pSchema
|
||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||
&& 0==sqlite3StrICmp(pTrig->table, pTab->zName)
|
||||
){
|
||||
pTrig->pNext = (pList ? pList : pTab->pTrigger);
|
||||
pTrig->pNext = pList;
|
||||
pList = pTrig;
|
||||
}
|
||||
}else if( pTrig->op==TK_RETURNING ){
|
||||
assert( pParse->bReturning );
|
||||
assert( &(pParse->u1.pReturning->retTrig) == pTrig );
|
||||
pTrig->table = pTab->zName;
|
||||
pTrig->pTabSchema = pTab->pSchema;
|
||||
pTrig->pNext = pList;
|
||||
pList = pTrig;
|
||||
}
|
||||
p = sqliteHashNext(p);
|
||||
}
|
||||
}
|
||||
|
||||
return (pList ? pList : pTab->pTrigger);
|
||||
return pList;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -345,7 +356,7 @@ void sqlite3FinishTrigger(
|
||||
sqlite3DbFree(db, z);
|
||||
sqlite3ChangeCookie(pParse, iDb);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb,
|
||||
sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName));
|
||||
sqlite3MPrintf(db, "type='trigger' AND name='%q'", zName), 0);
|
||||
}
|
||||
|
||||
if( db->init.busy ){
|
||||
@@ -558,7 +569,7 @@ TriggerStep *sqlite3TriggerDeleteStep(
|
||||
** Recursively delete a Trigger structure
|
||||
*/
|
||||
void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){
|
||||
if( pTrigger==0 ) return;
|
||||
if( pTrigger==0 || pTrigger->bReturning ) return;
|
||||
sqlite3DeleteTriggerStep(db, pTrigger->step_list);
|
||||
sqlite3DbFree(db, pTrigger->zName);
|
||||
sqlite3DbFree(db, pTrigger->table);
|
||||
@@ -723,15 +734,53 @@ Trigger *sqlite3TriggersExist(
|
||||
Trigger *pList = 0;
|
||||
Trigger *p;
|
||||
|
||||
if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){
|
||||
pList = sqlite3TriggerList(pParse, pTab);
|
||||
}
|
||||
assert( pList==0 || IsVirtual(pTab)==0 );
|
||||
for(p=pList; p; p=p->pNext){
|
||||
if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
|
||||
mask |= p->tr_tm;
|
||||
pList = sqlite3TriggerList(pParse, pTab);
|
||||
assert( pList==0 || IsVirtual(pTab)==0
|
||||
|| (pList->bReturning && pList->pNext==0) );
|
||||
if( pList!=0 ){
|
||||
p = pList;
|
||||
if( (pParse->db->flags & SQLITE_EnableTrigger)==0
|
||||
&& pTab->pTrigger!=0
|
||||
){
|
||||
/* The SQLITE_DBCONFIG_ENABLE_TRIGGER setting is off. That means that
|
||||
** only TEMP triggers are allowed. Truncate the pList so that it
|
||||
** includes only TEMP triggers */
|
||||
if( pList==pTab->pTrigger ){
|
||||
pList = 0;
|
||||
goto exit_triggers_exist;
|
||||
}
|
||||
while( ALWAYS(p->pNext) && p->pNext!=pTab->pTrigger ) p = p->pNext;
|
||||
p->pNext = 0;
|
||||
p = pList;
|
||||
}
|
||||
do{
|
||||
if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){
|
||||
mask |= p->tr_tm;
|
||||
}else if( p->op==TK_RETURNING ){
|
||||
/* The first time a RETURNING trigger is seen, the "op" value tells
|
||||
** us what time of trigger it should be. */
|
||||
assert( sqlite3IsToplevel(pParse) );
|
||||
p->op = op;
|
||||
if( IsVirtual(pTab) ){
|
||||
if( op!=TK_INSERT ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"%s RETURNING is not available on virtual tables",
|
||||
op==TK_DELETE ? "DELETE" : "UPDATE");
|
||||
}
|
||||
p->tr_tm = TRIGGER_BEFORE;
|
||||
}else{
|
||||
p->tr_tm = TRIGGER_AFTER;
|
||||
}
|
||||
mask |= p->tr_tm;
|
||||
}else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
|
||||
&& sqlite3IsToplevel(pParse) ){
|
||||
/* Also fire a RETURNING trigger for an UPSERT */
|
||||
mask |= p->tr_tm;
|
||||
}
|
||||
p = p->pNext;
|
||||
}while( p );
|
||||
}
|
||||
exit_triggers_exist:
|
||||
if( pMask ){
|
||||
*pMask = mask;
|
||||
}
|
||||
@@ -774,6 +823,111 @@ SrcList *sqlite3TriggerStepSrc(
|
||||
return pSrc;
|
||||
}
|
||||
|
||||
/* The input list pList is the list of result set terms from a RETURNING
|
||||
** clause. The table that we are returning from is pTab.
|
||||
**
|
||||
** This routine makes a copy of the pList, and at the same time expands
|
||||
** any "*" wildcards to be the complete set of columns from pTab.
|
||||
*/
|
||||
static ExprList *sqlite3ExpandReturning(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprList *pList, /* The arguments to RETURNING */
|
||||
Table *pTab /* The table being updated */
|
||||
){
|
||||
ExprList *pNew = 0;
|
||||
sqlite3 *db = pParse->db;
|
||||
int i;
|
||||
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
Expr *pOldExpr = pList->a[i].pExpr;
|
||||
if( ALWAYS(pOldExpr!=0) && pOldExpr->op==TK_ASTERISK ){
|
||||
int jj;
|
||||
for(jj=0; jj<pTab->nCol; jj++){
|
||||
Expr *pNewExpr;
|
||||
if( IsHiddenColumn(pTab->aCol+jj) ) continue;
|
||||
pNewExpr = sqlite3Expr(db, TK_ID, pTab->aCol[jj].zName);
|
||||
pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
|
||||
if( !db->mallocFailed ){
|
||||
struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
|
||||
pItem->zEName = sqlite3DbStrDup(db, pTab->aCol[jj].zName);
|
||||
pItem->eEName = ENAME_NAME;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Expr *pNewExpr = sqlite3ExprDup(db, pOldExpr, 0);
|
||||
pNew = sqlite3ExprListAppend(pParse, pNew, pNewExpr);
|
||||
if( !db->mallocFailed && ALWAYS(pList->a[i].zEName!=0) ){
|
||||
struct ExprList_item *pItem = &pNew->a[pNew->nExpr-1];
|
||||
pItem->zEName = sqlite3DbStrDup(db, pList->a[i].zEName);
|
||||
pItem->eEName = pList->a[i].eEName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !db->mallocFailed ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
assert( v!=0 );
|
||||
sqlite3VdbeSetNumCols(v, pNew->nExpr);
|
||||
for(i=0; i<pNew->nExpr; i++){
|
||||
sqlite3VdbeSetColName(v, i, COLNAME_NAME, pNew->a[i].zEName,
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate code for the RETURNING trigger. Unlike other triggers
|
||||
** that invoke a subprogram in the bytecode, the code for RETURNING
|
||||
** is generated in-line.
|
||||
*/
|
||||
static void codeReturningTrigger(
|
||||
Parse *pParse, /* Parse context */
|
||||
Trigger *pTrigger, /* The trigger step that defines the RETURNING */
|
||||
Table *pTab, /* The table to code triggers from */
|
||||
int regIn /* The first in an array of registers */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
ExprList *pNew;
|
||||
Returning *pReturning;
|
||||
|
||||
assert( v!=0 );
|
||||
assert( pParse->bReturning );
|
||||
pReturning = pParse->u1.pReturning;
|
||||
assert( pTrigger == &(pReturning->retTrig) );
|
||||
pNew = sqlite3ExpandReturning(pParse, pReturning->pReturnEL, pTab);
|
||||
if( pNew ){
|
||||
NameContext sNC;
|
||||
memset(&sNC, 0, sizeof(sNC));
|
||||
if( pReturning->nRetCol==0 ){
|
||||
pReturning->nRetCol = pNew->nExpr;
|
||||
pReturning->iRetCur = pParse->nTab++;
|
||||
}
|
||||
sNC.pParse = pParse;
|
||||
sNC.uNC.iBaseReg = regIn;
|
||||
sNC.ncFlags = NC_UBaseReg;
|
||||
pParse->eTriggerOp = pTrigger->op;
|
||||
pParse->pTriggerTab = pTab;
|
||||
if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK ){
|
||||
int i;
|
||||
int nCol = pNew->nExpr;
|
||||
int reg = pParse->nMem+1;
|
||||
pParse->nMem += nCol+2;
|
||||
pReturning->iRetReg = reg;
|
||||
for(i=0; i<nCol; i++){
|
||||
sqlite3ExprCodeFactorable(pParse, pNew->a[i].pExpr, reg+i);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, i, reg+i);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, pReturning->iRetCur, reg+i+1);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, pReturning->iRetCur, reg+i, reg+i+1);
|
||||
}
|
||||
sqlite3ExprListDelete(pParse->db, pNew);
|
||||
pParse->eTriggerOp = 0;
|
||||
pParse->pTriggerTab = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Generate VDBE code for the statements inside the body of a single
|
||||
** trigger.
|
||||
@@ -823,6 +977,7 @@ static int codeTriggerProgram(
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0),
|
||||
pParse->eOrconf, 0, 0, 0
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
case TK_INSERT: {
|
||||
@@ -833,6 +988,7 @@ static int codeTriggerProgram(
|
||||
pParse->eOrconf,
|
||||
sqlite3UpsertDup(db, pStep->pUpsert)
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
case TK_DELETE: {
|
||||
@@ -840,6 +996,7 @@ static int codeTriggerProgram(
|
||||
sqlite3TriggerStepSrc(pParse, pStep),
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
default: assert( pStep->op==TK_SELECT ); {
|
||||
@@ -851,9 +1008,6 @@ static int codeTriggerProgram(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pStep->op!=TK_SELECT ){
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1101,7 +1255,7 @@ void sqlite3CodeRowTriggerDirect(
|
||||
** ... ...
|
||||
** reg+N OLD.* value of right-most column of pTab
|
||||
** reg+N+1 NEW.rowid
|
||||
** reg+N+2 OLD.* value of left-most column of pTab
|
||||
** reg+N+2 NEW.* value of left-most column of pTab
|
||||
** ... ...
|
||||
** reg+N+N+1 NEW.* value of right-most column of pTab
|
||||
**
|
||||
@@ -1146,12 +1300,20 @@ void sqlite3CodeRowTrigger(
|
||||
assert( p->pSchema==p->pTabSchema
|
||||
|| p->pSchema==pParse->db->aDb[1].pSchema );
|
||||
|
||||
/* Determine whether we should code this trigger */
|
||||
if( p->op==op
|
||||
/* Determine whether we should code this trigger. One of two choices:
|
||||
** 1. The trigger is an exact match to the current DML statement
|
||||
** 2. This is a RETURNING trigger for INSERT but we are currently
|
||||
** doing the UPDATE part of an UPSERT.
|
||||
*/
|
||||
if( (p->op==op || (p->bReturning && p->op==TK_INSERT && op==TK_UPDATE))
|
||||
&& p->tr_tm==tr_tm
|
||||
&& checkColumnOverlap(p->pColumns, pChanges)
|
||||
){
|
||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||
if( !p->bReturning ){
|
||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||
}else if( sqlite3IsToplevel(pParse) ){
|
||||
codeReturningTrigger(pParse, p, pTab, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1196,13 +1358,18 @@ u32 sqlite3TriggerColmask(
|
||||
|
||||
assert( isNew==1 || isNew==0 );
|
||||
for(p=pTrigger; p; p=p->pNext){
|
||||
if( p->op==op && (tr_tm&p->tr_tm)
|
||||
if( p->op==op
|
||||
&& (tr_tm&p->tr_tm)
|
||||
&& checkColumnOverlap(p->pColumns,pChanges)
|
||||
){
|
||||
TriggerPrg *pPrg;
|
||||
pPrg = getRowTrigger(pParse, p, pTab, orconf);
|
||||
if( pPrg ){
|
||||
mask |= pPrg->aColmask[isNew];
|
||||
if( p->bReturning ){
|
||||
mask = 0xffffffff;
|
||||
}else{
|
||||
TriggerPrg *pPrg;
|
||||
pPrg = getRowTrigger(pParse, p, pTab, orconf);
|
||||
if( pPrg ){
|
||||
mask |= pPrg->aColmask[isNew];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -643,6 +643,7 @@ void sqlite3Update(
|
||||
if( (db->flags&SQLITE_CountRows)!=0
|
||||
&& !pParse->pTriggerTab
|
||||
&& !pParse->nested
|
||||
&& !pParse->bReturning
|
||||
&& pUpsert==0
|
||||
){
|
||||
regRowCount = ++pParse->nMem;
|
||||
@@ -1106,7 +1107,7 @@ void sqlite3Update(
|
||||
** that information.
|
||||
*/
|
||||
if( regRowCount ){
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_ChngCntRow, regRowCount, 1);
|
||||
sqlite3VdbeSetNumCols(v, 1);
|
||||
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
|
||||
}
|
||||
|
||||
+23
-31
@@ -685,7 +685,7 @@ int sqlite3VdbeExec(
|
||||
#endif
|
||||
/*** INSERT STACK UNION HERE ***/
|
||||
|
||||
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
|
||||
sqlite3VdbeEnter(p);
|
||||
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
|
||||
if( db->xProgress ){
|
||||
@@ -1445,6 +1445,26 @@ case OP_IntCopy: { /* out2 */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ChngCntRow P1 P2 * * *
|
||||
** Synopsis: output=r[P1]
|
||||
**
|
||||
** Output value in register P1 as the chance count for a DML statement,
|
||||
** due to the "PRAGMA count_changes=ON" setting. Or, if there was a
|
||||
** foreign key error in the statement, trigger the error now.
|
||||
**
|
||||
** This opcode is a variant of OP_ResultRow that checks the foreign key
|
||||
** immediate constraint count and throws an error if the count is
|
||||
** non-zero. The P2 opcode must be 1.
|
||||
*/
|
||||
case OP_ChngCntRow: {
|
||||
assert( pOp->p2==1 );
|
||||
if( (rc = sqlite3VdbeCheckFk(p,0))!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
/* Fall through to the next case, OP_String */
|
||||
/* no break */ deliberate_fall_through
|
||||
}
|
||||
|
||||
/* Opcode: ResultRow P1 P2 * * *
|
||||
** Synopsis: output=r[P1@P2]
|
||||
**
|
||||
@@ -1461,34 +1481,6 @@ case OP_ResultRow: {
|
||||
assert( pOp->p1>0 );
|
||||
assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
|
||||
|
||||
/* If this statement has violated immediate foreign key constraints, do
|
||||
** not return the number of rows modified. And do not RELEASE the statement
|
||||
** transaction. It needs to be rolled back. */
|
||||
if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
|
||||
assert( db->flags&SQLITE_CountRows );
|
||||
assert( p->usesStmtJournal );
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
|
||||
/* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
|
||||
** DML statements invoke this opcode to return the number of rows
|
||||
** modified to the user. This is the only way that a VM that
|
||||
** opens a statement transaction may invoke this opcode.
|
||||
**
|
||||
** In case this is such a statement, close any statement transaction
|
||||
** opened by this VM before returning control to the user. This is to
|
||||
** ensure that statement-transactions are always nested, not overlapping.
|
||||
** If the open statement-transaction is not closed here, then the user
|
||||
** may step another VM that opens its own statement transaction. This
|
||||
** may lead to overlapping statement transactions.
|
||||
**
|
||||
** The statement transaction is never a top-level transaction. Hence
|
||||
** the RELEASE call below can never fail.
|
||||
*/
|
||||
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
|
||||
rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
|
||||
assert( rc==SQLITE_OK );
|
||||
|
||||
/* Invalidate all ephemeral cursor row caches */
|
||||
p->cacheCtr = (p->cacheCtr + 2)|1;
|
||||
|
||||
@@ -5912,7 +5904,7 @@ case OP_IdxDelete: {
|
||||
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
}else if( pOp->p5 ){
|
||||
rc = SQLITE_CORRUPT_INDEX;
|
||||
rc = sqlite3ReportError(SQLITE_CORRUPT_INDEX, __LINE__, "index corruption");
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
assert( pC->deferredMoveto==0 );
|
||||
@@ -6338,7 +6330,7 @@ case OP_ParseSchema: {
|
||||
if( pOp->p4.z==0 ){
|
||||
sqlite3SchemaClear(db->aDb[iDb].pSchema);
|
||||
db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
|
||||
rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable);
|
||||
rc = sqlite3InitOne(db, iDb, &p->zErrMsg, pOp->p5);
|
||||
db->mDbFlags |= DBFLAG_SchemaChange;
|
||||
p->expired = 0;
|
||||
}else
|
||||
|
||||
+1
-1
@@ -223,7 +223,7 @@ VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
||||
#else
|
||||
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
|
||||
#endif
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*, int, char*, u16);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
|
||||
|
||||
+1
-1
@@ -381,7 +381,7 @@ struct Vdbe {
|
||||
Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
|
||||
Parse *pParse; /* Parsing context used to create this Vdbe */
|
||||
ynVar nVar; /* Number of entries in aVar[] */
|
||||
u32 magic; /* Magic number for sanity checking */
|
||||
u32 iVdbeMagic; /* Magic number defining state of the SQL statement */
|
||||
int nMem; /* Number of memory locations currently allocated */
|
||||
int nCursor; /* Number of slots in apCsr[] */
|
||||
u32 cacheCtr; /* VdbeCursor row cache generation counter */
|
||||
|
||||
+3
-3
@@ -617,7 +617,7 @@ static int sqlite3Step(Vdbe *p){
|
||||
int rc;
|
||||
|
||||
assert(p);
|
||||
if( p->magic!=VDBE_MAGIC_RUN ){
|
||||
if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
|
||||
/* We used to require that sqlite3_reset() be called before retrying
|
||||
** sqlite3_step() after any error or after SQLITE_DONE. But beginning
|
||||
** with version 3.7.0, we changed this so that sqlite3_reset() would
|
||||
@@ -1333,7 +1333,7 @@ static int vdbeUnbind(Vdbe *p, int i){
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
sqlite3_mutex_enter(p->db->mutex);
|
||||
if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
|
||||
if( p->iVdbeMagic!=VDBE_MAGIC_RUN || p->pc>=0 ){
|
||||
sqlite3Error(p->db, SQLITE_MISUSE);
|
||||
sqlite3_mutex_leave(p->db->mutex);
|
||||
sqlite3_log(SQLITE_MISUSE,
|
||||
@@ -1687,7 +1687,7 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
|
||||
*/
|
||||
int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
|
||||
Vdbe *v = (Vdbe*)pStmt;
|
||||
return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
|
||||
return v!=0 && v->iVdbeMagic==VDBE_MAGIC_RUN && v->pc>=0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+19
-18
@@ -35,7 +35,7 @@ Vdbe *sqlite3VdbeCreate(Parse *pParse){
|
||||
p->pNext = db->pVdbe;
|
||||
p->pPrev = 0;
|
||||
db->pVdbe = p;
|
||||
p->magic = VDBE_MAGIC_INIT;
|
||||
p->iVdbeMagic = VDBE_MAGIC_INIT;
|
||||
p->pParse = pParse;
|
||||
pParse->pVdbe = p;
|
||||
assert( pParse->aLabel==0 );
|
||||
@@ -236,7 +236,7 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
|
||||
VdbeOp *pOp;
|
||||
|
||||
i = p->nOp;
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
assert( op>=0 && op<0xff );
|
||||
if( p->nOpAlloc<=i ){
|
||||
return growOp3(p, op, p1, p2, p3);
|
||||
@@ -471,9 +471,10 @@ void sqlite3VdbeExplainPop(Parse *pParse){
|
||||
** The zWhere string must have been obtained from sqlite3_malloc().
|
||||
** This routine will take ownership of the allocated memory.
|
||||
*/
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere, u16 p5){
|
||||
int j;
|
||||
sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
|
||||
sqlite3VdbeChangeP5(p, p5);
|
||||
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
|
||||
sqlite3MayAbort(p->pParse);
|
||||
}
|
||||
@@ -565,7 +566,7 @@ static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){
|
||||
void sqlite3VdbeResolveLabel(Vdbe *v, int x){
|
||||
Parse *p = v->pParse;
|
||||
int j = ADDR(x);
|
||||
assert( v->magic==VDBE_MAGIC_INIT );
|
||||
assert( v->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
assert( j<-p->nLabel );
|
||||
assert( j>=0 );
|
||||
#ifdef SQLITE_DEBUG
|
||||
@@ -890,7 +891,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
|
||||
** Return the address of the next instruction to be inserted.
|
||||
*/
|
||||
int sqlite3VdbeCurrentAddr(Vdbe *p){
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
return p->nOp;
|
||||
}
|
||||
|
||||
@@ -975,7 +976,7 @@ VdbeOp *sqlite3VdbeAddOpList(
|
||||
int i;
|
||||
VdbeOp *pOut, *pFirst;
|
||||
assert( nOp>0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){
|
||||
return 0;
|
||||
}
|
||||
@@ -1299,7 +1300,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
sqlite3 *db;
|
||||
assert( p!=0 );
|
||||
db = p->db;
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
assert( p->aOp!=0 || db->mallocFailed );
|
||||
if( db->mallocFailed ){
|
||||
if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
|
||||
@@ -1428,7 +1429,7 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
|
||||
/* C89 specifies that the constant "dummy" will be initialized to all
|
||||
** zeros, which is correct. MSVC generates a warning, nevertheless. */
|
||||
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
if( addr<0 ){
|
||||
addr = p->nOp - 1;
|
||||
}
|
||||
@@ -2113,7 +2114,7 @@ int sqlite3VdbeList(
|
||||
Op *pOp; /* Current opcode */
|
||||
|
||||
assert( p->explain );
|
||||
assert( p->magic==VDBE_MAGIC_RUN );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_RUN );
|
||||
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
|
||||
|
||||
/* Even though this opcode does not use dynamic strings for
|
||||
@@ -2293,14 +2294,14 @@ void sqlite3VdbeRewind(Vdbe *p){
|
||||
int i;
|
||||
#endif
|
||||
assert( p!=0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT || p->iVdbeMagic==VDBE_MAGIC_RESET );
|
||||
|
||||
/* There should be at least one opcode.
|
||||
*/
|
||||
assert( p->nOp>0 );
|
||||
|
||||
/* Set the magic to VDBE_MAGIC_RUN sooner rather than later. */
|
||||
p->magic = VDBE_MAGIC_RUN;
|
||||
p->iVdbeMagic = VDBE_MAGIC_RUN;
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(i=0; i<p->nMem; i++){
|
||||
@@ -2356,7 +2357,7 @@ void sqlite3VdbeMakeReady(
|
||||
assert( p!=0 );
|
||||
assert( p->nOp>0 );
|
||||
assert( pParse!=0 );
|
||||
assert( p->magic==VDBE_MAGIC_INIT );
|
||||
assert( p->iVdbeMagic==VDBE_MAGIC_INIT );
|
||||
assert( pParse==p->pParse );
|
||||
p->pVList = pParse->pVList;
|
||||
pParse->pVList = 0;
|
||||
@@ -3041,7 +3042,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
** one, or the complete transaction if there is no statement transaction.
|
||||
*/
|
||||
|
||||
if( p->magic!=VDBE_MAGIC_RUN ){
|
||||
if( p->iVdbeMagic!=VDBE_MAGIC_RUN ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( db->mallocFailed ){
|
||||
@@ -3199,7 +3200,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
assert( db->nVdbeRead>=db->nVdbeWrite );
|
||||
assert( db->nVdbeWrite>=0 );
|
||||
}
|
||||
p->magic = VDBE_MAGIC_HALT;
|
||||
p->iVdbeMagic = VDBE_MAGIC_HALT;
|
||||
checkActiveVdbeCnt(db);
|
||||
if( db->mallocFailed ){
|
||||
p->rc = SQLITE_NOMEM_BKPT;
|
||||
@@ -3372,7 +3373,7 @@ int sqlite3VdbeReset(Vdbe *p){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
p->magic = VDBE_MAGIC_RESET;
|
||||
p->iVdbeMagic = VDBE_MAGIC_RESET;
|
||||
return p->rc & db->errMask;
|
||||
}
|
||||
|
||||
@@ -3382,7 +3383,7 @@ int sqlite3VdbeReset(Vdbe *p){
|
||||
*/
|
||||
int sqlite3VdbeFinalize(Vdbe *p){
|
||||
int rc = SQLITE_OK;
|
||||
if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
|
||||
if( p->iVdbeMagic==VDBE_MAGIC_RUN || p->iVdbeMagic==VDBE_MAGIC_HALT ){
|
||||
rc = sqlite3VdbeReset(p);
|
||||
assert( (rc & p->db->errMask)==rc );
|
||||
}
|
||||
@@ -3443,7 +3444,7 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
|
||||
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
|
||||
sqlite3DbFree(db, pSub);
|
||||
}
|
||||
if( p->magic!=VDBE_MAGIC_INIT ){
|
||||
if( p->iVdbeMagic!=VDBE_MAGIC_INIT ){
|
||||
releaseMemArray(p->aVar, p->nVar);
|
||||
sqlite3DbFree(db, p->pVList);
|
||||
sqlite3DbFree(db, p->pFree);
|
||||
@@ -3491,7 +3492,7 @@ void sqlite3VdbeDelete(Vdbe *p){
|
||||
if( p->pNext ){
|
||||
p->pNext->pPrev = p->pPrev;
|
||||
}
|
||||
p->magic = VDBE_MAGIC_DEAD;
|
||||
p->iVdbeMagic = VDBE_MAGIC_DEAD;
|
||||
p->db = 0;
|
||||
sqlite3DbFreeNN(db, p);
|
||||
}
|
||||
|
||||
+3
-2
@@ -489,7 +489,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
|
||||
|
||||
sqlite3VdbeAddOp0(v, OP_Expire);
|
||||
zWhere = sqlite3MPrintf(db, "name=%Q AND sql=%Q", pTab->zName, zStmt);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
|
||||
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere, 0);
|
||||
sqlite3DbFree(db, zStmt);
|
||||
|
||||
iReg = ++pParse->nMem;
|
||||
@@ -660,6 +660,7 @@ static int vtabCallConstructor(
|
||||
zType[i-1] = '\0';
|
||||
}
|
||||
pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
|
||||
pTab->tabFlags |= TF_HasHidden;
|
||||
oooHidden = TF_OOOHidden;
|
||||
}else{
|
||||
pTab->tabFlags |= oooHidden;
|
||||
@@ -828,7 +829,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
|
||||
Table *pNew = sParse.pNewTable;
|
||||
Index *pIdx;
|
||||
pTab->aCol = pNew->aCol;
|
||||
pTab->nCol = pNew->nCol;
|
||||
pTab->nNVCol = pTab->nCol = pNew->nCol;
|
||||
pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
|
||||
pNew->nCol = 0;
|
||||
pNew->aCol = 0;
|
||||
|
||||
+1
-1
@@ -5565,7 +5565,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
#endif
|
||||
pOp = sqlite3VdbeGetOp(v, k);
|
||||
pLastOp = pOp + (last - k);
|
||||
assert( pOp<pLastOp );
|
||||
assert( pOp<pLastOp || (pParse->nErr>0 && pOp==pLastOp) );
|
||||
do{
|
||||
if( pOp->p1!=pLevel->iTabCur ){
|
||||
/* no-op */
|
||||
|
||||
@@ -1743,6 +1743,12 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
SWAP(u8, nBtm, nTop);
|
||||
}
|
||||
|
||||
if( iLevel>0 && (pLoop->wsFlags & WHERE_IN_SEEKSCAN)!=0 ){
|
||||
/* In case OP_SeekScan is used, ensure that the index cursor does not
|
||||
** point to a valid row for the first iteration of this loop. */
|
||||
sqlite3VdbeAddOp1(v, OP_NullRow, iIdxCur);
|
||||
}
|
||||
|
||||
/* Generate code to evaluate all constraint terms using == or IN
|
||||
** and store the values of those terms in an array of registers
|
||||
** starting at regBase.
|
||||
|
||||
+3
-1
@@ -1028,7 +1028,9 @@ static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
|
||||
/*
|
||||
** Select callback for exprUsesSrclist().
|
||||
*/
|
||||
static int exprUsesSrclistSelectCb(Walker *p, Select *pSelect){
|
||||
static int exprUsesSrclistSelectCb(Walker *NotUsed1, Select *NotUsed2){
|
||||
UNUSED_PARAMETER(NotUsed1);
|
||||
UNUSED_PARAMETER(NotUsed2);
|
||||
return WRC_Abort;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,4 +95,21 @@ do_auth_test 1.2 {
|
||||
{SQLITE_UPDATE sqlite_temp_master sql temp {}}
|
||||
}
|
||||
|
||||
do_auth_test 1.3 {
|
||||
ALTER TABLE t2 DROP COLUMN c;
|
||||
} {
|
||||
{SQLITE_FUNCTION {} like {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_drop_column {} {}}
|
||||
{SQLITE_FUNCTION {} sqlite_rename_test {} {}}
|
||||
{SQLITE_READ sqlite_master name main {}}
|
||||
{SQLITE_READ sqlite_master sql main {}}
|
||||
{SQLITE_READ sqlite_master tbl_name main {}}
|
||||
{SQLITE_READ sqlite_master type main {}}
|
||||
{SQLITE_READ sqlite_temp_master name temp {}}
|
||||
{SQLITE_READ sqlite_temp_master sql temp {}}
|
||||
{SQLITE_READ sqlite_temp_master type temp {}}
|
||||
{SQLITE_SELECT {} {} {} {}}
|
||||
{SQLITE_UPDATE sqlite_master sql main {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# 2019-01-11
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix altercorrupt
|
||||
|
||||
database_may_be_corrupt
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 1.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename crash-685346d89b5e5f.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 00 00 00 00 06 .....@ ........
|
||||
| 32: 00 00 63 00 00 05 f0 00 00 00 00 04 10 00 00 04 ..c.............
|
||||
| 48: 00 00 00 00 00 00 0f f0 00 00 00 01 00 00 00 00 ................
|
||||
| 64: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 96: 00 00 00 00 0d 0f f8 00 05 0e cf 00 0f 79 0f d3 .............y..
|
||||
| 112: 0f 2e 0e f3 0e cf 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3776: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 ................
|
||||
| 3792: 05 06 17 11 11 01 31 74 61 62 6c 65 74 34 74 34 ......1tablet4t4
|
||||
| 3808: 06 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 34 .CREATE TABLE t4
|
||||
| 3824: 28 7a 29 39 04 06 17 11 11 01 5f 74 61 62 6c 65 (z)9......_table
|
||||
| 3840: 74 33 74 33 05 43 52 45 41 54 45 20 54 41 42 4c t3t3.CREATE TABL
|
||||
| 3856: 45 20 74 33 28 78 20 49 4e 54 45 47 45 52 20 50 E t3(x INTEGER P
|
||||
| 3872: 52 49 4d 41 52 59 20 4b 45 59 2c 20 79 29 49 03 RIMARY KEY, y)I.
|
||||
| 3888: 06 17 11 11 01 7f 74 61 62 6c 65 74 32 74 32 04 ......tablet2t2.
|
||||
| 3904: 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 32 28 CREATE TABLE t2(
|
||||
| 3920: 61 2c 62 2c 63 20 50 52 49 4d 41 52 59 20 4b 45 a,b,c PRIMARY KE
|
||||
| 3936: 59 2c 20 64 2c 20 65 2c 20 66 29 20 57 49 54 48 Y, d, e, f) WITH
|
||||
| 3952: 4f 55 54 20 52 4f 57 49 44 58 03 07 17 11 11 01 OUT ROWIDX......
|
||||
| 3968: 81 1b 74 61 62 6c 65 74 31 74 31 02 43 52 45 41 ..tablet1t1.CREA
|
||||
| 3984: 54 45 20 54 41 42 4c 45 20 74 31 28 61 2c 62 2c TE TABLE t1(a,b,
|
||||
| 4000: 63 20 41 53 20 28 2d 62 29 20 56 49 52 54 55 41 c AS (-b) VIRTUA
|
||||
| 4016: 4c 2c 64 20 43 48 45 43 4b 28 64 3e 35 29 2c 65 L,d CHECK(d>5),e
|
||||
| 4032: 20 55 4e 49 51 55 45 2c 20 66 20 41 53 20 28 2b UNIQUE, f AS (+
|
||||
| 4048: 62 29 29 23 02 06 17 37 11 01 00 69 6e 64 65 78 b))#...7...index
|
||||
| 4064: 73 71 6c 69 74 65 5f 61 75 74 6f 69 6e 64 65 78 sqlite_autoindex
|
||||
| 4080: 5f 74 31 5f 31 74 31 03 00 00 00 08 00 00 00 00 _t1_1t1.........
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 0a 0f 93 00 0f f6 0f eb 0f e0 0f d5 ................
|
||||
| 16: 0f ca 0f 8f 0f b4 0f a9 0f 9e 0f 93 00 00 00 00 ................
|
||||
| 3984: 00 00 00 09 0a 05 01 01 01 01 0a 64 6e 14 09 09 ...........dn...
|
||||
| 4000: 05 01 01 01 01 09 5a 6d 12 09 08 05 01 01 01 01 ......Zm........
|
||||
| 4016: 08 50 6c 10 09 07 05 01 01 01 01 07 46 6b 0e 09 .Pl.........Fk..
|
||||
| 4032: 06 05 01 01 01 01 06 3c 6a 0c 09 05 05 01 01 01 .......<j.......
|
||||
| 4048: 01 05 32 69 0a 09 04 05 01 01 01 01 04 28 68 08 ..2i.........(h.
|
||||
| 4064: 09 03 05 01 01 01 01 03 1e 67 06 09 02 05 01 01 .........g......
|
||||
| 4080: 01 01 02 14 66 04 08 01 05 09 01 01 01 0a 65 02 ....f.........e.
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 0a 0f c5 00 0f fb 0f f5 0f ef 0f e9 ................
|
||||
| 16: 0f e3 0f dd 0f d7 0f d1 0f cb 0f c5 00 00 00 00 ................
|
||||
| 4032: 00 00 00 00 00 05 03 01 01 14 0a 05 03 01 01 12 ................
|
||||
| 4048: 09 05 03 01 01 10 08 05 03 01 01 0e 07 05 03 01 ................
|
||||
| 4064: 01 0c 06 05 03 01 01 0a 05 05 03 01 01 08 04 05 ................
|
||||
| 4080: 03 01 01 06 03 05 03 01 01 04 02 04 03 01 09 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0a 00 00 00 0a 0f 75 00 0f 75 0f 83 0f 91 0f 9f ......u..u......
|
||||
| 16: 0f ad 0f bb 0f 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 3952: 00 00 00 00 00 0d 07 01 01 01 01 01 01 9c 0a 64 ...............d
|
||||
| 3968: 6e 14 64 0d 07 02 01 01 01 01 01 a6 09 5a 6d 12 n.d..........Zm.
|
||||
| 3984: 5a 0d 07 01 01 01 01 01 01 b0 08 50 6c 10 50 0d Z..........Pl.P.
|
||||
| 4000: 07 01 01 01 01 01 01 ba 07 46 6b 0e 46 0d 07 01 .........Fk.F...
|
||||
| 4016: 01 01 01 01 01 c4 06 3c 6a 0c 3c 0d 07 01 01 01 .......<j.<.....
|
||||
| 4032: 01 01 01 ce 05 32 69 0a 32 0d 07 01 01 01 01 01 .....2i.2.......
|
||||
| 4048: 01 d8 04 28 68 08 28 0d 07 01 01 01 01 01 01 e2 ...(h.(.........
|
||||
| 4064: 03 1e 67 06 1e 0d 07 01 01 01 01 01 01 ec 02 14 ..g.............
|
||||
| 4080: 66 04 14 0c 07 01 09 01 01 01 01 f6 0a 65 02 0a f............e..
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 03 0f e9 00 0f e9 0f fb 0f f6 00 00 ................
|
||||
| 16: 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 00 03 ff ff ff ff ff ff ................
|
||||
| 4080: ff ff 9c 03 00 00 03 64 03 00 00 03 01 03 00 00 .......d........
|
||||
| page 6 offset 20480
|
||||
| 0: 0d 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 ................
|
||||
| end crash-685346d89b5e5f.db
|
||||
}]} {}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
ALTER TABLE t2 DROP COLUMN e;
|
||||
}
|
||||
do_catchsql_test 1.2 {
|
||||
ALTER TABLE t1 DROP COLUMN f;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,258 @@
|
||||
# 2021 February 16
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix alterdropcol
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE VIEW v1 AS SELECT * FROM t1;
|
||||
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY, y, z UNIQUE);
|
||||
CREATE INDEX t2y ON t2(y);
|
||||
|
||||
CREATE TABLE t3(q, r, s);
|
||||
CREATE INDEX t3rs ON t3(r+s);
|
||||
}
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
ALTER TABLE nosuch DROP COLUMN z;
|
||||
} {1 {no such table: nosuch}}
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
ALTER TABLE v1 DROP COLUMN c;
|
||||
} {1 {cannot drop column from view "v1"}}
|
||||
|
||||
ifcapable fts5 {
|
||||
do_execsql_test 1.3.1 {
|
||||
CREATE VIRTUAL TABLE ft1 USING fts5(one, two);
|
||||
}
|
||||
do_catchsql_test 1.3.2 {
|
||||
ALTER TABLE ft1 DROP COLUMN two;
|
||||
} {1 {cannot drop column from virtual table "ft1"}}
|
||||
}
|
||||
|
||||
do_catchsql_test 1.4 {
|
||||
ALTER TABLE sqlite_schema DROP COLUMN sql;
|
||||
} {1 {table sqlite_master may not be altered}}
|
||||
|
||||
do_catchsql_test 1.5 {
|
||||
ALTER TABLE t1 DROP COLUMN d;
|
||||
} {1 {no such column: "d"}}
|
||||
|
||||
do_execsql_test 1.6.1 {
|
||||
ALTER TABLE t1 DROP COLUMN b;
|
||||
}
|
||||
do_execsql_test 1.6.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE name = 't1'
|
||||
} {{CREATE TABLE t1(a, c)}}
|
||||
|
||||
do_execsql_test 1.7.1 {
|
||||
ALTER TABLE t1 DROP COLUMN c;
|
||||
}
|
||||
do_execsql_test 1.7.2 {
|
||||
SELECT sql FROM sqlite_schema WHERE name = 't1'
|
||||
} {{CREATE TABLE t1(a)}}
|
||||
|
||||
do_catchsql_test 1.7.3 {
|
||||
ALTER TABLE t1 DROP COLUMN a;
|
||||
} {1 {cannot drop column "a": no other columns exist}}
|
||||
|
||||
|
||||
do_catchsql_test 1.8 {
|
||||
ALTER TABLE t2 DROP COLUMN z
|
||||
} {1 {cannot drop UNIQUE column: "z"}}
|
||||
|
||||
do_catchsql_test 1.9 {
|
||||
ALTER TABLE t2 DROP COLUMN x
|
||||
} {1 {cannot drop PRIMARY KEY column: "x"}}
|
||||
|
||||
do_catchsql_test 1.10 {
|
||||
ALTER TABLE t2 DROP COLUMN y
|
||||
} {1 {error in index t2y after drop column: no such column: y}}
|
||||
|
||||
do_catchsql_test 1.11 {
|
||||
ALTER TABLE t3 DROP COLUMN s
|
||||
} {1 {error in index t3rs after drop column: no such column: s}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
foreach {tn wo} {
|
||||
1 {}
|
||||
2 {WITHOUT ROWID}
|
||||
} { eval [string map [list %TN% $tn %WO% $wo] {
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.%TN%.0 {
|
||||
CREATE TABLE t1(x, y INTEGER PRIMARY KEY, z) %WO% ;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
}
|
||||
|
||||
do_execsql_test 2.%TN%.1 {
|
||||
ALTER TABLE t1 DROP COLUMN x;
|
||||
SELECT * FROM t1;
|
||||
} {
|
||||
2 3 5 6 8 9
|
||||
}
|
||||
do_execsql_test 2.%TN%.2 {
|
||||
ALTER TABLE t1 DROP COLUMN z;
|
||||
SELECT * FROM t1;
|
||||
} {
|
||||
2 5 8
|
||||
}
|
||||
}]}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t12(a, b, c, CHECK(c>10));
|
||||
CREATE TABLE t13(a, b, c CHECK(c>10));
|
||||
}
|
||||
do_catchsql_test 3.1 {
|
||||
ALTER TABLE t12 DROP COLUMN c;
|
||||
} {1 {error in table t12 after drop column: no such column: c}}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
ALTER TABLE t13 DROP COLUMN c;
|
||||
} {0 {}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that generated columns can be dropped. And that other columns from
|
||||
# tables that contain generated columns can be dropped.
|
||||
#
|
||||
foreach {tn wo vs} {
|
||||
1 "" ""
|
||||
2 "" VIRTUAL
|
||||
3 "" STORED
|
||||
4 "WITHOUT ROWID" STORED
|
||||
5 "WITHOUT ROWID" VIRTUAL
|
||||
} {
|
||||
reset_db
|
||||
|
||||
do_execsql_test 4.$tn.0 "
|
||||
CREATE TABLE 'my table'(a, b PRIMARY KEY, c AS (a+b) $vs, d) $wo
|
||||
"
|
||||
do_execsql_test 4.$tn.1 {
|
||||
INSERT INTO "my table"(a, b, d) VALUES(1, 2, 'hello');
|
||||
INSERT INTO "my table"(a, b, d) VALUES(3, 4, 'world');
|
||||
|
||||
SELECT * FROM "my table"
|
||||
} {
|
||||
1 2 3 hello
|
||||
3 4 7 world
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.2 {
|
||||
ALTER TABLE "my table" DROP COLUMN c;
|
||||
}
|
||||
do_execsql_test 4.$tn.3 {
|
||||
SELECT * FROM "my table"
|
||||
} {
|
||||
1 2 hello
|
||||
3 4 world
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.4 "
|
||||
CREATE TABLE x1(a, b, c PRIMARY KEY, d AS (b+c) $vs, e) $wo
|
||||
"
|
||||
do_execsql_test 4.$tn.5 {
|
||||
INSERT INTO x1(a, b, c, e) VALUES(1, 2, 3, 4);
|
||||
INSERT INTO x1(a, b, c, e) VALUES(5, 6, 7, 8);
|
||||
INSERT INTO x1(a, b, c, e) VALUES(9, 10, 11, 12);
|
||||
SELECT * FROM x1;
|
||||
} {
|
||||
1 2 3 5 4
|
||||
5 6 7 13 8
|
||||
9 10 11 21 12
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.6 {
|
||||
ALTER TABLE x1 DROP COLUMN a
|
||||
}
|
||||
do_execsql_test 4.$tn.7 {
|
||||
SELECT * FROM x1
|
||||
} {
|
||||
2 3 5 4
|
||||
6 7 13 8
|
||||
10 11 21 12
|
||||
}
|
||||
do_execsql_test 4.$tn.8 {
|
||||
ALTER TABLE x1 DROP COLUMN e
|
||||
}
|
||||
do_execsql_test 4.$tn.9 {
|
||||
SELECT * FROM x1
|
||||
} {
|
||||
2 3 5
|
||||
6 7 13
|
||||
10 11 21
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE p1(a PRIMARY KEY, b UNIQUE);
|
||||
CREATE TABLE c1(x, y, z REFERENCES p1(c));
|
||||
CREATE TABLE c2(x, y, z, w REFERENCES p1(b));
|
||||
}
|
||||
do_execsql_test 5.1 {
|
||||
ALTER TABLE c1 DROP COLUMN z;
|
||||
ALTER TABLE c2 DROP COLUMN z;
|
||||
SELECT sql FROM sqlite_schema WHERE name IN ('c1', 'c2');
|
||||
} {
|
||||
{CREATE TABLE c1(x, y)}
|
||||
{CREATE TABLE c2(x, y, w REFERENCES p1(b))}
|
||||
}
|
||||
|
||||
do_execsql_test 5.2.1 {
|
||||
CREATE VIEW v1 AS SELECT d, e FROM p1
|
||||
}
|
||||
do_catchsql_test 5.2.2 {
|
||||
ALTER TABLE c1 DROP COLUMN x
|
||||
} {1 {error in view v1: no such column: d}}
|
||||
do_execsql_test 5.3.1 {
|
||||
DROP VIEW v1;
|
||||
CREATE VIEW v1 AS SELECT x, y FROM c1;
|
||||
}
|
||||
do_catchsql_test 5.3.2 {
|
||||
ALTER TABLE c1 DROP COLUMN x
|
||||
} {1 {error in view v1 after drop column: no such column: x}}
|
||||
|
||||
do_execsql_test 5.4.1 {
|
||||
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
|
||||
INSERT INTO p1 VALUES(new.y, new.xyz);
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 5.4.2 {
|
||||
ALTER TABLE c1 DROP COLUMN y
|
||||
} {1 {error in trigger tr: no such column: new.xyz}}
|
||||
do_execsql_test 5.5.1 {
|
||||
DROP TRIGGER tr;
|
||||
CREATE TRIGGER tr AFTER INSERT ON c1 BEGIN
|
||||
INSERT INTO p1 VALUES(new.y, new.z);
|
||||
END;
|
||||
}
|
||||
do_catchsql_test 5.5.2 {
|
||||
ALTER TABLE c1 DROP COLUMN y
|
||||
} {1 {error in trigger tr: no such column: new.z}}
|
||||
|
||||
finish_test
|
||||
@@ -40,7 +40,7 @@ do_execsql_test 1.1 {
|
||||
# slightly different - it rejects the change and rolls back the transaction.
|
||||
do_catchsql_test 1.2 {
|
||||
ALTER TABLE t1 RENAME TO t1new;
|
||||
} {1 {no such column: t1.a}}
|
||||
} {1 {error in table t1new after rename: no such column: t1.a}}
|
||||
|
||||
do_execsql_test 1.3 {
|
||||
CREATE TABLE t3(c, d);
|
||||
@@ -59,7 +59,7 @@ do_execsql_test 1.4 {
|
||||
|
||||
do_catchsql_test 1.3 {
|
||||
ALTER TABLE t2 RENAME TO t2new;
|
||||
} {1 {no such column: t2.b}}
|
||||
} {1 {error in index t2expr after rename: no such column: t2.b}}
|
||||
do_execsql_test 1.4 {
|
||||
SELECT sql FROM sqlite_master
|
||||
} {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# 2021 February 18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#*************************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set testprefix altermalloc3
|
||||
|
||||
# If SQLITE_OMIT_ALTERTABLE is defined, omit this file.
|
||||
ifcapable !altertable {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c, d, PRIMARY KEY(d, b)) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 2, 3, 4);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql { ALTER TABLE t1 DROP COLUMN c }
|
||||
} -test {
|
||||
faultsim_test_result {0 {}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+3
-2
@@ -144,7 +144,7 @@ do_test backup2-9 {
|
||||
if {$tcl_platform(platform)=="windows"} {
|
||||
set msg {cannot open source database: unable to open database file}
|
||||
} elseif {[string match *BSD $tcl_platform(os)]} {
|
||||
set msg {restore failed: file is not a database}
|
||||
set msg {}
|
||||
} else {
|
||||
set msg {cannot open source database: disk I/O error}
|
||||
}
|
||||
@@ -152,7 +152,8 @@ do_test backup2-10 {
|
||||
forcedelete bu3.db
|
||||
file mkdir bu3.db
|
||||
set rc [catch {db restore temp bu3.db} res]
|
||||
lappend rc $res
|
||||
if {[string match *BSD $tcl_platform(os)]} { set res "" }
|
||||
list $rc $res
|
||||
} [list 1 $msg]
|
||||
|
||||
# Try to restore from something that is not a database file.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2021 February 15
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the sqlite3_column_count() API.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix columncount
|
||||
|
||||
proc do_ccsql_test {tn sql res} {
|
||||
|
||||
uplevel [list do_test $tn [subst -nocommands {
|
||||
set stmt [sqlite3_prepare_v2 db {$sql} -1 dummy]
|
||||
set res [sqlite3_column_count [set stmt]]
|
||||
while {[sqlite3_step [set stmt]]=="SQLITE_ROW"} {
|
||||
for {set i 0} {[set i] < [sqlite3_data_count [set stmt]]} {incr i} {
|
||||
lappend res [sqlite3_column_text [set stmt] [set i]]
|
||||
}
|
||||
}
|
||||
|
||||
set rc [sqlite3_finalize [set stmt]]
|
||||
if {[set rc]!="SQLITE_OK"} {
|
||||
error [sqlite3_errmsg db]
|
||||
}
|
||||
|
||||
set res
|
||||
}] [list {*}$res]]
|
||||
|
||||
}
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y, z);
|
||||
INSERT INTO t1 VALUES('a', 'b', 'c');
|
||||
}
|
||||
|
||||
do_ccsql_test 1.1 { SELECT * FROM t1 } {3 a b c}
|
||||
do_ccsql_test 1.2 { CREATE TABLE t2(a, b) } {0}
|
||||
|
||||
do_ccsql_test 1.3 { ALTER TABLE t2 RENAME TO t3 } {0}
|
||||
do_ccsql_test 1.4 { ALTER TABLE t3 RENAME b TO ccc } {0}
|
||||
do_ccsql_test 1.5 { ALTER TABLE t3 ADD COLUMN d } {0}
|
||||
|
||||
do_ccsql_test 1.6 { DROP TABLE t3 } {0}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -29,7 +29,7 @@ do_execsql_test 1.0 {
|
||||
|
||||
do_catchsql_test 1.1 {
|
||||
ALTER TABLE t1_content RENAME c0a TO docid;
|
||||
} {1 {duplicate column name: docid}}
|
||||
} {1 {error in table t1_content after rename: duplicate column name: docid}}
|
||||
|
||||
do_catchsql_test 1.2 {
|
||||
UPDATE t1 SET Col0 = 1 ;
|
||||
|
||||
+4
-4
@@ -202,11 +202,11 @@ do_execsql_test func7-mysql-210 {
|
||||
#} {0.6931472 NULL}
|
||||
# log() means natural logarithm in MySQL
|
||||
do_execsql_test func7-mysql-230 {
|
||||
SELECT log(2,65536), log(10,100), quote(log(1,100));
|
||||
} {16.0 2.0 Inf}
|
||||
SELECT log(2,65536), log(10,100), quote(log(1,100)), quote(log(0,100));
|
||||
} {16.0 2.0 NULL NULL}
|
||||
do_execsql_test func7-mysql-240 {
|
||||
SELECT log2(65536), quote(log2(-100));
|
||||
} {16.0 NULL}
|
||||
SELECT log2(65536), quote(log2(-100)), quote(log2(0));
|
||||
} {16.0 NULL NULL}
|
||||
do_execsql_test func7-mysql-250 {
|
||||
SELECT round(log10(2),7), log10(100), quote(log10(-100));
|
||||
} {0.30103 2.0 NULL}
|
||||
|
||||
Binary file not shown.
@@ -956,5 +956,63 @@ ifcapable analyze {
|
||||
}]
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that the pre-update hook is fired for INSERT statements that use
|
||||
# the xfer optimization on without rowid tables.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 12.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
INSERT INTO t2 VALUES(5, 6);
|
||||
INSERT INTO t2 VALUES(7, 8);
|
||||
|
||||
CREATE TABLE t3 (a INTEGER PRIMARY KEY, b) WITHOUT ROWID;
|
||||
}
|
||||
|
||||
db preupdate hook preupdate_cb
|
||||
db update_hook update_cb
|
||||
|
||||
proc preupdate_cb {args} { lappend ::res "preupdate" $args }
|
||||
proc update_cb {args} { lappend ::res "update" $args }
|
||||
|
||||
set ::res [list]
|
||||
do_test 12.2 {
|
||||
execsql VACUUM
|
||||
set ::res
|
||||
} {}
|
||||
|
||||
do_test 12.3 {
|
||||
set ::res [list]
|
||||
execsql { INSERT INTO t3 SELECT a, b FROM t2 }
|
||||
set ::res
|
||||
} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
|
||||
|
||||
do_test 12.4 {
|
||||
execsql { DELETE FROM t3 }
|
||||
set ::res [list]
|
||||
execsql { INSERT INTO t3 SELECT * FROM t2 }
|
||||
set ::res
|
||||
} {preupdate {INSERT main t3 0 0} preupdate {INSERT main t3 0 0}}
|
||||
|
||||
do_execsql_test 12.5 {
|
||||
CREATE TABLE t4(a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID;
|
||||
INSERT INTO t4 VALUES('abc', 1);
|
||||
INSERT INTO t4 VALUES('DEF', 2);
|
||||
}
|
||||
|
||||
set ::res [list]
|
||||
do_test 12.6 {
|
||||
execsql VACUUM
|
||||
set ::res
|
||||
} {}
|
||||
|
||||
do_catchsql_test 12.6 {
|
||||
INSERT INTO t4 VALUES('def', 3);
|
||||
} {1 {UNIQUE constraint failed: t4.a}}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -365,4 +365,32 @@ ifcapable rtree {
|
||||
} {{} 1 {} {} {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 8.0 {
|
||||
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
|
||||
CREATE UNIQUE INDEX t1y ON t1(y);
|
||||
INSERT INTO t1 VALUES(111, 'AAA'),(222, 'BBB'),(333, 'CCC');
|
||||
CREATE TABLE t2(z);
|
||||
INSERT INTO t2 VALUES('BBB'),('AAA');
|
||||
ANALYZE sqlite_schema;
|
||||
INSERT INTO sqlite_stat1 VALUES('t1', 't1y','100 1');
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y;
|
||||
} {222 111}
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND +t1.x IN (111, 222);
|
||||
} {222 111}
|
||||
|
||||
do_execsql_test 8.3 {
|
||||
SELECT t1.x FROM t2 CROSS JOIN t1 WHERE t2.z = t1.y AND t1.x IN (111, 222);
|
||||
} {222 111}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -1131,4 +1131,11 @@ do_execsql_test 17.1 {
|
||||
SELECT id FROM t1 WHERE x LIKE 'abc__' ESCAPE '_';
|
||||
} {2}
|
||||
|
||||
# 2021-02-15 ticket c0aeea67d58ae0fd
|
||||
#
|
||||
do_execsql_test 17.1 {
|
||||
SELECT 'x' LIKE '%' ESCAPE '_';
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -289,7 +289,7 @@ if {$tcl_platform(os)=="Darwin"} {
|
||||
array set ::Platforms [strip_comments {
|
||||
Linux-x86_64 {
|
||||
"Check-Symbols*" checksymbols
|
||||
"Fast-One" "fuzztest test"
|
||||
"Fast-One" "QUICKTEST_INCLUDE=rbu.test fuzztest test"
|
||||
"Debug-One" "mptest test"
|
||||
"Debug-Two" "test"
|
||||
"Have-Not" test
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# 2021-01-28
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is the new RETURNING clause
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix returning1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c DEFAULT 'pax');
|
||||
INSERT INTO t1(b) VALUES(10),('happy'),(NULL) RETURNING a,b,c;
|
||||
} {1 10 pax 2 happy pax 3 {} pax}
|
||||
do_execsql_test 1.1 {
|
||||
SELECT * FROM t1;
|
||||
} {1 10 pax 2 happy pax 3 {} pax}
|
||||
do_execsql_test 1.2 {
|
||||
INSERT INTO t1(b,c) VALUES(5,99) RETURNING b,c,a,rowid;
|
||||
} {5 99 4 4}
|
||||
do_execsql_test 1.3 {
|
||||
SELECT * FROM t1;
|
||||
} {1 10 pax 2 happy pax 3 {} pax 4 5 99}
|
||||
do_execsql_test 1.4 {
|
||||
INSERT INTO t1 DEFAULT VALUES RETURNING *;
|
||||
} {5 {} pax}
|
||||
do_execsql_test 1.5 {
|
||||
SELECT * FROM t1;
|
||||
} {1 10 pax 2 happy pax 3 {} pax 4 5 99 5 {} pax}
|
||||
do_execsql_test 1.6 {
|
||||
CREATE TABLE t2(x,y,z);
|
||||
INSERT INTO t2 VALUES(11,12,13),(21,'b','c'),(31,'b-value',4.75);
|
||||
}
|
||||
do_execsql_test 1.7 {
|
||||
INSERT INTO t1 SELECT * FROM t2 RETURNING *;
|
||||
} {11 12 13 21 b c 31 b-value 4.75}
|
||||
do_execsql_test 1.8 {
|
||||
SELECT *, '|' FROM t1;
|
||||
} {1 10 pax | 2 happy pax | 3 {} pax | 4 5 99 | 5 {} pax | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||
|
||||
do_execsql_test 2.1 {
|
||||
UPDATE t1 SET c='bellum' WHERE c='pax' RETURNING rowid, b, '|';
|
||||
} {1 10 | 2 happy | 3 {} | 5 {} |}
|
||||
do_execsql_test 2.2 {
|
||||
SELECT *, '|' FROM t1;
|
||||
} {1 10 bellum | 2 happy bellum | 3 {} bellum | 4 5 99 | 5 {} bellum | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
DELETE FROM t1 WHERE c='bellum' RETURNING rowid, *, '|';
|
||||
} {1 1 10 bellum | 2 2 happy bellum | 3 3 {} bellum | 5 5 {} bellum |}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT *, '|' FROM t1;
|
||||
} {4 5 99 | 11 12 13 | 21 b c | 31 b-value 4.75 |}
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE t4(a INT, b INT DEFAULT 1234, c INT DEFAULT -16);
|
||||
CREATE UNIQUE INDEX t4a ON t4(a);
|
||||
INSERT INTO t4(a,b,c) VALUES(1,2,3);
|
||||
} {}
|
||||
do_execsql_test 4.2 {
|
||||
INSERT INTO t4(a,b,c) VALUES(1,22,33)
|
||||
ON CONFLICT(a) DO UPDATE SET b=44
|
||||
RETURNING *;
|
||||
} {1 44 3}
|
||||
do_execsql_test 4.3 {
|
||||
SELECT * FROM t4;
|
||||
} {1 44 3}
|
||||
do_execsql_test 4.4 {
|
||||
DELETE FROM t4;
|
||||
INSERT INTO t4 VALUES(1,2,3),(4,5,6),(7,8,9);
|
||||
} {}
|
||||
do_execsql_test 4.5 {
|
||||
INSERT INTO t4(a,b,c) VALUES(2,3,4),(4,5,6),(5,6,7)
|
||||
ON CONFLICT(a) DO UPDATE SET b=100
|
||||
RETURNING *, '|';
|
||||
} {2 3 4 | 4 100 6 | 5 6 7 |}
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -644,4 +644,17 @@ do_catchsql_test 27.10 {
|
||||
INSERT INTO t0(c0) VALUES(0) ON CONFLICT(c0) DO UPDATE SET c0 = 3;
|
||||
} {1 {ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint}}
|
||||
|
||||
# 2021-02-03
|
||||
# https://bugs.chromium.org/p/chromium/issues/detail?id=1173511
|
||||
# Faulty assert() statement.
|
||||
#
|
||||
reset_db
|
||||
do_catchsql_test 28.10 {
|
||||
CREATE TABLE t0(c0 PRIMARY KEY, c1);
|
||||
CREATE TRIGGER trigger0 BEFORE DELETE ON t0 BEGIN
|
||||
SELECT (SELECT c0,c1 FROM t0) FROM t0;
|
||||
END ;
|
||||
DELETE FROM t0;
|
||||
} {1 {sub-select returns 2 columns - expected 1}}
|
||||
|
||||
finish_test
|
||||
|
||||
+6
-6
@@ -1167,17 +1167,17 @@ do_execsql_test select1-18.4 {
|
||||
|
||||
# 2019-12-17 gramfuzz find
|
||||
#
|
||||
do_execsql_test select-19.10 {
|
||||
do_execsql_test select1-19.10 {
|
||||
DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1(x);
|
||||
} {}
|
||||
do_catchsql_test select-19.20 {
|
||||
do_catchsql_test select1-19.20 {
|
||||
INSERT INTO t1
|
||||
SELECT 1,2,3,4,5,6,7
|
||||
UNION ALL SELECT 1,2,3,4,5,6,7
|
||||
ORDER BY 1;
|
||||
} {1 {table t1 has 1 columns but 7 values were supplied}}
|
||||
do_catchsql_test select-19.21 {
|
||||
do_catchsql_test select1-19.21 {
|
||||
INSERT INTO t1
|
||||
SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
||||
UNION ALL SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
|
||||
@@ -1187,7 +1187,7 @@ do_catchsql_test select-19.21 {
|
||||
# 2020-01-01 Found by Yongheng's fuzzer
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test select-20.10 {
|
||||
do_execsql_test select1-20.10 {
|
||||
CREATE TABLE t1 (
|
||||
a INTEGER PRIMARY KEY,
|
||||
b AS('Y') UNIQUE
|
||||
@@ -1197,13 +1197,13 @@ do_execsql_test select-20.10 {
|
||||
WHERE ((SELECT t1.a FROM t1 AS x GROUP BY b) AND b=0)
|
||||
OR a = 10;
|
||||
} {10 Y}
|
||||
do_execsql_test select-20.20 {
|
||||
do_execsql_test select1-20.20 {
|
||||
SELECT ifnull(a, max((SELECT 123))), count(a) FROM t1 ;
|
||||
} {10 1}
|
||||
|
||||
# 2020-10-02 dbsqlfuzz find
|
||||
reset_db
|
||||
do_execsql_test select-21.1 {
|
||||
do_execsql_test select1-21.1 {
|
||||
CREATE TABLE t1(a IMTEGES PRIMARY KEY,R);
|
||||
CREATE TABLE t2(x UNIQUE);
|
||||
CREATE VIEW v1a(z,y) AS SELECT x IS NULL, x FROM t2;
|
||||
|
||||
+2
-2
@@ -908,8 +908,8 @@ proc catchcmdex {db {cmd ""}} {
|
||||
proc filepath_normalize {p} {
|
||||
# test cases should be written to assume "unix"-like file paths
|
||||
if {$::tcl_platform(platform)!="unix"} {
|
||||
# lreverse*2 as a hack to remove any unneeded {} after the string map
|
||||
lreverse [lreverse [string map {\\ /} [regsub -nocase -all {[a-z]:[/\\]+} $p {/}]]]
|
||||
string map [list \\ / \{/ / .db\} .db] \
|
||||
[regsub -nocase -all {[a-z]:[/\\]+} $p {/}]
|
||||
} {
|
||||
set p
|
||||
}
|
||||
|
||||
@@ -326,4 +326,33 @@ do_execsql_test 5.20 {
|
||||
SELECT *, '+' FROM t1 LEFT JOIN t3 ON (a NOT IN(SELECT v FROM t1 LEFT JOIN t2 ON (a=k))=k);
|
||||
} {0 {} {} {} + 1 one {} {} + 2 two {} {} + 5 five {} {} + 3 three {} {} + 6 six {} {} +}
|
||||
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1 VALUES(1,2);
|
||||
CREATE TABLE t2(a,b);
|
||||
INSERT INTO t2 VALUES(3,4);
|
||||
|
||||
CREATE TABLE t3(a,b);
|
||||
INSERT INTO t3 VALUES(5,6);
|
||||
CREATE TABLE t4(a,b);
|
||||
INSERT INTO t4 VALUES(7,8);
|
||||
|
||||
CREATE TABLE t5(a,b);
|
||||
INSERT INTO t5 VALUES(9,10);
|
||||
}
|
||||
|
||||
do_execsql_test 6.1 {
|
||||
WITH x(c) AS (
|
||||
SELECT 1000 FROM t1 UNION ALL SELECT 800 FROM t2
|
||||
),
|
||||
y(d) AS (
|
||||
SELECT 100 FROM t3 UNION ALL SELECT 400 FROM t4
|
||||
)
|
||||
SELECT * FROM t5, x, y;
|
||||
} {
|
||||
9 10 1000 100 9 10 1000 400
|
||||
9 10 800 100 9 10 800 400
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+17
-5
@@ -94,21 +94,31 @@ ifcapable {update_delete_limit} {
|
||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5}
|
||||
execsql {SELECT count(*) FROM t1}
|
||||
} {15}
|
||||
create_test_data 4
|
||||
do_test wherelimit-1.3b {
|
||||
# limit 5
|
||||
execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x, y LIMIT 5}
|
||||
} {1 1 | 1 2 | 1 3 | 1 4 | 2 1 |}
|
||||
do_test wherelimit-1.3c {
|
||||
execsql {SELECT count(*) FROM t1}
|
||||
} {11}
|
||||
do_test wherelimit-1.4 {
|
||||
# limit 5, offset 2
|
||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET 2}
|
||||
execsql {DELETE FROM t1 RETURNING x, y, '|' ORDER BY x LIMIT 5 OFFSET 2}
|
||||
} {2 4 | 3 1 | 3 2 | 3 3 | 3 4 |}
|
||||
do_test wherelimit-1.4cnt {
|
||||
execsql {SELECT count(*) FROM t1}
|
||||
} {10}
|
||||
} {6}
|
||||
do_test wherelimit-1.5 {
|
||||
# limit 5, offset -2
|
||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 5 OFFSET -2}
|
||||
execsql {SELECT count(*) FROM t1}
|
||||
} {5}
|
||||
} {1}
|
||||
do_test wherelimit-1.6 {
|
||||
# limit -5 (no limit), offset 2
|
||||
execsql {DELETE FROM t1 ORDER BY x LIMIT 2, -5}
|
||||
execsql {SELECT count(*) FROM t1}
|
||||
} {2}
|
||||
} {1}
|
||||
do_test wherelimit-1.7 {
|
||||
# limit 5, offset -2 (no offset)
|
||||
execsql {DELETE FROM t1 ORDER BY x LIMIT -2, 5}
|
||||
@@ -227,7 +237,9 @@ ifcapable {update_delete_limit} {
|
||||
} {11}
|
||||
create_test_data 6
|
||||
do_test wherelimit-3.2 {
|
||||
execsql {UPDATE t1 SET y=1 WHERE x=1 LIMIT 5}
|
||||
execsql {UPDATE t1 SET y=1 WHERE x=1 RETURNING x, old.y, '|' LIMIT 5}
|
||||
} {1 1 | 1 2 | 1 3 | 1 4 | 1 5 |}
|
||||
do_test wherelimit-3.2cnt {
|
||||
execsql {SELECT count(*) FROM t1 WHERE y=1}
|
||||
} {10}
|
||||
do_test wherelimit-3.3 {
|
||||
|
||||
@@ -198,4 +198,33 @@ do_execsql_test 4.2 {
|
||||
GROUP BY 1;
|
||||
} {elvis}
|
||||
|
||||
# 2021-02-13
|
||||
# Avoid manifesting the same CTE multiple times.
|
||||
#
|
||||
do_eqp_test 5.1 {
|
||||
WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
|
||||
SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
|
||||
ORDER BY 1;
|
||||
} {
|
||||
QUERY PLAN
|
||||
|--MATERIALIZE xxxxxx
|
||||
| |--SETUP
|
||||
| | `--SCAN CONSTANT ROW
|
||||
| `--RECURSIVE STEP
|
||||
| `--SCAN TABLE c
|
||||
|--SCAN SUBQUERY xxxxxx AS x1
|
||||
|--SCAN SUBQUERY xxxxxx AS x2
|
||||
|--SCAN SUBQUERY xxxxxx AS x3
|
||||
|--SCAN SUBQUERY xxxxxx AS x4
|
||||
`--USE TEMP B-TREE FOR ORDER BY
|
||||
}
|
||||
do_execsql_test 5.2 {
|
||||
WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<1)
|
||||
SELECT x1.x||x2.x||x3.x||x4.x FROM c AS x1, c AS x2, c AS x3, c AS x4
|
||||
ORDER BY 1;
|
||||
} {0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -155,10 +155,16 @@ struct Keyword {
|
||||
# define WINDOWFUNC 0x00100000
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
|
||||
# define GENCOL 0
|
||||
# define GENCOL 0
|
||||
#else
|
||||
# define GENCOL 0x00200000
|
||||
# define GENCOL 0x00200000
|
||||
#endif
|
||||
#ifdef SQLITE_OMIT_RETURNING
|
||||
# define RETURNING 0
|
||||
#else
|
||||
# define RETURNING 0x00400000
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** These are the keywords
|
||||
@@ -280,6 +286,7 @@ static Keyword aKeywordTable[] = {
|
||||
{ "RENAME", "TK_RENAME", ALTER, 1 },
|
||||
{ "REPLACE", "TK_REPLACE", CONFLICT, 10 },
|
||||
{ "RESTRICT", "TK_RESTRICT", FKEY, 1 },
|
||||
{ "RETURNING", "TK_RETURNING", RETURNING, 10 },
|
||||
{ "RIGHT", "TK_JOIN_KW", ALWAYS, 0 },
|
||||
{ "ROLLBACK", "TK_ROLLBACK", ALWAYS, 1 },
|
||||
{ "ROW", "TK_ROW", TRIGGER, 1 },
|
||||
|
||||
Reference in New Issue
Block a user