From c9b84a1fb1e7ce504dc8f7a9de4c7e8cd8fecedc Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 20 Jun 2002 11:36:48 +0000 Subject: [PATCH] This patch contains the beginnings of the data-typing infrastructure. The new build-in TypeOf() function is added. New opcodes for doing pure text comparisons are added. Most changes are disabled pending the 2.6.0 release. (CVS 632) FossilOrigin-Name: cbbc858d973c2d515c6a2464981316549a241b73 --- manifest | 26 +++---- manifest.uuid | 2 +- src/build.c | 39 +++++++++- src/expr.c | 158 +++++++++++++++++++++++++++++++++---- src/func.c | 61 ++++++++------- src/main.c | 18 ++++- src/sqlite.h.in | 23 +++++- src/sqliteInt.h | 17 +++- src/vdbe.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++-- src/vdbe.h | 36 +++++---- 10 files changed, 497 insertions(+), 85 deletions(-) diff --git a/manifest b/manifest index 4de36bbeff..6855d669e1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -20,15 +20,15 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea 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 @@ -40,8 +40,8 @@ F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe 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 @@ -52,8 +52,8 @@ F src/tokenize.c ac4c46f190346b87da54ec3e2605d160af80c619 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 @@ -137,7 +137,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f 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 diff --git a/manifest.uuid b/manifest.uuid index fcd3b71bdf..67e7b0b972 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -43c5aff5d078bce9292683cd40311e0dcc81ac14 \ No newline at end of file +cbbc858d973c2d515c6a2464981316549a241b73 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 22f87dd242..7e7920278e 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** 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 @@ -504,6 +504,7 @@ void sqliteAddColumn(Parse *pParse, Token *pName){ 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; @@ -522,8 +523,11 @@ void sqliteAddColumn(Parse *pParse, Token *pName){ 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++; } /* @@ -554,10 +558,12 @@ void sqliteAddColumnType(Parse *pParse, Token *pFirst, Token *pLast){ 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; @@ -568,6 +574,31 @@ void sqliteAddColumnType(Parse *pParse, Token *pFirst, Token *pLast){ 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; + } + } + } } /* diff --git a/src/expr.c b/src/expr.c index e6a3cfac85..92ae657924 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** 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 @@ -460,8 +460,10 @@ int sqliteExprResolveIds( 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; } @@ -485,6 +487,7 @@ int sqliteExprResolveIds( 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]!='"' ){ @@ -546,6 +549,7 @@ int sqliteExprResolveIds( }else{ pExpr->iColumn = j; } + pExpr->dataType = pTab->aCol[j].sortOrder & SQLITE_SO_TYPEMASK; } } } @@ -568,10 +572,12 @@ int sqliteExprResolveIds( 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; } } } @@ -580,6 +586,7 @@ int sqliteExprResolveIds( if( cnt==0 && cntTab==1 && sqliteIsRowid(zRight) ){ cnt = 1; pExpr->iColumn = -1; + pExpr->dataType = SQLITE_SO_NUM; } sqliteFree(zLeft); sqliteFree(zRight); @@ -714,10 +721,11 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ 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; @@ -727,7 +735,12 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ 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; } @@ -758,6 +771,37 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ 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->dataTypedataType = + 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; ipList->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 ){ @@ -780,6 +824,78 @@ int sqliteExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){ 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. @@ -856,6 +972,17 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ 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: @@ -864,13 +991,7 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){ 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); @@ -1090,6 +1211,9 @@ void sqliteExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ 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; } @@ -1180,6 +1304,9 @@ void sqliteExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){ 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); @@ -1395,6 +1522,7 @@ FuncDef *sqliteFindFunction( 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; diff --git a/src/func.c b/src/func.c index 3a50a6ee35..518973f955 100644 --- a/src/func.c +++ b/src/func.c @@ -16,7 +16,7 @@ ** 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 #include @@ -416,42 +416,44 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){ 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; @@ -459,11 +461,16 @@ void sqliteRegisterBuiltinFunctions(sqlite *db){ for(i=0; ifile_format = atoi(argv[3]); break; } @@ -827,3 +832,14 @@ int sqlite_create_aggregate( 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; + } +} diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 29dbb87f44..5d7d52c1f4 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** 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_ @@ -426,6 +426,27 @@ int sqlite_create_aggregate( 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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index f20caa7f82..79a44f97ac 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** 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" @@ -176,7 +176,18 @@ typedef struct TriggerStep TriggerStep; 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 */ @@ -244,6 +255,7 @@ struct FuncDef { 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 */ }; @@ -876,6 +888,7 @@ int sqliteGlobCompare(const unsigned char*,const unsigned char*); 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*); diff --git a/src/vdbe.c b/src/vdbe.c index 8589fd39d5..d5ad988510 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.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 @@ -1077,10 +1077,11 @@ static char *zOpName[] = { 0, "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", }; /* @@ -1992,6 +1993,11 @@ mismatch: ** 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. @@ -2004,6 +2010,11 @@ mismatch: ** 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. @@ -2018,6 +2029,12 @@ mismatch: ** 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. @@ -2031,6 +2048,12 @@ mismatch: ** 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. @@ -2044,6 +2067,12 @@ mismatch: ** 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. @@ -2057,6 +2086,12 @@ mismatch: ** 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. @@ -2116,6 +2151,145 @@ case OP_Ge: { 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 NOSTOS. +** +** 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 @@ -2435,7 +2609,7 @@ case OP_MakeRecord: { 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 @@ -2449,9 +2623,14 @@ case OP_MakeRecord: { ** 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 @@ -2473,6 +2652,11 @@ case OP_MakeRecord: { ** 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: @@ -2488,13 +2672,15 @@ case OP_MakeKey: { nField = pOp->p1; VERIFY( if( p->tos+1+addRowidtos-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]); diff --git a/src/vdbe.h b/src/vdbe.h index 4e2327e9da..91b34f24dc 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.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_ @@ -187,27 +187,37 @@ typedef struct VdbeOp VdbeOp; #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_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_Limit 120 -#define OP_LimitCk 121 +#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 121 +#define OP_MAX 127 /* ** Prototypes for the VDBE interface. See comments on the implementation