From b76541116170c64564d0316de4eb2d66f50333d7 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 12 Jan 2008 12:48:07 +0000 Subject: [PATCH] Continuing work toward converting the VM into a register machine. (CVS 4707) FossilOrigin-Name: a6dddebcc5ccbbf3009c9d06163a8b59036331de --- manifest | 31 ++++++++------- manifest.uuid | 2 +- src/analyze.c | 14 +++---- src/build.c | 66 ++++++++++++++++--------------- src/delete.c | 24 +----------- src/expr.c | 12 ++---- src/insert.c | 58 +++++++++++++++------------ src/select.c | 99 ++++++++++++++++++++++++----------------------- src/sqliteInt.h | 6 +-- src/vdbe.c | 74 ++++++++++++++--------------------- src/vtab.c | 14 +++---- test/select4.test | 53 ++++++++++++++++++++++++- test/select8.test | 62 +++++++++++++++++++++++++++++ 13 files changed, 299 insertions(+), 216 deletions(-) create mode 100644 test/select8.test diff --git a/manifest b/manifest index a36016af58..91864257a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\sexplicit\srange\stests\sbefore\sattempting\sto\sconvert\sa\s64-bit\sfloat\ninto\sa\s64-bit\sinteger.\s\sSome\ssystems\s(windows)\sseem\sto\sthrow\sexceptions\nif\sthe\sconversion\sis\sout\sof\srange.\s\sTicket\s#2880.\s(CVS\s4706) -D 2008-01-11T15:27:03 +C Continuing\swork\stoward\sconverting\sthe\sVM\sinto\sa\sregister\smachine.\s(CVS\s4707) +D 2008-01-12T12:48:08 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7 F Makefile.in 30789bf70614bad659351660d76b8e533f3340e9 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -79,24 +79,24 @@ F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.def a96c1d0d39362b763d2ddba220a32da41a15c4b4 F sqlite3.pc.in abed4664817e1cd500f2276142c71958087c16bc F src/alter.c 5a54f58d9481ac14c4e58b702f3f8758dee84d04 -F src/analyze.c 52cbc3577c962975445d3e1341cba046f228d21e +F src/analyze.c 5aa93f191b365ea308795f62ca6f2e8caa9de3e1 F src/attach.c 61f0cae7b1a0e7c3b5f5286be5dc1a7c4d7112d2 F src/auth.c c8b2ab5c8bad4bd90ed7c294694f48269162c627 F src/btmutex.c 5d39da37c9d1282f3c6f9967afae6a34ee36b7ff F src/btree.c 5164b32950cfd41f2c5c31e8ff82c4a499918aef F src/btree.h 19dcf5ad23c17b98855da548e9a8e3eb4429d5eb F src/btreeInt.h 1c5a9da165718ef7de81e35ce9ab5d9ba9283f76 -F src/build.c e5bb1ddf630d4fbd042bf6a09168fe2feb4f7549 +F src/build.c 353cce0f4e773b0889d94663dca6dc99598d6b3d F src/callback.c 77b302b0d41468dcda78c70e706e5b84577f0fa0 F src/complete.c 4cf68fd75d60257524cbe74f87351b9848399131 F src/date.c 49c5a6d2de6c12000905b4d36868b07d3011bbf6 -F src/delete.c 44eac0a8a1bad1e62ee64e59e07a5a97a838bd0f +F src/delete.c 00e536847b8eedc5d35f89f7f38a8a7c1d2a22f9 F src/experimental.c 1b2d1a6cd62ecc39610e97670332ca073c50792b -F src/expr.c 560fbd4aff7c65e716e0310f85f044141abd9523 +F src/expr.c 68bcc07517c763cc7601be45d60084bc11ee0212 F src/func.c 996071cf0af9d967e58b69fce1909555059ebc7d F src/hash.c 45a7005aac044b6c86bd7e49c44bc15d30006d6c F src/hash.h 031cd9f915aff27e12262cb9eb570ac1b8326b53 -F src/insert.c 405cf0550252cdd3ba5b14ce1545571c82e35250 +F src/insert.c a19d9f51b611943386fcff714e3a0ffb137e463b F src/journal.c 807bed7a158979ac8d63953e1774e8d85bff65e2 F src/legacy.c 4ac53191fad2e3c4d59bde1228879b2dc5a96d66 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35 @@ -131,12 +131,12 @@ F src/pragma.c 1d3d9deefcf325e14a99b94f9f506f1a90a9232b F src/prepare.c c31a879d6795f4765fd0b113675c6debbc96b7fd F src/printf.c eb27822ba2eec669161409ca31279a24c26ac910 F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da -F src/select.c ff00897172bad962c7f775f0a1daa19e2a3ed80a +F src/select.c 9e3c65ca3d2508b585b6786dbcbe6cd65560b013 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c 5391e889384d2062249f668110d64ed16f601c4b F src/sqlite.h.in 2a7e3776534bbe6ff2cdc058f3abebe91e7e429f F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb -F src/sqliteInt.h ba917f5ed26ab1b47d3814d624e36a73dfe4c5bd +F src/sqliteInt.h b143c53760aeb4feeb4eaf8cb10eb26ad5b6c89e F src/sqliteLimit.h ee4430f88f69bf63527967bb35ca52af7b0ccb1e F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4 F src/tclsqlite.c 9923abeffc9b3d7dad58e92b319661521f60debf @@ -168,7 +168,7 @@ F src/update.c aad823f97a930e6982264299863837d4c6107d3b F src/utf.c ef4b7d83bae533b76c3e1bf635b113fdad86a736 F src/util.c 05f31144bbd3f1a24f4139ae029c42545cb72624 F src/vacuum.c 3f34f278809bf3eb0b62ec46ff779e9c385b28f0 -F src/vdbe.c 2a62b69e8344f28438ba0a007582be93b33b53ca +F src/vdbe.c 20bd088657280d75b630d299c59cdb3c6a5c9b9e F src/vdbe.h a9166e1601f5b74c20516a74182773a20baee43e F src/vdbeInt.h 31bd686595356284d5484592e2dc6e58025aa346 F src/vdbeapi.c f14174843bf4be2c9afdf2ef48b61e7c3ac62d7c @@ -176,7 +176,7 @@ F src/vdbeaux.c db33a4c2477546da05e772352be43896d24d51d5 F src/vdbeblob.c e386d49d8354aa5a58f0a7f2794303442c149120 F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6 F src/vdbemem.c a86119b5ccc41ab8653e4746f83d591ff0ae892e -F src/vtab.c 03014b2bfa8096ecac5fcdc80d34cd76e06af52a +F src/vtab.c 7f206d148f6e76f427f008f589610c72a4b9336c F src/where.c 9705df3c2b78ea8e02a768be8ac5d3f7a2902f1e F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -417,10 +417,11 @@ F test/schema2.test 35e1c9696443d6694c8980c411497c2b5190d32e F test/select1.test 871df931cbbc0e78170605628e8b5fc60765e265 F test/select2.test f3c2678c3a9f3cf08ec4988a3845bda64be6d9e3 F test/select3.test 7f99c0d4067064e0865479a56faa7aaa29b9041a -F test/select4.test 69015a6c02c3d8efed01f15e5d025fb4e5256183 +F test/select4.test 491193f50799e7fdb375ef04a1a8f40393dd7cfe F test/select5.test 0b47058d3e916c1fc9fe81f44b438e02bade21ce F test/select6.test 399f14b9ba37b768afe5d2cd8c12e4f340a69db8 F test/select7.test 7906735805cfbee4dddc0bed4c14e68d7f5f9c5f +F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d F test/server1.test f5b790d4c0498179151ca8a7715a65a7802c859c F test/shared.test 9a1e81629efaebaad2247e9e81aa33570fba8f13 F test/shared2.test 0ee9de8964d70e451936a48c41cb161d9134ccf4 @@ -605,7 +606,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P 6de0ee49073c7a47d5e10495b569b33df76d1448 -R 72b005345d91bd390788cc14572cdea6 +P 4744257d3cd2dd96485fde6d9f60542714383421 +R 9fded3f0e818ece501c0ececcbd26a94 U drh -Z f60ae65829aa8eb88bba7196814bec78 +Z fe475f28d0df336b5a859493e9293538 diff --git a/manifest.uuid b/manifest.uuid index da894ad9cb..992dde0d9b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4744257d3cd2dd96485fde6d9f60542714383421 \ No newline at end of file +a6dddebcc5ccbbf3009c9d06163a8b59036331de \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index d1873f0f50..a1bc2ee3c0 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code associated with the ANALYZE command. ** -** @(#) $Id: analyze.c,v 1.37 2008/01/10 23:50:11 drh Exp $ +** @(#) $Id: analyze.c,v 1.38 2008/01/12 12:48:08 drh Exp $ */ #ifndef SQLITE_OMIT_ANALYZE #include "sqliteInt.h" @@ -33,6 +33,7 @@ static void openStatTable( sqlite3 *db = pParse->db; Db *pDb; int iRootPage; + int createStat1 = 0; Table *pStat; Vdbe *v = sqlite3GetVdbe(pParse); @@ -43,13 +44,14 @@ static void openStatTable( if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){ /* The sqlite_stat1 tables does not exist. Create it. ** Note that a side-effect of the CREATE TABLE statement is to leave - ** the rootpage of the new table on the top of the stack. This is + ** the rootpage of the new table in register pParse->regRoot. This is ** important because the OpenWrite opcode below will be needing it. */ sqlite3NestedParse(pParse, "CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)", pDb->zName ); - iRootPage = 0; /* Cause rootpage to be taken from top of stack */ + iRootPage = pParse->regRoot; + createStat1 = 1; /* Cause rootpage to be taken from top of stack */ }else if( zWhere ){ /* The sqlite_stat1 table exists. Delete all entries associated with ** the table zWhere. */ @@ -69,13 +71,11 @@ static void openStatTable( ** If this vdbe did create the sqlite_stat1 table, then it must have ** already obtained a schema-lock, making the write-lock redundant. */ - if( iRootPage>0 ){ + if( !createStat1 ){ sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); } sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb); - if( iRootPage==0 ){ - sqlite3VdbeChangeP5(v, 1); - } + sqlite3VdbeChangeP5(v, createStat1); sqlite3VdbeAddOp2(v, OP_SetNumColumns, iStatCur, 3); } diff --git a/src/build.c b/src/build.c index b11e203756..12a330df6a 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.463 2008/01/10 23:50:11 drh Exp $ +** $Id: build.c,v 1.464 2008/01/12 12:48:08 drh Exp $ */ #include "sqliteInt.h" #include @@ -838,8 +838,9 @@ void sqlite3StartTable( ** now. */ if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){ - int lbl; + int j1; int fileFormat; + int reg1, reg2, reg3; sqlite3BeginWriteOperation(pParse, 0, iDb); #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -851,17 +852,19 @@ void sqlite3StartTable( /* If the file format and encoding in the database have not been set, ** set them now. */ - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, 0, 1); /* file_format */ + reg1 = pParse->regRowid = ++pParse->nMem; + reg2 = pParse->regRoot = ++pParse->nMem; + reg3 = ++pParse->nMem; + sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, 1); /* file_format */ sqlite3VdbeUsesBtree(v, iDb); - lbl = sqlite3VdbeMakeLabel(v); - sqlite3VdbeAddOp2(v, OP_If, 0, lbl); + j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? 1 : SQLITE_MAX_FILE_FORMAT; - sqlite3VdbeAddOp1(v, OP_Integer, fileFormat); - sqlite3VdbeAddOp2(v, OP_SetCookie, iDb, 1); - sqlite3VdbeAddOp1(v, OP_Integer, ENC(db)); - sqlite3VdbeAddOp2(v, OP_SetCookie, iDb, 4); - sqlite3VdbeResolveLabel(v, lbl); + sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 1, reg3); + sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, 4, reg3); + sqlite3VdbeJumpHere(v, j1); /* This just creates a place-holder record in the sqlite_master table. ** The record created does not contain anything yet. It will be replaced @@ -873,19 +876,18 @@ void sqlite3StartTable( */ #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE) if( isView || isVirtual ){ - sqlite3VdbeAddOp0(v, OP_Integer); + sqlite3VdbeAddOp2(v, OP_Integer, 0, reg2); }else #endif { - sqlite3VdbeAddOp1(v, OP_CreateTable, iDb); + sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2); } sqlite3OpenMasterTable(pParse, iDb); - sqlite3VdbeAddOp0(v, OP_NewRowid); - sqlite3VdbeAddOp0(v, OP_Copy); - sqlite3VdbeAddOp0(v, OP_Null); - sqlite3CodeInsert(pParse, 0, OPFLAG_APPEND); + sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1); + sqlite3VdbeAddOp2(v, OP_Null, 0, reg3); + sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1); + sqlite3VdbeChangeP5(v, OPFLAG_APPEND); sqlite3VdbeAddOp0(v, OP_Close); - sqlite3VdbeAddOp1(v, OP_Pull, 1); } /* Normal (non-error) return. */ @@ -1494,8 +1496,7 @@ void sqlite3EndTable( SelectDest dest; Table *pSelTab; - sqlite3VdbeAddOp0(v, OP_Copy); - sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, 0, iDb); + sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb); sqlite3VdbeChangeP5(v, 1); pParse->nTab = 2; sqlite3SelectDestInit(&dest, SRT_Table, 1); @@ -1531,13 +1532,15 @@ void sqlite3EndTable( */ sqlite3NestedParse(pParse, "UPDATE %Q.%s " - "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#0, sql=%Q " - "WHERE rowid=#1", + "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " + "WHERE rowid=#%d", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zType, p->zName, p->zName, - zStmt + pParse->regRoot, + zStmt, + pParse->regRowid ); sqlite3_free(zStmt); sqlite3ChangeCookie(db, v, iDb); @@ -1835,20 +1838,22 @@ void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ */ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); - sqlite3VdbeAddOp3(v, OP_Destroy, iTable, 0, iDb); + int r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); #ifndef SQLITE_OMIT_AUTOVACUUM - /* OP_Destroy pushes an integer onto the stack. If this integer + /* OP_Destroy stores an in integer r1. If this integer ** is non-zero, then it is the root page number of a table moved to ** location iTable. The following code modifies the sqlite_master table to ** reflect this. ** - ** The "#0" in the SQL is a special constant that means whatever value + ** The "#%d" in the SQL is a special constant that means whatever value ** is on the top of the stack. See sqlite3RegisterExpr(). */ sqlite3NestedParse(pParse, - "UPDATE %Q.%s SET rootpage=%d WHERE #0 AND rootpage=#0", - pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable); + "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", + pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1); #endif + sqlite3ReleaseTempReg(pParse, r1); } /* @@ -2649,8 +2654,7 @@ void sqlite3CreateIndex( /* Create the rootpage for the index */ sqlite3BeginWriteOperation(pParse, 1, iDb); - sqlite3VdbeAddOp1(v, OP_CreateIndex, iDb); - sqlite3VdbeAddOp2(v, OP_Copy, 0, iMem); + sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem); /* Gather the complete text of the CREATE INDEX statement into ** the zStmt variable @@ -2670,13 +2674,13 @@ void sqlite3CreateIndex( /* Add an entry in sqlite_master for this index */ sqlite3NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#0,%Q);", + "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName, pTab->zName, + iMem, zStmt ); - sqlite3VdbeAddOp1(v, OP_Pop, 1); sqlite3_free(zStmt); /* Fill the index with data and reparse the schema. Code an OP_Expire diff --git a/src/delete.c b/src/delete.c index 18a16ed557..87436ae935 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.156 2008/01/10 23:50:11 drh Exp $ +** $Id: delete.c,v 1.157 2008/01/12 12:48:08 drh Exp $ */ #include "sqliteInt.h" @@ -60,20 +60,6 @@ int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ return 0; } -/* -** This function is a temporary measure required because OP_Insert now -** reads the key and data to insert from memory cells. -*/ -void sqlite3CodeInsert(Parse *p, int iCur, u8 flags){ - int iData = ++p->nMem; - int iKey = ++p->nMem; - Vdbe *v = sqlite3GetVdbe(p); - sqlite3VdbeAddOp2(v, OP_Move, 0, iData); - sqlite3VdbeAddOp2(v, OP_Move, 0, iKey); - sqlite3VdbeAddOp3(v, OP_Insert, iCur, iData, iKey); - sqlite3VdbeChangeP5(v, flags); -} - /* ** Allocate nVal contiguous memory cells and return the index of the ** first. Also pop nVal elements from the stack and store them in the @@ -91,14 +77,6 @@ int sqlite3StackToReg(Parse *p, int nVal){ } return iRet; } -void sqlite3RegToStack(Parse *p, int iReg, int nVal){ - int i; - Vdbe *v = sqlite3GetVdbe(p); - assert(v); - for(i=0; i @@ -289,9 +289,8 @@ Expr *sqlite3PExpr( /* ** When doing a nested parse, you can include terms in an expression -** that look like this: #0 #1 #2 ... These terms refer to elements -** on the stack. "#0" means the top of the stack. -** "#1" means the next down on the stack. And so forth. +** that look like this: #1 #2 ... These terms refer to registers +** in the virtual machine. #N is the N-th register. ** ** This routine is called by the parser to deal with on of those terms. ** It immediately generates code to store the value in a memory location. @@ -301,7 +300,6 @@ Expr *sqlite3PExpr( Expr *sqlite3RegisterExpr(Parse *pParse, Token *pToken){ Vdbe *v = pParse->pVdbe; Expr *p; - int depth; if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", pToken); return sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); @@ -311,9 +309,7 @@ Expr *sqlite3RegisterExpr(Parse *pParse, Token *pToken){ if( p==0 ){ return 0; /* Malloc failed */ } - depth = atoi((char*)&pToken->z[1]); - p->iTable = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Copy, -depth, p->iTable); + p->iTable = atoi((char*)&pToken->z[1]); return p; } diff --git a/src/insert.c b/src/insert.c index 7577f46774..024a3403f4 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.222 2008/01/10 23:50:11 drh Exp $ +** $Id: insert.c,v 1.223 2008/01/12 12:48:08 drh Exp $ */ #include "sqliteInt.h" @@ -519,13 +519,18 @@ void sqlite3Insert( /* Generate the subroutine that SELECT calls to process each row of ** the result. Store the result in a temporary table */ + int regRec, regRowid; + srcTab = pParse->nTab++; + regRec = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); sqlite3VdbeResolveLabel(v, iInsertBlock); - sqlite3VdbeAddOp2(v, OP_RegMakeRec, regFromSelect, nColumn); - sqlite3VdbeAddOp1(v, OP_NewRowid, srcTab); - sqlite3VdbeAddOp2(v, OP_Pull, 1, 0); - sqlite3CodeInsert(pParse, srcTab, OPFLAG_APPEND); + sqlite3VdbeAddOp3(v, OP_RegMakeRec, regFromSelect, nColumn, regRec); + sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regRowid); sqlite3VdbeAddOp2(v, OP_Return, 0, 0); + sqlite3ReleaseTempReg(pParse, regRec); + sqlite3ReleaseTempReg(pParse, regRowid); /* The following code runs first because the GOTO at the very top ** of the program jumps to it. Create the temporary table, then jump @@ -663,7 +668,6 @@ void sqlite3Insert( }else if( pSelect ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iSelectLoop); sqlite3VdbeResolveLabel(v, iInsertBlock); - sqlite3RegToStack(pParse, regFromSelect, nColumn); sqlite3VdbeAddOp2(v, OP_StackDepth, -1, 0); } @@ -770,7 +774,7 @@ void sqlite3Insert( if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, -(nColumn - keyColumn - 1), regRowid); + sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid); }else{ VdbeOp *pOp; sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, 0); @@ -835,7 +839,7 @@ void sqlite3Insert( }else if( useTempTable ){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_SCopy, -(nColumn-j-1), iRegStore); + sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore); }else{ sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore); } @@ -899,7 +903,6 @@ void sqlite3Insert( sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, srcTab, 0); }else if( pSelect ){ - sqlite3VdbeAddOp2(v, OP_Pop, nColumn, 0); sqlite3VdbeAddOp2(v, OP_Return, 0, 0); sqlite3VdbeResolveLabel(v, iCleanup); } @@ -1279,6 +1282,7 @@ void sqlite3CompleteInsertion( Index *pIdx; int pik_flags; int regData; + int regRec; v = sqlite3GetVdbe(pParse); assert( v!=0 ); @@ -1289,14 +1293,12 @@ void sqlite3CompleteInsertion( sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]); } regData = regRowid + 1; - sqlite3VdbeAddOp1(v, OP_SCopy, regRowid); - sqlite3VdbeAddOp2(v, OP_RegMakeRec, regData, pTab->nCol); + regRec = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_RegMakeRec, regData, pTab->nCol, regRec); sqlite3TableAffinityStr(v, pTab); #ifndef SQLITE_OMIT_TRIGGER if( newIdx>=0 ){ - sqlite3VdbeAddOp1(v, OP_SCopy, regRowid); - sqlite3VdbeAddOp1(v, OP_SCopy, -1); - sqlite3CodeInsert(pParse, newIdx, 0); + sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid); } #endif if( pParse->nested ){ @@ -1308,10 +1310,11 @@ void sqlite3CompleteInsertion( if( appendBias ){ pik_flags |= OPFLAG_APPEND; } - sqlite3CodeInsert(pParse, baseCur, pik_flags); + sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid); if( !pParse->nested ){ sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); } + sqlite3VdbeChangeP5(v, pik_flags); } /* @@ -1465,6 +1468,7 @@ static int xferOptimization( KeyInfo *pKey; /* Key information for an index */ int regAutoinc; /* Memory register used by AUTOINC */ int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */ + int regData, regRowid; /* Registers holding data and rowid */ if( pSelect==0 ){ return 0; /* Must be of the form INSERT INTO ... SELECT ... */ @@ -1612,22 +1616,24 @@ static int xferOptimization( } sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead); emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); + regData = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); if( pDest->iPKey>=0 ){ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, 0); - sqlite3VdbeAddOp0(v, OP_Copy); - addr2 = sqlite3VdbeAddOp2(v, OP_NotExists, iDest, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); + addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, "PRIMARY KEY must be unique", P4_STATIC); sqlite3VdbeJumpHere(v, addr2); - autoIncStep(pParse, regAutoinc, 0); + autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 ){ - addr1 = sqlite3VdbeAddOp1(v, OP_NewRowid, iDest); + addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ - addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, 0); + addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( pDest->autoInc==0 ); } - sqlite3VdbeAddOp1(v, OP_RowData, iSrc); - sqlite3CodeInsert(pParse,iDest,OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); + sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData); + sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid); + sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND); sqlite3VdbeChangeP4(v, -1, pDest->zName, 0); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); autoIncEnd(pParse, iDbDest, pDest, regAutoinc); @@ -1647,12 +1653,14 @@ static int xferOptimization( (char*)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); - sqlite3VdbeAddOp1(v, OP_RowKey, iSrc); - sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, 0, 1); + sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData); + sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); sqlite3VdbeJumpHere(v, addr1); } sqlite3VdbeJumpHere(v, emptySrcTest); + sqlite3ReleaseTempReg(pParse, regRowid); + sqlite3ReleaseTempReg(pParse, regData); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); if( emptyDestTest ){ diff --git a/src/select.c b/src/select.c index 65aa362269..6d28f40636 100644 --- a/src/select.c +++ b/src/select.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. ** -** $Id: select.c,v 1.397 2008/01/10 23:50:11 drh Exp $ +** $Id: select.c,v 1.398 2008/01/12 12:48:08 drh Exp $ */ #include "sqliteInt.h" @@ -394,7 +394,8 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){ static void pushOntoSorter( Parse *pParse, /* Parser context */ ExprList *pOrderBy, /* The ORDER BY clause */ - Select *pSelect /* The whole SELECT statement */ + Select *pSelect, /* The whole SELECT statement */ + int regData /* Register holding data to be sorted */ ){ Vdbe *v = pParse->pVdbe; int nExpr = pOrderBy->nExpr; @@ -402,15 +403,21 @@ static void pushOntoSorter( int regRecord = sqlite3GetTempReg(pParse); sqlite3ExprCodeExprList(pParse, pOrderBy, regBase); sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr); - sqlite3VdbeAddOp2(v, OP_Move, 0, regBase+nExpr+1); + sqlite3VdbeAddOp2(v, OP_Move, regData, regBase+nExpr+1); sqlite3VdbeAddOp3(v, OP_RegMakeRec, regBase, nExpr + 2, regRecord); sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ReleaseTempRange(pParse, regBase, nExpr+2); if( pSelect->iLimit>=0 ){ int addr1, addr2; - addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, pSelect->iLimit+1); - sqlite3VdbeAddOp2(v, OP_AddImm, pSelect->iLimit+1, -1); + int iLimit; + if( pSelect->pOffset ){ + iLimit = pSelect->iOffset+1; + }else{ + iLimit = pSelect->iLimit; + } + addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); + sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); @@ -426,16 +433,12 @@ static void pushOntoSorter( static void codeOffset( Vdbe *v, /* Generate code into this VM */ Select *p, /* The SELECT statement being coded */ - int iContinue, /* Jump here to skip the current record */ - int nPop /* Number of times to pop stack when jumping */ + int iContinue /* Jump here to skip the current record */ ){ if( p->iOffset>=0 && iContinue!=0 ){ int addr; sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1); addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset); - if( nPop>0 ){ - sqlite3VdbeAddOp1(v, OP_Pop, nPop); - } sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue); VdbeComment((v, "skip OFFSET records")); sqlite3VdbeJumpHere(v, addr); @@ -524,7 +527,7 @@ static int selectInnerLoop( */ hasDistinct = distinct>=0 && pEList->nExpr>0; if( pOrderBy==0 && !hasDistinct ){ - codeOffset(v, p, iContinue, 0); + codeOffset(v, p, iContinue); } /* Pull the requested columns. @@ -563,7 +566,7 @@ static int selectInnerLoop( assert( pEList->nExpr==nColumn ); codeDistinct(v, distinct, iContinue, nColumn, iMem); if( pOrderBy==0 ){ - codeOffset(v, p, iContinue, nColumn); + codeOffset(v, p, iContinue); } } @@ -603,14 +606,18 @@ static int selectInnerLoop( */ case SRT_Table: case SRT_EphemTab: { - sqlite3VdbeAddOp2(v, OP_RegMakeRec, iMem, nColumn); + int r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_RegMakeRec, iMem, nColumn, r1); if( pOrderBy ){ - pushOntoSorter(pParse, pOrderBy, p); + pushOntoSorter(pParse, pOrderBy, p, r1); }else{ - sqlite3VdbeAddOp1(v, OP_NewRowid, iParm); - sqlite3VdbeAddOp2(v, OP_Pull, 1, 0); - sqlite3CodeInsert(pParse, iParm, OPFLAG_APPEND); + int r2 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2); + sqlite3VdbeAddOp3(v, OP_Insert, iParm, r1, r2); + sqlite3VdbeChangeP5(v, OPFLAG_APPEND); + sqlite3ReleaseTempReg(pParse, r2); } + sqlite3ReleaseTempReg(pParse, r1); break; } @@ -630,11 +637,12 @@ static int selectInnerLoop( ** ORDER BY in this case since the order of entries in the set ** does not matter. But there might be a LIMIT clause, in which ** case the order does matter */ - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, 0); - pushOntoSorter(pParse, pOrderBy, p); + pushOntoSorter(pParse, pOrderBy, p, iMem); }else{ - sqlite3VdbeAddOp4(v, OP_RegMakeRec, iMem, 1, 0, &p->affinity, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, 0); + int r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp4(v, OP_RegMakeRec, iMem, 1, r1, &p->affinity, 1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1); + sqlite3ReleaseTempReg(pParse, r1); } sqlite3VdbeJumpHere(v, addr2); break; @@ -654,11 +662,10 @@ static int selectInnerLoop( */ case SRT_Mem: { assert( nColumn==1 ); - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, 0); if( pOrderBy ){ - pushOntoSorter(pParse, pOrderBy, p); + pushOntoSorter(pParse, pOrderBy, p, iMem); }else{ - sqlite3VdbeAddOp2(v, OP_Move, 0, iParm); + sqlite3VdbeAddOp2(v, OP_Move, iMem, iParm); /* The LIMIT clause will jump out of the loop for us */ } break; @@ -672,8 +679,10 @@ static int selectInnerLoop( case SRT_Subroutine: case SRT_Callback: { if( pOrderBy ){ - sqlite3VdbeAddOp2(v, OP_RegMakeRec, iMem, nColumn); - pushOntoSorter(pParse, pOrderBy, p); + int r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_RegMakeRec, iMem, nColumn, r1); + pushOntoSorter(pParse, pOrderBy, p, r1); + sqlite3ReleaseTempReg(pParse, r1); }else if( eDest==SRT_Subroutine ){ sqlite3VdbeAddOp2(v, OP_Gosub, 0, iParm); }else{ @@ -779,7 +788,7 @@ static void generateSortTail( sqlite3VdbeAddOp2(v, OP_SetNumColumns, pseudoTab, nColumn); } addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, brk); - codeOffset(v, p, cont, 0); + codeOffset(v, p, cont); regRow = sqlite3GetTempReg(pParse); regRowid = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow); @@ -1737,7 +1746,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ Vdbe *v = 0; int iLimit = 0; int iOffset; - int addr1, addr2; + int addr1; /* ** "LIMIT -1" always shows all rows. There is some @@ -1747,42 +1756,34 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){ */ if( p->pLimit ){ p->iLimit = iLimit = ++pParse->nMem; - pParse->nMem++; v = sqlite3GetVdbe(pParse); if( v==0 ) return; - sqlite3ExprCode(pParse, p->pLimit, 0); - sqlite3VdbeAddOp0(v, OP_MustBeInt); - sqlite3VdbeAddOp2(v, OP_Move, 0, iLimit); + sqlite3ExprCode(pParse, p->pLimit, iLimit); + sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeComment((v, "LIMIT counter")); sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); - sqlite3VdbeAddOp2(v, OP_SCopy, iLimit, 0); } if( p->pOffset ){ p->iOffset = iOffset = ++pParse->nMem; + if( p->pLimit ){ + pParse->nMem++; /* Allocate an extra register for limit+offset */ + } v = sqlite3GetVdbe(pParse); if( v==0 ) return; - sqlite3ExprCode(pParse, p->pOffset, 0); - sqlite3VdbeAddOp0(v, OP_MustBeInt); - sqlite3VdbeAddOp2(v, p->pLimit==0 ? OP_Move : OP_Copy, 0, iOffset); + sqlite3ExprCode(pParse, p->pOffset, iOffset); + sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeComment((v, "OFFSET counter")); addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); - sqlite3VdbeAddOp2(v, OP_Pop, 1, 0); - sqlite3VdbeAddOp2(v, OP_Integer, 0, 0); + sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset); sqlite3VdbeJumpHere(v, addr1); if( p->pLimit ){ - sqlite3VdbeAddOp2(v, OP_Add, 0, 0); + sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1); + VdbeComment((v, "LIMIT+OFFSET")); + addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); + sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1); + sqlite3VdbeJumpHere(v, addr1); } } - if( p->pLimit ){ - addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); - sqlite3VdbeAddOp1(v, OP_Pop, 1); - sqlite3VdbeAddOp2(v, OP_Integer, -1, iLimit+1); - addr2 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, addr1); - sqlite3VdbeAddOp2(v, OP_Move, 0, iLimit+1); - VdbeComment((v, "LIMIT+OFFSET")); - sqlite3VdbeJumpHere(v, addr2); - } } /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 52ee5db32f..d9c9dd40a6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.647 2008/01/10 23:50:11 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.648 2008/01/12 12:48:08 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1420,6 +1420,8 @@ struct Parse { /* Above is constant between recursions. Below is reset before and after ** each recursion */ + int regRowid; /* Register holding rowid of CREATE TABLE entry */ + int regRoot; /* Register holding root page number for new objects */ int nVar; /* Number of '?' variables seen in the SQL so far */ int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ @@ -1945,9 +1947,7 @@ int sqlite3OpenTempDatabase(Parse *); void sqlite3StrAccumAppend(StrAccum*,const char*,int); char *sqlite3StrAccumFinish(StrAccum*); void sqlite3StrAccumReset(StrAccum*); -void sqlite3CodeInsert(Parse *, int, u8); int sqlite3StackToReg(Parse *, int); -void sqlite3RegToStack(Parse *, int, int); void sqlite3SelectDestInit(SelectDest*,int,int); /* diff --git a/src/vdbe.c b/src/vdbe.c index 66c49af600..b56babc3ac 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.692 2008/01/10 23:50:11 drh Exp $ +** $Id: vdbe.c,v 1.693 2008/01/12 12:48:08 drh Exp $ */ #include "sqliteInt.h" #include @@ -94,6 +94,18 @@ int sqlite3_sort_count = 0; int sqlite3_max_blobsize = 0; #endif +/* +** Test a register to see if it exceeds the current maximum blob size. +** If it does, record the new maximum blob size. +*/ +#ifdef SQLITE_TEST +# define UPDATE_MAX_BLOBSIZE(P) if( ((P)->flags&(MEM_Str|MEM_Blob))!=0 \ + && (P)->n>sqlite3_max_blobsize ) \ + {sqlite3_max_blobsize = (P)->n;} +#else +# define UPDATE_MAX_BLOBSIZE(P) +#endif + /* ** Release the memory associated with the given stack level. This ** leaves the Mem.flags field in an inconsistent state. @@ -939,6 +951,7 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */ if( pOp->p1>SQLITE_MAX_LENGTH ){ goto too_big; } + UPDATE_MAX_BLOBSIZE(pOut); break; } #endif @@ -959,6 +972,7 @@ case OP_String: { /* out2-prerelease */ pOut->z = pOp->p4.z; pOut->n = pOp->p1; pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1019,6 +1033,7 @@ case OP_Blob: { /* out2-prerelease */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; + UPDATE_MAX_BLOBSIZE(pOut); break; } #endif /* SQLITE_OMIT_BLOB_LITERAL */ @@ -1043,6 +1058,7 @@ case OP_Variable: { /* out2-prerelease */ goto too_big; } sqlite3VdbeMemShallowCopy(pOut, &p->aVar[j], MEM_Static); + UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1136,40 +1152,6 @@ case OP_SCopy: { break; } -/* Opcode: Pull P1 * * -** -** The P1-th element is removed from its current location on -** the stack and pushed back on top of the stack. The -** top of the stack is element 0, so "Pull 0 0 0" is -** a no-op. "Pull 1 0 0" swaps the top two elements of -** the stack. -*/ -case OP_Pull: { /* no-push */ - Mem *pFrom = &pTos[-pOp->p1]; - int i; - Mem ts; - - ts = *pFrom; - Deephemeralize(pTos); - for(i=0; ip1; i++, pFrom++){ - Deephemeralize(&pFrom[1]); - assert( (pFrom[1].flags & MEM_Ephem)==0 ); - *pFrom = pFrom[1]; - if( pFrom->flags & MEM_Short ){ - assert( pFrom->flags & (MEM_Str|MEM_Blob) ); - assert( pFrom->z==pFrom[1].zShort ); - pFrom->z = pFrom->zShort; - } - } - *pTos = ts; - if( pTos->flags & MEM_Short ){ - assert( pTos->flags & (MEM_Str|MEM_Blob) ); - assert( pTos->z==pTos[-pOp->p1].zShort ); - pTos->z = pTos->zShort; - } - break; -} - /* Opcode: ResultRow P1 P2 * ** ** The registers P1 throught P1+P2-1 contain a single row of @@ -1257,6 +1239,7 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */ pOut->xDel = 0; pOut->enc = encoding; pOut->z = zNew; + UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1489,6 +1472,7 @@ case OP_Function: { if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } + UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -1658,6 +1642,7 @@ case OP_ToText: { /* same as TK_TO_TEXT, no-push, in1 */ rc = ExpandBlob(pIn1); assert( pIn1->flags & MEM_Str ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob); + UPDATE_MAX_BLOBSIZE(pIn1); break; } @@ -1679,6 +1664,7 @@ case OP_ToBlob: { /* same as TK_TO_BLOB, no-push, in1 */ pIn1->flags |= MEM_Blob; } pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Str); + UPDATE_MAX_BLOBSIZE(pIn1); break; } @@ -2337,6 +2323,7 @@ case OP_Column: { rc = sqlite3VdbeMemMakeWriteable(pDest); op_column_out: + UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); break; } @@ -2525,6 +2512,7 @@ case OP_MakeRecord: { /* jump */ } pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */ REGISTER_TRACE(pOp->p3, pOut); + UPDATE_MAX_BLOBSIZE(pOut); /* If a NULL was encountered and jumpIfNull is non-zero, take the jump. */ if( jumpIfNull && containsNull ){ @@ -2683,7 +2671,7 @@ case OP_ReadCookie: { /* out2-prerelease */ int iDb = pOp->p1; int iCookie = pOp->p3; - assert( pOp->p2p3flags = MEM_Null; } pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ + UPDATE_MAX_BLOBSIZE(pOut); break; } @@ -4440,6 +4429,7 @@ case OP_IntegrityCk: { pIn1->xDel = 0; } pIn1->enc = SQLITE_UTF8; + UPDATE_MAX_BLOBSIZE(pIn1); sqlite3VdbeChangeEncoding(pIn1, encoding); sqlite3_free(aRoot); break; @@ -4656,6 +4646,7 @@ case OP_AggFinal: { /* no-push */ if( rc==SQLITE_ERROR ){ sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pMem), (char*)0); } + UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } @@ -4961,6 +4952,7 @@ case OP_VColumn: { pDest->flags = 0; } sqlite3VdbeMemMove(pDest, &sContext.s); + UPDATE_MAX_BLOBSIZE(pDest); if( sqlite3SafetyOn(db) ){ goto abort_due_to_misuse; @@ -5134,16 +5126,6 @@ default: { } #endif -#ifdef SQLITE_TEST - /* Keep track of the size of the largest BLOB or STR that has appeared - ** on the top of the VDBE stack. - */ - if( pTos>=p->aStack && (pTos->flags & (MEM_Blob|MEM_Str))!=0 - && pTos->n>sqlite3_max_blobsize ){ - sqlite3_max_blobsize = pTos->n; - } -#endif - /* The following code adds nothing to the actual functionality ** of the program. It is only here for testing and debugging. ** On the other hand, it does burn CPU cycles every time through diff --git a/src/vtab.c b/src/vtab.c index 9746f40e76..398fd649fe 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.60 2008/01/03 00:01:25 drh Exp $ +** $Id: vtab.c,v 1.61 2008/01/12 12:48:08 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -259,20 +259,20 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ ** SQLITE_MASTER table. We just need to update that slot with all ** the information we've collected. ** - ** The top of the stack is the rootpage allocated by sqlite3StartTable(). - ** This value is always 0 and is ignored, a virtual table does not have a - ** rootpage. The next entry on the stack is the rowid of the record - ** in the sqlite_master table. + ** The VM register number pParse->regRowid holds the rowid of an + ** entry in the sqlite_master table tht was created for this vtab + ** by sqlite3StartTable(). */ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3NestedParse(pParse, "UPDATE %Q.%s " "SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q " - "WHERE rowid=#1", + "WHERE rowid=#%d", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pTab->zName, pTab->zName, - zStmt + zStmt, + pParse->regRowid ); sqlite3_free(zStmt); v = sqlite3GetVdbe(pParse); diff --git a/test/select4.test b/test/select4.test index 62b041adcc..adbc57104d 100644 --- a/test/select4.test +++ b/test/select4.test @@ -12,7 +12,7 @@ # focus of this file is testing UNION, INTERSECT and EXCEPT operators # in SELECT statements. # -# $Id: select4.test,v 1.24 2007/12/13 07:58:51 danielk1977 Exp $ +# $Id: select4.test,v 1.25 2008/01/12 12:48:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -618,4 +618,55 @@ do_test select4-9.12 { } ;# ifcapable compound +# Try combining DISTINCT, LIMIT, and OFFSET. Make sure they all work +# together. +# +do_test select4-10.1 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log + } +} {0 1 2 3 4 5} +do_test select4-10.2 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 4 + } +} {0 1 2 3} +do_test select4-10.3 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 0 + } +} {} +do_test select4-10.4 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT -1 + } +} {0 1 2 3 4 5} +do_test select4-10.5 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT -1 OFFSET 2 + } +} {2 3 4 5} +do_test select4-10.6 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 3 OFFSET 2 + } +} {2 3 4} +do_test select4-10.7 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY +log LIMIT 3 OFFSET 20 + } +} {} +do_test select4-10.8 { + execsql { + SELECT DISTINCT log FROM t1 ORDER BY log LIMIT 0 OFFSET 3 + } +} {} +do_test select4-10.9 { +breakpoint + execsql { + SELECT DISTINCT max(n), log FROM t1 ORDER BY +log; -- LIMIT 2 OFFSET 1 + } +} {31 5} + + finish_test diff --git a/test/select8.test b/test/select8.test new file mode 100644 index 0000000000..98626641bf --- /dev/null +++ b/test/select8.test @@ -0,0 +1,62 @@ +# 2001 September 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 that LIMIT and OFFSET work for +# unusual combinations SELECT statements. +# +# $Id: select8.test,v 1.1 2008/01/12 12:48:09 drh Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +execsql { + CREATE TABLE songs(songid, artist, timesplayed); + INSERT INTO songs VALUES(1,'one',1); + INSERT INTO songs VALUES(2,'one',2); + INSERT INTO songs VALUES(3,'two',3); + INSERT INTO songs VALUES(4,'three',5); + INSERT INTO songs VALUES(5,'one',7); + INSERT INTO songs VALUES(6,'two',11); +} +set result [execsql { + SELECT DISTINCT artist,sum(timesplayed) AS total + FROM songs + GROUP BY LOWER(artist) +}] +puts result=$result +do_test select8-1.1 { + execsql { + SELECT DISTINCT artist,sum(timesplayed) AS total + FROM songs + GROUP BY LOWER(artist) + LIMIT 1 OFFSET 1 + } +} [lrange $result 2 3] +do_test select8-1.2 { + execsql { + SELECT DISTINCT artist,sum(timesplayed) AS total + FROM songs + GROUP BY LOWER(artist) + LIMIT 2 OFFSET 1 + } +} [lrange $result 2 5] +do_test select8-1.3 { + execsql { + SELECT DISTINCT artist,sum(timesplayed) AS total + FROM songs + GROUP BY LOWER(artist) + LIMIT -1 OFFSET 2 + } +} [lrange $result 4 end] + + +finish_test