Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -2305,10 +2305,255 @@ sqlite4ReleaseTempReg(pParse, regKey); sqlite4VdbeAddOp1(v, OP_Close, iTab); sqlite4VdbeAddOp1(v, OP_Close, iIdx); } + +/* +** The CreateIndex structure indicated by the first argument contains the +** results of parsing the first part of a CREATE INDEX statement. +** Specifically, everything up to and including the "ON tblname" clause. +** The index may be an ordinary index, or it may be a "USING fts5" index. +** This function performs processing common to both. +*/ +static Table *createIndexFindTable( + Parse *pParse, /* Parsing context */ + CreateIndex *p, /* First part of CREATE INDEX statement */ + Token **ppIdxName, /* OUT: Pointer to index name token */ + char **pzIdxName, /* OUT: DbMalloc'd copy of index name */ + int *piDb /* OUT: Database to create index in */ +){ + DbFixer sFix; /* For assigning database names to pTblName */ + sqlite4 *db = pParse->db; /* Database handle */ + Token *pName = 0; /* Token containing unqualified index name */ + char *zName; /* Name of index being created */ + int iDb; /* Index of database in db->aDb[] */ + Table *pTab; /* Table object to return */ + + /* Use the two-part index name to determine the database + ** to search for the table. 'Fix' the table name to this db + ** before looking up the table. */ + iDb = sqlite4TwoPartName(pParse, &p->tName1, &p->tName2, &pName); + if( iDb<0 ) return 0; + assert( pName && pName->z ); + +#ifndef SQLITE4_OMIT_TEMPDB + /* If the index name was unqualified, check if the the table + ** is a temp table. If so, set the database to 1. Do not do this + ** if initialising a database schema. */ + if( !db->init.busy ){ + pTab = sqlite4SrcListLookup(pParse, p->pTblName); + if( p->tName2.n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){ + iDb = 1; + } + } +#endif + + if( sqlite4FixInit(&sFix, pParse, iDb, "index", pName) && + sqlite4FixSrcList(&sFix, p->pTblName) + ){ + /* Because the parser constructs pTblName from a single identifier, + ** sqlite4FixSrcList can never fail. */ + assert(0); + } + + pTab = sqlite4SrcListLookup(pParse, p->pTblName); + if( !pTab || db->mallocFailed ) return 0; + assert( db->aDb[iDb].pSchema==pTab->pSchema ); + assert( pParse->nErr==0 ); + + /* TODO: We will need to reinstate this block when sqlite_master is + ** modified to use an implicit primary key. */ +#if 0 + if( sqlite4StrNICmp(pTab->zName, "sqlite_", 7)==0 + && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){ + sqlite4ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); + goto exit_create_index; + } +#endif + + /* Verify that this is not an attempt to create an index on a view or + ** virtual table. */ + if( IsView(pTab) ){ + sqlite4ErrorMsg(pParse, "views may not be indexed"); + return 0; + } + if( IsVirtual(pTab) ){ + sqlite4ErrorMsg(pParse, "virtual tables may not be indexed"); + return 0; + } + + /* Ensure that the proposed index name is not reserved. */ + assert( pName->z!=0 ); + zName = sqlite4NameFromToken(db, pName); + if( zName==0 || sqlite4CheckObjectName(pParse, zName) ) return 0; + + /* Unless SQLite is currently parsing an existing database schema, check + ** that there is not already an index or table using the proposed name. */ + if( !db->init.busy ){ + char *zDb = db->aDb[iDb].zName; + if( sqlite4FindTable(db, zName, zDb)!=0 ){ + sqlite4ErrorMsg(pParse, "there is already a table named %s", zName); + } + else if( sqlite4FindIndex(db, zName, zDb)!=0 ){ + if( p->bIfnotexist ){ + assert( !db->init.busy ); + sqlite4CodeVerifySchema(pParse, iDb); + pTab = 0; + }else{ + sqlite4ErrorMsg(pParse, "index %s already exists", zName); + } + } + } + + if( pParse->nErr || pTab==0 ){ + sqlite4DbFree(db, zName); + pTab = 0; + zName = 0; + } + *ppIdxName = pName; + *pzIdxName = zName; + *piDb = iDb; + return pTab; +} + +#ifndef SQLITE4_OMIT_AUTHORIZATION +static int createIndexAuth( + Parse *pParse, /* Parser context */ + Table *pTab, /* Table index is being created on */ + const char *zIdx /* Name of index being created */ +){ + sqlite4 *db; + int iDb; + int iOp; + const char *zDb; + + db = pParse->db; + iDb = sqlite4SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zName; + + if( sqlite4AuthCheck(pParse, SQLITE4_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ + return 1; + } + + iOp = (!OMIT_TEMPDB && iDb==1)?SQLITE4_CREATE_TEMP_INDEX:SQLITE4_CREATE_INDEX; + if( sqlite4AuthCheck(pParse, iOp, zIdx, pTab->zName, zDb) ){ + return 1; + } + + return 0; +} +#else +# define createIndexAuth(a, b, c) 0 +#endif // SQLITE4_OMIT_AUTHORIZATION + +static void createIndexWriteSchema( + Parse *pParse, + Index *pIdx, + Token *pName, + Token *pEnd +){ + sqlite4 *db = pParse->db; + int iDb; + + assert( db->init.busy==0 ); + iDb = sqlite4SchemaToIndex(db, pIdx->pSchema); + pIdx->tnum = ++pParse->nMem; + allocateTableNumber(pParse, iDb, pIdx->tnum); + + if( pIdx->eIndexType!=SQLITE4_INDEX_PRIMARYKEY ){ + Vdbe *v; + char *zStmt; + + v = sqlite4GetVdbe(pParse); + if( v==0 ) return; + + sqlite4BeginWriteOperation(pParse, 1, iDb); + + /* Unless this index is an automatic index created by a UNIQUE + ** constraint, assemble a CREATE INDEX statement to write into the + ** sqlite_master table. */ + if( pIdx->eIndexType!=SQLITE4_INDEX_UNIQUE ){ + int n = (int)(pEnd->z - pName->z) + pEnd->n; + const char *zUnique = (pIdx->onError==OE_None ? "" : " UNIQUE"); + zStmt = sqlite4MPrintf(db, "CREATE%s INDEX %.*s", zUnique, n, pName->z); + }else{ + /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ + zStmt = 0; + } + + /* Add an entry in sqlite_master for this index */ + sqlite4NestedParse(pParse, + "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", + db->aDb[iDb].zName, SCHEMA_TABLE(iDb), + pIdx->zName, + pIdx->pTable->zName, + pIdx->tnum, + zStmt + ); + sqlite4DbFree(db, zStmt); + + /* Fill the index with data and reparse the schema. Code an OP_Expire + ** to invalidate all pre-compiled statements. + */ + if( pIdx->eIndexType!=SQLITE4_INDEX_UNIQUE ){ + sqlite4RefillIndex(pParse, pIdx, 1); + sqlite4ChangeCookie(pParse, iDb); + sqlite4VdbeAddParseSchemaOp(v, iDb, + sqlite4MPrintf(db, "name='%q' AND type='index'", pIdx->zName)); + sqlite4VdbeAddOp1(v, OP_Expire, 0); + } + } +} + +void sqlite4CreateUsingIndex( + Parse *pParse, /* Parsing context */ + CreateIndex *p, /* First part of CREATE INDEX statement */ + Token *pUsing, /* Token following USING keyword */ + Token *pEnd /* Final '(' in CREATE INDEX */ +){ + sqlite4 *db = pParse->db; + if( p->bUnique ){ + sqlite4ErrorMsg(pParse, "USING %.*s index may not be UNIQUE", + pUsing->n, pUsing->z + ); + }else{ + Index *pIdx = 0; /* New index object */ + Table *pTab; + Token *pIdxName = 0; + char *zIdx = 0; + int iDb = 0; + + pTab = createIndexFindTable(pParse, p, &pIdxName, &zIdx, &iDb); + if( pTab && 0==createIndexAuth(pParse, pTab, zIdx) ){ + char *zExtra = 0; + pIdx = newIndex(pParse, pTab, zIdx, 0, 0, 0, &zExtra); + } + if( pIdx ){ + pIdx->eIndexType = SQLITE4_INDEX_FTS5; + if( db->init.busy ){ + db->flags |= SQLITE4_InternChanges; + pIdx->tnum = db->init.newTnum; + }else{ + createIndexWriteSchema(pParse, pIdx, pIdxName, pEnd); + } + + addIndexToHash(db, pIdx); + + if( pParse->nErr==0 ){ + pIdx->pNext = pTab->pIndex; + pTab->pIndex = pIdx; + }else{ + sqlite4DbFree(db, pIdx); + } + } + + sqlite4DbFree(db, zIdx); + } + + sqlite4SrcListDelete(db, p->pTblName); +} /* ** Create a new index for an SQL table. pName1.pName2 is the name of the index ** and pTblList is the name of the table that is to be indexed. Both will ** be NULL for a primary key or an index that is created to satisfy a @@ -2363,46 +2608,23 @@ /* ** Find the table that is to be indexed. Return early if not found. */ if( pTblName!=0 ){ - - /* Use the two-part index name to determine the database - ** to search for the table. 'Fix' the table name to this db - ** before looking up the table. - */ - assert( !bPrimaryKey ); - assert( pName1 && pName2 ); - iDb = sqlite4TwoPartName(pParse, pName1, pName2, &pName); - if( iDb<0 ) goto exit_create_index; - assert( pName && pName->z ); - -#ifndef SQLITE4_OMIT_TEMPDB - /* If the index name was unqualified, check if the the table - ** is a temp table. If so, set the database to 1. Do not do this - ** if initialising a database schema. - */ - if( !db->init.busy ){ - pTab = sqlite4SrcListLookup(pParse, pTblName); - if( pName2->n==0 && pTab && pTab->pSchema==db->aDb[1].pSchema ){ - iDb = 1; - } - } -#endif - - if( sqlite4FixInit(&sFix, pParse, iDb, "index", pName) && - sqlite4FixSrcList(&sFix, pTblName) - ){ - /* Because the parser constructs pTblName from a single identifier, - ** sqlite4FixSrcList can never fail. */ - assert(0); - } - pTab = sqlite4LocateTable(pParse, 0, pTblName->a[0].zName, - pTblName->a[0].zDatabase); - if( !pTab || db->mallocFailed ) goto exit_create_index; - assert( db->aDb[iDb].pSchema==pTab->pSchema ); + CreateIndex sCreate; + sCreate.bUnique = onError; + sCreate.bIfnotexist = ifNotExist; + sCreate.tCreate = *pStart; + sCreate.tName1 = *pName1; + sCreate.tName2 = *pName2; + sCreate.pTblName = pTblName; + + pTab = createIndexFindTable(pParse, &sCreate, &pName, &zName, &iDb); + if( !pTab ) goto exit_create_index; + }else{ + assert( pName==0 ); assert( pStart==0 ); pTab = pParse->pNewTable; if( !pTab ) goto exit_create_index; iDb = sqlite4SchemaToIndex(db, pTab->pSchema); @@ -2409,99 +2631,32 @@ } pDb = &db->aDb[iDb]; assert( pTab!=0 ); assert( pParse->nErr==0 ); - - /* TODO: We will need to reinstate this block when sqlite_master is - ** modified to use an implicit primary key. */ -#if 0 - if( sqlite4StrNICmp(pTab->zName, "sqlite_", 7)==0 - && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){ - sqlite4ErrorMsg(pParse, "table %s may not be indexed", pTab->zName); - goto exit_create_index; - } -#endif - -#ifndef SQLITE4_OMIT_VIEW - if( pTab->pSelect ){ - assert( !bPrimaryKey ); - sqlite4ErrorMsg(pParse, "views may not be indexed"); - goto exit_create_index; - } -#endif -#ifndef SQLITE4_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - assert( !bPrimaryKey ); - sqlite4ErrorMsg(pParse, "virtual tables may not be indexed"); - goto exit_create_index; - } -#endif - - /* - ** Find the name of the index. Make sure there is not already another - ** index or table with the same name. - ** - ** Exception: If we are reading the names of permanent indices from the - ** sqlite_master table (because some other process changed the schema) and - ** one of the index names collides with the name of a temporary table or - ** index, then we will continue to process this index. - ** - ** If pName==0 it means that we are - ** dealing with a primary key or UNIQUE constraint. We have to invent our - ** own name. - */ - if( pName ){ - assert( !bPrimaryKey ); - zName = sqlite4NameFromToken(db, pName); - if( zName==0 ) goto exit_create_index; - assert( pName->z!=0 ); - if( SQLITE4_OK!=sqlite4CheckObjectName(pParse, zName) ){ - goto exit_create_index; - } - if( !db->init.busy ){ - if( sqlite4FindTable(db, zName, 0)!=0 ){ - sqlite4ErrorMsg(pParse, "there is already a table named %s", zName); - goto exit_create_index; - } - } - if( sqlite4FindIndex(db, zName, pDb->zName)!=0 ){ - if( !ifNotExist ){ - sqlite4ErrorMsg(pParse, "index %s already exists", zName); - }else{ - assert( !db->init.busy ); - sqlite4CodeVerifySchema(pParse, iDb); - } - goto exit_create_index; - } - }else if( !bPrimaryKey ){ - int n; - Index *pLoop; - for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} - zName = sqlite4MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName, n); - }else{ - zName = sqlite4MPrintf(db, "%s", pTab->zName); - } - if( zName==0 ){ - goto exit_create_index; - } - - /* Check for authorization to create an index. - */ -#ifndef SQLITE4_OMIT_AUTHORIZATION - if( bPrimaryKey==0 ){ - const char *zDb = pDb->zName; - if( sqlite4AuthCheck(pParse, SQLITE4_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){ - goto exit_create_index; - } - i = SQLITE4_CREATE_INDEX; - if( !OMIT_TEMPDB && iDb==1 ) i = SQLITE4_CREATE_TEMP_INDEX; - if( sqlite4AuthCheck(pParse, i, zName, pTab->zName, zDb) ){ - goto exit_create_index; - } - } -#endif + assert( IsVirtual(pTab)==0 && IsView(pTab)==0 ); + + /* If pName==0 it means that we are dealing with a primary key or + ** UNIQUE constraint. We have to invent our own name. */ + if( pName==0 ){ + if( !bPrimaryKey ){ + int n; + Index *pLoop; + for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){} + zName = sqlite4MPrintf(db, "sqlite_autoindex_%s_%d", pTab->zName, n); + }else{ + zName = sqlite4MPrintf(db, "%s", pTab->zName); + } + if( zName==0 ){ + goto exit_create_index; + } + } + + /* Check for authorization to create the index. */ + if( bPrimaryKey==0 && createIndexAuth(pParse, pTab, zName) ){ + goto exit_create_index; + } /* If pList==0, it means this routine was called as a result of a PRIMARY ** KEY or UNIQUE constraint attached to the last column added to the table ** under construction. So create a fake list to simulate this. ** @@ -2690,62 +2845,11 @@ ** or UNIQUE constraint of a CREATE TABLE statement. Since the table ** has just been created, it contains no data and the index initialization ** step can be skipped. */ else{ - pIndex->tnum = ++pParse->nMem; - allocateTableNumber(pParse, iDb, pIndex->tnum); - if( bPrimaryKey==0 ){ - Vdbe *v; - char *zStmt; - - v = sqlite4GetVdbe(pParse); - if( v==0 ) goto exit_create_index; - - /* Create the rootpage for the index - */ - sqlite4BeginWriteOperation(pParse, 1, iDb); - - /* Gather the complete text of the CREATE INDEX statement into - ** the zStmt variable - */ - if( pStart ){ - assert( pEnd!=0 ); - /* A named index with an explicit CREATE INDEX statement */ - zStmt = sqlite4MPrintf(db, "CREATE%s INDEX %.*s", - onError==OE_None ? "" : " UNIQUE", - (int)(pEnd->z - pName->z) + 1, - pName->z); - }else{ - /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */ - /* zStmt = sqlite4MPrintf(""); */ - zStmt = 0; - } - - /* Add an entry in sqlite_master for this index - */ - sqlite4NestedParse(pParse, - "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);", - db->aDb[iDb].zName, SCHEMA_TABLE(iDb), - pIndex->zName, - pTab->zName, - pIndex->tnum, - zStmt - ); - sqlite4DbFree(db, zStmt); - - /* Fill the index with data and reparse the schema. Code an OP_Expire - ** to invalidate all pre-compiled statements. - */ - if( pTblName ){ - sqlite4RefillIndex(pParse, pIndex, 1); - sqlite4ChangeCookie(pParse, iDb); - sqlite4VdbeAddParseSchemaOp(v, iDb, - sqlite4MPrintf(db, "name='%q' AND type='index'", pIndex->zName)); - sqlite4VdbeAddOp1(v, OP_Expire, 0); - } - } + createIndexWriteSchema(pParse, pIndex, pName, pEnd); } /* When adding an index to the list of indices for a table, make ** sure all indices labeled OE_Replace come after all those labeled ** OE_Ignore. This is necessary for the correct constraint check @@ -2842,11 +2946,13 @@ sqlite4CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); } pParse->checkSchema = 1; goto exit_drop_index; } - if( pIndex->eIndexType!=SQLITE4_INDEX_USER ){ + if( pIndex->eIndexType!=SQLITE4_INDEX_USER + && pIndex->eIndexType!=SQLITE4_INDEX_FTS5 + ){ sqlite4ErrorMsg(pParse, "index associated with UNIQUE " "or PRIMARY KEY constraint cannot be dropped", 0); goto exit_drop_index; } iDb = sqlite4SchemaToIndex(db, pIndex->pSchema); Index: src/fts5.c ================================================================== --- src/fts5.c +++ src/fts5.c @@ -11,10 +11,13 @@ ************************************************************************* */ #include "sqliteInt.h" +/* +** Default distance value for NEAR operators. +*/ #define FTS5_DEFAULT_NEAR 10 /* ** Token types used by expression parser. */ @@ -41,11 +44,11 @@ void *pCtx; int (*xCreate)(void*, const char**, int, sqlite4_tokenizer**); int (*xTokenize)(void*, sqlite4_tokenizer*, const char*, int, int(*x)(void*, int, const char*, int) ); - int (*xDestroy)(void*, sqlite4_tokenizer *); + int (*xDestroy)(sqlite4_tokenizer *); Fts5Tokenizer *pNext; }; /* ** Expression grammar: @@ -729,11 +732,11 @@ void *pCtx, int (*xCreate)(void*, const char**, int, sqlite4_tokenizer**), int (*xTokenize)(void*, sqlite4_tokenizer*, const char*, int, int(*x)(void*, int, const char*, int) ), - int (*xDestroy)(void*, sqlite4_tokenizer *) + int (*xDestroy)(sqlite4_tokenizer *) ){ int rc = SQLITE4_OK; sqlite4_mutex_enter(db->mutex); /* It is not possible to override an existing tokenizer */ @@ -874,11 +877,11 @@ sqlite4_value **aVal ){ int rc; Fts5Expr *pExpr = 0; Fts5Tokenizer *pTok; - sqlite4_tokenizer *p; + sqlite4_tokenizer *p = 0; sqlite4 *db; const char *zTokenizer; const char *zExpr; const char *zTbl; @@ -935,10 +938,11 @@ sqlite4_result_text(pCtx, zRet, -1, SQLITE4_TRANSIENT); fts5ExpressionFree(db, pExpr); sqlite4_free(sqlite4_db_env(db), zRet); fts5_parse_expr_out: + if( p ) pTok->xDestroy(p); sqlite4DbFree(db, azCol); sqlite4_finalize(pStmt); if( zErr ){ sqlite4_result_error(pCtx, zErr, -1); sqlite4DbFree(db, zErr); Index: src/fts5func.c ================================================================== --- src/fts5func.c +++ src/fts5func.c @@ -21,11 +21,11 @@ ){ *pp = (sqlite4_tokenizer *)pCtx; return SQLITE4_OK; } -static int fts5SimpleDestroy(void *pCtx, sqlite4_tokenizer *p){ +static int fts5SimpleDestroy(sqlite4_tokenizer *p){ return SQLITE4_OK; } static char fts5Tolower(char c){ if( c>='A' && c<='Z' ) c = c + ('a' - 'A'); Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -1085,15 +1085,26 @@ {A = sqlite4ExprListAppend(pParse,0,Y.pExpr);} ///////////////////////////// The CREATE INDEX command /////////////////////// // -cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D) - ON nm(Y) LP idxlist(Z) RP(E). { - sqlite4CreateIndex(pParse, &X, &D, - sqlite4SrcListAppend(pParse->db,0,&Y,0), Z, U, - &S, &E, SQLITE4_SO_ASC, NE, 0); +%type createindex {CreateIndex} +%destructor createindex {sqlite4SrcListDelete(pParse->db, $$.pTblName);} + +createindex(C) ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) + nm(X) dbnm(D) ON nm(Y). { + C.bUnique = U; + C.bIfnotexist = NE; + C.tCreate = S; + C.tName1 = X; + C.tName2 = D; + C.pTblName = sqlite4SrcListAppend(pParse->db, 0, &Y, 0); +} + +cmd ::= createindex(C) LP idxlist(Z) RP(E). { + sqlite4CreateIndex(pParse, &C.tName1, &C.tName2, C.pTblName, Z, + C.bUnique, &C.tCreate, &E, SQLITE4_SO_ASC, C.bIfnotexist, 0); } %type uniqueflag {int} uniqueflag(A) ::= UNIQUE. {A = OE_Abort;} uniqueflag(A) ::= . {A = OE_None;} @@ -1129,10 +1140,14 @@ } %type collate {Token} collate(C) ::= . {C.z = 0; C.n = 0;} collate(C) ::= COLLATE ids(X). {C = X;} + +cmd ::= createindex(C) USING nm(F) LP RP(E). { + sqlite4CreateUsingIndex(pParse, &C, &F, &E); +} ///////////////////////////// The DROP INDEX command ///////////////////////// // cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite4DropIndex(pParse, X, E);} @@ -1359,5 +1374,6 @@ lp ::= LP(X). {sqlite4VtabArgExtend(pParse,&X);} anylist ::= . anylist ::= anylist LP anylist RP. anylist ::= anylist ANY. %endif SQLITE4_OMIT_VIRTUALTABLE + Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -4386,11 +4386,11 @@ int (*xCreate)(void*, const char**, int, sqlite4_tokenizer**), int (*xTokenize)(void*, sqlite4_tokenizer*, const char*, int, int(*x)(void *ctx, int iWeight, const char *zToken, int nToken) ), - int (*xDestroy)(void*, sqlite4_tokenizer *) + int (*xDestroy)(sqlite4_tokenizer *) ); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -549,10 +549,11 @@ typedef struct AggInfoFunc AggInfoFunc; typedef struct AuthContext AuthContext; typedef struct AutoincInfo AutoincInfo; typedef struct CollSeq CollSeq; typedef struct Column Column; +typedef struct CreateIndex CreateIndex; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprListItem ExprListItem; @@ -1435,11 +1436,12 @@ /* Index.eIndexType must be set to one of the following. */ #define SQLITE4_INDEX_USER 0 /* Index created by CREATE INDEX statement */ #define SQLITE4_INDEX_UNIQUE 1 /* Index created by UNIQUE constraint */ #define SQLITE4_INDEX_PRIMARYKEY 2 /* Index is the tables PRIMARY KEY */ -#define SQLITE4_INDEX_TEMP 3 /* Index is an automatic index */ +#define SQLITE4_INDEX_FTS5 3 /* Index is an FTS5 index */ +#define SQLITE4_INDEX_TEMP 4 /* Index is an automatic index */ /* ** Each sample stored in the sqlite_stat3 table is represented in memory ** using a structure of this type. See documentation at the top of the ** analyze.c source file for additional information. @@ -1467,10 +1469,24 @@ */ struct Token { const char *z; /* Text of the token. Not NULL-terminated! */ unsigned int n; /* Number of characters in this token */ }; + +/* +** An instance of this structure holds the results of parsing the first +** part of a CREATE INDEX statement. Instances exist only transiently +** during parsing. +*/ +struct CreateIndex { + int bUnique; /* True if the UNIQUE keyword was present */ + int bIfnotexist; /* True if IF NOT EXISTS was present */ + Token tCreate; /* CREATE token */ + Token tName1; /* First part of two part name */ + Token tName2; /* Second part of two part name */ + SrcList *pTblName; /* Table index is created on */ +}; /* ** One for each column used in source tables. */ struct AggInfoCol { Index: src/vdbecodec.c ================================================================== --- src/vdbecodec.c +++ src/vdbecodec.c @@ -333,10 +333,11 @@ /* ** Enlarge a memory allocation, if necessary */ static int enlargeEncoderAllocation(KeyEncoder *p, int needed){ + assert( p->nOut<=p->nAlloc ); if( p->nOut+needed>p->nAlloc ){ u8 *aNew; p->nAlloc = p->nAlloc + needed + 10; aNew = sqlite4DbRealloc(p->db, p->aOut, p->nAlloc); if( aNew==0 ){ @@ -594,12 +595,12 @@ memcpy(p->aOut+p->nOut, pEnc->z, pEnc->n); p->nOut += pEnc->n; }else{ int nSpc = p->nAlloc-p->nOut; n = pColl->xMkKey(pColl->pUser, pEnc->n, pEnc->z, nSpc, p->aOut+p->nOut); - if( n>nSpc ){ - if( enlargeEncoderAllocation(p, n) ) return SQLITE4_NOMEM; + if( n+1>nSpc ){ + if( enlargeEncoderAllocation(p, n+1) ) return SQLITE4_NOMEM; n = pColl->xMkKey(pColl->pUser, pEnc->n, pEnc->z, n, p->aOut+p->nOut); } p->nOut += n; } p->aOut[p->nOut++] = 0x00; @@ -634,10 +635,11 @@ p->aOut[p->nOut++] = 0x00; } if( sortOrder==SQLITE4_SO_DESC ){ for(i=iStart; inOut; i++) p->aOut[i] ^= 0xff; } + assert( p->nOut<=p->nAlloc ); return SQLITE4_OK; } /* ** Variables aKey/nKey contain an encoded index key. This function returns ADDED test/fts5create.test Index: test/fts5create.test ================================================================== --- /dev/null +++ test/fts5create.test @@ -0,0 +1,37 @@ +# 2012 December 18 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#************************************************************************* +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix fts5create + +do_execsql_test 1.1 { + CREATE TABLE t1(a, b, c); +} + +do_execsql_test 1.2 { + CREATE INDEX ft1 ON t1 USING fts5(); + SELECT * FROM sqlite_master; +} { + table t1 t1 2 "CREATE TABLE t1(a, b, c)" + index ft1 t1 3 "CREATE INDEX ft1 ON t1 USING fts5()" +} + +do_execsql_test 1.3 { + DROP INDEX ft1; + SELECT * FROM sqlite_master; +} { + table t1 t1 2 "CREATE TABLE t1(a, b, c)" +} + + +finish_test Index: test/simple.test ================================================================== --- test/simple.test +++ test/simple.test @@ -98,10 +98,11 @@ #------------------------------------------------------------------------- reset_db +breakpoint do_execsql_test 4.1 { CREATE TABLE t1(k PRIMARY KEY, v) } do_execsql_test 4.2 { CREATE INDEX i1 ON t1(v) } do_execsql_test 4.3 { SELECT * FROM sqlite_master