-C Fix\sfor\sticket\s#73:\sThe\sORDER\sBY\sclause\sis\ssignificant\sfor\ssubqueries.\nThis\spasses\sall\sregression\stests,\sbut\smore\stesting\sis\sneeded\sto\sexercise\nall\spaths\sthrough\sthe\snew\scode.\s(CVS\s631)
-D 2002-06-20T03:38:26
+C This\spatch\scontains\sthe\sbeginnings\sof\sthe\sdata-typing\sinfrastructure.\nThe\snew\sbuild-in\sTypeOf()\sfunction\sis\sadded.\s\sNew\sopcodes\sfor\sdoing\npure\stext\scomparisons\sare\sadded.\s\sMost\schanges\sare\sdisabled\spending\nthe\s2.6.0\srelease.\s(CVS\s632)
+D 2002-06-20T11:36:49
F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c
F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495
F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
F src/btree.c 8b86be8f234c1c5dab3186f69cee2544ec9d7257
F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
-F src/build.c f4633493f57660587c35c76dc7948f5da691a718
+F src/build.c 95eac6ce4ae2871388d49066c78dd0657ce40a1f
F src/delete.c 44c45460b1e03033756e35adc6d569ffbf30b725
F src/encode.c 346b12b46148506c32038524b95c4631ab46d760
-F src/expr.c ec0689af4e355356df47dc1514ff17523d2f9c71
-F src/func.c b8d0fd3011f53ea0e46b6bab857612eb36b5d1ea
+F src/expr.c cb50a72c491954d58be2f182366e45a1e252bf2e
+F src/func.c 5eae8227a8b0d276a64d51a3880a6e86f238fedf
F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 4bb40ed9dbaba4516fc2abbcff3f08d5687b073c
-F src/main.c 07f56387147f00e69eea7cea369071452bc4706f
+F src/main.c d026463c501a7eaf740494dfab0faae09980c224
F src/md5.c 0ae1f3e2cac92d06fc6246d1b4b8f61a2fe66d3b
F src/os.c 9cc40c5384baba4a85e160e67807645ca98ba3cc
F src/os.h 4a361fccfbc4e7609b3e1557f604f94c1e96ad10
F src/select.c 3eadcde4c74341d8ee7db69948cbcb16df9ae9fc
F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
-F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b
-F src/sqliteInt.h 1e9904f9baa536333d9d1168e075abf96426baad
+F src/sqlite.h.in 7c8882e352cb70818cfaf9bdb5b1b3bee81ef144
+F src/sqliteInt.h 16ccbf72dd823d5764b475353927410ec272305e
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1
F src/test1.c 5cc4f0bbf38237e04e1b2077e285b41bfb4c4cbf
F src/trigger.c 21342af6ac031fece39c8fc6eabd1739ca5327c1
F src/update.c 6f6a4dcd71cd9ff730b7f12c83de5498cde4924f
F src/util.c 876b259f9186e84b944b72e793dd3dad50e63e95
-F src/vdbe.c 7d9bb3701ea00576c5d5fb3f3de63af7b7304241
-F src/vdbe.h fba15f3280688f6f32f11d1042078e3c557bac43
+F src/vdbe.c c33572f803b853c50c22f84d6dfa6922ec79bf6b
+F src/vdbe.h 9b6e632bfa5d52507130f1ae456ef2c01bc0be7e
F src/where.c 1fdb7aca26c1963eb42615a95e0fc2978eec566a
F test/all.test e4d3821eeba751829b419cd47814bd20af4286d1
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P d599f75b659809a6e5eee09b0e9e6e90bde5af1e
-R 086d9eb833675513d9039b13c48320be
+P 43c5aff5d078bce9292683cd40311e0dcc81ac14
+R 581584e0b7a6ff65ed6ebc56fea3ccf6
U drh
-Z 8d182409003e8da6cfa46f473fde17e7
+Z 063bdfea24283b4aeee93e932e657f35
-43c5aff5d078bce9292683cd40311e0dcc81ac14
\ No newline at end of file
+cbbc858d973c2d515c6a2464981316549a241b73
\ No newline at end of file
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.96 2002/06/17 17:07:20 drh Exp $
+** $Id: build.c,v 1.97 2002/06/20 11:36:49 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
Table *p;
int i;
char *z = 0;
+ Column *pCol;
if( (p = pParse->pNewTable)==0 ) return;
sqliteSetNString(&z, pName->z, pName->n, 0);
if( z==0 ) return;
if( aNew==0 ) return;
p->aCol = aNew;
}
- memset(&p->aCol[p->nCol], 0, sizeof(p->aCol[0]));
- p->aCol[p->nCol++].zName = z;
+ pCol = &p->aCol[p->nCol];
+ memset(pCol, 0, sizeof(p->aCol[0]));
+ pCol->zName = z;
+ pCol->sortOrder = SQLITE_SO_NUM;
+ p->nCol++;
}
/*
int i, j;
int n;
char *z, **pz;
+ Column *pCol;
if( (p = pParse->pNewTable)==0 ) return;
i = p->nCol-1;
if( i<0 ) return;
- pz = &p->aCol[i].zType;
+ pCol = &p->aCol[i];
+ pz = &pCol->zType;
n = pLast->n + Addr(pLast->z) - Addr(pFirst->z);
sqliteSetNString(pz, pFirst->z, n, 0);
z = *pz;
z[j++] = c;
}
z[j] = 0;
+ pCol->sortOrder = SQLITE_SO_NUM;
+ for(i=0; z[i]; i++){
+ switch( z[i] ){
+ case 'c':
+ case 'C': {
+ if( sqliteStrNICmp(&z[i],"char",4)==0 ||
+ sqliteStrNICmp(&z[i],"clob",4)==0 ){
+ pCol->sortOrder = SQLITE_SO_TEXT;
+ return;
+ }
+ break;
+ }
+ case 'x':
+ case 'X': {
+ if( i>=2 && sqliteStrNICmp(&z[i-2],"text",4)==0 ){
+ pCol->sortOrder = SQLITE_SO_TEXT;
+ return;
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
}
/*
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
-** $Id: expr.c,v 1.72 2002/06/17 17:07:20 drh Exp $
+** $Id: expr.c,v 1.73 2002/06/20 11:36:49 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
if( j==pTab->iPKey ){
/* Substitute the record number for the INTEGER PRIMARY KEY */
pExpr->iColumn = -1;
+ pExpr->dataType = SQLITE_SO_NUM;
}else{
pExpr->iColumn = j;
+ pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
}
pExpr->op = TK_COLUMN;
}
pExpr->iTable = base;
cnt = 1 + (pTabList->nSrc>1);
pExpr->op = TK_COLUMN;
+ pExpr->dataType = SQLITE_SO_NUM;
}
sqliteFree(z);
if( cnt==0 && pExpr->token.z[0]!='"' ){
}else{
pExpr->iColumn = j;
}
+ pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
}
}
}
if( t ){
int j;
- for(j=0; j < pTriggerStack->pTab->nCol; j++) {
- if( sqliteStrICmp(pTriggerStack->pTab->aCol[j].zName, zRight)==0 ){
+ Table *pTab = pTriggerStack->pTab;
+ for(j=0; j < pTab->nCol; j++) {
+ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){
cnt++;
pExpr->iColumn = j;
+ pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK;
}
}
}
if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){
cnt = 1;
pExpr->iColumn = -1;
+ pExpr->dataType = SQLITE_SO_NUM;
}
sqliteFree(zLeft);
sqliteFree(zRight);
if( pExpr==0 ) return 0;
switch( pExpr->op ){
case TK_FUNCTION: {
- int n = pExpr->pList ? pExpr->pList->nExpr : 0;
- int no_such_func = 0;
- int wrong_num_args = 0;
- int is_agg = 0;
+ int n = pExpr->pList ? pExpr->pList->nExpr : 0; /* Number of arguments */
+ int no_such_func = 0; /* True if no such function exists */
+ int is_type_of = 0; /* True if is the special TypeOf() function */
+ int wrong_num_args = 0; /* True if wrong number of arguments */
+ int is_agg = 0; /* True if is an aggregate function */
int i;
FuncDef *pDef;
pDef = sqliteFindFunction(pParse->db,
pExpr->token.z, pExpr->token.n, -1, 0);
if( pDef==0 ){
- no_such_func = 1;
+ if( n==1 && pExpr->token.n==6
+ && sqliteStrNICmp(pExpr->token.z, "typeof", 6)==0 ){
+ is_type_of = 1;
+ }else {
+ no_such_func = 1;
+ }
}else{
wrong_num_args = 1;
}
nErr = sqliteExprCheck(pParse, pExpr->pList->a[i].pExpr,
allowAgg && !is_agg, pIsAgg);
}
+ if( pDef==0 ){
+ if( is_type_of ){
+ pExpr->op = TK_STRING;
+ if( sqliteExprType(pExpr->pList->a[0].pExpr)==SQLITE_SO_NUM ){
+ pExpr->token.z = "numeric";
+ pExpr->token.n = 7;
+ }else{
+ pExpr->token.z = "text";
+ pExpr->token.n = 4;
+ }
+ }
+ }else if( pDef->dataType>=0 ){
+ if( pDef->dataType<n ){
+ pExpr->dataType =
+ sqliteExprType(pExpr->pList->a[pDef->dataType].pExpr);
+ }else{
+ pExpr->dataType = SQLITE_SO_NUM;
+ }
+ }else if( pDef->dataType==SQLITE_ARGS ){
+ pDef->dataType = SQLITE_SO_TEXT;
+ for(i=0; i<n; i++){
+ if( sqliteExprType(pExpr->pList->a[i].pExpr)==SQLITE_SO_NUM ){
+ pExpr->dataType = SQLITE_SO_NUM;
+ break;
+ }
+ }
+ }else if( pDef->dataType==SQLITE_NUMERIC ){
+ pExpr->dataType = SQLITE_SO_NUM;
+ }else{
+ pExpr->dataType = SQLITE_SO_TEXT;
+ }
}
default: {
if( pExpr->pLeft ){
return nErr;
}
+/*
+** Return either SQLITE_SO_NUM or SQLITE_SO_TEXT to indicate whether the
+** given expression should sort as numeric values or as text.
+**
+** The sqliteExprResolveIds() and sqliteExprCheck() routines must have
+** both been called on the expression before it is passed to this routine.
+*/
+int sqliteExprType(Expr *p){
+ if( p==0 ) return SQLITE_SO_NUM;
+ while( p ) switch( p->op ){
+ case TK_PLUS:
+ case TK_MINUS:
+ case TK_STAR:
+ case TK_SLASH:
+ case TK_AND:
+ case TK_OR:
+ case TK_ISNULL:
+ case TK_NOTNULL:
+ case TK_NOT:
+ case TK_UMINUS:
+ case TK_BITAND:
+ case TK_BITOR:
+ case TK_BITNOT:
+ case TK_LSHIFT:
+ case TK_RSHIFT:
+ case TK_REM:
+ case TK_INTEGER:
+ case TK_FLOAT:
+ case TK_IN:
+ case TK_BETWEEN:
+ return SQLITE_SO_NUM;
+
+ case TK_STRING:
+ case TK_NULL:
+ case TK_CONCAT:
+ return SQLITE_SO_TEXT;
+
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ:
+ if( sqliteExprType(p->pLeft)==SQLITE_SO_NUM ){
+ return SQLITE_SO_NUM;
+ }
+ p = p->pRight;
+ break;
+
+ case TK_AS:
+ p = p->pLeft;
+ break;
+
+ case TK_COLUMN:
+ case TK_FUNCTION:
+ case TK_AGG_FUNCTION:
+ return p->dataType;
+
+ case TK_SELECT:
+ assert( p->pSelect );
+ assert( p->pSelect->pEList );
+ assert( p->pSelect->pEList->nExpr>0 );
+ p = p->pSelect->pEList->a[0].pExpr;
+ break;
+
+ default:
+ assert( p->op==TK_ABORT ); /* Can't Happen */
+ break;
+ }
+ return SQLITE_SO_NUM;
+}
+
/*
** Generate code into the current Vdbe to evaluate the given
** expression and leave the result on the top of stack.
sqliteVdbeAddOp(v, OP_String, 0, 0);
break;
}
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){
+ op += 6; /* Convert numeric opcodes to text opcodes */
+ }
+ /* Fall through into the next case */
+ }
case TK_AND:
case TK_OR:
case TK_PLUS:
case TK_REM:
case TK_BITAND:
case TK_BITOR:
- case TK_SLASH:
- case TK_LT:
- case TK_LE:
- case TK_GT:
- case TK_GE:
- case TK_NE:
- case TK_EQ: {
+ case TK_SLASH: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, 0, 0);
case TK_EQ: {
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
+ if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){
+ op += 6; /* Convert numeric opcodes to text opcodes */
+ }
sqliteVdbeAddOp(v, op, jumpIfNull, dest);
break;
}
case TK_GE:
case TK_NE:
case TK_EQ: {
+ if( pParse->db->file_format>=3 && sqliteExprType(pExpr)==SQLITE_SO_TEXT ){
+ op += 6; /* Convert numeric opcodes to text opcodes */
+ }
sqliteExprCode(pParse, pExpr->pLeft);
sqliteExprCode(pParse, pExpr->pRight);
sqliteVdbeAddOp(v, op, jumpIfNull, dest);
if( p==0 && createFlag && (p = sqliteMalloc(sizeof(*p)))!=0 ){
p->nArg = nArg;
p->pNext = pFirst;
+ p->dataType = pFirst ? pFirst->dataType : SQLITE_NUMERIC;
sqliteHashInsert(&db->aFunc, zName, nName, (void*)p);
}
return p;
** sqliteRegisterBuildinFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** $Id: func.c,v 1.20 2002/06/09 10:14:19 drh Exp $
+** $Id: func.c,v 1.21 2002/06/20 11:36:49 drh Exp $
*/
#include <ctype.h>
#include <math.h>
static struct {
char *zName;
int nArg;
+ int dataType;
void (*xFunc)(sqlite_func*,int,const char**);
} aFuncs[] = {
- { "min", -1, minFunc },
- { "min", 0, 0 },
- { "max", -1, maxFunc },
- { "max", 0, 0 },
- { "length", 1, lengthFunc },
- { "substr", 3, substrFunc },
- { "abs", 1, absFunc },
- { "round", 1, roundFunc },
- { "round", 2, roundFunc },
- { "upper", 1, upperFunc },
- { "lower", 1, lowerFunc },
- { "coalesce", -1, ifnullFunc },
- { "coalesce", 0, 0 },
- { "coalesce", 1, 0 },
- { "ifnull", 2, ifnullFunc },
- { "random", -1, randomFunc },
- { "like", 2, likeFunc },
- { "glob", 2, globFunc },
- { "nullif", 2, nullifFunc },
+ { "min", -1, SQLITE_ARGS, minFunc },
+ { "min", 0, 0, 0 },
+ { "max", -1, SQLITE_ARGS, maxFunc },
+ { "max", 0, 0, 0 },
+ { "length", 1, SQLITE_NUMERIC, lengthFunc },
+ { "substr", 3, SQLITE_TEXT, substrFunc },
+ { "abs", 1, SQLITE_NUMERIC, absFunc },
+ { "round", 1, SQLITE_NUMERIC, roundFunc },
+ { "round", 2, SQLITE_NUMERIC, roundFunc },
+ { "upper", 1, SQLITE_TEXT, upperFunc },
+ { "lower", 1, SQLITE_TEXT, lowerFunc },
+ { "coalesce", -1, SQLITE_ARGS, ifnullFunc },
+ { "coalesce", 0, 0, 0 },
+ { "coalesce", 1, 0, 0 },
+ { "ifnull", 2, SQLITE_ARGS, ifnullFunc },
+ { "random", -1, SQLITE_NUMERIC, randomFunc },
+ { "like", 2, SQLITE_NUMERIC, likeFunc },
+ { "glob", 2, SQLITE_NUMERIC, globFunc },
+ { "nullif", 2, SQLITE_ARGS, nullifFunc },
};
static struct {
char *zName;
int nArg;
+ int dataType;
void (*xStep)(sqlite_func*,int,const char**);
void (*xFinalize)(sqlite_func*);
} aAggs[] = {
- { "min", 1, minStep, minMaxFinalize },
- { "max", 1, maxStep, minMaxFinalize },
- { "sum", 1, sumStep, sumFinalize },
- { "avg", 1, sumStep, avgFinalize },
- { "count", 0, countStep, countFinalize },
- { "count", 1, countStep, countFinalize },
+ { "min", 1, 0, minStep, minMaxFinalize },
+ { "max", 1, 0, maxStep, minMaxFinalize },
+ { "sum", 1, SQLITE_NUMERIC, sumStep, sumFinalize },
+ { "avg", 1, SQLITE_NUMERIC, sumStep, avgFinalize },
+ { "count", 0, SQLITE_NUMERIC, countStep, countFinalize },
+ { "count", 1, SQLITE_NUMERIC, countStep, countFinalize },
#if 0
- { "stddev", 1, stdDevStep, stdDevFinalize },
+ { "stddev", 1, SQLITE_NUMERIC, stdDevStep, stdDevFinalize },
#endif
};
int i;
for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
sqlite_create_function(db, aFuncs[i].zName,
aFuncs[i].nArg, aFuncs[i].xFunc, 0);
+ if( aFuncs[i].xFunc ){
+ sqlite_function_type(db, aFuncs[i].zName, aFuncs[i].dataType);
+ }
}
sqlite_create_function(db, "last_insert_rowid", 0,
last_insert_rowid, db);
+ sqlite_function_type(db, "last_insert_rowid", SQLITE_NUMERIC);
for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
sqlite_create_aggregate(db, aAggs[i].zName,
aAggs[i].nArg, aAggs[i].xStep, aAggs[i].xFinalize, 0);
+ sqlite_function_type(db, aAggs[i].zName, aAggs[i].dataType);
}
}
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.80 2002/06/16 18:21:44 drh Exp $
+** $Id: main.c,v 1.81 2002/06/20 11:36:49 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
break;
}
case 'f': { /* File format */
+ /*
+ ** file_format==1 Version 2.1.0.
+ ** file_format==2 Version 2.2.0. Integer primary key.
+ ** file_format==3 Version 2.6.0. Separate text and numeric datatypes.
+ */
db->file_format = atoi(argv[3]);
break;
}
p->pUserData = pUserData;
return 0;
}
+
+/*
+** Change the datatype for all functions with a given name.
+*/
+int sqlite_function_type(sqlite *db, const char *zName, int dataType){
+ FuncDef *p = (FuncDef*)sqliteHashFind(&db->aFunc, zName, strlen(zName));
+ while( p ){
+ p->dataType = dataType;
+ p = p->pNext;
+ }
+}
** This header file defines the interface that the SQLite library
** presents to client programs.
**
-** @(#) $Id: sqlite.h.in,v 1.31 2002/05/10 05:44:56 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.32 2002/06/20 11:36:50 drh Exp $
*/
#ifndef _SQLITE_H_
#define _SQLITE_H_
void *pUserData /* Available via the sqlite_user_data() call */
);
+/*
+** Use the following routine to define the datatype returned by a
+** user-defined function. The second argument can be one of the
+** constants SQLITE_NUMERIC, SQLITE_TEXT, or SQLITE_ARGS or it
+** can be an integer greater than or equal to zero. The datatype
+** will be numeric or text (the only two types supported) if the
+** argument is SQLITE_NUMERIC or SQLITE_TEXT. If the argument is
+** SQLITE_ARGS, then the datatype is numeric if any argument to the
+** function is numeric and is text otherwise. If the second argument
+** is an integer, then the datatype of the result is the same as the
+** parameter to the function that corresponds to that integer.
+*/
+int sqlite_function_type(
+ sqlite *db, /* The database there the function is registered */
+ const char *zName, /* Name of the function */
+ int datatype /* The datatype for this function */
+);
+#define SQLITE_NUMERIC (-1)
+#define SQLITE_TEXT (-2)
+#define SQLITE_ARGS (-3)
+
/*
** The user function implementations call one of the following four routines
** in order to return their results. The first parameter to each of these
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.126 2002/06/19 14:27:05 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.127 2002/06/20 11:36:50 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
typedef struct TriggerStack TriggerStack;
/*
-** Each database is an instance of the following structure
+** Each database is an instance of the following structure.
+**
+** The sqlite.file_format is initialized by the database file
+** and helps determines how the data in the database file is
+** represented. This field allows newer versions of the library
+** to read and write older databases. The various file formats
+** are as follows:
+**
+** file_format==1 Version 2.1.0.
+** file_format==2 Version 2.2.0. Add support for INTEGER PRIMARY KEY.
+** file_format==3 Version 2.6.0. Add support for separate numeric and
+** text datatypes.
*/
struct sqlite {
Btree *pBe; /* The B*Tree backend */
void (*xStep)(sqlite_func*,int,const char**); /* Aggregate function step */
void (*xFinalize)(sqlite_func*); /* Aggregate function finializer */
int nArg; /* Number of arguments */
+ int dataType; /* Datatype of the result */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
};
int sqliteLikeCompare(const unsigned char*,const unsigned char*);
char *sqliteTableNameFromToken(Token*);
int sqliteExprCheck(Parse*, Expr*, int, int*);
+int sqliteExprType(Expr*);
int sqliteExprCompare(Expr*, Expr*);
int sqliteFuncId(Token*);
int sqliteExprResolveIds(Parse*, int, SrcList*, ExprList*, Expr*);
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.156 2002/06/14 22:38:43 drh Exp $
+** $Id: vdbe.c,v 1.157 2002/06/20 11:36:50 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
"Divide", "Remainder", "BitAnd", "BitOr",
"BitNot", "ShiftLeft", "ShiftRight", "AbsValue",
"Eq", "Ne", "Lt", "Le",
- "Gt", "Ge", "IsNull", "NotNull",
- "Negative", "And", "Or", "Not",
- "Concat", "Noop", "Function", "Limit",
- "LimitCk",
+ "Gt", "Ge", "StrEq", "StrNe",
+ "StrLt", "StrLe", "StrGt", "StrGe",
+ "IsNull", "NotNull", "Negative", "And",
+ "Or", "Not", "Concat", "Noop",
+ "Function", "Limit", "LimitCk",
};
/*
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared for equality that way. Otherwise the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrEq.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared in that format. Otherwise the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrNe.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared in that format. Numeric values are always less than
+** non-numeric values. If both operands are non-numeric, the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrLt.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared in that format. Numeric values are always less than
+** non-numeric values. If both operands are non-numeric, the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrLe.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared in that format. Numeric values are always less than
+** non-numeric values. If both operands are non-numeric, the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrGt.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
** If either operand is NULL (and thus if the result is unknown) then
** take the jump if P1 is true.
**
+** If both values are numeric, they are converted to doubles using atof()
+** and compared in that format. Numeric values are always less than
+** non-numeric values. If both operands are non-numeric, the strcmp() library
+** routine is used for the comparison. For a pure text comparison
+** use OP_StrGe.
+**
** If P2 is zero, do not jump. Instead, push an integer 1 onto the
** stack if the jump would have been taken, or a 0 if not. Push a
** NULL if either operand was NULL.
break;
}
+/* Opcode: StrEq P1 P2 *
+**
+** Pop the top two elements from the stack. If they are equal, then
+** jump to instruction P2. Otherwise, continue to the next instruction.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Eq.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+/* Opcode: StrNe P1 P2 *
+**
+** Pop the top two elements from the stack. If they are not equal, then
+** jump to instruction P2. Otherwise, continue to the next instruction.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Ne.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+/* Opcode: StrLt P1 P2 *
+**
+** Pop the top two elements from the stack. If second element (the
+** next on stack) is less than the first (the top of stack), then
+** jump to instruction P2. Otherwise, continue to the next instruction.
+** In other words, jump if NOS<TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Lt.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+/* Opcode: StrLe P1 P2 *
+**
+** Pop the top two elements from the stack. If second element (the
+** next on stack) is less than or equal to the first (the top of stack),
+** then jump to instruction P2. In other words, jump if NOS<=TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Le.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+/* Opcode: StrGt P1 P2 *
+**
+** Pop the top two elements from the stack. If second element (the
+** next on stack) is greater than the first (the top of stack),
+** then jump to instruction P2. In other words, jump if NOS>TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Gt.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+/* Opcode: StrGe P1 P2 *
+**
+** Pop the top two elements from the stack. If second element (the next
+** on stack) is greater than or equal to the first (the top of stack),
+** then jump to instruction P2. In other words, jump if NOS>=TOS.
+**
+** If either operand is NULL (and thus if the result is unknown) then
+** take the jump if P1 is true.
+**
+** The strcmp() library routine is used for the comparison. For a
+** numeric comparison, use OP_Ge.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+*/
+case OP_StrEq:
+case OP_StrNe:
+case OP_StrLt:
+case OP_StrLe:
+case OP_StrGt:
+case OP_StrGe: {
+ int tos = p->tos;
+ int nos = tos - 1;
+ int c;
+ VERIFY( if( nos<0 ) goto not_enough_stack; )
+ if( (aStack[nos].flags | aStack[tos].flags) & STK_Null ){
+ POPSTACK;
+ POPSTACK;
+ if( pOp->p2 ){
+ if( pOp->p1 ) pc = pOp->p2-1;
+ }else{
+ p->tos++;
+ aStack[nos].flags = STK_Null;
+ }
+ break;
+ }else{
+ if( Stringify(p, tos) || Stringify(p, nos) ) goto no_mem;
+ c = strcmp(zStack[nos], zStack[tos]);
+ }
+ switch( pOp->opcode ){
+ case OP_Eq: c = c==0; break;
+ case OP_Ne: c = c!=0; break;
+ case OP_Lt: c = c<0; break;
+ case OP_Le: c = c<=0; break;
+ case OP_Gt: c = c>0; break;
+ default: c = c>=0; break;
+ }
+ POPSTACK;
+ POPSTACK;
+ if( pOp->p2 ){
+ if( c ) pc = pOp->p2-1;
+ }else{
+ p->tos++;
+ aStack[nos].flags = STK_Int;
+ aStack[nos].i = c;
+ }
+ break;
+}
+
/* Opcode: And * * *
**
** Pop two values off the stack. Take the logical AND of the
break;
}
-/* Opcode: MakeKey P1 P2 *
+/* Opcode: MakeKey P1 P2 P3
**
** Convert the top P1 entries of the stack into a single entry suitable
** for use as the key in an index. The top P1 records are
** data is popped off the stack first then the new key is pushed
** back in its place.
**
+** P3 is a string that is P1 characters long. Each character is either
+** an 'n' or a 't' to indicates if the argument should be numeric or
+** text. The first character corresponds to the lowest element on the
+** stack.
+**
** See also: MakeIdxKey, SortMakeKey
*/
-/* Opcode: MakeIdxKey P1 P2 *
+/* Opcode: MakeIdxKey P1 P2 P3
**
** Convert the top P1 entries of the stack into a single entry suitable
** for use as the key in an index. In addition, take one additional integer
** guaranteed to be unique. This jump can be used to skip a subsequent
** uniqueness test.
**
+** P3 is a string that is P1 characters long. Each character is either
+** an 'n' or a 't' to indicates if the argument should be numeric or
+** text. The first character corresponds to the lowest element on the
+** stack.
+**
** See also: MakeKey, SortMakeKey
*/
case OP_MakeIdxKey:
nField = pOp->p1;
VERIFY( if( p->tos+1+addRowid<nField ) goto not_enough_stack; )
nByte = 0;
- for(i=p->tos-nField+1; i<=p->tos; i++){
+ for(j=0, i=p->tos-nField+1; i<=p->tos; i++, j++){
int flags = aStack[i].flags;
int len;
char *z;
if( flags & STK_Null ){
nByte += 2;
containsNull = 1;
+ }else if( pOp->p3 && pOp->p3[j]=='t' ){
+ Stringify(p, i);
}else if( flags & STK_Real ){
z = aStack[i].z;
sqliteRealToSortable(aStack[i].r, &z[1]);
** 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.55 2002/06/14 22:38:43 drh Exp $
+** $Id: vdbe.h,v 1.56 2002/06/20 11:36:50 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
#define OP_ShiftLeft 102
#define OP_ShiftRight 103
#define OP_AbsValue 104
+
+/* Note: The code generator assumes that OP_XX+6==OP_StrXX */
#define OP_Eq 105
#define OP_Ne 106
#define OP_Lt 107
#define OP_Le 108
#define OP_Gt 109
#define OP_Ge 110
-#define OP_IsNull 111
-#define OP_NotNull 112
-#define OP_Negative 113
-#define OP_And 114
-#define OP_Or 115
-#define OP_Not 116
-#define OP_Concat 117
-#define OP_Noop 118
-#define OP_Function 119
-
-#define OP_Limit 120
-#define OP_LimitCk 121
-
-
-#define OP_MAX 121
+#define OP_StrEq 111
+#define OP_StrNe 112
+#define OP_StrLt 113
+#define OP_StrLe 114
+#define OP_StrGt 115
+#define OP_StrGe 116
+/* Note: the code generator assumes that OP_XX+6==OP_StrXX */
+
+#define OP_IsNull 117
+#define OP_NotNull 118
+#define OP_Negative 119
+#define OP_And 120
+#define OP_Or 121
+#define OP_Not 122
+#define OP_Concat 123
+#define OP_Noop 124
+#define OP_Function 125
+
+#define OP_Limit 126
+#define OP_LimitCk 127
+
+
+#define OP_MAX 127
/*
** Prototypes for the VDBE interface. See comments on the implementation