Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add a prototype intarray($PTR,$N) table valued function. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | prototype-int-array |
Files: | files | file ages | folders |
SHA1: |
233b33382dc70de45f90b6dfdb5785f2 |
User & Date: | drh 2016-06-29 05:00:30.819 |
Context
2016-06-29
| ||
05:08 | Another test case for the intarray($PTR,$N) virtual table. (check-in: 06e1fab752 user: drh tags: prototype-int-array) | |
05:00 | Add a prototype intarray($PTR,$N) table valued function. (check-in: 233b33382d user: drh tags: prototype-int-array) | |
2016-06-26
| ||
04:06 | Prevent the WhereLoop.rSetup cost estimate from going negative on complex queries. (check-in: f810508591 user: drh tags: trunk) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
412 413 414 415 416 417 418 419 420 421 422 423 424 425 | $(TOP)/ext/session/test_session.c \ $(TOP)/ext/rbu/test_rbu.c # Statically linked extensions # TESTSRC += \ $(TOP)/ext/misc/amatch.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ | > | 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 | $(TOP)/ext/session/test_session.c \ $(TOP)/ext/rbu/test_rbu.c # Statically linked extensions # TESTSRC += \ $(TOP)/ext/misc/amatch.c \ $(TOP)/ext/misc/array.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/csv.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ |
︙ | ︙ |
Added ext/misc/array.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 | /* ** 2016-06-29 ** ** 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. ** ************************************************************************* ** ** This file demonstrates how to create a table-valued-function that ** returns the values in a C-language array. ** Examples: ** ** SELECT * FROM intarray($ptr,5) ** ** The query above returns 5 integers contained in a C-language array ** at the address $ptr. $ptr is a pointer to the array of integers that ** has been cast to an integer. ** ** HOW IT WORKS ** ** The intarray "function" is really a virtual table with the ** following schema: ** ** CREATE FUNCTION intarray( ** value, ** pointer HIDDEN, ** count HIDDEN ** ); */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* intarray_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct intarray_cursor intarray_cursor; struct intarray_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ int isDesc; /* True to count down rather than up */ sqlite3_int64 iRowid; /* The rowid */ sqlite3_int64 iPtr; /* Pointer to array of integers */ sqlite3_int64 iCnt; /* Number of integers in the array */ }; /* ** The intarrayConnect() method is invoked to create a new ** intarray_vtab that describes the intarray virtual table. ** ** Think of this routine as the constructor for intarray_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the intarray_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against intarray will look like. */ static int intarrayConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ sqlite3_vtab *pNew; int rc; /* Column numbers */ #define INTARRAY_COLUMN_VALUE 0 #define INTARRAY_COLUMN_POINTER 1 #define INTARRAY_COLUMN_COUNT 2 rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value,pointer hidden,count hidden)"); if( rc==SQLITE_OK ){ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for intarray_cursor objects. */ static int intarrayDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new intarray_cursor object. */ static int intarrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ intarray_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a intarray_cursor. */ static int intarrayClose(sqlite3_vtab_cursor *cur){ sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a intarray_cursor to its next row of output. */ static int intarrayNext(sqlite3_vtab_cursor *cur){ intarray_cursor *pCur = (intarray_cursor*)cur; pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the intarray_cursor ** is currently pointing. */ static int intarrayColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ intarray_cursor *pCur = (intarray_cursor*)cur; sqlite3_int64 x = 0; switch( i ){ case INTARRAY_COLUMN_POINTER: x = pCur->iPtr; break; case INTARRAY_COLUMN_COUNT: x = pCur->iCnt; break; default: { int *p = (int*)pCur->iPtr; x = (int)p[pCur->iRowid-1]; break; } } sqlite3_result_int64(ctx, x); return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int intarrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ intarray_cursor *pCur = (intarray_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int intarrayEof(sqlite3_vtab_cursor *cur){ intarray_cursor *pCur = (intarray_cursor*)cur; return pCur->iRowid>=pCur->iCnt; } /* ** This method is called to "rewind" the intarray_cursor object back ** to the first row of output. */ static int intarrayFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ intarray_cursor *pCur = (intarray_cursor *)pVtabCursor; int i = 0; if( idxNum ){ pCur->iPtr = sqlite3_value_int64(argv[0]); pCur->iCnt = sqlite3_value_int64(argv[1]); }else{ pCur->iPtr = 0; pCur->iCnt = 0; } pCur->iRowid = 1; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the intarray virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** ** idxNum is 1 if the pointer= and count= constraints exist and is 0 otherwise. ** If idxNum is 0, then intarray becomes an empty table. */ static int intarrayBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */ int cntIdx = -1; /* Index of the count= constraint, or -1 if none */ int nArg = 0; /* Number of arguments that intarrayFilter() expects */ const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; switch( pConstraint->iColumn ){ case INTARRAY_COLUMN_POINTER: ptrIdx = i; break; case INTARRAY_COLUMN_COUNT: cntIdx = i; break; } } if( ptrIdx>=0 && cntIdx>=0 ){ pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1; pIdxInfo->aConstraintUsage[ptrIdx].omit = 1; pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2; pIdxInfo->aConstraintUsage[cntIdx].omit = 1; pIdxInfo->estimatedCost = (double)1; pIdxInfo->estimatedRows = (double)100; pIdxInfo->idxNum = 1; }else{ pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedRows = (double)2147483647; pIdxInfo->idxNum = 0; } return SQLITE_OK; } /* ** This following structure defines all the methods for the ** intarray virtual table. */ static sqlite3_module intarrayModule = { 0, /* iVersion */ 0, /* xCreate */ intarrayConnect, /* xConnect */ intarrayBestIndex, /* xBestIndex */ intarrayDisconnect, /* xDisconnect */ 0, /* xDestroy */ intarrayOpen, /* xOpen - open a cursor */ intarrayClose, /* xClose - close a cursor */ intarrayFilter, /* xFilter - configure scan constraints */ intarrayNext, /* xNext - advance a cursor */ intarrayEof, /* xEof - check for end of scan */ intarrayColumn, /* xColumn - read data */ intarrayRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_array_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3_libversion_number()<3008012 ){ *pzErrMsg = sqlite3_mprintf( "intarray() requires SQLite 3.8.12 or later"); return SQLITE_ERROR; } rc = sqlite3_create_module(db, "intarray", &intarrayModule, 0); #endif return rc; } |
Changes to src/test1.c.
︙ | ︙ | |||
3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 | if( rc!=SQLITE_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite3_bind_int64 STMT N VALUE ** ** Test the sqlite3_bind_int64 interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 64-bit integer VALUE to that wildcard. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 | if( rc!=SQLITE_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite3_bind_intarray STMT N INT ... ** ** Create a C-language array of integers from the arguments. Bind a pointer ** to this array to the NAME parameter of STMT. */ static int test_bind_intarray( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_stmt *pStmt; int idx; int i; static int *p = 0; sqlite3_free(p); p = 0; if( objc<4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT NAME INT...", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; p = sqlite3_malloc( sizeof(int)*(objc-3) ); if( p==0 ) return TCL_ERROR; for(i=0; i<objc-3; i++){ if( Tcl_GetIntFromObj(interp, objv[3+i], &p[i]) ){ sqlite3_free(p); return TCL_ERROR; } } sqlite3_bind_int64(pStmt, idx, (sqlite3_int64)p); return TCL_OK; } /* ** Usage: sqlite3_bind_int64 STMT N VALUE ** ** Test the sqlite3_bind_int64 interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 64-bit integer VALUE to that wildcard. |
︙ | ︙ | |||
6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 | static int tclLoadStaticExtensionCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "closure", sqlite3_closure_init }, { "csv", sqlite3_csv_init }, { "eval", sqlite3_eval_init }, { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, | > > | 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 | static int tclLoadStaticExtensionCmd( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_array_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_csv_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "array", sqlite3_array_init }, { "closure", sqlite3_closure_init }, { "csv", sqlite3_csv_init }, { "eval", sqlite3_eval_init }, { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, |
︙ | ︙ | |||
7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 | void *clientData; } aObjCmd[] = { { "sqlite3_db_config", test_sqlite3_db_config, 0 }, { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "register_dbstat_vtab", test_register_dbstat_vtab }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_zeroblob64", test_bind_zeroblob64, 0 }, { "sqlite3_bind_int64", test_bind_int64, 0 }, { "sqlite3_bind_double", test_bind_double, 0 }, { "sqlite3_bind_null", test_bind_null ,0 }, { "sqlite3_bind_text", test_bind_text ,0 }, { "sqlite3_bind_text16", test_bind_text16 ,0 }, | > | 7134 7135 7136 7137 7138 7139 7140 7141 7142 7143 7144 7145 7146 7147 7148 | void *clientData; } aObjCmd[] = { { "sqlite3_db_config", test_sqlite3_db_config, 0 }, { "bad_behavior", test_bad_behavior, (void*)&iZero }, { "register_dbstat_vtab", test_register_dbstat_vtab }, { "sqlite3_connection_pointer", get_sqlite_pointer, 0 }, { "sqlite3_bind_int", test_bind_int, 0 }, { "sqlite3_bind_intarray", test_bind_intarray, 0 }, { "sqlite3_bind_zeroblob", test_bind_zeroblob, 0 }, { "sqlite3_bind_zeroblob64", test_bind_zeroblob64, 0 }, { "sqlite3_bind_int64", test_bind_int64, 0 }, { "sqlite3_bind_double", test_bind_double, 0 }, { "sqlite3_bind_null", test_bind_null ,0 }, { "sqlite3_bind_text", test_bind_text ,0 }, { "sqlite3_bind_text16", test_bind_text16 ,0 }, |
︙ | ︙ |
Changes to test/tabfunc01.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | set testprefix tabfunc01 ifcapable !vtab { finish_test return } load_static_extension db series do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; } {1 | 3 | 5 | 7 | 9 |} do_execsql_test tabfunc01-1.2 { SELECT *, '|' FROM generate_series LIMIT 5; } {0 | 1 | 2 | 3 | 4 |} | > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | set testprefix tabfunc01 ifcapable !vtab { finish_test return } load_static_extension db series load_static_extension db array do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; } {1 | 3 | 5 | 7 | 9 |} do_execsql_test tabfunc01-1.2 { SELECT *, '|' FROM generate_series LIMIT 5; } {0 | 1 | 2 | 3 | 4 |} |
︙ | ︙ | |||
131 132 133 134 135 136 137 138 | # by virtual tables unless omit was set. # do_execsql_test tabfunc01-500 { SELECT * FROM generate_series WHERE start IN (1,7) AND stop=20 AND step=10 ORDER BY +1; } {1 7 11 17} finish_test | > > > > > > > > > > > > > > > > > > > > > | 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 | # by virtual tables unless omit was set. # do_execsql_test tabfunc01-500 { SELECT * FROM generate_series WHERE start IN (1,7) AND stop=20 AND step=10 ORDER BY +1; } {1 7 11 17} do_test tabfunc01-600 { set TAIL {} set VM [sqlite3_prepare db {SELECT * FROM intarray(?2,?3)} -1 TAIL] set TAIL } {} do_test tabfunc01-610 { sqlite3_bind_intarray $VM 2 11 22 33 44 55 sqlite3_bind_int $VM 3 4 sqlite3_step $VM } SQLITE_ROW do_test tabfunc01-620 { sqlite3_column_int $VM 0 } 11 do_test tabfunc01-621 { sqlite3_step $VM sqlite3_column_int $VM 0 } 22 sqlite3_finalize $VM catch {sqlite3_bind_intarray} finish_test |