mirror of
https://github.com/php/php-src.git
synced 2024-11-24 18:34:21 +08:00
Upgraded bundled libsqlite to 2.8.11 (fixed critical bug of *NIX systems).
This commit is contained in:
parent
49b698c67e
commit
6e350b553b
2
NEWS
2
NEWS
@ -1,7 +1,7 @@
|
||||
PHP NEWS
|
||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
?? ??? 2004, PHP 5 RC1
|
||||
- Upgraded SQLite library to version 2.8.9. (Ilia, Wez)
|
||||
- Upgraded SQLite library to version 2.8.11. (Ilia, Wez)
|
||||
- Fixed var_export() to show public, protected and private modifiers properly.
|
||||
(Derick)
|
||||
- Fixed problems with longlong values in mysqli. (Georg)
|
||||
|
@ -1 +1 @@
|
||||
2.8.9
|
||||
2.8.11
|
||||
|
@ -618,10 +618,12 @@ int sqliteRbtreeOpen(
|
||||
){
|
||||
Rbtree **ppRbtree = (Rbtree**)ppBtree;
|
||||
*ppRbtree = (Rbtree *)sqliteMalloc(sizeof(Rbtree));
|
||||
if( sqlite_malloc_failed ) goto open_no_mem;
|
||||
sqliteHashInit(&(*ppRbtree)->tblHash, SQLITE_HASH_INT, 0);
|
||||
|
||||
/* Create a binary tree for the SQLITE_MASTER table at location 2 */
|
||||
btreeCreateTable(*ppRbtree, 2);
|
||||
if( sqlite_malloc_failed ) goto open_no_mem;
|
||||
(*ppRbtree)->next_idx = 3;
|
||||
(*ppRbtree)->pOps = &sqliteRbtreeOps;
|
||||
/* Set file type to 4; this is so that "attach ':memory:' as ...." does not
|
||||
@ -630,6 +632,10 @@ int sqliteRbtreeOpen(
|
||||
(*ppRbtree)->aMetaData[2] = 4;
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
open_no_mem:
|
||||
*ppBtree = 0;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -642,11 +648,13 @@ static int memRbtreeCreateTable(Rbtree* tree, int* n)
|
||||
|
||||
*n = tree->next_idx++;
|
||||
btreeCreateTable(tree, *n);
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
|
||||
/* Set up the rollback structure (if we are not doing this as part of a
|
||||
* rollback) */
|
||||
if( tree->eTransState != TRANS_ROLLBACK ){
|
||||
BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
|
||||
if( pRollbackOp==0 ) return SQLITE_NOMEM;
|
||||
pRollbackOp->eOp = ROLLBACK_DROP;
|
||||
pRollbackOp->iTab = *n;
|
||||
btreeLogRollbackOp(tree, pRollbackOp);
|
||||
@ -671,6 +679,7 @@ static int memRbtreeDropTable(Rbtree* tree, int n)
|
||||
|
||||
if( tree->eTransState != TRANS_ROLLBACK ){
|
||||
BtRollbackOp *pRollbackOp = sqliteMalloc(sizeof(BtRollbackOp));
|
||||
if( pRollbackOp==0 ) return SQLITE_NOMEM;
|
||||
pRollbackOp->eOp = ROLLBACK_CREATE;
|
||||
pRollbackOp->iTab = n;
|
||||
btreeLogRollbackOp(tree, pRollbackOp);
|
||||
@ -712,6 +721,7 @@ static int memRbtreeCursor(
|
||||
RbtCursor *pCur;
|
||||
assert(tree);
|
||||
pCur = *ppCur = sqliteMalloc(sizeof(RbtCursor));
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
pCur->pTree = sqliteHashFind(&tree->tblHash, 0, iTable);
|
||||
pCur->pRbtree = tree;
|
||||
pCur->iTree = iTable;
|
||||
@ -755,6 +765,7 @@ static int memRbtreeInsert(
|
||||
/* Take a copy of the input data now, in case we need it for the
|
||||
* replace case */
|
||||
pData = sqliteMallocRaw(nData);
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
memcpy(pData, pDataInput, nData);
|
||||
|
||||
/* Move the cursor to a node near the key to be inserted. If the key already
|
||||
@ -771,8 +782,10 @@ static int memRbtreeInsert(
|
||||
memRbtreeMoveto( pCur, pKey, nKey, &match);
|
||||
if( match ){
|
||||
BtRbNode *pNode = sqliteMalloc(sizeof(BtRbNode));
|
||||
if( pNode==0 ) return SQLITE_NOMEM;
|
||||
pNode->nKey = nKey;
|
||||
pNode->pKey = sqliteMallocRaw(nKey);
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
memcpy(pNode->pKey, pKey, nKey);
|
||||
pNode->nData = nData;
|
||||
pNode->pData = pData;
|
||||
@ -804,10 +817,12 @@ static int memRbtreeInsert(
|
||||
/* Set up a rollback-op in case we have to roll this operation back */
|
||||
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
|
||||
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
|
||||
if( pOp==0 ) return SQLITE_NOMEM;
|
||||
pOp->eOp = ROLLBACK_DELETE;
|
||||
pOp->iTab = pCur->iTree;
|
||||
pOp->nKey = pNode->nKey;
|
||||
pOp->pKey = sqliteMallocRaw( pOp->nKey );
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
memcpy( pOp->pKey, pNode->pKey, pOp->nKey );
|
||||
btreeLogRollbackOp(pCur->pRbtree, pOp);
|
||||
}
|
||||
@ -819,9 +834,11 @@ static int memRbtreeInsert(
|
||||
/* Set up a rollback-op in case we have to roll this operation back */
|
||||
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
|
||||
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
|
||||
if( pOp==0 ) return SQLITE_NOMEM;
|
||||
pOp->iTab = pCur->iTree;
|
||||
pOp->nKey = pCur->pNode->nKey;
|
||||
pOp->pKey = sqliteMallocRaw( pOp->nKey );
|
||||
if( sqlite_malloc_failed ) return SQLITE_NOMEM;
|
||||
memcpy( pOp->pKey, pCur->pNode->pKey, pOp->nKey );
|
||||
pOp->nData = pCur->pNode->nData;
|
||||
pOp->pData = pCur->pNode->pData;
|
||||
@ -924,6 +941,7 @@ static int memRbtreeDelete(RbtCursor* pCur)
|
||||
* deletion */
|
||||
if( pCur->pRbtree->eTransState != TRANS_ROLLBACK ){
|
||||
BtRollbackOp *pOp = sqliteMalloc( sizeof(BtRollbackOp) );
|
||||
if( pOp==0 ) return SQLITE_NOMEM;
|
||||
pOp->iTab = pCur->iTree;
|
||||
pOp->nKey = pZ->nKey;
|
||||
pOp->pKey = pZ->pKey;
|
||||
@ -1029,6 +1047,7 @@ static int memRbtreeClearTable(Rbtree* tree, int n)
|
||||
sqliteFree( pNode->pData );
|
||||
}else{
|
||||
BtRollbackOp *pRollbackOp = sqliteMallocRaw(sizeof(BtRollbackOp));
|
||||
if( pRollbackOp==0 ) return SQLITE_NOMEM;
|
||||
pRollbackOp->eOp = ROLLBACK_INSERT;
|
||||
pRollbackOp->iTab = n;
|
||||
pRollbackOp->nKey = pNode->nKey;
|
||||
|
@ -221,7 +221,7 @@ static void computeJD(DateTime *p){
|
||||
M = p->M;
|
||||
D = p->D;
|
||||
}else{
|
||||
Y = 2000;
|
||||
Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */
|
||||
M = 1;
|
||||
D = 1;
|
||||
}
|
||||
@ -368,6 +368,23 @@ static void computeHMS(DateTime *p){
|
||||
p->validHMS = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute both YMD and HMS
|
||||
*/
|
||||
static void computeYMD_HMS(DateTime *p){
|
||||
computeYMD(p);
|
||||
computeHMS(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the YMD and HMS and the TZ
|
||||
*/
|
||||
static void clearYMD_HMS_TZ(DateTime *p){
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the difference (in days) between localtime and UTC (a.k.a. GMT)
|
||||
** for the time value p where p is in UTC.
|
||||
@ -376,9 +393,8 @@ static double localtimeOffset(DateTime *p){
|
||||
DateTime x, y;
|
||||
time_t t;
|
||||
struct tm *pTm;
|
||||
computeYMD(p);
|
||||
computeHMS(p);
|
||||
x = *p;
|
||||
computeYMD_HMS(&x);
|
||||
if( x.Y<1971 || x.Y>=2038 ){
|
||||
x.Y = 2000;
|
||||
x.M = 1;
|
||||
@ -408,9 +424,6 @@ static double localtimeOffset(DateTime *p){
|
||||
y.validJD = 0;
|
||||
y.validTZ = 0;
|
||||
computeJD(&y);
|
||||
/* printf("x=%d-%02d-%02d %02d:%02d:%02d\n",x.Y,x.M,x.D,x.h,x.m,(int)x.s); */
|
||||
/* printf("y=%d-%02d-%02d %02d:%02d:%02d\n",y.Y,y.M,y.D,y.h,y.m,(int)y.s); */
|
||||
/* printf("diff=%.17g\n", y.rJD - x.rJD); */
|
||||
return y.rJD - x.rJD;
|
||||
}
|
||||
|
||||
@ -454,9 +467,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
if( strcmp(z, "localtime")==0 ){
|
||||
computeJD(p);
|
||||
p->rJD += localtimeOffset(p);
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
@ -470,22 +481,15 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
*/
|
||||
if( strcmp(z, "unixepoch")==0 && p->validJD ){
|
||||
p->rJD = p->rJD/86400.0 + 2440587.5;
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}else if( strcmp(z, "utc")==0 ){
|
||||
double c1;
|
||||
computeJD(p);
|
||||
c1 = localtimeOffset(p);
|
||||
p->rJD -= c1;
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
p->rJD += c1 - localtimeOffset(p);
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
@ -494,16 +498,14 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
/*
|
||||
** weekday N
|
||||
**
|
||||
** Move the date to the beginning of the next occurrance of
|
||||
** Move the date to the same time on the next occurrance of
|
||||
** weekday N where 0==Sunday, 1==Monday, and so forth. If the
|
||||
** date is already on the appropriate weekday, this is equivalent
|
||||
** to "start of day".
|
||||
** date is already on the appropriate weekday, this is a no-op.
|
||||
*/
|
||||
if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
|
||||
&& (n=r)==r && n>=0 && r<7 ){
|
||||
int Z;
|
||||
computeYMD(p);
|
||||
p->validHMS = 0;
|
||||
computeYMD_HMS(p);
|
||||
p->validTZ = 0;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
@ -511,8 +513,7 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
Z %= 7;
|
||||
if( Z>n ) Z -= 7;
|
||||
p->rJD += n - Z;
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
@ -570,17 +571,14 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
if( n==3 && strcmp(z,"day")==0 ){
|
||||
p->rJD += r;
|
||||
}else if( n==4 && strcmp(z,"hour")==0 ){
|
||||
computeJD(p);
|
||||
p->rJD += r/24.0;
|
||||
}else if( n==6 && strcmp(z,"minute")==0 ){
|
||||
computeJD(p);
|
||||
p->rJD += r/(24.0*60.0);
|
||||
}else if( n==6 && strcmp(z,"second")==0 ){
|
||||
computeJD(p);
|
||||
p->rJD += r/(24.0*60.0*60.0);
|
||||
}else if( n==5 && strcmp(z,"month")==0 ){
|
||||
int x, y;
|
||||
computeYMD(p);
|
||||
computeYMD_HMS(p);
|
||||
p->M += r;
|
||||
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
|
||||
p->Y += x;
|
||||
@ -592,16 +590,14 @@ static int parseModifier(const char *zMod, DateTime *p){
|
||||
p->rJD += (r - y)*30.0;
|
||||
}
|
||||
}else if( n==4 && strcmp(z,"year")==0 ){
|
||||
computeYMD(p);
|
||||
computeYMD_HMS(p);
|
||||
p->Y += r;
|
||||
p->validJD = 0;
|
||||
computeJD(p);
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
p->validYMD = 0;
|
||||
p->validHMS = 0;
|
||||
p->validTZ = 0;
|
||||
clearYMD_HMS_TZ(p);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -655,8 +651,7 @@ static void datetimeFunc(sqlite_func *context, int argc, const char **argv){
|
||||
DateTime x;
|
||||
if( isDate(argc, argv, &x)==0 ){
|
||||
char zBuf[100];
|
||||
computeYMD(&x);
|
||||
computeHMS(&x);
|
||||
computeYMD_HMS(&x);
|
||||
sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d",x.Y, x.M, x.D, x.h, x.m,
|
||||
(int)(x.s));
|
||||
sqlite_set_result_string(context, zBuf, -1);
|
||||
@ -759,8 +754,7 @@ static void strftimeFunc(sqlite_func *context, int argc, const char **argv){
|
||||
if( z==0 ) return;
|
||||
}
|
||||
computeJD(&x);
|
||||
computeYMD(&x);
|
||||
computeHMS(&x);
|
||||
computeYMD_HMS(&x);
|
||||
for(i=j=0; zFmt[i]; i++){
|
||||
if( zFmt[i]!='%' ){
|
||||
z[j++] = zFmt[i];
|
||||
@ -798,11 +792,11 @@ static void strftimeFunc(sqlite_func *context, int argc, const char **argv){
|
||||
case 'm': sprintf(&z[j],"%02d",x.M); j+=2; break;
|
||||
case 'M': sprintf(&z[j],"%02d",x.m); j+=2; break;
|
||||
case 's': {
|
||||
sprintf(&z[j],"%d",(int)((x.rJD-2440587.5)*86400.0));
|
||||
sprintf(&z[j],"%d",(int)((x.rJD-2440587.5)*86400.0 + 0.5));
|
||||
j += strlen(&z[j]);
|
||||
break;
|
||||
}
|
||||
case 'S': sprintf(&z[j],"%02d",(int)x.s); j+=2; break;
|
||||
case 'S': sprintf(&z[j],"%02d",(int)(x.s+0.5)); j+=2; break;
|
||||
case 'w': z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break;
|
||||
case 'Y': sprintf(&z[j],"%04d",x.Y); j+=strlen(&z[j]); break;
|
||||
case '%': z[j++] = '%'; break;
|
||||
|
@ -75,6 +75,7 @@ static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
return n2 - n1;
|
||||
}
|
||||
|
||||
#if 0 /* NOT USED */
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
|
||||
*/
|
||||
@ -87,6 +88,7 @@ static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
if( pKey1<pKey2 ) return -1;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Hash and comparison functions when the mode is SQLITE_HASH_STRING
|
||||
@ -130,7 +132,7 @@ static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
|
||||
static int (*hashFunction(int keyClass))(const void*,int){
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intHash;
|
||||
case SQLITE_HASH_POINTER: return &ptrHash;
|
||||
/* case SQLITE_HASH_POINTER: return &ptrHash; // NOT USED */
|
||||
case SQLITE_HASH_STRING: return &strHash;
|
||||
case SQLITE_HASH_BINARY: return &binHash;;
|
||||
default: break;
|
||||
@ -147,7 +149,7 @@ static int (*hashFunction(int keyClass))(const void*,int){
|
||||
static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
|
||||
switch( keyClass ){
|
||||
case SQLITE_HASH_INT: return &intCompare;
|
||||
case SQLITE_HASH_POINTER: return &ptrCompare;
|
||||
/* case SQLITE_HASH_POINTER: return &ptrCompare; // NOT USED */
|
||||
case SQLITE_HASH_STRING: return &strCompare;
|
||||
case SQLITE_HASH_BINARY: return &binCompare;
|
||||
default: break;
|
||||
|
@ -71,7 +71,7 @@ struct HashElem {
|
||||
** if the copyKey parameter to HashInit is 1.
|
||||
*/
|
||||
#define SQLITE_HASH_INT 1
|
||||
#define SQLITE_HASH_POINTER 2
|
||||
/* #define SQLITE_HASH_POINTER 2 // NOT USED */
|
||||
#define SQLITE_HASH_STRING 3
|
||||
#define SQLITE_HASH_BINARY 4
|
||||
|
||||
|
@ -79,6 +79,7 @@ char *sqliteOpcodeNames[] = { "???",
|
||||
"PutStrKey",
|
||||
"Delete",
|
||||
"KeyAsData",
|
||||
"RowKey",
|
||||
"RowData",
|
||||
"Column",
|
||||
"Recno",
|
||||
@ -94,6 +95,7 @@ char *sqliteOpcodeNames[] = { "???",
|
||||
"IdxLT",
|
||||
"IdxGT",
|
||||
"IdxGE",
|
||||
"IdxIsNull",
|
||||
"Destroy",
|
||||
"Clear",
|
||||
"CreateIndex",
|
||||
|
@ -78,55 +78,57 @@
|
||||
#define OP_PutStrKey 77
|
||||
#define OP_Delete 78
|
||||
#define OP_KeyAsData 79
|
||||
#define OP_RowData 80
|
||||
#define OP_Column 81
|
||||
#define OP_Recno 82
|
||||
#define OP_FullKey 83
|
||||
#define OP_NullRow 84
|
||||
#define OP_Last 85
|
||||
#define OP_Rewind 86
|
||||
#define OP_Prev 87
|
||||
#define OP_Next 88
|
||||
#define OP_IdxPut 89
|
||||
#define OP_IdxDelete 90
|
||||
#define OP_IdxRecno 91
|
||||
#define OP_IdxLT 92
|
||||
#define OP_IdxGT 93
|
||||
#define OP_IdxGE 94
|
||||
#define OP_Destroy 95
|
||||
#define OP_Clear 96
|
||||
#define OP_CreateIndex 97
|
||||
#define OP_CreateTable 98
|
||||
#define OP_IntegrityCk 99
|
||||
#define OP_ListWrite 100
|
||||
#define OP_ListRewind 101
|
||||
#define OP_ListRead 102
|
||||
#define OP_ListReset 103
|
||||
#define OP_ListPush 104
|
||||
#define OP_ListPop 105
|
||||
#define OP_SortPut 106
|
||||
#define OP_SortMakeRec 107
|
||||
#define OP_SortMakeKey 108
|
||||
#define OP_Sort 109
|
||||
#define OP_SortNext 110
|
||||
#define OP_SortCallback 111
|
||||
#define OP_SortReset 112
|
||||
#define OP_FileOpen 113
|
||||
#define OP_FileRead 114
|
||||
#define OP_FileColumn 115
|
||||
#define OP_MemStore 116
|
||||
#define OP_MemLoad 117
|
||||
#define OP_MemIncr 118
|
||||
#define OP_AggReset 119
|
||||
#define OP_AggInit 120
|
||||
#define OP_AggFunc 121
|
||||
#define OP_AggFocus 122
|
||||
#define OP_AggSet 123
|
||||
#define OP_AggGet 124
|
||||
#define OP_AggNext 125
|
||||
#define OP_SetInsert 126
|
||||
#define OP_SetFound 127
|
||||
#define OP_SetNotFound 128
|
||||
#define OP_SetFirst 129
|
||||
#define OP_SetNext 130
|
||||
#define OP_Vacuum 131
|
||||
#define OP_RowKey 80
|
||||
#define OP_RowData 81
|
||||
#define OP_Column 82
|
||||
#define OP_Recno 83
|
||||
#define OP_FullKey 84
|
||||
#define OP_NullRow 85
|
||||
#define OP_Last 86
|
||||
#define OP_Rewind 87
|
||||
#define OP_Prev 88
|
||||
#define OP_Next 89
|
||||
#define OP_IdxPut 90
|
||||
#define OP_IdxDelete 91
|
||||
#define OP_IdxRecno 92
|
||||
#define OP_IdxLT 93
|
||||
#define OP_IdxGT 94
|
||||
#define OP_IdxGE 95
|
||||
#define OP_IdxIsNull 96
|
||||
#define OP_Destroy 97
|
||||
#define OP_Clear 98
|
||||
#define OP_CreateIndex 99
|
||||
#define OP_CreateTable 100
|
||||
#define OP_IntegrityCk 101
|
||||
#define OP_ListWrite 102
|
||||
#define OP_ListRewind 103
|
||||
#define OP_ListRead 104
|
||||
#define OP_ListReset 105
|
||||
#define OP_ListPush 106
|
||||
#define OP_ListPop 107
|
||||
#define OP_SortPut 108
|
||||
#define OP_SortMakeRec 109
|
||||
#define OP_SortMakeKey 110
|
||||
#define OP_Sort 111
|
||||
#define OP_SortNext 112
|
||||
#define OP_SortCallback 113
|
||||
#define OP_SortReset 114
|
||||
#define OP_FileOpen 115
|
||||
#define OP_FileRead 116
|
||||
#define OP_FileColumn 117
|
||||
#define OP_MemStore 118
|
||||
#define OP_MemLoad 119
|
||||
#define OP_MemIncr 120
|
||||
#define OP_AggReset 121
|
||||
#define OP_AggInit 122
|
||||
#define OP_AggFunc 123
|
||||
#define OP_AggFocus 124
|
||||
#define OP_AggSet 125
|
||||
#define OP_AggGet 126
|
||||
#define OP_AggNext 127
|
||||
#define OP_SetInsert 128
|
||||
#define OP_SetFound 129
|
||||
#define OP_SetNotFound 130
|
||||
#define OP_SetFirst 131
|
||||
#define OP_SetNext 132
|
||||
#define OP_Vacuum 133
|
||||
|
@ -164,6 +164,39 @@ static unsigned int elapse;
|
||||
** structure. The fcntl() system call is only invoked to set a
|
||||
** POSIX lock if the internal lock structure transitions between
|
||||
** a locked and an unlocked state.
|
||||
**
|
||||
** 2004-Jan-11:
|
||||
** More recent discoveries about POSIX advisory locks. (The more
|
||||
** I discover, the more I realize the a POSIX advisory locks are
|
||||
** an abomination.)
|
||||
**
|
||||
** If you close a file descriptor that points to a file that has locks,
|
||||
** all locks on that file that are owned by the current process are
|
||||
** released. To work around this problem, each OsFile structure contains
|
||||
** a pointer to an openCnt structure. There is one openCnt structure
|
||||
** per open inode, which means that multiple OsFiles can point to a single
|
||||
** openCnt. When an attempt is made to close an OsFile, if there are
|
||||
** other OsFiles open on the same inode that are holding locks, the call
|
||||
** to close() the file descriptor is deferred until all of the locks clear.
|
||||
** The openCnt structure keeps a list of file descriptors that need to
|
||||
** be closed and that list is walked (and cleared) when the last lock
|
||||
** clears.
|
||||
**
|
||||
** First, under Linux threads, because each thread has a separate
|
||||
** process ID, lock operations in one thread do not override locks
|
||||
** to the same file in other threads. Linux threads behave like
|
||||
** separate processes in this respect. But, if you close a file
|
||||
** descriptor in linux threads, all locks are cleared, even locks
|
||||
** on other threads and even though the other threads have different
|
||||
** process IDs. Linux threads is inconsistent in this respect.
|
||||
** (I'm beginning to think that linux threads is an abomination too.)
|
||||
** The consequence of this all is that the hash table for the lockInfo
|
||||
** structure has to include the process id as part of its key because
|
||||
** locks in different threads are treated as distinct. But the
|
||||
** openCnt structure should not include the process id in its
|
||||
** key because close() clears lock on all threads, not just the current
|
||||
** thread. Were it not for this goofiness in linux threads, we could
|
||||
** combine the lockInfo and openCnt structures into a single structure.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -180,69 +213,147 @@ struct lockKey {
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure is allocated for each inode.
|
||||
** An instance of the following structure is allocated for each open
|
||||
** inode on each thread with a different process ID. (Threads have
|
||||
** different process IDs on linux, but not on most other unixes.)
|
||||
**
|
||||
** A single inode can have multiple file descriptors, so each OsFile
|
||||
** structure contains a pointer to an instance of this object and this
|
||||
** object keeps a count of the number of OsFiles pointing to it.
|
||||
*/
|
||||
struct lockInfo {
|
||||
struct lockKey key; /* The lookup key */
|
||||
int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */
|
||||
int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */
|
||||
int nRef; /* Number of pointers to this structure */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure serves as the key used
|
||||
** to locate a particular openCnt structure given its inode. This
|
||||
** is the same as the lockKey except that the process ID is omitted.
|
||||
*/
|
||||
struct openKey {
|
||||
dev_t dev; /* Device number */
|
||||
ino_t ino; /* Inode number */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following structure is allocated for each open
|
||||
** inode. This structure keeps track of the number of locks on that
|
||||
** inode. If a close is attempted against an inode that is holding
|
||||
** locks, the close is deferred until all locks clear by adding the
|
||||
** file descriptor to be closed to the pending list.
|
||||
*/
|
||||
struct openCnt {
|
||||
struct openKey key; /* The lookup key */
|
||||
int nRef; /* Number of pointers to this structure */
|
||||
int nLock; /* Number of outstanding locks */
|
||||
int nPending; /* Number of pending close() operations */
|
||||
int *aPending; /* Malloced space holding fd's awaiting a close() */
|
||||
};
|
||||
|
||||
/*
|
||||
** This hash table maps inodes (in the form of lockKey structures) into
|
||||
** pointers to lockInfo structures.
|
||||
** These hash table maps inodes and process IDs into lockInfo and openCnt
|
||||
** structures. Access to these hash tables must be protected by a mutex.
|
||||
*/
|
||||
static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
|
||||
|
||||
/*
|
||||
** Given a file descriptor, locate a lockInfo structure that describes
|
||||
** that file descriptor. Create a new one if necessary. NULL might
|
||||
** be returned if malloc() fails.
|
||||
*/
|
||||
static struct lockInfo *findLockInfo(int fd){
|
||||
int rc;
|
||||
struct lockKey key;
|
||||
struct stat statbuf;
|
||||
struct lockInfo *pInfo;
|
||||
rc = fstat(fd, &statbuf);
|
||||
if( rc!=0 ) return 0;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.dev = statbuf.st_dev;
|
||||
key.ino = statbuf.st_ino;
|
||||
key.pid = getpid();
|
||||
pInfo = (struct lockInfo*)sqliteHashFind(&lockHash, &key, sizeof(key));
|
||||
if( pInfo==0 ){
|
||||
struct lockInfo *pOld;
|
||||
pInfo = sqliteMalloc( sizeof(*pInfo) );
|
||||
if( pInfo==0 ) return 0;
|
||||
pInfo->key = key;
|
||||
pInfo->nRef = 1;
|
||||
pInfo->cnt = 0;
|
||||
pOld = sqliteHashInsert(&lockHash, &pInfo->key, sizeof(key), pInfo);
|
||||
if( pOld!=0 ){
|
||||
assert( pOld==pInfo );
|
||||
sqliteFree(pInfo);
|
||||
pInfo = 0;
|
||||
}
|
||||
}else{
|
||||
pInfo->nRef++;
|
||||
}
|
||||
return pInfo;
|
||||
}
|
||||
static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
|
||||
|
||||
/*
|
||||
** Release a lockInfo structure previously allocated by findLockInfo().
|
||||
*/
|
||||
static void releaseLockInfo(struct lockInfo *pInfo){
|
||||
pInfo->nRef--;
|
||||
if( pInfo->nRef==0 ){
|
||||
sqliteHashInsert(&lockHash, &pInfo->key, sizeof(pInfo->key), 0);
|
||||
sqliteFree(pInfo);
|
||||
static void releaseLockInfo(struct lockInfo *pLock){
|
||||
pLock->nRef--;
|
||||
if( pLock->nRef==0 ){
|
||||
sqliteHashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0);
|
||||
sqliteFree(pLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Release a openCnt structure previously allocated by findLockInfo().
|
||||
*/
|
||||
static void releaseOpenCnt(struct openCnt *pOpen){
|
||||
pOpen->nRef--;
|
||||
if( pOpen->nRef==0 ){
|
||||
sqliteHashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0);
|
||||
sqliteFree(pOpen->aPending);
|
||||
sqliteFree(pOpen);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a file descriptor, locate lockInfo and openCnt structures that
|
||||
** describes that file descriptor. Create a new ones if necessary. The
|
||||
** return values might be unset if an error occurs.
|
||||
**
|
||||
** Return the number of errors.
|
||||
*/
|
||||
int findLockInfo(
|
||||
int fd, /* The file descriptor used in the key */
|
||||
struct lockInfo **ppLock, /* Return the lockInfo structure here */
|
||||
struct openCnt **ppOpen /* Return the openCnt structure here */
|
||||
){
|
||||
int rc;
|
||||
struct lockKey key1;
|
||||
struct openKey key2;
|
||||
struct stat statbuf;
|
||||
struct lockInfo *pLock;
|
||||
struct openCnt *pOpen;
|
||||
rc = fstat(fd, &statbuf);
|
||||
if( rc!=0 ) return 1;
|
||||
memset(&key1, 0, sizeof(key1));
|
||||
key1.dev = statbuf.st_dev;
|
||||
key1.ino = statbuf.st_ino;
|
||||
key1.pid = getpid();
|
||||
memset(&key2, 0, sizeof(key2));
|
||||
key2.dev = statbuf.st_dev;
|
||||
key2.ino = statbuf.st_ino;
|
||||
pLock = (struct lockInfo*)sqliteHashFind(&lockHash, &key1, sizeof(key1));
|
||||
if( pLock==0 ){
|
||||
struct lockInfo *pOld;
|
||||
pLock = sqliteMallocRaw( sizeof(*pLock) );
|
||||
if( pLock==0 ) return 1;
|
||||
pLock->key = key1;
|
||||
pLock->nRef = 1;
|
||||
pLock->cnt = 0;
|
||||
pOld = sqliteHashInsert(&lockHash, &pLock->key, sizeof(key1), pLock);
|
||||
if( pOld!=0 ){
|
||||
assert( pOld==pLock );
|
||||
sqliteFree(pLock);
|
||||
return 1;
|
||||
}
|
||||
}else{
|
||||
pLock->nRef++;
|
||||
}
|
||||
*ppLock = pLock;
|
||||
pOpen = (struct openCnt*)sqliteHashFind(&openHash, &key2, sizeof(key2));
|
||||
if( pOpen==0 ){
|
||||
struct openCnt *pOld;
|
||||
pOpen = sqliteMallocRaw( sizeof(*pOpen) );
|
||||
if( pOpen==0 ){
|
||||
releaseLockInfo(pLock);
|
||||
return 1;
|
||||
}
|
||||
pOpen->key = key2;
|
||||
pOpen->nRef = 1;
|
||||
pOpen->nLock = 0;
|
||||
pOpen->nPending = 0;
|
||||
pOpen->aPending = 0;
|
||||
pOld = sqliteHashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen);
|
||||
if( pOld!=0 ){
|
||||
assert( pOld==pOpen );
|
||||
sqliteFree(pOpen);
|
||||
releaseLockInfo(pLock);
|
||||
return 1;
|
||||
}
|
||||
}else{
|
||||
pOpen->nRef++;
|
||||
}
|
||||
*ppOpen = pOpen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /** POSIX advisory lock work-around **/
|
||||
|
||||
/*
|
||||
@ -349,6 +460,7 @@ int sqliteOsOpenReadWrite(
|
||||
int *pReadonly
|
||||
){
|
||||
#if OS_UNIX
|
||||
int rc;
|
||||
id->dirfd = -1;
|
||||
id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
|
||||
if( id->fd<0 ){
|
||||
@ -361,9 +473,9 @@ int sqliteOsOpenReadWrite(
|
||||
*pReadonly = 0;
|
||||
}
|
||||
sqliteOsEnterMutex();
|
||||
id->pLock = findLockInfo(id->fd);
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
sqliteOsLeaveMutex();
|
||||
if( id->pLock==0 ){
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@ -471,6 +583,7 @@ int sqliteOsOpenReadWrite(
|
||||
*/
|
||||
int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
#if OS_UNIX
|
||||
int rc;
|
||||
if( access(zFilename, 0)==0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
@ -481,9 +594,9 @@ int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
sqliteOsEnterMutex();
|
||||
id->pLock = findLockInfo(id->fd);
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
sqliteOsLeaveMutex();
|
||||
if( id->pLock==0 ){
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
unlink(zFilename);
|
||||
return SQLITE_NOMEM;
|
||||
@ -561,15 +674,16 @@ int sqliteOsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
*/
|
||||
int sqliteOsOpenReadOnly(const char *zFilename, OsFile *id){
|
||||
#if OS_UNIX
|
||||
int rc;
|
||||
id->dirfd = -1;
|
||||
id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->fd<0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
sqliteOsEnterMutex();
|
||||
id->pLock = findLockInfo(id->fd);
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
sqliteOsLeaveMutex();
|
||||
if( id->pLock==0 ){
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@ -763,15 +877,36 @@ int sqliteOsTempFileName(char *zBuf){
|
||||
}
|
||||
|
||||
/*
|
||||
** Close a file
|
||||
** Close a file.
|
||||
*/
|
||||
int sqliteOsClose(OsFile *id){
|
||||
#if OS_UNIX
|
||||
close(id->fd);
|
||||
sqliteOsUnlock(id);
|
||||
if( id->dirfd>=0 ) close(id->dirfd);
|
||||
id->dirfd = -1;
|
||||
sqliteOsEnterMutex();
|
||||
if( id->pOpen->nLock ){
|
||||
/* If there are outstanding locks, do not actually close the file just
|
||||
** yet because that would clear those locks. Instead, add the file
|
||||
** descriptor to pOpen->aPending. It will be automatically closed when
|
||||
** the last lock is cleared.
|
||||
*/
|
||||
int *aNew;
|
||||
struct openCnt *pOpen = id->pOpen;
|
||||
pOpen->nPending++;
|
||||
aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) );
|
||||
if( aNew==0 ){
|
||||
/* If a malloc fails, just leak the file descriptor */
|
||||
}else{
|
||||
pOpen->aPending = aNew;
|
||||
pOpen->aPending[pOpen->nPending-1] = id->fd;
|
||||
}
|
||||
}else{
|
||||
/* There are no outstanding locks so we can close the file immediately */
|
||||
close(id->fd);
|
||||
}
|
||||
releaseLockInfo(id->pLock);
|
||||
releaseOpenCnt(id->pOpen);
|
||||
sqliteOsLeaveMutex();
|
||||
TRACE2("CLOSE %-3d\n", id->fd);
|
||||
OpenCounter(-1);
|
||||
@ -1159,6 +1294,7 @@ int sqliteOsReadLock(OsFile *id){
|
||||
if( !id->locked ){
|
||||
id->pLock->cnt++;
|
||||
id->locked = 1;
|
||||
id->pOpen->nLock++;
|
||||
}
|
||||
rc = SQLITE_OK;
|
||||
}else if( id->locked || id->pLock->cnt==0 ){
|
||||
@ -1172,8 +1308,11 @@ int sqliteOsReadLock(OsFile *id){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
if( !id->locked ){
|
||||
id->pOpen->nLock++;
|
||||
id->locked = 1;
|
||||
}
|
||||
id->pLock->cnt = 1;
|
||||
id->locked = 1;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_BUSY;
|
||||
@ -1276,8 +1415,11 @@ int sqliteOsWriteLock(OsFile *id){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
if( !id->locked ){
|
||||
id->pOpen->nLock++;
|
||||
id->locked = 1;
|
||||
}
|
||||
id->pLock->cnt = -1;
|
||||
id->locked = 1;
|
||||
}
|
||||
}else{
|
||||
rc = SQLITE_BUSY;
|
||||
@ -1391,6 +1533,24 @@ int sqliteOsUnlock(OsFile *id){
|
||||
id->pLock->cnt = 0;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* Decrement the count of locks against this same file. When the
|
||||
** count reaches zero, close any other file descriptors whose close
|
||||
** was deferred because of outstanding locks.
|
||||
*/
|
||||
struct openCnt *pOpen = id->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
int i;
|
||||
for(i=0; i<pOpen->nPending; i++){
|
||||
close(pOpen->aPending[i]);
|
||||
}
|
||||
sqliteFree(pOpen->aPending);
|
||||
pOpen->nPending = 0;
|
||||
pOpen->aPending = 0;
|
||||
}
|
||||
}
|
||||
sqliteOsLeaveMutex();
|
||||
id->locked = 0;
|
||||
return rc;
|
||||
|
@ -103,10 +103,11 @@
|
||||
# include <unistd.h>
|
||||
typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
struct lockInfo *pLock; /* Information about locks on this inode */
|
||||
int fd; /* The file descriptor */
|
||||
int locked; /* True if this user holds the lock */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
struct openCnt *pOpen; /* Info about all open fd's on this inode */
|
||||
struct lockInfo *pLock; /* Info about locks on this inode */
|
||||
int fd; /* The file descriptor */
|
||||
int locked; /* True if this instance holds the lock */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
};
|
||||
# define SQLITE_TEMPNAME_SIZE 200
|
||||
# if defined(HAVE_USLEEP) && HAVE_USLEEP
|
||||
|
@ -594,7 +594,7 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
goto end_playback;
|
||||
}
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
rc = read32bits(format, &pPager->jfd, &nRec);
|
||||
rc = read32bits(format, &pPager->jfd, (u32*)&nRec);
|
||||
if( rc ) goto end_playback;
|
||||
rc = read32bits(format, &pPager->jfd, &pPager->cksumInit);
|
||||
if( rc ) goto end_playback;
|
||||
|
@ -139,9 +139,9 @@ static et_info fmtinfo[] = {
|
||||
** 16 (the number of significant digits in a 64-bit float) '0' is
|
||||
** always returned.
|
||||
*/
|
||||
static int et_getdigit(double *val, int *cnt){
|
||||
static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
|
||||
int digit;
|
||||
double d;
|
||||
LONGDOUBLE_TYPE d;
|
||||
if( (*cnt)++ >= 16 ) return '0';
|
||||
digit = (int)*val;
|
||||
d = digit;
|
||||
@ -202,7 +202,7 @@ static int vxprintf(
|
||||
int flag_long; /* True if "l" flag is present */
|
||||
int flag_center; /* True if "=" flag is present */
|
||||
unsigned long longvalue; /* Value for integer types */
|
||||
double realvalue; /* Value for real types */
|
||||
LONGDOUBLE_TYPE realvalue; /* Value for real types */
|
||||
et_info *infop; /* Pointer to the appropriate info structure */
|
||||
char buf[etBUFSIZE]; /* Conversion buffer */
|
||||
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
|
||||
|
@ -479,16 +479,19 @@ static int selectInnerLoop(
|
||||
** item into the set table with bogus data.
|
||||
*/
|
||||
case SRT_Set: {
|
||||
int lbl = sqliteVdbeMakeLabel(v);
|
||||
int addr1 = sqliteVdbeCurrentAddr(v);
|
||||
int addr2;
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -1, lbl);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, addr1+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
addr2 = sqliteVdbeAddOp(v, OP_Goto, 0, 0);
|
||||
if( pOrderBy ){
|
||||
pushOntoSorter(pParse, v, pOrderBy);
|
||||
}else{
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
|
||||
}
|
||||
sqliteVdbeResolveLabel(v, lbl);
|
||||
sqliteVdbeChangeP2(v, addr2, sqliteVdbeCurrentAddr(v));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -588,7 +591,9 @@ static void generateSortTail(
|
||||
}
|
||||
case SRT_Set: {
|
||||
assert( nColumn==1 );
|
||||
sqliteVdbeAddOp(v, OP_IsNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -1, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, 1, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||
sqliteVdbeAddOp(v, OP_PutStrKey, iParm, 0);
|
||||
break;
|
||||
|
@ -119,6 +119,15 @@ typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
|
||||
typedef INTPTR_TYPE ptr; /* Big enough to hold a pointer */
|
||||
typedef unsigned INTPTR_TYPE uptr; /* Big enough to hold a pointer */
|
||||
|
||||
/*
|
||||
** Most C compilers these days recognize "long double", don't they?
|
||||
** Just in case we encounter one that does not, we will create a macro
|
||||
** for long double so that it can be easily changed to just "double".
|
||||
*/
|
||||
#ifndef LONGDOUBLE_TYPE
|
||||
# define LONGDOUBLE_TYPE long double
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This macro casts a pointer to an integer. Useful for doing
|
||||
** pointer arithmetic.
|
||||
|
@ -29,122 +29,122 @@
|
||||
typedef struct Keyword Keyword;
|
||||
struct Keyword {
|
||||
char *zName; /* The keyword name */
|
||||
u16 len; /* Number of characters in the keyword */
|
||||
u16 tokenType; /* The token value for this keyword */
|
||||
Keyword *pNext; /* Next keyword with the same hash */
|
||||
u8 tokenType; /* Token value for this keyword */
|
||||
u8 len; /* Length of this keyword */
|
||||
u8 iNext; /* Index in aKeywordTable[] of next with same hash */
|
||||
};
|
||||
|
||||
/*
|
||||
** These are the keywords
|
||||
*/
|
||||
static Keyword aKeywordTable[] = {
|
||||
{ "ABORT", 0, TK_ABORT, 0 },
|
||||
{ "AFTER", 0, TK_AFTER, 0 },
|
||||
{ "ALL", 0, TK_ALL, 0 },
|
||||
{ "AND", 0, TK_AND, 0 },
|
||||
{ "AS", 0, TK_AS, 0 },
|
||||
{ "ASC", 0, TK_ASC, 0 },
|
||||
{ "ATTACH", 0, TK_ATTACH, 0 },
|
||||
{ "BEFORE", 0, TK_BEFORE, 0 },
|
||||
{ "BEGIN", 0, TK_BEGIN, 0 },
|
||||
{ "BETWEEN", 0, TK_BETWEEN, 0 },
|
||||
{ "BY", 0, TK_BY, 0 },
|
||||
{ "CASCADE", 0, TK_CASCADE, 0 },
|
||||
{ "CASE", 0, TK_CASE, 0 },
|
||||
{ "CHECK", 0, TK_CHECK, 0 },
|
||||
{ "CLUSTER", 0, TK_CLUSTER, 0 },
|
||||
{ "COLLATE", 0, TK_COLLATE, 0 },
|
||||
{ "COMMIT", 0, TK_COMMIT, 0 },
|
||||
{ "CONFLICT", 0, TK_CONFLICT, 0 },
|
||||
{ "CONSTRAINT", 0, TK_CONSTRAINT, 0 },
|
||||
{ "COPY", 0, TK_COPY, 0 },
|
||||
{ "CREATE", 0, TK_CREATE, 0 },
|
||||
{ "CROSS", 0, TK_JOIN_KW, 0 },
|
||||
{ "DATABASE", 0, TK_DATABASE, 0 },
|
||||
{ "DEFAULT", 0, TK_DEFAULT, 0 },
|
||||
{ "DEFERRED", 0, TK_DEFERRED, 0 },
|
||||
{ "DEFERRABLE", 0, TK_DEFERRABLE, 0 },
|
||||
{ "DELETE", 0, TK_DELETE, 0 },
|
||||
{ "DELIMITERS", 0, TK_DELIMITERS, 0 },
|
||||
{ "DESC", 0, TK_DESC, 0 },
|
||||
{ "DETACH", 0, TK_DETACH, 0 },
|
||||
{ "DISTINCT", 0, TK_DISTINCT, 0 },
|
||||
{ "DROP", 0, TK_DROP, 0 },
|
||||
{ "END", 0, TK_END, 0 },
|
||||
{ "EACH", 0, TK_EACH, 0 },
|
||||
{ "ELSE", 0, TK_ELSE, 0 },
|
||||
{ "EXCEPT", 0, TK_EXCEPT, 0 },
|
||||
{ "EXPLAIN", 0, TK_EXPLAIN, 0 },
|
||||
{ "FAIL", 0, TK_FAIL, 0 },
|
||||
{ "FOR", 0, TK_FOR, 0 },
|
||||
{ "FOREIGN", 0, TK_FOREIGN, 0 },
|
||||
{ "FROM", 0, TK_FROM, 0 },
|
||||
{ "FULL", 0, TK_JOIN_KW, 0 },
|
||||
{ "GLOB", 0, TK_GLOB, 0 },
|
||||
{ "GROUP", 0, TK_GROUP, 0 },
|
||||
{ "HAVING", 0, TK_HAVING, 0 },
|
||||
{ "IGNORE", 0, TK_IGNORE, 0 },
|
||||
{ "IMMEDIATE", 0, TK_IMMEDIATE, 0 },
|
||||
{ "IN", 0, TK_IN, 0 },
|
||||
{ "INDEX", 0, TK_INDEX, 0 },
|
||||
{ "INITIALLY", 0, TK_INITIALLY, 0 },
|
||||
{ "INNER", 0, TK_JOIN_KW, 0 },
|
||||
{ "INSERT", 0, TK_INSERT, 0 },
|
||||
{ "INSTEAD", 0, TK_INSTEAD, 0 },
|
||||
{ "INTERSECT", 0, TK_INTERSECT, 0 },
|
||||
{ "INTO", 0, TK_INTO, 0 },
|
||||
{ "IS", 0, TK_IS, 0 },
|
||||
{ "ISNULL", 0, TK_ISNULL, 0 },
|
||||
{ "JOIN", 0, TK_JOIN, 0 },
|
||||
{ "KEY", 0, TK_KEY, 0 },
|
||||
{ "LEFT", 0, TK_JOIN_KW, 0 },
|
||||
{ "LIKE", 0, TK_LIKE, 0 },
|
||||
{ "LIMIT", 0, TK_LIMIT, 0 },
|
||||
{ "MATCH", 0, TK_MATCH, 0 },
|
||||
{ "NATURAL", 0, TK_JOIN_KW, 0 },
|
||||
{ "NOT", 0, TK_NOT, 0 },
|
||||
{ "NOTNULL", 0, TK_NOTNULL, 0 },
|
||||
{ "NULL", 0, TK_NULL, 0 },
|
||||
{ "OF", 0, TK_OF, 0 },
|
||||
{ "OFFSET", 0, TK_OFFSET, 0 },
|
||||
{ "ON", 0, TK_ON, 0 },
|
||||
{ "OR", 0, TK_OR, 0 },
|
||||
{ "ORDER", 0, TK_ORDER, 0 },
|
||||
{ "OUTER", 0, TK_JOIN_KW, 0 },
|
||||
{ "PRAGMA", 0, TK_PRAGMA, 0 },
|
||||
{ "PRIMARY", 0, TK_PRIMARY, 0 },
|
||||
{ "RAISE", 0, TK_RAISE, 0 },
|
||||
{ "REFERENCES", 0, TK_REFERENCES, 0 },
|
||||
{ "REPLACE", 0, TK_REPLACE, 0 },
|
||||
{ "RESTRICT", 0, TK_RESTRICT, 0 },
|
||||
{ "RIGHT", 0, TK_JOIN_KW, 0 },
|
||||
{ "ROLLBACK", 0, TK_ROLLBACK, 0 },
|
||||
{ "ROW", 0, TK_ROW, 0 },
|
||||
{ "SELECT", 0, TK_SELECT, 0 },
|
||||
{ "SET", 0, TK_SET, 0 },
|
||||
{ "STATEMENT", 0, TK_STATEMENT, 0 },
|
||||
{ "TABLE", 0, TK_TABLE, 0 },
|
||||
{ "TEMP", 0, TK_TEMP, 0 },
|
||||
{ "TEMPORARY", 0, TK_TEMP, 0 },
|
||||
{ "THEN", 0, TK_THEN, 0 },
|
||||
{ "TRANSACTION", 0, TK_TRANSACTION, 0 },
|
||||
{ "TRIGGER", 0, TK_TRIGGER, 0 },
|
||||
{ "UNION", 0, TK_UNION, 0 },
|
||||
{ "UNIQUE", 0, TK_UNIQUE, 0 },
|
||||
{ "UPDATE", 0, TK_UPDATE, 0 },
|
||||
{ "USING", 0, TK_USING, 0 },
|
||||
{ "VACUUM", 0, TK_VACUUM, 0 },
|
||||
{ "VALUES", 0, TK_VALUES, 0 },
|
||||
{ "VIEW", 0, TK_VIEW, 0 },
|
||||
{ "WHEN", 0, TK_WHEN, 0 },
|
||||
{ "WHERE", 0, TK_WHERE, 0 },
|
||||
{ "ABORT", TK_ABORT, },
|
||||
{ "AFTER", TK_AFTER, },
|
||||
{ "ALL", TK_ALL, },
|
||||
{ "AND", TK_AND, },
|
||||
{ "AS", TK_AS, },
|
||||
{ "ASC", TK_ASC, },
|
||||
{ "ATTACH", TK_ATTACH, },
|
||||
{ "BEFORE", TK_BEFORE, },
|
||||
{ "BEGIN", TK_BEGIN, },
|
||||
{ "BETWEEN", TK_BETWEEN, },
|
||||
{ "BY", TK_BY, },
|
||||
{ "CASCADE", TK_CASCADE, },
|
||||
{ "CASE", TK_CASE, },
|
||||
{ "CHECK", TK_CHECK, },
|
||||
{ "CLUSTER", TK_CLUSTER, },
|
||||
{ "COLLATE", TK_COLLATE, },
|
||||
{ "COMMIT", TK_COMMIT, },
|
||||
{ "CONFLICT", TK_CONFLICT, },
|
||||
{ "CONSTRAINT", TK_CONSTRAINT, },
|
||||
{ "COPY", TK_COPY, },
|
||||
{ "CREATE", TK_CREATE, },
|
||||
{ "CROSS", TK_JOIN_KW, },
|
||||
{ "DATABASE", TK_DATABASE, },
|
||||
{ "DEFAULT", TK_DEFAULT, },
|
||||
{ "DEFERRED", TK_DEFERRED, },
|
||||
{ "DEFERRABLE", TK_DEFERRABLE, },
|
||||
{ "DELETE", TK_DELETE, },
|
||||
{ "DELIMITERS", TK_DELIMITERS, },
|
||||
{ "DESC", TK_DESC, },
|
||||
{ "DETACH", TK_DETACH, },
|
||||
{ "DISTINCT", TK_DISTINCT, },
|
||||
{ "DROP", TK_DROP, },
|
||||
{ "END", TK_END, },
|
||||
{ "EACH", TK_EACH, },
|
||||
{ "ELSE", TK_ELSE, },
|
||||
{ "EXCEPT", TK_EXCEPT, },
|
||||
{ "EXPLAIN", TK_EXPLAIN, },
|
||||
{ "FAIL", TK_FAIL, },
|
||||
{ "FOR", TK_FOR, },
|
||||
{ "FOREIGN", TK_FOREIGN, },
|
||||
{ "FROM", TK_FROM, },
|
||||
{ "FULL", TK_JOIN_KW, },
|
||||
{ "GLOB", TK_GLOB, },
|
||||
{ "GROUP", TK_GROUP, },
|
||||
{ "HAVING", TK_HAVING, },
|
||||
{ "IGNORE", TK_IGNORE, },
|
||||
{ "IMMEDIATE", TK_IMMEDIATE, },
|
||||
{ "IN", TK_IN, },
|
||||
{ "INDEX", TK_INDEX, },
|
||||
{ "INITIALLY", TK_INITIALLY, },
|
||||
{ "INNER", TK_JOIN_KW, },
|
||||
{ "INSERT", TK_INSERT, },
|
||||
{ "INSTEAD", TK_INSTEAD, },
|
||||
{ "INTERSECT", TK_INTERSECT, },
|
||||
{ "INTO", TK_INTO, },
|
||||
{ "IS", TK_IS, },
|
||||
{ "ISNULL", TK_ISNULL, },
|
||||
{ "JOIN", TK_JOIN, },
|
||||
{ "KEY", TK_KEY, },
|
||||
{ "LEFT", TK_JOIN_KW, },
|
||||
{ "LIKE", TK_LIKE, },
|
||||
{ "LIMIT", TK_LIMIT, },
|
||||
{ "MATCH", TK_MATCH, },
|
||||
{ "NATURAL", TK_JOIN_KW, },
|
||||
{ "NOT", TK_NOT, },
|
||||
{ "NOTNULL", TK_NOTNULL, },
|
||||
{ "NULL", TK_NULL, },
|
||||
{ "OF", TK_OF, },
|
||||
{ "OFFSET", TK_OFFSET, },
|
||||
{ "ON", TK_ON, },
|
||||
{ "OR", TK_OR, },
|
||||
{ "ORDER", TK_ORDER, },
|
||||
{ "OUTER", TK_JOIN_KW, },
|
||||
{ "PRAGMA", TK_PRAGMA, },
|
||||
{ "PRIMARY", TK_PRIMARY, },
|
||||
{ "RAISE", TK_RAISE, },
|
||||
{ "REFERENCES", TK_REFERENCES, },
|
||||
{ "REPLACE", TK_REPLACE, },
|
||||
{ "RESTRICT", TK_RESTRICT, },
|
||||
{ "RIGHT", TK_JOIN_KW, },
|
||||
{ "ROLLBACK", TK_ROLLBACK, },
|
||||
{ "ROW", TK_ROW, },
|
||||
{ "SELECT", TK_SELECT, },
|
||||
{ "SET", TK_SET, },
|
||||
{ "STATEMENT", TK_STATEMENT, },
|
||||
{ "TABLE", TK_TABLE, },
|
||||
{ "TEMP", TK_TEMP, },
|
||||
{ "TEMPORARY", TK_TEMP, },
|
||||
{ "THEN", TK_THEN, },
|
||||
{ "TRANSACTION", TK_TRANSACTION, },
|
||||
{ "TRIGGER", TK_TRIGGER, },
|
||||
{ "UNION", TK_UNION, },
|
||||
{ "UNIQUE", TK_UNIQUE, },
|
||||
{ "UPDATE", TK_UPDATE, },
|
||||
{ "USING", TK_USING, },
|
||||
{ "VACUUM", TK_VACUUM, },
|
||||
{ "VALUES", TK_VALUES, },
|
||||
{ "VIEW", TK_VIEW, },
|
||||
{ "WHEN", TK_WHEN, },
|
||||
{ "WHERE", TK_WHERE, },
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the hash table
|
||||
*/
|
||||
#define KEY_HASH_SIZE 71
|
||||
static Keyword *apHashTable[KEY_HASH_SIZE];
|
||||
#define KEY_HASH_SIZE 101
|
||||
static u8 aiHashTable[KEY_HASH_SIZE];
|
||||
|
||||
|
||||
/*
|
||||
@ -153,29 +153,29 @@ static Keyword *apHashTable[KEY_HASH_SIZE];
|
||||
** returned. If the input is not a keyword, TK_ID is returned.
|
||||
*/
|
||||
int sqliteKeywordCode(const char *z, int n){
|
||||
int h;
|
||||
int h, i;
|
||||
Keyword *p;
|
||||
static char needInit = 1;
|
||||
if( needInit ){
|
||||
/* Initialize the keyword hash table */
|
||||
sqliteOsEnterMutex();
|
||||
if( needInit ){
|
||||
int i;
|
||||
int n;
|
||||
n = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
|
||||
for(i=0; i<n; i++){
|
||||
int nk;
|
||||
nk = sizeof(aKeywordTable)/sizeof(aKeywordTable[0]);
|
||||
for(i=0; i<nk; i++){
|
||||
aKeywordTable[i].len = strlen(aKeywordTable[i].zName);
|
||||
h = sqliteHashNoCase(aKeywordTable[i].zName, aKeywordTable[i].len);
|
||||
h %= KEY_HASH_SIZE;
|
||||
aKeywordTable[i].pNext = apHashTable[h];
|
||||
apHashTable[h] = &aKeywordTable[i];
|
||||
aKeywordTable[i].iNext = aiHashTable[h];
|
||||
aiHashTable[h] = i+1;
|
||||
}
|
||||
needInit = 0;
|
||||
}
|
||||
sqliteOsLeaveMutex();
|
||||
}
|
||||
h = sqliteHashNoCase(z, n) % KEY_HASH_SIZE;
|
||||
for(p=apHashTable[h]; p; p=p->pNext){
|
||||
for(i=aiHashTable[h]; i; i=p->iNext){
|
||||
p = &aKeywordTable[i-1];
|
||||
if( p->len==n && sqliteStrNICmp(p->zName, z, n)==0 ){
|
||||
return p->tokenType;
|
||||
}
|
||||
@ -185,8 +185,12 @@ int sqliteKeywordCode(const char *z, int n){
|
||||
|
||||
|
||||
/*
|
||||
** If X is a character that can be used in an identifier then
|
||||
** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0.
|
||||
** If X is a character that can be used in an identifier and
|
||||
** X&0x80==0 then isIdChar[X] will be 1. If X&0x80==0x80 then
|
||||
** X is always an identifier character. (Hence all UTF-8
|
||||
** characters can be part of an identifier). isIdChar[X] will
|
||||
** be 0 for every character in the lower 128 ASCII characters
|
||||
** that cannot be used as part of an identifier.
|
||||
**
|
||||
** In this implementation, an identifier can be a string of
|
||||
** alphabetic characters, digits, and "_" plus any character
|
||||
@ -204,14 +208,6 @@ static const char isIdChar[] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
|
||||
};
|
||||
|
||||
|
||||
@ -380,10 +376,10 @@ static int sqliteGetToken(const unsigned char *z, int *tokenType){
|
||||
return 1;
|
||||
}
|
||||
default: {
|
||||
if( !isIdChar[*z] ){
|
||||
if( (*z&0x80)==0 && !isIdChar[*z] ){
|
||||
break;
|
||||
}
|
||||
for(i=1; isIdChar[z[i]]; i++){}
|
||||
for(i=1; (z[i]&0x80)!=0 || isIdChar[z[i]]; i++){}
|
||||
*tokenType = sqliteKeywordCode((char*)z, i);
|
||||
return i;
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ char *sqliteStrNDup_(const char *z, int n, char *zFile, int line){
|
||||
void *sqliteMalloc(int n){
|
||||
void *p;
|
||||
if( (p = malloc(n))==0 ){
|
||||
sqlite_malloc_failed++;
|
||||
if( n>0 ) sqlite_malloc_failed++;
|
||||
}else{
|
||||
memset(p, 0, n);
|
||||
}
|
||||
@ -266,7 +266,7 @@ void *sqliteMalloc(int n){
|
||||
void *sqliteMallocRaw(int n){
|
||||
void *p;
|
||||
if( (p = malloc(n))==0 ){
|
||||
sqlite_malloc_failed++;
|
||||
if( n>0 ) sqlite_malloc_failed++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -664,7 +664,7 @@ int sqliteIsNumber(const char *z){
|
||||
*/
|
||||
double sqliteAtoF(const char *z){
|
||||
int sign = 1;
|
||||
double v1 = 0.0;
|
||||
LONGDOUBLE_TYPE v1 = 0.0;
|
||||
if( *z=='-' ){
|
||||
sign = -1;
|
||||
z++;
|
||||
@ -676,7 +676,7 @@ double sqliteAtoF(const char *z){
|
||||
z++;
|
||||
}
|
||||
if( *z=='.' ){
|
||||
double divisor = 1.0;
|
||||
LONGDOUBLE_TYPE divisor = 1.0;
|
||||
z++;
|
||||
while( isdigit(*z) ){
|
||||
v1 = v1*10.0 + (*z - '0');
|
||||
@ -688,7 +688,7 @@ double sqliteAtoF(const char *z){
|
||||
if( *z=='e' || *z=='E' ){
|
||||
int esign = 1;
|
||||
int eval = 0;
|
||||
double scale = 1.0;
|
||||
LONGDOUBLE_TYPE scale = 1.0;
|
||||
z++;
|
||||
if( *z=='-' ){
|
||||
esign = -1;
|
||||
|
@ -362,42 +362,6 @@ static Sorter *Merge(Sorter *pLeft, Sorter *pRight){
|
||||
return sHead.pNext;
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an integer in between the native integer format and
|
||||
** the bigEndian format used as the record number for tables.
|
||||
**
|
||||
** The bigEndian format (most significant byte first) is used for
|
||||
** record numbers so that records will sort into the correct order
|
||||
** even though memcmp() is used to compare the keys. On machines
|
||||
** whose native integer format is little endian (ex: i486) the
|
||||
** order of bytes is reversed. On native big-endian machines
|
||||
** (ex: Alpha, Sparc, Motorola) the byte order is the same.
|
||||
**
|
||||
** This function is its own inverse. In other words
|
||||
**
|
||||
** X == byteSwap(byteSwap(X))
|
||||
*/
|
||||
static int byteSwap(int x){
|
||||
union {
|
||||
char zBuf[sizeof(int)];
|
||||
int i;
|
||||
} ux;
|
||||
ux.zBuf[3] = x&0xff;
|
||||
ux.zBuf[2] = (x>>8)&0xff;
|
||||
ux.zBuf[1] = (x>>16)&0xff;
|
||||
ux.zBuf[0] = (x>>24)&0xff;
|
||||
return ux.i;
|
||||
}
|
||||
|
||||
/*
|
||||
** When converting from the native format to the key format and back
|
||||
** again, in addition to changing the byte order we invert the high-order
|
||||
** bit of the most significant byte. This causes negative numbers to
|
||||
** sort before positive numbers in the memcmp() function.
|
||||
*/
|
||||
#define keyToInt(X) (byteSwap(X) ^ 0x80000000)
|
||||
#define intToKey(X) (byteSwap((X) ^ 0x80000000))
|
||||
|
||||
/*
|
||||
** Code contained within the VERIFY() macro is not needed for correct
|
||||
** execution. It is there only to catch errors. So when we compile
|
||||
@ -1157,7 +1121,9 @@ case OP_Function: {
|
||||
ctx.z = 0;
|
||||
ctx.isError = 0;
|
||||
ctx.isStep = 0;
|
||||
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
|
||||
(*ctx.pFunc->xFunc)(&ctx, n, (const char**)&zStack[p->tos-n+1]);
|
||||
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
|
||||
sqliteVdbePopStack(p, n);
|
||||
p->tos++;
|
||||
aStack[p->tos] = ctx.s;
|
||||
@ -1831,8 +1797,8 @@ case OP_IfNot: {
|
||||
/* Opcode: IsNull P1 P2 *
|
||||
**
|
||||
** If any of the top abs(P1) values on the stack are NULL, then jump
|
||||
** to P2. The stack is popped P1 times if P1>0. If P1<0 then all values
|
||||
** are left unchanged on the stack.
|
||||
** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack
|
||||
** unchanged.
|
||||
*/
|
||||
case OP_IsNull: {
|
||||
int i, cnt;
|
||||
@ -1851,14 +1817,18 @@ case OP_IsNull: {
|
||||
|
||||
/* Opcode: NotNull P1 P2 *
|
||||
**
|
||||
** Jump to P2 if the top value on the stack is not NULL. Pop the
|
||||
** stack if P1 is greater than zero. If P1 is less than or equal to
|
||||
** zero then leave the value on the stack.
|
||||
** Jump to P2 if the top P1 values on the stack are all not NULL. Pop the
|
||||
** stack if P1 times if P1 is greater than zero. If P1 is less than
|
||||
** zero then leave the stack unchanged.
|
||||
*/
|
||||
case OP_NotNull: {
|
||||
VERIFY( if( p->tos<0 ) goto not_enough_stack; )
|
||||
if( (aStack[p->tos].flags & STK_Null)==0 ) pc = pOp->p2-1;
|
||||
if( pOp->p1>0 ){ POPSTACK; }
|
||||
int i, cnt;
|
||||
cnt = pOp->p1;
|
||||
if( cnt<0 ) cnt = -cnt;
|
||||
VERIFY( if( p->tos+1-cnt<0 ) goto not_enough_stack; )
|
||||
for(i=0; i<cnt && (aStack[p->tos-i].flags & STK_Null)==0; i++){}
|
||||
if( i>=cnt ) pc = pOp->p2-1;
|
||||
if( pOp->p1>0 ) sqliteVdbePopStack(p, cnt);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2597,8 +2567,15 @@ case OP_MoveTo: {
|
||||
pC = &p->aCsr[i];
|
||||
if( pC->pCursor!=0 ){
|
||||
int res, oc;
|
||||
pC->nullRow = 0;
|
||||
if( aStack[tos].flags & STK_Int ){
|
||||
int iKey = intToKey(aStack[tos].i);
|
||||
if( pOp->p2==0 && pOp->opcode==OP_MoveTo ){
|
||||
pC->movetoTarget = iKey;
|
||||
pC->deferredMoveto = 1;
|
||||
POPSTACK;
|
||||
break;
|
||||
}
|
||||
sqliteBtreeMoveto(pC->pCursor, (char*)&iKey, sizeof(int), &res);
|
||||
pC->lastRecno = aStack[tos].i;
|
||||
pC->recnoIsValid = res==0;
|
||||
@ -2607,7 +2584,7 @@ case OP_MoveTo: {
|
||||
sqliteBtreeMoveto(pC->pCursor, zStack[tos], aStack[tos].n, &res);
|
||||
pC->recnoIsValid = 0;
|
||||
}
|
||||
pC->nullRow = 0;
|
||||
pC->deferredMoveto = 0;
|
||||
sqlite_search_count++;
|
||||
oc = pOp->opcode;
|
||||
if( oc==OP_MoveTo && res<0 ){
|
||||
@ -2682,6 +2659,7 @@ case OP_Found: {
|
||||
Stringify(p, tos);
|
||||
rx = sqliteBtreeMoveto(pC->pCursor, zStack[tos], aStack[tos].n, &res);
|
||||
alreadyExists = rx==SQLITE_OK && res==0;
|
||||
pC->deferredMoveto = 0;
|
||||
}
|
||||
if( pOp->opcode==OP_Found ){
|
||||
if( alreadyExists ) pc = pOp->p2 - 1;
|
||||
@ -2743,6 +2721,7 @@ case OP_IsUnique: {
|
||||
/* Search for an entry in P1 where all but the last four bytes match K.
|
||||
** If there is no such entry, jump immediately to P2.
|
||||
*/
|
||||
assert( p->aCsr[i].deferredMoveto==0 );
|
||||
rc = sqliteBtreeMoveto(pCrsr, zKey, nKey-4, &res);
|
||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
||||
if( res<0 ){
|
||||
@ -2911,6 +2890,7 @@ case OP_NewRecno: {
|
||||
}
|
||||
}
|
||||
pC->recnoIsValid = 0;
|
||||
pC->deferredMoveto = 0;
|
||||
}
|
||||
p->tos++;
|
||||
aStack[p->tos].i = v;
|
||||
@ -2993,6 +2973,7 @@ case OP_PutStrKey: {
|
||||
zStack[tos], aStack[tos].n);
|
||||
}
|
||||
pC->recnoIsValid = 0;
|
||||
pC->deferredMoveto = 0;
|
||||
}
|
||||
POPSTACK;
|
||||
POPSTACK;
|
||||
@ -3019,6 +3000,7 @@ case OP_Delete: {
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
pC = &p->aCsr[i];
|
||||
if( pC->pCursor!=0 ){
|
||||
sqliteVdbeCursorMoveto(pC);
|
||||
rc = sqliteBtreeDelete(pC->pCursor);
|
||||
pC->nextRowidValid = 0;
|
||||
}
|
||||
@ -3049,6 +3031,16 @@ case OP_KeyAsData: {
|
||||
** If the cursor is not pointing to a valid row, a NULL is pushed
|
||||
** onto the stack.
|
||||
*/
|
||||
/* Opcode: RowKey P1 * *
|
||||
**
|
||||
** Push onto the stack the complete row key for cursor P1.
|
||||
** There is no interpretation of the key. It is just copied
|
||||
** onto the stack exactly as it is found in the database file.
|
||||
**
|
||||
** If the cursor is not pointing to a valid row, a NULL is pushed
|
||||
** onto the stack.
|
||||
*/
|
||||
case OP_RowKey:
|
||||
case OP_RowData: {
|
||||
int i = pOp->p1;
|
||||
int tos = ++p->tos;
|
||||
@ -3061,10 +3053,11 @@ case OP_RowData: {
|
||||
aStack[tos].flags = STK_Null;
|
||||
}else if( pC->pCursor!=0 ){
|
||||
BtCursor *pCrsr = pC->pCursor;
|
||||
sqliteVdbeCursorMoveto(pC);
|
||||
if( pC->nullRow ){
|
||||
aStack[tos].flags = STK_Null;
|
||||
break;
|
||||
}else if( pC->keyAsData ){
|
||||
}else if( pC->keyAsData || pOp->opcode==OP_RowKey ){
|
||||
sqliteBtreeKeySize(pCrsr, &n);
|
||||
}else{
|
||||
sqliteBtreeDataSize(pCrsr, &n);
|
||||
@ -3079,7 +3072,7 @@ case OP_RowData: {
|
||||
aStack[tos].flags = STK_Str | STK_Dyn;
|
||||
zStack[tos] = z;
|
||||
}
|
||||
if( pC->keyAsData ){
|
||||
if( pC->keyAsData || pOp->opcode==OP_RowKey ){
|
||||
sqliteBtreeKey(pCrsr, 0, n, zStack[tos]);
|
||||
}else{
|
||||
sqliteBtreeData(pCrsr, 0, n, zStack[tos]);
|
||||
@ -3131,6 +3124,7 @@ case OP_Column: {
|
||||
zRec = zStack[tos+i];
|
||||
payloadSize = aStack[tos+i].n;
|
||||
}else if( (pC = &p->aCsr[i])->pCursor!=0 ){
|
||||
sqliteVdbeCursorMoveto(pC);
|
||||
zRec = 0;
|
||||
pCrsr = pC->pCursor;
|
||||
if( pC->nullRow ){
|
||||
@ -3237,7 +3231,9 @@ case OP_Recno: {
|
||||
int v;
|
||||
|
||||
assert( i>=0 && i<p->nCursor );
|
||||
if( (pC = &p->aCsr[i])->recnoIsValid ){
|
||||
pC = &p->aCsr[i];
|
||||
sqliteVdbeCursorMoveto(pC);
|
||||
if( pC->recnoIsValid ){
|
||||
v = pC->lastRecno;
|
||||
}else if( pC->pseudoTable ){
|
||||
v = keyToInt(pC->iKey);
|
||||
@ -3276,6 +3272,7 @@ case OP_FullKey: {
|
||||
int amt;
|
||||
char *z;
|
||||
|
||||
sqliteVdbeCursorMoveto(&p->aCsr[i]);
|
||||
sqliteBtreeKeySize(pCrsr, &amt);
|
||||
if( amt<=0 ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
@ -3329,7 +3326,8 @@ case OP_Last: {
|
||||
if( (pCrsr = pC->pCursor)!=0 ){
|
||||
int res;
|
||||
rc = sqliteBtreeLast(pCrsr, &res);
|
||||
p->aCsr[i].nullRow = res;
|
||||
pC->nullRow = res;
|
||||
pC->deferredMoveto = 0;
|
||||
if( res && pOp->p2>0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
@ -3359,6 +3357,7 @@ case OP_Rewind: {
|
||||
rc = sqliteBtreeFirst(pCrsr, &res);
|
||||
pC->atFirst = res==0;
|
||||
pC->nullRow = res;
|
||||
pC->deferredMoveto = 0;
|
||||
if( res && pOp->p2>0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
@ -3397,6 +3396,7 @@ case OP_Next: {
|
||||
if( pC->nullRow ){
|
||||
res = 1;
|
||||
}else{
|
||||
assert( pC->deferredMoveto==0 );
|
||||
rc = pOp->opcode==OP_Next ? sqliteBtreeNext(pCrsr, &res) :
|
||||
sqliteBtreePrevious(pCrsr, &res);
|
||||
pC->nullRow = res;
|
||||
@ -3458,6 +3458,7 @@ case OP_IdxPut: {
|
||||
}
|
||||
}
|
||||
rc = sqliteBtreeInsert(pCrsr, zKey, nKey, "", 0);
|
||||
assert( p->aCsr[i].deferredMoveto==0 );
|
||||
}
|
||||
POPSTACK;
|
||||
break;
|
||||
@ -3479,6 +3480,7 @@ case OP_IdxDelete: {
|
||||
if( rx==SQLITE_OK && res==0 ){
|
||||
rc = sqliteBtreeDelete(pCrsr);
|
||||
}
|
||||
assert( p->aCsr[i].deferredMoveto==0 );
|
||||
}
|
||||
POPSTACK;
|
||||
break;
|
||||
@ -3501,6 +3503,7 @@ case OP_IdxRecno: {
|
||||
if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
|
||||
int v;
|
||||
int sz;
|
||||
assert( p->aCsr[i].deferredMoveto==0 );
|
||||
sqliteBtreeKeySize(pCrsr, &sz);
|
||||
if( sz<sizeof(u32) ){
|
||||
aStack[tos].flags = STK_Null;
|
||||
@ -3550,6 +3553,7 @@ case OP_IdxGE: {
|
||||
int res, rc;
|
||||
|
||||
Stringify(p, tos);
|
||||
assert( p->aCsr[i].deferredMoveto==0 );
|
||||
rc = sqliteBtreeKeyCompare(pCrsr, zStack[tos], aStack[tos].n, 4, &res);
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
@ -3567,6 +3571,37 @@ case OP_IdxGE: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxIsNull P1 P2 *
|
||||
**
|
||||
** The top of the stack contains an index entry such as might be generated
|
||||
** by the MakeIdxKey opcode. This routine looks at the first P1 fields of
|
||||
** that key. If any of the first P1 fields are NULL, then a jump is made
|
||||
** to address P2. Otherwise we fall straight through.
|
||||
**
|
||||
** The index entry is always popped from the stack.
|
||||
*/
|
||||
case OP_IdxIsNull: {
|
||||
int i = pOp->p1;
|
||||
int tos = p->tos;
|
||||
int k, n;
|
||||
const char *z;
|
||||
|
||||
assert( tos>=0 );
|
||||
assert( aStack[tos].flags & STK_Str );
|
||||
z = zStack[tos];
|
||||
n = aStack[tos].n;
|
||||
for(k=0; k<n && i>0; i--){
|
||||
if( z[k]=='a' ){
|
||||
pc = pOp->p2-1;
|
||||
break;
|
||||
}
|
||||
while( k<n && z[k] ){ k++; }
|
||||
k++;
|
||||
}
|
||||
POPSTACK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Destroy P1 P2 *
|
||||
**
|
||||
** Delete an entire database table or index whose root page in the database
|
||||
|
@ -16,6 +16,15 @@
|
||||
** this header information was factored out.
|
||||
*/
|
||||
|
||||
/*
|
||||
** When converting from the native format to the key format and back
|
||||
** again, in addition to changing the byte order we invert the high-order
|
||||
** bit of the most significant byte. This causes negative numbers to
|
||||
** sort before positive numbers in the memcmp() function.
|
||||
*/
|
||||
#define keyToInt(X) (sqliteVdbeByteSwap(X) ^ 0x80000000)
|
||||
#define intToKey(X) (sqliteVdbeByteSwap((X) ^ 0x80000000))
|
||||
|
||||
/*
|
||||
** The makefile scans this source file and creates the following
|
||||
** array of string constants which are the names of all VDBE opcodes.
|
||||
@ -62,6 +71,8 @@ struct Cursor {
|
||||
Bool nullRow; /* True if pointing to a row with no data */
|
||||
Bool nextRowidValid; /* True if the nextRowid field is valid */
|
||||
Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
|
||||
Bool deferredMoveto; /* A call to sqliteBtreeMoveto() is needed */
|
||||
int movetoTarget; /* Argument to the deferred sqliteBtreeMoveto() */
|
||||
Btree *pBt; /* Separate file holding temporary table */
|
||||
int nData; /* Number of bytes in pData */
|
||||
char *pData; /* Data for a NEW or OLD pseudo-table */
|
||||
@ -294,6 +305,8 @@ void sqliteVdbeSorterReset(Vdbe*);
|
||||
void sqliteVdbeAggReset(Agg*);
|
||||
void sqliteVdbeKeylistFree(Keylist*);
|
||||
void sqliteVdbePopStack(Vdbe*,int);
|
||||
int sqliteVdbeCursorMoveto(Cursor*);
|
||||
int sqliteVdbeByteSwap(int);
|
||||
#if !defined(NDEBUG) || defined(VDBE_PROFILE)
|
||||
void sqliteVdbePrintOp(FILE*, int, Op*);
|
||||
#endif
|
||||
|
@ -992,3 +992,51 @@ void sqliteVdbeDelete(Vdbe *p){
|
||||
p->magic = VDBE_MAGIC_DEAD;
|
||||
sqliteFree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
** Convert an integer in between the native integer format and
|
||||
** the bigEndian format used as the record number for tables.
|
||||
**
|
||||
** The bigEndian format (most significant byte first) is used for
|
||||
** record numbers so that records will sort into the correct order
|
||||
** even though memcmp() is used to compare the keys. On machines
|
||||
** whose native integer format is little endian (ex: i486) the
|
||||
** order of bytes is reversed. On native big-endian machines
|
||||
** (ex: Alpha, Sparc, Motorola) the byte order is the same.
|
||||
**
|
||||
** This function is its own inverse. In other words
|
||||
**
|
||||
** X == byteSwap(byteSwap(X))
|
||||
*/
|
||||
int sqliteVdbeByteSwap(int x){
|
||||
union {
|
||||
char zBuf[sizeof(int)];
|
||||
int i;
|
||||
} ux;
|
||||
ux.zBuf[3] = x&0xff;
|
||||
ux.zBuf[2] = (x>>8)&0xff;
|
||||
ux.zBuf[1] = (x>>16)&0xff;
|
||||
ux.zBuf[0] = (x>>24)&0xff;
|
||||
return ux.i;
|
||||
}
|
||||
|
||||
/*
|
||||
** If a MoveTo operation is pending on the given cursor, then do that
|
||||
** MoveTo now. Return an error code. If no MoveTo is pending, this
|
||||
** routine does nothing and returns SQLITE_OK.
|
||||
*/
|
||||
int sqliteVdbeCursorMoveto(Cursor *p){
|
||||
if( p->deferredMoveto ){
|
||||
int res;
|
||||
extern int sqlite_search_count;
|
||||
sqliteBtreeMoveto(p->pCursor, (char*)&p->movetoTarget, sizeof(int), &res);
|
||||
p->lastRecno = keyToInt(p->movetoTarget);
|
||||
p->recnoIsValid = res==0;
|
||||
if( res<0 ){
|
||||
sqliteBtreeNext(p->pCursor, &res);
|
||||
}
|
||||
sqlite_search_count++;
|
||||
p->deferredMoveto = 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
@ -764,7 +764,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
){
|
||||
if( pX->op==TK_EQ ){
|
||||
sqliteExprCode(pParse, pX->pRight);
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
if( pX->op==TK_IN && nColumn==1 ){
|
||||
@ -781,7 +781,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
pLevel->inOp = OP_Next;
|
||||
pLevel->inP1 = pX->iTable;
|
||||
}
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -791,13 +791,16 @@ WhereInfo *sqliteWhereBegin(
|
||||
&& aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
|
||||
){
|
||||
sqliteExprCode(pParse, aExpr[k].p->pLeft);
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pLevel->iMem = pParse->nMem++;
|
||||
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -nColumn, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, nColumn, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0);
|
||||
sqliteAddIdxKeyType(v, pIdx);
|
||||
if( nColumn==pIdx->nColumn || pLevel->bRev ){
|
||||
@ -815,16 +818,17 @@ WhereInfo *sqliteWhereBegin(
|
||||
sqliteVdbeAddOp(v, OP_MoveLt, pLevel->iCur, brk);
|
||||
start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqliteVdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk);
|
||||
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
pLevel->op = OP_Prev;
|
||||
}else{
|
||||
/* Scan in the forward order */
|
||||
sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk);
|
||||
start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
|
||||
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
pLevel->op = OP_Next;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_IdxIsNull, nColumn, cont);
|
||||
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
if( i==pTabList->nSrc-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
}else{
|
||||
@ -933,7 +937,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
&& aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
|
||||
){
|
||||
sqliteExprCode(pParse, aExpr[k].p->pRight);
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
if( aExpr[k].idxRight==iCur
|
||||
@ -942,7 +946,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
&& aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
|
||||
){
|
||||
sqliteExprCode(pParse, aExpr[k].p->pLeft);
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -979,7 +983,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
){
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
leFlag = pExpr->op==TK_LE;
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
if( aExpr[k].idxRight==iCur
|
||||
@ -989,7 +993,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
){
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
leFlag = pExpr->op==TK_GE;
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -999,8 +1003,12 @@ WhereInfo *sqliteWhereBegin(
|
||||
leFlag = 1;
|
||||
}
|
||||
if( testOp!=OP_Noop ){
|
||||
int nCol = nEqColumn + (score & 1);
|
||||
pLevel->iMem = pParse->nMem++;
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, nEqColumn + (score & 1), 0);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0);
|
||||
sqliteAddIdxKeyType(v, pIdx);
|
||||
if( leFlag ){
|
||||
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
|
||||
@ -1034,7 +1042,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
){
|
||||
sqliteExprCode(pParse, pExpr->pRight);
|
||||
geFlag = pExpr->op==TK_GE;
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
if( aExpr[k].idxRight==iCur
|
||||
@ -1044,7 +1052,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
){
|
||||
sqliteExprCode(pParse, pExpr->pLeft);
|
||||
geFlag = pExpr->op==TK_LE;
|
||||
/* aExpr[k].p = 0; // See ticket #461 */
|
||||
aExpr[k].p = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1052,7 +1060,11 @@ WhereInfo *sqliteWhereBegin(
|
||||
geFlag = 1;
|
||||
}
|
||||
if( nEqColumn>0 || (score&2)!=0 ){
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, nEqColumn + ((score&2)!=0), 0);
|
||||
int nCol = nEqColumn + ((score&2)!=0);
|
||||
sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3);
|
||||
sqliteVdbeAddOp(v, OP_Pop, nCol, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
|
||||
sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0);
|
||||
sqliteAddIdxKeyType(v, pIdx);
|
||||
if( !geFlag ){
|
||||
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
|
||||
@ -1079,6 +1091,8 @@ WhereInfo *sqliteWhereBegin(
|
||||
sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
|
||||
sqliteVdbeAddOp(v, OP_IdxIsNull, nEqColumn + (score & 1), cont);
|
||||
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
if( i==pTabList->nSrc-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
|
Loading…
Reference in New Issue
Block a user