Implement indices that occur in sort order and the LIMIT...OFFSET clause

of SELECT statements. (CVS 301)

FossilOrigin-Name: eb07768ae93f14bf6c150c1c4329948857a9d01c
This commit is contained in:
drh 2001-11-06 04:00:18 +00:00
parent 80ff32f5be
commit 9bbca4c1c0
9 changed files with 469 additions and 295 deletions

View File

@ -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

View File

@ -1 +1 @@
7dd58fad398253608f55867cf1c7749eef005657
eb07768ae93f14bf6c150c1c4329948857a9d01c

View File

@ -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).

View File

@ -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.
*/

View File

@ -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*);

View File

@ -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 },

View File

@ -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 <stdarg.h>
@ -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

View File

@ -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 <ctype.h>
@ -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 ){
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);
}else{
zStack[j] = zStack[i];
aStack[j].flags = STK_Str | STK_Dyn;
}
}
break;
}
@ -1194,10 +1215,18 @@ case OP_Pull: {
tz = zStack[from];
for(i=from; i<to; i++){
aStack[i] = aStack[i+1];
if( aStack[i].flags & (STK_Dyn|STK_Static) ){
zStack[i] = zStack[i+1];
}else{
zStack[i] = aStack[i].z;
}
}
aStack[to] = ts;
if( aStack[to].flags & (STK_Dyn|STK_Static) ){
zStack[to] = tz;
}else{
zStack[to] = aStack[to].z;
}
break;
}
@ -1230,11 +1259,18 @@ case OP_ColumnName: {
break;
}
/* Opcode: Callback P1 * *
/* Opcode: Callback P1 P2 *
**
** Pop P1 values off the stack and form them into an array. Then
** invoke the callback function using the newly formed array as the
** 3rd parameter.
**
** 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_Callback: {
int i = p->tos - pOp->p1 + 1;
@ -1248,10 +1284,20 @@ case OP_Callback: {
}
zStack[p->tos+1] = 0;
if( xCallback!=0 ){
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;
}
}
}
}
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].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].i<aStack[tos].i;
}else if( ( (ft|fn) & (STK_Int|STK_Real) ) !=0 ){
Realify(p, tos);
Realify(p, nos);
copy = aStack[tos].r>aStack[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].r<aStack[nos].r;
copy = aStack[a].r>aStack[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];
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+1<nField ) goto not_enough_stack; )
nByte = 0;
for(i=p->tos-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+1<nField ) goto not_enough_stack; )
nByte = sizeof(u32);
VERIFY( if( p->tos+1+addRowid<nField ) goto not_enough_stack; )
nByte = 0;
for(i=p->tos-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;
}
}
if( addRowid ){
u32 iKey;
Integerify(p, p->tos-nField);
iKey = bigEndian(aStack[p->tos-nField].i);
memcpy(&zNewKey[j], &iKey, sizeof(u32));
PopStack(p, nField+1);
}
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;
}
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,21 +3427,39 @@ 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) ){
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++;
}
POPSTACK;
if( sqlite_malloc_failed ) goto no_mem;
@ -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 && i<p->nSet ){
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

View File

@ -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