mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
:-) (CVS 74)
FossilOrigin-Name: 2ffeb8509c469f5a499d56bb109da079fcdff570
This commit is contained in:
30
manifest
30
manifest
@@ -1,26 +1,26 @@
|
|||||||
C :-)\s(CVS\s73)
|
C :-)\s(CVS\s74)
|
||||||
D 2000-06-07T15:39:04
|
D 2000-06-07T23:51:50
|
||||||
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
|
||||||
F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
|
F Makefile.in 17ba1ccf8d2d40c627796bba8f72952365d6d644
|
||||||
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
|
||||||
F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
|
F configure 00a5b5c82147a576fa6e82d7c1b0d55c321d6d2c x
|
||||||
F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
|
F configure.in 6ccfd5fc80517f7cfe605a7fc7e0f62d962a233c
|
||||||
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
|
||||||
F src/build.c 6c9454b2e2b866979527fb41b19ad8bc49c27a20
|
F src/build.c 5e3b6bab5604cd99019ea6d58f9166d879476991
|
||||||
F src/dbbe.c 9b191b16ff01ec5bc0af436558501d07938ba4f0
|
F src/dbbe.c 9b191b16ff01ec5bc0af436558501d07938ba4f0
|
||||||
F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
|
F src/dbbe.h a8a46f71238e0f09f3ec08fd9d1c8c7f4cdc49bf
|
||||||
F src/delete.c 8c733bb82a1b84126116d03dcdccf433c0856f5d
|
F src/delete.c c267b93f7ccb5493b677fa18201880267c699aa8
|
||||||
F src/expr.c d350fe393e1753aaa733a5d21f0830a23e547400
|
F src/expr.c baa8a4229b3acf69d908efcd697ab63922009c9f
|
||||||
F src/insert.c 93f9e36bf96b57326d8d5709ed44b568b8627aa5
|
F src/insert.c ac4edfff474589c00b2490f206317dc5822122e5
|
||||||
F src/main.c b4275952180b30e1ca3db266157e2d233c0a24d9
|
F src/main.c e3297835b8e38ca726ac73f2c2bdb7cf08103197
|
||||||
F src/parse.y 4a2e6b14177c97f61fc3e21d4570f5a9c541c1e1
|
F src/parse.y bb2126c8313c111184b89af8675911dcb57f1dca
|
||||||
F src/select.c 4834ab68a3308871f17fe8e22d903fcdf1b42420
|
F src/select.c 21d1097e32e0a8430c286309817be33b734eab9d
|
||||||
F src/shell.c 3f4afc39a36e4824e8aa262623fd03568874799e
|
F src/shell.c 3f4afc39a36e4824e8aa262623fd03568874799e
|
||||||
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
F src/sqlite.h 58da0a8590133777b741f9836beaef3d58f40268
|
||||||
F src/sqliteInt.h 1ac4ba75fef8249b9bd64a7736d0f584092f24a5
|
F src/sqliteInt.h 816c491f9896090dde03804fd3f60346456b99df
|
||||||
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
F src/tclsqlite.c 9f358618ae803bedf4fb96da5154fd45023bc1f7
|
||||||
F src/tokenize.c 900af9479d0feaa76b0225680196aa81afec930a
|
F src/tokenize.c 900af9479d0feaa76b0225680196aa81afec930a
|
||||||
F src/update.c 18746f920f989b3d19d96c08263c92584823cd35
|
F src/update.c d8d90df714bac99c68446a0c49f3d957ca6fc3c8
|
||||||
F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
|
F src/util.c 33f9baa01e45394ef0cf85361a0e872987884315
|
||||||
F src/vdbe.c 0ce44df13c97472686b376cc93cca5f40079878d
|
F src/vdbe.c 0ce44df13c97472686b376cc93cca5f40079878d
|
||||||
F src/vdbe.h 8f79f57c66ce1030f6371ff067b326d627a52c6d
|
F src/vdbe.h 8f79f57c66ce1030f6371ff067b326d627a52c6d
|
||||||
@@ -30,7 +30,7 @@ F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb
|
|||||||
F test/dbbe.test 3978ab21ff2a0531a85618c538d27047d560fc5d
|
F test/dbbe.test 3978ab21ff2a0531a85618c538d27047d560fc5d
|
||||||
F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
|
F test/delete.test 30451333f89479d2deb5410edd3f3cce67339944
|
||||||
F test/expr.test 7d017f1aa64c981b161408a015424cd90592bc16
|
F test/expr.test 7d017f1aa64c981b161408a015424cd90592bc16
|
||||||
F test/in.test 17cd46a9ca0e5d4a804483e6fb496458494858e6
|
F test/in.test 962a605b6a3a619214f84d1950dfc44fcf0d8b8f
|
||||||
F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
|
F test/index.test 9f99dca2d904b8de330863a978587f136e2df65a
|
||||||
F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
|
F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c
|
||||||
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
|
F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6
|
||||||
@@ -54,7 +54,7 @@ F www/c_interface.tcl 9ac800854272db5fe439e07b7435b243a5422293
|
|||||||
F www/changes.tcl 04e66b4257589ff78a7e1de93e9dda4725fb03d6
|
F www/changes.tcl 04e66b4257589ff78a7e1de93e9dda4725fb03d6
|
||||||
F www/index.tcl 52e29a4eeda8d59e91af43c61fef177c5f2ffd53
|
F www/index.tcl 52e29a4eeda8d59e91af43c61fef177c5f2ffd53
|
||||||
F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
|
F www/sqlite.tcl 2f933ce18cffd34a0a020a82435ab937137970fd
|
||||||
P 6f8de336d0c0c5b74fa2ff541ab974ba13638a58
|
P a00b81b0e1878487a6edaaca1b0dff002ad506d8
|
||||||
R e8ba4da21fa9f208a62c1c1e8095f5b6
|
R edeb782218e986f032924498f63df3c4
|
||||||
U drh
|
U drh
|
||||||
Z 9c51fcb8cc6b0390f306d27b43fea58b
|
Z e7171fdd9bf45a1d225b747155ebb197
|
||||||
|
@@ -1 +1 @@
|
|||||||
a00b81b0e1878487a6edaaca1b0dff002ad506d8
|
2ffeb8509c469f5a499d56bb109da079fcdff570
|
24
src/build.c
24
src/build.c
@@ -33,7 +33,7 @@
|
|||||||
** COPY
|
** COPY
|
||||||
** VACUUM
|
** VACUUM
|
||||||
**
|
**
|
||||||
** $Id: build.c,v 1.15 2000/06/05 18:54:46 drh Exp $
|
** $Id: build.c,v 1.16 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -60,6 +60,7 @@ void sqliteExec(Parse *pParse){
|
|||||||
}
|
}
|
||||||
sqliteVdbeDelete(pParse->pVdbe);
|
sqliteVdbeDelete(pParse->pVdbe);
|
||||||
pParse->pVdbe = 0;
|
pParse->pVdbe = 0;
|
||||||
|
pParse->colNamesSet = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,11 +331,9 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
|
|||||||
{ OP_Close, 0, 0, 0},
|
{ OP_Close, 0, 0, 0},
|
||||||
};
|
};
|
||||||
int n, base;
|
int n, base;
|
||||||
Vdbe *v = pParse->pVdbe;
|
Vdbe *v;
|
||||||
|
|
||||||
if( v==0 ){
|
v = sqliteGetVdbe(pParse);
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) return;
|
if( v==0 ) return;
|
||||||
n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1;
|
n = (int)pEnd->z - (int)pParse->sFirstToken.z + 1;
|
||||||
base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
|
base = sqliteVdbeAddOpList(v, ArraySize(addTable), addTable);
|
||||||
@@ -379,10 +378,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Generate code to remove the table and its reference in sys_master */
|
/* Generate code to remove the table and its reference in sys_master */
|
||||||
v = pParse->pVdbe;
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ){
|
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v ){
|
if( v ){
|
||||||
static VdbeOp dropTable[] = {
|
static VdbeOp dropTable[] = {
|
||||||
{ OP_Open, 0, 1, MASTER_NAME },
|
{ OP_Open, 0, 1, MASTER_NAME },
|
||||||
@@ -576,9 +572,7 @@ void sqliteCreateIndex(
|
|||||||
int lbl1, lbl2;
|
int lbl1, lbl2;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if( v==0 ){
|
v = sqliteGetVdbe(pParse);
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) goto exit_create_index;
|
if( v==0 ) goto exit_create_index;
|
||||||
sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
|
sqliteVdbeAddOp(v, OP_Open, 0, 0, pTab->zName, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Open, 1, 1, pIndex->zName, 0);
|
sqliteVdbeAddOp(v, OP_Open, 1, 1, pIndex->zName, 0);
|
||||||
@@ -637,7 +631,7 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Generate code to remove the index and from the master table */
|
/* Generate code to remove the index and from the master table */
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v ){
|
if( v ){
|
||||||
static VdbeOp dropIndex[] = {
|
static VdbeOp dropIndex[] = {
|
||||||
{ OP_Open, 0, 1, MASTER_NAME},
|
{ OP_Open, 0, 1, MASTER_NAME},
|
||||||
@@ -808,7 +802,7 @@ void sqliteCopy(
|
|||||||
pParse->nErr++;
|
pParse->nErr++;
|
||||||
goto copy_cleanup;
|
goto copy_cleanup;
|
||||||
}
|
}
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v ){
|
if( v ){
|
||||||
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0);
|
addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0, 0, 0);
|
||||||
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
|
sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
|
||||||
@@ -872,7 +866,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
|
|||||||
pParse->nErr++;
|
pParse->nErr++;
|
||||||
goto vacuum_cleanup;
|
goto vacuum_cleanup;
|
||||||
}
|
}
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ) goto vacuum_cleanup;
|
if( v==0 ) goto vacuum_cleanup;
|
||||||
if( zName ){
|
if( zName ){
|
||||||
sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0);
|
sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0);
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle DELETE FROM statements.
|
** to handle DELETE FROM statements.
|
||||||
**
|
**
|
||||||
** $Id: delete.c,v 1.3 2000/06/06 13:54:15 drh Exp $
|
** $Id: delete.c,v 1.4 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -82,10 +82,7 @@ void sqliteDeleteFrom(
|
|||||||
|
|
||||||
/* Begin generating code.
|
/* Begin generating code.
|
||||||
*/
|
*/
|
||||||
v = pParse->pVdbe;
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ){
|
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) goto delete_from_cleanup;
|
if( v==0 ) goto delete_from_cleanup;
|
||||||
|
|
||||||
/* Begin the database scan
|
/* Begin the database scan
|
||||||
|
17
src/expr.c
17
src/expr.c
@@ -23,7 +23,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** This file contains C code routines used for processing expressions
|
** This file contains C code routines used for processing expressions
|
||||||
**
|
**
|
||||||
** $Id: expr.c,v 1.10 2000/06/06 17:27:05 drh Exp $
|
** $Id: expr.c,v 1.11 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -206,10 +206,7 @@ int sqliteExprResolveIds(Parse *pParse, IdList *pTabList, Expr *pExpr){
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TK_IN: {
|
case TK_IN: {
|
||||||
Vdbe *v = pParse->pVdbe;
|
Vdbe *v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ){
|
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) return 1;
|
if( v==0 ) return 1;
|
||||||
if( sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
|
if( sqliteExprResolveIds(pParse, pTabList, pExpr->pLeft) ){
|
||||||
return 1;
|
return 1;
|
||||||
@@ -766,7 +763,7 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest){
|
|||||||
** Do a deep comparison of two expression trees. Return TRUE (non-zero)
|
** Do a deep comparison of two expression trees. Return TRUE (non-zero)
|
||||||
** if they are identical and return FALSE if they differ in any way.
|
** if they are identical and return FALSE if they differ in any way.
|
||||||
*/
|
*/
|
||||||
static int exprDeepCompare(Expr *pA, Expr *pB){
|
int sqliteExprCompare(Expr *pA, Expr *pB){
|
||||||
int i;
|
int i;
|
||||||
if( pA==0 ){
|
if( pA==0 ){
|
||||||
return pB==0;
|
return pB==0;
|
||||||
@@ -774,13 +771,13 @@ static int exprDeepCompare(Expr *pA, Expr *pB){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if( pA->op!=pB->op ) return 0;
|
if( pA->op!=pB->op ) return 0;
|
||||||
if( !exprDeepCompare(pA->pLeft, pB->pLeft) ) return 0;
|
if( !sqliteExprCompare(pA->pLeft, pB->pLeft) ) return 0;
|
||||||
if( !exprDeepCompare(pA->pRight, pB->pRight) ) return 0;
|
if( !sqliteExprCompare(pA->pRight, pB->pRight) ) return 0;
|
||||||
if( pA->pList ){
|
if( pA->pList ){
|
||||||
if( pB->pList==0 ) return 0;
|
if( pB->pList==0 ) return 0;
|
||||||
if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
|
if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
|
||||||
for(i=0; i<pA->pList->nExpr; i++){
|
for(i=0; i<pA->pList->nExpr; i++){
|
||||||
if( !exprDeepCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
|
if( !sqliteExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -868,7 +865,7 @@ int sqliteExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
|
|||||||
aAgg = pParse->aAgg;
|
aAgg = pParse->aAgg;
|
||||||
for(i=0; i<pParse->nAgg; i++){
|
for(i=0; i<pParse->nAgg; i++){
|
||||||
if( !aAgg[i].isAgg ) continue;
|
if( !aAgg[i].isAgg ) continue;
|
||||||
if( exprDeepCompare(aAgg[i].pExpr, pExpr) ){
|
if( sqliteExprCompare(aAgg[i].pExpr, pExpr) ){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle INSERT statements.
|
** to handle INSERT statements.
|
||||||
**
|
**
|
||||||
** $Id: insert.c,v 1.8 2000/06/07 15:11:27 drh Exp $
|
** $Id: insert.c,v 1.9 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -67,10 +67,7 @@ void sqliteInsert(
|
|||||||
pParse->nErr++;
|
pParse->nErr++;
|
||||||
goto insert_cleanup;
|
goto insert_cleanup;
|
||||||
}
|
}
|
||||||
v = pParse->pVdbe;
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ){
|
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) goto insert_cleanup;
|
if( v==0 ) goto insert_cleanup;
|
||||||
if( pSelect ){
|
if( pSelect ){
|
||||||
int rc;
|
int rc;
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
** other files are for internal use by SQLite and should not be
|
** other files are for internal use by SQLite and should not be
|
||||||
** accessed by users of the library.
|
** accessed by users of the library.
|
||||||
**
|
**
|
||||||
** $Id: main.c,v 1.11 2000/06/07 15:11:27 drh Exp $
|
** $Id: main.c,v 1.12 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -129,6 +129,10 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
|
|||||||
** the program. The delete the virtual machine.
|
** the program. The delete the virtual machine.
|
||||||
*/
|
*/
|
||||||
vdbe = sqliteVdbeCreate(db->pBe);
|
vdbe = sqliteVdbeCreate(db->pBe);
|
||||||
|
if( vdbe==0 ){
|
||||||
|
sqliteSetString(pzErrMsg, "out of memory",0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
|
sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg);
|
||||||
rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
|
rc = sqliteVdbeExec(vdbe, sqliteOpenCb, db, pzErrMsg);
|
||||||
sqliteVdbeDelete(vdbe);
|
sqliteVdbeDelete(vdbe);
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
** the parser. Lemon will also generate a header file containing
|
** the parser. Lemon will also generate a header file containing
|
||||||
** numeric codes for all of the tokens.
|
** numeric codes for all of the tokens.
|
||||||
**
|
**
|
||||||
** @(#) $Id: parse.y,v 1.15 2000/06/07 14:42:27 drh Exp $
|
** @(#) $Id: parse.y,v 1.16 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
%token_prefix TK_
|
%token_prefix TK_
|
||||||
%token_type {Token}
|
%token_type {Token}
|
||||||
@@ -211,11 +211,11 @@ orderby_opt(A) ::= . {A = 0;}
|
|||||||
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
orderby_opt(A) ::= ORDER BY sortlist(X). {A = X;}
|
||||||
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
|
sortlist(A) ::= sortlist(X) COMMA sortitem(Y) sortorder(Z). {
|
||||||
A = sqliteExprListAppend(X,Y,0);
|
A = sqliteExprListAppend(X,Y,0);
|
||||||
A->a[A->nExpr-1].idx = Z; /* 0 for ascending order, 1 for decending */
|
A->a[A->nExpr-1].sortOrder = Z; /* 0 for ascending order, 1 for decending */
|
||||||
}
|
}
|
||||||
sortlist(A) ::= sortitem(Y) sortorder(Z). {
|
sortlist(A) ::= sortitem(Y) sortorder(Z). {
|
||||||
A = sqliteExprListAppend(0,Y,0);
|
A = sqliteExprListAppend(0,Y,0);
|
||||||
A->a[0].idx = Z;
|
A->a[0].sortOrder = Z;
|
||||||
}
|
}
|
||||||
sortitem(A) ::= expr(X). {A = X;}
|
sortitem(A) ::= expr(X). {A = X;}
|
||||||
|
|
||||||
|
355
src/select.c
355
src/select.c
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle SELECT statements.
|
** to handle SELECT statements.
|
||||||
**
|
**
|
||||||
** $Id: select.c,v 1.15 2000/06/07 14:42:27 drh Exp $
|
** $Id: select.c,v 1.16 2000/06/07 23:51:50 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -135,11 +135,11 @@ static int selectInnerLoop(
|
|||||||
*/
|
*/
|
||||||
if( pOrderBy ){
|
if( pOrderBy ){
|
||||||
char *zSortOrder;
|
char *zSortOrder;
|
||||||
sqliteVdbeAddOp(v, OP_SortMakeRec, pEList->nExpr, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_SortMakeRec, nField, 0, 0, 0);
|
||||||
zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
|
zSortOrder = sqliteMalloc( pOrderBy->nExpr + 1 );
|
||||||
if( zSortOrder==0 ) return 1;
|
if( zSortOrder==0 ) return 1;
|
||||||
for(i=0; i<pOrderBy->nExpr; i++){
|
for(i=0; i<pOrderBy->nExpr; i++){
|
||||||
zSortOrder[i] = pOrderBy->a[i].idx ? '-' : '+';
|
zSortOrder[i] = pOrderBy->a[i].sortOrder ? '-' : '+';
|
||||||
sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
|
sqliteExprCode(pParse, pOrderBy->a[i].pExpr);
|
||||||
}
|
}
|
||||||
zSortOrder[pOrderBy->nExpr] = 0;
|
zSortOrder[pOrderBy->nExpr] = 0;
|
||||||
@@ -179,7 +179,7 @@ static int selectInnerLoop(
|
|||||||
** item into the set table with bogus data.
|
** item into the set table with bogus data.
|
||||||
*/
|
*/
|
||||||
if( eDest==SRT_Set ){
|
if( eDest==SRT_Set ){
|
||||||
assert( pEList->nExpr==1 );
|
assert( nField==1 );
|
||||||
sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
|
sqliteVdbeAddOp(v, OP_String, 0, 0, "", 0);
|
||||||
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Put, iParm, 0, 0, 0);
|
||||||
}else
|
}else
|
||||||
@@ -190,7 +190,7 @@ static int selectInnerLoop(
|
|||||||
** of the scan loop.
|
** of the scan loop.
|
||||||
*/
|
*/
|
||||||
if( eDest==SRT_Mem ){
|
if( eDest==SRT_Mem ){
|
||||||
assert( pEList->nExpr==1 );
|
assert( nField==1 );
|
||||||
sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_MemStore, iParm, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
|
sqliteVdbeAddOp(v, OP_Goto, 0, iBreak, 0, 0);
|
||||||
}else
|
}else
|
||||||
@@ -203,22 +203,39 @@ static int selectInnerLoop(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If the inner loop was generated using a non-null pOrderBy argument,
|
||||||
|
** then the results were placed in a sorter. After the loop is terminated
|
||||||
|
** we need to run the sorter and output the results. The following
|
||||||
|
** routine generates the code needed to do that.
|
||||||
|
*/
|
||||||
|
static void generateSortTail(Vdbe *v, int nField){
|
||||||
|
int end = sqliteVdbeMakeLabel(v);
|
||||||
|
int addr;
|
||||||
|
sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
|
||||||
|
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_SortCallback, nField, 0, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate code that will tell the VDBE how many columns there
|
** Generate code that will tell the VDBE how many columns there
|
||||||
** are in the result and the name for each column. This information
|
** are in the result and the name for each column. This information
|
||||||
** is used to provide "argc" and "azCol[]" values in the callback.
|
** is used to provide "argc" and "azCol[]" values in the callback.
|
||||||
*/
|
*/
|
||||||
static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
|
static
|
||||||
|
void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
int i;
|
int i;
|
||||||
|
if( pParse->colNamesSet ) return;
|
||||||
|
pParse->colNamesSet = 1;
|
||||||
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_ColumnCount, pEList->nExpr, 0, 0, 0);
|
||||||
for(i=0; i<pEList->nExpr; i++){
|
for(i=0; i<pEList->nExpr; i++){
|
||||||
Expr *p;
|
Expr *p;
|
||||||
if( pEList->a[i].zName ){
|
if( pEList->a[i].zName ){
|
||||||
char *zName = pEList->a[i].zName;
|
char *zName = pEList->a[i].zName;
|
||||||
int addr = sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
|
sqliteVdbeAddOp(v, OP_ColumnName, i, 0, zName, 0);
|
||||||
if( zName[0]=='\'' || zName[0]=='"' ){
|
|
||||||
sqliteVdbeDequoteP3(v, addr);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
p = pEList->a[i].pExpr;
|
p = pEList->a[i].pExpr;
|
||||||
@@ -246,6 +263,167 @@ static void generateColumnNames(Vdbe *v, IdList *pTabList, ExprList *pEList){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Name of the connection operator, used for error messages.
|
||||||
|
*/
|
||||||
|
static const char *selectOpName(int id){
|
||||||
|
char *z;
|
||||||
|
switch( id ){
|
||||||
|
case TK_ALL: z = "UNION ALL"; break;
|
||||||
|
case TK_INTERSECT: z = "INTERSECT"; break;
|
||||||
|
case TK_EXCEPT: z = "EXCEPT"; break;
|
||||||
|
default: z = "UNION"; break;
|
||||||
|
}
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** For the given SELECT statement, do two things.
|
||||||
|
**
|
||||||
|
** (1) Fill in the pTab fields of the IdList that defines the set
|
||||||
|
** of tables we are scanning.
|
||||||
|
**
|
||||||
|
** (2) If the columns to be extracted variable (pEList) is NULL
|
||||||
|
** (meaning that a "*" was used in the SQL statement) then
|
||||||
|
** create a fake pEList containing the names of all columns
|
||||||
|
** of all tables.
|
||||||
|
**
|
||||||
|
** Return 0 on success. If there are problems, leave an error message
|
||||||
|
** in pParse and return non-zero.
|
||||||
|
*/
|
||||||
|
static int fillInColumnList(Parse *pParse, Select *p){
|
||||||
|
int i, j;
|
||||||
|
IdList *pTabList = p->pSrc;
|
||||||
|
ExprList *pEList = p->pEList;
|
||||||
|
|
||||||
|
/* Look up every table in the table list.
|
||||||
|
*/
|
||||||
|
for(i=0; i<pTabList->nId; i++){
|
||||||
|
if( pTabList->a[i].pTab ){
|
||||||
|
/* This routine has run before! No need to continue */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
|
||||||
|
if( pTabList->a[i].pTab==0 ){
|
||||||
|
sqliteSetString(&pParse->zErrMsg, "no such table: ",
|
||||||
|
pTabList->a[i].zName, 0);
|
||||||
|
pParse->nErr++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the list of columns to retrieve is "*" then replace it with
|
||||||
|
** a list of all columns from all tables.
|
||||||
|
*/
|
||||||
|
if( pEList==0 ){
|
||||||
|
for(i=0; i<pTabList->nId; i++){
|
||||||
|
Table *pTab = pTabList->a[i].pTab;
|
||||||
|
for(j=0; j<pTab->nCol; j++){
|
||||||
|
Expr *pExpr = sqliteExpr(TK_DOT, 0, 0, 0);
|
||||||
|
pExpr->pLeft = sqliteExpr(TK_ID, 0, 0, 0);
|
||||||
|
pExpr->pLeft->token.z = pTab->zName;
|
||||||
|
pExpr->pLeft->token.n = strlen(pTab->zName);
|
||||||
|
pExpr->pRight = sqliteExpr(TK_ID, 0, 0, 0);
|
||||||
|
pExpr->pRight->token.z = pTab->aCol[j].zName;
|
||||||
|
pExpr->pRight->token.n = strlen(pTab->aCol[j].zName);
|
||||||
|
pEList = sqliteExprListAppend(pEList, pExpr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p->pEList = pEList;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine associates entries in an ORDER BY expression list with
|
||||||
|
** columns in a result. For each ORDER BY expression, the opcode of
|
||||||
|
** the top-level node is changed to TK_FIELD and the iField value of
|
||||||
|
** the top-level node is filled in with column number and the iTable
|
||||||
|
** value of the top-level node is filled with iTable parameter.
|
||||||
|
**
|
||||||
|
** If there are prior SELECT clauses, they are processed first. A match
|
||||||
|
** in an earlier SELECT takes precedence over a later SELECT.
|
||||||
|
**
|
||||||
|
** Any entry that does not match is flagged as an error. The number
|
||||||
|
** of errors is returned.
|
||||||
|
*/
|
||||||
|
static int matchOrderbyToColumn(
|
||||||
|
Parse *pParse, /* A place to leave error messages */
|
||||||
|
Select *pSelect, /* Match to result columns of this SELECT */
|
||||||
|
ExprList *pOrderBy, /* The ORDER BY values to match against columns */
|
||||||
|
int iTable, /* Insert this this value in iTable */
|
||||||
|
int mustComplete /* If TRUE all ORDER BYs must match */
|
||||||
|
){
|
||||||
|
int nErr = 0;
|
||||||
|
int i, j;
|
||||||
|
ExprList *pEList;
|
||||||
|
|
||||||
|
assert( pSelect && pOrderBy );
|
||||||
|
if( mustComplete ){
|
||||||
|
for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; }
|
||||||
|
}
|
||||||
|
if( fillInColumnList(pParse, pSelect) ){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if( pSelect->pPrior ){
|
||||||
|
matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0);
|
||||||
|
}
|
||||||
|
pEList = pSelect->pEList;
|
||||||
|
for(i=0; i<pOrderBy->nExpr; i++){
|
||||||
|
Expr *pE = pOrderBy->a[i].pExpr;
|
||||||
|
if( pOrderBy->a[i].done ) continue;
|
||||||
|
for(j=0; j<pEList->nExpr; j++){
|
||||||
|
int match = 0;
|
||||||
|
if( pEList->a[i].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){
|
||||||
|
char *zName = pEList->a[i].zName;
|
||||||
|
char *zLabel = 0;
|
||||||
|
sqliteSetString(&zLabel, pE->token.z, pE->token.n, 0);
|
||||||
|
sqliteDequote(zLabel);
|
||||||
|
if( sqliteStrICmp(zName, zLabel)==0 ){
|
||||||
|
match = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( match==0 && sqliteExprCompare(pE, pEList->a[i].pExpr) ){
|
||||||
|
match = 1;
|
||||||
|
}
|
||||||
|
if( match ){
|
||||||
|
pE->op = TK_FIELD;
|
||||||
|
pE->iField = j;
|
||||||
|
pE->iTable = iTable;
|
||||||
|
pOrderBy->a[i].done = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( mustComplete ){
|
||||||
|
char zBuf[30];
|
||||||
|
sprintf(zBuf,"%d",i+1);
|
||||||
|
sqliteSetString(&pParse->zErrMsg, "ORDER BY term number ", zBuf,
|
||||||
|
" does not match any result column", 0);
|
||||||
|
pParse->nErr++;
|
||||||
|
nErr++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get a VDBE for the given parser context. Create a new one if necessary.
|
||||||
|
** If an error occurs, return NULL and leave a message in pParse.
|
||||||
|
*/
|
||||||
|
Vdbe *sqliteGetVdbe(Parse *pParse){
|
||||||
|
Vdbe *v = pParse->pVdbe;
|
||||||
|
if( v==0 ){
|
||||||
|
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
||||||
|
}
|
||||||
|
if( v==0 ){
|
||||||
|
sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
|
||||||
|
pParse->nErr++;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This routine is called to process a query that is really the union
|
** This routine is called to process a query that is really the union
|
||||||
** or intersection of two or more separate queries.
|
** or intersection of two or more separate queries.
|
||||||
@@ -254,62 +432,92 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
|||||||
int rc;
|
int rc;
|
||||||
Select *pPrior;
|
Select *pPrior;
|
||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Make sure we have a valid query engine. If not, create a new one.
|
/* Make sure there is no ORDER BY clause on prior SELECTs. Only the
|
||||||
|
** last SELECT in the series may have an ORDER BY.
|
||||||
*/
|
*/
|
||||||
v = pParse->pVdbe;
|
assert( p->pPrior!=0 );
|
||||||
if( v==0 ){
|
pPrior = p->pPrior;
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
if( pPrior->pOrderBy ){
|
||||||
}
|
sqliteSetString(&pParse->zErrMsg,"ORDER BY clause should come after ",
|
||||||
if( v==0 ){
|
selectOpName(p->op), " not before", 0);
|
||||||
sqliteSetString(&pParse->zErrMsg, "out of memory", 0);
|
|
||||||
pParse->nErr++;
|
pParse->nErr++;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert( p->pPrior!=0 );
|
/* Make sure we have a valid query engine. If not, create a new one.
|
||||||
pPrior = p->pPrior;
|
*/
|
||||||
|
v = sqliteGetVdbe(pParse);
|
||||||
|
if( v==0 ) return 1;
|
||||||
|
|
||||||
|
/* Process the UNION or INTERSECTION
|
||||||
|
*/
|
||||||
switch( p->op ){
|
switch( p->op ){
|
||||||
case TK_ALL: {
|
case TK_ALL:
|
||||||
rc = sqliteSelect(pParse, pPrior, eDest, iParm);
|
|
||||||
if( rc ) return rc;
|
|
||||||
p->pPrior = 0;
|
|
||||||
rc = sqliteSelect(pParse, p, eDest, iParm);
|
|
||||||
p->pPrior = pPrior;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TK_EXCEPT:
|
case TK_EXCEPT:
|
||||||
case TK_UNION: {
|
case TK_UNION: {
|
||||||
int unionTab;
|
int unionTab; /* Cursor number of the temporary table holding result */
|
||||||
int op;
|
int op; /* One of the SRT_ operations to apply to self */
|
||||||
|
int priorOp; /* The SRT_ operation to apply to prior selects */
|
||||||
|
|
||||||
if( eDest==SRT_Union ){
|
priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
|
||||||
|
if( eDest==priorOp ){
|
||||||
|
/* We can reuse a temporary table generated by a SELECT to our
|
||||||
|
** right. This also means we are not the right-most select and so
|
||||||
|
** we cannot have an ORDER BY clause
|
||||||
|
*/
|
||||||
unionTab = iParm;
|
unionTab = iParm;
|
||||||
|
assert( p->pOrderBy==0 );
|
||||||
}else{
|
}else{
|
||||||
unionTab = pParse->nTab++;
|
/* We will need to create our own temporary table to hold the
|
||||||
|
** intermediate results.
|
||||||
|
*/
|
||||||
|
unionTab = pParse->nTab++;
|
||||||
|
if( p->pOrderBy
|
||||||
|
&& matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_Open, unionTab, 1, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
|
if( p->op!=TK_ALL ){
|
||||||
|
sqliteVdbeAddOp(v, OP_KeyAsData, unionTab, 1, 0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rc = sqliteSelect(pParse, pPrior, SRT_Union, unionTab);
|
|
||||||
|
/* Code the SELECT statements to our left
|
||||||
|
*/
|
||||||
|
rc = sqliteSelect(pParse, pPrior, priorOp, unionTab);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
op = p->op==TK_EXCEPT ? SRT_Except : SRT_Union;
|
|
||||||
|
/* Code the current SELECT statement
|
||||||
|
*/
|
||||||
|
switch( p->op ){
|
||||||
|
case TK_EXCEPT: op = SRT_Except; break;
|
||||||
|
case TK_UNION: op = SRT_Union; break;
|
||||||
|
case TK_ALL: op = SRT_Table; break;
|
||||||
|
}
|
||||||
p->pPrior = 0;
|
p->pPrior = 0;
|
||||||
rc = sqliteSelect(pParse, p, op, unionTab);
|
rc = sqliteSelect(pParse, p, op, unionTab);
|
||||||
p->pPrior = pPrior;
|
p->pPrior = pPrior;
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
if( eDest!=SRT_Union ){
|
|
||||||
|
/* Convert the data in the temporary table into whatever form
|
||||||
|
** it is that we currently need.
|
||||||
|
*/
|
||||||
|
if( eDest!=priorOp ){
|
||||||
int iCont, iBreak;
|
int iCont, iBreak;
|
||||||
assert( p->pEList );
|
assert( p->pEList );
|
||||||
generateColumnNames(v, 0, p->pEList);
|
generateColumnNames(pParse, 0, p->pEList);
|
||||||
iBreak = sqliteVdbeMakeLabel(v);
|
iBreak = sqliteVdbeMakeLabel(v);
|
||||||
iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0);
|
iCont = sqliteVdbeAddOp(v, OP_Next, unionTab, iBreak, 0, 0);
|
||||||
rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
|
rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
|
||||||
0, -1, eDest, iParm,
|
p->pOrderBy, -1, eDest, iParm,
|
||||||
iCont, iBreak);
|
iCont, iBreak);
|
||||||
if( rc ) return 1;
|
if( rc ) return 1;
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
|
sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak);
|
sqliteVdbeAddOp(v, OP_Close, unionTab, 0, 0, iBreak);
|
||||||
|
if( p->pOrderBy ){
|
||||||
|
generateSortTail(v, p->pEList->nExpr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -317,38 +525,58 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
|||||||
int tab1, tab2;
|
int tab1, tab2;
|
||||||
int iCont, iBreak;
|
int iCont, iBreak;
|
||||||
|
|
||||||
|
/* INTERSECT is different from the others since it requires
|
||||||
|
** two temporary tables. Hence it has its own case. Begine
|
||||||
|
** by allocating the tables we will need.
|
||||||
|
*/
|
||||||
tab1 = pParse->nTab++;
|
tab1 = pParse->nTab++;
|
||||||
tab2 = pParse->nTab++;
|
tab2 = pParse->nTab++;
|
||||||
|
if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_Open, tab1, 1, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_KeyAsData, tab1, 1, 0, 0);
|
||||||
|
|
||||||
|
/* Code the SELECTs to our left into temporary table "tab1".
|
||||||
|
*/
|
||||||
rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
|
rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1);
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
|
|
||||||
|
/* Code the current SELECT into temporary table "tab2"
|
||||||
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_Open, tab2, 1, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
|
sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1, 0, 0);
|
||||||
p->pPrior = 0;
|
p->pPrior = 0;
|
||||||
rc = sqliteSelect(pParse, p, SRT_Union, tab2);
|
rc = sqliteSelect(pParse, p, SRT_Union, tab2);
|
||||||
p->pPrior = pPrior;
|
p->pPrior = pPrior;
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
|
|
||||||
|
/* Generate code to take the intersection of the two temporary
|
||||||
|
** tables.
|
||||||
|
*/
|
||||||
assert( p->pEList );
|
assert( p->pEList );
|
||||||
generateColumnNames(v, 0, p->pEList);
|
generateColumnNames(pParse, 0, p->pEList);
|
||||||
iBreak = sqliteVdbeMakeLabel(v);
|
iBreak = sqliteVdbeMakeLabel(v);
|
||||||
iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
|
iCont = sqliteVdbeAddOp(v, OP_Next, tab1, iBreak, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Key, tab1, 0, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
|
sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont, 0, 0);
|
||||||
rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
|
rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
|
||||||
0, -1, eDest, iParm,
|
p->pOrderBy, -1, eDest, iParm,
|
||||||
iCont, iBreak);
|
iCont, iBreak);
|
||||||
if( rc ) return 1;
|
if( rc ) return 1;
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
|
sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak);
|
sqliteVdbeAddOp(v, OP_Close, tab2, 0, 0, iBreak);
|
||||||
sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0);
|
sqliteVdbeAddOp(v, OP_Close, tab1, 0, 0, 0);
|
||||||
|
if( p->pOrderBy ){
|
||||||
|
generateSortTail(v, p->pEList->nExpr);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert( p->pEList && pPrior->pEList );
|
assert( p->pEList && pPrior->pEList );
|
||||||
if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
|
if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
|
||||||
sqliteSetString(&pParse->zErrMsg, "SELECTs have different numbers "
|
sqliteSetString(&pParse->zErrMsg, "SELECTs to the left and right of ",
|
||||||
"of columns and therefore cannot be joined", 0);
|
selectOpName(p->op), " do not have the same number of result columns", 0);
|
||||||
pParse->nErr++;
|
pParse->nErr++;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -386,7 +614,7 @@ int sqliteSelect(
|
|||||||
int eDest, /* One of: SRT_Callback Mem Set Union Except */
|
int eDest, /* One of: SRT_Callback Mem Set Union Except */
|
||||||
int iParm /* Save result in this memory location, if >=0 */
|
int iParm /* Save result in this memory location, if >=0 */
|
||||||
){
|
){
|
||||||
int i, j;
|
int i;
|
||||||
WhereInfo *pWInfo;
|
WhereInfo *pWInfo;
|
||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
int isAgg = 0; /* True for select lists like "count(*)" */
|
int isAgg = 0; /* True for select lists like "count(*)" */
|
||||||
@@ -407,7 +635,6 @@ int sqliteSelect(
|
|||||||
|
|
||||||
/* Make local copies of the parameters for this query.
|
/* Make local copies of the parameters for this query.
|
||||||
*/
|
*/
|
||||||
pEList = p->pEList;
|
|
||||||
pTabList = p->pSrc;
|
pTabList = p->pSrc;
|
||||||
pWhere = p->pWhere;
|
pWhere = p->pWhere;
|
||||||
pOrderBy = p->pOrderBy;
|
pOrderBy = p->pOrderBy;
|
||||||
@@ -422,17 +649,14 @@ int sqliteSelect(
|
|||||||
if( pParse->nErr>0 ) return 0;
|
if( pParse->nErr>0 ) return 0;
|
||||||
sqliteParseInfoReset(pParse);
|
sqliteParseInfoReset(pParse);
|
||||||
|
|
||||||
/* Look up every table in the table list.
|
/* Look up every table in the table list and create an appropriate
|
||||||
|
** columnlist in pEList if there isn't one already. (The parser leaves
|
||||||
|
** a NULL in the pEList field if the SQL said "SELECT * FROM ...")
|
||||||
*/
|
*/
|
||||||
for(i=0; i<pTabList->nId; i++){
|
if( fillInColumnList(pParse, p) ){
|
||||||
pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName);
|
return 1;
|
||||||
if( pTabList->a[i].pTab==0 ){
|
|
||||||
sqliteSetString(&pParse->zErrMsg, "no such table: ",
|
|
||||||
pTabList->a[i].zName, 0);
|
|
||||||
pParse->nErr++;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
pEList = p->pEList;
|
||||||
|
|
||||||
/* Allocate a temporary table to use for the DISTINCT set, if
|
/* Allocate a temporary table to use for the DISTINCT set, if
|
||||||
** necessary. This must be done early to allocate the cursor before
|
** necessary. This must be done early to allocate the cursor before
|
||||||
@@ -444,21 +668,6 @@ int sqliteSelect(
|
|||||||
distinct = -1;
|
distinct = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the list of fields to retrieve is "*" then replace it with
|
|
||||||
** a list of all fields from all tables.
|
|
||||||
*/
|
|
||||||
if( pEList==0 ){
|
|
||||||
for(i=0; i<pTabList->nId; i++){
|
|
||||||
Table *pTab = pTabList->a[i].pTab;
|
|
||||||
for(j=0; j<pTab->nCol; j++){
|
|
||||||
Expr *pExpr = sqliteExpr(TK_FIELD, 0, 0, 0);
|
|
||||||
pExpr->iTable = i + pParse->nTab;
|
|
||||||
pExpr->iField = j;
|
|
||||||
p->pEList = pEList = sqliteExprListAppend(pEList, pExpr, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If writing to memory or generating a set
|
/* If writing to memory or generating a set
|
||||||
** only a single column may be output.
|
** only a single column may be output.
|
||||||
*/
|
*/
|
||||||
@@ -587,7 +796,7 @@ int sqliteSelect(
|
|||||||
** step is skipped if the output is going to a table or a memory cell.
|
** step is skipped if the output is going to a table or a memory cell.
|
||||||
*/
|
*/
|
||||||
if( eDest==SRT_Callback ){
|
if( eDest==SRT_Callback ){
|
||||||
generateColumnNames(v, pTabList, pEList);
|
generateColumnNames(pParse, pTabList, pEList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset the aggregator
|
/* Reset the aggregator
|
||||||
@@ -707,13 +916,7 @@ int sqliteSelect(
|
|||||||
** and send them to the callback one by one.
|
** and send them to the callback one by one.
|
||||||
*/
|
*/
|
||||||
if( pOrderBy ){
|
if( pOrderBy ){
|
||||||
int end = sqliteVdbeMakeLabel(v);
|
generateSortTail(v, pEList->nExpr);
|
||||||
int addr;
|
|
||||||
sqliteVdbeAddOp(v, OP_Sort, 0, 0, 0, 0);
|
|
||||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end, 0, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_SortCallback, pEList->nExpr, 0, 0, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr, 0, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_SortClose, 0, 0, 0, end);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.21 2000/06/07 15:24:40 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.22 2000/06/07 23:51:51 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqlite.h"
|
#include "sqlite.h"
|
||||||
#include "dbbe.h"
|
#include "dbbe.h"
|
||||||
@@ -182,8 +182,9 @@ struct ExprList {
|
|||||||
struct {
|
struct {
|
||||||
Expr *pExpr; /* The list of expressions */
|
Expr *pExpr; /* The list of expressions */
|
||||||
char *zName; /* Token associated with this expression */
|
char *zName; /* Token associated with this expression */
|
||||||
int idx; /* ... */
|
char sortOrder; /* 1 for DESC or 0 for ASC */
|
||||||
int isAgg; /* True if this is an aggregate like count(*) */
|
char isAgg; /* True if this is an aggregate like count(*) */
|
||||||
|
char done; /* A flag to indicate when processing is finished */
|
||||||
} *a; /* One entry for each expression */
|
} *a; /* One entry for each expression */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,6 +280,7 @@ struct Parse {
|
|||||||
Token sLastToken; /* The last token parsed */
|
Token sLastToken; /* The last token parsed */
|
||||||
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
Table *pNewTable; /* A table being constructed by CREATE TABLE */
|
||||||
Vdbe *pVdbe; /* An engine for executing database bytecode */
|
Vdbe *pVdbe; /* An engine for executing database bytecode */
|
||||||
|
int colNamesSet; /* TRUE after OP_ColumnCount has been issued to pVdbe */
|
||||||
int explain; /* True if the EXPLAIN flag is found on the query */
|
int explain; /* True if the EXPLAIN flag is found on the query */
|
||||||
int initFlag; /* True if reparsing CREATE TABLEs */
|
int initFlag; /* True if reparsing CREATE TABLEs */
|
||||||
int nErr; /* Number of errors seen */
|
int nErr; /* Number of errors seen */
|
||||||
@@ -349,8 +351,10 @@ int sqliteGlobCompare(const char*,const char*);
|
|||||||
int sqliteLikeCompare(const unsigned char*,const unsigned char*);
|
int sqliteLikeCompare(const unsigned char*,const unsigned char*);
|
||||||
char *sqliteTableNameFromToken(Token*);
|
char *sqliteTableNameFromToken(Token*);
|
||||||
int sqliteExprCheck(Parse*, Expr*, int, int*);
|
int sqliteExprCheck(Parse*, Expr*, int, int*);
|
||||||
|
int sqliteExprCompare(Expr*, Expr*);
|
||||||
int sqliteFuncId(Token*);
|
int sqliteFuncId(Token*);
|
||||||
int sqliteExprResolveIds(Parse*, IdList*, Expr*);
|
int sqliteExprResolveIds(Parse*, IdList*, Expr*);
|
||||||
void sqliteExprResolveInSelect(Parse*, Expr*);
|
void sqliteExprResolveInSelect(Parse*, Expr*);
|
||||||
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
|
int sqliteExprAnalyzeAggregates(Parse*, Expr*);
|
||||||
void sqlitePArseInfoReset(Parse*);
|
void sqliteParseInfoReset(Parse*);
|
||||||
|
Vdbe *sqliteGetVdbe(Parse*);
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle UPDATE statements.
|
** to handle UPDATE statements.
|
||||||
**
|
**
|
||||||
** $Id: update.c,v 1.4 2000/06/06 13:54:16 drh Exp $
|
** $Id: update.c,v 1.5 2000/06/07 23:51:51 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ void sqliteUpdate(
|
|||||||
}
|
}
|
||||||
for(j=0; j<pTab->nCol; j++){
|
for(j=0; j<pTab->nCol; j++){
|
||||||
if( strcmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
|
if( strcmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
|
||||||
pChanges->a[i].idx = j;
|
/* pChanges->a[i].idx = j; */
|
||||||
aXRef[j] = i;
|
aXRef[j] = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -138,10 +138,7 @@ void sqliteUpdate(
|
|||||||
|
|
||||||
/* Begin generating code.
|
/* Begin generating code.
|
||||||
*/
|
*/
|
||||||
v = pParse->pVdbe;
|
v = sqliteGetVdbe(pParse);
|
||||||
if( v==0 ){
|
|
||||||
v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe);
|
|
||||||
}
|
|
||||||
if( v==0 ) goto update_cleanup;
|
if( v==0 ) goto update_cleanup;
|
||||||
|
|
||||||
/* Begin the database scan
|
/* Begin the database scan
|
||||||
|
17
test/in.test
17
test/in.test
@@ -23,7 +23,7 @@
|
|||||||
# This file implements regression tests for SQLite library. The
|
# This file implements regression tests for SQLite library. The
|
||||||
# focus of this file is testing the IN and BETWEEN operator.
|
# focus of this file is testing the IN and BETWEEN operator.
|
||||||
#
|
#
|
||||||
# $Id: in.test,v 1.1 2000/06/06 13:54:16 drh Exp $
|
# $Id: in.test,v 1.2 2000/06/07 23:51:51 drh Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -146,5 +146,20 @@ do_test in-4.2 {
|
|||||||
execsql {SELECT a FROM t1 ORDER BY a}
|
execsql {SELECT a FROM t1 ORDER BY a}
|
||||||
} {1 2 3 4 5 6 7 8}
|
} {1 2 3 4 5 6 7 8}
|
||||||
|
|
||||||
|
# Do an IN with a constant RHS but where the RHS has many, many
|
||||||
|
# elements. We need to test that collisions in the hash table
|
||||||
|
# are resolved properly.
|
||||||
|
#
|
||||||
|
do_test in-5.1 {
|
||||||
|
execsql {
|
||||||
|
INSERT INTO t1 VALUES('hello', 'world');
|
||||||
|
SELECT * FROM t1
|
||||||
|
WHERE a IN (
|
||||||
|
'Do','an','IN','with','a','constant','RHS','but','where','the',
|
||||||
|
'has','many','elements','We','need','to','test','that',
|
||||||
|
'collisions','hash','table','are','resolved','properly',
|
||||||
|
'This','in-set','contains','thirty','one','entries','hello');
|
||||||
|
}
|
||||||
|
} {hello world}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Reference in New Issue
Block a user