SQLite

Check-in [ed49f297bc]
Login

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

Overview
Comment:Experimental integration of schemalint functionality with the shell tool. Does not work yet.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | schemalint
Files: files | file ages | folders
SHA1: ed49f297bcee86674ed673e195610b8cc1d35647
User & Date: dan 2016-02-11 21:01:16.253
Context
2016-02-15
20:12
Progress towards integrating schemalint into the shell tool. Some cases work now. (check-in: 58d4cf26e1 user: dan tags: schemalint)
2016-02-11
21:01
Experimental integration of schemalint functionality with the shell tool. Does not work yet. (check-in: ed49f297bc user: dan tags: schemalint)
2016-02-09
15:10
Merge latest trunk changes with this branch. (check-in: 1a4182eedd user: dan tags: schemalint)
Changes
Unified Diff Ignore Whitespace Patch
Changes to main.mk.
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
#
all:	sqlite3.h libsqlite3.a sqlite3$(EXE)

libsqlite3.a:	$(LIBOBJ)
	$(AR) libsqlite3.a $(LIBOBJ)
	$(RANLIB) libsqlite3.a

sqlite3$(EXE):	$(TOP)/src/shell.c libsqlite3.a sqlite3.h
	$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
		$(TOP)/src/shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)

sqldiff$(EXE):	$(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
	$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
		$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)








|







468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
#
all:	sqlite3.h libsqlite3.a sqlite3$(EXE)

libsqlite3.a:	$(LIBOBJ)
	$(AR) libsqlite3.a $(LIBOBJ)
	$(RANLIB) libsqlite3.a

sqlite3$(EXE):	$(TOP)/src/shell.c libsqlite3.a sqlite3.h $(TOP)/src/shell_indexes.c
	$(TCCX) $(READLINE_FLAGS) -o sqlite3$(EXE) $(SHELL_OPT) \
		$(TOP)/src/shell.c libsqlite3.a $(LIBREADLINE) $(TLIBS) $(THREADLIB)

sqldiff$(EXE):	$(TOP)/tool/sqldiff.c sqlite3.c sqlite3.h
	$(TCCX) -o sqldiff$(EXE) -DSQLITE_THREADSAFE=0 \
		$(TOP)/tool/sqldiff.c sqlite3.c $(TLIBS) $(THREADLIB)

Changes to src/main.c.
788
789
790
791
792
793
794







795
796
797
798
799
800
801
    case SQLITE_DBCONFIG_LOOKASIDE: {
      void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
      int sz = va_arg(ap, int);       /* IMP: R-47871-25994 */
      int cnt = va_arg(ap, int);      /* IMP: R-04460-53386 */
      rc = setupLookaside(db, pBuf, sz, cnt);
      break;
    }







    default: {
      static const struct {
        int op;      /* The opcode */
        u32 mask;    /* Mask of the bit in sqlite3.flags to set/clear */
      } aFlagOp[] = {
        { SQLITE_DBCONFIG_ENABLE_FKEY,    SQLITE_ForeignKeys    },
        { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger  },







>
>
>
>
>
>
>







788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
    case SQLITE_DBCONFIG_LOOKASIDE: {
      void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
      int sz = va_arg(ap, int);       /* IMP: R-47871-25994 */
      int cnt = va_arg(ap, int);      /* IMP: R-04460-53386 */
      rc = setupLookaside(db, pBuf, sz, cnt);
      break;
    }
#ifdef SQLITE_SCHEMA_LINT
    case SQLITE_DBCONFIG_WHEREINFO: {
      db->xWhereInfo = va_arg(ap, void(*)(void*, int, const char*, int, i64));
      db->pWhereInfoCtx = va_arg(ap, void*);
      break;
    }
#endif
    default: {
      static const struct {
        int op;      /* The opcode */
        u32 mask;    /* Mask of the bit in sqlite3.flags to set/clear */
      } aFlagOp[] = {
        { SQLITE_DBCONFIG_ENABLE_FKEY,    SQLITE_ForeignKeys    },
        { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger  },
Changes to src/pragma.c.
1044
1045
1046
1047
1048
1049
1050

1051
1052
1053
1054
1055
1056
1057
  ** the returned data set are:
  **
  ** cid:        Column id (numbered from left to right, starting at 0)
  ** name:       Column name
  ** type:       Column declaration type.
  ** notnull:    True if 'NOT NULL' is part of column declaration
  ** dflt_value: The default value for the column, if any.

  */
  case PragTyp_TABLE_INFO: if( zRight ){
    Table *pTab;
    pTab = sqlite3FindTable(db, zRight, zDb);
    if( pTab ){
      static const char *azCol[] = {
         "cid", "name", "type", "notnull", "dflt_value", "pk"







>







1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
  ** the returned data set are:
  **
  ** cid:        Column id (numbered from left to right, starting at 0)
  ** name:       Column name
  ** type:       Column declaration type.
  ** notnull:    True if 'NOT NULL' is part of column declaration
  ** dflt_value: The default value for the column, if any.
  ** pk:         Non-zero for PK fields.
  */
  case PragTyp_TABLE_INFO: if( zRight ){
    Table *pTab;
    pTab = sqlite3FindTable(db, zRight, zDb);
    if( pTab ){
      static const char *azCol[] = {
         "cid", "name", "type", "notnull", "dflt_value", "pk"
Changes to src/shell.c.
152
153
154
155
156
157
158

159
160
161
162
163
164
165
  _setmode(_fileno(out), _O_TEXT);
}
#else
# define setBinaryMode(X)
# define setTextMode(X)
#endif



/* True if the timer is enabled */
static int enableTimer = 0;

/* Return the current wall-clock time */
static sqlite3_int64 timeOfDay(void){
  static sqlite3_vfs *clockVfs = 0;







>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  _setmode(_fileno(out), _O_TEXT);
}
#else
# define setBinaryMode(X)
# define setTextMode(X)
#endif

#include "shell_indexes.c"

/* True if the timer is enabled */
static int enableTimer = 0;

/* Return the current wall-clock time */
static sqlite3_int64 timeOfDay(void){
  static sqlite3_vfs *clockVfs = 0;
588
589
590
591
592
593
594
595

596
597
598
599
600
601
602
** State information about the database connection is contained in an
** instance of the following structure.
*/
typedef struct ShellState ShellState;
struct ShellState {
  sqlite3 *db;           /* The database */
  int echoOn;            /* True to echo input commands */
  int autoEQP;           /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */

  int statsOn;           /* True to display memory stats before each finalize */
  int scanstatsOn;       /* True to display scan stats before each finalize */
  int countChanges;      /* True to display change counts */
  int backslashOn;       /* Resolve C-style \x escapes in SQL input text */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  FILE *out;             /* Write results here */







|
>







589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
** State information about the database connection is contained in an
** instance of the following structure.
*/
typedef struct ShellState ShellState;
struct ShellState {
  sqlite3 *db;           /* The database */
  int echoOn;            /* True to echo input commands */
  int autoEQP;           /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */
  int bRecommend;        /* Instead of sqlite3_exec(), recommend indexes */
  int statsOn;           /* True to display memory stats before each finalize */
  int scanstatsOn;       /* True to display scan stats before each finalize */
  int countChanges;      /* True to display change counts */
  int backslashOn;       /* Resolve C-style \x escapes in SQL input text */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  FILE *out;             /* Write results here */
1539
1540
1541
1542
1543
1544
1545













1546
1547
1548
1549
1550
1551
1552
*/
static void explain_data_delete(ShellState *p){
  sqlite3_free(p->aiIndent);
  p->aiIndent = 0;
  p->nIndent = 0;
  p->iIndent = 0;
}














/*
** Execute a statement or set of statements.  Print 
** any result rows/columns depending on the current mode 
** set via the supplied callback.
**
** This is very similar to SQLite's built-in sqlite3_exec() 







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







1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
*/
static void explain_data_delete(ShellState *p){
  sqlite3_free(p->aiIndent);
  p->aiIndent = 0;
  p->nIndent = 0;
  p->iIndent = 0;
}

typedef struct RecCommandCtx RecCommandCtx;
struct RecCommandCtx {
  int (*xCallback)(void*,int,char**,char**,int*);
  ShellState *pArg;
};

static void recCommandOut(void *pCtx, const char *zLine){
  const char *zCol = "output";
  RecCommandCtx *p = (RecCommandCtx*)pCtx;
  int t = SQLITE_TEXT;
  p->xCallback(p->pArg, 1, (char**)&zLine, (char**)&zCol, &t);
}

/*
** Execute a statement or set of statements.  Print 
** any result rows/columns depending on the current mode 
** set via the supplied callback.
**
** This is very similar to SQLite's built-in sqlite3_exec() 
1565
1566
1567
1568
1569
1570
1571







1572
1573
1574
1575
1576
1577
1578
  int rc = SQLITE_OK;             /* Return Code */
  int rc2;
  const char *zLeftover;          /* Tail of unprocessed SQL */

  if( pzErrMsg ){
    *pzErrMsg = NULL;
  }








  while( zSql[0] && (SQLITE_OK == rc) ){
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
    if( SQLITE_OK != rc ){
      if( pzErrMsg ){
        *pzErrMsg = save_err_msg(db);
      }







>
>
>
>
>
>
>







1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
  int rc = SQLITE_OK;             /* Return Code */
  int rc2;
  const char *zLeftover;          /* Tail of unprocessed SQL */

  if( pzErrMsg ){
    *pzErrMsg = NULL;
  }

  if( pArg->bRecommend ){
    RecCommandCtx ctx;
    ctx.xCallback = xCallback;
    ctx.pArg = pArg;
    rc = shellIndexesCommand(db, zSql, recCommandOut, &ctx, pzErrMsg);
  }else

  while( zSql[0] && (SQLITE_OK == rc) ){
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
    if( SQLITE_OK != rc ){
      if( pzErrMsg ){
        *pzErrMsg = save_err_msg(db);
      }
3605
3606
3607
3608
3609
3610
3611









3612
3613
3614
3615
3616
3617
3618
    }else{
      utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
      rc = 1;
    }
    sqlite3_close(pSrc);
  }else











  if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
    if( nArg==2 ){
      p->scanstatsOn = booleanValue(azArg[1]);
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
      raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
#endif







>
>
>
>
>
>
>
>
>







3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
    }else{
      utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
      rc = 1;
    }
    sqlite3_close(pSrc);
  }else

  if( c=='r' && n>=2 && strncmp(azArg[0], "recommend", n)==0 ){
    if( nArg==2 ){
      p->bRecommend = booleanValue(azArg[1]);
    }else{
      raw_printf(stderr, "Usage: .recommend on|off\n");
      rc = 1;
    }
  }else


  if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
    if( nArg==2 ){
      p->scanstatsOn = booleanValue(azArg[1]);
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
      raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
#endif
4899
4900
4901
4902
4903
4904
4905



4906
4907
4908
4909
4910
4911
4912
          utf8_printf(stderr,"Error: %s\n", zErrMsg);
          if( bail_on_error ) return rc!=0 ? rc : 1;
        }else if( rc!=0 ){
          utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
          if( bail_on_error ) return rc;
        }
      }



    }else{
      utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
      raw_printf(stderr,"Use -help for a list of options.\n");
      return 1;
    }
  }








>
>
>







4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
          utf8_printf(stderr,"Error: %s\n", zErrMsg);
          if( bail_on_error ) return rc!=0 ? rc : 1;
        }else if( rc!=0 ){
          utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
          if( bail_on_error ) return rc;
        }
      }

    }else if( strcmp(z, "-recommend") ){
      data.bRecommend = 1;
    }else{
      utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
      raw_printf(stderr,"Use -help for a list of options.\n");
      return 1;
    }
  }

Added src/shell_indexes.c.








































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
/*
** 2016 February 10
**
** 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.
**
*************************************************************************
*/

typedef sqlite3_int64 i64;

typedef struct IdxConstraint IdxConstraint;
typedef struct IdxContext IdxContext;
typedef struct IdxScan IdxScan;
typedef struct IdxWhere IdxWhere;

/*
** A single constraint. Equivalent to either "col = ?" or "col < ?".
**
** pLink:
**   ... todo ...
*/
struct IdxConstraint {
  char *zColl;                    /* Collation sequence */
  int bRange;                     /* True for range, false for eq */
  int iCol;                       /* Constrained table column */
  i64 depmask;                    /* Dependency mask */
  IdxConstraint *pNext;           /* Next constraint in pEq or pRange list */
  IdxConstraint *pLink;           /* See above */
};

/*
** A WHERE clause. Made up of IdxConstraint objects.
**
**   a=? AND b=? AND (c=? OR d=?) AND (e=? OR f=?)
**
*/
struct IdxWhere {
  IdxConstraint *pEq;             /* List of == constraints */
  IdxConstraint *pRange;          /* List of < constraints */
  IdxWhere **apOr;                /* Array of OR branches (joined by pNextOr) */
  IdxWhere *pNextOr;              /* Next in OR'd terms */
  IdxWhere *pParent;              /* Parent object (or NULL) */
};

/*
** A single scan of a single table.
*/
struct IdxScan {
  char *zTable;                   /* Name of table to scan */
  int iDb;                        /* Database containing table zTable */
  i64 covering;                   /* Mask of columns required for cov. index */
  IdxConstraint *pOrder;          /* ORDER BY columns */
  IdxWhere where;                 /* WHERE Constraints */
  IdxScan *pNextScan;             /* Next IdxScan object for same query */
};

/*
** Context object passed to idxWhereInfo()
*/
struct IdxContext {
  IdxWhere *pCurrent;             /* Current where clause */
  IdxScan *pScan;                 /* List of scan objects */
  sqlite3 *dbm;                   /* In-memory db for this analysis */
  int rc;                         /* Error code (if error has occurred) */
};

typedef struct PragmaTable PragmaTable;
typedef struct PragmaCursor PragmaCursor;

struct PragmaTable {
  sqlite3_vtab base;
  sqlite3 *db;
};

struct PragmaCursor {
  sqlite3_vtab_cursor base;
  sqlite3_stmt *pStmt;
  i64 iRowid;
};

/*
** Connect to or create a pragma virtual table.
*/
static int pragmaConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  const char *zSchema = 
    "CREATE TABLE a(tbl HIDDEN, cid, name, type, notnull, dflt_value, pk)";
  PragmaTable *pTab = 0;
  int rc = SQLITE_OK;

  rc = sqlite3_declare_vtab(db, zSchema);
  if( rc==SQLITE_OK ){
    pTab = (PragmaTable *)sqlite3_malloc64(sizeof(PragmaTable));
    if( pTab==0 ) rc = SQLITE_NOMEM;
  }

  assert( rc==SQLITE_OK || pTab==0 );
  if( rc==SQLITE_OK ){
    memset(pTab, 0, sizeof(PragmaTable));
    pTab->db = db;
  }

  *ppVtab = (sqlite3_vtab*)pTab;
  return rc;
}

/*
** Disconnect from or destroy a pragma virtual table.
*/
static int pragmaDisconnect(sqlite3_vtab *pVtab){
  sqlite3_free(pVtab);
  return SQLITE_OK;
}

/*
** xBestIndex method for pragma virtual tables.
*/
static int pragmaBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
  int i;

  pIdxInfo->estimatedCost = 1.0e6;  /* Initial cost estimate */

  /* Look for a valid tbl=? constraint. */
  for(i=0; i<pIdxInfo->nConstraint; i++){
    if( pIdxInfo->aConstraint[i].usable==0 ) continue;
    if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
    if( pIdxInfo->aConstraint[i].iColumn!=0 ) continue;
    pIdxInfo->idxNum = 1;
    pIdxInfo->estimatedCost = 1.0;
    pIdxInfo->aConstraintUsage[i].argvIndex = 1;
    pIdxInfo->aConstraintUsage[i].omit = 1;
    break;
  }
  if( i==pIdxInfo->nConstraint ){
    tab->zErrMsg = sqlite3_mprintf("missing required tbl=? constraint");
    return SQLITE_ERROR;
  }
  return SQLITE_OK;
}

/*
** Open a new pragma cursor.
*/
static int pragmaOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
  PragmaTable *pTab = (PragmaTable *)pVTab;
  PragmaCursor *pCsr;

  pCsr = (PragmaCursor*)sqlite3_malloc64(sizeof(PragmaCursor));
  if( pCsr==0 ){
    return SQLITE_NOMEM;
  }else{
    memset(pCsr, 0, sizeof(PragmaCursor));
    pCsr->base.pVtab = pVTab;
  }

  *ppCursor = (sqlite3_vtab_cursor*)pCsr;
  return SQLITE_OK;
}

/*
** Move a statvfs cursor to the next entry in the file.
*/
static int pragmaNext(sqlite3_vtab_cursor *pCursor){
  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
  int rc = SQLITE_OK;

  if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){
    rc = sqlite3_finalize(pCsr->pStmt);
    pCsr->pStmt = 0;
  }
  pCsr->iRowid++;
  return rc;
}

static int pragmaEof(sqlite3_vtab_cursor *pCursor){
  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
  return pCsr->pStmt==0;
}

static int pragmaFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  PragmaCursor *pCsr = (PragmaCursor*)pCursor;
  PragmaTable *pTab = (PragmaTable*)(pCursor->pVtab);
  char *zSql;
  const char *zTbl;
  int rc = SQLITE_OK;

  if( pCsr->pStmt ){
    sqlite3_finalize(pCsr->pStmt);
    pCsr->pStmt = 0;
  }
  pCsr->iRowid = 0;

  assert( argc==1 );
  zTbl = (const char*)sqlite3_value_text(argv[0]);
  zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", zTbl);
  if( zSql==0 ){
    rc = SQLITE_NOMEM;
  }else{
    rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
  }
  return pragmaNext(pCursor);;
}

/*
** xColumn method.
*/
static int pragmaColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int iCol
){
  PragmaCursor *pCsr = (PragmaCursor *)pCursor;
  if( iCol>0 ){
    sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, iCol-1));
  }
  return SQLITE_OK;
}

static int pragmaRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
  PragmaCursor *pCsr = (PragmaCursor *)pCursor;
  *pRowid = pCsr->iRowid;
  return SQLITE_OK;
}

static int registerPragmaVtabs(sqlite3 *db){
  static sqlite3_module pragma_module = {
    0,                            /* iVersion */
    pragmaConnect,                /* xCreate */
    pragmaConnect,                /* xConnect */
    pragmaBestIndex,              /* xBestIndex */
    pragmaDisconnect,             /* xDisconnect */
    pragmaDisconnect,             /* xDestroy */
    pragmaOpen,                   /* xOpen - open a cursor */
    pragmaClose,                  /* xClose - close a cursor */
    pragmaFilter,                 /* xFilter - configure scan constraints */
    pragmaNext,                   /* xNext - advance a cursor */
    pragmaEof,                    /* xEof - check for end of scan */
    pragmaColumn,                 /* xColumn - read data */
    pragmaRowid,                  /* xRowid - read data */
    0,                            /* xUpdate */
    0,                            /* xBegin */
    0,                            /* xSync */
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindMethod */
    0,                            /* xRename */
  };
  return sqlite3_create_module(db, "pragma_table_info", &pragma_module, 0);
}

/*
** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc(). 
** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
*/
static void *idxMalloc(int *pRc, int nByte){
  void *pRet;
  assert( *pRc==SQLITE_OK );
  assert( nByte>0 );
  pRet = sqlite3_malloc(nByte);
  if( pRet ){
    memset(pRet, 0, nByte);
  }else{
    *pRc = SQLITE_NOMEM;
  }
  return pRet;
}

/*
** Allocate and return a new IdxConstraint object. Set the IdxConstraint.zColl
** variable to point to a copy of nul-terminated string zColl.
*/
static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){
  IdxConstraint *pNew;
  int nColl = strlen(zColl);

  assert( *pRc==SQLITE_OK );
  pNew = (IdxConstraint*)idxMalloc(pRc, sizeof(IdxConstraint) * nColl + 1);
  if( pNew ){
    pNew->zColl = (char*)&pNew[1];
    memcpy(pNew->zColl, zColl, nColl+1);
  }
  return pNew;
}

/*
** SQLITE_DBCONFIG_WHEREINFO callback.
*/
static void idxWhereInfo(
  void *pCtx,                     /* Pointer to IdxContext structure */
  int eOp, 
  const char *zVal, 
  int iVal, 
  i64 mask
){
  IdxContext *p = (IdxContext*)pCtx;

#if 1
  const char *zOp = 
    eOp==SQLITE_WHEREINFO_TABLE ? "TABLE" :
    eOp==SQLITE_WHEREINFO_EQUALS ? "EQUALS" :
    eOp==SQLITE_WHEREINFO_RANGE ? "RANGE" :
    eOp==SQLITE_WHEREINFO_ORDERBY ? "ORDERBY" :
    eOp==SQLITE_WHEREINFO_NEXTOR ? "NEXTOR" :
    eOp==SQLITE_WHEREINFO_ENDOR ? "ENDOR" :
    eOp==SQLITE_WHEREINFO_BEGINOR ? "BEGINOR" :
    "!error!";
  printf("op=%s zVal=%s iVal=%d mask=%llx\n", zOp, zVal, iVal, mask);
#endif

  if( p->rc==SQLITE_OK ){
    assert( eOp==SQLITE_WHEREINFO_TABLE || p->pScan!=0 );
    switch( eOp ){
      case SQLITE_WHEREINFO_TABLE: {
        int nVal = strlen(zVal);
        IdxScan *pNew = (IdxScan*)idxMalloc(&p->rc, sizeof(IdxScan) + nVal + 1);
        if( !pNew ) return;
        pNew->zTable = (char*)&pNew[1];
        memcpy(pNew->zTable, zVal, nVal+1);
        pNew->pNextScan = p->pScan;
        pNew->covering = mask;
        p->pScan = pNew;
        p->pCurrent = &pNew->where;
        break;
      }

      case SQLITE_WHEREINFO_ORDERBY: {
        IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal);
        IdxConstraint **pp;
        if( pNew==0 ) return;
        pNew->iCol = iVal;
        for(pp=&p->pScan->pOrder; *pp; pp=&(*pp)->pNext);
        *pp = pNew;
        break;
      }

      case SQLITE_WHEREINFO_EQUALS:
      case SQLITE_WHEREINFO_RANGE: {
        IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal);
        if( pNew==0 ) return;
        pNew->iCol = iVal;
        pNew->depmask = mask;

        if( eOp==SQLITE_WHEREINFO_RANGE ){
          pNew->pNext = p->pCurrent->pRange;
          p->pCurrent->pRange = pNew;
        }else{
          pNew->pNext = p->pCurrent->pEq;
          p->pCurrent->pEq = pNew;
        }
        break;
      }

      case SQLITE_WHEREINFO_BEGINOR: {
        assert( 0 );
        break;
      }
      case SQLITE_WHEREINFO_ENDOR: {
        assert( 0 );
        break;
      }
      case SQLITE_WHEREINFO_NEXTOR: {
        assert( 0 );
        break;
      }
    }
  }
}

/*
** An error associated with database handle db has just occurred. Pass
** the error message to callback function xOut.
*/
static void idxDatabaseError(
  sqlite3 *db,                    /* Database handle */
  char **pzErrmsg                 /* Write error here */
){
  *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}

static int idxCreateTables(sqlite3 *db, sqlite3 *dbm, IdxScan *pScan){
  int rc = SQLITE_OK;
  IdxScan *pIter;
  for(pIter=pScan; pIter; pIter=pIter->pNextScan){
  }
}

static void idxScanFree(IdxScan *pScan){
}

/*
** The xOut callback is invoked to return command output to the user. The
** second argument is always a nul-terminated string. The first argument is
** passed zero if the string contains normal output or non-zero if it is an
** error message.
*/
int shellIndexesCommand(
  sqlite3 *db,                         /* Database handle */
  const char *zSql,                    /* SQL to find indexes for */
  void (*xOut)(void*, const char*),    /* Output callback */
  void *pOutCtx,                       /* Context for xOut() */
  char **pzErrmsg                      /* OUT: Error message (sqlite3_malloc) */
){
  int rc = SQLITE_OK;
  sqlite3 *dbm = 0;
  IdxContext ctx;
  sqlite3_stmt *pStmt = 0;        /* Statement compiled from zSql */

  memset(&ctx, 0, sizeof(IdxContext));

  /* Open an in-memory database to work with. The main in-memory 
  ** database schema contains tables similar to those in the users 
  ** database (handle db). The attached in-memory db (aux) contains
  ** application tables used by the code in this file.  */
  rc = sqlite3_open(":memory:", &dbm);
  if( rc==SQLITE_OK ){
    rc = sqlite3_exec(dbm, 
        "ATTACH ':memory:' AS aux;"
        "CREATE TABLE aux.depmask(mask PRIMARY KEY) WITHOUT ROWID;"
        , 0, 0, 0
    );
  }
  if( rc!=SQLITE_OK ){
    idxDatabaseError(dbm, pzErrmsg);
    goto indexes_out;
  }

  /* Analyze the SELECT statement in zSql. */
  ctx.dbm = dbm;
  sqlite3_db_config(db, SQLITE_DBCONFIG_WHEREINFO, idxWhereInfo, (void*)&ctx);
  rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
  sqlite3_db_config(db, SQLITE_DBCONFIG_WHEREINFO, (void*)0, (void*)0);
  if( rc!=SQLITE_OK ){
    idxDatabaseError(db, pzErrmsg);
    goto indexes_out;
  }

  /* Create tables within the main in-memory database. These tables
  ** have the same names, columns and declared types as the tables in
  ** the user database. All constraints except for PRIMARY KEY are
  ** removed. */
  rc = idxCreateTables(db, dbm, ctx.pScan);
  if( rc!=SQLITE_OK ){
    goto indexes_out;
  }

  /* Create candidate indexes within the in-memory database file */

 indexes_out:
  idxScanFree(ctx.pScan);
  sqlite3_close(dbm);
  return rc;
}


Changes to src/sqlite.h.in.
1905
1906
1907
1908
1909
1910
1911









1912
1913
1914
1915
1916
1917
1918
** which case the trigger setting is not reported back. </dd>
**
** </dl>
*/
#define SQLITE_DBCONFIG_LOOKASIDE       1001  /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY     1002  /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER  1003  /* int int* */











/*
** CAPI3REF: Enable Or Disable Extended Result Codes
** METHOD: sqlite3
**
** ^The sqlite3_extended_result_codes() routine enables or disables the







>
>
>
>
>
>
>
>
>







1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
** which case the trigger setting is not reported back. </dd>
**
** </dl>
*/
#define SQLITE_DBCONFIG_LOOKASIDE       1001  /* void* int int */
#define SQLITE_DBCONFIG_ENABLE_FKEY     1002  /* int int* */
#define SQLITE_DBCONFIG_ENABLE_TRIGGER  1003  /* int int* */
#define SQLITE_DBCONFIG_WHEREINFO       1004  /* xWhereInfo void* */

#define SQLITE_WHEREINFO_TABLE   1
#define SQLITE_WHEREINFO_EQUALS  2
#define SQLITE_WHEREINFO_RANGE   3
#define SQLITE_WHEREINFO_ORDERBY 4
#define SQLITE_WHEREINFO_BEGINOR 5
#define SQLITE_WHEREINFO_ENDOR   6
#define SQLITE_WHEREINFO_NEXTOR  7


/*
** CAPI3REF: Enable Or Disable Extended Result Codes
** METHOD: sqlite3
**
** ^The sqlite3_extended_result_codes() routine enables or disables the
Changes to src/sqliteInt.h.
1269
1270
1271
1272
1273
1274
1275




1276
1277
1278
1279
1280
1281
1282
  sqlite3 *pUnlockConnection;           /* Connection to watch for unlock */
  void *pUnlockArg;                     /* Argument to xUnlockNotify */
  void (*xUnlockNotify)(void **, int);  /* Unlock notify callback */
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif
#ifdef SQLITE_USER_AUTHENTICATION
  sqlite3_userauth auth;        /* User authentication information */




#endif
};

/*
** A macro to discover the encoding of a database.
*/
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)







>
>
>
>







1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
  sqlite3 *pUnlockConnection;           /* Connection to watch for unlock */
  void *pUnlockArg;                     /* Argument to xUnlockNotify */
  void (*xUnlockNotify)(void **, int);  /* Unlock notify callback */
  sqlite3 *pNextBlocked;        /* Next in list of all blocked connections */
#endif
#ifdef SQLITE_USER_AUTHENTICATION
  sqlite3_userauth auth;        /* User authentication information */
#endif
#ifdef SQLITE_SCHEMA_LINT
  void (*xWhereInfo)(void*, int, const char*, int, i64);
  void *pWhereInfoCtx;
#endif
};

/*
** A macro to discover the encoding of a database.
*/
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
Changes to src/where.c.
3902
3903
3904
3905
3906
3907
3908
3909

3910
3911
3912
3913
3914
3915
3916
3917
3918
3919



3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933

3934



3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998





3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019

4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065

4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078

4079
4080
4081
4082
4083
4084
4085
#endif
    return 1;
  }
  return 0;
}

#ifdef SQLITE_SCHEMA_LINT
static char *whereAppendPrintf(sqlite3 *db, const char *zFmt, ...){

  va_list ap;
  char *zRes = 0;
  va_start(ap, zFmt);
  zRes = sqlite3_vmprintf(zFmt, ap);
  if( zRes==0 ){
    db->mallocFailed = 1;
  }else if( db->mallocFailed ){
    sqlite3_free(zRes);
    zRes = 0;
  }



  va_end(ap);
  return zRes;
}

/*
** Append a representation of term pTerm to the string in zIn and return
** the result. Or, if an OOM occurs, free zIn and return a NULL pointer.
*/
static char *whereAppendSingleTerm(
  Parse *pParse,
  Table *pTab,
  int iCol,
  int bOr,
  char *zIn,

  WhereTerm *pTerm



){
  char *zBuf;
  sqlite3 *db = pParse->db;
  Expr *pX = pTerm->pExpr;
  CollSeq *pColl;
  const char *zOp = 0;

  if( pTerm->eOperator & (WO_IS|WO_EQ|WO_IN) ){
    zOp = "eq";
  }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GE|WO_GT) ){
    zOp = "range";
  }
  pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);

  if( zOp ){
    const char *zFmt = bOr ? "%z{{%s \"%w\" \"%w\" %lld}}" :
                             "%z{%s \"%w\" \"%w\" %lld}";
    zBuf = whereAppendPrintf(db, zFmt, zIn, 
        zOp, pTab->aCol[iCol].zName, 
        (pColl ? pColl->zName : "BINARY"),
        pTerm->prereqRight
    );
  }else{
    zBuf = zIn;
  }

  return zBuf;
}

static char *whereTraceWC(
  Parse *pParse, 
  int bInitialSpace,
  struct SrcList_item *pItem,
  char *zIn,
  WhereClause *pWC
){
  sqlite3 *db = pParse->db;
  Table *pTab = pItem->pTab;
  char *zBuf = zIn;
  int iCol;
  int ii;
  int bFirst = !bInitialSpace;
  int bOr = (pWC->op==TK_OR);

  /* List of WO_SINGLE constraints */
  for(iCol=0; iCol<pTab->nCol; iCol++){
    int opMask = WO_SINGLE; 
    WhereScan scan;
    WhereTerm *pTerm;
    for(pTerm=whereScanInit(&scan, pWC, pItem->iCursor, iCol, opMask, 0);
        pTerm;
        pTerm=whereScanNext(&scan)
    ){
      /* assert( iCol==pTerm->u.leftColumn ); */
      if( bFirst==0 ) zBuf = whereAppendPrintf(db, "%z ", zBuf);
      zBuf = whereAppendSingleTerm(pParse, pTab, iCol, bOr, zBuf, pTerm);
      bFirst = 0;
    }
  }

  /* Add composite - (WO_OR|WO_AND) - constraints */
  for(ii=0; ii<pWC->nTerm; ii++){
    WhereTerm *pTerm = &pWC->a[ii];
    if( pTerm->eOperator & (WO_OR|WO_AND) ){





      const char *zFmt = ((pTerm->eOperator&WO_OR) ? "%z%s{or " : "%z%s{");
      zBuf = whereAppendPrintf(db, zFmt, zBuf, bFirst ? "" : " ");
      zBuf = whereTraceWC(pParse, 0, pItem, zBuf, &pTerm->u.pOrInfo->wc);
      zBuf = whereAppendPrintf(db, "%z}", zBuf);
      bFirst = 0;
    }
  }

  return zBuf;
}

static void whereTraceBuilder(
  Parse *pParse,
  WhereLoopBuilder *p
){
  sqlite3 *db = pParse->db;
  if( db->xTrace ){
    ExprList *pOrderBy = p->pOrderBy;
    WhereInfo *pWInfo = p->pWInfo;
    int nTablist = pWInfo->pTabList->nSrc;
    int ii;


    /* Loop through each element of the FROM clause. Ignore any sub-selects
    ** or views. Invoke the xTrace() callback once for each real table. */
    for(ii=0; ii<nTablist; ii++){
      char *zBuf = 0;
      int iCol;
      int nCol;
      Table *pTab;

      struct SrcList_item *pItem = &pWInfo->pTabList->a[ii];
      if( pItem->pSelect ) continue;
      pTab = pItem->pTab;
      nCol = pTab->nCol;

      /* Append the table name to the buffer. */
      zBuf = whereAppendPrintf(db, "\"%w\"", pTab->zName);

      /* Append the list of columns required to create a covering index */
      zBuf = whereAppendPrintf(db, "%z {cols", zBuf);
      if( 0==(pItem->colUsed & ((u64)1 << (sizeof(Bitmask)*8-1))) ){
        for(iCol=0; iCol<nCol; iCol++){
          if( iCol==(sizeof(Bitmask)*8-1) ) break;
          if( pItem->colUsed & ((u64)1 << iCol) ){
            const char *zName = pTab->aCol[iCol].zName;
            zBuf = whereAppendPrintf(db, "%z \"%w\"", zBuf, zName);
          }
        }
      }
      zBuf = whereAppendPrintf(db, "%z}",zBuf);

      /* Append the contents of WHERE clause */
      zBuf = whereTraceWC(pParse, 1, pItem, zBuf, p->pWC);

      /* Append the ORDER BY clause, if any */
      if( pOrderBy ){
        int i;
        int bFirst = 1;
        for(i=0; i<pOrderBy->nExpr; i++){
          Expr *pExpr = pOrderBy->a[i].pExpr; 
          CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);

          pExpr = sqlite3ExprSkipCollate(pExpr);
          if( pExpr->op==TK_COLUMN && pExpr->iTable==pItem->iCursor ){
            if( pExpr->iColumn>=0 ){
              const char *zName = pTab->aCol[pExpr->iColumn].zName;
              zBuf = whereAppendPrintf(db, "%z%s\"%w\" \"%w\" %s", zBuf,

                  bFirst ? " {orderby " : " ", zName, pColl->zName,
                  (pOrderBy->a[i].sortOrder ? "DESC" : "ASC")
              );
              bFirst = 0;
            }
          }
        }
        if( bFirst==0 ) zBuf = whereAppendPrintf(db, "%z}", zBuf);
      }

      /* Pass the buffer to the xTrace() callback, then free it */
      db->xTrace(db->pTraceArg, zBuf);
      sqlite3DbFree(db, zBuf);

    }
  }
}
#else
# define whereTraceBuilder(x,y)
#endif








|
>
|
|
<
|
|
<
<
|
|
<
>
>
>
|
<
|
|
<
<
<
<
<
<
|
<
|
<
>
|
>
>
>
|
|
<
|
|
<
<
|
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
|
|
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|


|
>
>
>
>
>
|
|
|
<



|
<
|






|
|
|
<

>


|
<
<
<
<
|
|
|
|
|
|

|
<
|
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
|
|
|
|
|
|
|
<
|
|
|
<
<
>
|
<
<
<
|
|
|
<
|

|
|
<
>







3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912

3913
3914


3915
3916

3917
3918
3919
3920

3921
3922






3923

3924

3925
3926
3927
3928
3929
3930
3931

3932
3933


3934





3935








3936
3937
3938
3939


3940



























3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956

3957
3958
3959
3960

3961
3962
3963
3964
3965
3966
3967
3968
3969
3970

3971
3972
3973
3974
3975




3976
3977
3978
3979
3980
3981
3982
3983

3984










3985





3986
3987
3988
3989
3990
3991
3992

3993
3994
3995


3996
3997



3998
3999
4000

4001
4002
4003
4004

4005
4006
4007
4008
4009
4010
4011
4012
#endif
    return 1;
  }
  return 0;
}

#ifdef SQLITE_SCHEMA_LINT
static void whereTraceWC(
  Parse *pParse, 
  struct SrcList_item *pItem,
  WhereClause *pWC,

  int bOr
){


  sqlite3 *db = pParse->db;
  Table *pTab = pItem->pTab;

  void (*x)(void*, int, const char*, int, i64) = db->xWhereInfo;
  void *pCtx = db->pWhereInfoCtx;
  int bFirst = 1;                 /* True until first callback is made */
  int ii;


  /* Issue callbacks for WO_SINGLE constraints */






  for(ii=0; ii<pTab->nCol; ii++){

    int opMask = WO_SINGLE; 

    WhereScan scan;
    WhereTerm *pTerm;
    for(pTerm=whereScanInit(&scan, pWC, pItem->iCursor, ii, opMask, 0);
        pTerm;
        pTerm=whereScanNext(&scan)
    ){
      int eOp;

      Expr *pX = pTerm->pExpr;
      CollSeq *pC = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);


      if( pTerm->eOperator & (WO_IS|WO_EQ|WO_IN) ){





        eOp = SQLITE_WHEREINFO_EQUALS;








      }else{
        eOp = SQLITE_WHEREINFO_RANGE;
      }
      if( bOr && !bFirst ) x(pCtx, SQLITE_WHEREINFO_NEXTOR, 0, 0, 0);


      x(pCtx, eOp, (pC ? pC->zName : "BINARY"), ii, pTerm->prereqRight);



























      bFirst = 0;
    }
  }

  /* Callbacks for composite - (WO_OR|WO_AND) - constraints */
  for(ii=0; ii<pWC->nTerm; ii++){
    WhereTerm *pTerm = &pWC->a[ii];
    if( pTerm->eOperator & WO_OR ){
      assert( bOr==0 );
      x(pCtx, SQLITE_WHEREINFO_BEGINOR, 0, 0, 0);
      whereTraceWC(pParse, pItem, &pTerm->u.pOrInfo->wc, 1);
      x(pCtx, SQLITE_WHEREINFO_ENDOR, 0, 0, 0);
    }
    if( pTerm->eOperator & WO_AND ){
      if( bOr && !bFirst ) x(pCtx, SQLITE_WHEREINFO_NEXTOR, 0, 0, 0);
      whereTraceWC(pParse, pItem, &pTerm->u.pAndInfo->wc, 0);

      bFirst = 0;
    }
  }
}



static void whereTraceBuilder(
  Parse *pParse,
  WhereLoopBuilder *p
){
  sqlite3 *db = pParse->db;
  if( db->xWhereInfo && db->init.busy==0 ){
    void (*x)(void*, int, const char*, int, i64) = db->xWhereInfo;
    void *pCtx = db->pWhereInfoCtx;

    int ii;
    int nTab = p->pWInfo->pTabList->nSrc;

    /* Loop through each element of the FROM clause. Ignore any sub-selects
    ** or views. Invoke the xWhereInfo() callback multiple times for each




    ** real table.  */
    for(ii=0; ii<p->pWInfo->pTabList->nSrc; ii++){
      struct SrcList_item *pItem = &p->pWInfo->pTabList->a[ii];
      if( pItem->pSelect==0 ){
        Table *pTab = pItem->pTab;
        int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);

        /* Table name callback */

        x(pCtx, SQLITE_WHEREINFO_TABLE, pTab->zName, iDb, pItem->colUsed);
















        /* ORDER BY callbacks */
        if( p->pOrderBy ){
          int i;
          int bFirst = 1;
          for(i=0; i<p->pOrderBy->nExpr; i++){
            Expr *pExpr = p->pOrderBy->a[i].pExpr; 
            CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);

            pExpr = sqlite3ExprSkipCollate(pExpr);
            if( pExpr->op==TK_COLUMN && pExpr->iTable==pItem->iCursor ){
              int iCol = pExpr->iColumn;


              if( iCol>=0 ){
                x(pCtx, SQLITE_WHEREINFO_ORDERBY, pColl->zName, iCol, 0); 



              }
            }
          }

        }

        /* WHERE callbacks */
        whereTraceWC(pParse, pItem, p->pWC, 0);

      }
    }
  }
}
#else
# define whereTraceBuilder(x,y)
#endif