mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
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
This commit is contained in:
39
src/build.c
39
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 <ctype.h>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
158
src/expr.c
158
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 <ctype.h>
|
||||
@ -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->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 ){
|
||||
@ -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;
|
||||
|
61
src/func.c
61
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 <ctype.h>
|
||||
#include <math.h>
|
||||
@ -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; 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);
|
||||
}
|
||||
}
|
||||
|
18
src/main.c
18
src/main.c
@ -14,7 +14,7 @@
|
||||
** 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"
|
||||
@ -51,6 +51,11 @@ int sqliteInitCallback(void *pDb, int argc, char **argv, char **azColName){
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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*);
|
||||
|
202
src/vdbe.c
202
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 <ctype.h>
|
||||
@ -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 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
|
||||
@ -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+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]);
|
||||
|
36
src/vdbe.h
36
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
|
||||
|
Reference in New Issue
Block a user