Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 952dce9160 | |||
| 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 | |||
| 17988aae5b | |||
| 906602ac3f | |||
| 71aff85503 | |||
| 6cca0aa9df | |||
| 7bb0a7d947 | |||
| f380c3f13c | |||
| 7b88f54914 | |||
| a2560ce934 | |||
| d6665c51e2 | |||
| f3ebea8114 | |||
| abc6b67aff | |||
| 06afa291cf | |||
| 9fcc8c69bb | |||
| 9ffa258a01 | |||
| 4be8bdccd4 | |||
| 2a3be742ca | |||
| 1e2896ec6e | |||
| f7588d4072 | |||
| a828d565b6 | |||
| e8f7fcf6f4 | |||
| 9d326d6793 | |||
| 19ef211d85 | |||
| 10c9ef65fa | |||
| 19f9cd73ff | |||
| 76cac6ef06 | |||
| 41d01407ea | |||
| 6bfc167a67 | |||
| 2053f313bf |
+2
-2
@@ -1077,7 +1077,7 @@ can choose a different start symbol using the
|
||||
<a id='syntax_error'></a>
|
||||
<h4>4.4.19 The <tt>%syntax_error</tt> directive</h4>
|
||||
|
||||
<p>See <a href='#error_processing'>Error Processing</a>.</p>
|
||||
<p>See <a href='#errors'>Error Processing</a>.</p>
|
||||
|
||||
<a id='token_class'></a>
|
||||
<h4>4.4.20 The <tt>%token_class</tt> directive</h4>
|
||||
@@ -1176,7 +1176,7 @@ match any input token.</p>
|
||||
the wildcard token and some other token, the other token is always used.
|
||||
The wildcard token is only matched if there are no alternatives.</p>
|
||||
|
||||
<a id='error_processing'></a>
|
||||
<a id='errors'></a>
|
||||
<h2>5.0 Error Processing</h2>
|
||||
|
||||
<p>After extensive experimentation over several years, it has been
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2020 December 23
|
||||
#
|
||||
# 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 the SQLite sessions module
|
||||
# Specifically, for the sqlite3session_memory_used() API.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionmem
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(i INTEGER PRIMARY KEY, x, y);
|
||||
CREATE TABLE t2(i INTEGER, x, y, PRIMARY KEY(x, y));
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
} {}
|
||||
|
||||
foreach {tn sql eRes} {
|
||||
1 { INSERT INTO t1 VALUES(1, 2, 3) } 1
|
||||
2 { UPDATE t1 SET x=5 } 0
|
||||
3 { UPDATE t1 SET i=5 } 1
|
||||
4 { DELETE FROM t1 } 0
|
||||
5 { INSERT INTO t1 VALUES(1, 2, 3) } 0
|
||||
6 { INSERT INTO t1 VALUES(5, 2, 3) } 0
|
||||
7 { INSERT INTO t2 VALUES('a', 'b', 'c') } 1
|
||||
8 { INSERT INTO t2 VALUES('d', 'e', 'f') } 1
|
||||
9 { UPDATE t2 SET i='e' } 0
|
||||
} {
|
||||
set mem1 [S memory_used]
|
||||
do_test 1.2.$tn.(mu=$mem1) {
|
||||
execsql $sql
|
||||
set mem2 [S memory_used]
|
||||
expr {$mem2 > $mem1}
|
||||
} $eRes
|
||||
}
|
||||
|
||||
do_test 1.3 {
|
||||
S delete
|
||||
} {}
|
||||
|
||||
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);
|
||||
|
||||
@@ -550,13 +550,13 @@ ST_OPT = -DSQLITE_THREADSAFE=0
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
#
|
||||
all: sqlite3.h libsqlite3.a sqlite3$(EXE)
|
||||
all: sqlite3.h sqlite3ext.h libsqlite3.a sqlite3$(EXE)
|
||||
|
||||
libsqlite3.a: $(LIBOBJ)
|
||||
libsqlite3.a: sqlite3.h $(LIBOBJ)
|
||||
$(AR) libsqlite3.a $(LIBOBJ)
|
||||
$(RANLIB) libsqlite3.a
|
||||
|
||||
sqlite3$(EXE): shell.c libsqlite3.a sqlite3.h
|
||||
sqlite3$(EXE): sqlite3.h libsqlite3.a shell.c
|
||||
$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
|
||||
shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)
|
||||
|
||||
@@ -824,13 +824,13 @@ fts3_unicode2.o: $(TOP)/ext/fts3/fts3_unicode2.c $(HDR) $(EXTHDR)
|
||||
fts3_write.o: $(TOP)/ext/fts3/fts3_write.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_write.c
|
||||
|
||||
fts5.o: fts5.c
|
||||
fts5.o: fts5.c sqlite3ext.h sqlite3.h
|
||||
$(TCCX) -DSQLITE_CORE -c fts5.c
|
||||
|
||||
json1.o: $(TOP)/ext/misc/json1.c
|
||||
json1.o: $(TOP)/ext/misc/json1.c sqlite3ext.h sqlite3.h
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/json1.c
|
||||
|
||||
stmt.o: $(TOP)/ext/misc/stmt.c
|
||||
stmt.o: $(TOP)/ext/misc/stmt.c sqlite3ext.h sqlite3.h
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/misc/stmt.c
|
||||
|
||||
rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C The\searly-out\sof\sthe\sinner\sloop\son\sthe\smin/max\soptimization\swas\soverly\naggressive\sfor\sthe\scases\swhere\sthere\sis\sa\sjoin\sand\souter\sloops\scontain\nIN\soperators.\s\sFix\sthis.\s\sTest\scase\sin\sTH3.
|
||||
D 2021-01-14T00:53:14.879
|
||||
C Initial\scommit
|
||||
D 2021-02-04T21:19:24.732
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -38,7 +38,7 @@ F configure 91893a81f698778dda4d8fb24bfca606ded31ef02bcfbc2ab072d30fb67138d6 x
|
||||
F configure.ac 412b65c6107e41c098ad7f5f2e6a3f74ac02ffc6e92b9a6264b9f1060c235a04
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd
|
||||
F doc/lemon.html c5d8ba85ac1daef7be8c2d389899480eb62451ff5c09b0c28ff8157bb8770746
|
||||
F doc/lemon.html 1bb72ece6271df0d901d233551dd985f2c6ba30d09382cf2d321ed951ab57491
|
||||
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
|
||||
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
|
||||
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
|
||||
@@ -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
|
||||
@@ -450,10 +450,11 @@ F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
F ext/session/sessionfault2.test dd593f80b6b4786f7adfe83c5939620bc505559770cc181332da26f29cddd7bb
|
||||
F ext/session/sessioninvert.test 04075517a9497a80d39c495ba6b44f3982c7371129b89e2c52219819bc105a25
|
||||
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/sqlite3session.c 1d0553077b55ffcfa69963c354e9bad3bace6ce79bbe7368e650c6ae1e106314
|
||||
F ext/session/sqlite3session.h f53c99731882bf59c7362855cdeba176ce1fe8eeba089e38a8cce0172f8473aa
|
||||
F ext/session/test_session.c 93ca965112d2b4d9d669c9c0be6b1e52942a268796050a145612df1eee175ce0
|
||||
F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
@@ -462,7 +463,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 57451ea5b3d5cd86e9c5324b10c9de184b12e8dcccc31c65d24fbcb55ccd9c53
|
||||
F main.mk 443a4ec1ca89ad267cbde45dadc68861154b99c7bd26e7b7dc74303a664002b8
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -476,32 +477,32 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 36cae0d6e3e91a1996e1a472f8c7242c31a4e38ba4295e3056da198c04fd2a87
|
||||
F src/analyze.c 01c6c6765cb4d40b473b71d85535093730770bb186f2f473abac25f07fcdee5c
|
||||
F src/attach.c 0f497c15c4cfe3bdcb214f0dbdbbb6c5ed7e8a9308ac445c7959f5e5780437a9
|
||||
F src/attach.c e80162a47411f296bea550ed8fafd730481f4aa71e89ece23ba9c957eed15d4a
|
||||
F src/auth.c 8d1df0e2ef8bafbedd4f1fe4baff03eb27507da4bf6e449df3613d383c4018b2
|
||||
F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
|
||||
F src/btree.c 0f9cb686871ae668817673f0823b55d1bcadbc86ea28bd22c590b064a8322d5a
|
||||
F src/btree.c 694020ad8a3af3d79b09f74c8f1421272a419cdea42a13401e3b0f7dea6e9c3e
|
||||
F src/btree.h 285f8377aa1353185a32bf455faafa9ff9a0d40d074d60509534d14990c7829e
|
||||
F src/btreeInt.h 7614cae30f95b6aed0c7cac7718276a55cfe2c77058cbfd8bef5b75329757331
|
||||
F src/build.c d4c06261b0e532523ede58dc511381a7a9c155132e4b65a6bb2ff76fe657793a
|
||||
F src/build.c 4a70f3ce393dba6963e7d74f72fab0108da6c87e43f1d8afe3fdb31034e190f6
|
||||
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/expr.c 0d196ed5a2ebf96be7e8df88add4fabfad0dce16c0fed81a4b8f6a26e259797f
|
||||
F src/delete.c 352ea931218c45a3daf17472d4141b9c7fc026d85da3f1ade404ea5bb6d67f77
|
||||
F src/expr.c 47c85263e6d179424e6b09e2c79db5704ab5b8cbc2fae2ee3285faa2566f2e74
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 83372403298e6a7dd989a47aaacdbaa5b4307b5199dbd56e07d4896066b3de72
|
||||
F src/func.c 251b5953cecd0ce3e282213c5e623134415793d3569d7804d13460559d7e45ff
|
||||
F src/fkey.c 73adaca988d0dd517d373b432dc9dfa2cd7fa3108b114260132a80832de19037
|
||||
F src/func.c 2ea99e9e0531b7f020d5e8e167d25344d618afc718ddc94dd91fa8fef1c85a91
|
||||
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 c5e0c25cfb9960d9b7d49043de6adc12748853bc6dea76f5adef059e366f2f70
|
||||
F src/insert.c 97be36c52c667a64aacbba76398544d224268d62444b67d011a077c486e375bb
|
||||
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
|
||||
F src/loadext.c 8c9c8cd2bd8eecdb06d9b6e89de7e9e65bae45cc8fc33609cc74023a5c296067
|
||||
F src/main.c 1c5de7b3fabcdf05f4fe563aab5d81d175b89c67a8678a12ba86629356afa356
|
||||
@@ -529,7 +530,7 @@ 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 67ba503780de64b967ae195b7e14c33531329228e1bc0b83d63324beb733680b
|
||||
F src/pcache.c 385ff064bca69789d199a98e2169445dc16e4291fa807babd61d4890c3b34177
|
||||
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
|
||||
F src/pcache1.c 6596e10baf3d8f84cc1585d226cf1ab26564a5f5caf85a15757a281ff977d51a
|
||||
@@ -538,14 +539,14 @@ F src/pragma.h 8dc78ab7e9ec6ce3ded8332810a2066f1ef6267e2e03cd7356ee00276125c6cf
|
||||
F src/prepare.c f288cbc35f79eb32e162de7e80a63ebe00d80e639dcfac071bee11570cbdb16f
|
||||
F src/printf.c 30e92b638fac71dcd85cdea1d12ecfae354c9adee2c71e8e1ae4727cde7c91ed
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 1948a92ca9eab776632816b97e57c61d933474a78aad4f4ef835c916a83dbb1c
|
||||
F src/resolve.c f6761473ea4b51190fc52f8f2121498b78717266e106e7bff12849ea2d52165f
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c 738cb746189f721f59972993c13085fa2975c4cbfd04ba26445f3b42c81237dc
|
||||
F src/shell.c.in 9ebc74e4f05cfbd0f4a36060fdaeff1da4e9af4458358722bc08c5a1ab9a0879
|
||||
F src/sqlite.h.in 0af968a1fa3c717261e1df0ed105fa7bddb4d82de7e0adb3eab49e6aa81b4de7
|
||||
F src/sqlite.h.in 8855a19f37ade8dad189a9e48233a2ebe1b46faf469c7eb0906a654e252dcc57
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||
F src/sqliteInt.h 6aad58a5ae1374e18ea53d0c3ea71f047b67313426767783bd7fa14ee786725a
|
||||
F src/sqliteInt.h eb303ae364d57210a745bbcba1bc2fb9b0dd004682fd86a7645a63d65156876a
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@@ -606,17 +607,17 @@ 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 0a242d65dd9b9822d4e990653eb4ece3557dcda01374934aa3cc1f9718d8dee3
|
||||
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 67de20067fa3a2ee8566342c751e941a2fe3fd88940fd9886ca5115f04165cce
|
||||
F src/vdbe.h 83603854bfa5851af601fc0947671eb260f4363e62e960e8a994fb9bbcd2aaa1
|
||||
F src/vdbeInt.h 3ca5e9fd6e095a8b6cf6bc3587a46fc93499503b2fe48951e1034ba9e2ce2f6e
|
||||
F src/vdbeapi.c c5e7cb2ab89a24d7f723e87b508f21bfb1359a04db5277d8a99fd1e015c12eb9
|
||||
F src/vdbeaux.c e91d74e24babcf61969279b193e228cf4f8bc724a9cc59ed287db064326876f8
|
||||
F src/vdbe.c 50350d7db53b676c6efa120f95da7f6b1ce3588f4ddbd75907ad744c173a0c06
|
||||
F src/vdbe.h a71bf43572d3de57923d1928ac01ae8d355cd67e94462ba4f7462265cedbef9a
|
||||
F src/vdbeInt.h 3df118924e1711f1bbc8e30c46260d0ab6c3b029b32dd411f789111f76434f3c
|
||||
F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b
|
||||
F src/vdbeaux.c 34994221fbde303e362cf11c7d97fe9b115055cfdf716260ca0140811de94aab
|
||||
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
||||
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
|
||||
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
|
||||
@@ -627,10 +628,10 @@ 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/whereInt.h 9a3f577619f07700d16d89eeb2f3d94d6b7ed7f109c2dacf0ce8844921549506
|
||||
F src/wherecode.c a3a1aff30fe99a818d8e7c607980f033f40c68d890e03ed25838b9dbb7908bee
|
||||
F src/whereexpr.c 3a463e156ea388083c501502229c2c7f4f5c6b5330ea59bdf40d6eb6e155a25f
|
||||
F src/where.c 6efc4a10bfe0ec908c4f3c9112afb7675c952204b4b0b255672f488860141fec
|
||||
F src/whereInt.h ae03b5e3a4cca9bd9cb1b7d3c63faf8f1f177200fc8cecc87d3d0cab6ca338e6
|
||||
F src/wherecode.c 43a63441f8662ddf86b15975683a502ec33f08167e9636f4d19e38e265e95fd9
|
||||
F src/whereexpr.c a182038cf7d10aa9a95a96b8563a34f34990323cf307dee6559e995961966d43
|
||||
F src/window.c edd6f5e25a1e8f2b6f5305b7f5f7da7bb35f07f0d432b255b1d4c2fcab4205aa
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
|
||||
@@ -651,7 +652,7 @@ F test/altermalloc.test 167a47de41b5c638f5f5c6efb59784002b196fff70f98d9b4ed3cd74
|
||||
F test/altermalloc2.test fa7b1c1139ea39b8dec407cf1feb032ca8e0076bd429574969b619175ad0174b
|
||||
F test/altertab.test 6d7bbac2c4a6ef71b775094a3298fa3a92274d95034ee23157ffba92768e47e6
|
||||
F test/altertab2.test b0d62f323ca5dab42b0bc028c52e310ebdd13e655e8fac070fe622bad7852c2b
|
||||
F test/altertab3.test 1db384eb85b4a30b0b332842f5c596b0dc0126f7c61959be3f85ae8b1c271d9a
|
||||
F test/altertab3.test 2b82fa2236a3a91553d53ae5555d8e723c7eec174c41f1fa62ff497355398479
|
||||
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
|
||||
F test/analyze.test 547bb700f903107b38611b014ca645d6b5bb819f5210d7bf39c40802aafeb7d7
|
||||
F test/analyze3.test fca2a9de0017becfdcc201647f03b1cfd5ba0e7b5b5c852936e4ec62780cde49
|
||||
@@ -800,7 +801,7 @@ F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
|
||||
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
|
||||
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
|
||||
F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
|
||||
F test/crash5.test 98f77ad22bceaea9043bf87088d5b61d004a1f40bb7c9dc3b6a7c70bd502c0bb
|
||||
F test/crash5.test 4aa55e7ac3c4bc511873e457aa65d2827d52da9b51e061511899dadcfe22b1e8
|
||||
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
|
||||
F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df
|
||||
F test/crash8.test 64366e459c28dd62edfb7ad87253a409c7533b92d16fcc479a6a8131bdcc3100
|
||||
@@ -874,6 +875,8 @@ F test/exclusive.test 7ff63be7503990921838d5c9f77f6e33e68e48ed1a9d48cd28745bf650
|
||||
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
|
||||
F test/exists2.test 3e5726d6a67ebd4bd3466db58be424c09156c1f276c7594abb260cbf6ad494d3
|
||||
F test/existsfault.test 74f7edc713f5a335e7ff47adf503067bf05c6f8630f88b2a19c24f0fa5486ab8
|
||||
F test/expr.test 26cd01e8485bc48c8aa6a1add598e9ce1e706b4eb4f3f554e0b0223022e8c2cf
|
||||
F test/expr2.test c27327ae9c017a7ff6280123f67aff496f912da74d78c888926d68b46ec75fd8
|
||||
F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
|
||||
@@ -1027,7 +1030,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
|
||||
@@ -1043,7 +1046,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
|
||||
@@ -1061,7 +1064,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
|
||||
@@ -1283,6 +1286,7 @@ F test/releasetest.tcl fb76d8fcc95ac29d6356cd9e52b726ab9e43a24082897618dfbcb7c2b
|
||||
F test/releasetest_data.tcl b9cb30360759b80d92d4ea86b84ebfd8035b97f9078a482deb3cf9d0b2442655
|
||||
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
|
||||
@@ -1290,7 +1294,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
|
||||
@@ -1435,7 +1439,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 56c059c88c5b96a624f1193ba48b0bac034190b79cd9c75cb4acbfe84baf7ec5
|
||||
F test/tester.tcl 19d2a19a6dd55a2b4e2b943963959a05a2c088495dd5f5274b04e0494ce86d66
|
||||
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1614,7 +1618,7 @@ F test/triggerA.test 837be862d8721f903dba3f3ceff05b32e0bee5214cf6ea3da5fadf12d36
|
||||
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
|
||||
F test/triggerC.test 29f5a28d0fe39e6e2c01f6e1f53f08c0955170ae10a63ad023e33cb0a1682a51
|
||||
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
|
||||
F test/triggerE.test ede2e4bce4ba802337bd69d39447fa04a938e06d84a8bfc53c76850fc36ed86d
|
||||
F test/triggerE.test 612969cb57a4ef792059ad6d01af0117e1ae862c283753ffcc9a6428642b22ee
|
||||
F test/triggerF.test 5d76f0a8c428ff87a4d5ed52da06f6096a2c787a1e21b846111dfac4123de3ad
|
||||
F test/triggerG.test 2b816093c91ba73c733cfa8aedcc210ad819d72a98b1da30768a3c56505233e9
|
||||
F test/triggerupfrom.test d25961fa70a99b6736193da7b49a36d8c1d28d56188f0be6406d4366315cd6e4
|
||||
@@ -1748,7 +1752,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
|
||||
@@ -1823,7 +1827,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
|
||||
@@ -1895,7 +1899,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 83ec01e38cbd22147ba544e15eae32c72e0523a55b54851e483dc2effc64f206
|
||||
R 3dc0d948d76c5e90d01be3a954f85921
|
||||
U drh
|
||||
Z 4b60af58a6a03c91f6ab835dbd142db0
|
||||
P 5c8e6296aa9f69a092364524b716ba894f113f7f1e6024b9a2eaa01c239e65c1
|
||||
R 070e022aecbc893ee01aafb7a2c4ea5f
|
||||
T *branch * selective-debug
|
||||
T *sym-selective-debug *
|
||||
T -sym-trunk *
|
||||
U shearer
|
||||
Z e1856ad9f09c03d3f05e46c1ad06680d
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ccd3bae14b6b47bb0f9622700c04db989f76ce65e10e0709964cfd0675eca762
|
||||
e73dbce8c5501faf25e1d6fa99173ea83b279b7920fd273eff3056d008e2b286
|
||||
+83
-114
@@ -433,6 +433,62 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
|
||||
}
|
||||
#endif /* SQLITE_OMIT_ATTACH */
|
||||
|
||||
/*
|
||||
** Expression callback used by sqlite3FixAAAA() routines.
|
||||
*/
|
||||
static int fixExprCb(Walker *p, Expr *pExpr){
|
||||
DbFixer *pFix = p->u.pFix;
|
||||
if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
|
||||
if( pExpr->op==TK_VARIABLE ){
|
||||
if( pFix->pParse->db->init.busy ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else{
|
||||
sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
|
||||
return WRC_Abort;
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Select callback used by sqlite3FixAAAA() routines.
|
||||
*/
|
||||
static int fixSelectCb(Walker *p, Select *pSelect){
|
||||
DbFixer *pFix = p->u.pFix;
|
||||
int i;
|
||||
struct SrcList_item *pItem;
|
||||
sqlite3 *db = pFix->pParse->db;
|
||||
int iDb = sqlite3FindDbName(db, pFix->zDb);
|
||||
SrcList *pList = pSelect->pSrc;
|
||||
|
||||
if( NEVER(pList==0) ) return WRC_Continue;
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pFix->bTemp==0 ){
|
||||
if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return WRC_Abort;
|
||||
}
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
pItem->fg.fromDDL = 1;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( sqlite3WalkExpr(&pFix->w, pList->a[i].pOn) ) return WRC_Abort;
|
||||
#endif
|
||||
}
|
||||
if( pSelect->pWith ){
|
||||
for(i=0; i<pSelect->pWith->nCte; i++){
|
||||
if( sqlite3WalkSelect(p, pSelect->pWith->a[i].pSelect) ){
|
||||
return WRC_Abort;
|
||||
}
|
||||
}
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Initialize a DbFixer structure. This routine must be called prior
|
||||
** to passing the structure to one of the sqliteFixAAAA() routines below.
|
||||
@@ -444,9 +500,7 @@ void sqlite3FixInit(
|
||||
const char *zType, /* "view", "trigger", or "index" */
|
||||
const Token *pName /* Name of the view, trigger, or index */
|
||||
){
|
||||
sqlite3 *db;
|
||||
|
||||
db = pParse->db;
|
||||
sqlite3 *db = pParse->db;
|
||||
assert( db->nDb>iDb );
|
||||
pFix->pParse = pParse;
|
||||
pFix->zDb = db->aDb[iDb].zDbSName;
|
||||
@@ -454,6 +508,13 @@ void sqlite3FixInit(
|
||||
pFix->zType = zType;
|
||||
pFix->pName = pName;
|
||||
pFix->bTemp = (iDb==1);
|
||||
pFix->w.pParse = pParse;
|
||||
pFix->w.xExprCallback = fixExprCb;
|
||||
pFix->w.xSelectCallback = fixSelectCb;
|
||||
pFix->w.xSelectCallback2 = 0;
|
||||
pFix->w.walkerDepth = 0;
|
||||
pFix->w.eCode = 0;
|
||||
pFix->w.u.pFix = pFix;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -474,115 +535,27 @@ int sqlite3FixSrcList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
SrcList *pList /* The Source list to check and modify */
|
||||
){
|
||||
int i;
|
||||
struct SrcList_item *pItem;
|
||||
sqlite3 *db = pFix->pParse->db;
|
||||
int iDb = sqlite3FindDbName(db, pFix->zDb);
|
||||
|
||||
if( NEVER(pList==0) ) return 0;
|
||||
|
||||
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
|
||||
if( pFix->bTemp==0 ){
|
||||
if( pItem->zDatabase && iDb!=sqlite3FindDbName(db, pItem->zDatabase) ){
|
||||
sqlite3ErrorMsg(pFix->pParse,
|
||||
"%s %T cannot reference objects in database %s",
|
||||
pFix->zType, pFix->pName, pItem->zDatabase);
|
||||
return 1;
|
||||
}
|
||||
sqlite3DbFree(db, pItem->zDatabase);
|
||||
pItem->zDatabase = 0;
|
||||
pItem->pSchema = pFix->pSchema;
|
||||
pItem->fg.fromDDL = 1;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
|
||||
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
|
||||
#endif
|
||||
if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
|
||||
return 1;
|
||||
}
|
||||
int res = 0;
|
||||
if( pList ){
|
||||
Select s;
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.pSrc = pList;
|
||||
res = sqlite3WalkSelect(&pFix->w, &s);
|
||||
}
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
|
||||
int sqlite3FixSelect(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Select *pSelect /* The SELECT statement to be fixed to one database */
|
||||
){
|
||||
while( pSelect ){
|
||||
if( sqlite3FixExprList(pFix, pSelect->pEList) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
|
||||
return 1;
|
||||
}
|
||||
if( pSelect->pWith ){
|
||||
int i;
|
||||
for(i=0; i<pSelect->pWith->nCte; i++){
|
||||
if( sqlite3FixSelect(pFix, pSelect->pWith->a[i].pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
pSelect = pSelect->pPrior;
|
||||
}
|
||||
return 0;
|
||||
return sqlite3WalkSelect(&pFix->w, pSelect);
|
||||
}
|
||||
int sqlite3FixExpr(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
Expr *pExpr /* The expression to be fixed to one database */
|
||||
){
|
||||
while( pExpr ){
|
||||
if( !pFix->bTemp ) ExprSetProperty(pExpr, EP_FromDDL);
|
||||
if( pExpr->op==TK_VARIABLE ){
|
||||
if( pFix->pParse->db->init.busy ){
|
||||
pExpr->op = TK_NULL;
|
||||
}else{
|
||||
sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
|
||||
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
|
||||
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
|
||||
}else{
|
||||
if( sqlite3FixExprList(pFix, pExpr->x.pList) ) return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pExpr->pRight) ){
|
||||
return 1;
|
||||
}
|
||||
pExpr = pExpr->pLeft;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int sqlite3FixExprList(
|
||||
DbFixer *pFix, /* Context of the fixation */
|
||||
ExprList *pList /* The expression to be fixed to one database */
|
||||
){
|
||||
int i;
|
||||
struct ExprList_item *pItem;
|
||||
if( pList==0 ) return 0;
|
||||
for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){
|
||||
if( sqlite3FixExpr(pFix, pItem->pExpr) ){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return sqlite3WalkExpr(&pFix->w, pExpr);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -592,25 +565,20 @@ int sqlite3FixTriggerStep(
|
||||
TriggerStep *pStep /* The trigger step be fixed to one database */
|
||||
){
|
||||
while( pStep ){
|
||||
if( sqlite3FixSelect(pFix, pStep->pSelect) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExpr(pFix, pStep->pWhere) ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
|
||||
return 1;
|
||||
}
|
||||
if( pStep->pFrom && sqlite3FixSrcList(pFix, pStep->pFrom) ){
|
||||
if( sqlite3WalkSelect(&pFix->w, pStep->pSelect)
|
||||
|| sqlite3WalkExpr(&pFix->w, pStep->pWhere)
|
||||
|| sqlite3WalkExprList(&pFix->w, pStep->pExprList)
|
||||
|| sqlite3FixSrcList(pFix, pStep->pFrom)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
#ifndef SQLITE_OMIT_UPSERT
|
||||
if( pStep->pUpsert ){
|
||||
Upsert *pUp = pStep->pUpsert;
|
||||
if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
|
||||
|| sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
|
||||
|| sqlite3FixExprList(pFix, pUp->pUpsertSet)
|
||||
|| sqlite3FixExpr(pFix, pUp->pUpsertWhere)
|
||||
if( sqlite3WalkExprList(&pFix->w, pUp->pUpsertTarget)
|
||||
|| sqlite3WalkExpr(&pFix->w, pUp->pUpsertTargetWhere)
|
||||
|| sqlite3WalkExprList(&pFix->w, pUp->pUpsertSet)
|
||||
|| sqlite3WalkExpr(&pFix->w, pUp->pUpsertWhere)
|
||||
){
|
||||
return 1;
|
||||
}
|
||||
@@ -618,6 +586,7 @@ int sqlite3FixTriggerStep(
|
||||
#endif
|
||||
pStep = pStep->pNext;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
+5
-1
@@ -2901,6 +2901,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
|
||||
((pageSize-1)&pageSize)==0 ){
|
||||
assert( (pageSize & 7)==0 );
|
||||
assert( !pBt->pCursor );
|
||||
if( nReserve>32 && pageSize==512 ) pageSize = 1024;
|
||||
pBt->pageSize = (u32)pageSize;
|
||||
freeTempSpace(pBt);
|
||||
}
|
||||
@@ -7974,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 );
|
||||
@@ -8010,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
|
||||
|
||||
+77
-5
@@ -143,11 +143,15 @@ 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 ){
|
||||
@@ -233,7 +237,6 @@ void sqlite3FinishCoding(Parse *pParse){
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Get the VDBE program ready for execution
|
||||
*/
|
||||
if( v && pParse->nErr==0 && !db->mallocFailed ){
|
||||
@@ -1243,6 +1246,75 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
|
||||
}
|
||||
#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;
|
||||
assert( !pParse->bReturning );
|
||||
pParse->bReturning = 1;
|
||||
pRet = sqlite3DbMallocZero(db, sizeof(*pRet));
|
||||
if( pRet==0 ){
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
return;
|
||||
}
|
||||
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.pSelect = &pRet->retSel;
|
||||
pRet->retSel.op = TK_ALL;
|
||||
pRet->retSel.pEList = pList;
|
||||
pRet->retSel.pSrc = (SrcList*)&pRet->retSrcList;
|
||||
pHash = &(db->aDb[1].pSchema->trigHash);
|
||||
assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0 );
|
||||
if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
|
||||
==&pRet->retTrig ){
|
||||
sqlite3OomFault(db);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new column to the table currently being constructed.
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
+12
-1
@@ -95,7 +95,18 @@ Expr *sqlite3ExprAddCollateToken(
|
||||
const Token *pCollName, /* Name of collating sequence */
|
||||
int dequote /* True to dequote pCollName */
|
||||
){
|
||||
if( pCollName->n>0 ){
|
||||
assert( pExpr!=0 || pParse->db->mallocFailed );
|
||||
if( pExpr==0 ) return 0;
|
||||
if( pExpr->op==TK_VECTOR ){
|
||||
ExprList *pList = pExpr->x.pList;
|
||||
if( ALWAYS(pList!=0) ){
|
||||
int i;
|
||||
for(i=0; i<pList->nExpr; i++){
|
||||
pList->a[i].pExpr = sqlite3ExprAddCollateToken(pParse,pList->a[i].pExpr,
|
||||
pCollName, dequote);
|
||||
}
|
||||
}
|
||||
}else if( pCollName->n>0 ){
|
||||
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
|
||||
if( pNew ){
|
||||
pNew->pLeft = pExpr;
|
||||
|
||||
+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 ){
|
||||
|
||||
+8
-5
@@ -1865,7 +1865,9 @@ void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){
|
||||
int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
|
||||
FuncDef *pDef;
|
||||
int nExpr;
|
||||
if( pExpr->op!=TK_FUNCTION || !pExpr->x.pList ){
|
||||
assert( pExpr!=0 );
|
||||
assert( pExpr->op==TK_FUNCTION );
|
||||
if( !pExpr->x.pList ){
|
||||
return 0;
|
||||
}
|
||||
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
@@ -1978,7 +1980,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;
|
||||
@@ -1987,14 +1989,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)) ){
|
||||
|
||||
+4
-1
@@ -954,6 +954,7 @@ void sqlite3Insert(
|
||||
if( (db->flags & SQLITE_CountRows)!=0
|
||||
&& !pParse->nested
|
||||
&& !pParse->pTriggerTab
|
||||
&& !pParse->bReturning
|
||||
){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
@@ -1301,7 +1302,9 @@ void sqlite3Insert(
|
||||
sqlite3VdbeJumpHere(v, addrInsTop);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_XFER_OPT
|
||||
insert_end:
|
||||
#endif /* SQLITE_OMIT_XFER_OPT */
|
||||
/* Update the sqlite_sequence table by storing the content of the
|
||||
** maximum rowid counter values recorded while inserting into
|
||||
** autoincrement tables.
|
||||
@@ -1316,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);
|
||||
}
|
||||
|
||||
+19
-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;}
|
||||
|
||||
+7
-4
@@ -371,23 +371,26 @@ static int lookupName(
|
||||
** it is a new.* or old.* trigger argument reference. Or
|
||||
** maybe it is an excluded.* from an upsert.
|
||||
*/
|
||||
if( zDb==0 && zTab!=0 && cntTab==0 ){
|
||||
if( zDb==0 && cntTab==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 ){
|
||||
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 && ALWAYS(zTab) ){
|
||||
Upsert *pUpsert = pNC->uNC.pUpsert;
|
||||
if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
|
||||
pTab = pUpsert->pUpsertSrc->a[0].pTab;
|
||||
|
||||
+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>
|
||||
|
||||
+87
-14
@@ -294,6 +294,26 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** These are the Selective Debugging macros, which give finer-grained debugging
|
||||
** than available via SQLITE_DEBUG. No use of SQLITE_DEBUG is disturbed
|
||||
** if code uses Selective Debugging. Selective Debugging introduces both
|
||||
** debug levels (ie a value for DEBUG_LEVEL) and also debug classes (for
|
||||
** example, DEBUG_VIRTUAL_MACHINE or DEBUG_STORAGE).
|
||||
**
|
||||
** Levels of Debugging Info, currently 3 levels 1-3
|
||||
**
|
||||
** Level 3 is the default, and applies if DEBUG_LEVEL is undefined
|
||||
**
|
||||
**
|
||||
** Level 1 status labels, such as "Flag status in Opcode XX is %s". Also
|
||||
** useful for ad-hoc debugging
|
||||
** Level 2 Level 1 plus flow labels, such as notification of
|
||||
** "Entering/Leaving Function X"
|
||||
** Level 3 Level 2 plus data structure dumps, and anything else
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
|
||||
** 0 means mutexes are permanently disable and the library is never
|
||||
@@ -1137,6 +1157,7 @@ typedef struct Bitvec Bitvec;
|
||||
typedef struct CollSeq CollSeq;
|
||||
typedef struct Column Column;
|
||||
typedef struct Db Db;
|
||||
typedef struct DbFixer DbFixer;
|
||||
typedef struct Schema Schema;
|
||||
typedef struct Expr Expr;
|
||||
typedef struct ExprList ExprList;
|
||||
@@ -1158,6 +1179,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;
|
||||
@@ -1721,6 +1743,7 @@ struct sqlite3 {
|
||||
#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
|
||||
#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
|
||||
#define SQLITE_MinMaxOpt 0x00010000 /* The min/max optimization */
|
||||
#define SQLITE_ExistsToIN 0x00020000 /* The EXISTS-to-IN optimization */
|
||||
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
|
||||
|
||||
/*
|
||||
@@ -3427,6 +3450,7 @@ struct Parse {
|
||||
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 */
|
||||
|
||||
@@ -3576,6 +3600,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 */
|
||||
@@ -3634,7 +3659,8 @@ 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 ... */
|
||||
@@ -3650,18 +3676,15 @@ struct TriggerStep {
|
||||
};
|
||||
|
||||
/*
|
||||
** The following structure contains information used by the sqliteFix...
|
||||
** routines as they walk the parse tree to make database references
|
||||
** explicit.
|
||||
** Information about a RETURNING clause
|
||||
*/
|
||||
typedef struct DbFixer DbFixer;
|
||||
struct DbFixer {
|
||||
Parse *pParse; /* The parsing context. Error messages written here */
|
||||
Schema *pSchema; /* Fix items to this schema */
|
||||
u8 bTemp; /* True for TEMP schema entries */
|
||||
const char *zDb; /* Make sure all objects are contained in this database */
|
||||
const char *zType; /* Type of the container - used for error messages */
|
||||
const Token *pName; /* Name of the container - used for error messages */
|
||||
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 */
|
||||
Select retSel; /* The SELECT statement that implements RETURNING */
|
||||
u64 retSrcList; /* The empty FROM clause of the SELECT */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -3814,9 +3837,25 @@ struct Walker {
|
||||
struct RenameCtx *pRename; /* RENAME COLUMN context */
|
||||
struct Table *pTab; /* Table of generated column */
|
||||
struct SrcList_item *pSrcItem; /* A single FROM clause item */
|
||||
DbFixer *pFix;
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
** The following structure contains information used by the sqliteFix...
|
||||
** routines as they walk the parse tree to make database references
|
||||
** explicit.
|
||||
*/
|
||||
struct DbFixer {
|
||||
Parse *pParse; /* The parsing context. Error messages written here */
|
||||
Walker w; /* Walker object */
|
||||
Schema *pSchema; /* Fix items to this schema */
|
||||
u8 bTemp; /* True for TEMP schema entries */
|
||||
const char *zDb; /* Make sure all objects are contained in this database */
|
||||
const char *zType; /* Type of the container - used for error messages */
|
||||
const Token *pName; /* Name of the container - used for error messages */
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
int sqlite3WalkExpr(Walker*, Expr*);
|
||||
int sqlite3WalkExprList(Walker*, ExprList*);
|
||||
@@ -4152,13 +4191,47 @@ struct PrintfArguments {
|
||||
|
||||
char *sqlite3MPrintf(sqlite3*,const char*, ...);
|
||||
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
|
||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) || defined(SQLITE_SELECTIVE_DEBUG)
|
||||
void sqlite3DebugPrintf(const char*, ...);
|
||||
#endif
|
||||
#if defined(SQLITE_TEST)
|
||||
void *sqlite3TestTextToPtr(const char*);
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_SELECTIVE_DEBUG)
|
||||
#define SELECTIVE_DEBUG(l) DEBUG_ALL || (l)
|
||||
|
||||
#if !defined(DEBUG_LEVEL) /* default to 3 */
|
||||
#define DEBUG_LEVEL 3
|
||||
#endif
|
||||
|
||||
#if (DEBUG_LEVEL < 1) || (DEBUG_LEVEL > 3)
|
||||
#error "DEBUG_LEVEL must be between 1 and 3. Default is 3"
|
||||
#endif
|
||||
#else
|
||||
#define SELECTIVE_DEBUG(l) 0
|
||||
|
||||
#if defined(DEBUG_ALL)
|
||||
#error "DEBUG_ALL specified without SQLITE_SELECTIVE_DEBUG"
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_LEVEL)
|
||||
#error "DEBUG_LEVEL specified without SQLITE_SELECTIVE_DEBUG"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(SELECTIVE_DEBUG)
|
||||
|
||||
#define DEBUG_PRINT(level,x) \
|
||||
if (level <= DEBUG_LEVEL) { \
|
||||
sqlite3DebugPrintf("Level: %d file:%s line:%d %s\n",level,__FILE__,__LINE__,x); \
|
||||
}
|
||||
|
||||
#else
|
||||
#define DEBUG_PRINT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(SQLITE_DEBUG)
|
||||
void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
|
||||
void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
|
||||
@@ -4249,6 +4322,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
|
||||
@@ -4526,7 +4600,6 @@ void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
|
||||
int sqlite3FixSrcList(DbFixer*, SrcList*);
|
||||
int sqlite3FixSelect(DbFixer*, Select*);
|
||||
int sqlite3FixExpr(DbFixer*, Expr*);
|
||||
int sqlite3FixExprList(DbFixer*, ExprList*);
|
||||
int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
|
||||
int sqlite3RealSameAsInt(double,sqlite3_int64);
|
||||
void sqlite3Int64ToText(i64,char*);
|
||||
|
||||
+140
-25
@@ -48,28 +48,37 @@ 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 ){
|
||||
pTrig->table = pTab->zName;
|
||||
pTrig->pTabSchema = pTab->pSchema;
|
||||
pTrig->pNext = pList;
|
||||
pList = pTrig;
|
||||
}
|
||||
p = sqliteHashNext(p);
|
||||
}
|
||||
}
|
||||
|
||||
return (pList ? pList : pTab->pTrigger);
|
||||
return pList;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -558,7 +567,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 +732,48 @@ 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;
|
||||
mask |= TRIGGER_AFTER;
|
||||
if( IsVirtual(pTab) && op!=TK_INSERT ){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"%s RETURNING is not available on virtual tables",
|
||||
op==TK_DELETE ? "DELETE" : "UPDATE");
|
||||
}
|
||||
}else if( p->bReturning && p->op==TK_INSERT && op==TK_UPDATE
|
||||
&& sqlite3IsToplevel(pParse) ){
|
||||
/* Also fire a RETURNING trigger for an UPSERT */
|
||||
mask |= TRIGGER_AFTER;
|
||||
}
|
||||
p = p->pNext;
|
||||
}while( p );
|
||||
}
|
||||
exit_triggers_exist:
|
||||
if( pMask ){
|
||||
*pMask = mask;
|
||||
}
|
||||
@@ -774,6 +816,47 @@ 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++){
|
||||
if( IsHiddenColumn(pTab->aCol+jj) ) continue;
|
||||
Expr *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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Generate VDBE code for the statements inside the body of a single
|
||||
** trigger.
|
||||
@@ -823,6 +906,7 @@ static int codeTriggerProgram(
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0),
|
||||
pParse->eOrconf, 0, 0, 0
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
case TK_INSERT: {
|
||||
@@ -833,6 +917,7 @@ static int codeTriggerProgram(
|
||||
pParse->eOrconf,
|
||||
sqlite3UpsertDup(db, pStep->pUpsert)
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
case TK_DELETE: {
|
||||
@@ -840,9 +925,10 @@ static int codeTriggerProgram(
|
||||
sqlite3TriggerStepSrc(pParse, pStep),
|
||||
sqlite3ExprDup(db, pStep->pWhere, 0), 0, 0
|
||||
);
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
break;
|
||||
}
|
||||
default: assert( pStep->op==TK_SELECT ); {
|
||||
case TK_SELECT: {
|
||||
SelectDest sDest;
|
||||
Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0);
|
||||
sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
|
||||
@@ -850,10 +936,27 @@ static int codeTriggerProgram(
|
||||
sqlite3SelectDelete(db, pSelect);
|
||||
break;
|
||||
}
|
||||
default: assert( pStep->op==TK_RETURNING ); {
|
||||
Select *pSelect = pStep->pSelect;
|
||||
ExprList *pList = pSelect->pEList;
|
||||
SelectDest sDest;
|
||||
Select *pNew;
|
||||
pSelect->pEList =
|
||||
sqlite3ExpandReturning(pParse, pList, pParse->pTriggerTab);
|
||||
sqlite3SelectDestInit(&sDest, SRT_Output, 0);
|
||||
pNew = sqlite3SelectDup(db, pSelect, 0);
|
||||
if( pNew ){
|
||||
sqlite3Select(pParse, pNew, &sDest);
|
||||
if( pNew->selFlags & (SF_Aggregate|SF_HasAgg|SF_WinRewrite) ){
|
||||
sqlite3ErrorMsg(pParse, "aggregates not allowed in RETURNING");
|
||||
}
|
||||
sqlite3SelectDelete(db, pNew);
|
||||
}
|
||||
sqlite3ExprListDelete(db, pSelect->pEList);
|
||||
pStep->pSelect->pEList = pList;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pStep->op!=TK_SELECT ){
|
||||
sqlite3VdbeAddOp0(v, OP_ResetCount);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -943,6 +1046,7 @@ static TriggerPrg *codeRowTrigger(
|
||||
pSubParse->pToplevel = pTop;
|
||||
pSubParse->zAuthContext = pTrigger->zName;
|
||||
pSubParse->eTriggerOp = pTrigger->op;
|
||||
pSubParse->bReturning = pTrigger->bReturning;
|
||||
pSubParse->nQueryLoop = pParse->nQueryLoop;
|
||||
pSubParse->disableVtab = pParse->disableVtab;
|
||||
|
||||
@@ -992,6 +1096,9 @@ static TriggerPrg *codeRowTrigger(
|
||||
if( db->mallocFailed==0 && pParse->nErr==0 ){
|
||||
pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg);
|
||||
}
|
||||
if( pTrigger->bReturning ){
|
||||
sqlite3VdbeColumnInfoXfer(sqlite3ParseToplevel(pParse)->pVdbe, v);
|
||||
}
|
||||
pProgram->nMem = pSubParse->nMem;
|
||||
pProgram->nCsr = pSubParse->nTab;
|
||||
pProgram->token = (void *)pTrigger;
|
||||
@@ -1146,12 +1253,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)
|
||||
&& (sqlite3IsToplevel(pParse) || !p->bReturning)
|
||||
){
|
||||
u8 origOp = p->op;
|
||||
p->op = op;
|
||||
sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump);
|
||||
p->op = origOp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
+74
-80
@@ -30,7 +30,7 @@
|
||||
** be changed out from under the copy. This macro verifies that nothing
|
||||
** like that ever happens.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M)
|
||||
#else
|
||||
# define memAboutToChange(P,M)
|
||||
@@ -117,7 +117,7 @@ int sqlite3_found_count = 0;
|
||||
# define UPDATE_MAX_BLOBSIZE(P)
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* This routine provides a convenient place to set a breakpoint during
|
||||
** tracing with PRAGMA vdbe_trace=on. The breakpoint fires right after
|
||||
** each opcode is printed. Variables "pc" (program counter) and pOp are
|
||||
@@ -476,7 +476,7 @@ static u16 numericType(Mem *pMem){
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/*
|
||||
** Write a nice string representation of the contents of cell pMem
|
||||
** into buffer zBuf, length nBuf.
|
||||
@@ -537,7 +537,7 @@ void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/*
|
||||
** Print the value of a register for tracing purposes:
|
||||
*/
|
||||
@@ -578,7 +578,7 @@ static void registerTrace(int iReg, Mem *p){
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/*
|
||||
** Show the values of all registers in the virtual machine. Used for
|
||||
** interactive debugging.
|
||||
@@ -590,7 +590,7 @@ void sqlite3VdbeRegisterDump(Vdbe *v){
|
||||
#endif /* SQLITE_DEBUG */
|
||||
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
|
||||
#else
|
||||
# define REGISTER_TRACE(R,M)
|
||||
@@ -660,10 +660,10 @@ int sqlite3VdbeExec(
|
||||
){
|
||||
Op *aOp = p->aOp; /* Copy of p->aOp */
|
||||
Op *pOp = aOp; /* Current operation */
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
Op *pOrigOp; /* Value of pOp at the top of the loop */
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
|
||||
#endif
|
||||
int rc = SQLITE_OK; /* Value to return */
|
||||
@@ -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 ){
|
||||
@@ -711,7 +711,7 @@ int sqlite3VdbeExec(
|
||||
db->busyHandler.nBusy = 0;
|
||||
if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
|
||||
sqlite3VdbeIOTraceSql(p);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
sqlite3BeginBenignMalloc();
|
||||
if( p->pc==0
|
||||
&& (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
|
||||
@@ -752,9 +752,9 @@ int sqlite3VdbeExec(
|
||||
if( p->anExec ) p->anExec[(int)(pOp-aOp)]++;
|
||||
#endif
|
||||
|
||||
/* Only allow tracing if SQLITE_DEBUG is defined.
|
||||
/* Only allow tracing if SQLITE_DEBUG or DEBUG_VIRTUAL_MACHINE is defined.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags & SQLITE_VdbeTrace ){
|
||||
sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp);
|
||||
test_trace_breakpoint((int)(pOp - aOp),pOp,p);
|
||||
@@ -775,7 +775,7 @@ int sqlite3VdbeExec(
|
||||
#endif
|
||||
|
||||
/* Sanity checking on other operands */
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
{
|
||||
u8 opProperty = sqlite3OpcodeProperty[pOp->opcode];
|
||||
if( (opProperty & OPFLG_IN1)!=0 ){
|
||||
@@ -811,7 +811,7 @@ int sqlite3VdbeExec(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
|
||||
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pOrigOp = pOp;
|
||||
#endif
|
||||
|
||||
@@ -866,7 +866,7 @@ int sqlite3VdbeExec(
|
||||
*/
|
||||
case OP_Goto: { /* jump */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* In debuggging mode, when the p5 flags is set on an OP_Goto, that
|
||||
** means we should really jump back to the preceeding OP_ReleaseReg
|
||||
** instruction. */
|
||||
@@ -1028,7 +1028,7 @@ case OP_Yield: { /* in1, jump */
|
||||
*/
|
||||
case OP_HaltIfNull: { /* in3 */
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
|
||||
#endif
|
||||
if( (pIn3->flags & MEM_Null)==0 ) break;
|
||||
@@ -1071,7 +1071,7 @@ case OP_Halt: {
|
||||
int pcx;
|
||||
|
||||
pcx = (int)(pOp - aOp);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
|
||||
#endif
|
||||
if( pOp->p1==SQLITE_OK && p->pFrame ){
|
||||
@@ -1258,7 +1258,7 @@ case OP_Null: { /* out2 */
|
||||
assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
|
||||
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
|
||||
pOut->n = 0;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pOut->uTemp = 0;
|
||||
#endif
|
||||
while( cnt>0 ){
|
||||
@@ -1356,7 +1356,7 @@ case OP_Move: {
|
||||
assert( memIsValid(pIn1) );
|
||||
memAboutToChange(p, pOut);
|
||||
sqlite3VdbeMemMove(pOut, pIn1);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pIn1->pScopyFrom = 0;
|
||||
{ int i;
|
||||
for(i=1; i<p->nMem; i++){
|
||||
@@ -1393,7 +1393,7 @@ case OP_Copy: {
|
||||
memAboutToChange(p, pOut);
|
||||
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
||||
Deephemeralize(pOut);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pOut->pScopyFrom = 0;
|
||||
#endif
|
||||
REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut);
|
||||
@@ -1422,7 +1422,7 @@ case OP_SCopy: { /* out2 */
|
||||
pOut = &aMem[pOp->p2];
|
||||
assert( pOut!=pIn1 );
|
||||
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pOut->pScopyFrom = pIn1;
|
||||
pOut->mScopyFlags = pIn1->flags;
|
||||
#endif
|
||||
@@ -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;
|
||||
|
||||
@@ -1504,7 +1496,7 @@ case OP_ResultRow: {
|
||||
|| (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
|
||||
sqlite3VdbeMemNulTerminate(&pMem[i]);
|
||||
REGISTER_TRACE(pOp->p1+i, &pMem[i]);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* The registers in the result will not be used again when the
|
||||
** prepared statement restarts. This is because sqlite3_column()
|
||||
** APIs might have caused type conversions of made other changes to
|
||||
@@ -2188,7 +2180,7 @@ compare_op:
|
||||
*/
|
||||
case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* Verify the preconditions of this opcode - that it follows an OP_Lt or
|
||||
** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening
|
||||
** OP_ReleaseReg opcodes */
|
||||
@@ -2272,7 +2264,7 @@ case OP_Compare: {
|
||||
assert( pKeyInfo!=0 );
|
||||
p1 = pOp->p1;
|
||||
p2 = pOp->p2;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( aPermute ){
|
||||
int k, mx = 0;
|
||||
for(k=0; k<n; k++) if( aPermute[k]>(u32)mx ) mx = aPermute[k];
|
||||
@@ -3845,7 +3837,7 @@ case OP_OpenWrite:
|
||||
pCur->nullRow = 1;
|
||||
pCur->isOrdered = 1;
|
||||
pCur->pgnoRoot = p2;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pCur->wrFlag = wrFlag;
|
||||
#endif
|
||||
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor);
|
||||
@@ -4231,7 +4223,7 @@ case OP_SeekGT: { /* jump, in3, group */
|
||||
oc = pOp->opcode;
|
||||
eqOnly = 0;
|
||||
pC->nullRow = 0;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pC->seekOp = pOp->opcode;
|
||||
#endif
|
||||
|
||||
@@ -4335,7 +4327,7 @@ case OP_SeekGT: { /* jump, in3, group */
|
||||
assert( oc!=OP_SeekLT || r.default_rc==+1 );
|
||||
|
||||
r.aMem = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
{ int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
|
||||
#endif
|
||||
r.eqSeen = 0;
|
||||
@@ -4467,7 +4459,7 @@ case OP_SeekScan: {
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( !pC->isTable );
|
||||
if( !sqlite3BtreeCursorIsValidNN(pC->uc.pCursor) ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... cursor not valid - fall through\n");
|
||||
}
|
||||
@@ -4480,7 +4472,7 @@ case OP_SeekScan: {
|
||||
r.nField = (u16)pOp[1].p4.i;
|
||||
r.default_rc = 0;
|
||||
r.aMem = &aMem[pOp[1].p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<r.nField; i++){
|
||||
@@ -4495,7 +4487,7 @@ case OP_SeekScan: {
|
||||
if( rc ) goto abort_due_to_error;
|
||||
if( res>0 ){
|
||||
seekscan_search_fail:
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... %d steps and then skip\n", pOp->p1 - nStep);
|
||||
}
|
||||
@@ -4505,7 +4497,7 @@ case OP_SeekScan: {
|
||||
goto jump_to_p2;
|
||||
}
|
||||
if( res==0 ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... %d steps and then success\n", pOp->p1 - nStep);
|
||||
}
|
||||
@@ -4515,7 +4507,7 @@ case OP_SeekScan: {
|
||||
break;
|
||||
}
|
||||
if( nStep<=0 ){
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags&SQLITE_VdbeTrace ){
|
||||
printf("... fall through after %d steps\n", pOp->p1);
|
||||
}
|
||||
@@ -4700,7 +4692,7 @@ case OP_Found: { /* jump, in3 */
|
||||
assert( pOp->p4type==P4_INT32 );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pC->seekOp = pOp->opcode;
|
||||
#endif
|
||||
pIn3 = &aMem[pOp->p3];
|
||||
@@ -4711,7 +4703,7 @@ case OP_Found: { /* jump, in3 */
|
||||
r.pKeyInfo = pC->pKeyInfo;
|
||||
r.nField = (u16)pOp->p4.i;
|
||||
r.aMem = pIn3;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
for(ii=0; ii<r.nField; ii++){
|
||||
assert( memIsValid(&r.aMem[ii]) );
|
||||
assert( (r.aMem[ii].flags & MEM_Zero)==0 || r.aMem[ii].n==0 );
|
||||
@@ -4843,7 +4835,7 @@ case OP_NotExists: /* jump, in3 */
|
||||
notExistsWithKey:
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid;
|
||||
#endif
|
||||
assert( pC->isTable );
|
||||
@@ -4909,8 +4901,10 @@ case OP_NewRowid: { /* out2 */
|
||||
VdbeCursor *pC; /* Cursor of table to get the new rowid */
|
||||
int res; /* Result of an sqlite3BtreeLast() */
|
||||
int cnt; /* Counter to limit the number of searches */
|
||||
#ifndef SQLITE_OMIT_AUTOINCREMENT
|
||||
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
|
||||
VdbeFrame *pFrame; /* Root frame of VDBE */
|
||||
#endif
|
||||
|
||||
v = 0;
|
||||
res = 0;
|
||||
@@ -5221,7 +5215,7 @@ case OP_Delete: {
|
||||
assert( pC->deferredMoveto==0 );
|
||||
sqlite3VdbeIncrWriteCounter(p, pC);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pOp->p4type==P4_TABLE
|
||||
&& HasRowid(pOp->p4.pTab)
|
||||
&& pOp->p5==0
|
||||
@@ -5274,7 +5268,7 @@ case OP_Delete: {
|
||||
assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION );
|
||||
assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE );
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( p->pFrame==0 ){
|
||||
if( pC->isEphemeral==0
|
||||
&& (pOp->p5 & OPFLAG_AUXDELETE)==0
|
||||
@@ -5514,7 +5508,7 @@ case OP_NullRow: {
|
||||
assert( pC->uc.pCursor!=0 );
|
||||
sqlite3BtreeClearCursor(pC->uc.pCursor);
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pC->seekOp==0 ) pC->seekOp = OP_NullRow;
|
||||
#endif
|
||||
break;
|
||||
@@ -5555,7 +5549,7 @@ case OP_Last: { /* jump */
|
||||
pCrsr = pC->uc.pCursor;
|
||||
res = 0;
|
||||
assert( pCrsr!=0 );
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pC->seekOp = pOp->opcode;
|
||||
#endif
|
||||
if( pOp->opcode==OP_SeekEnd ){
|
||||
@@ -5659,7 +5653,7 @@ case OP_Rewind: { /* jump */
|
||||
assert( pC!=0 );
|
||||
assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
|
||||
res = 1;
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pC->seekOp = OP_Rewind;
|
||||
#endif
|
||||
if( isSorter(pC) ){
|
||||
@@ -5910,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 );
|
||||
@@ -6089,7 +6083,7 @@ case OP_IdxGE: { /* jump */
|
||||
r.default_rc = 0;
|
||||
}
|
||||
r.aMem = &aMem[pOp->p3];
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
{
|
||||
int i;
|
||||
for(i=0; i<r.nField; i++){
|
||||
@@ -6322,7 +6316,7 @@ case OP_ParseSchema: {
|
||||
** on every btree. This is a prerequisite for invoking
|
||||
** sqlite3InitCallback().
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
for(iDb=0; iDb<db->nDb; iDb++){
|
||||
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
}
|
||||
@@ -6691,7 +6685,7 @@ case OP_Program: { /* jump */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
pFrame->anExec = p->anExec;
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
pFrame->iFrameMagic = SQLITE_FRAME_MAGIC;
|
||||
#endif
|
||||
|
||||
@@ -6730,7 +6724,7 @@ case OP_Program: { /* jump */
|
||||
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
|
||||
p->anExec = 0;
|
||||
#endif
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* Verify that second and subsequent executions of the same trigger do not
|
||||
** try to reuse register values from the first use. */
|
||||
{
|
||||
@@ -7021,7 +7015,7 @@ case OP_AggStep1: {
|
||||
pCtx = pOp->p4.pCtx;
|
||||
pMem = &aMem[pOp->p3];
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( pOp->p1 ){
|
||||
/* This is an OP_AggInverse call. Verify that xStep has always
|
||||
** been called at least once prior to any xInverse call. */
|
||||
@@ -7041,7 +7035,7 @@ case OP_AggStep1: {
|
||||
for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
for(i=0; i<pCtx->argc; i++){
|
||||
assert( memIsValid(pCtx->argv[i]) );
|
||||
REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
|
||||
@@ -7915,7 +7909,7 @@ case OP_Function: { /* group */
|
||||
assert( pCtx->pVdbe==p );
|
||||
|
||||
memAboutToChange(p, pOut);
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
for(i=0; i<pCtx->argc; i++){
|
||||
assert( memIsValid(pCtx->argv[i]) );
|
||||
REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
|
||||
@@ -8023,7 +8017,7 @@ case OP_Init: { /* jump */
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_USE_FCNTL_TRACE */
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( (db->flags & SQLITE_SqlTrace)!=0
|
||||
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
|
||||
){
|
||||
@@ -8067,7 +8061,7 @@ case OP_CursorHint: {
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* Opcode: Abortable * * * * *
|
||||
**
|
||||
** Verify that an Abort can happen. Assert if an Abort at this point
|
||||
@@ -8083,7 +8077,7 @@ case OP_Abortable: {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
/* Opcode: ReleaseReg P1 P2 P3 * P5
|
||||
** Synopsis: release r[P1@P2] mask P3
|
||||
**
|
||||
@@ -8173,7 +8167,7 @@ default: { /* This is really OP_Noop, OP_Explain */
|
||||
#ifndef NDEBUG
|
||||
assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] );
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
#if defined(SQLITE_DEBUG) || defined(DEBUG_VIRTUAL_MACHINE)
|
||||
if( db->flags & SQLITE_VdbeTrace ){
|
||||
u8 opProperty = sqlite3OpcodeProperty[pOrigOp->opcode];
|
||||
if( rc!=0 ) printf("rc=%d\n",rc);
|
||||
|
||||
@@ -259,6 +259,7 @@ void sqlite3VdbeResetStepResult(Vdbe*);
|
||||
void sqlite3VdbeRewind(Vdbe*);
|
||||
int sqlite3VdbeReset(Vdbe*);
|
||||
void sqlite3VdbeSetNumCols(Vdbe*,int);
|
||||
void sqlite3VdbeColumnInfoXfer(Vdbe*,Vdbe*);
|
||||
int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*));
|
||||
void sqlite3VdbeCountChanges(Vdbe*);
|
||||
sqlite3 *sqlite3VdbeDb(Vdbe*);
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+34
-17
@@ -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);
|
||||
@@ -565,7 +565,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 +890,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 +975,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 +1299,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 +1428,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 +2113,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 +2293,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 +2356,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;
|
||||
@@ -2595,6 +2595,23 @@ void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
|
||||
initMemArray(p->aColName, n, db, MEM_Null);
|
||||
}
|
||||
|
||||
/*
|
||||
** Transfer the column count and name information from one Vdbe to
|
||||
** another.
|
||||
*/
|
||||
void sqlite3VdbeColumnInfoXfer(Vdbe *pTo, Vdbe *pFrom){
|
||||
sqlite3 *db = pTo->db;
|
||||
assert( db==pFrom->db );
|
||||
if( pTo->nResColumn ){
|
||||
releaseMemArray(pTo->aColName, pTo->nResColumn*COLNAME_N);
|
||||
sqlite3DbFree(db, pTo->aColName);
|
||||
}
|
||||
pTo->aColName = pFrom->aColName;
|
||||
pFrom->aColName = 0;
|
||||
pTo->nResColumn = pFrom->nResColumn;
|
||||
pFrom->nResColumn = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the name of the idx'th column to be returned by the SQL statement.
|
||||
** zName must be a pointer to a nul terminated string.
|
||||
@@ -3041,7 +3058,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 +3216,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 +3389,7 @@ int sqlite3VdbeReset(Vdbe *p){
|
||||
}
|
||||
}
|
||||
#endif
|
||||
p->magic = VDBE_MAGIC_RESET;
|
||||
p->iVdbeMagic = VDBE_MAGIC_RESET;
|
||||
return p->rc & db->errMask;
|
||||
}
|
||||
|
||||
@@ -3382,7 +3399,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 +3460,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 +3508,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);
|
||||
}
|
||||
|
||||
+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 */
|
||||
|
||||
+1
-5
@@ -270,11 +270,7 @@ struct WhereTerm {
|
||||
#define TERM_ORINFO 0x0010 /* Need to free the WhereTerm.u.pOrInfo object */
|
||||
#define TERM_ANDINFO 0x0020 /* Need to free the WhereTerm.u.pAndInfo obj */
|
||||
#define TERM_OR_OK 0x0040 /* Used during OR-clause processing */
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
# define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
|
||||
#else
|
||||
# define TERM_VNULL 0x0000 /* Disabled if not using stat4 */
|
||||
#endif
|
||||
#define TERM_VNULL 0x0080 /* Manufactured x>NULL or x<=NULL term */
|
||||
#define TERM_LIKEOPT 0x0100 /* Virtual terms from the LIKE optimization */
|
||||
#define TERM_LIKECOND 0x0200 /* Conditionally this LIKE operator term */
|
||||
#define TERM_LIKE 0x0400 /* The original LIKE operator */
|
||||
|
||||
@@ -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.
|
||||
|
||||
+373
-98
@@ -1007,6 +1007,271 @@ static int exprMightBeIndexed(
|
||||
return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
|
||||
}
|
||||
|
||||
/*
|
||||
** Expression callback for exprUsesSrclist().
|
||||
*/
|
||||
static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
|
||||
if( pExpr->op==TK_COLUMN ){
|
||||
SrcList *pSrc = p->u.pSrcList;
|
||||
int iCsr = pExpr->iTable;
|
||||
int ii;
|
||||
for(ii=0; ii<pSrc->nSrc; ii++){
|
||||
if( pSrc->a[ii].iCursor==iCsr ){
|
||||
return p->eCode ? WRC_Abort : WRC_Continue;
|
||||
}
|
||||
}
|
||||
return p->eCode ? WRC_Continue : WRC_Abort;
|
||||
}
|
||||
return WRC_Continue;
|
||||
}
|
||||
|
||||
/*
|
||||
** Select callback for exprUsesSrclist().
|
||||
*/
|
||||
static int exprUsesSrclistSelectCb(Walker *p, Select *pSelect){
|
||||
return WRC_Abort;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function always returns true if expression pExpr contains
|
||||
** a sub-select.
|
||||
**
|
||||
** If there is no sub-select in pExpr, then return true if pExpr
|
||||
** contains a TK_COLUMN node for a table that is (bUses==1)
|
||||
** or is not (bUses==0) in pSrc.
|
||||
**
|
||||
** Said another way:
|
||||
**
|
||||
** bUses Return Meaning
|
||||
** -------- ------ ------------------------------------------------
|
||||
**
|
||||
** bUses==1 true pExpr contains either a sub-select or a
|
||||
** TK_COLUMN referencing pSrc.
|
||||
**
|
||||
** bUses==1 false pExpr contains no sub-selects and all TK_COLUMN
|
||||
** nodes reference tables not found in pSrc
|
||||
**
|
||||
** bUses==0 true pExpr contains either a sub-select or a TK_COLUMN
|
||||
** that references a table not in pSrc.
|
||||
**
|
||||
** bUses==0 false pExpr contains no sub-selects and all TK_COLUMN
|
||||
** nodes reference pSrc
|
||||
*/
|
||||
static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){
|
||||
Walker sWalker;
|
||||
memset(&sWalker, 0, sizeof(Walker));
|
||||
sWalker.eCode = bUses;
|
||||
sWalker.u.pSrcList = pSrc;
|
||||
sWalker.xExprCallback = exprUsesSrclistCb;
|
||||
sWalker.xSelectCallback = exprUsesSrclistSelectCb;
|
||||
return (sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort);
|
||||
}
|
||||
|
||||
/*
|
||||
** Context object used by exprExistsToInIter() as it iterates through an
|
||||
** expression tree.
|
||||
*/
|
||||
struct ExistsToInCtx {
|
||||
SrcList *pSrc; /* The tables in an EXISTS(SELECT ... FROM <here> ...) */
|
||||
Expr *pInLhs; /* OUT: Use this as the LHS of the IN operator */
|
||||
Expr *pEq; /* OUT: The == term that include pInLhs */
|
||||
Expr **ppAnd; /* OUT: The AND operator that includes pEq as a child */
|
||||
Expr **ppParent; /* The AND operator currently being examined */
|
||||
};
|
||||
|
||||
/*
|
||||
** Iterate through all AND connected nodes in the expression tree
|
||||
** headed by (*ppExpr), populating the structure passed as the first
|
||||
** argument with the values required by exprAnalyzeExistsFindEq().
|
||||
**
|
||||
** This function returns non-zero if the expression tree does not meet
|
||||
** the two conditions described by the header comment for
|
||||
** exprAnalyzeExistsFindEq(), or zero if it does.
|
||||
*/
|
||||
static int exprExistsToInIter(struct ExistsToInCtx *p, Expr **ppExpr){
|
||||
Expr *pExpr = *ppExpr;
|
||||
switch( pExpr->op ){
|
||||
case TK_AND:
|
||||
p->ppParent = ppExpr;
|
||||
if( exprExistsToInIter(p, &pExpr->pLeft) ) return 1;
|
||||
p->ppParent = ppExpr;
|
||||
if( exprExistsToInIter(p, &pExpr->pRight) ) return 1;
|
||||
break;
|
||||
case TK_EQ: {
|
||||
int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0);
|
||||
int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0);
|
||||
if( bLeft || bRight ){
|
||||
if( (bLeft && bRight) || p->pInLhs ) return 1;
|
||||
p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight;
|
||||
if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1;
|
||||
p->pEq = pExpr;
|
||||
p->ppAnd = p->ppParent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if( exprUsesSrclist(p->pSrc, pExpr, 0) ){
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is used by exprAnalyzeExists() when creating virtual IN(...)
|
||||
** terms equivalent to user-supplied EXIST(...) clauses. It splits the WHERE
|
||||
** clause of the Select object passed as the first argument into one or more
|
||||
** expressions joined by AND operators, and then tests if the following are
|
||||
** true:
|
||||
**
|
||||
** 1. Exactly one of the AND separated terms refers to the outer
|
||||
** query, and it is an == (TK_EQ) expression.
|
||||
**
|
||||
** 2. Only one side of the == expression refers to the outer query, and
|
||||
** it does not refer to any columns from the inner query.
|
||||
**
|
||||
** If both these conditions are true, then a pointer to the side of the ==
|
||||
** expression that refers to the outer query is returned. The caller will
|
||||
** use this expression as the LHS of the IN(...) virtual term. Or, if one
|
||||
** or both of the above conditions are not true, NULL is returned.
|
||||
**
|
||||
** If non-NULL is returned and ppEq is non-NULL, *ppEq is set to point
|
||||
** to the == expression node before returning. If pppAnd is non-NULL and
|
||||
** the == node is not the root of the WHERE clause, then *pppAnd is set
|
||||
** to point to the pointer to the AND node that is the parent of the ==
|
||||
** node within the WHERE expression tree.
|
||||
*/
|
||||
static Expr *exprAnalyzeExistsFindEq(
|
||||
Select *pSel, /* The SELECT of the EXISTS */
|
||||
Expr **ppEq, /* OUT: == node from WHERE clause */
|
||||
Expr ***pppAnd /* OUT: Pointer to parent of ==, if any */
|
||||
){
|
||||
struct ExistsToInCtx ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.pSrc = pSel->pSrc;
|
||||
if( exprExistsToInIter(&ctx, &pSel->pWhere) ){
|
||||
return 0;
|
||||
}
|
||||
if( ppEq ) *ppEq = ctx.pEq;
|
||||
if( pppAnd ) *pppAnd = ctx.ppAnd;
|
||||
return ctx.pInLhs;
|
||||
}
|
||||
|
||||
/*
|
||||
** Term idxTerm of the WHERE clause passed as the second argument is an
|
||||
** EXISTS expression with a correlated SELECT statement on the RHS.
|
||||
** This function analyzes the SELECT statement, and if possible adds an
|
||||
** equivalent "? IN(SELECT...)" virtual term to the WHERE clause.
|
||||
**
|
||||
** For an EXISTS term such as the following:
|
||||
**
|
||||
** EXISTS (SELECT ... FROM <srclist> WHERE <e1> = <e2> AND <e3>)
|
||||
**
|
||||
** The virtual IN() term added is:
|
||||
**
|
||||
** <e1> IN (SELECT <e2> FROM <srclist> WHERE <e3>)
|
||||
**
|
||||
** The virtual term is only added if the following conditions are met:
|
||||
**
|
||||
** 1. The sub-select must not be an aggregate or use window functions,
|
||||
**
|
||||
** 2. The sub-select must not be a compound SELECT,
|
||||
**
|
||||
** 3. Expression <e1> must refer to at least one column from the outer
|
||||
** query, and must not refer to any column from the inner query
|
||||
** (i.e. from <srclist>).
|
||||
**
|
||||
** 4. <e2> and <e3> must not refer to any values from the outer query.
|
||||
** In other words, once <e1> has been removed, the inner query
|
||||
** must not be correlated.
|
||||
**
|
||||
*/
|
||||
static void exprAnalyzeExists(
|
||||
SrcList *pSrc, /* the FROM clause */
|
||||
WhereClause *pWC, /* the WHERE clause */
|
||||
int idxTerm /* Index of the term to be analyzed */
|
||||
){
|
||||
Parse *pParse = pWC->pWInfo->pParse;
|
||||
WhereTerm *pTerm = &pWC->a[idxTerm];
|
||||
Expr *pExpr = pTerm->pExpr;
|
||||
Select *pSel = pExpr->x.pSelect;
|
||||
Expr *pDup = 0;
|
||||
Expr *pEq = 0;
|
||||
Expr *pRet = 0;
|
||||
Expr *pInLhs = 0;
|
||||
Expr **ppAnd = 0;
|
||||
int idxNew;
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
assert( pExpr->op==TK_EXISTS );
|
||||
assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );
|
||||
|
||||
if( (pSel->selFlags & SF_Aggregate) || pSel->pWin ) return;
|
||||
if( pSel->pPrior ) return;
|
||||
if( pSel->pWhere==0 ) return;
|
||||
if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;
|
||||
|
||||
pDup = sqlite3ExprDup(db, pExpr, 0);
|
||||
if( db->mallocFailed ){
|
||||
sqlite3ExprDelete(db, pDup);
|
||||
return;
|
||||
}
|
||||
pSel = pDup->x.pSelect;
|
||||
sqlite3ExprListDelete(db, pSel->pEList);
|
||||
pSel->pEList = 0;
|
||||
|
||||
pInLhs = exprAnalyzeExistsFindEq(pSel, &pEq, &ppAnd);
|
||||
assert( pInLhs && pEq );
|
||||
assert( pEq==pSel->pWhere || ppAnd );
|
||||
if( pInLhs==pEq->pLeft ){
|
||||
pRet = pEq->pRight;
|
||||
}else{
|
||||
CollSeq *p = sqlite3ExprCompareCollSeq(pParse, pEq);
|
||||
pInLhs = sqlite3ExprAddCollateString(pParse, pInLhs, p?p->zName:"BINARY");
|
||||
pRet = pEq->pLeft;
|
||||
}
|
||||
|
||||
assert( pDup->pLeft==0 );
|
||||
pDup->op = TK_IN;
|
||||
pDup->pLeft = pInLhs;
|
||||
pDup->flags &= ~EP_VarSelect;
|
||||
if( pRet->op==TK_VECTOR ){
|
||||
pSel->pEList = pRet->x.pList;
|
||||
pRet->x.pList = 0;
|
||||
sqlite3ExprDelete(db, pRet);
|
||||
}else{
|
||||
pSel->pEList = sqlite3ExprListAppend(pParse, 0, pRet);
|
||||
}
|
||||
pEq->pLeft = 0;
|
||||
pEq->pRight = 0;
|
||||
if( ppAnd ){
|
||||
Expr *pAnd = *ppAnd;
|
||||
Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft;
|
||||
pAnd->pLeft = pAnd->pRight = 0;
|
||||
sqlite3ExprDelete(db, pAnd);
|
||||
*ppAnd = pOther;
|
||||
}else{
|
||||
assert( pSel->pWhere==pEq );
|
||||
pSel->pWhere = 0;
|
||||
}
|
||||
sqlite3ExprDelete(db, pEq);
|
||||
|
||||
#ifdef WHERETRACE_ENABLED /* 0x20 */
|
||||
if( sqlite3WhereTrace & 0x20 ){
|
||||
sqlite3DebugPrintf("Convert EXISTS:\n");
|
||||
sqlite3TreeViewExpr(0, pExpr, 0);
|
||||
sqlite3DebugPrintf("into IN:\n");
|
||||
sqlite3TreeViewExpr(0, pDup, 0);
|
||||
}
|
||||
#endif
|
||||
idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
pWC->a[idxTerm].wtFlags |= TERM_COPIED;
|
||||
}
|
||||
|
||||
/*
|
||||
** The input to this routine is an WhereTerm structure with only the
|
||||
** "pExpr" field filled in. The job of this routine is to analyze the
|
||||
@@ -1192,6 +1457,52 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
|
||||
|
||||
else if( pExpr->op==TK_EXISTS ){
|
||||
/* Perhaps treat an EXISTS operator as an IN operator */
|
||||
if( (pExpr->flags & EP_VarSelect)!=0
|
||||
&& OptimizationEnabled(db, SQLITE_ExistsToIN)
|
||||
){
|
||||
exprAnalyzeExists(pSrc, pWC, idxTerm);
|
||||
}
|
||||
}
|
||||
|
||||
/* The form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
|
||||
** virtual term of that form.
|
||||
**
|
||||
** The virtual term must be tagged with TERM_VNULL.
|
||||
*/
|
||||
else if( pExpr->op==TK_NOTNULL ){
|
||||
if( pExpr->pLeft->op==TK_COLUMN
|
||||
&& pExpr->pLeft->iColumn>=0
|
||||
&& !ExprHasProperty(pExpr, EP_FromJoin)
|
||||
){
|
||||
Expr *pNewExpr;
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
int idxNew;
|
||||
WhereTerm *pNewTerm;
|
||||
|
||||
pNewExpr = sqlite3PExpr(pParse, TK_GT,
|
||||
sqlite3ExprDup(db, pLeft, 0),
|
||||
sqlite3ExprAlloc(db, TK_NULL, 0, 0));
|
||||
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr,
|
||||
TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
|
||||
if( idxNew ){
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
pNewTerm->prereqRight = 0;
|
||||
pNewTerm->leftCursor = pLeft->iTable;
|
||||
pNewTerm->u.x.leftColumn = pLeft->iColumn;
|
||||
pNewTerm->eOperator = WO_GT;
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_COPIED;
|
||||
pNewTerm->prereqAll = pTerm->prereqAll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
|
||||
/* Add constraints to reduce the search space on a LIKE or GLOB
|
||||
** operator.
|
||||
@@ -1206,7 +1517,8 @@ static void exprAnalyze(
|
||||
** bound is made all lowercase so that the bounds also work when comparing
|
||||
** BLOBs.
|
||||
*/
|
||||
if( pWC->op==TK_AND
|
||||
else if( pExpr->op==TK_FUNCTION
|
||||
&& pWC->op==TK_AND
|
||||
&& isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
|
||||
){
|
||||
Expr *pLeft; /* LHS of LIKE/GLOB operator */
|
||||
@@ -1276,6 +1588,65 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
|
||||
|
||||
/* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
|
||||
** new terms for each component comparison - "a = ?" and "b = ?". The
|
||||
** new terms completely replace the original vector comparison, which is
|
||||
** no longer used.
|
||||
**
|
||||
** This is only required if at least one side of the comparison operation
|
||||
** is not a sub-select. */
|
||||
if( (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||
&& (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
|
||||
&& sqlite3ExprVectorSize(pExpr->pRight)==nLeft
|
||||
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|
||||
|| (pExpr->pRight->flags & EP_xIsSelect)==0)
|
||||
&& pWC->op==TK_AND
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<nLeft; i++){
|
||||
int idxNew;
|
||||
Expr *pNew;
|
||||
Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
|
||||
Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
|
||||
|
||||
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->eOperator = 0;
|
||||
}
|
||||
|
||||
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
|
||||
** a virtual term for each vector component. The expression object
|
||||
** used by each such virtual term is pExpr (the full vector IN(...)
|
||||
** expression). The WhereTerm.u.x.iField variable identifies the index within
|
||||
** the vector on the LHS that the virtual term represents.
|
||||
**
|
||||
** This only works if the RHS is a simple SELECT (not a compound) that does
|
||||
** not use window functions.
|
||||
*/
|
||||
else if( pExpr->op==TK_IN
|
||||
&& pTerm->u.x.iField==0
|
||||
&& pExpr->pLeft->op==TK_VECTOR
|
||||
&& pExpr->x.pSelect->pPrior==0
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
&& pExpr->x.pSelect->pWin==0
|
||||
#endif
|
||||
&& pWC->op==TK_AND
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
|
||||
int idxNew;
|
||||
idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
|
||||
pWC->a[idxNew].u.x.iField = i+1;
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
/* Add a WO_AUX auxiliary term to the constraint set if the
|
||||
** current expression is of the form "column OP expr" where OP
|
||||
@@ -1286,7 +1657,7 @@ static void exprAnalyze(
|
||||
** virtual tables. The native query optimizer does not attempt
|
||||
** to do anything with MATCH functions.
|
||||
*/
|
||||
if( pWC->op==TK_AND ){
|
||||
else if( pWC->op==TK_AND ){
|
||||
Expr *pRight = 0, *pLeft = 0;
|
||||
int res = isAuxiliaryVtabOperator(db, pExpr, &eOp2, &pLeft, &pRight);
|
||||
while( res-- > 0 ){
|
||||
@@ -1322,102 +1693,6 @@ static void exprAnalyze(
|
||||
}
|
||||
#endif /* SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
|
||||
** new terms for each component comparison - "a = ?" and "b = ?". The
|
||||
** new terms completely replace the original vector comparison, which is
|
||||
** no longer used.
|
||||
**
|
||||
** This is only required if at least one side of the comparison operation
|
||||
** is not a sub-select. */
|
||||
if( pWC->op==TK_AND
|
||||
&& (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||
&& (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
|
||||
&& sqlite3ExprVectorSize(pExpr->pRight)==nLeft
|
||||
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|
||||
|| (pExpr->pRight->flags & EP_xIsSelect)==0)
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<nLeft; i++){
|
||||
int idxNew;
|
||||
Expr *pNew;
|
||||
Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
|
||||
Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
|
||||
|
||||
pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight);
|
||||
transferJoinMarkings(pNew, pExpr);
|
||||
idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
}
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_CODED|TERM_VIRTUAL; /* Disable the original */
|
||||
pTerm->eOperator = 0;
|
||||
}
|
||||
|
||||
/* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
|
||||
** a virtual term for each vector component. The expression object
|
||||
** used by each such virtual term is pExpr (the full vector IN(...)
|
||||
** expression). The WhereTerm.u.x.iField variable identifies the index within
|
||||
** the vector on the LHS that the virtual term represents.
|
||||
**
|
||||
** This only works if the RHS is a simple SELECT (not a compound) that does
|
||||
** not use window functions.
|
||||
*/
|
||||
if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->u.x.iField==0
|
||||
&& pExpr->pLeft->op==TK_VECTOR
|
||||
&& pExpr->x.pSelect->pPrior==0
|
||||
#ifndef SQLITE_OMIT_WINDOWFUNC
|
||||
&& pExpr->x.pSelect->pWin==0
|
||||
#endif
|
||||
){
|
||||
int i;
|
||||
for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
|
||||
int idxNew;
|
||||
idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
|
||||
pWC->a[idxNew].u.x.iField = i+1;
|
||||
exprAnalyze(pSrc, pWC, idxNew);
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_STAT4
|
||||
/* When sqlite_stat4 histogram data is available an operator of the
|
||||
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
|
||||
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
|
||||
** virtual term of that form.
|
||||
**
|
||||
** Note that the virtual term must be tagged with TERM_VNULL.
|
||||
*/
|
||||
if( pExpr->op==TK_NOTNULL
|
||||
&& pExpr->pLeft->op==TK_COLUMN
|
||||
&& pExpr->pLeft->iColumn>=0
|
||||
&& !ExprHasProperty(pExpr, EP_FromJoin)
|
||||
&& OptimizationEnabled(db, SQLITE_Stat4)
|
||||
){
|
||||
Expr *pNewExpr;
|
||||
Expr *pLeft = pExpr->pLeft;
|
||||
int idxNew;
|
||||
WhereTerm *pNewTerm;
|
||||
|
||||
pNewExpr = sqlite3PExpr(pParse, TK_GT,
|
||||
sqlite3ExprDup(db, pLeft, 0),
|
||||
sqlite3ExprAlloc(db, TK_NULL, 0, 0));
|
||||
|
||||
idxNew = whereClauseInsert(pWC, pNewExpr,
|
||||
TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
|
||||
if( idxNew ){
|
||||
pNewTerm = &pWC->a[idxNew];
|
||||
pNewTerm->prereqRight = 0;
|
||||
pNewTerm->leftCursor = pLeft->iTable;
|
||||
pNewTerm->u.x.leftColumn = pLeft->iColumn;
|
||||
pNewTerm->eOperator = WO_GT;
|
||||
markTermAsChild(pWC, idxNew, idxTerm);
|
||||
pTerm = &pWC->a[idxTerm];
|
||||
pTerm->wtFlags |= TERM_COPIED;
|
||||
pNewTerm->prereqAll = pTerm->prereqAll;
|
||||
}
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_STAT4 */
|
||||
|
||||
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
|
||||
** an index for tables to the left of the join.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -253,7 +253,7 @@ do_execsql_test 11.1 {
|
||||
|
||||
do_catchsql_test 11.2 {
|
||||
ALTER TABLE t1 RENAME TO t1x;
|
||||
} {1 {error in trigger b: no such table: abc}}
|
||||
} {1 {error in trigger b: no such table: main.abc}}
|
||||
|
||||
do_execsql_test 11.3 {
|
||||
DROP TRIGGER b;
|
||||
|
||||
+8
-2
@@ -21,7 +21,7 @@ source $testdir/tester.tcl
|
||||
# Only run these tests if memory debugging is turned on.
|
||||
#
|
||||
ifcapable !crashtest||!memorymanage {
|
||||
puts "Skipping crash5 tests: not compiled with -DSQLITE_MEMDEBUG..."
|
||||
puts "Skipping crash5 tests: not compiled with -DSQLITE_ENABLE_MEMORY_MANAGEMENT..."
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
@@ -49,8 +49,14 @@ for {set ii 0} {$ii < 10} {incr ii} {
|
||||
[list set iFail $jj] {
|
||||
proc get_pwd {} {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
if {[info exists ::env(ComSpec)]} {
|
||||
set comSpec $::env(ComSpec)
|
||||
} else {
|
||||
# NOTE: Hard-code the typical default value.
|
||||
set comSpec {C:\Windows\system32\cmd.exe}
|
||||
}
|
||||
return [string map [list \\ /] \
|
||||
[string trim [exec -- $::env(ComSpec) /c echo %CD%]]]
|
||||
[string trim [exec -- $comSpec /c echo %CD%]]]
|
||||
} else {
|
||||
return [pwd]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
# 2021 January 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 cases where EXISTS expressions are
|
||||
# transformed to IN() expressions by where.c
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix exists2
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
INSERT INTO t1 VALUES(6, 'six');
|
||||
INSERT INTO t1 VALUES(7, 'seven');
|
||||
|
||||
CREATE TABLE t2(c INTEGER, d INTEGER);
|
||||
INSERT INTO t2 VALUES(1, 1);
|
||||
INSERT INTO t2 VALUES(3, 2);
|
||||
INSERT INTO t2 VALUES(5, 3);
|
||||
INSERT INTO t2 VALUES(7, 4);
|
||||
}
|
||||
|
||||
proc do_execsql_eqp_test {tn sql eqp res} {
|
||||
uplevel [list do_eqp_test $tn.1 $sql [string trim $eqp]]
|
||||
uplevel [list do_execsql_test $tn.2 $sql $res]
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.1 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t1.a=t2.c);
|
||||
} {
|
||||
USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
1 one 3 three 5 five 7 seven
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.2 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c=t1.a);
|
||||
} {
|
||||
SEARCH TABLE t1 USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
1 one 3 three 5 five 7 seven
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.3 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a);
|
||||
} {
|
||||
SEARCH TABLE t1 USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
2 two 4 four 6 six
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.4 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c+1=t1.a+1);
|
||||
} {
|
||||
SCAN TABLE t1
|
||||
} {
|
||||
1 one 3 three 5 five 7 seven
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.5 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(
|
||||
SELECT * FROM t2 WHERE t1.a=t2.c AND d IN (1, 2, 3)
|
||||
);
|
||||
} {
|
||||
SEARCH TABLE t1 USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
1 one 3 three 5 five
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.6 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(
|
||||
SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
|
||||
);
|
||||
} {
|
||||
SEARCH TABLE t1 USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
1 one 3 three 5 five
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 1.7 {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(
|
||||
SELECT * FROM t2 WHERE d IN (1, 2, 3)AND t1.a=t2.c
|
||||
);
|
||||
} {
|
||||
SEARCH TABLE t1 USING INTEGER PRIMARY KEY
|
||||
} {
|
||||
1 one 3 three 5 five
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t3(a TEXT PRIMARY KEY, b TEXT, x INT) WITHOUT ROWID;
|
||||
CREATE TABLE t4(c TEXT COLLATE nocase, y INT);
|
||||
|
||||
INSERT INTO t3 VALUES('one', 'i', 1);
|
||||
INSERT INTO t3 VALUES('two', 'ii', 2);
|
||||
INSERT INTO t3 VALUES('three', 'iii', 3);
|
||||
INSERT INTO t3 VALUES('four', 'iv', 4);
|
||||
INSERT INTO t3 VALUES('five', 'v', 5);
|
||||
|
||||
INSERT INTO t4 VALUES('FIVE',5), ('four',4), ('TWO',2), ('one',1);
|
||||
}
|
||||
|
||||
do_execsql_test 2.1 { SELECT a FROM t3, t4 WHERE a=c } {four one}
|
||||
do_execsql_test 2.2 { SELECT a FROM t3, t4 WHERE c=a } {five four one two}
|
||||
|
||||
do_execsql_eqp_test 2.3 {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
|
||||
} {
|
||||
SEARCH TABLE t3 USING PRIMARY KEY
|
||||
} {
|
||||
four one
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 2.4 {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
|
||||
} {
|
||||
SCAN TABLE t3
|
||||
} {
|
||||
five four one two
|
||||
}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
CREATE INDEX t3anc ON t3(a COLLATE nocase, x);
|
||||
}
|
||||
|
||||
do_execsql_eqp_test 2.6 {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE c=a)
|
||||
} {
|
||||
SEARCH TABLE t3 USING COVERING INDEX t3anc
|
||||
} {
|
||||
five four one two
|
||||
}
|
||||
do_execsql_test 2.6a {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (c,y)=(a,x))
|
||||
} {five four one two}
|
||||
|
||||
do_execsql_eqp_test 2.7 {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE a=c)
|
||||
} {
|
||||
SEARCH TABLE t3 USING PRIMARY KEY
|
||||
} {
|
||||
four one
|
||||
}
|
||||
do_execsql_test 2.7a {
|
||||
SELECT a FROM t3 WHERE EXISTS (SELECT 1 FROM t4 WHERE (a,x)=(c,y))
|
||||
} {
|
||||
four one
|
||||
}
|
||||
|
||||
# EXISTS clauses using vector expressions in the WHERE clause.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a,b);
|
||||
INSERT INTO t1(a,b) VALUES(1,111),(2,222),(8,888);
|
||||
CREATE TABLE t2(x INTEGER PRIMARY KEY, y);
|
||||
INSERT INTO t2(x,y) VALUES(2,222),(3,333),(7,333);
|
||||
SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,y)=(a,b));
|
||||
} {222}
|
||||
do_execsql_test 3.1 {
|
||||
SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (a,b)=(x,y));
|
||||
} {222}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT y FROM t2 WHERE EXISTS(SELECT 1 FROM t1 WHERE (x,b)=(a,y));
|
||||
} {222}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -0,0 +1,52 @@
|
||||
# 2021 January 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 cases where EXISTS expressions are
|
||||
# transformed to IN() expressions by where.c
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix existsfault
|
||||
|
||||
do_execsql_test 1 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
INSERT INTO t1 VALUES(6, 'six');
|
||||
INSERT INTO t1 VALUES(7, 'seven');
|
||||
|
||||
CREATE TABLE t2(c INTEGER, d INTEGER);
|
||||
INSERT INTO t2 VALUES(1, 1);
|
||||
INSERT INTO t2 VALUES(3, 2);
|
||||
INSERT INTO t2 VALUES(5, 3);
|
||||
INSERT INTO t2 VALUES(7, 4);
|
||||
}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_faultsim_test 1 -prep {
|
||||
faultsim_restore_and_reopen
|
||||
} -body {
|
||||
execsql {
|
||||
SELECT t1.* FROM t1 WHERE EXISTS(
|
||||
SELECT * FROM t2 WHERE t2.c=t1.a AND d IN (1, 2, 3)
|
||||
)
|
||||
}
|
||||
} -test {
|
||||
faultsim_test_result {0 {1 one 3 three 5 five}}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+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.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+9
-3
@@ -174,8 +174,14 @@ proc get_pwd {} {
|
||||
# case of the result to what Tcl considers canonical, which would
|
||||
# defeat the purpose of this procedure.
|
||||
#
|
||||
if {[info exists ::env(ComSpec)]} {
|
||||
set comSpec $::env(ComSpec)
|
||||
} else {
|
||||
# NOTE: Hard-code the typical default value.
|
||||
set comSpec {C:\Windows\system32\cmd.exe}
|
||||
}
|
||||
return [string map [list \\ /] \
|
||||
[string trim [exec -- $::env(ComSpec) /c echo %CD%]]]
|
||||
[string trim [exec -- $comSpec /c echo %CD%]]]
|
||||
} else {
|
||||
return [pwd]
|
||||
}
|
||||
@@ -902,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
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ foreach {tn defn} {
|
||||
8 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = ?; END; }
|
||||
9 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = 1 WHERE d = ?; END; }
|
||||
10 { AFTER INSERT ON t1 BEGIN SELECT * FROM pragma_stats(?); END; }
|
||||
11 { BEFORE INSERT ON t1 BEGIN
|
||||
INSERT INTO t1 SELECT max(b) OVER(ORDER BY $1) FROM t1; END }
|
||||
} {
|
||||
catchsql {drop trigger tr1}
|
||||
do_catchsql_test 1.1.$tn "CREATE TRIGGER tr1 $defn" [list 1 $errmsg]
|
||||
|
||||
+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 {
|
||||
|
||||
@@ -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