SQLite4
Check-in [e0748900db]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add "tokenizer=xxx" syntax to fts5.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e0748900dbc6221cf5d483799c969fe07c75e397
User & Date: dan 2012-12-20 18:41:03
Context
2012-12-21
19:58
Add update function to fts5.c. check-in: 49eff5d82d user: dan tags: trunk
2012-12-20
18:41
Add "tokenizer=xxx" syntax to fts5. check-in: e0748900db user: dan tags: trunk
2012-12-19
20:01
Add the "CREATE INDEX idx ON tbl USING nm(...)" syntax. check-in: 8ac71062f5 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/build.c.

2413
2414
2415
2416
2417
2418
2419





2420
2421
2422
2423
2424
2425
2426
....
2442
2443
2444
2445
2446
2447
2448






2449
2450
2451
2452
2453


2454
2455
2456
2457
2458
2459
2460
....
2504
2505
2506
2507
2508
2509
2510

2511
2512
2513
2514
2515
2516
2517
....
2521
2522
2523
2524
2525
2526
2527

2528
2529
2530
2531


2532




2533
2534
2535




2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552

2553
2554
2555
2556
2557
2558
2559
  *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;
................................................................................

  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;
................................................................................
    }
  }
}

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
................................................................................
    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







>
>
>
>
>







 







>
>
>
>
>
>

|
|
<
<
>
>







 







>







 







>

|
<
|
>
>
|
>
>
>
>



>
>
>
>



|
<

<
<
<
<
|
<
<
<



>







2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
....
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462


2463
2464
2465
2466
2467
2468
2469
2470
2471
....
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
....
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542

2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561

2562




2563



2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
  *ppIdxName = pName;
  *pzIdxName = zName;
  *piDb = iDb;
  return pTab;
}

#ifndef SQLITE4_OMIT_AUTHORIZATION
/*
** Check for authorization to create index zIdx on table pTab. If 
** authorization is granted, return zero. Otherwise, return non-zero
** and leave an error in pParse.
*/
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;
................................................................................

  return 0;
}
#else
# define createIndexAuth(a, b, c) 0
#endif // SQLITE4_OMIT_AUTHORIZATION

/*
** This function is called when parsing a CREATE statement executed by the
** user (not when parsing the schema of an existing database). It generates
** the VDBE code to assign a root page number to the new index and, if 
** required, to write a new entry into the sqlite_master table.
*/
static void createIndexWriteSchema(
  Parse *pParse,                  /* Parser context */
  Index *pIdx,                    /* New index object */


  Token *pName,                   /* Token containing name of new index */
  Token *pEnd                     /* Token for final closing paren of CREATE */
){
  sqlite4 *db = pParse->db;
  int iDb;

  assert( db->init.busy==0 );
  iDb = sqlite4SchemaToIndex(db, pIdx->pSchema);
  pIdx->tnum = ++pParse->nMem;
................................................................................
    }
  }
}

void sqlite4CreateUsingIndex(
  Parse *pParse,                  /* Parsing context */
  CreateIndex *p,                 /* First part of CREATE INDEX statement */
  ExprList *pList,
  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
................................................................................
    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) ){
      int nExtra = sqlite4Fts5IndexSz();
      char *zExtra = 0;
      pIdx = newIndex(pParse, pTab, zIdx, 0, 0, nExtra, &zExtra);

      if( pIdx ){
        pIdx->pFts = (Fts5Index *)zExtra;
        sqlite4Fts5IndexInit(pParse, pIdx, pList);
        pIdx->eIndexType = SQLITE4_INDEX_FTS5;
      }
    }

    if( pIdx ){
      if( db->init.busy ){
        db->flags |= SQLITE4_InternChanges;
        pIdx->tnum = db->init.newTnum;
        pIdx->pNext = pTab->pIndex;
        pTab->pIndex = pIdx;
        addIndexToHash(db, pIdx);
        pIdx = 0;
      }else{
        createIndexWriteSchema(pParse, pIdx, pIdxName, pEnd);
      }
    }






    sqlite4DbFree(db, pIdx);



    sqlite4DbFree(db, zIdx);
  }

  sqlite4ExprListDelete(db, pList);
  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

Changes to src/fts5.c.

46
47
48
49
50
51
52











53
54
55
56
57
58
59
...
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
...
709
710
711
712
713
714
715


























716
717
718
719
720
721
722
...
762
763
764
765
766
767
768






































































769
770
771
772
773
774
775
  int (*xTokenize)(void*, sqlite4_tokenizer*,
      const char*, int, int(*x)(void*, int, const char*, int)
  );
  int (*xDestroy)(sqlite4_tokenizer *);
  Fts5Tokenizer *pNext;
};












/*
** Expression grammar:
**
**   phrase := PRIMITIVE
**   phrase := PRIMITIVE *
**   phrase := phrase + phrase
**   phrase := phrase NEAR phrase
................................................................................
    if( !aNew ) return SQLITE4_NOMEM;
    memset(&aNew[pPhrase->nStr], 0, nIncr*sizeof(Fts5Str));
    pPhrase->aStr = aNew;
  }
  if( pPhrase->nStr>0 ){
    if( ((pPhrase->nStr-1) % nIncr)==0 ){
      int *aNew;
      aNew = (Fts5Str *)sqlite4DbRealloc(pParse->db, 
        pPhrase->aiNear, (pPhrase->nStr+nIncr-1)*sizeof(int)
      );
      if( !aNew ) return SQLITE4_NOMEM;
      pPhrase->aiNear = aNew;
    }
    pPhrase->aiNear[pPhrase->nStr-1] = nNear;
  }
................................................................................
static Fts5Tokenizer *fts5FindTokenizer(sqlite4 *db, const char *zName){
  Fts5Tokenizer *p;
  for(p=db->pTokenizer; p; p=p->pNext){
    if( 0==sqlite4StrICmp(zName, p->zName) ) break;
  }
  return p;
}



























void sqlite4ShutdownFts5(sqlite4 *db){
  Fts5Tokenizer *p;
  Fts5Tokenizer *pNext;
  for(p=db->pTokenizer; p; p=pNext){
    pNext = p->pNext;
    sqlite4DbFree(db, p);
................................................................................
    }
  }

  rc = sqlite4ApiExit(db, rc);
  sqlite4_mutex_leave(db->mutex);
  return rc;
}







































































/**************************************************************************
***************************************************************************
** Below this point is test code.
*/
#ifdef SQLITE4_TEST
static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **);







>
>
>
>
>
>
>
>
>
>
>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
...
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
...
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  int (*xTokenize)(void*, sqlite4_tokenizer*,
      const char*, int, int(*x)(void*, int, const char*, int)
  );
  int (*xDestroy)(sqlite4_tokenizer *);
  Fts5Tokenizer *pNext;
};

/*
** FTS5 specific index data.
**
** This object is part of a database schema, so it may be shared between
** multiple connections.
*/
struct Fts5Index {
  int nTokenizer;                 /* Elements in azTokenizer[] array */
  char **azTokenizer;             /* Name and arguments for tokenizer */
};

/*
** Expression grammar:
**
**   phrase := PRIMITIVE
**   phrase := PRIMITIVE *
**   phrase := phrase + phrase
**   phrase := phrase NEAR phrase
................................................................................
    if( !aNew ) return SQLITE4_NOMEM;
    memset(&aNew[pPhrase->nStr], 0, nIncr*sizeof(Fts5Str));
    pPhrase->aStr = aNew;
  }
  if( pPhrase->nStr>0 ){
    if( ((pPhrase->nStr-1) % nIncr)==0 ){
      int *aNew;
      aNew = (int *)sqlite4DbRealloc(pParse->db, 
        pPhrase->aiNear, (pPhrase->nStr+nIncr-1)*sizeof(int)
      );
      if( !aNew ) return SQLITE4_NOMEM;
      pPhrase->aiNear = aNew;
    }
    pPhrase->aiNear[pPhrase->nStr-1] = nNear;
  }
................................................................................
static Fts5Tokenizer *fts5FindTokenizer(sqlite4 *db, const char *zName){
  Fts5Tokenizer *p;
  for(p=db->pTokenizer; p; p=p->pNext){
    if( 0==sqlite4StrICmp(zName, p->zName) ) break;
  }
  return p;
}

static void fts5TokenizerCreate(
  Parse *pParse, 
  Fts5Index *pFts, 
  Fts5Tokenizer **ppTokenizer,
  sqlite4_tokenizer **pp
){
  Fts5Tokenizer *pTok;
  char *zTok = pFts->azTokenizer[0];

  *ppTokenizer = pTok = fts5FindTokenizer(pParse->db, zTok);
  if( !pTok ){
    sqlite4ErrorMsg(pParse, "no such tokenizer: \"%s\"", zTok);
  }else{
    const char **azArg = (const char **)&pFts->azTokenizer[1];
    int rc = pTok->xCreate(pTok->pCtx, azArg, pFts->nTokenizer-1, pp);
    if( rc!=SQLITE4_OK ){
      assert( *pp==0 );
      sqlite4ErrorMsg(pParse, "error creating tokenizer");
    }
  }
}

static void fts5TokenizerDestroy(Fts5Tokenizer *pTok, sqlite4_tokenizer *p){
  if( p ) pTok->xDestroy(p);
}

void sqlite4ShutdownFts5(sqlite4 *db){
  Fts5Tokenizer *p;
  Fts5Tokenizer *pNext;
  for(p=db->pTokenizer; p; p=pNext){
    pNext = p->pNext;
    sqlite4DbFree(db, p);
................................................................................
    }
  }

  rc = sqlite4ApiExit(db, rc);
  sqlite4_mutex_leave(db->mutex);
  return rc;
}

/*
** Return the size of an Fts5Index structure, in bytes.
*/
int sqlite4Fts5IndexSz(void){ 
  return sizeof(Fts5Index); 
}

/*
** Initialize the fts5 specific part of the index object passed as the
** second argument.
*/
void sqlite4Fts5IndexInit(Parse *pParse, Index *pIdx, ExprList *pArgs){
  Fts5Index *pFts = pIdx->pFts;

  if( pArgs ){
    int i;
    for(i=0; pParse->nErr==0 && i<pArgs->nExpr; i++){
      char *zArg = pArgs->a[i].zName;
      char *zVal = pArgs->a[i].pExpr->u.zToken;

      if( zArg && sqlite4StrICmp(zArg, "tokenizer")==0 ){
        /* zVal is the name of the tokenizer to use. Any subsequent arguments
         ** that do not contain assignment operators (=) are also passed to
         ** the tokenizer. Figure out how many bytes of space are required for
         ** all.  */
        int j;
        char *pSpace;
        int nByte = sqlite4Strlen30(zVal) + 1;
        for(j=i+1; j<pArgs->nExpr; j++){
          ExprListItem *pItem = &pArgs->a[j];
          if( pItem->zName ) break;
          nByte += sqlite4Strlen30(pItem->pExpr->u.zToken) + 1;
        }
        nByte += sizeof(char *) * (j-i);
        pFts->azTokenizer = (char **)sqlite4DbMallocZero(pParse->db, nByte);
        if( pFts->azTokenizer==0 ) return;

        pSpace = (char *)&pFts->azTokenizer[j-i];
        for(j=i; j<pArgs->nExpr; j++){
          ExprListItem *pItem = &pArgs->a[j];
          if( pItem->zName && j>i ){
            break;
          }else{
            int nToken = sqlite4Strlen30(pItem->pExpr->u.zToken);
            memcpy(pSpace, pItem->pExpr->u.zToken, nToken+1);
            pFts->azTokenizer[j-i] = pSpace;
            pSpace += nToken+1;
          }
        }

        /* If this function is being called as part of a CREATE INDEX statement
        ** issued by the user (to create a new index) check if the tokenizer
        ** is valid. If not, return an error. Do not do this if this function
        ** is being called as part of parsing an existing database schema.
        */
        if( pParse->db->init.busy==0 ){
          Fts5Tokenizer *pTok = 0;
          sqlite4_tokenizer *t = 0;

          fts5TokenizerCreate(pParse, pFts, &pTok, &t);
          fts5TokenizerDestroy(pTok, t);
        }
      }
      else{
        sqlite4ErrorMsg(pParse,"unrecognized argument: \"%s\"", zArg?zArg:zVal);
      }
    }
  }
}

/**************************************************************************
***************************************************************************
** Below this point is test code.
*/
#ifdef SQLITE4_TEST
static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **);

Changes to src/parse.y.

1139
1140
1141
1142
1143
1144
1145





1146
1147


















1148
1149
1150
1151
1152
1153
1154
  if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
}

%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);}








>
>
>
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
  if( A ) A->a[A->nExpr-1].sortOrder = (u8)Z;
}

%type collate {Token}
collate(C) ::= .                 {C.z = 0; C.n = 0;}
collate(C) ::= COLLATE ids(X).   {C = X;}

%type uidxlist_opt {ExprList*}
%destructor uidxlist_opt {sqlite4ExprListDelete(pParse->db, $$);}
%type uidxlist {ExprList*}
%destructor uidxlist {sqlite4ExprListDelete(pParse->db, $$);}

cmd ::= createindex(C) USING nm(F) LP uidxlist_opt(U) RP(E). {
  sqlite4CreateUsingIndex(pParse, &C, U, &F, &E);
}

uidxlist_opt(A) ::= uidxlist(B). { A = B; }
uidxlist_opt(A) ::= .            { A = 0; }

uidxlist(A) ::= uidxlist(Z) COMMA ids(X) EQ ids(Y). {
  A = sqlite4ExprListAppend(pParse, Z, sqlite4PExpr(pParse, @Y, 0, 0, &Y));
  sqlite4ExprListSetName(pParse, A, &X, 1);
}
uidxlist(A) ::= uidxlist(Z) COMMA ids(Y). {
  A = sqlite4ExprListAppend(pParse, Z, sqlite4PExpr(pParse, @Y, 0, 0, &Y));
}
uidxlist(A) ::= ids(X) EQ ids(Y). {
  A = sqlite4ExprListAppend(pParse, 0, sqlite4PExpr(pParse, @Y, 0, 0, &Y));
  sqlite4ExprListSetName(pParse, A, &X, 1);
}
uidxlist(A) ::= ids(Y). {
  A = sqlite4ExprListAppend(pParse, 0, sqlite4PExpr(pParse, @Y, 0, 0, &Y));
}


///////////////////////////// The DROP INDEX command /////////////////////////
//
cmd ::= DROP INDEX ifexists(E) fullname(X).   {sqlite4DropIndex(pParse, X, E);}

Changes to src/sqliteInt.h.

559
560
561
562
563
564
565

566
567
568
569
570
571
572
....
1428
1429
1430
1431
1432
1433
1434

1435
1436
1437
1438
1439
1440
1441
....
3244
3245
3246
3247
3248
3249
3250

3251



3252
typedef struct ExprListItem ExprListItem;
typedef struct ExprSpan ExprSpan;
typedef struct FKey FKey;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
typedef struct FuncDefTable FuncDefTable;
typedef struct Fts5Tokenizer Fts5Tokenizer;

typedef struct IdList IdList;
typedef struct IdListItem IdListItem;
typedef struct Index Index;
typedef struct IndexSample IndexSample;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
................................................................................
  u8 *aSortOrder;  /* Array of size Index.nColumn. True==DESC, False==ASC */
  char **azColl;   /* Array of collation sequence names for index */
#ifdef SQLITE4_ENABLE_STAT3
  int nSample;             /* Number of elements in aSample[] */
  tRowcnt avgEq;           /* Average nEq value for key values not in aSample */
  IndexSample *aSample;    /* Samples of the left-most key */
#endif

};

/* 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_FTS5       3 /* Index is an FTS5 index */
................................................................................
#define MEMTYPE_LOOKASIDE  0x02  /* Might have been lookaside memory */
#define MEMTYPE_SCRATCH    0x04  /* Scratch allocations */
#define MEMTYPE_DB         0x10  /* Uses sqlite4DbMalloc, not sqlite_malloc */

int sqlite4InitFts5(sqlite4 *db);
int sqlite4InitFts5Func(sqlite4 *db);
void sqlite4ShutdownFts5(sqlite4 *db);





#endif /* _SQLITEINT_H_ */







>







 







>







 







>

>
>
>

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
....
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
typedef struct ExprListItem ExprListItem;
typedef struct ExprSpan ExprSpan;
typedef struct FKey FKey;
typedef struct FuncDestructor FuncDestructor;
typedef struct FuncDef FuncDef;
typedef struct FuncDefTable FuncDefTable;
typedef struct Fts5Tokenizer Fts5Tokenizer;
typedef struct Fts5Index Fts5Index;
typedef struct IdList IdList;
typedef struct IdListItem IdListItem;
typedef struct Index Index;
typedef struct IndexSample IndexSample;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
................................................................................
  u8 *aSortOrder;  /* Array of size Index.nColumn. True==DESC, False==ASC */
  char **azColl;   /* Array of collation sequence names for index */
#ifdef SQLITE4_ENABLE_STAT3
  int nSample;             /* Number of elements in aSample[] */
  tRowcnt avgEq;           /* Average nEq value for key values not in aSample */
  IndexSample *aSample;    /* Samples of the left-most key */
#endif
  Fts5Index *pFts; /* Fts5 data (or NULL if this is not an fts index) */
};

/* 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_FTS5       3 /* Index is an FTS5 index */
................................................................................
#define MEMTYPE_LOOKASIDE  0x02  /* Might have been lookaside memory */
#define MEMTYPE_SCRATCH    0x04  /* Scratch allocations */
#define MEMTYPE_DB         0x10  /* Uses sqlite4DbMalloc, not sqlite_malloc */

int sqlite4InitFts5(sqlite4 *db);
int sqlite4InitFts5Func(sqlite4 *db);
void sqlite4ShutdownFts5(sqlite4 *db);
void sqlite4CreateUsingIndex(Parse*, CreateIndex*, ExprList*, Token*, Token*);

int sqlite4Fts5IndexSz(void);
void sqlite4Fts5IndexInit(Parse *pParse, Index *pIdx, ExprList *pArgs);

#endif /* _SQLITEINT_H_ */

Changes to test/fts5create.test.

29
30
31
32
33
34
35






36






































37



do_execsql_test 1.3 { 
  DROP INDEX ft1;
  SELECT * FROM sqlite_master;
} {
  table t1 t1  2 "CREATE TABLE t1(a, b, c)"
}














































finish_test










>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
do_execsql_test 1.3 { 
  DROP INDEX ft1;
  SELECT * FROM sqlite_master;
} {
  table t1 t1  2 "CREATE TABLE t1(a, b, c)"
}

do_execsql_test 1.4 { 
  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.5 { 
  DROP INDEX ft1;
  SELECT * FROM sqlite_master;
} {
  table t1 t1  2 "CREATE TABLE t1(a, b, c)"
}

do_catchsql_test 1.6 { 
  CREATE UNIQUE INDEX ft2 ON t1 USING fts5();
} {1 {USING fts5 index may not be UNIQUE}}

#-------------------------------------------------------------------------
#
reset_db

do_execsql_test 2.1 {
  CREATE TABLE t2(x, y);
  CREATE INDEX fulltext ON t2 USING fts5(tokenizer=simple);
  SELECT * FROM sqlite_master;
} {
  table t2 t2 2 {CREATE TABLE t2(x, y)}
  index fulltext t2 3 {CREATE INDEX fulltext ON t2 USING fts5(tokenizer=simple)}
}

do_catchsql_test 2.2 { 
  DROP INDEX fulltext; 
  CREATE INDEX ft ON t2 USING fts5(tukenizer=simple);
} {1 {unrecognized argument: "tukenizer"}}

do_catchsql_test 2.3 { 
  CREATE INDEX ft ON t2 USING fts5("a b c");
} {1 {unrecognized argument: "a b c"}}

do_catchsql_test 2.4 { 
  CREATE INDEX ft ON t2 USING fts5(tokenizer="nosuch");
} {1 {no such tokenizer: "nosuch"}}

finish_test