SQLite

Check-in [45c59802f6]
Login

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

Overview
Comment:Testing coverage enhancements to sqlite3_get_table() and to the SELECT code generator. (CVS 4746)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 45c59802f6d35c7745b96c578ab43d5a336fe822
User & Date: drh 2008-01-23 14:51:49.000
Context
2008-01-23
15:44
Fix a couple of segfaults that could occur after a malloc() failure in the SQL compiler. (CVS 4747) (check-in: 6bd8db3839 user: danielk1977 tags: trunk)
14:51
Testing coverage enhancements to sqlite3_get_table() and to the SELECT code generator. (CVS 4746) (check-in: 45c59802f6 user: drh tags: trunk)
12:52
Improvements to test coverage in the lemon-generated parser and in the sqlite3_get_table() interface. (CVS 4745) (check-in: 9f95d79dae user: drh tags: trunk)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to src/expr.c.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22







-
+







**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
**
** $Id: expr.c,v 1.351 2008/01/19 23:50:26 drh Exp $
** $Id: expr.c,v 1.352 2008/01/23 14:51:49 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Return the 'affinity' of the expression pExpr if any.
**
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929

2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941

2942
2943
2944
2945
2946
2947


2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2918
2919
2920
2921
2922
2923
2924



2925

2926

2927

2928
2929
2930
2931
2932
2933
2934
2935

2936
2937
2938

2939


2940
2941
2942
2943

2944
2945
2946
2947
2948
2949
2950







-
-
-

-
+
-

-








-
+


-

-
-
+
+


-







/*
** Analyze the given expression looking for aggregate functions and
** for variables that need to be added to the pParse->aAgg[] array.
** Make additional entries to the pParse->aAgg[] array as necessary.
**
** This routine should only be called after the expression has been
** analyzed by sqlite3ExprResolveNames().
**
** If errors are seen, leave an error message in zErrMsg and return
** the number of errors.
*/
int sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
  int nErr = pNC->pParse->nErr;
  walkExprTree(pExpr, analyzeAggregate, pNC);
  return pNC->pParse->nErr - nErr;
}

/*
** Call sqlite3ExprAnalyzeAggregates() for every expression in an
** expression list.  Return the number of errors.
**
** If an error is found, the analysis is cut short.
*/
int sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){
void sqlite3ExprAnalyzeAggList(NameContext *pNC, ExprList *pList){
  struct ExprList_item *pItem;
  int i;
  int nErr = 0;
  if( pList ){
    for(pItem=pList->a, i=0; nErr==0 && i<pList->nExpr; i++, pItem++){
      nErr += sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr);
    for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
      sqlite3ExprAnalyzeAggregates(pNC, pItem->pExpr);
    }
  }
  return nErr;
}

/*
** Allocate or deallocate temporary use registers during code generation.
*/
int sqlite3GetTempReg(Parse *pParse){
  if( pParse->nTempReg ){
Changes to src/select.c.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22







-
+







**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.406 2008/01/19 03:35:59 drh Exp $
** $Id: select.c,v 1.407 2008/01/23 14:51:50 drh Exp $
*/
#include "sqliteInt.h"


/*
** Delete all the content of a Select structure but do not deallocate
** the select structure itself.
498
499
500
501
502
503
504
505

506
507
508
509
510
511
512
498
499
500
501
502
503
504

505
506
507
508
509
510
511
512







-
+







** of a SELECT.
**
** If srcTab and nColumn are both zero, then the pEList expressions
** are evaluated in order to get the data for this row.  If nColumn>0
** then data is pulled from srcTab and pEList is used only to get the
** datatypes for each column.
*/
static int selectInnerLoop(
static void selectInnerLoop(
  Parse *pParse,          /* The parser context */
  Select *p,              /* The complete select statement being coded */
  ExprList *pEList,       /* List of values being extracted */
  int srcTab,             /* Pull data from this table */
  int nColumn,            /* Number of columns in the source table */
  ExprList *pOrderBy,     /* If not NULL, sort results using this key */
  int distinct,           /* If >=0, make sure results are distinct */
520
521
522
523
524
525
526
527

528
529
530
531
532
533
534
520
521
522
523
524
525
526

527
528
529
530
531
532
533
534







-
+







  int hasDistinct;        /* True if the DISTINCT keyword is present */
  int regResult;              /* Start of memory holding result set */
  int eDest = pDest->eDest;   /* How to dispose of results */
  int iParm = pDest->iParm;   /* First argument to disposal method */
  int nResultCol;             /* Number of result columns */
  int nToFree;                /* Number of result columns to release */

  if( v==0 ) return 0;
  if( v==0 ) return;
  assert( pEList!=0 );

  /* If there was a LIMIT clause on the SELECT statement, then do the check
  ** to see if this row should be output.
  */
  hasDistinct = distinct>=0 && pEList->nExpr>0;
  if( pOrderBy==0 && !hasDistinct ){
573
574
575
576
577
578
579
580

581
582
583
584
585
586
587
573
574
575
576
577
578
579

580
581
582
583
584
585
586
587







-
+







    codeDistinct(pParse, distinct, iContinue, nColumn, regResult);
    if( pOrderBy==0 ){
      codeOffset(v, p, iContinue);
    }
  }

  if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
    return 0;
    return;
  }

  switch( eDest ){
    /* In this mode, write each query result to the key of the temporary
    ** table iParm.
    */
#ifndef SQLITE_OMIT_COMPOUND_SELECT
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
718
719
720
721
722
723
724

725
726
727
728
729
730
731







-







  /* Jump to the end of the loop if the LIMIT is reached.
  */
  if( p->iLimit>=0 && pOrderBy==0 ){
    sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1);
    sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak);
  }
  sqlite3ReleaseTempRange(pParse, regResult, nToFree);
  return 0;
}

/*
** Given an expression list, generate a KeyInfo structure that records
** the collating sequence for each expression in that expression list.
**
** If the ExprList is an ORDER BY or GROUP BY clause then the resulting
2053
2054
2055
2056
2057
2058
2059
2060
2061


2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2052
2053
2054
2055
2056
2057
2058


2059
2060




2061
2062
2063
2064
2065
2066
2067







-
-
+
+
-
-
-
-







          generateColumnNames(pParse, 0, pFirst->pEList);
        }
        iBreak = sqlite3VdbeMakeLabel(v);
        iCont = sqlite3VdbeMakeLabel(v);
        computeLimitRegisters(pParse, p, iBreak);
        sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak);
        iStart = sqlite3VdbeCurrentAddr(v);
        rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
                             pOrderBy, -1, &dest, iCont, iBreak, 0);
        selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
                        pOrderBy, -1, &dest, iCont, iBreak, 0);
        if( rc ){
          rc = 1;
          goto multi_select_end;
        }
        sqlite3VdbeResolveLabel(v, iCont);
        sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart);
        sqlite3VdbeResolveLabel(v, iBreak);
        sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
      }
      break;
    }
2137
2138
2139
2140
2141
2142
2143
2144
2145


2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2132
2133
2134
2135
2136
2137
2138


2139
2140




2141
2142
2143
2144
2145
2146
2147







-
-
+
+
-
-
-
-







      iCont = sqlite3VdbeMakeLabel(v);
      computeLimitRegisters(pParse, p, iBreak);
      sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak);
      r1 = sqlite3GetTempReg(pParse);
      iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1);
      sqlite3VdbeAddOp3(v, OP_NotFound, tab2, iCont, r1);
      sqlite3ReleaseTempReg(pParse, r1);
      rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
                             pOrderBy, -1, &dest, iCont, iBreak, 0);
      selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
                      pOrderBy, -1, &dest, iCont, iBreak, 0);
      if( rc ){
        rc = 1;
        goto multi_select_end;
      }
      sqlite3VdbeResolveLabel(v, iCont);
      sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart);
      sqlite3VdbeResolveLabel(v, iBreak);
      sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
      sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
      break;
    }
2915
2916
2917
2918
2919
2920
2921
2922

2923
2924
2925
2926

2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2906
2907
2908
2909
2910
2911
2912

2913
2914
2915
2916

2917


2918
2919
2920
2921
2922
2923
2924
2925

2926
2927
2928
2929
2930
2931
2932







-
+



-
+
-
-








-







** UPDATE or INSTEAD OF DELETE trigger. 
**
** If possible, the SELECT statement is modified so that NULL values
** are stored in the temporary table for all columns for which the 
** corresponding bit in argument mask is not set. If mask takes the
** special value 0xffffffff, then all columns are populated.
*/
int sqlite3SelectMask(Parse *pParse, Select *p, u32 mask){
void sqlite3SelectMask(Parse *pParse, Select *p, u32 mask){
  if( !p->pPrior && !p->isDistinct && mask!=0xffffffff ){
    ExprList *pEList;
    int i;
    if( sqlite3SelectResolve(pParse, p, 0) ){
    sqlite3SelectResolve(pParse, p, 0);
      return SQLITE_ERROR;
    }
    pEList = p->pEList;
    for(i=0; i<pEList->nExpr && i<32; i++){
      if( !(mask&((u32)1<<i)) ){
        sqlite3ExprDelete(pEList->a[i].pExpr);
        pEList->a[i].pExpr = sqlite3Expr(pParse->db, TK_NULL, 0, 0, 0);
      }
    }
  }
  return SQLITE_OK;
}
#endif

/*
** Generate code for the given SELECT statement.
**
** The results are distributed in various ways depending on the
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3185
3186
3187
3188
3189
3190
3191



3192
3193
3194
3195
3196
3197
3198







-
-
-







  ** extracted in pre-sorted order.  If that is the case, then the
  ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
  ** we figure out that the sorting index is not needed.  The addrSortIndex
  ** variable is used to facilitate that change.
  */
  if( pOrderBy ){
    KeyInfo *pKeyInfo;
    if( pParse->nErr ){
      goto select_end;
    }
    pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
    pOrderBy->iECursor = pParse->nTab++;
    p->addrOpenEphm[2] = addrSortIndex =
      sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
                           pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
                           (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
  }else{
3254
3255
3256
3257
3258
3259
3260
3261
3262


3263
3264
3265
3266
3267
3268
3269
3270
3271
3239
3240
3241
3242
3243
3244
3245


3246
3247


3248
3249
3250
3251
3252
3253
3254







-
-
+
+
-
-







      sqlite3VdbeChangeToNoop(v, addrSortIndex, 1);
      p->addrOpenEphm[2] = -1;
    }

    /* Use the standard inner loop
    */
    assert(!isDistinct);
    if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
                    pWInfo->iContinue, pWInfo->iBreak, aff) ){
    selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, -1, pDest,
                    pWInfo->iContinue, pWInfo->iBreak, aff);
       goto select_end;
    }

    /* End the database scan loop.
    */
    sqlite3WhereEnd(pWInfo);
  }else{
    /* This is the processing for aggregate queries */
    NameContext sNC;    /* Name context for processing aggregate information */
3298
3299
3300
3301
3302
3303
3304
3305

3306
3307
3308

3309
3310
3311


3312
3313
3314
3315
3316

3317
3318
3319
3320
3321
3322
3323
3324
3325
3281
3282
3283
3284
3285
3286
3287

3288



3289



3290
3291

3292
3293
3294

3295


3296
3297
3298
3299
3300
3301
3302







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



-
+
-
-







    */
    memset(&sNC, 0, sizeof(sNC));
    sNC.pParse = pParse;
    sNC.pSrcList = pTabList;
    sNC.pAggInfo = &sAggInfo;
    sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
    sAggInfo.pGroupBy = pGroupBy;
    if( sqlite3ExprAnalyzeAggList(&sNC, pEList) ){
    sqlite3ExprAnalyzeAggList(&sNC, pEList);
      goto select_end;
    }
    if( sqlite3ExprAnalyzeAggList(&sNC, pOrderBy) ){
    sqlite3ExprAnalyzeAggList(&sNC, pOrderBy);
      goto select_end;
    }
    if( pHaving && sqlite3ExprAnalyzeAggregates(&sNC, pHaving) ){
    if( pHaving ){
      sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
      goto select_end;
    }
    sAggInfo.nAccumulator = sAggInfo.nColumn;
    for(i=0; i<sAggInfo.nFunc; i++){
      if( sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList) ){
      sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->pList);
        goto select_end;
      }
    }
    if( db->mallocFailed ) goto select_end;

    /* Processing for aggregates with GROUP BY is very different and
    ** much more complex than aggregates without a GROUP BY.
    */
    if( pGroupBy ){
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382



3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3350
3351
3352
3353
3354
3355
3356



3357
3358
3359



3360
3361
3362
3363
3364
3365
3366







-
-
-
+
+
+
-
-
-







      sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
      VdbeComment((v, "Groupby result generator entry point"));
      sqlite3VdbeAddOp2(v, OP_Return, 0, 0);
      finalizeAggFunctions(pParse, &sAggInfo);
      if( pHaving ){
        sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
      }
      rc = selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
                           distinct, pDest,
                           addrOutputRow+1, addrSetAbort, aff);
      selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
                      distinct, pDest,
                      addrOutputRow+1, addrSetAbort, aff);
      if( rc ){
        goto select_end;
      }
      sqlite3VdbeAddOp2(v, OP_Return, 0, 0);
      VdbeComment((v, "end groupby result generator"));

      /* Generate a subroutine that will reset the group-by accumulator
      */
      addrReset = sqlite3VdbeCurrentAddr(v);
      resetAccumulator(pParse, &sAggInfo);
Changes to src/sqliteInt.h.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21













-
+







/*
** 2001 September 15
**
** 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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.654 2008/01/23 03:03:05 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.655 2008/01/23 14:51:50 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** The macro unlikely() is a hint that surrounds a boolean
** expression that is usually false.  Macro likely() surrounds
1756
1757
1758
1759
1760
1761
1762
1763

1764
1765
1766
1767
1768
1769
1770
1756
1757
1758
1759
1760
1761
1762

1763
1764
1765
1766
1767
1768
1769
1770







-
+







void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
                        Token*, int, int);
void sqlite3DropIndex(Parse*, SrcList*, int);
int sqlite3Select(Parse*, Select*, SelectDest*, Select*, int, int*, char *aff);
Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
                         Expr*,ExprList*,int,Expr*,Expr*);
void sqlite3SelectDelete(Select*);
int sqlite3SelectMask(Parse *, Select *, u32);
void sqlite3SelectMask(Parse *, Select *, u32);
Table *sqlite3SrcListLookup(Parse*, SrcList*);
int sqlite3IsReadOnly(Parse*, Table*, int);
void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u8);
void sqlite3WhereEnd(WhereInfo*);
1781
1782
1783
1784
1785
1786
1787
1788
1789


1790
1791
1792
1793
1794
1795
1796
1781
1782
1783
1784
1785
1786
1787


1788
1789
1790
1791
1792
1793
1794
1795
1796







-
-
+
+







void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
void sqlite3Vacuum(Parse*);
int sqlite3RunVacuum(char**, sqlite3*);
char *sqlite3NameFromToken(sqlite3*, Token*);
int sqlite3ExprCompare(Expr*, Expr*);
int sqlite3ExprResolveNames(NameContext *, Expr *);
int sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
int sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
Vdbe *sqlite3GetVdbe(Parse*);
Expr *sqlite3CreateIdExpr(Parse *, const char*);
void sqlite3Randomness(int, void*);
void sqlite3RollbackAll(sqlite3*);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3BeginTransaction(Parse*, int);
void sqlite3CommitTransaction(Parse*);
Changes to src/table.c.
66
67
68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
66
67
68
69
70
71
72




73

74
75
76
77
78
79
80







-
-
-
-
+
-








  /* If this is the first row, then generate an extra row containing
  ** the names of all columns.
  */
  if( p->nRow==0 ){
    p->nColumn = nCol;
    for(i=0; i<nCol; i++){
      if( colv[i]==0 ){
        z = sqlite3_mprintf("");
      }else{
        z = sqlite3_mprintf("%s", colv[i]);
      z = sqlite3_mprintf("%s", colv[i]);
      }
      if( z==0 ) goto malloc_failed;
      p->azResult[p->nData++] = z;
    }
  }else if( p->nColumn!=nCol ){
    sqlite3_free(p->zErrMsg);
    p->zErrMsg = sqlite3_mprintf(
       "sqlite3_get_table() called with two or more incompatible queries"
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
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







-
+

















-
-
-
+
+
-







  char ***pazResult,          /* Write the result table here */
  int *pnRow,                 /* Write the number of rows in the result here */
  int *pnColumn,              /* Write the number of columns of result here */
  char **pzErrMsg             /* Write error messages here */
){
  int rc;
  TabResult res;
  if( pazResult==0 ){ return SQLITE_ERROR; }

  *pazResult = 0;
  if( pnColumn ) *pnColumn = 0;
  if( pnRow ) *pnRow = 0;
  res.zErrMsg = 0;
  res.nResult = 0;
  res.nRow = 0;
  res.nColumn = 0;
  res.nData = 1;
  res.nAlloc = 20;
  res.rc = SQLITE_OK;
  res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc );
  if( res.azResult==0 ){
     db->errCode = SQLITE_NOMEM;
     return SQLITE_NOMEM;
  }
  res.azResult[0] = 0;
  rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
  if( res.azResult ){
    assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
    res.azResult[0] = (char*)res.nData;
  assert( sizeof(res.azResult[0])>= sizeof(res.nData) );
  res.azResult[0] = (char*)res.nData;
  }
  if( (rc&0xff)==SQLITE_ABORT ){
    sqlite3_free_table(&res.azResult[1]);
    if( res.zErrMsg ){
      if( pzErrMsg ){
        sqlite3_free(*pzErrMsg);
        *pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
      }
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199







-
+







*/
void sqlite3_free_table(
  char **azResult            /* Result returned from from sqlite3_get_table() */
){
  if( azResult ){
    int i, n;
    azResult--;
    if( azResult==0 ) return;
    assert( azResult!=0 );
    n = (int)azResult[0];
    for(i=1; i<n; i++){ if( azResult[i] ) sqlite3_free(azResult[i]); }
    sqlite3_free(azResult);
  }
}

#endif /* SQLITE_OMIT_GET_TABLE */
Changes to src/test1.c.
9
10
11
12
13
14
15
16

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

16
17
18
19
20
21
22
23







-
+







**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.286 2008/01/22 21:30:53 drh Exp $
** $Id: test1.c,v 1.287 2008/01/23 14:51:50 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include <stdlib.h>
#include <string.h>

/*
490
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517




518

519
520

521
522
523
524
525



526



527
528
529
530

531
532
533
534
535






536
537
538
539
540
541
542
490
491
492
493
494
495
496

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521

522
523

524
525
526
527
528
529
530
531
532

533
534
535
536
537
538
539
540





541
542
543
544
545
546
547
548
549
550
551
552
553







-
+




















+
+
+
+
-
+

-
+





+
+
+
-
+
+
+




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







  strcpy(zStr, "abcdefghijklmnopqrstuvwxyz");
  sqlite3_snprintf(n, zStr, zFormat, a1);
  Tcl_AppendResult(interp, zStr, 0);
  return TCL_OK;
}

/*
** Usage:  sqlite3_get_table_printf  DB  FORMAT  STRING
** Usage:  sqlite3_get_table_printf  DB  FORMAT  STRING  ?--no-counts?
**
** Invoke the sqlite3_get_table_printf() interface using the open database
** DB.  The SQL is the string FORMAT.  The format string should contain
** one %s or %q.  STRING is the value inserted into %s or %q.
*/
static int test_get_table_printf(
  void *NotUsed,
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int argc,              /* Number of arguments */
  char **argv            /* Text of each argument */
){
  sqlite3 *db;
  Tcl_DString str;
  int rc;
  char *zErr = 0;
  int nRow, nCol;
  char **aResult;
  int i;
  char zBuf[30];
  char *zSql;
  int resCount = -1;
  if( argc==5 ){
    if( Tcl_GetInt(interp, argv[4], &resCount) ) return TCL_ERROR;
  }
  if( argc!=4 ){
  if( argc!=4 && argc!=5 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
       " DB FORMAT STRING", 0);
       " DB FORMAT STRING ?COUNT?", 0);
    return TCL_ERROR;
  }
  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
  Tcl_DStringInit(&str);
  zSql = sqlite3_mprintf(argv[2],argv[3]);
  if( argc==5 ){
    rc = sqlite3_get_table(db, zSql, &aResult, 0, 0, &zErr);
  }else{
  rc = sqlite3_get_table(db, zSql, &aResult, &nRow, &nCol, &zErr);
    rc = sqlite3_get_table(db, zSql, &aResult, &nRow, &nCol, &zErr);
    resCount = (nRow+1)*nCol;
  }
  sqlite3_free(zSql);
  sprintf(zBuf, "%d", rc);
  Tcl_AppendElement(interp, zBuf);
  if( rc==SQLITE_OK ){
    if( argc==4 ){
    sprintf(zBuf, "%d", nRow);
    Tcl_AppendElement(interp, zBuf);
    sprintf(zBuf, "%d", nCol);
    Tcl_AppendElement(interp, zBuf);
    for(i=0; i<(nRow+1)*nCol; i++){
      sprintf(zBuf, "%d", nRow);
      Tcl_AppendElement(interp, zBuf);
      sprintf(zBuf, "%d", nCol);
      Tcl_AppendElement(interp, zBuf);
    }
    for(i=0; i<resCount; i++){
      Tcl_AppendElement(interp, aResult[i] ? aResult[i] : "NULL");
    }
  }else{
    Tcl_AppendElement(interp, zErr);
  }
  sqlite3_free_table(aResult);
  if( zErr ) sqlite3_free(zErr);
Changes to test/tableapi.test.
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22







-
+







#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the sqlite_exec_printf() and
# sqlite_get_table_printf() APIs.
#
# $Id: tableapi.test,v 1.14 2008/01/23 12:52:41 drh Exp $
# $Id: tableapi.test,v 1.15 2008/01/23 14:51:50 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test tableapi-1.0 {
  set ::dbx [sqlite3_open test.db]
  catch {sqlite_exec_printf $::dbx {DROP TABLE xyz} {}}
62
63
64
65
66
67
68






69
70
71
72
73
74
75
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81







+
+
+
+
+
+







  } {}
} {0 3 2 a b 48 (48) 49 (49) 50 (50)}
do_test tableapi-2.3.3 {
  sqlite3_get_table_printf $::dbx {
    SELECT * FROM xyz WHERE a>47 ORDER BY a; invalid
  } {}
} {1 {near "invalid": syntax error}}
do_test tableapi-2.3.4 {
breakpoint
  sqlite3_get_table_printf $::dbx {
    SELECT * FROM xyz WHERE a>47 ORDER BY a
  } {} 8
} {0 a b 48 (48) 49 (49) 50 (50)}
do_test tableapi-2.4 {
  set manyquote ''''''''
  append manyquote $manyquote
  append manyquote $manyquote
  append manyquote $manyquote
  append manyquote $manyquote
  append manyquote $manyquote