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

Overview
Comment:Add the "CREATE INDEX idx ON tbl USING nm(...)" syntax.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8ac71062f53c96ddbf9b513da5799b9f5f688cad
User & Date: dan 2012-12-19 20:01:06.214
Context
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
2012-12-18
15:47
Add support for NEAR to the fts expression parser. check-in: b1a2a17679 user: dan tags: trunk
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/build.c.
2303
2304
2305
2306
2307
2308
2309





















































































































































































































































2310
2311
2312
2313
2314
2315
2316
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
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
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
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
2560
2561







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  sqlite4VdbeAddOp2(v, OP_Next, iTab, addr1+1);
  sqlite4VdbeJumpHere(v, addr1);
  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
** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse->pNewTable
** as the table to be indexed.  pParse->pNewTable is a table that is
2361
2362
2363
2364
2365
2366
2367
2368

2369
2370
2371
2372
2373
2374
2375
2376

2377
2378

2379
2380
2381
2382
2383
2384
2385
2386
2387

2388
2389
2390
2391
2392
2393



2394
2395
2396
2397
2398
2399


2400
2401
2402


2403

2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433

2434
2435
2436
2437

2438
2439
2440
2441
2442
2443
2444
2445
2446
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
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488














2489
2490
2491

2492
2493
2494
2495


2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2606
2607
2608
2609
2610
2611
2612

2613








2614


2615









2616






2617
2618
2619






2620
2621



2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635




















2636




2637













2638
2639



2640



































2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654



2655




2656
2657







2658
2659
2660
2661
2662
2663
2664







-
+
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
+
+
-
-
-
+
+

+










-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
-
-
-
-
+
+
-
-
-
-
-
-
-







    goto exit_create_index;
  }

  /*
  ** Find the table that is to be indexed.  Return early if not found.
  */
  if( pTblName!=0 ){

    CreateIndex sCreate;
    /* 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;
    sCreate.bUnique = onError;
    assert( pName && pName->z );

    sCreate.bIfnotexist = ifNotExist;
#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;
    sCreate.tCreate = *pStart;
      }
    }
#endif

    if( sqlite4FixInit(&sFix, pParse, iDb, "index", pName) &&
        sqlite4FixSrcList(&sFix, pTblName)
    sCreate.tName1 = *pName1;
    sCreate.tName2 = *pName2;
    sCreate.pTblName = pTblName;
    ){
      /* Because the parser constructs pTblName from a single identifier,
      ** sqlite4FixSrcList can never fail. */
      assert(0);
    }
    pTab = sqlite4LocateTable(pParse, 0, pTblName->a[0].zName, 

    pTab = createIndexFindTable(pParse, &sCreate, &pName, &zName, &iDb);
        pTblName->a[0].zDatabase);
    if( !pTab || db->mallocFailed ) goto exit_create_index;
    assert( db->aDb[iDb].pSchema==pTab->pSchema );
    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);
  }
  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( IsVirtual(pTab)==0 && IsView(pTab)==0 );
    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
  /* If pName==0 it means that we are dealing with a primary key or 
  ** UNIQUE constraint.  We have to invent our own name.  */
  ** own name.
  */
  if( pName ){
  if( pName==0 ){
    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.
    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.  */
  */
#ifndef SQLITE4_OMIT_AUTHORIZATION
  if( bPrimaryKey==0 ){
  if( bPrimaryKey==0 && createIndexAuth(pParse, pTab, zName) ){
    const char *zDb = pDb->zName;
    if( sqlite4AuthCheck(pParse, SQLITE4_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
      goto exit_create_index;
    }
    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

  /* 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.
  **
  ** TODO: This 'fake list' could be created by the caller to reduce the
  ** number of parameters passed to this function.
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740

2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2843
2844
2845
2846
2847
2848
2849














































2850






2851
2852
2853
2854
2855
2856
2857







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-







  **
  ** If pTblName==0 it means this index is generated as a primary key
  ** 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);
    createIndexWriteSchema(pParse, pIndex, pName, pEnd);
        sqlite4ChangeCookie(pParse, iDb);
        sqlite4VdbeAddParseSchemaOp(v, iDb,
            sqlite4MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
        sqlite4VdbeAddOp1(v, OP_Expire, 0);
      }
    }
  }

  /* 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
  ** processing (in sqlite4GenerateConstraintChecks()) as part of
  ** UPDATE and INSERT statements.  
2840
2841
2842
2843
2844
2845
2846
2847



2848
2849
2850
2851
2852
2853
2854
2944
2945
2946
2947
2948
2949
2950

2951
2952
2953
2954
2955
2956
2957
2958
2959
2960







-
+
+
+







      sqlite4ErrorMsg(pParse, "no such index: %S", pName, 0);
    }else{
      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);
#ifndef SQLITE4_OMIT_AUTHORIZATION
  {
Changes to src/fts5.c.
9
10
11
12
13
14
15



16
17
18
19
20
21
22
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25







+
+
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
*/

#include "sqliteInt.h"

/*
** Default distance value for NEAR operators.
*/
#define FTS5_DEFAULT_NEAR 10

/*
** Token types used by expression parser.
*/
#define TOKEN_EOF       0         /* end of expression - no more tokens */
#define TOKEN_PRIMITIVE 1         /* quoted string or non-keyword */
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
42
43
44
45
46
47
48

49
50
51
52
53
54
55
56







-
+







struct Fts5Tokenizer {
  char *zName;                   /* Name of tokenizer (nul-terminated) */
  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:
**
**   phrase := PRIMITIVE
727
728
729
730
731
732
733
734

735
736
737
738
739
740
741
730
731
732
733
734
735
736

737
738
739
740
741
742
743
744







-
+







  sqlite4 *db,
  const char *zName,
  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 */
  if( fts5FindTokenizer(db, zName) ){
    rc = SQLITE4_ERROR;
872
873
874
875
876
877
878
879

880
881
882
883
884
885
886
875
876
877
878
879
880
881

882
883
884
885
886
887
888
889







-
+







  sqlite4_context *pCtx, 
  int nVal, 
  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;
  char *zErr = 0;
  char *zRet = 0;
933
934
935
936
937
938
939

940
941
942
943
944
945
946
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950







+








  fts5PrintExpr(db, azCol, pExpr, &zRet);
  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);
  }
}
Changes to src/fts5func.c.
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33







-
+







  int nArg, 
  sqlite4_tokenizer **pp
){
  *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');
  return c;
}
Changes to src/parse.y.
1083
1084
1085
1086
1087
1088
1089



1090
1091
1092












1093
1094

1095
1096
1097
1098
1099
1100
1101
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092



1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104


1105
1106
1107
1108
1109
1110
1111
1112







+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+







    {A = sqlite4ExprListAppend(pParse,X,Y.pExpr);}
nexprlist(A) ::= expr(Y).
    {A = sqlite4ExprListAppend(pParse,0,Y.pExpr);}


///////////////////////////// The CREATE INDEX command ///////////////////////
//
%type createindex {CreateIndex}
%destructor createindex {sqlite4SrcListDelete(pParse->db, $$.pTblName);}

cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
        ON nm(Y) LP idxlist(Z) RP(E). {
  sqlite4CreateIndex(pParse, &X, &D, 
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, 
                     sqlite4SrcListAppend(pParse->db,0,&Y,0), Z, U,
                      &S, &E, SQLITE4_SO_ASC, NE, 0);
                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;}

%type idxlist {ExprList*}
1127
1128
1129
1130
1131
1132
1133




1134
1135
1136
1137
1138
1139
1140
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155







+
+
+
+







  sqlite4ExprListCheckLength(pParse, A, "index");
  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);}

///////////////////////////// The PRAGMA command /////////////////////////////
1357
1358
1359
1360
1361
1362
1363

1372
1373
1374
1375
1376
1377
1378
1379







+
vtabargtoken ::= ANY(X).            {sqlite4VtabArgExtend(pParse,&X);}
vtabargtoken ::= lp anylist RP(X).  {sqlite4VtabArgExtend(pParse,&X);}
lp ::= LP(X).                       {sqlite4VtabArgExtend(pParse,&X);}
anylist ::= .
anylist ::= anylist LP anylist RP.
anylist ::= anylist ANY.
%endif  SQLITE4_OMIT_VIRTUALTABLE

Changes to src/sqlite.h.in.
4384
4385
4386
4387
4388
4389
4390
4391

4392
4393
4394
4395
4396
4397
4398
4384
4385
4386
4387
4388
4389
4390

4391
4392
4393
4394
4395
4396
4397
4398







-
+







  const char *zName,
  void *pCtx,
  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.
*/
#ifdef SQLITE4_OMIT_FLOATING_POINT
Changes to src/sqliteInt.h.
547
548
549
550
551
552
553

554
555
556
557
558
559
560
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561







+







typedef struct AggInfo AggInfo;
typedef struct AggInfoCol AggInfoCol;
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;
typedef struct ExprSpan ExprSpan;
typedef struct FKey FKey;
1433
1434
1435
1436
1437
1438
1439

1440

1441
1442
1443
1444
1445
1446
1447
1434
1435
1436
1437
1438
1439
1440
1441

1442
1443
1444
1445
1446
1447
1448
1449







+
-
+







#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 SQLITE4_INDEX_TEMP       3 /* Index is an automatic 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.
*/
struct IndexSample {
1465
1466
1467
1468
1469
1470
1471














1472
1473
1474
1475
1476
1477
1478
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494







+
+
+
+
+
+
+
+
+
+
+
+
+
+







** may contain random values.  Do not make any assumptions about Token.dyn
** and Token.n when Token.z==0.
*/
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 {
  Table *pTab;             /* Source table */
  int iTable;              /* Cursor number of the source table */
Changes to src/vdbecodec.c.
331
332
333
334
335
336
337

338
339
340
341
342
343
344
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345







+







  int nAlloc;    /* Slots of aOut[] allocated */
};

/*
** 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 ){
      sqlite4DbFree(p->db, p->aOut);
      memset(p, 0, sizeof(*p));
592
593
594
595
596
597
598
599
600


601
602
603
604
605
606
607
593
594
595
596
597
598
599


600
601
602
603
604
605
606
607
608







-
-
+
+







    p->aOut[p->nOut++] = 0x24;   /* Text */
    if( pColl==0 || pColl->xMkKey==0 ){
      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;

    /* Release any memory allocated to hold the translated text */
632
633
634
635
636
637
638

639
640
641
642
643
644
645
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647







+







    }
    if( s>1 ) p->aOut[p->nOut++] = 0x80 | t;
    p->aOut[p->nOut++] = 0x00;
  }
  if( sortOrder==SQLITE4_SO_DESC ){
    for(i=iStart; i<p->nOut; i++) p->aOut[i] ^= 0xff;
  }
  assert( p->nOut<=p->nAlloc );
  return SQLITE4_OK;
}

/*
** Variables aKey/nKey contain an encoded index key. This function returns
** the length (in bytes) of the key with all but the first nField fields
** removed.
Added test/fts5create.test.





































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
Changes to test/simple.test.
96
97
98
99
100
101
102

103
104
105
106
107
108
109
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110







+







#
do_execsql_test 3.3 { INSERT INTO t1 VALUES('one', '111') } {}


#-------------------------------------------------------------------------
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
} {
  table t1 t1 2 {CREATE TABLE t1(k PRIMARY KEY, v)}