From 9bbca4c1c07241684aa9f5e7a85271e3ceb9f4d2 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Nov 2001 04:00:18 +0000 Subject: [PATCH] Implement indices that occur in sort order and the LIMIT...OFFSET clause of SELECT statements. (CVS 301) FossilOrigin-Name: eb07768ae93f14bf6c150c1c4329948857a9d01c --- manifest | 24 +-- manifest.uuid | 2 +- src/parse.y | 20 +- src/select.c | 22 ++- src/sqliteInt.h | 7 +- src/tokenize.c | 4 +- src/util.c | 88 ++++++++- src/vdbe.c | 476 +++++++++++++++++++++++++++--------------------- src/vdbe.h | 121 ++++++------ 9 files changed, 469 insertions(+), 295 deletions(-) diff --git a/manifest b/manifest index 3b815255a8..4a6c2336a4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Increase\smaximum\srow\ssize\sto\s1MB.\s(CVS\s300) -D 2001-11-04T18:32:47 +C Implement\sindices\sthat\soccur\sin\ssort\sorder\sand\sthe\sLIMIT...OFFSET\sclause\nof\sSELECT\sstatements.\s(CVS\s301) +D 2001-11-06T04:00:18 F Makefile.in 6801df952cb1df64aa32e4de85fed24511d28efd F Makefile.template 1fdb891f14083ee0b63cf7282f91529634438e7a F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -33,24 +33,24 @@ F src/os.c 66b677479eae37e30bdfbe32deb0fe6a2efca983 F src/os.h bed702c9e3b768bc3cb1b12c90b83d099c1546be F src/pager.c 0bd0b4b693edb43c72774e3e749d8667e2ae7094 F src/pager.h a0d4c5ae271914aa07b62aee0707997d6932b6ca -F src/parse.y 148e4cd134d3cbd816dcb0df50e49e498faa6ba4 +F src/parse.y 5295f393f41ea89958287e5738e6c12c7cd67482 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 2a9cc2c9716d14815fd4c2accf89d87a1143e46b -F src/select.c c34b02eafaa69fde6b4428df7861c3417b3079f9 +F src/select.c 008d79761a6025e4db490c8f732e32631c0c57c3 F src/shell.c 71597951753b56a97fea1c7a30908f31e635c00c F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 934de9112747ad8d8e7d5fec44876246b24ca5a3 -F src/sqliteInt.h fa9f56b77e0790f0ec329195c2255e2d8e440b0a +F src/sqliteInt.h fc2f7da1fee1e871b20b375c50c582065d891c7f F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a F src/tclsqlite.c 4896e078495bf868742f5394dcf01c5efe5bea02 F src/test1.c e4b31f62ea71963cbae44338acf477a04fc8fc49 F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321 F src/test3.c 4a0d7b882fdae731dbb759f512ad867122452f96 -F src/tokenize.c 9ede24b17630351d70258bf8fa4f70f5990d45ae +F src/tokenize.c 830e9ef684334070a26583d94770bb869e2727bf F src/update.c 4eeb154a2da8a934d180e2d9e4211ac0a7a4ce8b -F src/util.c aa4d2de60cb2445239b71c79c3a8c0b7c0d3336a -F src/vdbe.c a71e73e9a4a63fe2f63546a1a43fce4de5136476 -F src/vdbe.h c29e6fdfa157b3cce18258c05d9d533eb9cd1377 +F src/util.c ac83973ecc647d3d3c58708f148442365abf9b94 +F src/vdbe.c 9f6ff3444a38d9bba27497be56e4ad386b316cbb +F src/vdbe.h ea71a2c29d43c03283dee30237a01f4726900b29 F src/where.c 601f096f2a37ca688a775ca36d33534b13b876cb F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe F test/bigrow.test 9458134d67f81559845f934fdd6802fe19a68ad1 @@ -114,7 +114,7 @@ F www/speed.tcl 212a91d555384e01873160d6a189f1490c791bc2 F www/sqlite.tcl 6a21242a272e9c0939a04419a51c3d50cae33e3e F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa F www/vdbe.tcl bb7d620995f0a987293e9d4fb6185a3b077e9b44 -P 0fd2874205f1a4b89fc069cb429c1b0c7a0b99c1 -R 810197be2f3f50401607f0c17a5fe11e +P 7dd58fad398253608f55867cf1c7749eef005657 +R 6b4d638b3f341e525807c77329fb1dc1 U drh -Z 863cf3e2c644c7eb0aa13f1cb68860b5 +Z e5f14755e0856f35fd2676a75ffdfa97 diff --git a/manifest.uuid b/manifest.uuid index 1cb9881ffa..475a506a2c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7dd58fad398253608f55867cf1c7749eef005657 \ No newline at end of file +eb07768ae93f14bf6c150c1c4329948857a9d01c \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 1180ff1782..86679e828c 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.37 2001/10/13 02:59:09 drh Exp $ +** @(#) $Id: parse.y,v 1.38 2001/11/06 04:00:18 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -28,6 +28,11 @@ %include { #include "sqliteInt.h" #include "parse.h" + +/* +** A structure for holding two integers +*/ +struct twoint { int a,b; }; } // These are extra tokens used by the lexer but never seen by the @@ -96,6 +101,7 @@ id(A) ::= PRAGMA(X). {A = X;} id(A) ::= CLUSTER(X). {A = X;} id(A) ::= ID(X). {A = X;} id(A) ::= TEMP(X). {A = X;} +id(A) ::= OFFSET(X). {A = X;} // And "ids" is an identifer-or-string. // @@ -179,8 +185,8 @@ joinop(A) ::= UNION ALL. {A = TK_ALL;} joinop(A) ::= INTERSECT. {A = TK_INTERSECT;} joinop(A) ::= EXCEPT. {A = TK_EXCEPT;} oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) - groupby_opt(P) having_opt(Q) orderby_opt(Z). { - A = sqliteSelectNew(W,X,Y,P,Q,Z,D); + groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { + A = sqliteSelectNew(W,X,Y,P,Q,Z,D,L.a,L.b); } // The "distinct" nonterminal is true (1) if the DISTINCT keyword is @@ -259,6 +265,14 @@ groupby_opt(A) ::= GROUP BY exprlist(X). {A = X;} having_opt(A) ::= . {A = 0;} having_opt(A) ::= HAVING expr(X). {A = X;} +%type limit_opt {struct twoint} +limit_opt(A) ::= . {A.a = -1; A.b = 0;} +limit_opt(A) ::= LIMIT INTEGER(X). {A.a = atoi(X.z); A.b = 0;} +limit_opt(A) ::= LIMIT INTEGER(X) limit_sep INTEGER(Y). + {A.a = atoi(X.z); A.b = atoi(Y.z);} +limit_sep ::= OFFSET. +limit_sep ::= COMMA. + /////////////////////////// The DELETE statement ///////////////////////////// // cmd ::= DELETE FROM ids(X) where_opt(Y). diff --git a/src/select.c b/src/select.c index bf17fa920d..2e1a4c0699 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.45 2001/11/01 14:41:34 drh Exp $ +** $Id: select.c,v 1.46 2001/11/06 04:00:19 drh Exp $ */ #include "sqliteInt.h" @@ -27,7 +27,9 @@ Select *sqliteSelectNew( ExprList *pGroupBy, /* the GROUP BY clause */ Expr *pHaving, /* the HAVING clause */ ExprList *pOrderBy, /* the ORDER BY clause */ - int isDistinct /* true if the DISTINCT keyword is present */ + int isDistinct, /* true if the DISTINCT keyword is present */ + int nLimit, /* LIMIT value. -1 means not used */ + int nOffset /* OFFSET value. -1 means not used */ ){ Select *pNew; pNew = sqliteMalloc( sizeof(*pNew) ); @@ -47,6 +49,8 @@ Select *sqliteSelectNew( pNew->pOrderBy = pOrderBy; pNew->isDistinct = isDistinct; pNew->op = TK_SELECT; + pNew->nLimit = nLimit; + pNew->nOffset = nOffset; } return pNew; } @@ -203,7 +207,7 @@ static int selectInnerLoop( /* If none of the above, send the data to the callback function. */ { - sqliteVdbeAddOp(v, OP_Callback, nColumn, 0); + sqliteVdbeAddOp(v, OP_Callback, nColumn, iBreak); } return 0; } @@ -219,7 +223,7 @@ static void generateSortTail(Vdbe *v, int nColumn){ int addr; sqliteVdbeAddOp(v, OP_Sort, 0, 0); addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end); - sqliteVdbeAddOp(v, OP_SortCallback, nColumn, 0); + sqliteVdbeAddOp(v, OP_SortCallback, nColumn, end); sqliteVdbeAddOp(v, OP_Goto, 0, addr); sqliteVdbeResolveLabel(v, end); sqliteVdbeAddOp(v, OP_SortReset, 0, 0); @@ -854,6 +858,16 @@ int sqliteSelect( v = sqliteGetVdbe(pParse); if( v==0 ) return 1; + /* Set the limiter + */ + if( p->nLimit<=0 ){ + p->nOffset = 0; + }else{ + if( p->nOffset<0 ) p->nOffset = 0; + sqliteVdbeAddOp(v, OP_Limit, p->nLimit, p->nOffset); + } + + /* Identify column names if we will be using in the callback. This ** step is skipped if the output is going to a table or a memory cell. */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c1232e6845..1f170d91d7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.66 2001/11/04 18:32:47 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.67 2001/11/06 04:00:19 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -328,6 +328,7 @@ struct Select { ExprList *pOrderBy; /* The ORDER BY clause */ int op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */ Select *pPrior; /* Prior select in a compound select statement */ + int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */ }; /* @@ -407,6 +408,7 @@ int sqliteStrNICmp(const char *, const char *, int); int sqliteHashNoCase(const char *, int); int sqliteCompare(const char *, const char *); int sqliteSortCompare(const char *, const char *); +void sqliteRealToSortable(double r, char *); #ifdef MEMORY_DEBUG void *sqliteMalloc_(int,char*,int); void sqliteFree_(void*,char*,int); @@ -449,7 +451,8 @@ void sqliteIdListDelete(IdList*); void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*); void sqliteDropIndex(Parse*, Token*); int sqliteSelect(Parse*, Select*, int, int); -Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,int); +Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*, + int,int,int); void sqliteSelectDelete(Select*); void sqliteDeleteFrom(Parse*, Token*, Expr*); void sqliteUpdate(Parse*, Token*, ExprList*, Expr*); diff --git a/src/tokenize.c b/src/tokenize.c index 61edd6202e..1917c652dc 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -15,7 +15,7 @@ ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.31 2001/11/04 18:32:48 drh Exp $ +** $Id: tokenize.c,v 1.32 2001/11/06 04:00:19 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -73,9 +73,11 @@ static Keyword aKeywordTable[] = { { "ISNULL", 0, TK_ISNULL, 0 }, { "KEY", 0, TK_KEY, 0 }, { "LIKE", 0, TK_LIKE, 0 }, + { "LIMIT", 0, TK_LIMIT, 0 }, { "NOT", 0, TK_NOT, 0 }, { "NOTNULL", 0, TK_NOTNULL, 0 }, { "NULL", 0, TK_NULL, 0 }, + { "OFFSET", 0, TK_OFFSET, 0 }, { "ON", 0, TK_ON, 0 }, { "OR", 0, TK_OR, 0 }, { "ORDER", 0, TK_ORDER, 0 }, diff --git a/src/util.c b/src/util.c index 3d0fa4c9b7..88b2f24481 100644 --- a/src/util.c +++ b/src/util.c @@ -14,7 +14,7 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.30 2001/10/22 02:58:10 drh Exp $ +** $Id: util.c,v 1.31 2001/11/06 04:00:19 drh Exp $ */ #include "sqliteInt.h" #include @@ -754,6 +754,92 @@ int sqliteSortCompare(const char *a, const char *b){ return res; } +/* +** Some powers of 64. These numbers and their recipricals should +** all have exact representations in the floating point format. +*/ +#define _64e3 (64.0 * 64.0 * 64.0) +#define _64e4 (64.0 * 64.0 * 64.0 * 64.0) +#define _64e15 (_64e3 * _64e4 * _64e4 * _64e4) +#define _64e16 (_64e4 * _64e4 * _64e4 * _64e4) +#define _64e63 (_64e15 * _64e16 * _64e16 * _64e16) +#define _64e64 (_64e16 * _64e16 * _64e16 * _64e16) + +/* +** The following procedure converts a double-precision floating point +** number into a string. The resulting string has the property that +** two such strings comparied using strcmp() or memcmp() will give the +** same results as comparing the original floating point numbers. +** +** This routine is used to generate database keys from floating point +** numbers such that the keys sort in the same order as the original +** floating point numbers even though the keys are compared using +** memcmp(). +** +** The calling function should have allocated at least 14 characters +** of space for the buffer z[]. +*/ +void sqliteRealToSortable(double r, char *z){ + int neg; + int exp; + int cnt = 0; + + /* This array maps integers between 0 and 63 into base-64 digits. + ** The digits must be chosen such at their ASCII codes are increasing. + ** This means we can not use the traditional base-64 digit set. */ + static const char zDigit[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "|~"; + if( r<0.0 ){ + neg = 1; + r = -r; + *z++ = '-'; + } else { + neg = 0; + *z++ = '0'; + } + exp = 0; + + if( r==0.0 ){ + exp = -1024; + }else if( r<(0.5/64.0) ){ + while( r < 0.5/_64e64 && exp > -961 ){ r *= _64e64; exp -= 64; } + while( r < 0.5/_64e16 && exp > -1009 ){ r *= _64e16; exp -= 16; } + while( r < 0.5/_64e4 && exp > -1021 ){ r *= _64e4; exp -= 4; } + while( r < 0.5/64.0 && exp > -1024 ){ r *= 64.0; exp -= 1; } + }else if( r>=0.5 ){ + while( r >= 0.5*_64e63 && exp < 960 ){ r *= 1.0/_64e64; exp += 64; } + while( r >= 0.5*_64e15 && exp < 1008 ){ r *= 1.0/_64e16; exp += 16; } + while( r >= 0.5*_64e3 && exp < 1020 ){ r *= 1.0/_64e4; exp += 4; } + while( r >= 0.5 && exp < 1023 ){ r *= 1.0/64.0; exp += 1; } + } + if( neg ){ + exp = -exp; + r = -r; + } + exp += 1024; + r += 0.5; + if( exp<0 ) return; + if( exp>=2048 || r>=1.0 ){ + strcpy(z, "~~~~~~~~~~~~"); + return; + } + *z++ = zDigit[(exp>>6)&0x3f]; + *z++ = zDigit[exp & 0x3f]; + while( r>0.0 && cnt<10 ){ + int digit; + r *= 64.0; + digit = r; + assert( digit>=0 && digit<64 ); + *z++ = zDigit[digit & 0x3f]; + r -= digit; + cnt++; + } + *z = 0; +} + #ifdef SQLITE_UTF8 /* ** X is a pointer to the first byte of a UTF-8 character. Increment diff --git a/src/vdbe.c b/src/vdbe.c index b121e186e4..5aea72e670 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -30,7 +30,7 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.92 2001/11/04 18:32:48 drh Exp $ +** $Id: vdbe.c,v 1.93 2001/11/06 04:00:19 drh Exp $ */ #include "sqliteInt.h" #include @@ -87,6 +87,13 @@ struct Sorter { */ #define NSORT 30 +/* +** Number of bytes of string storage space available to each stack +** layer without having to malloc. NBFS is short for Number of Bytes +** For Strings. +*/ +#define NBFS 30 + /* ** A single level of the stack is an instance of the following ** structure. Except, string values are stored on a separate @@ -99,6 +106,7 @@ struct Stack { int n; /* Number of characters in string value, including '\0' */ int flags; /* Some combination of STK_Null, STK_Str, STK_Dyn, etc. */ double r; /* Real value */ + char z[NBFS]; /* Space for short strings */ }; typedef struct Stack Stack; @@ -120,6 +128,7 @@ typedef struct Mem Mem; #define STK_Int 0x0004 /* Value is an integer */ #define STK_Real 0x0008 /* Value is a real number */ #define STK_Dyn 0x0010 /* Need to call sqliteFree() on zStack[*] */ +#define STK_Static 0x0020 /* zStack[] points to a static string */ /* ** An Agg structure describes an Aggregator. Each Agg consists of @@ -201,6 +210,8 @@ struct Vdbe { Set *aSet; /* An array of sets */ int nFetch; /* Number of OP_Fetch instructions executed */ int nCallback; /* Number of callbacks invoked so far */ + int iLimit; /* Limit on the number of callbacks remaining */ + int iOffset; /* Offset before beginning to do callbacks */ }; /* @@ -534,22 +545,17 @@ static AggElem *_AggInFocus(Agg *p){ static int hardStringify(Vdbe *p, int i){ Stack *pStack = &p->aStack[i]; char **pzStack = &p->zStack[i]; - char zBuf[30]; int fg = pStack->flags; if( fg & STK_Real ){ - sprintf(zBuf,"%.15g",pStack->r); + sprintf(pStack->z,"%.15g",pStack->r); }else if( fg & STK_Int ){ - sprintf(zBuf,"%d",pStack->i); + sprintf(pStack->z,"%d",pStack->i); }else{ - p->zStack[i] = ""; - pStack->n = 1; - pStack->flags |= STK_Str; - return 0; + pStack->z[0] = 0; } - *pzStack = sqliteStrDup(zBuf); - if( *pzStack==0 ) return 1; + *pzStack = pStack->z; pStack->n = strlen(*pzStack)+1; - pStack->flags |= STK_Str|STK_Dyn; + pStack->flags = STK_Str; return 0; } @@ -560,7 +566,7 @@ static int hardStringify(Vdbe *p, int i){ static void hardRelease(Vdbe *p, int i){ sqliteFree(p->zStack[i]); p->zStack[i] = 0; - p->aStack[i].flags &= ~(STK_Str|STK_Dyn); + p->aStack[i].flags &= ~(STK_Str|STK_Dyn|STK_Static); } /* @@ -675,6 +681,27 @@ static int hardNeedStack(Vdbe *p, int N){ return 0; } +/* +** Return TRUE if zNum is a floating-point or integer number. +*/ +static int isNumber(const char *zNum){ + if( *zNum=='-' || *zNum=='+' ) zNum++; + if( !isdigit(*zNum) ) return 0; + while( isdigit(*zNum) ) zNum++; + if( *zNum==0 ) return 1; + if( *zNum!='.' ) return 0; + zNum++; + if( !isdigit(*zNum) ) return 0; + while( isdigit(*zNum) ) zNum++; + if( *zNum==0 ) return 1; + if( *zNum!='e' && *zNum!='E' ) return 0; + zNum++; + if( *zNum=='-' || *zNum=='+' ) zNum++; + if( !isdigit(*zNum) ) return 0; + while( isdigit(*zNum) ) zNum++; + return *zNum==0; +} + /* ** Delete a keylist */ @@ -822,22 +849,22 @@ static char *zOpName[] = { 0, "MemStore", "ListWrite", "ListRewind", "ListRead", "ListReset", "SortPut", "SortMakeRec", "SortMakeKey", "Sort", "SortNext", "SortCallback", "SortReset", - "FileOpen", "FileRead", "FileColumn", "FileClose", - "AggReset", "AggFocus", "AggIncr", "AggNext", - "AggSet", "AggGet", "SetInsert", "SetFound", - "SetNotFound", "SetClear", "MakeRecord", "MakeKey", - "MakeIdxKey", "Goto", "If", "Halt", - "ColumnCount", "ColumnName", "Callback", "NullCallback", - "Integer", "String", "Null", "Pop", - "Dup", "Pull", "Add", "AddImm", - "Subtract", "Multiply", "Divide", "Remainder", - "BitAnd", "BitOr", "BitNot", "ShiftLeft", - "ShiftRight", "AbsValue", "Precision", "Min", - "Max", "Like", "Glob", "Eq", - "Ne", "Lt", "Le", "Gt", - "Ge", "IsNull", "NotNull", "Negative", - "And", "Or", "Not", "Concat", - "Noop", "Strlen", "Substr", + "FileOpen", "FileRead", "FileColumn", "AggReset", + "AggFocus", "AggIncr", "AggNext", "AggSet", + "AggGet", "SetInsert", "SetFound", "SetNotFound", + "MakeRecord", "MakeKey", "MakeIdxKey", "Goto", + "If", "Halt", "ColumnCount", "ColumnName", + "Callback", "NullCallback", "Integer", "String", + "Pop", "Dup", "Pull", "Add", + "AddImm", "Subtract", "Multiply", "Divide", + "Remainder", "BitAnd", "BitOr", "BitNot", + "ShiftLeft", "ShiftRight", "AbsValue", "Precision", + "Min", "Max", "Like", "Glob", + "Eq", "Ne", "Lt", "Le", + "Gt", "Ge", "IsNull", "NotNull", + "Negative", "And", "Or", "Not", + "Concat", "Noop", "Strlen", "Substr", + "Limit", }; /* @@ -1119,25 +1146,11 @@ case OP_String: { }else{ zStack[i] = z; aStack[i].n = strlen(z) + 1; - aStack[i].flags = STK_Str; + aStack[i].flags = STK_Str | STK_Static; } break; } -#if 0 /* NOT USED */ -/* Opcode: Null * * * -** -** Push a NULL value onto the stack. -*/ -case OP_Null: { - int i = ++p->tos; - VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - zStack[i] = 0; - aStack[i].flags = STK_Null; - break; -} -#endif - /* Opcode: Pop P1 * * ** ** P1 elements are popped off of the top of stack and discarded. @@ -1162,13 +1175,21 @@ case OP_Dup: { int j = ++p->tos; VERIFY( if( i<0 ) goto not_enough_stack; ) VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; ) - aStack[j] = aStack[i]; - if( aStack[i].flags & STK_Dyn ){ - zStack[j] = sqliteMalloc( aStack[j].n ); - if( zStack[j]==0 ) goto no_mem; - memcpy(zStack[j], zStack[i], aStack[j].n); - }else{ - zStack[j] = zStack[i]; + memcpy(&aStack[j], &aStack[i], sizeof(aStack[i])-NBFS); + if( aStack[j].flags & STK_Str ){ + if( aStack[j].flags & STK_Static ){ + zStack[j] = zStack[i]; + aStack[j].flags = STK_Str | STK_Static; + }else if( aStack[i].n<=NBFS ){ + memcpy(aStack[j].z, zStack[i], aStack[j].n); + zStack[j] = aStack[j].z; + aStack[j].flags = STK_Str; + }else{ + zStack[j] = sqliteMalloc( aStack[j].n ); + if( zStack[j]==0 ) goto no_mem; + memcpy(zStack[j], zStack[i], aStack[j].n); + aStack[j].flags = STK_Str | STK_Dyn; + } } break; } @@ -1194,10 +1215,18 @@ case OP_Pull: { tz = zStack[from]; for(i=from; itos - pOp->p1 + 1; @@ -1248,10 +1284,20 @@ case OP_Callback: { } zStack[p->tos+1] = 0; if( xCallback!=0 ){ - if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ - rc = SQLITE_ABORT; + if( p->iOffset>0 ){ + p->iOffset--; + }else{ + if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){ + rc = SQLITE_ABORT; + } + p->nCallback++; + if( p->iLimit>0 ){ + p->iLimit--; + if( p->iLimit==0 ){ + pc = pOp->p2 - 1; + } + } } - p->nCallback++; } PopStack(p, pOp->p1); if( sqlite_malloc_failed ) goto no_mem; @@ -1462,8 +1508,9 @@ case OP_Precision: { int tos = p->tos; int nos = tos - 1; int nDigit; + int len; double v; - char zBuf[100]; + char *zNew; VERIFY( if( nos<0 ) goto not_enough_stack; ) Realify(p, tos); @@ -1472,12 +1519,20 @@ case OP_Precision: { if( nDigit<0 ) nDigit = 0; if( nDigit>30 ) nDigit = 30; v = aStack[tos].r; - sprintf(zBuf, "%.*f", nDigit, v); + zNew = sqlite_mprintf("%.*f", nDigit, v); + if( zNew==0 ) goto no_mem; POPSTACK; Release(p, nos); - zStack[nos] = sqliteStrDup(zBuf); - aStack[nos].n = strlen(zStack[nos]) + 1; - aStack[nos].flags = STK_Str | STK_Dyn; + aStack[nos].n = len = strlen(zNew) + 1; + if( len<=NBFS ){ + strcpy(aStack[nos].z, zNew); + zStack[nos] = aStack[nos].z; + aStack[nos].flags = STK_Str; + sqliteFree(zNew); + }else{ + zStack[nos] = zNew; + aStack[nos].flags = STK_Str | STK_Dyn; + } break; } @@ -1486,70 +1541,50 @@ case OP_Precision: { ** Pop the top two elements from the stack then push back the ** largest of the two. */ -case OP_Max: { - int tos = p->tos; - int nos = tos - 1; - int ft, fn; - int copy = 0; - VERIFY( if( nos<0 ) goto not_enough_stack; ) - ft = aStack[tos].flags; - fn = aStack[nos].flags; - if( fn & STK_Null ){ - copy = 1; - }else if( (ft & fn & STK_Int)==STK_Int ){ - copy = aStack[nos].iaStack[nos].r; - }else{ - if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - copy = sqliteCompare(zStack[tos],zStack[nos])>0; - } - if( copy ){ - Release(p, nos); - aStack[nos] = aStack[tos]; - zStack[nos] = zStack[tos]; - zStack[tos] = 0; - aStack[tos].flags = 0; - }else{ - Release(p, tos); - } - p->tos = nos; - break; -} - /* Opcode: Min * * * ** ** Pop the top two elements from the stack then push back the ** smaller of the two. */ -case OP_Min: { +case OP_Min: +case OP_Max: { int tos = p->tos; int nos = tos - 1; + int a,b; int ft, fn; int copy = 0; VERIFY( if( nos<0 ) goto not_enough_stack; ) ft = aStack[tos].flags; fn = aStack[nos].flags; + if( pOp->opcode==OP_Max ){ + a = tos; + b = nos; + }else{ + a = nos; + b = tos; + } if( fn & STK_Null ){ copy = 1; }else if( ft & STK_Null ){ copy = 0; }else if( (ft & fn & STK_Int)==STK_Int ){ - copy = aStack[nos].i>aStack[tos].i; + copy = aStack[a].i>aStack[b].i; }else if( ( (ft|fn) & (STK_Int|STK_Real) ) !=0 ){ Realify(p, tos); Realify(p, nos); - copy = aStack[tos].raStack[b].r; }else{ if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem; - copy = sqliteCompare(zStack[tos],zStack[nos])<0; + copy = sqliteCompare(zStack[a],zStack[b])>0; } if( copy ){ Release(p, nos); aStack[nos] = aStack[tos]; - zStack[nos] = zStack[tos]; + if( aStack[nos].flags & (STK_Dyn|STK_Static) ){ + zStack[nos] = zStack[tos]; + }else{ + zStack[nos] = aStack[nos].z; + } zStack[tos] = 0; aStack[tos].flags = 0; }else{ @@ -2016,47 +2051,6 @@ case OP_MakeRecord: { ** ** See also: MakeIdxKey, SortMakeKey */ -case OP_MakeKey: { - char *zNewKey; - int nByte; - int nField; - int i, j; - - nField = pOp->p1; - VERIFY( if( p->tos+1tos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - nByte++; - }else{ - if( Stringify(p, i) ) goto no_mem; - nByte += aStack[i].n; - } - } - if( nByte+sizeof(u32)>MAX_BYTES_PER_ROW ){ - rc = SQLITE_TOOBIG; - goto abort_due_to_error; - } - zNewKey = sqliteMalloc( nByte ); - if( zNewKey==0 ) goto no_mem; - j = 0; - for(i=p->tos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - zNewKey[j++] = 0; - }else{ - memcpy(&zNewKey[j], zStack[i], aStack[i].n); - j += aStack[i].n; - } - } - if( pOp->p2==0 ) PopStack(p, nField); - VERIFY( NeedStack(p, p->tos+1); ) - p->tos++; - aStack[p->tos].n = nByte; - aStack[p->tos].flags = STK_Str|STK_Dyn; - zStack[p->tos] = zNewKey; - break; -} - /* Opcode: MakeIdxKey P1 * * ** ** Convert the top P1 entries of the stack into a single entry suitable @@ -2075,43 +2069,85 @@ case OP_MakeKey: { ** ** See also: MakeKey, SortMakeKey */ -case OP_MakeIdxKey: { +case OP_MakeIdxKey: +case OP_MakeKey: { char *zNewKey; int nByte; int nField; + int addRowid; int i, j; - u32 iKey; + addRowid = pOp->opcode==OP_MakeIdxKey; nField = pOp->p1; - VERIFY( if( p->tos+1tos+1+addRowidtos-nField+1; i<=p->tos; i++){ - if( aStack[i].flags & STK_Null ){ - nByte++; + int flags = aStack[i].flags; + int len; + char *z; + if( flags & STK_Null ){ + nByte += 2; + }else if( flags & STK_Real ){ + z = aStack[i].z; + sqliteRealToSortable(aStack[i].r, &z[1]); + z[0] = 0; + Release(p, i); + len = strlen(&z[1]); + zStack[i] = 0; + aStack[i].flags = STK_Real; + aStack[i].n = len+2; + nByte += aStack[i].n; + }else if( flags & STK_Int ){ + z = aStack[i].z; + aStack[i].r = aStack[i].i; + sqliteRealToSortable(aStack[i].r, &z[1]); + z[0] = 0; + Release(p, i); + len = strlen(&z[1]); + zStack[i] = 0; + aStack[i].flags = STK_Int; + aStack[i].n = len+2; + nByte += aStack[i].n; }else{ - if( Stringify(p, i) ) goto no_mem; + assert( flags & STK_Str ); + if( isNumber(zStack[i]) ){ + aStack[i].r = atof(zStack[i]); + Release(p, i); + z = aStack[i].z; + sqliteRealToSortable(aStack[i].r, &z[1]); + z[0] = 0; + len = strlen(&z[1]); + zStack[i] = 0; + aStack[i].flags = STK_Real; + aStack[i].n = len+2; + } nByte += aStack[i].n; } } - if( nByte>MAX_BYTES_PER_ROW ){ + if( nByte+sizeof(u32)>MAX_BYTES_PER_ROW ){ rc = SQLITE_TOOBIG; goto abort_due_to_error; } + if( addRowid ) nByte += sizeof(u32); zNewKey = sqliteMalloc( nByte ); if( zNewKey==0 ) goto no_mem; j = 0; for(i=p->tos-nField+1; i<=p->tos; i++){ if( aStack[i].flags & STK_Null ){ zNewKey[j++] = 0; + zNewKey[j++] = 0; }else{ - memcpy(&zNewKey[j], zStack[i], aStack[i].n); + memcpy(&zNewKey[j], zStack[i] ? zStack[i] : aStack[i].z, aStack[i].n); j += aStack[i].n; } } - Integerify(p, p->tos-nField); - iKey = bigEndian(aStack[p->tos-nField].i); - memcpy(&zNewKey[j], &iKey, sizeof(u32)); - PopStack(p, nField+1); + if( addRowid ){ + u32 iKey; + Integerify(p, p->tos-nField); + iKey = bigEndian(aStack[p->tos-nField].i); + memcpy(&zNewKey[j], &iKey, sizeof(u32)); + } + if( pOp->p2==0 ) PopStack(p, nField+addRowid); VERIFY( NeedStack(p, p->tos+1); ) p->tos++; aStack[p->tos].n = nByte; @@ -2686,7 +2722,7 @@ case OP_KeyAsData: { ** data. */ case OP_Column: { - int amt, offset, end, nCol, payloadSize; + int amt, offset, end, payloadSize; int i = pOp->p1; int p2 = pOp->p2; int tos = p->tos+1; @@ -2750,6 +2786,11 @@ case OP_Column: { */ if( amt==0 ){ aStack[tos].flags = STK_Null; + }else if( amt<=NBFS ){ + (*xRead)(pCrsr, offset, amt, aStack[tos].z); + aStack[tos].flags = STK_Str; + zStack[tos] = aStack[tos].z; + aStack[tos].n = amt; }else{ char *z = sqliteMalloc( amt ); if( z==0 ) goto no_mem; @@ -2815,10 +2856,15 @@ case OP_FullKey: { rc = SQLITE_CORRUPT; goto abort_due_to_error; } - z = sqliteMalloc( amt ); + if( amt>NBFS ){ + z = sqliteMalloc( amt ); + aStack[tos].flags = STK_Str | STK_Dyn; + }else{ + z = aStack[tos].z; + aStack[tos].flags = STK_Str; + } sqliteBtreeKey(pCrsr, 0, amt, z); zStack[tos] = z; - aStack[tos].flags = STK_Str | STK_Dyn; aStack[tos].n = amt; } break; @@ -3105,6 +3151,25 @@ case OP_Reorganize: { break; } +/* Opcode: Limit P1 P2 * +** +** Set a limit and offset on callbacks. P1 is the limit and P2 is +** the offset. If the offset counter is positive, no callbacks are +** invoked but instead the counter is decremented. Once the offset +** counter reaches zero, callbacks are invoked and the limit +** counter is decremented. When the limit counter reaches zero, +** the OP_Callback or OP_SortCallback instruction executes a jump +** that should end the query. +** +** This opcode is used to implement the "LIMIT x OFFSET y" clause +** of a SELECT statement. +*/ +case OP_Limit: { + p->iLimit = pOp->p1; + p->iOffset = pOp->p2; + break; +} + /* Opcode: ListWrite * * * ** ** Write the integer on the top of the stack @@ -3183,7 +3248,8 @@ case OP_ListReset: { /* Opcode: SortPut * * * ** ** The TOS is the key and the NOS is the data. Pop both from the stack -** and put them on the sorter. +** and put them on the sorter. The key and data should have been +** made using SortMakeKey and SortMakeRec, respectively. */ case OP_SortPut: { int tos = p->tos; @@ -3195,6 +3261,8 @@ case OP_SortPut: { if( pSorter==0 ) goto no_mem; pSorter->pNext = p->pSort; p->pSort = pSorter; + assert( aStack[tos].flags & STK_Dyn ); + assert( aStack[nos].flags & STK_Dyn ); pSorter->nKey = aStack[tos].n; pSorter->zKey = zStack[tos]; pSorter->nData = aStack[nos].n; @@ -3359,19 +3427,37 @@ case OP_SortNext: { break; } -/* Opcode: SortCallback * P2 * +/* Opcode: SortCallback P1 P2 * ** ** The top of the stack contains a callback record built using ** the SortMakeRec operation with the same P1 value as this ** instruction. Pop this record from the stack and invoke the ** callback on it. +** +** If the offset counter (set by the OP_Limit opcode) is positive, +** then decrement the counter and do not invoke the callback. +** +** If the callback is invoked, then after the callback returns +** decrement the limit counter. When the limit counter reaches +** zero, jump to address P2. */ case OP_SortCallback: { int i = p->tos; VERIFY( if( i<0 ) goto not_enough_stack; ) if( xCallback!=0 ){ - if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName) ){ - rc = SQLITE_ABORT; + if( p->iOffset>0 ){ + p->iOffset--; + }else{ + if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){ + rc = SQLITE_ABORT; + } + p->nCallback++; + if( p->iLimit>0 ){ + p->iLimit--; + if( p->iLimit==0 ){ + pc = pOp->p2 - 1; + } + } } p->nCallback++; } @@ -3413,31 +3499,6 @@ case OP_FileOpen: { break; } -#if 0 /* NOT USED */ -/* Opcode: FileClose * * * -** -** Close a file previously opened using FileOpen. This is a no-op -** if there is no prior FileOpen call. -*/ -case OP_FileClose: { - if( p->pFile ){ - if( p->pFile!=stdin ) fclose(p->pFile); - p->pFile = 0; - } - if( p->azField ){ - sqliteFree(p->azField); - p->azField = 0; - } - p->nField = 0; - if( p->zLine ){ - sqliteFree(p->zLine); - p->zLine = 0; - } - p->nLineAlloc = 0; - break; -} -#endif - /* Opcode: FileRead P1 P2 P3 ** ** Read a single line of input from the open file (the file opened using @@ -3561,8 +3622,8 @@ case OP_FileColumn: { case OP_MemStore: { int i = pOp->p1; int tos = p->tos; - Mem *pMem; char *zOld; + Mem *pMem; VERIFY( if( tos<0 ) goto not_enough_stack; ) if( i>=p->nMem ){ int nOld = p->nMem; @@ -3582,11 +3643,14 @@ case OP_MemStore: { zOld = 0; } pMem->s = aStack[tos]; - if( pMem->s.flags & STK_Str ){ - pMem->z = sqliteStrNDup(zStack[tos], pMem->s.n); - pMem->s.flags |= STK_Dyn; + if( pMem->s.flags & (STK_Static|STK_Dyn) ){ + pMem->z = zStack[tos]; + }else{ + pMem->z = pMem->s.z; } if( zOld ) sqliteFree(zOld); + zStack[tos] = 0; + aStack[tos].flags = 0; POPSTACK; break; } @@ -3604,12 +3668,16 @@ case OP_MemLoad: { zStack[tos] = 0; }else{ aStack[tos] = p->aMem[i].s; - if( aStack[tos].flags & STK_Str ){ + if( aStack[tos].flags & STK_Dyn ){ char *z = sqliteMalloc(aStack[tos].n); if( z==0 ) goto no_mem; memcpy(z, p->aMem[i].z, aStack[tos].n); zStack[tos] = z; aStack[tos].flags |= STK_Dyn; + }else if( aStack[tos].flags & STK_Static ){ + zStack[tos] = p->aMem[i].z; + }else if( aStack[tos].flags & STK_Str ){ + zStack[tos] = aStack[tos].z; } } break; @@ -3712,12 +3780,14 @@ case OP_AggSet: { zOld = 0; } pMem->s = aStack[tos]; - if( pMem->s.flags & STK_Str ){ - pMem->z = sqliteMalloc( aStack[tos].n ); - if( pMem->z ){ - memcpy(pMem->z, zStack[tos], pMem->s.n); - } - pMem->s.flags |= STK_Str|STK_Dyn; + if( pMem->s.flags & STK_Dyn ){ + pMem->z = zStack[tos]; + zStack[tos] = 0; + aStack[tos].flags = 0; + }else if( pMem->s.flags & STK_Static ){ + pMem->z = zStack[tos]; + }else if( pMem->s.flags & STK_Str ){ + pMem->z = pMem->s.z; } if( zOld ) sqliteFree(zOld); } @@ -3772,20 +3842,6 @@ case OP_AggNext: { break; } -#if 0 /* NOT USED */ -/* Opcode: SetClear P1 * * -** -** Remove all elements from the P1-th Set. -*/ -case OP_SetClear: { - int i = pOp->p1; - if( i>=0 && inSet ){ - sqliteHashClear(&p->aSet[i].hash); - } - break; -} -#endif /* NOT USED */ - /* Opcode: SetInsert P1 * P3 ** ** If Set P1 does not exist then create it. Then insert value diff --git a/src/vdbe.h b/src/vdbe.h index 30d955b784..d835b2a113 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.32 2001/11/04 18:32:48 drh Exp $ +** $Id: vdbe.h,v 1.33 2001/11/06 04:00:19 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -127,74 +127,73 @@ typedef struct VdbeOp VdbeOp; #define OP_FileOpen 49 #define OP_FileRead 50 #define OP_FileColumn 51 -#define OP_FileClose 52 -#define OP_AggReset 53 -#define OP_AggFocus 54 -#define OP_AggIncr 55 -#define OP_AggNext 56 -#define OP_AggSet 57 -#define OP_AggGet 58 +#define OP_AggReset 52 +#define OP_AggFocus 53 +#define OP_AggIncr 54 +#define OP_AggNext 55 +#define OP_AggSet 56 +#define OP_AggGet 57 -#define OP_SetInsert 59 -#define OP_SetFound 60 -#define OP_SetNotFound 61 -#define OP_SetClear 62 +#define OP_SetInsert 58 +#define OP_SetFound 59 +#define OP_SetNotFound 60 -#define OP_MakeRecord 63 -#define OP_MakeKey 64 -#define OP_MakeIdxKey 65 +#define OP_MakeRecord 61 +#define OP_MakeKey 62 +#define OP_MakeIdxKey 63 -#define OP_Goto 66 -#define OP_If 67 -#define OP_Halt 68 +#define OP_Goto 64 +#define OP_If 65 +#define OP_Halt 66 -#define OP_ColumnCount 69 -#define OP_ColumnName 70 -#define OP_Callback 71 -#define OP_NullCallback 72 +#define OP_ColumnCount 67 +#define OP_ColumnName 68 +#define OP_Callback 69 +#define OP_NullCallback 70 -#define OP_Integer 73 -#define OP_String 74 -#define OP_Null 75 -#define OP_Pop 76 -#define OP_Dup 77 -#define OP_Pull 78 +#define OP_Integer 71 +#define OP_String 72 +#define OP_Pop 73 +#define OP_Dup 74 +#define OP_Pull 75 -#define OP_Add 79 -#define OP_AddImm 80 -#define OP_Subtract 81 -#define OP_Multiply 82 -#define OP_Divide 83 -#define OP_Remainder 84 -#define OP_BitAnd 85 -#define OP_BitOr 86 -#define OP_BitNot 87 -#define OP_ShiftLeft 88 -#define OP_ShiftRight 89 -#define OP_AbsValue 90 -#define OP_Precision 91 -#define OP_Min 92 -#define OP_Max 93 -#define OP_Like 94 -#define OP_Glob 95 -#define OP_Eq 96 -#define OP_Ne 97 -#define OP_Lt 98 -#define OP_Le 99 -#define OP_Gt 100 -#define OP_Ge 101 -#define OP_IsNull 102 -#define OP_NotNull 103 -#define OP_Negative 104 -#define OP_And 105 -#define OP_Or 106 -#define OP_Not 107 -#define OP_Concat 108 -#define OP_Noop 109 +#define OP_Add 76 +#define OP_AddImm 77 +#define OP_Subtract 78 +#define OP_Multiply 79 +#define OP_Divide 80 +#define OP_Remainder 81 +#define OP_BitAnd 82 +#define OP_BitOr 83 +#define OP_BitNot 84 +#define OP_ShiftLeft 85 +#define OP_ShiftRight 86 +#define OP_AbsValue 87 +#define OP_Precision 88 +#define OP_Min 89 +#define OP_Max 90 +#define OP_Like 91 +#define OP_Glob 92 +#define OP_Eq 93 +#define OP_Ne 94 +#define OP_Lt 95 +#define OP_Le 96 +#define OP_Gt 97 +#define OP_Ge 98 +#define OP_IsNull 99 +#define OP_NotNull 100 +#define OP_Negative 101 +#define OP_And 102 +#define OP_Or 103 +#define OP_Not 104 +#define OP_Concat 105 +#define OP_Noop 106 -#define OP_Strlen 110 -#define OP_Substr 111 +#define OP_Strlen 107 +#define OP_Substr 108 + +#define OP_Limit 109 #define OP_MAX 111