drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 1 | /* |
drh | b19a2bc | 2001-09-16 00:13:26 +0000 | [diff] [blame] | 2 | ** 2001 September 15 |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 3 | ** |
drh | b19a2bc | 2001-09-16 00:13:26 +0000 | [diff] [blame] | 4 | ** The author disclaims copyright to this source code. In place of |
| 5 | ** a legal notice, here is a blessing: |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 6 | ** |
drh | b19a2bc | 2001-09-16 00:13:26 +0000 | [diff] [blame] | 7 | ** May you do good and not evil. |
| 8 | ** May you find forgiveness for yourself and forgive others. |
| 9 | ** May you share freely, never taking more than you give. |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 10 | ** |
| 11 | ************************************************************************* |
| 12 | ** Code for testing the printf() interface to SQLite. This code |
| 13 | ** is not included in the SQLite library. It is used for automated |
| 14 | ** testing of the SQLite library. |
| 15 | ** |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 16 | ** $Id: test1.c,v 1.45 2004/05/21 10:08:54 danielk1977 Exp $ |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 17 | */ |
| 18 | #include "sqliteInt.h" |
| 19 | #include "tcl.h" |
drh | 94e9203 | 2003-02-16 22:21:32 +0000 | [diff] [blame] | 20 | #include "os.h" |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 21 | #include <stdlib.h> |
| 22 | #include <string.h> |
| 23 | |
drh | 94e9203 | 2003-02-16 22:21:32 +0000 | [diff] [blame] | 24 | #if OS_WIN |
| 25 | # define PTR_FMT "%x" |
| 26 | #else |
| 27 | # define PTR_FMT "%p" |
| 28 | #endif |
| 29 | |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 30 | static const char * errorName(int rc){ |
| 31 | const char *zName = 0; |
| 32 | switch( rc ){ |
| 33 | case SQLITE_OK: zName = "SQLITE_OK"; break; |
| 34 | case SQLITE_ERROR: zName = "SQLITE_ERROR"; break; |
| 35 | case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break; |
| 36 | case SQLITE_PERM: zName = "SQLITE_PERM"; break; |
| 37 | case SQLITE_ABORT: zName = "SQLITE_ABORT"; break; |
| 38 | case SQLITE_BUSY: zName = "SQLITE_BUSY"; break; |
| 39 | case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break; |
| 40 | case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break; |
| 41 | case SQLITE_READONLY: zName = "SQLITE_READONLY"; break; |
| 42 | case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break; |
| 43 | case SQLITE_IOERR: zName = "SQLITE_IOERR"; break; |
| 44 | case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break; |
| 45 | case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break; |
| 46 | case SQLITE_FULL: zName = "SQLITE_FULL"; break; |
| 47 | case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break; |
| 48 | case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break; |
| 49 | case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break; |
| 50 | case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break; |
| 51 | case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break; |
| 52 | case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break; |
| 53 | case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break; |
| 54 | case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break; |
| 55 | case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break; |
| 56 | case SQLITE_AUTH: zName = "SQLITE_AUTH"; break; |
| 57 | case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break; |
| 58 | case SQLITE_RANGE: zName = "SQLITE_RANGE"; break; |
| 59 | case SQLITE_ROW: zName = "SQLITE_ROW"; break; |
| 60 | case SQLITE_DONE: zName = "SQLITE_DONE"; break; |
| 61 | default: zName = "SQLITE_Unknown"; break; |
| 62 | } |
| 63 | return zName; |
| 64 | } |
| 65 | |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 66 | /* |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 67 | ** Decode a pointer to an sqlite object. |
| 68 | */ |
drh | 90f405e | 2003-12-23 03:06:23 +0000 | [diff] [blame] | 69 | static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite **ppDb){ |
| 70 | if( sscanf(zA, PTR_FMT, (void**)ppDb)!=1 && |
| 71 | (zA[0]!='0' || zA[1]!='x' || sscanf(&zA[2], PTR_FMT, (void**)ppDb)!=1) |
| 72 | ){ |
| 73 | Tcl_AppendResult(interp, "\"", zA, "\" is not a valid pointer value", 0); |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 74 | return TCL_ERROR; |
| 75 | } |
| 76 | return TCL_OK; |
| 77 | } |
| 78 | |
| 79 | /* |
| 80 | ** Decode a pointer to an sqlite_vm object. |
| 81 | */ |
| 82 | static int getVmPointer(Tcl_Interp *interp, const char *zArg, sqlite_vm **ppVm){ |
drh | 94e9203 | 2003-02-16 22:21:32 +0000 | [diff] [blame] | 83 | if( sscanf(zArg, PTR_FMT, (void**)ppVm)!=1 ){ |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 84 | Tcl_AppendResult(interp, "\"", zArg, "\" is not a valid pointer value", 0); |
| 85 | return TCL_ERROR; |
| 86 | } |
| 87 | return TCL_OK; |
| 88 | } |
| 89 | |
| 90 | /* |
danielk1977 | 51e3d8e | 2004-05-20 01:12:34 +0000 | [diff] [blame] | 91 | ** Decode a pointer to an sqlite3_stmt object. |
| 92 | */ |
| 93 | static int getStmtPointer( |
| 94 | Tcl_Interp *interp, |
| 95 | const char *zArg, |
| 96 | sqlite3_stmt **ppStmt |
| 97 | ){ |
| 98 | if( sscanf(zArg, PTR_FMT, (void**)ppStmt)!=1 ){ |
| 99 | Tcl_AppendResult(interp, "\"", zArg, "\" is not a valid pointer value", 0); |
| 100 | return TCL_ERROR; |
| 101 | } |
| 102 | return TCL_OK; |
| 103 | } |
| 104 | |
| 105 | /* |
drh | 7d8085a | 2003-04-26 13:19:38 +0000 | [diff] [blame] | 106 | ** Generate a text representation of a pointer that can be understood |
| 107 | ** by the getDbPointer and getVmPointer routines above. |
| 108 | ** |
| 109 | ** The problem is, on some machines (Solaris) if you do a printf with |
| 110 | ** "%p" you cannot turn around and do a scanf with the same "%p" and |
| 111 | ** get your pointer back. You have to prepend a "0x" before it will |
| 112 | ** work. Or at least that is what is reported to me (drh). But this |
| 113 | ** behavior varies from machine to machine. The solution used her is |
| 114 | ** to test the string right after it is generated to see if it can be |
| 115 | ** understood by scanf, and if not, try prepending an "0x" to see if |
| 116 | ** that helps. If nothing works, a fatal error is generated. |
| 117 | */ |
| 118 | static int makePointerStr(Tcl_Interp *interp, char *zPtr, void *p){ |
| 119 | void *p2; |
| 120 | sprintf(zPtr, PTR_FMT, p); |
| 121 | if( sscanf(zPtr, PTR_FMT, &p2)!=1 || p2!=p ){ |
| 122 | sprintf(zPtr, "0x" PTR_FMT, p); |
| 123 | if( sscanf(zPtr, PTR_FMT, &p2)!=1 || p2!=p ){ |
| 124 | Tcl_AppendResult(interp, "unable to convert a pointer to a string " |
| 125 | "in the file " __FILE__ " in function makePointerStr(). Please " |
| 126 | "report this problem to the SQLite mailing list or as a new but " |
| 127 | "report. Please provide detailed information about how you compiled " |
| 128 | "SQLite and what computer you are running on.", 0); |
| 129 | return TCL_ERROR; |
| 130 | } |
| 131 | } |
| 132 | return TCL_OK; |
| 133 | } |
| 134 | |
| 135 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 136 | ** The callback routine for sqlite3_exec_printf(). |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 137 | */ |
| 138 | static int exec_printf_cb(void *pArg, int argc, char **argv, char **name){ |
| 139 | Tcl_DString *str = (Tcl_DString*)pArg; |
| 140 | int i; |
| 141 | |
| 142 | if( Tcl_DStringLength(str)==0 ){ |
| 143 | for(i=0; i<argc; i++){ |
| 144 | Tcl_DStringAppendElement(str, name[i] ? name[i] : "NULL"); |
| 145 | } |
| 146 | } |
| 147 | for(i=0; i<argc; i++){ |
| 148 | Tcl_DStringAppendElement(str, argv[i] ? argv[i] : "NULL"); |
| 149 | } |
| 150 | return 0; |
| 151 | } |
| 152 | |
| 153 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 154 | ** Usage: sqlite3_exec_printf DB FORMAT STRING |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 155 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 156 | ** Invoke the sqlite3_exec_printf() interface using the open database |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 157 | ** DB. The SQL is the string FORMAT. The format string should contain |
| 158 | ** one %s or %q. STRING is the value inserted into %s or %q. |
| 159 | */ |
| 160 | static int test_exec_printf( |
| 161 | void *NotUsed, |
| 162 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 163 | int argc, /* Number of arguments */ |
| 164 | char **argv /* Text of each argument */ |
| 165 | ){ |
| 166 | sqlite *db; |
| 167 | Tcl_DString str; |
| 168 | int rc; |
| 169 | char *zErr = 0; |
| 170 | char zBuf[30]; |
| 171 | if( argc!=4 ){ |
| 172 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 173 | " DB FORMAT STRING", 0); |
| 174 | return TCL_ERROR; |
| 175 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 176 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 177 | Tcl_DStringInit(&str); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 178 | rc = sqlite3_exec_printf(db, argv[2], exec_printf_cb, &str, &zErr, argv[3]); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 179 | sprintf(zBuf, "%d", rc); |
| 180 | Tcl_AppendElement(interp, zBuf); |
| 181 | Tcl_AppendElement(interp, rc==SQLITE_OK ? Tcl_DStringValue(&str) : zErr); |
| 182 | Tcl_DStringFree(&str); |
| 183 | if( zErr ) free(zErr); |
| 184 | return TCL_OK; |
| 185 | } |
| 186 | |
| 187 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 188 | ** Usage: sqlite3_mprintf_z_test SEPARATOR ARG0 ARG1 ... |
drh | d93d8a8 | 2003-06-16 03:08:18 +0000 | [diff] [blame] | 189 | ** |
| 190 | ** Test the %z format of mprintf(). Use multiple mprintf() calls to |
| 191 | ** concatenate arg0 through argn using separator as the separator. |
| 192 | ** Return the result. |
| 193 | */ |
| 194 | static int test_mprintf_z( |
| 195 | void *NotUsed, |
| 196 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 197 | int argc, /* Number of arguments */ |
| 198 | char **argv /* Text of each argument */ |
| 199 | ){ |
| 200 | char *zResult = 0; |
| 201 | int i; |
| 202 | |
| 203 | for(i=2; i<argc; i++){ |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 204 | zResult = sqlite3MPrintf("%z%s%s", zResult, argv[1], argv[i]); |
drh | d93d8a8 | 2003-06-16 03:08:18 +0000 | [diff] [blame] | 205 | } |
| 206 | Tcl_AppendResult(interp, zResult, 0); |
drh | 5f96843 | 2004-02-21 19:02:30 +0000 | [diff] [blame] | 207 | sqliteFree(zResult); |
drh | d93d8a8 | 2003-06-16 03:08:18 +0000 | [diff] [blame] | 208 | return TCL_OK; |
| 209 | } |
| 210 | |
| 211 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 212 | ** Usage: sqlite3_get_table_printf DB FORMAT STRING |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 213 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 214 | ** Invoke the sqlite3_get_table_printf() interface using the open database |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 215 | ** DB. The SQL is the string FORMAT. The format string should contain |
| 216 | ** one %s or %q. STRING is the value inserted into %s or %q. |
| 217 | */ |
| 218 | static int test_get_table_printf( |
| 219 | void *NotUsed, |
| 220 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 221 | int argc, /* Number of arguments */ |
| 222 | char **argv /* Text of each argument */ |
| 223 | ){ |
| 224 | sqlite *db; |
| 225 | Tcl_DString str; |
| 226 | int rc; |
| 227 | char *zErr = 0; |
| 228 | int nRow, nCol; |
| 229 | char **aResult; |
| 230 | int i; |
| 231 | char zBuf[30]; |
| 232 | if( argc!=4 ){ |
| 233 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 234 | " DB FORMAT STRING", 0); |
| 235 | return TCL_ERROR; |
| 236 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 237 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 238 | Tcl_DStringInit(&str); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 239 | rc = sqlite3_get_table_printf(db, argv[2], &aResult, &nRow, &nCol, |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 240 | &zErr, argv[3]); |
| 241 | sprintf(zBuf, "%d", rc); |
| 242 | Tcl_AppendElement(interp, zBuf); |
| 243 | if( rc==SQLITE_OK ){ |
| 244 | sprintf(zBuf, "%d", nRow); |
| 245 | Tcl_AppendElement(interp, zBuf); |
| 246 | sprintf(zBuf, "%d", nCol); |
| 247 | Tcl_AppendElement(interp, zBuf); |
| 248 | for(i=0; i<(nRow+1)*nCol; i++){ |
| 249 | Tcl_AppendElement(interp, aResult[i] ? aResult[i] : "NULL"); |
| 250 | } |
| 251 | }else{ |
| 252 | Tcl_AppendElement(interp, zErr); |
| 253 | } |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 254 | sqlite3_free_table(aResult); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 255 | if( zErr ) free(zErr); |
| 256 | return TCL_OK; |
| 257 | } |
| 258 | |
drh | af9ff33 | 2002-01-16 21:00:27 +0000 | [diff] [blame] | 259 | |
| 260 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 261 | ** Usage: sqlite3_last_insert_rowid DB |
drh | af9ff33 | 2002-01-16 21:00:27 +0000 | [diff] [blame] | 262 | ** |
| 263 | ** Returns the integer ROWID of the most recent insert. |
| 264 | */ |
| 265 | static int test_last_rowid( |
| 266 | void *NotUsed, |
| 267 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 268 | int argc, /* Number of arguments */ |
| 269 | char **argv /* Text of each argument */ |
| 270 | ){ |
| 271 | sqlite *db; |
| 272 | char zBuf[30]; |
| 273 | |
| 274 | if( argc!=2 ){ |
| 275 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB\"", 0); |
| 276 | return TCL_ERROR; |
| 277 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 278 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 279 | sprintf(zBuf, "%d", sqlite3_last_insert_rowid(db)); |
drh | af9ff33 | 2002-01-16 21:00:27 +0000 | [diff] [blame] | 280 | Tcl_AppendResult(interp, zBuf, 0); |
| 281 | return SQLITE_OK; |
| 282 | } |
| 283 | |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 284 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 285 | ** Usage: sqlite3_close DB |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 286 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 287 | ** Closes the database opened by sqlite3_open. |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 288 | */ |
| 289 | static int sqlite_test_close( |
| 290 | void *NotUsed, |
| 291 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 292 | int argc, /* Number of arguments */ |
| 293 | char **argv /* Text of each argument */ |
| 294 | ){ |
| 295 | sqlite *db; |
| 296 | if( argc!=2 ){ |
| 297 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 298 | " FILENAME\"", 0); |
| 299 | return TCL_ERROR; |
| 300 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 301 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 302 | sqlite3_close(db); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 303 | return TCL_OK; |
| 304 | } |
| 305 | |
| 306 | /* |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 307 | ** Implementation of the x_coalesce() function. |
| 308 | ** Return the first argument non-NULL argument. |
| 309 | */ |
| 310 | static void ifnullFunc(sqlite_func *context, int argc, const char **argv){ |
| 311 | int i; |
| 312 | for(i=0; i<argc; i++){ |
| 313 | if( argv[i] ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 314 | sqlite3_set_result_string(context, argv[i], -1); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 315 | break; |
| 316 | } |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /* |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 321 | ** A structure into which to accumulate text. |
| 322 | */ |
| 323 | struct dstr { |
| 324 | int nAlloc; /* Space allocated */ |
| 325 | int nUsed; /* Space used */ |
| 326 | char *z; /* The space */ |
| 327 | }; |
| 328 | |
| 329 | /* |
| 330 | ** Append text to a dstr |
| 331 | */ |
| 332 | static void dstrAppend(struct dstr *p, const char *z, int divider){ |
| 333 | int n = strlen(z); |
| 334 | if( p->nUsed + n + 2 > p->nAlloc ){ |
| 335 | char *zNew; |
| 336 | p->nAlloc = p->nAlloc*2 + n + 200; |
| 337 | zNew = sqliteRealloc(p->z, p->nAlloc); |
| 338 | if( zNew==0 ){ |
| 339 | sqliteFree(p->z); |
| 340 | memset(p, 0, sizeof(*p)); |
| 341 | return; |
| 342 | } |
| 343 | p->z = zNew; |
| 344 | } |
| 345 | if( divider && p->nUsed>0 ){ |
| 346 | p->z[p->nUsed++] = divider; |
| 347 | } |
| 348 | memcpy(&p->z[p->nUsed], z, n+1); |
| 349 | p->nUsed += n; |
| 350 | } |
| 351 | |
| 352 | /* |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 353 | ** Invoked for each callback from sqlite3ExecFunc |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 354 | */ |
| 355 | static int execFuncCallback(void *pData, int argc, char **argv, char **NotUsed){ |
| 356 | struct dstr *p = (struct dstr*)pData; |
| 357 | int i; |
| 358 | for(i=0; i<argc; i++){ |
| 359 | if( argv[i]==0 ){ |
| 360 | dstrAppend(p, "NULL", ' '); |
| 361 | }else{ |
| 362 | dstrAppend(p, argv[i], ' '); |
| 363 | } |
| 364 | } |
| 365 | return 0; |
| 366 | } |
| 367 | |
| 368 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 369 | ** Implementation of the x_sqlite3_exec() function. This function takes |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 370 | ** a single argument and attempts to execute that argument as SQL code. |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 371 | ** This is illegal and should set the SQLITE_MISUSE flag on the database. |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 372 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 373 | ** 2004-Jan-07: We have changed this to make it legal to call sqlite3_exec() |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 374 | ** from within a function call. |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 375 | ** |
| 376 | ** This routine simulates the effect of having two threads attempt to |
| 377 | ** use the same database at the same time. |
| 378 | */ |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 379 | static void sqlite3ExecFunc(sqlite_func *context, int argc, const char **argv){ |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 380 | struct dstr x; |
| 381 | memset(&x, 0, sizeof(x)); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 382 | sqlite3_exec((sqlite*)sqlite3_user_data(context), argv[0], |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 383 | execFuncCallback, &x, 0); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 384 | sqlite3_set_result_string(context, x.z, x.nUsed); |
drh | d1d9fc3 | 2004-01-07 19:24:48 +0000 | [diff] [blame] | 385 | sqliteFree(x.z); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 386 | } |
| 387 | |
| 388 | /* |
| 389 | ** Usage: sqlite_test_create_function DB |
| 390 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 391 | ** Call the sqlite3_create_function API on the given database in order |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 392 | ** to create a function named "x_coalesce". This function does the same thing |
| 393 | ** as the "coalesce" function. This function also registers an SQL function |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 394 | ** named "x_sqlite3_exec" that invokes sqlite3_exec(). Invoking sqlite3_exec() |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 395 | ** in this way is illegal recursion and should raise an SQLITE_MISUSE error. |
| 396 | ** The effect is similar to trying to use the same database connection from |
| 397 | ** two threads at the same time. |
| 398 | ** |
| 399 | ** The original motivation for this routine was to be able to call the |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 400 | ** sqlite3_create_function function while a query is in progress in order |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 401 | ** to test the SQLITE_MISUSE detection logic. |
| 402 | */ |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 403 | static int test_create_function( |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 404 | void *NotUsed, |
| 405 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 406 | int argc, /* Number of arguments */ |
| 407 | char **argv /* Text of each argument */ |
| 408 | ){ |
| 409 | sqlite *db; |
| 410 | extern void Md5_Register(sqlite*); |
| 411 | if( argc!=2 ){ |
| 412 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 413 | " FILENAME\"", 0); |
| 414 | return TCL_ERROR; |
| 415 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 416 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 417 | sqlite3_create_function(db, "x_coalesce", -1, ifnullFunc, 0); |
| 418 | sqlite3_create_function(db, "x_sqlite3_exec", 1, sqlite3ExecFunc, db); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 419 | return TCL_OK; |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | ** Routines to implement the x_count() aggregate function. |
| 424 | */ |
| 425 | typedef struct CountCtx CountCtx; |
| 426 | struct CountCtx { |
| 427 | int n; |
| 428 | }; |
| 429 | static void countStep(sqlite_func *context, int argc, const char **argv){ |
| 430 | CountCtx *p; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 431 | p = sqlite3_aggregate_context(context, sizeof(*p)); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 432 | if( (argc==0 || argv[0]) && p ){ |
| 433 | p->n++; |
| 434 | } |
| 435 | } |
| 436 | static void countFinalize(sqlite_func *context){ |
| 437 | CountCtx *p; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 438 | p = sqlite3_aggregate_context(context, sizeof(*p)); |
| 439 | sqlite3_set_result_int(context, p ? p->n : 0); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 440 | } |
| 441 | |
| 442 | /* |
| 443 | ** Usage: sqlite_test_create_aggregate DB |
| 444 | ** |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 445 | ** Call the sqlite3_create_function API on the given database in order |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 446 | ** to create a function named "x_count". This function does the same thing |
| 447 | ** as the "md5sum" function. |
| 448 | ** |
| 449 | ** The original motivation for this routine was to be able to call the |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 450 | ** sqlite3_create_aggregate function while a query is in progress in order |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 451 | ** to test the SQLITE_MISUSE detection logic. |
| 452 | */ |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 453 | static int test_create_aggregate( |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 454 | void *NotUsed, |
| 455 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 456 | int argc, /* Number of arguments */ |
| 457 | char **argv /* Text of each argument */ |
| 458 | ){ |
| 459 | sqlite *db; |
| 460 | if( argc!=2 ){ |
| 461 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 462 | " FILENAME\"", 0); |
| 463 | return TCL_ERROR; |
| 464 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 465 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 466 | sqlite3_create_aggregate(db, "x_count", 0, countStep, countFinalize, 0); |
| 467 | sqlite3_create_aggregate(db, "x_count", 1, countStep, countFinalize, 0); |
drh | c22bd47 | 2002-05-10 13:14:07 +0000 | [diff] [blame] | 468 | return TCL_OK; |
| 469 | } |
| 470 | |
| 471 | |
| 472 | |
| 473 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 474 | ** Usage: sqlite3_mprintf_int FORMAT INTEGER INTEGER INTEGER |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 475 | ** |
| 476 | ** Call mprintf with three integer arguments |
| 477 | */ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 478 | static int sqlite3_mprintf_int( |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 479 | void *NotUsed, |
| 480 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 481 | int argc, /* Number of arguments */ |
| 482 | char **argv /* Text of each argument */ |
| 483 | ){ |
| 484 | int a[3], i; |
| 485 | char *z; |
| 486 | if( argc!=5 ){ |
| 487 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 488 | " FORMAT INT INT INT\"", 0); |
| 489 | return TCL_ERROR; |
| 490 | } |
| 491 | for(i=2; i<5; i++){ |
| 492 | if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; |
| 493 | } |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 494 | z = sqlite3_mprintf(argv[1], a[0], a[1], a[2]); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 495 | Tcl_AppendResult(interp, z, 0); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 496 | sqlite3_freemem(z); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 497 | return TCL_OK; |
| 498 | } |
| 499 | |
| 500 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 501 | ** Usage: sqlite3_mprintf_str FORMAT INTEGER INTEGER STRING |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 502 | ** |
| 503 | ** Call mprintf with two integer arguments and one string argument |
| 504 | */ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 505 | static int sqlite3_mprintf_str( |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 506 | void *NotUsed, |
| 507 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 508 | int argc, /* Number of arguments */ |
| 509 | char **argv /* Text of each argument */ |
| 510 | ){ |
| 511 | int a[3], i; |
| 512 | char *z; |
chw | f220b24 | 2002-06-16 04:54:28 +0000 | [diff] [blame] | 513 | if( argc<4 || argc>5 ){ |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 514 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
chw | f220b24 | 2002-06-16 04:54:28 +0000 | [diff] [blame] | 515 | " FORMAT INT INT ?STRING?\"", 0); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 516 | return TCL_ERROR; |
| 517 | } |
| 518 | for(i=2; i<4; i++){ |
| 519 | if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; |
| 520 | } |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 521 | z = sqlite3_mprintf(argv[1], a[0], a[1], argc>4 ? argv[4] : NULL); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 522 | Tcl_AppendResult(interp, z, 0); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 523 | sqlite3_freemem(z); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 524 | return TCL_OK; |
| 525 | } |
| 526 | |
| 527 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 528 | ** Usage: sqlite3_mprintf_str FORMAT INTEGER INTEGER DOUBLE |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 529 | ** |
| 530 | ** Call mprintf with two integer arguments and one double argument |
| 531 | */ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 532 | static int sqlite3_mprintf_double( |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 533 | void *NotUsed, |
| 534 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 535 | int argc, /* Number of arguments */ |
| 536 | char **argv /* Text of each argument */ |
| 537 | ){ |
| 538 | int a[3], i; |
| 539 | double r; |
| 540 | char *z; |
| 541 | if( argc!=5 ){ |
| 542 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 543 | " FORMAT INT INT STRING\"", 0); |
| 544 | return TCL_ERROR; |
| 545 | } |
| 546 | for(i=2; i<4; i++){ |
| 547 | if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; |
| 548 | } |
| 549 | if( Tcl_GetDouble(interp, argv[4], &r) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 550 | z = sqlite3_mprintf(argv[1], a[0], a[1], r); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 551 | Tcl_AppendResult(interp, z, 0); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 552 | sqlite3_freemem(z); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 553 | return TCL_OK; |
| 554 | } |
| 555 | |
| 556 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 557 | ** Usage: sqlite3_mprintf_str FORMAT DOUBLE DOUBLE |
drh | b621c23 | 2004-02-21 19:41:04 +0000 | [diff] [blame] | 558 | ** |
| 559 | ** Call mprintf with a single double argument which is the product of the |
| 560 | ** two arguments given above. This is used to generate overflow and underflow |
| 561 | ** doubles to test that they are converted properly. |
| 562 | */ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 563 | static int sqlite3_mprintf_scaled( |
drh | b621c23 | 2004-02-21 19:41:04 +0000 | [diff] [blame] | 564 | void *NotUsed, |
| 565 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 566 | int argc, /* Number of arguments */ |
| 567 | char **argv /* Text of each argument */ |
| 568 | ){ |
| 569 | int i; |
| 570 | double r[2]; |
| 571 | char *z; |
| 572 | if( argc!=4 ){ |
| 573 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 574 | " FORMAT DOUBLE DOUBLE\"", 0); |
| 575 | return TCL_ERROR; |
| 576 | } |
| 577 | for(i=2; i<4; i++){ |
| 578 | if( Tcl_GetDouble(interp, argv[i], &r[i-2]) ) return TCL_ERROR; |
| 579 | } |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 580 | z = sqlite3_mprintf(argv[1], r[0]*r[1]); |
drh | b621c23 | 2004-02-21 19:41:04 +0000 | [diff] [blame] | 581 | Tcl_AppendResult(interp, z, 0); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 582 | sqlite3_freemem(z); |
drh | b621c23 | 2004-02-21 19:41:04 +0000 | [diff] [blame] | 583 | return TCL_OK; |
| 584 | } |
| 585 | |
| 586 | /* |
drh | daffd0e | 2001-04-11 14:28:42 +0000 | [diff] [blame] | 587 | ** Usage: sqlite_malloc_fail N |
| 588 | ** |
drh | afa4a02 | 2001-09-24 03:12:39 +0000 | [diff] [blame] | 589 | ** Rig sqliteMalloc() to fail on the N-th call. Turn off this mechanism |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 590 | ** and reset the sqlite3_malloc_failed variable is N==0. |
drh | daffd0e | 2001-04-11 14:28:42 +0000 | [diff] [blame] | 591 | */ |
| 592 | #ifdef MEMORY_DEBUG |
| 593 | static int sqlite_malloc_fail( |
| 594 | void *NotUsed, |
| 595 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 596 | int argc, /* Number of arguments */ |
| 597 | char **argv /* Text of each argument */ |
| 598 | ){ |
| 599 | int n; |
| 600 | if( argc!=2 ){ |
| 601 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " N\"", 0); |
| 602 | return TCL_ERROR; |
| 603 | } |
| 604 | if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 605 | sqlite3_iMallocFail = n; |
| 606 | sqlite3_malloc_failed = 0; |
drh | daffd0e | 2001-04-11 14:28:42 +0000 | [diff] [blame] | 607 | return TCL_OK; |
| 608 | } |
| 609 | #endif |
| 610 | |
| 611 | /* |
| 612 | ** Usage: sqlite_malloc_stat |
| 613 | ** |
| 614 | ** Return the number of prior calls to sqliteMalloc() and sqliteFree(). |
| 615 | */ |
| 616 | #ifdef MEMORY_DEBUG |
| 617 | static int sqlite_malloc_stat( |
| 618 | void *NotUsed, |
| 619 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 620 | int argc, /* Number of arguments */ |
| 621 | char **argv /* Text of each argument */ |
| 622 | ){ |
| 623 | char zBuf[200]; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 624 | sprintf(zBuf, "%d %d %d", sqlite3_nMalloc, sqlite3_nFree, sqlite3_iMallocFail); |
drh | daffd0e | 2001-04-11 14:28:42 +0000 | [diff] [blame] | 625 | Tcl_AppendResult(interp, zBuf, 0); |
| 626 | return TCL_OK; |
| 627 | } |
| 628 | #endif |
| 629 | |
| 630 | /* |
drh | 28b4e48 | 2002-03-11 02:06:13 +0000 | [diff] [blame] | 631 | ** Usage: sqlite_abort |
| 632 | ** |
| 633 | ** Shutdown the process immediately. This is not a clean shutdown. |
| 634 | ** This command is used to test the recoverability of a database in |
| 635 | ** the event of a program crash. |
| 636 | */ |
| 637 | static int sqlite_abort( |
| 638 | void *NotUsed, |
| 639 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 640 | int argc, /* Number of arguments */ |
| 641 | char **argv /* Text of each argument */ |
| 642 | ){ |
| 643 | assert( interp==0 ); /* This will always fail */ |
| 644 | return TCL_OK; |
| 645 | } |
| 646 | |
| 647 | /* |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 648 | ** The following routine is a user-defined SQL function whose purpose |
| 649 | ** is to test the sqlite_set_result() API. |
| 650 | */ |
| 651 | static void testFunc(sqlite_func *context, int argc, const char **argv){ |
| 652 | while( argc>=2 ){ |
| 653 | if( argv[0]==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 654 | sqlite3_set_result_error(context, "first argument to test function " |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 655 | "may not be NULL", -1); |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 656 | }else if( sqlite3StrICmp(argv[0],"string")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 657 | sqlite3_set_result_string(context, argv[1], -1); |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 658 | }else if( argv[1]==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 659 | sqlite3_set_result_error(context, "2nd argument may not be NULL if the " |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 660 | "first argument is not \"string\"", -1); |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 661 | }else if( sqlite3StrICmp(argv[0],"int")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 662 | sqlite3_set_result_int(context, atoi(argv[1])); |
danielk1977 | 4adee20 | 2004-05-08 08:23:19 +0000 | [diff] [blame] | 663 | }else if( sqlite3StrICmp(argv[0],"double")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 664 | sqlite3_set_result_double(context, sqlite3AtoF(argv[1], 0)); |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 665 | }else{ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 666 | sqlite3_set_result_error(context,"first argument should be one of: " |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 667 | "string int double", -1); |
| 668 | } |
| 669 | argc -= 2; |
| 670 | argv += 2; |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | /* |
| 675 | ** Usage: sqlite_register_test_function DB NAME |
| 676 | ** |
| 677 | ** Register the test SQL function on the database DB under the name NAME. |
| 678 | */ |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 679 | static int test_register_func( |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 680 | void *NotUsed, |
| 681 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 682 | int argc, /* Number of arguments */ |
| 683 | char **argv /* Text of each argument */ |
| 684 | ){ |
| 685 | sqlite *db; |
| 686 | int rc; |
| 687 | if( argc!=3 ){ |
| 688 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 689 | " DB FUNCTION-NAME", 0); |
| 690 | return TCL_ERROR; |
| 691 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 692 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 693 | rc = sqlite3_create_function(db, argv[2], -1, testFunc, 0); |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 694 | if( rc!=0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 695 | Tcl_AppendResult(interp, sqlite3_error_string(rc), 0); |
drh | 6cbe1f1 | 2002-07-01 00:31:36 +0000 | [diff] [blame] | 696 | return TCL_ERROR; |
| 697 | } |
| 698 | return TCL_OK; |
| 699 | } |
| 700 | |
| 701 | /* |
drh | 5a38705 | 2003-01-11 14:19:51 +0000 | [diff] [blame] | 702 | ** This SQLite callback records the datatype of all columns. |
| 703 | ** |
| 704 | ** The pArg argument is really a pointer to a TCL interpreter. The |
| 705 | ** column names are inserted as the result of this interpreter. |
| 706 | ** |
| 707 | ** This routine returns non-zero which causes the query to abort. |
| 708 | */ |
| 709 | static int rememberDataTypes(void *pArg, int nCol, char **argv, char **colv){ |
| 710 | int i; |
| 711 | Tcl_Interp *interp = (Tcl_Interp*)pArg; |
| 712 | Tcl_Obj *pList, *pElem; |
| 713 | if( colv[nCol+1]==0 ){ |
| 714 | return 1; |
| 715 | } |
| 716 | pList = Tcl_NewObj(); |
| 717 | for(i=0; i<nCol; i++){ |
| 718 | pElem = Tcl_NewStringObj(colv[i+nCol] ? colv[i+nCol] : "NULL", -1); |
| 719 | Tcl_ListObjAppendElement(interp, pList, pElem); |
| 720 | } |
| 721 | Tcl_SetObjResult(interp, pList); |
| 722 | return 1; |
| 723 | } |
| 724 | |
| 725 | /* |
| 726 | ** Invoke an SQL statement but ignore all the data in the result. Instead, |
| 727 | ** return a list that consists of the datatypes of the various columns. |
| 728 | ** |
| 729 | ** This only works if "PRAGMA show_datatypes=on" has been executed against |
| 730 | ** the database connection. |
| 731 | */ |
| 732 | static int sqlite_datatypes( |
| 733 | void *NotUsed, |
| 734 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 735 | int argc, /* Number of arguments */ |
| 736 | char **argv /* Text of each argument */ |
| 737 | ){ |
| 738 | sqlite *db; |
| 739 | int rc; |
| 740 | if( argc!=3 ){ |
| 741 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 742 | " DB SQL", 0); |
| 743 | return TCL_ERROR; |
| 744 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 745 | if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 746 | rc = sqlite3_exec(db, argv[2], rememberDataTypes, interp, 0); |
drh | 5a38705 | 2003-01-11 14:19:51 +0000 | [diff] [blame] | 747 | if( rc!=0 && rc!=SQLITE_ABORT ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 748 | Tcl_AppendResult(interp, sqlite3_error_string(rc), 0); |
drh | 5a38705 | 2003-01-11 14:19:51 +0000 | [diff] [blame] | 749 | return TCL_ERROR; |
| 750 | } |
| 751 | return TCL_OK; |
| 752 | } |
| 753 | |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 754 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 755 | ** Usage: sqlite3_step VM ?NVAR? ?VALUEVAR? ?COLNAMEVAR? |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 756 | ** |
| 757 | ** Step a virtual machine. Return a the result code as a string. |
| 758 | ** Column results are written into three variables. |
| 759 | */ |
| 760 | static int test_step( |
| 761 | void *NotUsed, |
| 762 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 763 | int argc, /* Number of arguments */ |
| 764 | char **argv /* Text of each argument */ |
| 765 | ){ |
| 766 | sqlite_vm *vm; |
| 767 | int rc, i; |
drh | 3a84069 | 2003-01-29 22:58:26 +0000 | [diff] [blame] | 768 | const char **azValue = 0; |
| 769 | const char **azColName = 0; |
| 770 | int N = 0; |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 771 | char *zRc; |
| 772 | char zBuf[50]; |
drh | 073e5a7 | 2003-07-09 00:28:13 +0000 | [diff] [blame] | 773 | if( argc<2 || argc>5 ){ |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 774 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
| 775 | " VM NVAR VALUEVAR COLNAMEVAR", 0); |
| 776 | return TCL_ERROR; |
| 777 | } |
| 778 | if( getVmPointer(interp, argv[1], &vm) ) return TCL_ERROR; |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 779 | rc = sqlite3_step(vm, argc>=3?&N:0, argc>=4?&azValue:0, argc==5?&azColName:0); |
drh | 073e5a7 | 2003-07-09 00:28:13 +0000 | [diff] [blame] | 780 | if( argc>=3 ){ |
| 781 | sprintf(zBuf, "%d", N); |
| 782 | Tcl_SetVar(interp, argv[2], zBuf, 0); |
| 783 | } |
| 784 | if( argc>=4 ){ |
| 785 | Tcl_SetVar(interp, argv[3], "", 0); |
| 786 | if( azValue ){ |
| 787 | for(i=0; i<N; i++){ |
| 788 | Tcl_SetVar(interp, argv[3], azValue[i] ? azValue[i] : "", |
| 789 | TCL_APPEND_VALUE | TCL_LIST_ELEMENT); |
| 790 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 791 | } |
drh | 3a84069 | 2003-01-29 22:58:26 +0000 | [diff] [blame] | 792 | } |
drh | 073e5a7 | 2003-07-09 00:28:13 +0000 | [diff] [blame] | 793 | if( argc==5 ){ |
| 794 | Tcl_SetVar(interp, argv[4], "", 0); |
| 795 | if( azColName ){ |
| 796 | for(i=0; i<N*2; i++){ |
| 797 | Tcl_SetVar(interp, argv[4], azColName[i] ? azColName[i] : "", |
| 798 | TCL_APPEND_VALUE | TCL_LIST_ELEMENT); |
| 799 | } |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 800 | } |
| 801 | } |
| 802 | switch( rc ){ |
| 803 | case SQLITE_DONE: zRc = "SQLITE_DONE"; break; |
drh | 326dce7 | 2003-01-29 14:06:07 +0000 | [diff] [blame] | 804 | case SQLITE_BUSY: zRc = "SQLITE_BUSY"; break; |
| 805 | case SQLITE_ROW: zRc = "SQLITE_ROW"; break; |
| 806 | case SQLITE_ERROR: zRc = "SQLITE_ERROR"; break; |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 807 | case SQLITE_MISUSE: zRc = "SQLITE_MISUSE"; break; |
| 808 | default: zRc = "unknown"; break; |
| 809 | } |
| 810 | Tcl_AppendResult(interp, zRc, 0); |
| 811 | return TCL_OK; |
| 812 | } |
| 813 | |
| 814 | /* |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 815 | ** Usage: sqlite3_finalize STMT |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 816 | ** |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 817 | ** Finalize a statement handle. |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 818 | */ |
| 819 | static int test_finalize( |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 820 | void * clientData, |
| 821 | Tcl_Interp *interp, |
| 822 | int objc, |
| 823 | Tcl_Obj *CONST objv[] |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 824 | ){ |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 825 | sqlite3_stmt *pStmt; |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 826 | int rc; |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 827 | |
| 828 | if( objc!=2 ){ |
| 829 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 830 | Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0); |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 831 | return TCL_ERROR; |
| 832 | } |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 833 | |
| 834 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 835 | |
| 836 | rc = sqlite3_finalize_new(pStmt); |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 837 | if( rc ){ |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 838 | return TCL_ERROR; |
| 839 | } |
| 840 | return TCL_OK; |
| 841 | } |
| 842 | |
| 843 | /* |
| 844 | ** Usage: sqlite3_reset STMT |
| 845 | ** |
| 846 | ** Finalize a statement handle. |
| 847 | */ |
| 848 | static int test_reset( |
| 849 | void * clientData, |
| 850 | Tcl_Interp *interp, |
| 851 | int objc, |
| 852 | Tcl_Obj *CONST objv[] |
| 853 | ){ |
| 854 | sqlite3_stmt *pStmt; |
| 855 | int rc; |
| 856 | |
| 857 | if( objc!=2 ){ |
| 858 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 859 | Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0); |
| 860 | return TCL_ERROR; |
| 861 | } |
| 862 | |
| 863 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 864 | |
| 865 | rc = sqlite3_reset_new(pStmt); |
| 866 | if( rc ){ |
drh | b86ccfb | 2003-01-28 23:13:10 +0000 | [diff] [blame] | 867 | return TCL_ERROR; |
| 868 | } |
| 869 | return TCL_OK; |
| 870 | } |
| 871 | |
drh | 5a38705 | 2003-01-11 14:19:51 +0000 | [diff] [blame] | 872 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 873 | ** Usage: sqlite3_reset VM |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 874 | ** |
| 875 | ** Reset a virtual machine and prepare it to be run again. |
| 876 | */ |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 877 | |
| 878 | /* |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 879 | ** This is the "static_bind_value" that variables are bound to when |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 880 | ** the FLAG option of sqlite3_bind is "static" |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 881 | */ |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 882 | static char *sqlite_static_bind_value = 0; |
| 883 | |
| 884 | /* |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 885 | ** Usage: sqlite3_bind VM IDX VALUE FLAGS |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 886 | ** |
| 887 | ** Sets the value of the IDX-th occurance of "?" in the original SQL |
| 888 | ** string. VALUE is the new value. If FLAGS=="null" then VALUE is |
| 889 | ** ignored and the value is set to NULL. If FLAGS=="static" then |
| 890 | ** the value is set to the value of a static variable named |
| 891 | ** "sqlite_static_bind_value". If FLAGS=="normal" then a copy |
| 892 | ** of the VALUE is made. |
| 893 | */ |
| 894 | static int test_bind( |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 895 | void *NotUsed, |
| 896 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 897 | int argc, /* Number of arguments */ |
| 898 | char **argv /* Text of each argument */ |
| 899 | ){ |
| 900 | sqlite_vm *vm; |
| 901 | int rc; |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 902 | int idx; |
| 903 | if( argc!=5 ){ |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 904 | Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 905 | " VM IDX VALUE (null|static|normal)\"", 0); |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 906 | return TCL_ERROR; |
| 907 | } |
| 908 | if( getVmPointer(interp, argv[1], &vm) ) return TCL_ERROR; |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 909 | if( Tcl_GetInt(interp, argv[2], &idx) ) return TCL_ERROR; |
| 910 | if( strcmp(argv[4],"null")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 911 | rc = sqlite3_bind(vm, idx, 0, 0, 0); |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 912 | }else if( strcmp(argv[4],"static")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 913 | rc = sqlite3_bind(vm, idx, sqlite_static_bind_value, -1, 0); |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 914 | }else if( strcmp(argv[4],"normal")==0 ){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 915 | rc = sqlite3_bind(vm, idx, argv[3], -1, 1); |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 916 | }else{ |
| 917 | Tcl_AppendResult(interp, "4th argument should be " |
| 918 | "\"null\" or \"static\" or \"normal\"", 0); |
| 919 | return TCL_ERROR; |
| 920 | } |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 921 | if( rc ){ |
| 922 | char zBuf[50]; |
| 923 | sprintf(zBuf, "(%d) ", rc); |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 924 | Tcl_AppendResult(interp, zBuf, sqlite3_error_string(rc), 0); |
drh | 5045789 | 2003-09-06 01:10:47 +0000 | [diff] [blame] | 925 | return TCL_ERROR; |
| 926 | } |
| 927 | return TCL_OK; |
| 928 | } |
| 929 | |
| 930 | /* |
drh | 99ee360 | 2003-02-16 19:13:36 +0000 | [diff] [blame] | 931 | ** Usage: breakpoint |
| 932 | ** |
| 933 | ** This routine exists for one purpose - to provide a place to put a |
| 934 | ** breakpoint with GDB that can be triggered using TCL code. The use |
| 935 | ** for this is when a particular test fails on (say) the 1485th iteration. |
| 936 | ** In the TCL test script, we can add code like this: |
| 937 | ** |
| 938 | ** if {$i==1485} breakpoint |
| 939 | ** |
| 940 | ** Then run testfixture in the debugger and wait for the breakpoint to |
| 941 | ** fire. Then additional breakpoints can be set to trace down the bug. |
| 942 | */ |
| 943 | static int test_breakpoint( |
| 944 | void *NotUsed, |
| 945 | Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
| 946 | int argc, /* Number of arguments */ |
| 947 | char **argv /* Text of each argument */ |
| 948 | ){ |
| 949 | return TCL_OK; /* Do nothing */ |
| 950 | } |
| 951 | |
danielk1977 | 51e3d8e | 2004-05-20 01:12:34 +0000 | [diff] [blame] | 952 | static int test_bind_int32( |
| 953 | void * clientData, |
| 954 | Tcl_Interp *interp, |
| 955 | int objc, |
| 956 | Tcl_Obj *CONST objv[] |
| 957 | ){ |
| 958 | sqlite3_stmt *pStmt; |
| 959 | int idx; |
| 960 | int value; |
| 961 | int rc; |
| 962 | |
| 963 | if( objc!=4 ){ |
| 964 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 965 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); |
| 966 | return TCL_ERROR; |
| 967 | } |
| 968 | |
| 969 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 970 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 971 | if( Tcl_GetIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; |
| 972 | |
| 973 | rc = sqlite3_bind_int32(pStmt, idx, value); |
| 974 | if( rc!=SQLITE_OK ){ |
| 975 | return TCL_ERROR; |
| 976 | } |
| 977 | |
| 978 | return TCL_OK; |
| 979 | } |
| 980 | |
| 981 | static int test_bind_int64( |
| 982 | void * clientData, |
| 983 | Tcl_Interp *interp, |
| 984 | int objc, |
| 985 | Tcl_Obj *CONST objv[] |
| 986 | ){ |
| 987 | sqlite3_stmt *pStmt; |
| 988 | int idx; |
| 989 | i64 value; |
| 990 | int rc; |
| 991 | |
| 992 | if( objc!=4 ){ |
| 993 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 994 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); |
| 995 | return TCL_ERROR; |
| 996 | } |
| 997 | |
| 998 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 999 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1000 | if( Tcl_GetWideIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; |
| 1001 | |
| 1002 | rc = sqlite3_bind_int64(pStmt, idx, value); |
| 1003 | if( rc!=SQLITE_OK ){ |
| 1004 | return TCL_ERROR; |
| 1005 | } |
| 1006 | |
| 1007 | return TCL_OK; |
| 1008 | } |
| 1009 | |
| 1010 | static int test_bind_double( |
| 1011 | void * clientData, |
| 1012 | Tcl_Interp *interp, |
| 1013 | int objc, |
| 1014 | Tcl_Obj *CONST objv[] |
| 1015 | ){ |
| 1016 | sqlite3_stmt *pStmt; |
| 1017 | int idx; |
| 1018 | double value; |
| 1019 | int rc; |
| 1020 | |
| 1021 | if( objc!=4 ){ |
| 1022 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1023 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>", 0); |
| 1024 | return TCL_ERROR; |
| 1025 | } |
| 1026 | |
| 1027 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1028 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1029 | if( Tcl_GetDoubleFromObj(interp, objv[3], &value) ) return TCL_ERROR; |
| 1030 | |
| 1031 | rc = sqlite3_bind_double(pStmt, idx, value); |
| 1032 | if( rc!=SQLITE_OK ){ |
| 1033 | return TCL_ERROR; |
| 1034 | } |
| 1035 | |
| 1036 | return TCL_OK; |
| 1037 | } |
| 1038 | |
| 1039 | static int test_bind_null( |
| 1040 | void * clientData, |
| 1041 | Tcl_Interp *interp, |
| 1042 | int objc, |
| 1043 | Tcl_Obj *CONST objv[] |
| 1044 | ){ |
| 1045 | sqlite3_stmt *pStmt; |
| 1046 | int idx; |
| 1047 | int rc; |
| 1048 | |
| 1049 | if( objc!=3 ){ |
| 1050 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1051 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.>", 0); |
| 1052 | return TCL_ERROR; |
| 1053 | } |
| 1054 | |
| 1055 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1056 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1057 | |
| 1058 | rc = sqlite3_bind_null(pStmt, idx); |
| 1059 | if( rc!=SQLITE_OK ){ |
| 1060 | return TCL_ERROR; |
| 1061 | } |
| 1062 | |
| 1063 | return TCL_OK; |
| 1064 | } |
| 1065 | |
| 1066 | static int test_bind_text( |
| 1067 | void * clientData, |
| 1068 | Tcl_Interp *interp, |
| 1069 | int objc, |
| 1070 | Tcl_Obj *CONST objv[] |
| 1071 | ){ |
| 1072 | sqlite3_stmt *pStmt; |
| 1073 | int idx; |
| 1074 | int bytes; |
| 1075 | char *value; |
| 1076 | int rc; |
| 1077 | |
| 1078 | if( objc!=5 ){ |
| 1079 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1080 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" |
| 1081 | " <bytes>", 0); |
| 1082 | return TCL_ERROR; |
| 1083 | } |
| 1084 | |
| 1085 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1086 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1087 | value = Tcl_GetString(objv[3]); |
| 1088 | if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; |
| 1089 | |
| 1090 | rc = sqlite3_bind_text(pStmt, idx, value, bytes, 1); |
| 1091 | if( rc!=SQLITE_OK ){ |
| 1092 | return TCL_ERROR; |
| 1093 | } |
| 1094 | |
| 1095 | return TCL_OK; |
| 1096 | } |
| 1097 | |
| 1098 | static int test_bind_text16( |
| 1099 | void * clientData, |
| 1100 | Tcl_Interp *interp, |
| 1101 | int objc, |
| 1102 | Tcl_Obj *CONST objv[] |
| 1103 | ){ |
| 1104 | sqlite3_stmt *pStmt; |
| 1105 | int idx; |
| 1106 | int bytes; |
| 1107 | char *value; |
| 1108 | int rc; |
| 1109 | |
| 1110 | if( objc!=5 ){ |
| 1111 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1112 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" |
| 1113 | " <bytes>", 0); |
| 1114 | return TCL_ERROR; |
| 1115 | } |
| 1116 | |
| 1117 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1118 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1119 | value = Tcl_GetByteArrayFromObj(objv[3], 0); |
| 1120 | if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; |
| 1121 | |
| 1122 | rc = sqlite3_bind_text16(pStmt, idx, (void *)value, bytes, 1); |
| 1123 | if( rc!=SQLITE_OK ){ |
| 1124 | return TCL_ERROR; |
| 1125 | } |
| 1126 | |
| 1127 | return TCL_OK; |
| 1128 | } |
| 1129 | |
| 1130 | static int test_bind_blob( |
| 1131 | void * clientData, |
| 1132 | Tcl_Interp *interp, |
| 1133 | int objc, |
| 1134 | Tcl_Obj *CONST objv[] |
| 1135 | ){ |
| 1136 | sqlite3_stmt *pStmt; |
| 1137 | int idx; |
| 1138 | int bytes; |
| 1139 | char *value; |
| 1140 | int rc; |
| 1141 | |
| 1142 | if( objc!=5 ){ |
| 1143 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1144 | Tcl_GetStringFromObj(objv[0], 0), " <STMT> <param no.> <value>" |
| 1145 | " <bytes>", 0); |
| 1146 | return TCL_ERROR; |
| 1147 | } |
| 1148 | |
| 1149 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1150 | if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; |
| 1151 | value = Tcl_GetString(objv[3]); |
| 1152 | if( Tcl_GetIntFromObj(interp, objv[2], &bytes) ) return TCL_ERROR; |
| 1153 | |
| 1154 | rc = sqlite3_bind_blob(pStmt, idx, value, bytes, 1); |
| 1155 | if( rc!=SQLITE_OK ){ |
| 1156 | return TCL_ERROR; |
| 1157 | } |
| 1158 | |
| 1159 | return TCL_OK; |
| 1160 | } |
| 1161 | |
drh | 99ee360 | 2003-02-16 19:13:36 +0000 | [diff] [blame] | 1162 | /* |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1163 | ** Usage: sqlite3_errcode DB |
| 1164 | ** |
| 1165 | ** Return the string representation of the most recent sqlite3_* API |
| 1166 | ** error code. e.g. "SQLITE_ERROR". |
| 1167 | */ |
| 1168 | static int test_errcode( |
| 1169 | void * clientData, |
| 1170 | Tcl_Interp *interp, |
| 1171 | int objc, |
| 1172 | Tcl_Obj *CONST objv[] |
| 1173 | ){ |
| 1174 | sqlite3 *db; |
| 1175 | |
| 1176 | if( objc!=2 ){ |
| 1177 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1178 | Tcl_GetString(objv[0]), " DB", 0); |
| 1179 | return TCL_ERROR; |
| 1180 | } |
| 1181 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1182 | Tcl_SetResult(interp, (char *)errorName(sqlite3_errcode(db)), 0); |
| 1183 | return TCL_OK; |
| 1184 | } |
| 1185 | |
| 1186 | /* |
| 1187 | ** Usage: test_errmsg DB |
| 1188 | ** |
| 1189 | ** Returns the UTF-8 representation of the error message string for the |
| 1190 | ** most recent sqlite3_* API call. |
| 1191 | */ |
| 1192 | static int test_errmsg( |
| 1193 | void * clientData, |
| 1194 | Tcl_Interp *interp, |
| 1195 | int objc, |
| 1196 | Tcl_Obj *CONST objv[] |
| 1197 | ){ |
| 1198 | sqlite *db; |
| 1199 | const char *zErr; |
| 1200 | |
| 1201 | if( objc!=2 ){ |
| 1202 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1203 | Tcl_GetString(objv[0]), " DB", 0); |
| 1204 | return TCL_ERROR; |
| 1205 | } |
| 1206 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1207 | |
| 1208 | zErr = sqlite3_errmsg(db); |
| 1209 | Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); |
| 1210 | return TCL_OK; |
| 1211 | } |
| 1212 | |
| 1213 | /* |
| 1214 | ** Usage: test_errmsg16 DB |
| 1215 | ** |
| 1216 | ** Returns the UTF-16 representation of the error message string for the |
| 1217 | ** most recent sqlite3_* API call. This is a byte array object at the TCL |
| 1218 | ** level, and it includes the 0x00 0x00 terminator bytes at the end of the |
| 1219 | ** UTF-16 string. |
| 1220 | */ |
| 1221 | static int test_errmsg16( |
| 1222 | void * clientData, |
| 1223 | Tcl_Interp *interp, |
| 1224 | int objc, |
| 1225 | Tcl_Obj *CONST objv[] |
| 1226 | ){ |
| 1227 | sqlite *db; |
| 1228 | const void *zErr; |
| 1229 | int bytes; |
| 1230 | |
| 1231 | if( objc!=2 ){ |
| 1232 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1233 | Tcl_GetString(objv[0]), " DB", 0); |
| 1234 | return TCL_ERROR; |
| 1235 | } |
| 1236 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1237 | |
| 1238 | zErr = sqlite3_errmsg16(db); |
| 1239 | bytes = sqlite3utf16ByteLen(zErr, -1); |
| 1240 | Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zErr, bytes)); |
| 1241 | return TCL_OK; |
| 1242 | } |
| 1243 | |
| 1244 | /* |
| 1245 | ** Usage: sqlite3_prepare DB sql bytes tailvar |
| 1246 | ** |
| 1247 | ** Compile up to <bytes> bytes of the supplied SQL string <sql> using |
| 1248 | ** database handle <DB>. The parameter <tailval> is the name of a global |
| 1249 | ** variable that is set to the unused portion of <sql> (if any). A |
| 1250 | ** STMT handle is returned. |
| 1251 | */ |
| 1252 | static int test_prepare( |
| 1253 | void * clientData, |
| 1254 | Tcl_Interp *interp, |
| 1255 | int objc, |
| 1256 | Tcl_Obj *CONST objv[] |
| 1257 | ){ |
| 1258 | sqlite3 *db; |
| 1259 | const char *zSql; |
| 1260 | int bytes; |
| 1261 | const char *zTail = 0; |
| 1262 | sqlite3_stmt *pStmt = 0; |
| 1263 | char zBuf[50]; |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1264 | int rc; |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1265 | |
| 1266 | if( objc!=5 ){ |
| 1267 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1268 | Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); |
| 1269 | return TCL_ERROR; |
| 1270 | } |
| 1271 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1272 | zSql = Tcl_GetString(objv[2]); |
| 1273 | if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; |
| 1274 | |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1275 | rc = sqlite3_prepare(db, zSql, bytes, &pStmt, &zTail); |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1276 | if( zTail ){ |
| 1277 | if( bytes>=0 ){ |
| 1278 | bytes = bytes - (zTail-zSql); |
| 1279 | } |
| 1280 | Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0); |
| 1281 | } |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1282 | if( rc!=SQLITE_OK ){ |
| 1283 | assert( pStmt==0 ); |
| 1284 | sprintf(zBuf, "(%d) ", rc); |
| 1285 | Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0); |
| 1286 | return TCL_ERROR; |
| 1287 | } |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1288 | |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1289 | if( pStmt ){ |
| 1290 | if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; |
| 1291 | Tcl_AppendResult(interp, zBuf, 0); |
| 1292 | } |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1293 | return TCL_OK; |
| 1294 | } |
| 1295 | |
| 1296 | /* |
| 1297 | ** Usage: sqlite3_prepare DB sql bytes tailvar |
| 1298 | ** |
| 1299 | ** Compile up to <bytes> bytes of the supplied SQL string <sql> using |
| 1300 | ** database handle <DB>. The parameter <tailval> is the name of a global |
| 1301 | ** variable that is set to the unused portion of <sql> (if any). A |
| 1302 | ** STMT handle is returned. |
| 1303 | */ |
| 1304 | static int test_prepare16( |
| 1305 | void * clientData, |
| 1306 | Tcl_Interp *interp, |
| 1307 | int objc, |
| 1308 | Tcl_Obj *CONST objv[] |
| 1309 | ){ |
| 1310 | sqlite3 *db; |
| 1311 | const void *zSql; |
| 1312 | const void *zTail = 0; |
| 1313 | Tcl_Obj *pTail = 0; |
| 1314 | sqlite3_stmt *pStmt = 0; |
| 1315 | char zBuf[50]; |
| 1316 | int bytes; /* The integer specified as arg 3 */ |
| 1317 | int objlen; /* The byte-array length of arg 2 */ |
| 1318 | |
| 1319 | if( objc!=5 ){ |
| 1320 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1321 | Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); |
| 1322 | return TCL_ERROR; |
| 1323 | } |
| 1324 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1325 | zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen); |
| 1326 | if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; |
| 1327 | |
| 1328 | if( SQLITE_OK!=sqlite3_prepare16(db, zSql, bytes, &pStmt, &zTail) ){ |
| 1329 | return TCL_ERROR; |
| 1330 | } |
| 1331 | |
| 1332 | if( zTail ){ |
| 1333 | objlen = objlen - ((u8 *)zTail-(u8 *)zSql); |
| 1334 | }else{ |
| 1335 | objlen = 0; |
| 1336 | } |
| 1337 | pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen); |
| 1338 | Tcl_IncrRefCount(pTail); |
| 1339 | Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0); |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1340 | Tcl_DecrRefCount(pTail); |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1341 | |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1342 | if( pStmt ){ |
| 1343 | if( makePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; |
| 1344 | } |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1345 | Tcl_AppendResult(interp, zBuf, 0); |
| 1346 | return TCL_OK; |
| 1347 | } |
| 1348 | |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1349 | /* |
| 1350 | ** Usage: sqlite3_open filename ?options-list? |
| 1351 | */ |
| 1352 | static int test_open( |
| 1353 | void * clientData, |
| 1354 | Tcl_Interp *interp, |
| 1355 | int objc, |
| 1356 | Tcl_Obj *CONST objv[] |
| 1357 | ){ |
| 1358 | const char *zFilename; |
| 1359 | sqlite3 *db; |
| 1360 | int rc; |
| 1361 | char zBuf[100]; |
| 1362 | |
| 1363 | if( objc!=3 && objc!=2 ){ |
| 1364 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1365 | Tcl_GetString(objv[0]), " filename options-list", 0); |
| 1366 | return TCL_ERROR; |
| 1367 | } |
| 1368 | |
| 1369 | zFilename = Tcl_GetString(objv[1]); |
| 1370 | rc = sqlite3_open_new(zFilename, &db, 0); |
| 1371 | |
| 1372 | if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR; |
| 1373 | Tcl_AppendResult(interp, zBuf, 0); |
| 1374 | return TCL_OK; |
| 1375 | } |
| 1376 | |
| 1377 | /* |
| 1378 | ** Usage: sqlite3_open16 filename options |
| 1379 | */ |
| 1380 | static int test_open16( |
| 1381 | void * clientData, |
| 1382 | Tcl_Interp *interp, |
| 1383 | int objc, |
| 1384 | Tcl_Obj *CONST objv[] |
| 1385 | ){ |
| 1386 | const void *zFilename; |
| 1387 | sqlite3 *db; |
| 1388 | int rc; |
| 1389 | char zBuf[100]; |
| 1390 | |
| 1391 | if( objc!=3 ){ |
| 1392 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1393 | Tcl_GetString(objv[0]), " filename options-list", 0); |
| 1394 | return TCL_ERROR; |
| 1395 | } |
| 1396 | |
| 1397 | zFilename = Tcl_GetByteArrayFromObj(objv[1], 0); |
| 1398 | rc = sqlite3_open16(zFilename, &db, 0); |
| 1399 | |
| 1400 | if( makePointerStr(interp, zBuf, db) ) return TCL_ERROR; |
| 1401 | Tcl_AppendResult(interp, zBuf, 0); |
| 1402 | return TCL_OK; |
| 1403 | } |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1404 | |
| 1405 | /* |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 1406 | ** Usage: sqlite3_step STMT |
| 1407 | ** |
| 1408 | ** Advance the statement to the next row. |
| 1409 | */ |
| 1410 | static int test_step_new( |
| 1411 | void * clientData, |
| 1412 | Tcl_Interp *interp, |
| 1413 | int objc, |
| 1414 | Tcl_Obj *CONST objv[] |
| 1415 | ){ |
| 1416 | sqlite3_stmt *pStmt; |
| 1417 | int rc; |
| 1418 | |
| 1419 | if( objc!=3 ){ |
| 1420 | Tcl_AppendResult(interp, "wrong # args: should be \"", |
| 1421 | Tcl_GetString(objv[0]), " STMT", 0); |
| 1422 | return TCL_ERROR; |
| 1423 | } |
| 1424 | |
| 1425 | if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; |
| 1426 | rc = sqlite3_step_new(pStmt); |
| 1427 | |
| 1428 | if( rc!=SQLITE_OK ) return TCL_ERROR; |
| 1429 | return TCL_OK; |
| 1430 | } |
| 1431 | |
| 1432 | /* |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1433 | ** This is a collating function named "REVERSE" which sorts text |
| 1434 | ** in reverse order. |
| 1435 | */ |
| 1436 | static int reverseCollatingFunc( |
| 1437 | void *NotUsed, |
| 1438 | int nKey1, const void *pKey1, |
| 1439 | int nKey2, const void *pKey2 |
| 1440 | ){ |
| 1441 | int rc, n; |
| 1442 | n = nKey1<nKey2 ? nKey1 : nKey2; |
| 1443 | rc = memcmp(pKey1, pKey2, n); |
| 1444 | if( rc==0 ){ |
| 1445 | rc = nKey1 - nKey2; |
| 1446 | } |
| 1447 | return -rc; |
| 1448 | } |
| 1449 | |
| 1450 | /* |
| 1451 | ** Usage: add_reverse_collating_func DB |
| 1452 | ** |
| 1453 | ** This routine adds a collation named "REVERSE" to database given. |
| 1454 | ** REVERSE is used for testing only. |
| 1455 | */ |
| 1456 | static int reverse_collfunc( |
| 1457 | void * clientData, |
| 1458 | Tcl_Interp *interp, |
| 1459 | int objc, |
| 1460 | Tcl_Obj *CONST objv[] |
| 1461 | ){ |
| 1462 | sqlite3 *db; |
| 1463 | |
| 1464 | if( objc!=2 ){ |
| 1465 | Tcl_WrongNumArgs(interp, 1, objv, "DB"); |
| 1466 | return TCL_ERROR; |
| 1467 | } |
| 1468 | if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; |
| 1469 | sqlite3ChangeCollatingFunction(db, "REVERSE", 7, 0, reverseCollatingFunc); |
| 1470 | return TCL_OK; |
| 1471 | } |
| 1472 | |
| 1473 | |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1474 | /* |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 1475 | ** Register commands with the TCL interpreter. |
| 1476 | */ |
| 1477 | int Sqlitetest1_Init(Tcl_Interp *interp){ |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 1478 | extern int sqlite3_search_count; |
| 1479 | extern int sqlite3_interrupt_count; |
| 1480 | extern int sqlite3_open_file_count; |
| 1481 | extern int sqlite3_current_time; |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 1482 | static struct { |
| 1483 | char *zName; |
| 1484 | Tcl_CmdProc *xProc; |
| 1485 | } aCmd[] = { |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1486 | { "sqlite3_mprintf_int", (Tcl_CmdProc*)sqlite3_mprintf_int }, |
| 1487 | { "sqlite3_mprintf_str", (Tcl_CmdProc*)sqlite3_mprintf_str }, |
| 1488 | { "sqlite3_mprintf_double", (Tcl_CmdProc*)sqlite3_mprintf_double }, |
| 1489 | { "sqlite3_mprintf_scaled", (Tcl_CmdProc*)sqlite3_mprintf_scaled }, |
| 1490 | { "sqlite3_mprintf_z_test", (Tcl_CmdProc*)test_mprintf_z }, |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1491 | // { "sqlite3_open", (Tcl_CmdProc*)sqlite_test_open }, |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1492 | { "sqlite3_last_insert_rowid", (Tcl_CmdProc*)test_last_rowid }, |
| 1493 | { "sqlite3_exec_printf", (Tcl_CmdProc*)test_exec_printf }, |
| 1494 | { "sqlite3_get_table_printf", (Tcl_CmdProc*)test_get_table_printf }, |
| 1495 | { "sqlite3_close", (Tcl_CmdProc*)sqlite_test_close }, |
| 1496 | { "sqlite3_create_function", (Tcl_CmdProc*)test_create_function }, |
| 1497 | { "sqlite3_create_aggregate", (Tcl_CmdProc*)test_create_aggregate }, |
| 1498 | { "sqlite_register_test_function", (Tcl_CmdProc*)test_register_func }, |
| 1499 | { "sqlite_abort", (Tcl_CmdProc*)sqlite_abort }, |
| 1500 | { "sqlite_datatypes", (Tcl_CmdProc*)sqlite_datatypes }, |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 1501 | #ifdef MEMORY_DEBUG |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1502 | { "sqlite_malloc_fail", (Tcl_CmdProc*)sqlite_malloc_fail }, |
| 1503 | { "sqlite_malloc_stat", (Tcl_CmdProc*)sqlite_malloc_stat }, |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 1504 | #endif |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1505 | { "sqlite_step", (Tcl_CmdProc*)test_step }, |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 1506 | // { "sqlite_finalize", (Tcl_CmdProc*)test_finalize }, |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1507 | { "sqlite_bind", (Tcl_CmdProc*)test_bind }, |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 1508 | // { "sqlite_reset", (Tcl_CmdProc*)test_reset }, |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1509 | { "breakpoint", (Tcl_CmdProc*)test_breakpoint }, |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 1510 | }; |
danielk1977 | 51e3d8e | 2004-05-20 01:12:34 +0000 | [diff] [blame] | 1511 | static struct { |
| 1512 | char *zName; |
| 1513 | Tcl_ObjCmdProc *xProc; |
| 1514 | } aObjCmd[] = { |
| 1515 | { "sqlite3_bind_int32", (Tcl_ObjCmdProc*)test_bind_int32 }, |
| 1516 | { "sqlite3_bind_int64", (Tcl_ObjCmdProc*)test_bind_int64 }, |
| 1517 | { "sqlite3_bind_double", (Tcl_ObjCmdProc*)test_bind_double }, |
| 1518 | { "sqlite3_bind_null", (Tcl_ObjCmdProc*)test_bind_null }, |
| 1519 | { "sqlite3_bind_text", (Tcl_ObjCmdProc*)test_bind_text }, |
| 1520 | { "sqlite3_bind_text16", (Tcl_ObjCmdProc*)test_bind_text16 }, |
| 1521 | { "sqlite3_bind_blob", (Tcl_ObjCmdProc*)test_bind_blob }, |
danielk1977 | 6622cce | 2004-05-20 11:00:52 +0000 | [diff] [blame] | 1522 | { "sqlite3_errcode", (Tcl_ObjCmdProc*)test_errcode }, |
| 1523 | { "sqlite3_errmsg", (Tcl_ObjCmdProc*)test_errmsg }, |
| 1524 | { "sqlite3_errmsg16", (Tcl_ObjCmdProc*)test_errmsg16 }, |
| 1525 | { "sqlite3_prepare", (Tcl_ObjCmdProc*)test_prepare }, |
| 1526 | { "sqlite3_prepare16", (Tcl_ObjCmdProc*)test_prepare16 }, |
danielk1977 | 4ad1713 | 2004-05-21 01:47:26 +0000 | [diff] [blame] | 1527 | { "sqlite3_open", (Tcl_ObjCmdProc*)test_open }, |
| 1528 | { "sqlite3_open16", (Tcl_ObjCmdProc*)test_open16 }, |
danielk1977 | 106bb23 | 2004-05-21 10:08:53 +0000 | [diff] [blame^] | 1529 | { "sqlite3_finalize", (Tcl_ObjCmdProc*)test_finalize }, |
| 1530 | { "sqlite3_reset", (Tcl_ObjCmdProc*)test_reset }, |
| 1531 | { "sqlite3_step", (Tcl_ObjCmdProc*)test_step_new }, |
drh | d3d39e9 | 2004-05-20 22:16:29 +0000 | [diff] [blame] | 1532 | { "add_reverse_collating_func", (Tcl_ObjCmdProc*)reverse_collfunc }, |
danielk1977 | 51e3d8e | 2004-05-20 01:12:34 +0000 | [diff] [blame] | 1533 | }; |
drh | c2eef3b | 2002-08-31 18:53:06 +0000 | [diff] [blame] | 1534 | int i; |
| 1535 | |
| 1536 | for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ |
| 1537 | Tcl_CreateCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); |
| 1538 | } |
danielk1977 | 51e3d8e | 2004-05-20 01:12:34 +0000 | [diff] [blame] | 1539 | for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ |
| 1540 | Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0); |
| 1541 | } |
danielk1977 | 6490beb | 2004-05-11 06:17:21 +0000 | [diff] [blame] | 1542 | Tcl_LinkVar(interp, "sqlite_search_count", |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 1543 | (char*)&sqlite3_search_count, TCL_LINK_INT); |
danielk1977 | 6490beb | 2004-05-11 06:17:21 +0000 | [diff] [blame] | 1544 | Tcl_LinkVar(interp, "sqlite_interrupt_count", |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 1545 | (char*)&sqlite3_interrupt_count, TCL_LINK_INT); |
danielk1977 | 6490beb | 2004-05-11 06:17:21 +0000 | [diff] [blame] | 1546 | Tcl_LinkVar(interp, "sqlite_open_file_count", |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 1547 | (char*)&sqlite3_open_file_count, TCL_LINK_INT); |
danielk1977 | 6490beb | 2004-05-11 06:17:21 +0000 | [diff] [blame] | 1548 | Tcl_LinkVar(interp, "sqlite_current_time", |
danielk1977 | 6f8a503 | 2004-05-10 10:34:51 +0000 | [diff] [blame] | 1549 | (char*)&sqlite3_current_time, TCL_LINK_INT); |
drh | 7c972de | 2003-09-06 22:18:07 +0000 | [diff] [blame] | 1550 | Tcl_LinkVar(interp, "sqlite_static_bind_value", |
| 1551 | (char*)&sqlite_static_bind_value, TCL_LINK_STRING); |
drh | d1bf351 | 2001-04-07 15:24:33 +0000 | [diff] [blame] | 1552 | return TCL_OK; |
| 1553 | } |