wasCSharpSQLite – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using sqlite_int64 = System.Int64; |
||
4 | using sqlite_u3264 = System.UInt64; |
||
5 | using u32 = System.UInt32; |
||
6 | |||
7 | namespace Community.CsharpSqlite |
||
8 | { |
||
9 | #if TCLSH |
||
10 | using tcl.lang; |
||
11 | using ClientData = System.Object; |
||
12 | |||
13 | #if !SQLITE_OMIT_INCRBLOB |
||
14 | using sqlite3_blob = sqlite.Incrblob; |
||
15 | #endif |
||
16 | using sqlite3_stmt = Sqlite3.Vdbe; |
||
17 | using Tcl_DString = tcl.lang.TclString; |
||
18 | using Tcl_Interp = tcl.lang.Interp; |
||
19 | using Tcl_Obj = tcl.lang.TclObject; |
||
20 | using Tcl_WideInt = System.Int64; |
||
21 | |||
22 | using sqlite3_value = Sqlite3.Mem; |
||
23 | using System.IO; |
||
24 | using System.Text; |
||
25 | |||
26 | public partial class Sqlite3 |
||
27 | { |
||
28 | /* |
||
29 | ** 2001 September 15 |
||
30 | ** |
||
31 | ** The author disclaims copyright to this source code. In place of |
||
32 | ** a legal notice, here is a blessing: |
||
33 | ** |
||
34 | ** May you do good and not evil. |
||
35 | ** May you find forgiveness for yourself and forgive others. |
||
36 | ** May you share freely, never taking more than you give. |
||
37 | ** |
||
38 | ************************************************************************* |
||
39 | ** A TCL Interface to SQLite. Append this file to sqlite3.c and |
||
40 | ** compile the whole thing to build a TCL-enabled version of SQLite. |
||
41 | ** |
||
42 | ** Compile-time options: |
||
43 | ** |
||
44 | ** -DTCLSH=1 Add a "main()" routine that works as a tclsh. |
||
45 | ** |
||
46 | ** -DSQLITE_TCLMD5 When used in conjuction with -DTCLSH=1, add |
||
47 | ** four new commands to the TCL interpreter for |
||
48 | ** generating MD5 checksums: md5, md5file, |
||
49 | ** md5-10x8, and md5file-10x8. |
||
50 | ** |
||
51 | ** -DSQLITE_TEST When used in conjuction with -DTCLSH=1, add |
||
52 | ** hundreds of new commands used for testing |
||
53 | ** SQLite. This option implies -DSQLITE_TCLMD5. |
||
54 | ************************************************************************* |
||
55 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
56 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
57 | ** |
||
58 | ** SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2 |
||
59 | ** |
||
60 | ************************************************************************* |
||
61 | */ |
||
62 | //#include "tcl.h" |
||
63 | //#include <errno.h> |
||
64 | |||
65 | /* |
||
66 | ** Some additional include files are needed if this file is not |
||
67 | ** appended to the amalgamation. |
||
68 | */ |
||
69 | #if !SQLITE_AMALGAMATION |
||
70 | //# include "sqlite3.h" |
||
71 | //# include <stdlib.h> |
||
72 | //# include <string.h> |
||
73 | //# include <Debug.Assert.h> |
||
74 | // typedef unsigned char u8; |
||
75 | #endif |
||
76 | |||
77 | /* |
||
78 | * Windows needs to know which symbols to export. Unix does not. |
||
79 | * BUILD_sqlite should be undefined for Unix. |
||
80 | */ |
||
81 | #if BUILD_sqlite |
||
82 | //#undef TCL.Tcl_STORAGE_CLASS |
||
83 | //#define TCL.Tcl_STORAGE_CLASS DLLEXPORT |
||
84 | #endif // * BUILD_sqlite */ |
||
85 | |||
86 | const int NUM_PREPARED_STMTS = 10;//#define NUM_PREPARED_STMTS 10 |
||
87 | const int MAX_PREPARED_STMTS = 100;//#define MAX_PREPARED_STMTS 100 |
||
88 | |||
89 | /* |
||
90 | ** If TCL uses UTF-8 and SQLite is configured to use iso8859, then we |
||
91 | ** have to do a translation when going between the two. Set the |
||
92 | ** UTF_TRANSLATION_NEEDED macro to indicate that we need to do |
||
93 | ** this translation. |
||
94 | */ |
||
95 | #if Tcl_UTF_MAX && !SQLITE_UTF8 |
||
96 | //# define UTF_TRANSLATION_NEEDED 1 |
||
97 | #endif |
||
98 | |||
99 | /* |
||
100 | ** New SQL functions can be created as TCL scripts. Each such function |
||
101 | ** is described by an instance of the following structure. |
||
102 | */ |
||
103 | //typedef struct SqlFunc SqlFunc; |
||
104 | public class SqlFunc |
||
105 | { |
||
106 | public Tcl_Interp interp; /* The TCL interpret to execute the function */ |
||
107 | public Tcl_Obj pScript; /* The Tcl_Obj representation of the script */ |
||
108 | public int useEvalObjv; /* True if it is safe to use TCL.Tcl_EvalObjv */ |
||
109 | public string zName; /* Name of this function */ |
||
110 | public SqlFunc pNext; /* Next function on the list of them all */ |
||
111 | } |
||
112 | |||
113 | /* |
||
114 | ** New collation sequences function can be created as TCL scripts. Each such |
||
115 | ** function is described by an instance of the following structure. |
||
116 | */ |
||
117 | //typedef struct SqlCollate SqlCollate; |
||
118 | public class SqlCollate |
||
119 | { |
||
120 | public Tcl_Interp interp; /* The TCL interpret to execute the function */ |
||
121 | public string zScript; /* The script to be run */ |
||
122 | public SqlCollate pNext; /* Next function on the list of them all */ |
||
123 | } |
||
124 | |||
125 | /* |
||
126 | ** Prepared statements are cached for faster execution. Each prepared |
||
127 | ** statement is described by an instance of the following structure. |
||
128 | */ |
||
129 | //typedef struct SqlPreparedStmt SqlPreparedStmt; |
||
130 | public class SqlPreparedStmt |
||
131 | { |
||
132 | public SqlPreparedStmt pNext; /* Next in linked list */ |
||
133 | public SqlPreparedStmt pPrev; /* Previous on the list */ |
||
134 | public sqlite3_stmt pStmt; /* The prepared statement */ |
||
135 | public Mem[] aMem; /* Original Memory Values to be reused */ |
||
136 | public int nSql; /* chars in zSql[] */ |
||
137 | public string zSql; /* Text of the SQL statement */ |
||
138 | public int nParm; /* Size of apParm array */ |
||
139 | public Tcl_Obj[] apParm; /* Array of referenced object pointers */ |
||
140 | } |
||
141 | |||
142 | //typedef struct IncrblobChannel IncrblobChannel; |
||
143 | |||
144 | /* |
||
145 | ** There is one instance of this structure for each SQLite database |
||
146 | ** that has been opened by the SQLite TCL interface. |
||
147 | */ |
||
148 | //typedef struct SqliteDb SqliteDb; |
||
149 | public class SqliteDb : object |
||
150 | { |
||
151 | public sqlite3 db; /* The "real" database structure. MUST BE FIRST */ |
||
152 | public Tcl_Interp interp; /* The interpreter used for this database */ |
||
153 | public string zBusy; /* The busy callback routine */ |
||
154 | public string zCommit; /* The commit hook callback routine */ |
||
155 | public string zTrace; /* The trace callback routine */ |
||
156 | public string zProfile; /* The profile callback routine */ |
||
157 | public string zProgress; /* The progress callback routine */ |
||
158 | public string zAuth; /* The authorization callback routine */ |
||
159 | public int disableAuth; /* Disable the authorizer if it exists */ |
||
160 | public string zNull = ""; /* Text to substitute for an SQL NULL value */ |
||
161 | public SqlFunc pFunc; /* List of SQL functions */ |
||
162 | public Tcl_Obj pUpdateHook; /* Update hook script (if any) */ |
||
163 | public Tcl_Obj pRollbackHook; /* Rollback hook script (if any) */ |
||
164 | public Tcl_Obj pWalHook; /* WAL hook script (if any) */ |
||
165 | public Tcl_Obj pUnlockNotify; /* Unlock notify script (if any) */ |
||
166 | public SqlCollate pCollate; /* List of SQL collation functions */ |
||
167 | public int rc; /* Return code of most recent sqlite3_exec() */ |
||
168 | public Tcl_Obj pCollateNeeded; /* Collation needed script */ |
||
169 | public SqlPreparedStmt stmtList; /* List of prepared statements*/ |
||
170 | public SqlPreparedStmt stmtLast; /* Last statement in the list */ |
||
171 | public int maxStmt; /* The next maximum number of stmtList */ |
||
172 | public int nStmt; /* Number of statements in stmtList */ |
||
173 | #if !SQLITE_OMIT_INCRBLOB |
||
174 | public IncrblobChannel pIncrblob; /* Linked list of open incrblob channels */ |
||
175 | #endif |
||
176 | public int nStep, nSort, nIndex; /* Statistics for most recent operation */ |
||
177 | public int nTransaction; /* Number of nested [transaction] methods */ |
||
178 | } |
||
179 | |||
180 | #if !SQLITE_OMIT_INCRBLOB |
||
181 | class IncrblobChannel |
||
182 | { |
||
183 | public sqlite3_blob pBlob; /* sqlite3 blob handle */ |
||
184 | public SqliteDb pDb; /* Associated database connection */ |
||
185 | public int iSeek; /* Current seek offset */ |
||
186 | public Tcl_Channel channel; /* Channel identifier */ |
||
187 | public IncrblobChannel pNext; /* Linked list of all open incrblob channels */ |
||
188 | public IncrblobChannel pPrev; /* Linked list of all open incrblob channels */ |
||
189 | } |
||
190 | #endif |
||
191 | |||
192 | |||
193 | /* |
||
194 | ** Compute a string length that is limited to what can be stored in |
||
195 | ** lower 30 bits of a 32-bit signed integer. |
||
196 | */ |
||
197 | static int strlen30( StringBuilder z ) |
||
198 | { |
||
199 | //string z2 = z; |
||
200 | //while( *z2 ){ z2++; } |
||
201 | return 0x3fffffff & z.Length; |
||
202 | } |
||
203 | |||
204 | static int strlen30( string z ) |
||
205 | { |
||
206 | //string z2 = z; |
||
207 | //while( *z2 ){ z2++; } |
||
208 | return 0x3fffffff & z.Length; |
||
209 | } |
||
210 | |||
211 | |||
212 | #if !SQLITE_OMIT_INCRBLOB |
||
213 | /* |
||
214 | ** Close all incrblob channels opened using database connection pDb. |
||
215 | ** This is called when shutting down the database connection. |
||
216 | */ |
||
217 | static void closeIncrblobChannels( SqliteDb pDb ) |
||
218 | { |
||
219 | IncrblobChannel p; |
||
220 | IncrblobChannel pNext; |
||
221 | |||
222 | for ( p = pDb.pIncrblob ; p != null ; p = pNext ) |
||
223 | { |
||
224 | pNext = p.pNext; |
||
225 | |||
226 | /* Note: Calling unregister here call TCL.Tcl_Close on the incrblob channel, |
||
227 | ** which deletes the IncrblobChannel structure at p. So do not |
||
228 | ** call TCL.Tcl_Free() here. |
||
229 | */ |
||
230 | TCL.Tcl_UnregisterChannel( pDb.interp, p.channel ); |
||
231 | } |
||
232 | } |
||
233 | |||
234 | /* |
||
235 | ** Close an incremental blob channel. |
||
236 | */ |
||
237 | //static int incrblobClose(object instanceData, Tcl_Interp interp){ |
||
238 | // IncrblobChannel p = (IncrblobChannel )instanceData; |
||
239 | // int rc = sqlite3_blob_close(p.pBlob); |
||
240 | // sqlite3 db = p.pDb.db; |
||
241 | |||
242 | // /* Remove the channel from the SqliteDb.pIncrblob list. */ |
||
243 | // if( p.pNext ){ |
||
244 | // p.pNext.pPrev = p.pPrev; |
||
245 | // } |
||
246 | // if( p.pPrev ){ |
||
247 | // p.pPrev.pNext = p.pNext; |
||
248 | // } |
||
249 | // if( p.pDb.pIncrblob==p ){ |
||
250 | // p.pDb.pIncrblob = p.pNext; |
||
251 | // } |
||
252 | |||
253 | // /* Free the IncrblobChannel structure */ |
||
254 | // TCL.Tcl_Free((char )p); |
||
255 | |||
256 | // if( rc!=SQLITE_OK ){ |
||
257 | // TCL.Tcl_SetResult(interp, (char )sqlite3_errmsg(db), TCL.Tcl_VOLATILE); |
||
258 | // return TCL.TCL_ERROR; |
||
259 | // } |
||
260 | // return TCL.TCL_OK; |
||
261 | //} |
||
262 | |||
263 | /* |
||
264 | ** Read data from an incremental blob channel. |
||
265 | */ |
||
266 | //static int incrblobInput( |
||
267 | // object instanceData, |
||
268 | // char *buf, |
||
269 | // int bufSize, |
||
270 | // int *errorCodePtr |
||
271 | //){ |
||
272 | // IncrblobChannel p = (IncrblobChannel )instanceData; |
||
273 | // int nRead = bufSize; /* Number of bytes to read */ |
||
274 | // int nBlob; /* Total size of the blob */ |
||
275 | // int rc; /* sqlite error code */ |
||
276 | |||
277 | // nBlob = sqlite3_blob_bytes(p.pBlob); |
||
278 | // if( (p.iSeek+nRead)>nBlob ){ |
||
279 | // nRead = nBlob-p.iSeek; |
||
280 | // } |
||
281 | // if( nRead<=0 ){ |
||
282 | // return 0; |
||
283 | // } |
||
284 | |||
285 | // rc = sqlite3_blob_read(p.pBlob, (void )buf, nRead, p.iSeek); |
||
286 | // if( rc!=SQLITE_OK ){ |
||
287 | // *errorCodePtr = rc; |
||
288 | // return -1; |
||
289 | // } |
||
290 | |||
291 | // p.iSeek += nRead; |
||
292 | // return nRead; |
||
293 | //} |
||
294 | |||
295 | /* |
||
296 | ** Write data to an incremental blob channel. |
||
297 | */ |
||
298 | //static int incrblobOutput( |
||
299 | // object instanceData, |
||
300 | // string buf, |
||
301 | // int toWrite, |
||
302 | // int *errorCodePtr |
||
303 | //){ |
||
304 | // IncrblobChannel p = (IncrblobChannel )instanceData; |
||
305 | // int nWrite = toWrite; /* Number of bytes to write */ |
||
306 | // int nBlob; /* Total size of the blob */ |
||
307 | // int rc; /* sqlite error code */ |
||
308 | |||
309 | // nBlob = sqlite3_blob_bytes(p.pBlob); |
||
310 | // if( (p.iSeek+nWrite)>nBlob ){ |
||
311 | // *errorCodePtr = EINVAL; |
||
312 | // return -1; |
||
313 | // } |
||
314 | // if( nWrite<=0 ){ |
||
315 | // return 0; |
||
316 | // } |
||
317 | |||
318 | // rc = sqlite3_blob_write(p.pBlob, (void )buf, nWrite, p.iSeek); |
||
319 | // if( rc!=SQLITE_OK ){ |
||
320 | // *errorCodePtr = EIO; |
||
321 | // return -1; |
||
322 | // } |
||
323 | |||
324 | // p.iSeek += nWrite; |
||
325 | // return nWrite; |
||
326 | //} |
||
327 | |||
328 | /* |
||
329 | ** Seek an incremental blob channel. |
||
330 | */ |
||
331 | //static int incrblobSeek( |
||
332 | // object instanceData, |
||
333 | // long offset, |
||
334 | // int seekMode, |
||
335 | // int *errorCodePtr |
||
336 | //){ |
||
337 | // IncrblobChannel p = (IncrblobChannel )instanceData; |
||
338 | |||
339 | // switch( seekMode ){ |
||
340 | // case SEEK_SET: |
||
341 | // p.iSeek = offset; |
||
342 | // break; |
||
343 | // case SEEK_CUR: |
||
344 | // p.iSeek += offset; |
||
345 | // break; |
||
346 | // case SEEK_END: |
||
347 | // p.iSeek = sqlite3_blob_bytes(p.pBlob) + offset; |
||
348 | // break; |
||
349 | |||
350 | // default: Debug.Assert(!"Bad seekMode"); |
||
351 | // } |
||
352 | |||
353 | // return p.iSeek; |
||
354 | //} |
||
355 | |||
356 | |||
357 | //static void incrblobWatch(object instanceData, int mode){ |
||
358 | // /* NO-OP */ |
||
359 | //} |
||
360 | //static int incrblobHandle(object instanceData, int dir, object *hPtr){ |
||
361 | // return TCL.TCL_ERROR; |
||
362 | //} |
||
363 | |||
364 | static TCL.Tcl_ChannelType IncrblobChannelType = { |
||
365 | "incrblob", /* typeName */ |
||
366 | TCL.Tcl_CHANNEL_VERSION_2, /* version */ |
||
367 | incrblobClose, /* closeProc */ |
||
368 | incrblobInput, /* inputProc */ |
||
369 | incrblobOutput, /* outputProc */ |
||
370 | incrblobSeek, /* seekProc */ |
||
371 | 0, /* setOptionProc */ |
||
372 | 0, /* getOptionProc */ |
||
373 | incrblobWatch, /* watchProc (this is a no-op) */ |
||
374 | incrblobHandle, /* getHandleProc (always returns error) */ |
||
375 | 0, /* close2Proc */ |
||
376 | 0, /* blockModeProc */ |
||
377 | 0, /* flushProc */ |
||
378 | 0, /* handlerProc */ |
||
379 | 0, /* wideSeekProc */ |
||
380 | }; |
||
381 | |||
382 | /* |
||
383 | ** Create a new incrblob channel. |
||
384 | */ |
||
385 | static int count = 0; |
||
386 | static int createIncrblobChannel( |
||
387 | Tcl_Interp interp, |
||
388 | SqliteDb pDb, |
||
389 | string zDb, |
||
390 | string zTable, |
||
391 | string zColumn, |
||
392 | sqlite_int64 iRow, |
||
393 | int isReadonly |
||
394 | ){ |
||
395 | IncrblobChannel p; |
||
396 | sqlite3 db = pDb.db; |
||
397 | sqlite3_blob pBlob; |
||
398 | int rc; |
||
399 | int flags = TCL.Tcl_READABLE|(isReadonly ? 0 : TCL.Tcl_WRITABLE); |
||
400 | |||
401 | /* This variable is used to name the channels: "incrblob_[incr count]" */ |
||
402 | //static int count = 0; |
||
403 | string zChannel = "";//string[64]; |
||
404 | |||
405 | rc = sqlite3_blob_open(db, zDb, zTable, zColumn, iRow, !isReadonly, pBlob); |
||
406 | if( rc!=SQLITE_OK ){ |
||
407 | TCL.Tcl_SetResult(interp, sqlite3_errmsg(pDb.db), TCL.Tcl_VOLATILE); |
||
408 | return TCL.TCL_ERROR; |
||
409 | } |
||
410 | |||
411 | p = new IncrblobChannel();//(IncrblobChannel )Tcl_Alloc(sizeof(IncrblobChannel)); |
||
412 | p.iSeek = 0; |
||
413 | p.pBlob = pBlob; |
||
414 | |||
415 | sqlite3_snprintf(64, zChannel, "incrblob_%d", ++count); |
||
416 | p.channel = TCL.Tcl_CreateChannel(IncrblobChannelType, zChannel, p, flags); |
||
417 | TCL.Tcl_RegisterChannel(interp, p.channel); |
||
418 | |||
419 | /* Link the new channel into the SqliteDb.pIncrblob list. */ |
||
420 | p.pNext = pDb.pIncrblob; |
||
421 | p.pPrev = null; |
||
422 | if( p.pNext!=null ){ |
||
423 | p.pNext.pPrev = p; |
||
424 | } |
||
425 | pDb.pIncrblob = p; |
||
426 | p.pDb = pDb; |
||
427 | |||
428 | TCL.Tcl_SetResult(interp, Tcl_GetChannelName(p.channel), TCL.Tcl_VOLATILE); |
||
429 | return TCL.TCL_OK; |
||
430 | } |
||
431 | #else // * else clause for "#if !SQLITE_OMIT_INCRBLOB" */ |
||
432 | //#define closeIncrblobChannels(pDb) |
||
433 | static void closeIncrblobChannels( SqliteDb pDb ) |
||
434 | { |
||
435 | } |
||
436 | #endif |
||
437 | |||
438 | /* |
||
439 | ** Look at the script prefix in pCmd. We will be executing this script |
||
440 | ** after first appending one or more arguments. This routine analyzes |
||
441 | ** the script to see if it is safe to use TCL.Tcl_EvalObjv() on the script |
||
442 | ** rather than the more general TCL.Tcl_EvalEx(). TCL.Tcl_EvalObjv() is much |
||
443 | ** faster. |
||
444 | ** |
||
445 | ** Scripts that are safe to use with TCL.Tcl_EvalObjv() consists of a |
||
446 | ** command name followed by zero or more arguments with no [...] or $ |
||
447 | ** or {...} or ; to be seen anywhere. Most callback scripts consist |
||
448 | ** of just a single procedure name and they meet this requirement. |
||
449 | */ |
||
450 | static int safeToUseEvalObjv( Tcl_Interp interp, Tcl_Obj pCmd ) |
||
451 | { |
||
452 | /* We could try to do something with TCL.Tcl_Parse(). But we will instead |
||
453 | ** just do a search for forbidden characters. If any of the forbidden |
||
454 | ** characters appear in pCmd, we will report the string as unsafe. |
||
455 | */ |
||
456 | string z; |
||
457 | int n = 0; |
||
458 | z = TCL.Tcl_GetStringFromObj( pCmd, out n ); |
||
459 | while ( n-- > 0 ) |
||
460 | { |
||
461 | int c = z[n];// *( z++ ); |
||
462 | if ( c == '$' || c == '[' || c == ';' ) |
||
463 | return 0; |
||
464 | } |
||
465 | return 1; |
||
466 | } |
||
467 | |||
468 | /* |
||
469 | ** Find an SqlFunc structure with the given name. Or create a new |
||
470 | ** one if an existing one cannot be found. Return a pointer to the |
||
471 | ** structure. |
||
472 | */ |
||
473 | static SqlFunc findSqlFunc( SqliteDb pDb, string zName ) |
||
474 | { |
||
475 | SqlFunc p, pNew; |
||
476 | int i; |
||
477 | pNew = new SqlFunc();//(SqlFunc)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 ); |
||
478 | //pNew.zName = (char)&pNew[1]; |
||
479 | //for(i=0; zName[i]; i++){ pNew.zName[i] = tolower(zName[i]); } |
||
480 | //pNew.zName[i] = 0; |
||
481 | pNew.zName = zName.ToLower(); |
||
482 | for ( p = pDb.pFunc; p != null; p = p.pNext ) |
||
483 | { |
||
484 | if ( p.zName == pNew.zName ) |
||
485 | { |
||
486 | //Tcl_Free((char)pNew); |
||
487 | return p; |
||
488 | } |
||
489 | } |
||
490 | pNew.interp = pDb.interp; |
||
491 | pNew.pScript = null; |
||
492 | pNew.pNext = pDb.pFunc; |
||
493 | pDb.pFunc = pNew; |
||
494 | return pNew; |
||
495 | } |
||
496 | |||
497 | /* |
||
498 | ** Finalize and free a list of prepared statements |
||
499 | */ |
||
500 | static void flushStmtCache( SqliteDb pDb ) |
||
501 | { |
||
502 | SqlPreparedStmt pPreStmt; |
||
503 | |||
504 | while ( pDb.stmtList != null ) |
||
505 | { |
||
506 | sqlite3_finalize( pDb.stmtList.pStmt ); |
||
507 | pPreStmt = pDb.stmtList; |
||
508 | pDb.stmtList = pDb.stmtList.pNext; |
||
509 | TCL.Tcl_Free( ref pPreStmt ); |
||
510 | } |
||
511 | pDb.nStmt = 0; |
||
512 | pDb.stmtLast = null; |
||
513 | } |
||
514 | |||
515 | /* |
||
516 | ** TCL calls this procedure when an sqlite3 database command is |
||
517 | ** deleted. |
||
518 | */ |
||
519 | static void DbDeleteCmd( ref object db ) |
||
520 | { |
||
521 | SqliteDb pDb = (SqliteDb)db; |
||
522 | flushStmtCache( pDb ); |
||
523 | closeIncrblobChannels( pDb ); |
||
524 | sqlite3_close( pDb.db ); |
||
525 | while ( pDb.pFunc != null ) |
||
526 | { |
||
527 | SqlFunc pFunc = pDb.pFunc; |
||
528 | pDb.pFunc = pFunc.pNext; |
||
529 | TCL.Tcl_DecrRefCount( ref pFunc.pScript ); |
||
530 | TCL.Tcl_Free( ref pFunc ); |
||
531 | } |
||
532 | while ( pDb.pCollate != null ) |
||
533 | { |
||
534 | SqlCollate pCollate = pDb.pCollate; |
||
535 | pDb.pCollate = pCollate.pNext; |
||
536 | TCL.Tcl_Free( ref pCollate ); |
||
537 | } |
||
538 | if ( pDb.zBusy != null ) |
||
539 | { |
||
540 | TCL.Tcl_Free( ref pDb.zBusy ); |
||
541 | } |
||
542 | if ( pDb.zTrace != null ) |
||
543 | { |
||
544 | TCL.Tcl_Free( ref pDb.zTrace ); |
||
545 | } |
||
546 | if ( pDb.zProfile != null ) |
||
547 | { |
||
548 | TCL.Tcl_Free( ref pDb.zProfile ); |
||
549 | } |
||
550 | if ( pDb.zAuth != null ) |
||
551 | { |
||
552 | TCL.Tcl_Free( ref pDb.zAuth ); |
||
553 | } |
||
554 | if ( pDb.zNull != null ) |
||
555 | { |
||
556 | TCL.Tcl_Free( ref pDb.zNull ); |
||
557 | } |
||
558 | if ( pDb.pUpdateHook != null ) |
||
559 | { |
||
560 | TCL.Tcl_DecrRefCount( ref pDb.pUpdateHook ); |
||
561 | } |
||
562 | if ( pDb.pRollbackHook != null ) |
||
563 | { |
||
564 | TCL.Tcl_DecrRefCount( ref pDb.pRollbackHook ); |
||
565 | } |
||
566 | if ( pDb.pWalHook != null ) |
||
567 | { |
||
568 | TCL.Tcl_DecrRefCount( ref pDb.pWalHook ); |
||
569 | } |
||
570 | if ( pDb.pCollateNeeded != null ) |
||
571 | { |
||
572 | TCL.Tcl_DecrRefCount( ref pDb.pCollateNeeded ); |
||
573 | } |
||
574 | TCL.Tcl_Free( ref pDb ); |
||
575 | } |
||
576 | |||
577 | /* |
||
578 | ** This routine is called when a database file is locked while trying |
||
579 | ** to execute SQL. |
||
580 | */ |
||
581 | static int DbBusyHandler( object cd, int nTries ) |
||
582 | { |
||
583 | SqliteDb pDb = (SqliteDb)cd; |
||
584 | int rc; |
||
585 | StringBuilder zVal = new StringBuilder( 30 );//char zVal[30]; |
||
586 | |||
587 | sqlite3_snprintf( 30, zVal, "%d", nTries ); |
||
588 | rc = TCL.Tcl_VarEval( pDb.interp, pDb.zBusy, " ", zVal.ToString(), null ); |
||
589 | if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
||
590 | { |
||
591 | return 0; |
||
592 | } |
||
593 | return 1; |
||
594 | } |
||
595 | |||
596 | #if !SQLITE_OMIT_PROGRESS_CALLBACK |
||
597 | /* |
||
598 | ** This routine is invoked as the 'progress callback' for the database. |
||
599 | */ |
||
600 | static int DbProgressHandler( object cd ) |
||
601 | { |
||
602 | SqliteDb pDb = (SqliteDb)cd; |
||
603 | int rc; |
||
604 | |||
605 | Debug.Assert( pDb.zProgress != null ); |
||
606 | rc = TCL.Tcl_Eval( pDb.interp, pDb.zProgress ); |
||
607 | if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
||
608 | { |
||
609 | return 1; |
||
610 | } |
||
611 | return 0; |
||
612 | } |
||
613 | #endif |
||
614 | |||
615 | #if !SQLITE_OMIT_TRACE |
||
616 | /* |
||
617 | ** This routine is called by the SQLite trace handler whenever a new |
||
618 | ** block of SQL is executed. The TCL script in pDb.zTrace is executed. |
||
619 | */ |
||
620 | static void DbTraceHandler( object cd, string zSql ) |
||
621 | { |
||
622 | SqliteDb pDb = (SqliteDb)cd; |
||
623 | TclObject str = null; |
||
624 | |||
625 | TCL.Tcl_DStringInit( out str ); |
||
626 | TCL.Tcl_DStringAppendElement( str, pDb.zTrace ); |
||
627 | TCL.Tcl_DStringAppendElement( str, " {" + zSql + "}" ); |
||
628 | TCL.Tcl_EvalObjEx( pDb.interp, str, 0 );// TCL.Tcl_Eval( pDb.interp, TCL.Tcl_DStringValue( ref str ) ); |
||
629 | TCL.Tcl_DStringFree( ref str ); |
||
630 | TCL.Tcl_ResetResult( pDb.interp ); |
||
631 | } |
||
632 | #endif |
||
633 | |||
634 | #if !SQLITE_OMIT_TRACE |
||
635 | /* |
||
636 | ** This routine is called by the SQLite profile handler after a statement |
||
637 | ** SQL has executed. The TCL script in pDb.zProfile is evaluated. |
||
638 | */ |
||
639 | static void DbProfileHandler( object cd, string zSql, sqlite_int64 tm ) |
||
640 | { |
||
641 | SqliteDb pDb = (SqliteDb)cd; |
||
642 | TclObject str = null; |
||
643 | StringBuilder zTm = new StringBuilder( 100 );//char zTm[100]; |
||
644 | |||
645 | sqlite3_snprintf( 100, zTm, "%lld", tm ); |
||
646 | TCL.Tcl_DStringInit( out str ); |
||
647 | TCL.Tcl_DStringAppendElement( str, pDb.zProfile ); |
||
648 | TCL.Tcl_DStringAppendElement( str, " {" + zSql + "}" ); |
||
649 | TCL.Tcl_DStringAppendElement( str, " {" + zTm.ToString() + "}" ); |
||
650 | TCL.Tcl_Eval( pDb.interp, str.ToString() ); |
||
651 | TCL.Tcl_DStringFree( ref str ); |
||
652 | TCL.Tcl_ResetResult( pDb.interp ); |
||
653 | } |
||
654 | #endif |
||
655 | |||
656 | /* |
||
657 | ** This routine is called when a transaction is committed. The |
||
658 | ** TCL script in pDb.zCommit is executed. If it returns non-zero or |
||
659 | ** if it throws an exception, the transaction is rolled back instead |
||
660 | ** of being committed. |
||
661 | */ |
||
662 | static int DbCommitHandler( object cd ) |
||
663 | { |
||
664 | SqliteDb pDb = (SqliteDb)cd; |
||
665 | int rc; |
||
666 | |||
667 | rc = TCL.Tcl_Eval( pDb.interp, pDb.zCommit ); |
||
668 | if ( rc != TCL.TCL_OK || atoi( TCL.Tcl_GetStringResult( pDb.interp ) ) != 0 ) |
||
669 | { |
||
670 | return 1; |
||
671 | } |
||
672 | return 0; |
||
673 | } |
||
674 | |||
675 | static void DbRollbackHandler( object _object ) |
||
676 | { |
||
677 | SqliteDb pDb = (SqliteDb)_object; |
||
678 | Debug.Assert( pDb.pRollbackHook != null ); |
||
679 | if ( TCL.TCL_OK != TCL.Tcl_EvalObjEx( pDb.interp, pDb.pRollbackHook, 0 ) ) |
||
680 | { |
||
681 | TCL.Tcl_BackgroundError( pDb.interp ); |
||
682 | } |
||
683 | } |
||
684 | |||
685 | /* |
||
686 | ** This procedure handles wal_hook callbacks. |
||
687 | */ |
||
688 | static int DbWalHandler( |
||
689 | object clientData, |
||
690 | sqlite3 db, |
||
691 | string zDb, |
||
692 | int nEntry |
||
693 | ) |
||
694 | { |
||
695 | int ret = SQLITE_OK; |
||
696 | Tcl_Obj p; |
||
697 | SqliteDb pDb = (SqliteDb)clientData; |
||
698 | Tcl_Interp interp = pDb.interp; |
||
699 | Debug.Assert( pDb.pWalHook != null ); |
||
700 | |||
701 | p = TCL.Tcl_DuplicateObj( pDb.pWalHook ); |
||
702 | TCL.Tcl_IncrRefCount( p ); |
||
703 | TCL.Tcl_ListObjAppendElement( interp, p, TCL.Tcl_NewStringObj( zDb, -1 ) ); |
||
704 | TCL.Tcl_ListObjAppendElement( interp, p, TCL.Tcl_NewIntObj( nEntry ) ); |
||
705 | if ( TCL.TCL_OK != TCL.Tcl_EvalObjEx( interp, p, 0 ) |
||
706 | || TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, TCL.Tcl_GetObjResult( interp ), out ret ) |
||
707 | ) |
||
708 | { |
||
709 | TCL.Tcl_BackgroundError( interp ); |
||
710 | } |
||
711 | TCL.Tcl_DecrRefCount( ref p ); |
||
712 | |||
713 | return ret; |
||
714 | } |
||
715 | |||
716 | #if (SQLITE_TEST) && (SQLITE_ENABLE_UNLOCK_NOTIFY) |
||
717 | static void setTestUnlockNotifyVars(Tcl_Interp interp, int iArg, int nArg){ |
||
718 | char zBuf[64]; |
||
719 | sprintf(zBuf, "%d", iArg); |
||
720 | Tcl_SetVar(interp, "sqlite_unlock_notify_arg", zBuf, TCL_GLOBAL_ONLY); |
||
721 | sprintf(zBuf, "%d", nArg); |
||
722 | Tcl_SetVar(interp, "sqlite_unlock_notify_argcount", zBuf, TCL_GLOBAL_ONLY); |
||
723 | } |
||
724 | #else |
||
725 | //# define setTestUnlockNotifyVars(x,y,z) |
||
726 | #endif |
||
727 | |||
728 | #if SQLITE_ENABLE_UNLOCK_NOTIFY |
||
729 | static void DbUnlockNotify(void **apArg, int nArg){ |
||
730 | int i; |
||
731 | for(i=0; i<nArg; i++){ |
||
732 | const int flags = (TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT); |
||
733 | SqliteDb *pDb = (SqliteDb )apArg[i]; |
||
734 | setTestUnlockNotifyVars(pDb.interp, i, nArg); |
||
735 | Debug.Assert( pDb.pUnlockNotify); |
||
736 | Tcl_EvalObjEx(pDb.interp, pDb.pUnlockNotify, flags); |
||
737 | Tcl_DecrRefCount(pDb.pUnlockNotify); |
||
738 | pDb.pUnlockNotify = 0; |
||
739 | } |
||
740 | } |
||
741 | #endif |
||
742 | |||
743 | static void DbUpdateHandler( |
||
744 | object p, |
||
745 | int op, |
||
746 | string zDb, |
||
747 | string zTbl, |
||
748 | sqlite_int64 rowid |
||
749 | ) |
||
750 | { |
||
751 | SqliteDb pDb = (SqliteDb)p; |
||
752 | Tcl_Obj pCmd; |
||
753 | |||
754 | Debug.Assert( pDb.pUpdateHook != null ); |
||
755 | Debug.Assert( op == SQLITE_INSERT || op == SQLITE_UPDATE || op == SQLITE_DELETE ); |
||
756 | |||
757 | pCmd = TCL.Tcl_DuplicateObj( pDb.pUpdateHook ); |
||
758 | TCL.Tcl_IncrRefCount( pCmd ); |
||
759 | TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( |
||
760 | ( ( op == SQLITE_INSERT ) ? "INSERT" : ( op == SQLITE_UPDATE ) ? "UPDATE" : "DELETE" ), -1 ) ); |
||
761 | TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( zDb, -1 ) ); |
||
762 | TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewStringObj( zTbl, -1 ) ); |
||
763 | TCL.Tcl_ListObjAppendElement( null, pCmd, TCL.Tcl_NewWideIntObj( rowid ) ); |
||
764 | TCL.Tcl_EvalObjEx( pDb.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
||
765 | TCL.Tcl_DecrRefCount( ref pCmd ); |
||
766 | } |
||
767 | |||
768 | static void tclCollateNeeded( |
||
769 | object pCtx, |
||
770 | sqlite3 db, |
||
771 | int enc, |
||
772 | string zName |
||
773 | ) |
||
774 | { |
||
775 | SqliteDb pDb = (SqliteDb)pCtx; |
||
776 | Tcl_Obj pScript = TCL.Tcl_DuplicateObj( pDb.pCollateNeeded ); |
||
777 | TCL.Tcl_IncrRefCount( pScript ); |
||
778 | TCL.Tcl_ListObjAppendElement( null, pScript, TCL.Tcl_NewStringObj( zName, -1 ) ); |
||
779 | TCL.Tcl_EvalObjEx( pDb.interp, pScript, 0 ); |
||
780 | TCL.Tcl_DecrRefCount( ref pScript ); |
||
781 | } |
||
782 | |||
783 | /* |
||
784 | ** This routine is called to evaluate an SQL collation function implemented |
||
785 | ** using TCL script. |
||
786 | */ |
||
787 | static int tclSqlCollate( |
||
788 | object pCtx, |
||
789 | int nA, |
||
790 | string zA, |
||
791 | int nB, |
||
792 | string zB |
||
793 | ) |
||
794 | { |
||
795 | SqlCollate p = (SqlCollate)pCtx; |
||
796 | Tcl_Obj pCmd; |
||
797 | |||
798 | pCmd = TCL.Tcl_NewStringObj( p.zScript, -1 ); |
||
799 | TCL.Tcl_IncrRefCount( pCmd ); |
||
800 | TCL.Tcl_ListObjAppendElement( p.interp, pCmd, TCL.Tcl_NewStringObj( zA, nA ) ); |
||
801 | TCL.Tcl_ListObjAppendElement( p.interp, pCmd, TCL.Tcl_NewStringObj( zB, nB ) ); |
||
802 | TCL.Tcl_EvalObjEx( p.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
||
803 | TCL.Tcl_DecrRefCount( ref pCmd ); |
||
804 | return ( atoi( TCL.Tcl_GetStringResult( p.interp ) ) ); |
||
805 | } |
||
806 | |||
807 | /* |
||
808 | ** This routine is called to evaluate an SQL function implemented |
||
809 | ** using TCL script. |
||
810 | */ |
||
811 | static void tclSqlFunc( sqlite3_context context, int argc, sqlite3_value[] argv ) |
||
812 | { |
||
813 | SqlFunc p = (SqlFunc)sqlite3_user_data( context ); |
||
814 | Tcl_Obj pCmd = null; |
||
815 | int i; |
||
816 | int rc; |
||
817 | |||
818 | if ( argc == 0 ) |
||
819 | { |
||
820 | /* If there are no arguments to the function, call TCL.Tcl_EvalObjEx on the |
||
821 | ** script object directly. This allows the TCL compiler to generate |
||
822 | ** bytecode for the command on the first invocation and thus make |
||
823 | ** subsequent invocations much faster. */ |
||
824 | pCmd = p.pScript; |
||
825 | TCL.Tcl_IncrRefCount( pCmd ); |
||
826 | rc = TCL.Tcl_EvalObjEx( p.interp, pCmd, 0 ); |
||
827 | TCL.Tcl_DecrRefCount( ref pCmd ); |
||
828 | } |
||
829 | else |
||
830 | { |
||
831 | /* If there are arguments to the function, make a shallow copy of the |
||
832 | ** script object, lappend the arguments, then evaluate the copy. |
||
833 | ** |
||
834 | ** By "shallow" copy, we mean a only the outer list Tcl_Obj is duplicated. |
||
835 | ** The new Tcl_Obj contains pointers to the original list elements. |
||
836 | ** That way, when TCL.Tcl_EvalObjv() is run and shimmers the first element |
||
837 | ** of the list to tclCmdNameType, that alternate representation will |
||
838 | ** be preserved and reused on the next invocation. |
||
839 | */ |
||
840 | Tcl_Obj[] aArg = null; |
||
841 | int nArg = 0; |
||
842 | if ( TCL.Tcl_ListObjGetElements( p.interp, p.pScript, out nArg, out aArg ) ) |
||
843 | { |
||
844 | sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
||
845 | return; |
||
846 | } |
||
847 | pCmd = TCL.Tcl_NewListObj( nArg, aArg ); |
||
848 | TCL.Tcl_IncrRefCount( pCmd ); |
||
849 | for ( i = 0; i < argc; i++ ) |
||
850 | { |
||
851 | sqlite3_value pIn = argv[i]; |
||
852 | Tcl_Obj pVal; |
||
853 | |||
854 | /* Set pVal to contain the i'th column of this row. */ |
||
855 | switch ( sqlite3_value_type( pIn ) ) |
||
856 | { |
||
857 | case SQLITE_BLOB: |
||
858 | { |
||
859 | int bytes = sqlite3_value_bytes( pIn ); |
||
860 | pVal = TCL.Tcl_NewByteArrayObj( sqlite3_value_blob( pIn ), bytes ); |
||
861 | break; |
||
862 | } |
||
863 | case SQLITE_INTEGER: |
||
864 | { |
||
865 | sqlite_int64 v = sqlite3_value_int64( pIn ); |
||
866 | if ( v >= -2147483647 && v <= 2147483647 ) |
||
867 | { |
||
868 | pVal = TCL.Tcl_NewIntObj( (int)v ); |
||
869 | } |
||
870 | else |
||
871 | { |
||
872 | pVal = TCL.Tcl_NewWideIntObj( v ); |
||
873 | } |
||
874 | break; |
||
875 | } |
||
876 | case SQLITE_FLOAT: |
||
877 | { |
||
878 | double r = sqlite3_value_double( pIn ); |
||
879 | pVal = TCL.Tcl_NewDoubleObj( r ); |
||
880 | break; |
||
881 | } |
||
882 | case SQLITE_NULL: |
||
883 | { |
||
884 | pVal = TCL.Tcl_NewStringObj( "", 0 ); |
||
885 | break; |
||
886 | } |
||
887 | default: |
||
888 | { |
||
889 | int bytes = sqlite3_value_bytes( pIn ); |
||
890 | pVal = TCL.Tcl_NewStringObj( sqlite3_value_text( pIn ), bytes ); |
||
891 | break; |
||
892 | } |
||
893 | } |
||
894 | rc = TCL.Tcl_ListObjAppendElement( p.interp, pCmd, pVal ) ? 1 : 0; |
||
895 | if ( rc != 0 ) |
||
896 | { |
||
897 | TCL.Tcl_DecrRefCount( ref pCmd ); |
||
898 | sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
||
899 | return; |
||
900 | } |
||
901 | } |
||
902 | if ( p.useEvalObjv == 0 ) |
||
903 | { |
||
904 | /* TCL.Tcl_EvalObjEx() will automatically call TCL.Tcl_EvalObjv() if pCmd |
||
905 | ** is a list without a string representation. To prevent this from |
||
906 | ** happening, make sure pCmd has a valid string representation */ |
||
907 | TCL.Tcl_GetString( pCmd ); |
||
908 | } |
||
909 | rc = TCL.Tcl_EvalObjEx( p.interp, pCmd, TCL.TCL_EVAL_DIRECT ); |
||
910 | TCL.Tcl_DecrRefCount( ref pCmd ); |
||
911 | } |
||
912 | |||
913 | if ( rc != 0 && rc != TCL.TCL_RETURN ) |
||
914 | { |
||
915 | sqlite3_result_error( context, TCL.Tcl_GetStringResult( p.interp ), -1 ); |
||
916 | } |
||
917 | else |
||
918 | { |
||
919 | Tcl_Obj pVar = TCL.Tcl_GetObjResult( p.interp ); |
||
920 | int n = 0; |
||
921 | string data = ""; |
||
922 | Tcl_WideInt v = 0; |
||
923 | double r = 0; |
||
924 | string zType = pVar.typePtr;//string zType = (pVar.typePtr ? pVar.typePtr.name : ""); |
||
925 | if ( zType == "bytearray" ) |
||
926 | { //&& pVar.bytes==0 ){ |
||
927 | /* Only return a BLOB type if the Tcl variable is a bytearray and |
||
928 | ** has no string representation. */ |
||
929 | data = Encoding.UTF8.GetString( TCL.Tcl_GetByteArrayFromObj( pVar, out n ) ); |
||
930 | sqlite3_result_blob( context, data, n, null ); |
||
931 | } |
||
932 | else if ( zType == "boolean" ) |
||
933 | { |
||
934 | TCL.Tcl_GetIntFromObj( null, pVar, out n ); |
||
935 | sqlite3_result_int( context, n ); |
||
936 | } |
||
937 | else if ( zType == "wideint" || |
||
938 | zType == "int" || Int64.TryParse( pVar.ToString(), out v ) ) |
||
939 | { |
||
940 | TCL.Tcl_GetWideIntFromObj( null, pVar, out v ); |
||
941 | sqlite3_result_int64( context, v ); |
||
942 | } |
||
943 | else if ( zType == "double" || Double.TryParse( pVar.ToString(), out r ) ) |
||
944 | { |
||
945 | TCL.Tcl_GetDoubleFromObj( null, pVar, out r ); |
||
946 | sqlite3_result_double( context, r ); |
||
947 | } |
||
948 | else |
||
949 | { |
||
950 | data = TCL.Tcl_GetStringFromObj( pVar, n ); |
||
951 | n = data.Length; |
||
952 | sqlite3_result_text( context, data, n, SQLITE_TRANSIENT ); |
||
953 | } |
||
954 | } |
||
955 | } |
||
956 | |||
957 | #if !SQLITE_OMIT_AUTHORIZATION |
||
958 | /* |
||
959 | ** This is the authentication function. It appends the authentication |
||
960 | ** type code and the two arguments to zCmd[] then invokes the result |
||
961 | ** on the interpreter. The reply is examined to determine if the |
||
962 | ** authentication fails or succeeds. |
||
963 | */ |
||
964 | static int auth_callback( |
||
965 | object pArg, |
||
966 | int code, |
||
967 | const string zArg1, |
||
968 | const string zArg2, |
||
969 | const string zArg3, |
||
970 | const string zArg4 |
||
971 | ){ |
||
972 | string zCode; |
||
973 | TCL.Tcl_DString str; |
||
974 | int rc; |
||
975 | const string zReply; |
||
976 | SqliteDb pDb = (SqliteDb)pArg; |
||
977 | if( pdb.disableAuth ) return SQLITE_OK; |
||
978 | |||
979 | switch( code ){ |
||
980 | case SQLITE_COPY : zCode="SQLITE_COPY"; break; |
||
981 | case SQLITE_CREATE_INDEX : zCode="SQLITE_CREATE_INDEX"; break; |
||
982 | case SQLITE_CREATE_TABLE : zCode="SQLITE_CREATE_TABLE"; break; |
||
983 | case SQLITE_CREATE_TEMP_INDEX : zCode="SQLITE_CREATE_TEMP_INDEX"; break; |
||
984 | case SQLITE_CREATE_TEMP_TABLE : zCode="SQLITE_CREATE_TEMP_TABLE"; break; |
||
985 | case SQLITE_CREATE_TEMP_TRIGGER: zCode="SQLITE_CREATE_TEMP_TRIGGER"; break; |
||
986 | case SQLITE_CREATE_TEMP_VIEW : zCode="SQLITE_CREATE_TEMP_VIEW"; break; |
||
987 | case SQLITE_CREATE_TRIGGER : zCode="SQLITE_CREATE_TRIGGER"; break; |
||
988 | case SQLITE_CREATE_VIEW : zCode="SQLITE_CREATE_VIEW"; break; |
||
989 | case SQLITE_DELETE : zCode="SQLITE_DELETE"; break; |
||
990 | case SQLITE_DROP_INDEX : zCode="SQLITE_DROP_INDEX"; break; |
||
991 | case SQLITE_DROP_TABLE : zCode="SQLITE_DROP_TABLE"; break; |
||
992 | case SQLITE_DROP_TEMP_INDEX : zCode="SQLITE_DROP_TEMP_INDEX"; break; |
||
993 | case SQLITE_DROP_TEMP_TABLE : zCode="SQLITE_DROP_TEMP_TABLE"; break; |
||
994 | case SQLITE_DROP_TEMP_TRIGGER : zCode="SQLITE_DROP_TEMP_TRIGGER"; break; |
||
995 | case SQLITE_DROP_TEMP_VIEW : zCode="SQLITE_DROP_TEMP_VIEW"; break; |
||
996 | case SQLITE_DROP_TRIGGER : zCode="SQLITE_DROP_TRIGGER"; break; |
||
997 | case SQLITE_DROP_VIEW : zCode="SQLITE_DROP_VIEW"; break; |
||
998 | case SQLITE_INSERT : zCode="SQLITE_INSERT"; break; |
||
999 | case SQLITE_PRAGMA : zCode="SQLITE_PRAGMA"; break; |
||
1000 | case SQLITE_READ : zCode="SQLITE_READ"; break; |
||
1001 | case SQLITE_SELECT : zCode="SQLITE_SELECT"; break; |
||
1002 | case SQLITE_TRANSACTION : zCode="SQLITE_TRANSACTION"; break; |
||
1003 | case SQLITE_UPDATE : zCode="SQLITE_UPDATE"; break; |
||
1004 | case SQLITE_ATTACH : zCode="SQLITE_ATTACH"; break; |
||
1005 | case SQLITE_DETACH : zCode="SQLITE_DETACH"; break; |
||
1006 | case SQLITE_ALTER_TABLE : zCode="SQLITE_ALTER_TABLE"; break; |
||
1007 | case SQLITE_REINDEX : zCode="SQLITE_REINDEX"; break; |
||
1008 | case SQLITE_ANALYZE : zCode="SQLITE_ANALYZE"; break; |
||
1009 | case SQLITE_CREATE_VTABLE : zCode="SQLITE_CREATE_VTABLE"; break; |
||
1010 | case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break; |
||
1011 | case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break; |
||
1012 | case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break; |
||
1013 | default : zCode="????"; break; |
||
1014 | } |
||
1015 | TCL.Tcl_DStringInit(&str); |
||
1016 | TCL.Tcl_DStringAppend(&str, pDb.zAuth, -1); |
||
1017 | TCL.Tcl_DStringAppendElement(&str, zCode); |
||
1018 | TCL.Tcl_DStringAppendElement(&str, zArg1 ? zArg1 : ""); |
||
1019 | TCL.Tcl_DStringAppendElement(&str, zArg2 ? zArg2 : ""); |
||
1020 | TCL.Tcl_DStringAppendElement(&str, zArg3 ? zArg3 : ""); |
||
1021 | TCL.Tcl_DStringAppendElement(&str, zArg4 ? zArg4 : ""); |
||
1022 | rc = TCL.Tcl_GlobalEval(pDb.interp, TCL.Tcl_DStringValue(&str)); |
||
1023 | TCL.Tcl_DStringFree(&str); |
||
1024 | zReply = TCL.Tcl_GetStringResult(pDb.interp); |
||
1025 | if( strcmp(zReply,"SQLITE_OK")==0 ){ |
||
1026 | rc = SQLITE_OK; |
||
1027 | }else if( strcmp(zReply,"SQLITE_DENY")==0 ){ |
||
1028 | rc = SQLITE_DENY; |
||
1029 | }else if( strcmp(zReply,"SQLITE_IGNORE")==0 ){ |
||
1030 | rc = SQLITE_IGNORE; |
||
1031 | }else{ |
||
1032 | rc = 999; |
||
1033 | } |
||
1034 | return rc; |
||
1035 | } |
||
1036 | #endif // * SQLITE_OMIT_AUTHORIZATION */ |
||
1037 | |||
1038 | /* |
||
1039 | ** zText is a pointer to text obtained via an sqlite3_result_text() |
||
1040 | ** or similar interface. This routine returns a Tcl string object, |
||
1041 | ** reference count set to 0, containing the text. If a translation |
||
1042 | ** between iso8859 and UTF-8 is required, it is preformed. |
||
1043 | */ |
||
1044 | static Tcl_Obj dbTextToObj( string zText ) |
||
1045 | { |
||
1046 | Tcl_Obj pVal; |
||
1047 | #if UTF_TRANSLATION_NEEDED |
||
1048 | //TCL.Tcl_DString dCol; |
||
1049 | //TCL.Tcl_DStringInit(&dCol); |
||
1050 | //TCL.Tcl_ExternalToUtfDString(NULL, zText, -1, dCol); |
||
1051 | //pVal = TCL.Tcl_NewStringObj(Tcl_DStringValue(&dCol), -1); |
||
1052 | //TCL.Tcl_DStringFree(ref dCol); |
||
1053 | if (zText.Length == Encoding.UTF8.GetByteCount(zText)) pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
||
1054 | else pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
||
1055 | #else |
||
1056 | pVal = TCL.Tcl_NewStringObj( zText, -1 ); |
||
1057 | #endif |
||
1058 | return pVal; |
||
1059 | } |
||
1060 | |||
1061 | /* |
||
1062 | ** This routine reads a line of text from FILE in, stores |
||
1063 | ** the text in memory obtained from malloc() and returns a pointer |
||
1064 | ** to the text. NULL is returned at end of file, or if malloc() |
||
1065 | ** fails. |
||
1066 | ** |
||
1067 | ** The interface is like "readline" but no command-line editing |
||
1068 | ** is done. |
||
1069 | ** |
||
1070 | ** copied from shell.c from '.import' command |
||
1071 | */ |
||
1072 | //static char *local_getline(string zPrompt, FILE *in){ |
||
1073 | // string zLine; |
||
1074 | // int nLine; |
||
1075 | // int n; |
||
1076 | // int eol; |
||
1077 | |||
1078 | // nLine = 100; |
||
1079 | // zLine = malloc( nLine ); |
||
1080 | // if( zLine==0 ) return 0; |
||
1081 | // n = 0; |
||
1082 | // eol = 0; |
||
1083 | // while( !eol ){ |
||
1084 | // if( n+100>nLine ){ |
||
1085 | // nLine = nLine*2 + 100; |
||
1086 | // zLine = realloc(zLine, nLine); |
||
1087 | // if( zLine==0 ) return 0; |
||
1088 | // } |
||
1089 | // if( fgets(&zLine[n], nLine - n, in)==0 ){ |
||
1090 | // if( n==0 ){ |
||
1091 | // free(zLine); |
||
1092 | // return 0; |
||
1093 | // } |
||
1094 | // zLine[n] = 0; |
||
1095 | // eol = 1; |
||
1096 | // break; |
||
1097 | // } |
||
1098 | // while( zLine[n] ){ n++; } |
||
1099 | // if( n>0 && zLine[n-1]=='\n' ){ |
||
1100 | // n--; |
||
1101 | // zLine[n] = 0; |
||
1102 | // eol = 1; |
||
1103 | // } |
||
1104 | // } |
||
1105 | // zLine = realloc( zLine, n+1 ); |
||
1106 | // return zLine; |
||
1107 | //} |
||
1108 | |||
1109 | |||
1110 | /* |
||
1111 | ** This function is part of the implementation of the command: |
||
1112 | ** |
||
1113 | ** $db transaction [-deferred|-immediate|-exclusive] SCRIPT |
||
1114 | ** |
||
1115 | ** It is invoked after evaluating the script SCRIPT to commit or rollback |
||
1116 | ** the transaction or savepoint opened by the [transaction] command. |
||
1117 | */ |
||
1118 | static int DbTransPostCmd( |
||
1119 | object data, /* data[0] is the Sqlite3Db* for $db */ |
||
1120 | Tcl_Interp interp, /* Tcl interpreter */ |
||
1121 | int result /* Result of evaluating SCRIPT */ |
||
1122 | ) |
||
1123 | { |
||
1124 | string[] azEnd = { |
||
1125 | "RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */ |
||
1126 | "COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */ |
||
1127 | "ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction", |
||
1128 | "ROLLBACK" /* rc==TCL_ERROR, nTransaction==0 */ |
||
1129 | }; |
||
1130 | SqliteDb pDb = (SqliteDb)data; |
||
1131 | int rc = result; |
||
1132 | string zEnd; |
||
1133 | |||
1134 | pDb.nTransaction--; |
||
1135 | zEnd = azEnd[( ( rc == TCL.TCL_ERROR ) ? 1 : 0 ) * 2 + ( ( pDb.nTransaction == 0 ) ? 1 : 0 )]; |
||
1136 | |||
1137 | pDb.disableAuth++; |
||
1138 | if ( sqlite3_exec( pDb.db, zEnd, 0, 0, 0 ) != 0 ) |
||
1139 | { |
||
1140 | /* This is a tricky scenario to handle. The most likely cause of an |
||
1141 | ** error is that the exec() above was an attempt to commit the |
||
1142 | ** top-level transaction that returned SQLITE_BUSY. Or, less likely, |
||
1143 | ** that an IO-error has occured. In either case, throw a Tcl exception |
||
1144 | ** and try to rollback the transaction. |
||
1145 | ** |
||
1146 | ** But it could also be that the user executed one or more BEGIN, |
||
1147 | ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing |
||
1148 | ** this method's logic. Not clear how this would be best handled. |
||
1149 | */ |
||
1150 | if ( rc != TCL.TCL_ERROR ) |
||
1151 | { |
||
1152 | TCL.Tcl_AppendResult( interp, sqlite3_errmsg( pDb.db ), 0 ); |
||
1153 | rc = TCL.TCL_ERROR; |
||
1154 | } |
||
1155 | sqlite3_exec( pDb.db, "ROLLBACK", 0, 0, 0 ); |
||
1156 | } |
||
1157 | pDb.disableAuth--; |
||
1158 | |||
1159 | return rc; |
||
1160 | } |
||
1161 | |||
1162 | /* |
||
1163 | ** Search the cache for a prepared-statement object that implements the |
||
1164 | ** first SQL statement in the buffer pointed to by parameter zIn. If |
||
1165 | ** no such prepared-statement can be found, allocate and prepare a new |
||
1166 | ** one. In either case, bind the current values of the relevant Tcl |
||
1167 | ** variables to any $var, :var or @var variables in the statement. Before |
||
1168 | ** returning, set *ppPreStmt to point to the prepared-statement object. |
||
1169 | ** |
||
1170 | ** Output parameter *pzOut is set to point to the next SQL statement in |
||
1171 | ** buffer zIn, or to the '\0' byte at the end of zIn if there is no |
||
1172 | ** next statement. |
||
1173 | ** |
||
1174 | ** If successful, TCL_OK is returned. Otherwise, TCL_ERROR is returned |
||
1175 | ** and an error message loaded into interpreter pDb.interp. |
||
1176 | */ |
||
1177 | static int dbPrepareAndBind( |
||
1178 | SqliteDb pDb, /* Database object */ |
||
1179 | string zIn, /* SQL to compile */ |
||
1180 | ref string pzOut, /* OUT: Pointer to next SQL statement */ |
||
1181 | ref SqlPreparedStmt ppPreStmt /* OUT: Object used to cache statement */ |
||
1182 | ) |
||
1183 | { |
||
1184 | string zSql = zIn; /* Pointer to first SQL statement in zIn */ |
||
1185 | sqlite3_stmt pStmt = null; /* Prepared statement object */ |
||
1186 | SqlPreparedStmt pPreStmt; /* Pointer to cached statement */ |
||
1187 | int nSql; /* Length of zSql in bytes */ |
||
1188 | int nVar = 0; /* Number of variables in statement */ |
||
1189 | int iParm = 0; /* Next free entry in apParm */ |
||
1190 | int i; |
||
1191 | Tcl_Interp interp = pDb.interp; |
||
1192 | |||
1193 | pzOut = null; |
||
1194 | ppPreStmt = null; |
||
1195 | |||
1196 | /* Trim spaces from the start of zSql and calculate the remaining length. */ |
||
1197 | zSql = zSql.TrimStart(); //while ( isspace( zSql[0] ) ) { zSql++; } |
||
1198 | nSql = strlen30( zSql ); |
||
1199 | |||
1200 | for ( pPreStmt = pDb.stmtList; pPreStmt != null; pPreStmt = pPreStmt.pNext ) |
||
1201 | { |
||
1202 | int n = pPreStmt.nSql; |
||
1203 | if ( nSql >= n |
||
1204 | && zSql.StartsWith(pPreStmt.zSql) |
||
1205 | && ( nSql == n /* zSql[n]==0 */|| zSql[n - 1] == ';' ) |
||
1206 | ) |
||
1207 | { |
||
1208 | pStmt = pPreStmt.pStmt; |
||
1209 | /* Restore aMem values */ |
||
1210 | if ( pStmt.aMem.Length < pPreStmt.aMem.Length ) |
||
1211 | Array.Resize( ref pStmt.aMem, pPreStmt.aMem.Length ); |
||
1212 | for ( int ix = 0; ix < pPreStmt.aMem.Length; ix++ ) |
||
1213 | { |
||
1214 | pPreStmt.aMem[ix].CopyTo( ref pStmt.aMem[ix] ); |
||
1215 | } |
||
1216 | |||
1217 | pzOut = zSql.Substring( pPreStmt.nSql ); |
||
1218 | |||
1219 | /* When a prepared statement is found, unlink it from the |
||
1220 | ** cache list. It will later be added back to the beginning |
||
1221 | ** of the cache list in order to implement LRU replacement. |
||
1222 | */ |
||
1223 | if ( pPreStmt.pPrev != null ) |
||
1224 | { |
||
1225 | pPreStmt.pPrev.pNext = pPreStmt.pNext; |
||
1226 | } |
||
1227 | else |
||
1228 | { |
||
1229 | pDb.stmtList = pPreStmt.pNext; |
||
1230 | } |
||
1231 | if ( pPreStmt.pNext != null ) |
||
1232 | { |
||
1233 | pPreStmt.pNext.pPrev = pPreStmt.pPrev; |
||
1234 | } |
||
1235 | else |
||
1236 | { |
||
1237 | pDb.stmtLast = pPreStmt.pPrev; |
||
1238 | } |
||
1239 | pDb.nStmt--; |
||
1240 | nVar = sqlite3_bind_parameter_count( pStmt ); |
||
1241 | break; |
||
1242 | } |
||
1243 | } |
||
1244 | |||
1245 | /* If no prepared statement was found. Compile the SQL text. Also allocate |
||
1246 | ** a new SqlPreparedStmt structure. */ |
||
1247 | if ( pPreStmt == null ) |
||
1248 | { |
||
1249 | int nByte; |
||
1250 | |||
1251 | if ( SQLITE_OK != sqlite3_prepare_v2( pDb.db, zSql, -1, ref pStmt, ref pzOut ) ) |
||
1252 | { |
||
1253 | TCL.Tcl_SetObjResult( interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
||
1254 | pPreStmt = new SqlPreparedStmt();// (SqlPreparedStmt)Tcl_Alloc( nByte ); |
||
1255 | return TCL.TCL_ERROR; |
||
1256 | } |
||
1257 | if ( pStmt == null ) |
||
1258 | { |
||
1259 | if ( SQLITE_OK != sqlite3_errcode( pDb.db ) ) |
||
1260 | { |
||
1261 | /* A compile-time error in the statement. */ |
||
1262 | TCL.Tcl_SetObjResult( interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
||
1263 | return TCL.TCL_ERROR; |
||
1264 | } |
||
1265 | else |
||
1266 | { |
||
1267 | /* The statement was a no-op. Continue to the next statement |
||
1268 | ** in the SQL string. |
||
1269 | */ |
||
1270 | return TCL.TCL_OK; |
||
1271 | } |
||
1272 | } |
||
1273 | |||
1274 | Debug.Assert( pPreStmt == null ); |
||
1275 | nVar = sqlite3_bind_parameter_count( pStmt ); |
||
1276 | //nByte = sizeof(SqlPreparedStmt) + nVar*sizeof(Tcl_Obj ); |
||
1277 | pPreStmt = new SqlPreparedStmt();// (SqlPreparedStmt)Tcl_Alloc( nByte ); |
||
1278 | //memset(pPreStmt, 0, nByte); |
||
1279 | |||
1280 | pPreStmt.pStmt = pStmt; |
||
1281 | pPreStmt.nSql = ( zSql.Length - pzOut.Length ); |
||
1282 | pPreStmt.zSql = sqlite3_sql( pStmt ); |
||
1283 | pPreStmt.apParm = new TclObject[nVar];//pPreStmt[1]; |
||
1284 | } |
||
1285 | Debug.Assert( pPreStmt != null ); |
||
1286 | Debug.Assert( strlen30( pPreStmt.zSql ) == pPreStmt.nSql ); |
||
1287 | Debug.Assert( zSql.StartsWith( pPreStmt.zSql ) ); |
||
1288 | |||
1289 | /* Bind values to parameters that begin with $ or : */ |
||
1290 | for ( i = 1; i <= nVar; i++ ) |
||
1291 | { |
||
1292 | string zVar = sqlite3_bind_parameter_name( pStmt, i ); |
||
1293 | if ( !String.IsNullOrEmpty( zVar ) && ( zVar[0] == '$' || zVar[0] == ':' || zVar[0] == '@' ) ) |
||
1294 | { |
||
1295 | Tcl_Obj pVar = TCL.Tcl_GetVar2Ex( interp, zVar.Substring( 1 ), null, 0 ); |
||
1296 | if ( pVar != null && pVar.typePtr != "null" ) |
||
1297 | { |
||
1298 | int n = 0; |
||
1299 | string data; |
||
1300 | string zType = pVar.typePtr; |
||
1301 | //char c = zType[0]; |
||
1302 | if ( zVar[0] == '@' || |
||
1303 | ( zType == "bytearray" ) )// TODO -- && pVar.bytes == 0 ) ) |
||
1304 | { |
||
1305 | /* Load a BLOB type if the Tcl variable is a bytearray and |
||
1306 | ** it has no string representation or the host |
||
1307 | ** parameter name begins with "@". */ |
||
1308 | if ( zVar[0] == '@' || pVar.stringRep == null ) |
||
1309 | sqlite3_bind_blob( pStmt, i, TCL.Tcl_GetByteArrayFromObj( pVar, out n ), n, SQLITE_STATIC ); |
||
1310 | else |
||
1311 | sqlite3_bind_text( pStmt, i, TCL.Tcl_GetStringFromObj( pVar, out n ), n, SQLITE_STATIC ); |
||
1312 | TCL.Tcl_IncrRefCount( pVar ); |
||
1313 | pPreStmt.apParm[iParm++] = pVar; |
||
1314 | } |
||
1315 | else if ( zType == "boolean" ) |
||
1316 | { |
||
1317 | TCL.Tcl_GetIntFromObj( interp, pVar, out n ); |
||
1318 | sqlite3_bind_int( pStmt, i, n ); |
||
1319 | } |
||
1320 | else if ( zType == "double" ) |
||
1321 | { |
||
1322 | double r = 0; |
||
1323 | TCL.Tcl_GetDoubleFromObj( interp, pVar, out r ); |
||
1324 | sqlite3_bind_double( pStmt, i, r ); |
||
1325 | } |
||
1326 | else if ( zType == "wideint" || |
||
1327 | zType == "int" ) |
||
1328 | { |
||
1329 | Tcl_WideInt v = 0; |
||
1330 | TCL.Tcl_GetWideIntFromObj( interp, pVar, out v ); |
||
1331 | sqlite3_bind_int64( pStmt, i, v ); |
||
1332 | } |
||
1333 | else |
||
1334 | { |
||
1335 | data = TCL.Tcl_GetStringFromObj( pVar, out n ); |
||
1336 | sqlite3_bind_text( pStmt, i, data, n, SQLITE_STATIC ); |
||
1337 | TCL.Tcl_IncrRefCount( pVar ); |
||
1338 | pPreStmt.apParm[iParm++] = pVar; |
||
1339 | } |
||
1340 | } |
||
1341 | else |
||
1342 | { |
||
1343 | sqlite3_bind_null( pStmt, i ); |
||
1344 | } |
||
1345 | } |
||
1346 | } |
||
1347 | pPreStmt.nParm = iParm; |
||
1348 | /* save aMem values for later reuse */ |
||
1349 | pPreStmt.aMem = new Mem[pPreStmt.pStmt.aMem.Length]; |
||
1350 | for ( int ix = 0; ix < pPreStmt.pStmt.aMem.Length; ix++ ) |
||
1351 | { |
||
1352 | pPreStmt.pStmt.aMem[ix].CopyTo( ref pPreStmt.aMem[ix] ); |
||
1353 | } |
||
1354 | ppPreStmt = pPreStmt; |
||
1355 | |||
1356 | return TCL.TCL_OK; |
||
1357 | } |
||
1358 | |||
1359 | |||
1360 | /* |
||
1361 | ** Release a statement reference obtained by calling dbPrepareAndBind(). |
||
1362 | ** There should be exactly one call to this function for each call to |
||
1363 | ** dbPrepareAndBind(). |
||
1364 | ** |
||
1365 | ** If the discard parameter is non-zero, then the statement is deleted |
||
1366 | ** immediately. Otherwise it is added to the LRU list and may be returned |
||
1367 | ** by a subsequent call to dbPrepareAndBind(). |
||
1368 | */ |
||
1369 | static void dbReleaseStmt( |
||
1370 | SqliteDb pDb, /* Database handle */ |
||
1371 | SqlPreparedStmt pPreStmt, /* Prepared statement handle to release */ |
||
1372 | int discard /* True to delete (not cache) the pPreStmt */ |
||
1373 | ) |
||
1374 | { |
||
1375 | int i; |
||
1376 | |||
1377 | /* Free the bound string and blob parameters */ |
||
1378 | for ( i = 0; i < pPreStmt.nParm; i++ ) |
||
1379 | { |
||
1380 | TCL.Tcl_DecrRefCount( ref pPreStmt.apParm[i] ); |
||
1381 | } |
||
1382 | pPreStmt.nParm = 0; |
||
1383 | |||
1384 | if ( pDb.maxStmt <= 0 || discard != 0 ) |
||
1385 | { |
||
1386 | /* If the cache is turned off, deallocated the statement */ |
||
1387 | sqlite3_finalize( pPreStmt.pStmt ); |
||
1388 | TCL.Tcl_Free( ref pPreStmt ); |
||
1389 | } |
||
1390 | else |
||
1391 | { |
||
1392 | /* Add the prepared statement to the beginning of the cache list. */ |
||
1393 | pPreStmt.pNext = pDb.stmtList; |
||
1394 | pPreStmt.pPrev = null; |
||
1395 | if ( pDb.stmtList != null ) |
||
1396 | { |
||
1397 | pDb.stmtList.pPrev = pPreStmt; |
||
1398 | } |
||
1399 | pDb.stmtList = pPreStmt; |
||
1400 | if ( pDb.stmtLast == null ) |
||
1401 | { |
||
1402 | Debug.Assert( pDb.nStmt == 0 ); |
||
1403 | pDb.stmtLast = pPreStmt; |
||
1404 | } |
||
1405 | else |
||
1406 | { |
||
1407 | Debug.Assert( pDb.nStmt > 0 ); |
||
1408 | } |
||
1409 | pDb.nStmt++; |
||
1410 | |||
1411 | /* If we have too many statement in cache, remove the surplus from |
||
1412 | ** the end of the cache list. */ |
||
1413 | while ( pDb.nStmt > pDb.maxStmt ) |
||
1414 | { |
||
1415 | sqlite3_finalize( pDb.stmtLast.pStmt ); |
||
1416 | pDb.stmtLast = pDb.stmtLast.pPrev; |
||
1417 | TCL.Tcl_Free( ref pDb.stmtLast.pNext ); |
||
1418 | pDb.stmtLast.pNext = null; |
||
1419 | pDb.nStmt--; |
||
1420 | } |
||
1421 | } |
||
1422 | } |
||
1423 | |||
1424 | /* |
||
1425 | ** Structure used with dbEvalXXX() functions: |
||
1426 | ** |
||
1427 | ** dbEvalInit() |
||
1428 | ** dbEvalStep() |
||
1429 | ** dbEvalFinalize() |
||
1430 | ** dbEvalRowInfo() |
||
1431 | ** dbEvalColumnValue() |
||
1432 | */ |
||
1433 | //typedef struct DbEvalContext DbEvalContext; |
||
1434 | public class DbEvalContext |
||
1435 | { |
||
1436 | public SqliteDb pDb; /* Database handle */ |
||
1437 | public Tcl_Obj pSql; /* Object holding string zSql */ |
||
1438 | public string zSql; /* Remaining SQL to execute */ |
||
1439 | public SqlPreparedStmt pPreStmt; /* Current statement */ |
||
1440 | public int nCol; /* Number of columns returned by pStmt */ |
||
1441 | public Tcl_Obj pArray; /* Name of array variable */ |
||
1442 | public Tcl_Obj[] apColName; /* Array of column names */ |
||
1443 | |||
1444 | public void Clear() |
||
1445 | { |
||
1446 | pDb = null; |
||
1447 | pSql = null; |
||
1448 | zSql = null; |
||
1449 | pPreStmt = null; |
||
1450 | pArray = null; |
||
1451 | apColName = null; |
||
1452 | |||
1453 | } |
||
1454 | }; |
||
1455 | |||
1456 | /* |
||
1457 | ** Release any cache of column names currently held as part of |
||
1458 | ** the DbEvalContext structure passed as the first argument. |
||
1459 | */ |
||
1460 | static void dbReleaseColumnNames( DbEvalContext p ) |
||
1461 | { |
||
1462 | if ( p.apColName != null ) |
||
1463 | { |
||
1464 | int i; |
||
1465 | for ( i = 0; i < p.nCol; i++ ) |
||
1466 | { |
||
1467 | TCL.Tcl_DecrRefCount( ref p.apColName[i] ); |
||
1468 | } |
||
1469 | TCL.Tcl_Free( ref p.apColName ); |
||
1470 | p.apColName = null; |
||
1471 | } |
||
1472 | p.nCol = 0; |
||
1473 | } |
||
1474 | |||
1475 | /* |
||
1476 | ** Initialize a DbEvalContext structure. |
||
1477 | ** |
||
1478 | ** If pArray is not NULL, then it contains the name of a Tcl array |
||
1479 | ** variable. The "*" member of this array is set to a list containing |
||
1480 | ** the names of the columns returned by the statement as part of each |
||
1481 | ** call to dbEvalStep(), in order from left to right. e.g. if the names |
||
1482 | ** of the returned columns are a, b and c, it does the equivalent of the |
||
1483 | ** tcl command: |
||
1484 | ** |
||
1485 | ** set ${pArray}() {a b c} |
||
1486 | */ |
||
1487 | static void dbEvalInit( |
||
1488 | DbEvalContext p, /* Pointer to structure to initialize */ |
||
1489 | SqliteDb pDb, /* Database handle */ |
||
1490 | Tcl_Obj pSql, /* Object containing SQL script */ |
||
1491 | Tcl_Obj pArray /* Name of Tcl array to set () element of */ |
||
1492 | ) |
||
1493 | { |
||
1494 | if ( p != null ) |
||
1495 | p.Clear();// memset( p, 0, sizeof( DbEvalContext ) ); |
||
1496 | p.pDb = pDb; |
||
1497 | p.zSql = TCL.Tcl_GetString( pSql ); |
||
1498 | p.pSql = pSql; |
||
1499 | TCL.Tcl_IncrRefCount( pSql ); |
||
1500 | if ( pArray != null ) |
||
1501 | { |
||
1502 | p.pArray = pArray; |
||
1503 | TCL.Tcl_IncrRefCount( pArray ); |
||
1504 | } |
||
1505 | } |
||
1506 | |||
1507 | /* |
||
1508 | ** Obtain information about the row that the DbEvalContext passed as the |
||
1509 | ** first argument currently points to. |
||
1510 | */ |
||
1511 | static void dbEvalRowInfo( |
||
1512 | DbEvalContext p, /* Evaluation context */ |
||
1513 | out int pnCol, /* OUT: Number of column names */ |
||
1514 | out Tcl_Obj[] papColName /* OUT: Array of column names */ |
||
1515 | ) |
||
1516 | { |
||
1517 | /* Compute column names */ |
||
1518 | if ( null == p.apColName ) |
||
1519 | { |
||
1520 | sqlite3_stmt pStmt = p.pPreStmt.pStmt; |
||
1521 | int i; /* Iterator variable */ |
||
1522 | int nCol; /* Number of columns returned by pStmt */ |
||
1523 | Tcl_Obj[] apColName = null; /* Array of column names */ |
||
1524 | |||
1525 | p.nCol = nCol = sqlite3_column_count( pStmt ); |
||
1526 | if ( nCol > 0 )// && ( papColName != null || p.pArray != null ) ) |
||
1527 | { |
||
1528 | apColName = new TclObject[nCol];// (Tcl_Obj*)Tcl_Alloc( sizeof( Tcl_Obj* ) * nCol ); |
||
1529 | for ( i = 0; i < nCol; i++ ) |
||
1530 | { |
||
1531 | apColName[i] = dbTextToObj( sqlite3_column_name( pStmt, i ) ); |
||
1532 | TCL.Tcl_IncrRefCount( apColName[i] ); |
||
1533 | } |
||
1534 | p.apColName = apColName; |
||
1535 | } |
||
1536 | |||
1537 | /* If results are being stored in an array variable, then create |
||
1538 | ** the array() entry for that array |
||
1539 | */ |
||
1540 | if ( p.pArray != null ) |
||
1541 | { |
||
1542 | Tcl_Interp interp = p.pDb.interp; |
||
1543 | Tcl_Obj pColList = TCL.Tcl_NewObj(); |
||
1544 | Tcl_Obj pStar = TCL.Tcl_NewStringObj( "*", -1 ); |
||
1545 | |||
1546 | for ( i = 0; i < nCol; i++ ) |
||
1547 | { |
||
1548 | TCL.Tcl_ListObjAppendElement( interp, pColList, apColName[i] ); |
||
1549 | } |
||
1550 | TCL.Tcl_IncrRefCount( pStar ); |
||
1551 | TCL.Tcl_ObjSetVar2( interp, p.pArray, pStar, pColList, 0 ); |
||
1552 | TCL.Tcl_DecrRefCount( ref pStar ); |
||
1553 | } |
||
1554 | } |
||
1555 | |||
1556 | //if ( papColName != null ) |
||
1557 | { |
||
1558 | papColName = p.apColName; |
||
1559 | } |
||
1560 | //if ( pnCol !=0) |
||
1561 | { |
||
1562 | pnCol = p.nCol; |
||
1563 | } |
||
1564 | } |
||
1565 | |||
1566 | /* |
||
1567 | ** Return one of TCL_OK, TCL_BREAK or TCL_ERROR. If TCL_ERROR is |
||
1568 | ** returned, then an error message is stored in the interpreter before |
||
1569 | ** returning. |
||
1570 | ** |
||
1571 | ** A return value of TCL_OK means there is a row of data available. The |
||
1572 | ** data may be accessed using dbEvalRowInfo() and dbEvalColumnValue(). This |
||
1573 | ** is analogous to a return of SQLITE_ROW from sqlite3_step(). If TCL_BREAK |
||
1574 | ** is returned, then the SQL script has finished executing and there are |
||
1575 | ** no further rows available. This is similar to SQLITE_DONE. |
||
1576 | */ |
||
1577 | static int dbEvalStep( DbEvalContext p ) |
||
1578 | { |
||
1579 | while ( !String.IsNullOrEmpty( p.zSql ) || p.pPreStmt != null ) |
||
1580 | { |
||
1581 | int rc; |
||
1582 | if ( p.pPreStmt == null ) |
||
1583 | { |
||
1584 | rc = dbPrepareAndBind( p.pDb, p.zSql, ref p.zSql, ref p.pPreStmt ); |
||
1585 | if ( rc != TCL.TCL_OK ) |
||
1586 | return rc; |
||
1587 | } |
||
1588 | else |
||
1589 | { |
||
1590 | int rcs; |
||
1591 | SqliteDb pDb = p.pDb; |
||
1592 | SqlPreparedStmt pPreStmt = p.pPreStmt; |
||
1593 | sqlite3_stmt pStmt = pPreStmt.pStmt; |
||
1594 | |||
1595 | rcs = sqlite3_step( pStmt ); |
||
1596 | if ( rcs == SQLITE_ROW ) |
||
1597 | { |
||
1598 | return TCL.TCL_OK; |
||
1599 | } |
||
1600 | if ( p.pArray != null ) |
||
1601 | { |
||
1602 | TclObject[] pDummy; |
||
1603 | int iDummy; |
||
1604 | dbEvalRowInfo( p, out iDummy, out pDummy ); |
||
1605 | } |
||
1606 | rcs = sqlite3_reset( pStmt ); |
||
1607 | |||
1608 | pDb.nStep = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, 1 ); |
||
1609 | pDb.nSort = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_SORT, 1 ); |
||
1610 | pDb.nIndex = sqlite3_stmt_status( pStmt, SQLITE_STMTSTATUS_AUTOINDEX, 1 ); |
||
1611 | dbReleaseColumnNames( p ); |
||
1612 | p.pPreStmt = null; |
||
1613 | |||
1614 | if ( rcs != SQLITE_OK ) |
||
1615 | { |
||
1616 | /* If a run-time error occurs, report the error and stop reading |
||
1617 | ** the SQL. */ |
||
1618 | TCL.Tcl_SetObjResult( pDb.interp, dbTextToObj( sqlite3_errmsg( pDb.db ) ) ); |
||
1619 | dbReleaseStmt( pDb, pPreStmt, 1 ); |
||
1620 | return TCL.TCL_ERROR; |
||
1621 | } |
||
1622 | else |
||
1623 | { |
||
1624 | dbReleaseStmt( pDb, pPreStmt, 0 ); |
||
1625 | } |
||
1626 | } |
||
1627 | } |
||
1628 | |||
1629 | /* Finished */ |
||
1630 | return TCL.TCL_BREAK; |
||
1631 | } |
||
1632 | |||
1633 | /* |
||
1634 | ** Free all resources currently held by the DbEvalContext structure passed |
||
1635 | ** as the first argument. There should be exactly one call to this function |
||
1636 | ** for each call to dbEvalInit(). |
||
1637 | */ |
||
1638 | static void dbEvalFinalize( DbEvalContext p ) |
||
1639 | { |
||
1640 | if ( p.pPreStmt != null ) |
||
1641 | { |
||
1642 | sqlite3_reset( p.pPreStmt.pStmt ); |
||
1643 | dbReleaseStmt( p.pDb, p.pPreStmt, 1 ); |
||
1644 | p.pPreStmt = null; |
||
1645 | } |
||
1646 | if ( p.pArray != null ) |
||
1647 | { |
||
1648 | TCL.Tcl_DecrRefCount( ref p.pArray ); |
||
1649 | p.pArray = null; |
||
1650 | } |
||
1651 | TCL.Tcl_DecrRefCount( ref p.pSql ); |
||
1652 | dbReleaseColumnNames( p ); |
||
1653 | } |
||
1654 | |||
1655 | /* |
||
1656 | ** Return a pointer to a Tcl_Obj structure with ref-count 0 that contains |
||
1657 | ** the value for the iCol'th column of the row currently pointed to by |
||
1658 | ** the DbEvalContext structure passed as the first argument. |
||
1659 | */ |
||
1660 | static Tcl_Obj dbEvalColumnValue( DbEvalContext p, int iCol ) |
||
1661 | { |
||
1662 | sqlite3_stmt pStmt = p.pPreStmt.pStmt; |
||
1663 | switch ( sqlite3_column_type( pStmt, iCol ) ) |
||
1664 | { |
||
1665 | case SQLITE_BLOB: |
||
1666 | { |
||
1667 | int bytes = sqlite3_column_bytes( pStmt, iCol ); |
||
1668 | byte[] zBlob = sqlite3_column_blob( pStmt, iCol ); |
||
1669 | if ( null == zBlob ) |
||
1670 | bytes = 0; |
||
1671 | return TCL.Tcl_NewByteArrayObj( zBlob, bytes ); |
||
1672 | } |
||
1673 | case SQLITE_INTEGER: |
||
1674 | { |
||
1675 | sqlite_int64 v = sqlite3_column_int64( pStmt, iCol ); |
||
1676 | if ( v >= -2147483647 && v <= 2147483647 ) |
||
1677 | { |
||
1678 | return TCL.Tcl_NewIntObj( (int)v ); |
||
1679 | } |
||
1680 | else |
||
1681 | { |
||
1682 | return TCL.Tcl_NewWideIntObj( v ); |
||
1683 | } |
||
1684 | } |
||
1685 | case SQLITE_FLOAT: |
||
1686 | { |
||
1687 | return TCL.Tcl_NewDoubleObj( sqlite3_column_double( pStmt, iCol ) ); |
||
1688 | } |
||
1689 | case SQLITE_NULL: |
||
1690 | { |
||
1691 | return dbTextToObj( p.pDb.zNull ); |
||
1692 | } |
||
1693 | } |
||
1694 | |||
1695 | return dbTextToObj( sqlite3_column_text( pStmt, iCol ) ); |
||
1696 | } |
||
1697 | |||
1698 | /* |
||
1699 | ** If using Tcl version 8.6 or greater, use the NR functions to avoid |
||
1700 | ** recursive evalution of scripts by the [db eval] and [db trans] |
||
1701 | ** commands. Even if the headers used while compiling the extension |
||
1702 | ** are 8.6 or newer, the code still tests the Tcl version at runtime. |
||
1703 | ** This allows stubs-enabled builds to be used with older Tcl libraries. |
||
1704 | */ |
||
1705 | #if TCL_MAJOR_VERSION//>8 || (TCL_MAJOR_VERSION==8 && TCL_MINOR_VERSION>=6) |
||
1706 | //# define SQLITE_TCL_NRE 1 |
||
1707 | static int DbUseNre(void){ |
||
1708 | int major, minor; |
||
1709 | Tcl_GetVersion(&major, &minor, 0, 0); |
||
1710 | return( (major==8 && minor>=6) || major>8 ); |
||
1711 | } |
||
1712 | #else |
||
1713 | /* |
||
1714 | ** Compiling using headers earlier than 8.6. In this case NR cannot be |
||
1715 | ** used, so DbUseNre() to always return zero. Add #defines for the other |
||
1716 | ** Tcl_NRxxx() functions to prevent them from causing compilation errors, |
||
1717 | ** even though the only invocations of them are within conditional blocks |
||
1718 | ** of the form: |
||
1719 | ** |
||
1720 | ** if( DbUseNre() ) { ... } |
||
1721 | */ |
||
1722 | const int SQLITE_TCL_NRE = 0; //# define SQLITE_TCL_NRE 0 |
||
1723 | static bool DbUseNre() |
||
1724 | { |
||
1725 | return false; |
||
1726 | } //# define DbUseNre() 0 |
||
1727 | //# define Tcl_NRAddCallback(a,b,c,d,e,f) 0 |
||
1728 | //# define Tcl_NREvalObj(a,b,c) 0 |
||
1729 | //# define Tcl_NRCreateCommand(a,b,c,d,e,f) 0 |
||
1730 | #endif |
||
1731 | |||
1732 | /* |
||
1733 | ** This function is part of the implementation of the command: |
||
1734 | ** |
||
1735 | ** $db eval SQL ?ARRAYNAME? SCRIPT |
||
1736 | */ |
||
1737 | static int DbEvalNextCmd( |
||
1738 | object[] data, /* data[0] is the (DbEvalContext) */ |
||
1739 | Tcl_Interp interp, /* Tcl interpreter */ |
||
1740 | int result /* Result so far */ |
||
1741 | ) |
||
1742 | { |
||
1743 | int rc = result; /* Return code */ |
||
1744 | |||
1745 | /* The first element of the data[] array is a pointer to a DbEvalContext |
||
1746 | ** structure allocated using TCL.Tcl_Alloc(). The second element of data[] |
||
1747 | ** is a pointer to a TCL.Tcl_Obj containing the script to run for each row |
||
1748 | ** returned by the queries encapsulated in data[0]. */ |
||
1749 | DbEvalContext p = (DbEvalContext)data[0]; |
||
1750 | Tcl_Obj pScript = (Tcl_Obj)data[1]; |
||
1751 | Tcl_Obj pArray = p.pArray; |
||
1752 | |||
1753 | while ( ( rc == TCL.TCL_OK || rc == TCL.TCL_CONTINUE ) && TCL.TCL_OK == ( rc = dbEvalStep( p ) ) ) |
||
1754 | { |
||
1755 | int i; |
||
1756 | int nCol; |
||
1757 | Tcl_Obj[] apColName; |
||
1758 | dbEvalRowInfo( p, out nCol, out apColName ); |
||
1759 | for ( i = 0; i < nCol; i++ ) |
||
1760 | { |
||
1761 | Tcl_Obj pVal = dbEvalColumnValue( p, i ); |
||
1762 | if ( pArray == null ) |
||
1763 | { |
||
1764 | TCL.Tcl_ObjSetVar2( interp, apColName[i], null, pVal, 0 ); |
||
1765 | } |
||
1766 | else |
||
1767 | { |
||
1768 | TCL.Tcl_ObjSetVar2( interp, pArray, apColName[i], pVal, 0 ); |
||
1769 | } |
||
1770 | } |
||
1771 | |||
1772 | /* The required interpreter variables are now populated with the data |
||
1773 | ** from the current row. If using NRE, schedule callbacks to evaluate |
||
1774 | ** script pScript, then to invoke this function again to fetch the next |
||
1775 | ** row (or clean up if there is no next row or the script throws an |
||
1776 | ** exception). After scheduling the callbacks, return control to the |
||
1777 | ** caller. |
||
1778 | ** |
||
1779 | ** If not using NRE, evaluate pScript directly and continue with the |
||
1780 | ** next iteration of this while(...) loop. */ |
||
1781 | if ( DbUseNre() ) |
||
1782 | { |
||
1783 | Debugger.Break(); |
||
1784 | //TCL.Tcl_NRAddCallback(interp, DbEvalNextCmd, (void)p, (void)pScript, 0, 0); |
||
1785 | //return TCL.Tcl_NREvalObj(interp, pScript, 0); |
||
1786 | } |
||
1787 | else |
||
1788 | { |
||
1789 | rc = TCL.Tcl_EvalObjEx( interp, pScript, 0 ); |
||
1790 | } |
||
1791 | } |
||
1792 | |||
1793 | TCL.Tcl_DecrRefCount( ref pScript ); |
||
1794 | dbEvalFinalize( p ); |
||
1795 | TCL.Tcl_Free( ref p ); |
||
1796 | |||
1797 | if ( rc == TCL.TCL_OK || rc == TCL.TCL_BREAK ) |
||
1798 | { |
||
1799 | TCL.Tcl_ResetResult( interp ); |
||
1800 | rc = TCL.TCL_OK; |
||
1801 | } |
||
1802 | return rc; |
||
1803 | } |
||
1804 | |||
1805 | /* |
||
1806 | ** The "sqlite" command below creates a new Tcl command for each |
||
1807 | ** connection it opens to an SQLite database. This routine is invoked |
||
1808 | ** whenever one of those connection-specific commands is executed |
||
1809 | ** in Tcl. For example, if you run Tcl code like this: |
||
1810 | ** |
||
1811 | ** sqlite3 db1 "my_database" |
||
1812 | ** db1 close |
||
1813 | ** |
||
1814 | ** The first command opens a connection to the "my_database" database |
||
1815 | ** and calls that connection "db1". The second command causes this |
||
1816 | ** subroutine to be invoked. |
||
1817 | */ |
||
1818 | enum DB_enum |
||
1819 | { |
||
1820 | DB_AUTHORIZER, |
||
1821 | DB_BACKUP, |
||
1822 | DB_BUSY, |
||
1823 | DB_CACHE, |
||
1824 | DB_CHANGES, |
||
1825 | DB_CLOSE, |
||
1826 | DB_COLLATE, |
||
1827 | DB_COLLATION_NEEDED, |
||
1828 | DB_COMMIT_HOOK, |
||
1829 | DB_COMPLETE, |
||
1830 | DB_COPY, |
||
1831 | DB_ENABLE_LOAD_EXTENSION, |
||
1832 | DB_ERRORCODE, |
||
1833 | DB_EVAL, |
||
1834 | DB_EXISTS, |
||
1835 | DB_FUNCTION, |
||
1836 | DB_INCRBLOB, |
||
1837 | DB_INTERRUPT, |
||
1838 | DB_LAST_INSERT_ROWID, |
||
1839 | DB_NULLVALUE, |
||
1840 | DB_ONECOLUMN, |
||
1841 | DB_PROFILE, |
||
1842 | DB_PROGRESS, |
||
1843 | DB_REKEY, |
||
1844 | DB_RESTORE, |
||
1845 | DB_ROLLBACK_HOOK, |
||
1846 | DB_STATUS, |
||
1847 | DB_TIMEOUT, |
||
1848 | DB_TOTAL_CHANGES, |
||
1849 | DB_TRACE, |
||
1850 | DB_TRANSACTION, |
||
1851 | DB_UNLOCK_NOTIFY, |
||
1852 | DB_UPDATE_HOOK, |
||
1853 | DB_VERSION, |
||
1854 | DB_WAL_HOOK |
||
1855 | }; |
||
1856 | |||
1857 | enum TTYPE_enum |
||
1858 | { |
||
1859 | TTYPE_DEFERRED, |
||
1860 | TTYPE_EXCLUSIVE, |
||
1861 | TTYPE_IMMEDIATE |
||
1862 | }; |
||
1863 | |||
1864 | static int DbObjCmd( object cd, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) |
||
1865 | { |
||
1866 | SqliteDb pDb = (SqliteDb)cd; |
||
1867 | int choice = 0; |
||
1868 | int rc = TCL.TCL_OK; |
||
1869 | string[] DB_strs = { |
||
1870 | "authorizer", "backup", "busy", |
||
1871 | "cache", "changes", "close", |
||
1872 | "collate", "collation_needed", "commit_hook", |
||
1873 | "complete", "copy", "enable_load_extension", |
||
1874 | "errorcode", "eval", "exists", |
||
1875 | "function", "incrblob", "interrupt", |
||
1876 | "last_insert_rowid", "nullvalue", "onecolumn", |
||
1877 | "profile", "progress", "rekey", |
||
1878 | "restore", "rollback_hook", "status", |
||
1879 | "timeout", "total_changes", "trace", |
||
1880 | "transaction", "unlock_notify", "update_hook", |
||
1881 | "version", "wal_hook" |
||
1882 | }; |
||
1883 | |||
1884 | /* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */ |
||
1885 | if ( objc < 2 ) |
||
1886 | { |
||
1887 | TCL.Tcl_WrongNumArgs( interp, 1, objv, "SUBCOMMAND ..." ); |
||
1888 | return TCL.TCL_ERROR; |
||
1889 | } |
||
1890 | if ( TCL.Tcl_GetIndexFromObj( interp, objv[1], DB_strs, "option", 0, out choice ) ) |
||
1891 | { |
||
1892 | return TCL.TCL_ERROR; |
||
1893 | } |
||
1894 | |||
1895 | switch ( choice ) |
||
1896 | { |
||
1897 | |||
1898 | /* $db authorizer ?CALLBACK? |
||
1899 | ** |
||
1900 | ** Invoke the given callback to authorize each SQL operation as it is |
||
1901 | ** compiled. 5 arguments are appended to the callback before it is |
||
1902 | ** invoked: |
||
1903 | ** |
||
1904 | ** (1) The authorization type (ex: SQLITE_CREATE_TABLE, SQLITE_INSERT, ...) |
||
1905 | ** (2) First descriptive name (depends on authorization type) |
||
1906 | ** (3) Second descriptive name |
||
1907 | ** (4) Name of the database (ex: "main", "temp") |
||
1908 | ** (5) Name of trigger that is doing the access |
||
1909 | ** |
||
1910 | ** The callback should return on of the following strings: SQLITE_OK, |
||
1911 | ** SQLITE_IGNORE, or SQLITE_DENY. Any other return value is an error. |
||
1912 | ** |
||
1913 | ** If this method is invoked with no arguments, the current authorization |
||
1914 | ** callback string is returned. |
||
1915 | */ |
||
1916 | case (int)DB_enum.DB_AUTHORIZER: |
||
1917 | { |
||
1918 | #if SQLITE_OMIT_AUTHORIZATION |
||
1919 | TCL.Tcl_AppendResult( interp, "authorization not available in this build" ); |
||
1920 | return TCL.TCL_RETURN; |
||
1921 | #else |
||
1922 | if( objc>3 ){ |
||
1923 | TCL.Tcl_WrongNumArgs(interp, 2, objv, "?CALLBACK?"); |
||
1924 | return TCL.TCL_ERROR; |
||
1925 | }else if( objc==2 ){ |
||
1926 | if( pDb.zAuth ){ |
||
1927 | TCL.Tcl_AppendResult(interp, pDb.zAuth); |
||
1928 | } |
||
1929 | }else{ |
||
1930 | string zAuth; |
||
1931 | int len; |
||
1932 | if( pDb.zAuth ){ |
||
1933 | TCL.Tcl_Free(pDb.zAuth); |
||
1934 | } |
||
1935 | zAuth = TCL.Tcl_GetStringFromObj(objv[2], len); |
||
1936 | if( zAuth && len>0 ){ |
||
1937 | pDb.zAuth = TCL.Tcl_Alloc( len + 1 ); |
||
1938 | memcpy(pDb.zAuth, zAuth, len+1); |
||
1939 | }else{ |
||
1940 | pDb.zAuth = 0; |
||
1941 | } |
||
1942 | if( pDb.zAuth ){ |
||
1943 | pDb.interp = interp; |
||
1944 | sqlite3_set_authorizer(pDb.db, auth_callback, pDb); |
||
1945 | }else{ |
||
1946 | sqlite3_set_authorizer(pDb.db, 0, 0); |
||
1947 | } |
||
1948 | } |
||
1949 | break; |
||
1950 | #endif |
||
1951 | } |
||
1952 | |||
1953 | /* $db backup ?DATABASE? FILENAME |
||
1954 | ** |
||
1955 | ** Open or create a database file named FILENAME. Transfer the |
||
1956 | ** content of local database DATABASE (default: "main") into the |
||
1957 | ** FILENAME database. |
||
1958 | */ |
||
1959 | case (int)DB_enum.DB_BACKUP: |
||
1960 | { |
||
1961 | string zDestFile; |
||
1962 | string zSrcDb; |
||
1963 | sqlite3 pDest = null; |
||
1964 | sqlite3_backup pBackup; |
||
1965 | |||
1966 | if ( objc == 3 ) |
||
1967 | { |
||
1968 | zSrcDb = "main"; |
||
1969 | zDestFile = TCL.Tcl_GetString( objv[2] ); |
||
1970 | } |
||
1971 | else if ( objc == 4 ) |
||
1972 | { |
||
1973 | zSrcDb = TCL.Tcl_GetString( objv[2] ); |
||
1974 | zDestFile = TCL.Tcl_GetString( objv[3] ); |
||
1975 | } |
||
1976 | else |
||
1977 | { |
||
1978 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?DATABASE? FILENAME" ); |
||
1979 | return TCL.TCL_ERROR; |
||
1980 | } |
||
1981 | rc = sqlite3_open( zDestFile, out pDest ); |
||
1982 | if ( rc != SQLITE_OK ) |
||
1983 | { |
||
1984 | TCL.Tcl_AppendResult( interp, "cannot open target database: ", |
||
1985 | sqlite3_errmsg( pDest ) ); |
||
1986 | sqlite3_close( pDest ); |
||
1987 | return TCL.TCL_ERROR; |
||
1988 | } |
||
1989 | pBackup = sqlite3_backup_init( pDest, "main", pDb.db, zSrcDb ); |
||
1990 | if ( pBackup == null ) |
||
1991 | { |
||
1992 | TCL.Tcl_AppendResult( interp, "backup failed: ", |
||
1993 | sqlite3_errmsg( pDest ) ); |
||
1994 | sqlite3_close( pDest ); |
||
1995 | return TCL.TCL_ERROR; |
||
1996 | } |
||
1997 | #if SQLITE_HAS_CODEC |
||
1998 | if ( pBackup.pSrc.pBt.pPager.pCodec != null ) |
||
1999 | { |
||
2000 | pBackup.pDest.pBt.pPager.xCodec = pBackup.pSrc.pBt.pPager.xCodec; |
||
2001 | pBackup.pDest.pBt.pPager.xCodecFree = pBackup.pSrc.pBt.pPager.xCodecFree; |
||
2002 | pBackup.pDest.pBt.pPager.xCodecSizeChng = pBackup.pSrc.pBt.pPager.xCodecSizeChng; |
||
2003 | pBackup.pDest.pBt.pPager.pCodec = pBackup.pSrc.pBt.pPager.pCodec.Copy(); |
||
2004 | } |
||
2005 | #endif |
||
2006 | while ( ( rc = sqlite3_backup_step( pBackup, 100 ) ) == SQLITE_OK ) |
||
2007 | { |
||
2008 | } |
||
2009 | sqlite3_backup_finish( pBackup ); |
||
2010 | if ( rc == SQLITE_DONE ) |
||
2011 | { |
||
2012 | rc = TCL.TCL_OK; |
||
2013 | } |
||
2014 | else |
||
2015 | { |
||
2016 | TCL.Tcl_AppendResult( interp, "backup failed: ", |
||
2017 | sqlite3_errmsg( pDest ) ); |
||
2018 | rc = TCL.TCL_ERROR; |
||
2019 | } |
||
2020 | sqlite3_close( pDest ); |
||
2021 | break; |
||
2022 | } |
||
2023 | |||
2024 | // /* $db busy ?CALLBACK? |
||
2025 | // ** |
||
2026 | // ** Invoke the given callback if an SQL statement attempts to open |
||
2027 | // ** a locked database file. |
||
2028 | // */ |
||
2029 | case (int)DB_enum.DB_BUSY: |
||
2030 | { |
||
2031 | if ( objc > 3 ) |
||
2032 | { |
||
2033 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "CALLBACK" ); |
||
2034 | return TCL.TCL_ERROR; |
||
2035 | } |
||
2036 | else if ( objc == 2 ) |
||
2037 | { |
||
2038 | if ( pDb.zBusy != null ) |
||
2039 | { |
||
2040 | TCL.Tcl_AppendResult( interp, pDb.zBusy ); |
||
2041 | } |
||
2042 | } |
||
2043 | else |
||
2044 | { |
||
2045 | string zBusy; |
||
2046 | int len = 0; |
||
2047 | if ( pDb.zBusy != null ) |
||
2048 | { |
||
2049 | TCL.Tcl_Free( ref pDb.zBusy ); |
||
2050 | } |
||
2051 | zBusy = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
||
2052 | if ( zBusy != null && len > 0 ) |
||
2053 | { |
||
2054 | //pDb.zBusy = TCL.Tcl_Alloc( len + 1 ); |
||
2055 | pDb.zBusy = zBusy;// memcpy( pDb.zBusy, zBusy, len + 1 ); |
||
2056 | } |
||
2057 | else |
||
2058 | { |
||
2059 | pDb.zBusy = null; |
||
2060 | } |
||
2061 | if ( pDb.zBusy != null ) |
||
2062 | { |
||
2063 | pDb.interp = interp; |
||
2064 | sqlite3_busy_handler( pDb.db, (dxBusy)DbBusyHandler, pDb ); |
||
2065 | } |
||
2066 | else |
||
2067 | { |
||
2068 | sqlite3_busy_handler( pDb.db, null, null ); |
||
2069 | } |
||
2070 | } |
||
2071 | break; |
||
2072 | } |
||
2073 | |||
2074 | // /* $db cache flush |
||
2075 | // ** $db cache size n |
||
2076 | // ** |
||
2077 | // ** Flush the prepared statement cache, or set the maximum number of |
||
2078 | // ** cached statements. |
||
2079 | // */ |
||
2080 | case (int)DB_enum.DB_CACHE: |
||
2081 | { |
||
2082 | string subCmd; |
||
2083 | int n = 0; |
||
2084 | |||
2085 | if ( objc <= 2 ) |
||
2086 | { |
||
2087 | TCL.Tcl_WrongNumArgs( interp, 1, objv, "cache option ?arg?" ); |
||
2088 | return TCL.TCL_ERROR; |
||
2089 | } |
||
2090 | subCmd = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
||
2091 | if ( subCmd == "flush" ) |
||
2092 | { |
||
2093 | if ( objc != 3 ) |
||
2094 | { |
||
2095 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "flush" ); |
||
2096 | return TCL.TCL_ERROR; |
||
2097 | } |
||
2098 | else |
||
2099 | { |
||
2100 | flushStmtCache( pDb ); |
||
2101 | } |
||
2102 | } |
||
2103 | else if ( subCmd == "size" ) |
||
2104 | { |
||
2105 | if ( objc != 4 ) |
||
2106 | { |
||
2107 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "size n" ); |
||
2108 | return TCL.TCL_ERROR; |
||
2109 | } |
||
2110 | else |
||
2111 | { |
||
2112 | if ( TCL.TCL_ERROR == ( TCL.Tcl_GetIntFromObj( interp, objv[3], out n ) != TCL.TCL_OK ? TCL.TCL_ERROR : TCL.TCL_OK ) ) |
||
2113 | { |
||
2114 | TCL.Tcl_AppendResult( interp, "cannot convert \"", |
||
2115 | TCL.Tcl_GetStringFromObj( objv[3], 0 ), "\" to integer", 0 ); |
||
2116 | return TCL.TCL_ERROR; |
||
2117 | } |
||
2118 | else |
||
2119 | { |
||
2120 | if ( n < 0 ) |
||
2121 | { |
||
2122 | flushStmtCache( pDb ); |
||
2123 | n = 0; |
||
2124 | } |
||
2125 | else if ( n > MAX_PREPARED_STMTS ) |
||
2126 | { |
||
2127 | n = MAX_PREPARED_STMTS; |
||
2128 | } |
||
2129 | pDb.maxStmt = n; |
||
2130 | } |
||
2131 | } |
||
2132 | } |
||
2133 | else |
||
2134 | { |
||
2135 | TCL.Tcl_AppendResult( interp, "bad option \"", |
||
2136 | TCL.Tcl_GetStringFromObj( objv[2], 0 ), "\": must be flush or size", null ); |
||
2137 | return TCL.TCL_ERROR; |
||
2138 | } |
||
2139 | break; |
||
2140 | } |
||
2141 | |||
2142 | /* $db changes |
||
2143 | ** |
||
2144 | ** Return the number of rows that were modified, inserted, or deleted by |
||
2145 | ** the most recent INSERT, UPDATE or DELETE statement, not including |
||
2146 | ** any changes made by trigger programs. |
||
2147 | */ |
||
2148 | case (int)DB_enum.DB_CHANGES: |
||
2149 | { |
||
2150 | Tcl_Obj pResult; |
||
2151 | if ( objc != 2 ) |
||
2152 | { |
||
2153 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
||
2154 | return TCL.TCL_ERROR; |
||
2155 | } |
||
2156 | pResult = TCL.Tcl_GetObjResult( interp ); |
||
2157 | TCL.Tcl_SetResult( interp, sqlite3_changes( pDb.db ).ToString(), 0 ); |
||
2158 | break; |
||
2159 | } |
||
2160 | |||
2161 | /* $db close |
||
2162 | ** |
||
2163 | ** Shutdown the database |
||
2164 | */ |
||
2165 | case (int)DB_enum.DB_CLOSE: |
||
2166 | { |
||
2167 | TCL.Tcl_DeleteCommand( interp, TCL.Tcl_GetStringFromObj( objv[0], 0 ) ); |
||
2168 | break; |
||
2169 | } |
||
2170 | |||
2171 | /* |
||
2172 | ** $db collate NAME SCRIPT |
||
2173 | ** |
||
2174 | ** Create a new SQL collation function called NAME. Whenever |
||
2175 | ** that function is called, invoke SCRIPT to evaluate the function. |
||
2176 | */ |
||
2177 | case (int)DB_enum.DB_COLLATE: |
||
2178 | { |
||
2179 | SqlCollate pCollate; |
||
2180 | string zName; |
||
2181 | string zScript; |
||
2182 | int nScript = 0; |
||
2183 | if ( objc != 4 ) |
||
2184 | { |
||
2185 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "NAME SCRIPT" ); |
||
2186 | return TCL.TCL_ERROR; |
||
2187 | } |
||
2188 | zName = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
||
2189 | zScript = TCL.Tcl_GetStringFromObj( objv[3], nScript ); |
||
2190 | pCollate = new SqlCollate();//(SqlCollate)Tcl_Alloc( sizeof(*pCollate) + nScript + 1 ); |
||
2191 | //if ( pCollate == null ) return TCL.TCL_ERROR; |
||
2192 | pCollate.interp = interp; |
||
2193 | pCollate.pNext = pDb.pCollate; |
||
2194 | pCollate.zScript = zScript; // pCollate[1]; |
||
2195 | pDb.pCollate = pCollate; |
||
2196 | //memcpy( pCollate.zScript, zScript, nScript + 1 ); |
||
2197 | if ( sqlite3_create_collation( pDb.db, zName, SQLITE_UTF8, |
||
2198 | pCollate, (dxCompare)tclSqlCollate ) != 0 ) |
||
2199 | { |
||
2200 | TCL.Tcl_SetResult( interp, sqlite3_errmsg( pDb.db ), TCL.TCL_VOLATILE ); |
||
2201 | return TCL.TCL_ERROR; |
||
2202 | } |
||
2203 | break; |
||
2204 | } |
||
2205 | |||
2206 | /* |
||
2207 | ** $db collation_needed SCRIPT |
||
2208 | ** |
||
2209 | ** Create a new SQL collation function called NAME. Whenever |
||
2210 | ** that function is called, invoke SCRIPT to evaluate the function. |
||
2211 | */ |
||
2212 | case (int)DB_enum.DB_COLLATION_NEEDED: |
||
2213 | { |
||
2214 | if ( objc != 3 ) |
||
2215 | { |
||
2216 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "SCRIPT" ); |
||
2217 | return TCL.TCL_ERROR; |
||
2218 | } |
||
2219 | if ( pDb.pCollateNeeded != null ) |
||
2220 | { |
||
2221 | TCL.Tcl_DecrRefCount( ref pDb.pCollateNeeded ); |
||
2222 | } |
||
2223 | pDb.pCollateNeeded = TCL.Tcl_DuplicateObj( objv[2] ); |
||
2224 | TCL.Tcl_IncrRefCount( pDb.pCollateNeeded ); |
||
2225 | sqlite3_collation_needed( pDb.db, (object)pDb, (dxCollNeeded)tclCollateNeeded ); |
||
2226 | break; |
||
2227 | } |
||
2228 | |||
2229 | /* $db commit_hook ?CALLBACK? |
||
2230 | ** |
||
2231 | ** Invoke the given callback just before committing every SQL transaction. |
||
2232 | ** If the callback throws an exception or returns non-zero, then the |
||
2233 | ** transaction is aborted. If CALLBACK is an empty string, the callback |
||
2234 | ** is disabled. |
||
2235 | */ |
||
2236 | case (int)DB_enum.DB_COMMIT_HOOK: |
||
2237 | { |
||
2238 | if ( objc > 3 ) |
||
2239 | { |
||
2240 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
||
2241 | return TCL.TCL_ERROR; |
||
2242 | } |
||
2243 | else if ( objc == 2 ) |
||
2244 | { |
||
2245 | if ( pDb.zCommit != null ) |
||
2246 | { |
||
2247 | TCL.Tcl_AppendResult( interp, pDb.zCommit ); |
||
2248 | } |
||
2249 | } |
||
2250 | else |
||
2251 | { |
||
2252 | string zCommit; |
||
2253 | int len = 0; |
||
2254 | if ( pDb.zCommit != null ) |
||
2255 | { |
||
2256 | TCL.Tcl_Free( ref pDb.zCommit ); |
||
2257 | } |
||
2258 | zCommit = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
||
2259 | if ( zCommit != null && len > 0 ) |
||
2260 | { |
||
2261 | pDb.zCommit = zCommit;// TCL.Tcl_Alloc( len + 1 ); |
||
2262 | //memcpy( pDb.zCommit, zCommit, len + 1 ); |
||
2263 | } |
||
2264 | else |
||
2265 | { |
||
2266 | pDb.zCommit = null; |
||
2267 | } |
||
2268 | if ( pDb.zCommit != null ) |
||
2269 | { |
||
2270 | pDb.interp = interp; |
||
2271 | sqlite3_commit_hook( pDb.db, DbCommitHandler, pDb ); |
||
2272 | } |
||
2273 | else |
||
2274 | { |
||
2275 | sqlite3_commit_hook( pDb.db, null, null ); |
||
2276 | } |
||
2277 | } |
||
2278 | break; |
||
2279 | } |
||
2280 | |||
2281 | /* $db complete SQL |
||
2282 | ** |
||
2283 | ** Return TRUE if SQL is a complete SQL statement. Return FALSE if |
||
2284 | ** additional lines of input are needed. This is similar to the |
||
2285 | ** built-in "info complete" command of Tcl. |
||
2286 | */ |
||
2287 | case (int)DB_enum.DB_COMPLETE: |
||
2288 | { |
||
2289 | #if !SQLITE_OMIT_COMPLETE |
||
2290 | Tcl_Obj pResult; |
||
2291 | int isComplete; |
||
2292 | if ( objc != 3 ) |
||
2293 | { |
||
2294 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL" ); |
||
2295 | return TCL.TCL_ERROR; |
||
2296 | } |
||
2297 | isComplete = sqlite3_complete( TCL.Tcl_GetStringFromObj( objv[2], 0 ) ); |
||
2298 | pResult = TCL.Tcl_GetObjResult( interp ); |
||
2299 | TCL.Tcl_SetBooleanObj( pResult, isComplete ); |
||
2300 | #endif |
||
2301 | break; |
||
2302 | } |
||
2303 | |||
2304 | /* $db copy conflict-algorithm table filename ?SEPARATOR? ?NULLINDICATOR? |
||
2305 | ** |
||
2306 | ** Copy data into table from filename, optionally using SEPARATOR |
||
2307 | ** as column separators. If a column contains a null string, or the |
||
2308 | ** value of NULLINDICATOR, a NULL is inserted for the column. |
||
2309 | ** conflict-algorithm is one of the sqlite conflict algorithms: |
||
2310 | ** rollback, abort, fail, ignore, replace |
||
2311 | ** On success, return the number of lines processed, not necessarily same |
||
2312 | ** as 'db changes' due to conflict-algorithm selected. |
||
2313 | ** |
||
2314 | ** This code is basically an implementation/enhancement of |
||
2315 | ** the sqlite3 shell.c ".import" command. |
||
2316 | ** |
||
2317 | ** This command usage is equivalent to the sqlite2.x COPY statement, |
||
2318 | ** which imports file data into a table using the PostgreSQL COPY file format: |
||
2319 | ** $db copy $conflit_algo $table_name $filename \t \\N |
||
2320 | */ |
||
2321 | case (int)DB_enum.DB_COPY: |
||
2322 | { |
||
2323 | string zTable; /* Insert data into this table */ |
||
2324 | string zFile; /* The file from which to extract data */ |
||
2325 | string zConflict; /* The conflict algorithm to use */ |
||
2326 | sqlite3_stmt pStmt = null; /* A statement */ |
||
2327 | int nCol; /* Number of columns in the table */ |
||
2328 | int nByte; /* Number of bytes in an SQL string */ |
||
2329 | int i, j; /* Loop counters */ |
||
2330 | int nSep; /* Number of bytes in zSep[] */ |
||
2331 | int nNull; /* Number of bytes in zNull[] */ |
||
2332 | StringBuilder zSql = new StringBuilder( 200 ); /* An SQL statement */ |
||
2333 | string zLine; /* A single line of input from the file */ |
||
2334 | string[] azCol; /* zLine[] broken up into columns */ |
||
2335 | string zCommit; /* How to commit changes */ |
||
2336 | TextReader _in; /* The input file */ |
||
2337 | int lineno = 0; /* Line number of input file */ |
||
2338 | StringBuilder zLineNum = new StringBuilder( 80 ); /* Line number print buffer */ |
||
2339 | Tcl_Obj pResult; /* interp result */ |
||
2340 | |||
2341 | string zSep; |
||
2342 | string zNull; |
||
2343 | if ( objc < 5 || objc > 7 ) |
||
2344 | { |
||
2345 | TCL.Tcl_WrongNumArgs( interp, 2, objv, |
||
2346 | "CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?" ); |
||
2347 | return TCL.TCL_ERROR; |
||
2348 | } |
||
2349 | if ( objc >= 6 ) |
||
2350 | { |
||
2351 | zSep = TCL.Tcl_GetStringFromObj( objv[5], 0 ); |
||
2352 | } |
||
2353 | else |
||
2354 | { |
||
2355 | zSep = "\t"; |
||
2356 | } |
||
2357 | if ( objc >= 7 ) |
||
2358 | { |
||
2359 | zNull = TCL.Tcl_GetStringFromObj( objv[6], 0 ); |
||
2360 | } |
||
2361 | else |
||
2362 | { |
||
2363 | zNull = ""; |
||
2364 | } |
||
2365 | zConflict = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
||
2366 | zTable = TCL.Tcl_GetStringFromObj( objv[3], 0 ); |
||
2367 | zFile = TCL.Tcl_GetStringFromObj( objv[4], 0 ); |
||
2368 | nSep = strlen30( zSep ); |
||
2369 | nNull = strlen30( zNull ); |
||
2370 | if ( nSep == 0 ) |
||
2371 | { |
||
2372 | TCL.Tcl_AppendResult( interp, "Error: non-null separator required for copy" ); |
||
2373 | return TCL.TCL_ERROR; |
||
2374 | } |
||
2375 | if ( zConflict != "rollback" && |
||
2376 | zConflict != "abort" && |
||
2377 | zConflict != "fail" && |
||
2378 | zConflict != "ignore" && |
||
2379 | zConflict != "replace" ) |
||
2380 | { |
||
2381 | TCL.Tcl_AppendResult( interp, "Error: \"", zConflict, |
||
2382 | "\", conflict-algorithm must be one of: rollback, " + |
||
2383 | "abort, fail, ignore, or replace", 0 ); |
||
2384 | return TCL.TCL_ERROR; |
||
2385 | } |
||
2386 | zSql.Append( sqlite3_mprintf( "SELECT * FROM '%q'", zTable ) ); |
||
2387 | if ( zSql == null ) |
||
2388 | { |
||
2389 | TCL.Tcl_AppendResult( interp, "Error: no such table: ", zTable ); |
||
2390 | return TCL.TCL_ERROR; |
||
2391 | } |
||
2392 | nByte = strlen30( zSql ); |
||
2393 | rc = sqlite3_prepare( pDb.db, zSql.ToString(), -1, ref pStmt, 0 ); |
||
2394 | sqlite3DbFree( null, ref zSql ); |
||
2395 | if ( rc != 0 ) |
||
2396 | { |
||
2397 | TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
||
2398 | nCol = 0; |
||
2399 | } |
||
2400 | else |
||
2401 | { |
||
2402 | nCol = sqlite3_column_count( pStmt ); |
||
2403 | } |
||
2404 | sqlite3_finalize( pStmt ); |
||
2405 | if ( nCol == 0 ) |
||
2406 | { |
||
2407 | return TCL.TCL_ERROR; |
||
2408 | } |
||
2409 | //zSql.Append(malloc( nByte + 50 + nCol*2 ); |
||
2410 | //if( zSql==0 ) { |
||
2411 | // TCL.Tcl_AppendResult(interp, "Error: can't malloc()"); |
||
2412 | // return TCL.TCL_ERROR; |
||
2413 | //} |
||
2414 | sqlite3_snprintf( nByte + 50, zSql, "INSERT OR %q INTO '%q' VALUES(?", |
||
2415 | zConflict, zTable ); |
||
2416 | j = strlen30( zSql ); |
||
2417 | for ( i = 1; i < nCol; i++ ) |
||
2418 | { |
||
2419 | //zSql+=[j++] = ','; |
||
2420 | //zSql[j++] = '?'; |
||
2421 | zSql.Append( ",?" ); |
||
2422 | } |
||
2423 | //zSql[j++] = ')'; |
||
2424 | //zSql[j] = ""; |
||
2425 | zSql.Append( ")" ); |
||
2426 | rc = sqlite3_prepare( pDb.db, zSql.ToString(), -1, ref pStmt, 0 ); |
||
2427 | //free(zSql); |
||
2428 | if ( rc != 0 ) |
||
2429 | { |
||
2430 | TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
||
2431 | sqlite3_finalize( pStmt ); |
||
2432 | return TCL.TCL_ERROR; |
||
2433 | } |
||
2434 | _in = new StreamReader( zFile );//fopen(zFile, "rb"); |
||
2435 | if ( _in == null ) |
||
2436 | { |
||
2437 | TCL.Tcl_AppendResult( interp, "Error: cannot open file: ", zFile ); |
||
2438 | sqlite3_finalize( pStmt ); |
||
2439 | return TCL.TCL_ERROR; |
||
2440 | } |
||
2441 | azCol = new string[nCol + 1];//malloc( sizeof(azCol[0])*(nCol+1) ); |
||
2442 | if ( azCol == null ) |
||
2443 | { |
||
2444 | TCL.Tcl_AppendResult( interp, "Error: can't malloc()" ); |
||
2445 | _in.Close();//fclose(_in); |
||
2446 | return TCL.TCL_ERROR; |
||
2447 | } |
||
2448 | sqlite3_exec( pDb.db, "BEGIN", 0, 0, 0 ); |
||
2449 | zCommit = "COMMIT"; |
||
2450 | while ( ( zLine = _in.ReadLine() ) != null )//local_getline(0, _in))!=0 ) |
||
2451 | { |
||
2452 | string z; |
||
2453 | i = 0; |
||
2454 | lineno++; |
||
2455 | azCol = zLine.Split( zSep[0] ); |
||
2456 | //for(i=0, z=zLine; *z; z++){ |
||
2457 | // if( *z==zSep[0] && strncmp(z, zSep, nSep)==0 ){ |
||
2458 | // *z = 0; |
||
2459 | // i++; |
||
2460 | // if( i<nCol ){ |
||
2461 | // azCol[i] = z[nSep]; |
||
2462 | // z += nSep-1; |
||
2463 | // } |
||
2464 | // } |
||
2465 | //} |
||
2466 | if ( azCol.Length != nCol ) |
||
2467 | { |
||
2468 | StringBuilder zErr = new StringBuilder( 200 ); |
||
2469 | int nErr = strlen30( zFile ) + 200; |
||
2470 | //zErr = malloc(nErr); |
||
2471 | //if( zErr ){ |
||
2472 | sqlite3_snprintf( nErr, zErr, |
||
2473 | "Error: %s line %d: expected %d columns of data but found %d", |
||
2474 | zFile, lineno, nCol, i + 1 ); |
||
2475 | TCL.Tcl_AppendResult( interp, zErr ); |
||
2476 | // free(zErr); |
||
2477 | //} |
||
2478 | zCommit = "ROLLBACK"; |
||
2479 | break; |
||
2480 | } |
||
2481 | for ( i = 0; i < nCol; i++ ) |
||
2482 | { |
||
2483 | /* check for null data, if so, bind as null */ |
||
2484 | if ( ( nNull > 0 && azCol[i] == zNull ) |
||
2485 | || strlen30( azCol[i] ) == 0 |
||
2486 | ) |
||
2487 | { |
||
2488 | sqlite3_bind_null( pStmt, i + 1 ); |
||
2489 | } |
||
2490 | else |
||
2491 | { |
||
2492 | sqlite3_bind_text( pStmt, i + 1, azCol[i], -1, SQLITE_STATIC ); |
||
2493 | } |
||
2494 | } |
||
2495 | sqlite3_step( pStmt ); |
||
2496 | rc = sqlite3_reset( pStmt ); |
||
2497 | //free(zLine); |
||
2498 | if ( rc != SQLITE_OK ) |
||
2499 | { |
||
2500 | TCL.Tcl_AppendResult( interp, "Error: ", sqlite3_errmsg( pDb.db ) ); |
||
2501 | zCommit = "ROLLBACK"; |
||
2502 | break; |
||
2503 | } |
||
2504 | } |
||
2505 | //free(azCol); |
||
2506 | _in.Close();// fclose( _in ); |
||
2507 | sqlite3_finalize( pStmt ); |
||
2508 | sqlite3_exec( pDb.db, zCommit, 0, 0, 0 ); |
||
2509 | |||
2510 | if ( zCommit[0] == 'C' ) |
||
2511 | { |
||
2512 | /* success, set result as number of lines processed */ |
||
2513 | pResult = TCL.Tcl_GetObjResult( interp ); |
||
2514 | TCL.Tcl_SetIntObj( pResult, lineno ); |
||
2515 | rc = TCL.TCL_OK; |
||
2516 | } |
||
2517 | else |
||
2518 | { |
||
2519 | /* failure, append lineno where failed */ |
||
2520 | sqlite3_snprintf( 80, zLineNum, "%d", lineno ); |
||
2521 | TCL.Tcl_AppendResult( interp, ", failed while processing line: ", zLineNum ); |
||
2522 | rc = TCL.TCL_ERROR; |
||
2523 | } |
||
2524 | break; |
||
2525 | } |
||
2526 | |||
2527 | /* |
||
2528 | ** $db enable_load_extension BOOLEAN |
||
2529 | ** |
||
2530 | ** Turn the extension loading feature on or off. It if off by |
||
2531 | ** default. |
||
2532 | */ |
||
2533 | case (int)DB_enum.DB_ENABLE_LOAD_EXTENSION: |
||
2534 | { |
||
2535 | #if !SQLITE_OMIT_LOAD_EXTENSION |
||
2536 | bool onoff = false; |
||
2537 | if ( objc != 3 ) |
||
2538 | { |
||
2539 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "BOOLEAN" ); |
||
2540 | return TCL.TCL_ERROR; |
||
2541 | } |
||
2542 | if ( TCL.Tcl_GetBooleanFromObj( interp, objv[2], out onoff ) ) |
||
2543 | { |
||
2544 | return TCL.TCL_ERROR; |
||
2545 | } |
||
2546 | sqlite3_enable_load_extension( pDb.db, onoff ? 1 : 0 ); |
||
2547 | break; |
||
2548 | #else |
||
2549 | TCL.Tcl_AppendResult(interp, "extension loading is turned off at compile-time", |
||
2550 | 0); |
||
2551 | return TCL.TCL_ERROR; |
||
2552 | #endif |
||
2553 | } |
||
2554 | |||
2555 | /* |
||
2556 | ** $db errorcode |
||
2557 | ** |
||
2558 | ** Return the numeric error code that was returned by the most recent |
||
2559 | ** call to sqlite3_exec(). |
||
2560 | */ |
||
2561 | case (int)DB_enum.DB_ERRORCODE: |
||
2562 | { |
||
2563 | TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewIntObj( sqlite3_errcode( pDb.db ) ) ); |
||
2564 | break; |
||
2565 | } |
||
2566 | |||
2567 | /* |
||
2568 | ** $db exists $sql |
||
2569 | ** $db onecolumn $sql |
||
2570 | ** |
||
2571 | ** The onecolumn method is the equivalent of: |
||
2572 | ** lindex [$db eval $sql] 0 |
||
2573 | */ |
||
2574 | case (int)DB_enum.DB_EXISTS: |
||
2575 | case (int)DB_enum.DB_ONECOLUMN: |
||
2576 | { |
||
2577 | DbEvalContext sEval = new DbEvalContext(); |
||
2578 | ; |
||
2579 | if ( objc != 3 ) |
||
2580 | { |
||
2581 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL" ); |
||
2582 | return TCL.TCL_ERROR; |
||
2583 | } |
||
2584 | dbEvalInit( sEval, pDb, objv[2], null ); |
||
2585 | rc = dbEvalStep( sEval ); |
||
2586 | if ( choice == (int)DB_enum.DB_ONECOLUMN ) |
||
2587 | { |
||
2588 | if ( rc == TCL.TCL_OK ) |
||
2589 | { |
||
2590 | TCL.Tcl_SetObjResult( interp, dbEvalColumnValue( sEval, 0 ) ); |
||
2591 | } |
||
2592 | } |
||
2593 | else if ( rc == TCL.TCL_BREAK || rc == TCL.TCL_OK ) |
||
2594 | { |
||
2595 | TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewBooleanObj( ( rc == TCL.TCL_OK ? 1 : 0 ) ) ); |
||
2596 | } |
||
2597 | dbEvalFinalize( sEval ); |
||
2598 | |||
2599 | if ( rc == TCL.TCL_BREAK ) |
||
2600 | { |
||
2601 | rc = TCL.TCL_OK; |
||
2602 | } |
||
2603 | break; |
||
2604 | } |
||
2605 | |||
2606 | /* |
||
2607 | ** $db eval $sql ?array? ?{ ...code... }? |
||
2608 | ** |
||
2609 | ** The SQL statement in $sql is evaluated. For each row, the values are |
||
2610 | ** placed in elements of the array named "array" and ...code... is executed. |
||
2611 | ** If "array" and "code" are omitted, then no callback is every invoked. |
||
2612 | ** If "array" is an empty string, then the values are placed in variables |
||
2613 | ** that have the same name as the fields extracted by the query. |
||
2614 | */ |
||
2615 | case (int)DB_enum.DB_EVAL: |
||
2616 | { |
||
2617 | if ( objc < 3 || objc > 5 ) |
||
2618 | { |
||
2619 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "SQL ?ARRAY-NAME? ?SCRIPT?" ); |
||
2620 | return TCL.TCL_ERROR; |
||
2621 | } |
||
2622 | |||
2623 | if ( objc == 3 ) |
||
2624 | { |
||
2625 | DbEvalContext sEval = new DbEvalContext(); |
||
2626 | Tcl_Obj pRet = TCL.Tcl_NewObj(); |
||
2627 | TCL.Tcl_IncrRefCount( pRet ); |
||
2628 | dbEvalInit( sEval, pDb, objv[2], null ); |
||
2629 | //Console.WriteLine( objv[2].ToString() ); |
||
2630 | while ( TCL.TCL_OK == ( rc = dbEvalStep( sEval ) ) ) |
||
2631 | { |
||
2632 | int i; |
||
2633 | int nCol; |
||
2634 | TclObject[] pDummy; |
||
2635 | dbEvalRowInfo( sEval, out nCol, out pDummy ); |
||
2636 | for ( i = 0; i < nCol; i++ ) |
||
2637 | { |
||
2638 | TCL.Tcl_ListObjAppendElement( interp, pRet, dbEvalColumnValue( sEval, i ) ); |
||
2639 | } |
||
2640 | } |
||
2641 | dbEvalFinalize( sEval ); |
||
2642 | if ( rc == TCL.TCL_BREAK ) |
||
2643 | { |
||
2644 | TCL.Tcl_SetObjResult( interp, pRet ); |
||
2645 | rc = TCL.TCL_OK; |
||
2646 | } |
||
2647 | TCL.Tcl_DecrRefCount( ref pRet ); |
||
2648 | } |
||
2649 | else |
||
2650 | { |
||
2651 | cd = new object[2]; |
||
2652 | DbEvalContext p; |
||
2653 | Tcl_Obj pArray = null; |
||
2654 | Tcl_Obj pScript; |
||
2655 | |||
2656 | if ( objc == 5 && !String.IsNullOrEmpty( TCL.Tcl_GetString( objv[3] ) ) ) |
||
2657 | { |
||
2658 | pArray = objv[3]; |
||
2659 | } |
||
2660 | pScript = objv[objc - 1]; |
||
2661 | TCL.Tcl_IncrRefCount( pScript ); |
||
2662 | |||
2663 | p = new DbEvalContext();// (DbEvalContext)Tcl_Alloc( sizeof( DbEvalContext ) ); |
||
2664 | dbEvalInit( p, pDb, objv[2], pArray ); |
||
2665 | |||
2666 | ( (object[])cd )[0] = p; |
||
2667 | ( (object[])cd )[1] = pScript; |
||
2668 | rc = DbEvalNextCmd( (object[])cd, interp, TCL.TCL_OK ); |
||
2669 | } |
||
2670 | break; |
||
2671 | } |
||
2672 | |||
2673 | /* |
||
2674 | ** $db function NAME [-argcount N] SCRIPT |
||
2675 | ** |
||
2676 | ** Create a new SQL function called NAME. Whenever that function is |
||
2677 | ** called, invoke SCRIPT to evaluate the function. |
||
2678 | */ |
||
2679 | case (int)DB_enum.DB_FUNCTION: |
||
2680 | { |
||
2681 | SqlFunc pFunc; |
||
2682 | Tcl_Obj pScript; |
||
2683 | string zName; |
||
2684 | int nArg = -1; |
||
2685 | if ( objc == 6 ) |
||
2686 | { |
||
2687 | string z = TCL.Tcl_GetString( objv[3] ); |
||
2688 | int n = strlen30( z ); |
||
2689 | if ( n > 2 && z.StartsWith( "-argcount" ) )//strncmp( z, "-argcount", n ) == 0 ) |
||
2690 | { |
||
2691 | if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[4], out nArg ) ) |
||
2692 | return TCL.TCL_ERROR; |
||
2693 | if ( nArg < 0 ) |
||
2694 | { |
||
2695 | TCL.Tcl_AppendResult( interp, "number of arguments must be non-negative" ); |
||
2696 | return TCL.TCL_ERROR; |
||
2697 | } |
||
2698 | } |
||
2699 | pScript = objv[5]; |
||
2700 | } |
||
2701 | else if ( objc != 4 ) |
||
2702 | { |
||
2703 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "NAME [-argcount N] SCRIPT" ); |
||
2704 | return TCL.TCL_ERROR; |
||
2705 | } |
||
2706 | else |
||
2707 | { |
||
2708 | pScript = objv[3]; |
||
2709 | } |
||
2710 | zName = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
||
2711 | pFunc = findSqlFunc( pDb, zName ); |
||
2712 | if ( pFunc == null ) |
||
2713 | return TCL.TCL_ERROR; |
||
2714 | if ( pFunc.pScript != null ) |
||
2715 | { |
||
2716 | TCL.Tcl_DecrRefCount( ref pFunc.pScript ); |
||
2717 | } |
||
2718 | pFunc.pScript = pScript; |
||
2719 | TCL.Tcl_IncrRefCount( pScript ); |
||
2720 | pFunc.useEvalObjv = safeToUseEvalObjv( interp, pScript ); |
||
2721 | rc = sqlite3_create_function( pDb.db, zName, nArg, SQLITE_UTF8, |
||
2722 | pFunc, tclSqlFunc, null, null ); |
||
2723 | if ( rc != SQLITE_OK ) |
||
2724 | { |
||
2725 | rc = TCL.TCL_ERROR; |
||
2726 | TCL.Tcl_SetResult( interp, sqlite3_errmsg( pDb.db ), TCL.TCL_VOLATILE ); |
||
2727 | } |
||
2728 | break; |
||
2729 | } |
||
2730 | |||
2731 | /* |
||
2732 | ** $db incrblob ?-readonly? ?DB? TABLE COLUMN ROWID |
||
2733 | */ |
||
2734 | case (int)DB_enum.DB_INCRBLOB: |
||
2735 | { |
||
2736 | #if SQLITE_OMIT_INCRBLOB |
||
2737 | TCL.Tcl_AppendResult( interp, "incrblob not available in this build" ); |
||
2738 | return TCL.TCL_ERROR; |
||
2739 | #else |
||
2740 | int isReadonly = 0; |
||
2741 | string zDb = "main" ; |
||
2742 | string zTable; |
||
2743 | string zColumn; |
||
2744 | long iRow = 0; |
||
2745 | |||
2746 | /* Check for the -readonly option */ |
||
2747 | if ( objc > 3 && TCL.Tcl_GetString( objv[2] ) == "-readonly" ) |
||
2748 | { |
||
2749 | isReadonly = 1; |
||
2750 | } |
||
2751 | |||
2752 | if ( objc != ( 5 + isReadonly ) && objc != ( 6 + isReadonly ) ) |
||
2753 | { |
||
2754 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?-readonly? ?DB? TABLE COLUMN ROWID" ); |
||
2755 | return TCL.TCL_ERROR; |
||
2756 | } |
||
2757 | |||
2758 | if ( objc == ( 6 + isReadonly ) ) |
||
2759 | { |
||
2760 | zDb = TCL.Tcl_GetString( objv[2] ) ; |
||
2761 | } |
||
2762 | zTable = TCL.Tcl_GetString( objv[objc - 3] ); |
||
2763 | zColumn = TCL.Tcl_GetString( objv[objc - 2] ) ; |
||
2764 | rc = TCL.Tcl_GetWideIntFromObj( interp, objv[objc - 1], out iRow ) ? 1 : 0; |
||
2765 | |||
2766 | if ( rc == TCL.TCL_OK ) |
||
2767 | { |
||
2768 | rc = createIncrblobChannel( |
||
2769 | interp, pDb, zDb, zTable, zColumn, iRow, isReadonly |
||
2770 | ); |
||
2771 | } |
||
2772 | break; |
||
2773 | #endif |
||
2774 | } |
||
2775 | /* |
||
2776 | ** $db interrupt |
||
2777 | ** |
||
2778 | ** Interrupt the execution of the inner-most SQL interpreter. This |
||
2779 | ** causes the SQL statement to return an error of SQLITE_INTERRUPT. |
||
2780 | */ |
||
2781 | case (int)DB_enum.DB_INTERRUPT: |
||
2782 | { |
||
2783 | sqlite3_interrupt( pDb.db ); |
||
2784 | break; |
||
2785 | } |
||
2786 | |||
2787 | /* |
||
2788 | ** $db nullvalue ?STRING? |
||
2789 | ** |
||
2790 | ** Change text used when a NULL comes back from the database. If ?STRING? |
||
2791 | ** is not present, then the current string used for NULL is returned. |
||
2792 | ** If STRING is present, then STRING is returned. |
||
2793 | ** |
||
2794 | */ |
||
2795 | case (int)DB_enum.DB_NULLVALUE: |
||
2796 | { |
||
2797 | if ( objc != 2 && objc != 3 ) |
||
2798 | { |
||
2799 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "NULLVALUE" ); |
||
2800 | return TCL.TCL_ERROR; |
||
2801 | } |
||
2802 | if ( objc == 3 ) |
||
2803 | { |
||
2804 | int len = 0; |
||
2805 | string zNull = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
||
2806 | if ( pDb.zNull != null ) |
||
2807 | { |
||
2808 | TCL.Tcl_Free( ref pDb.zNull ); |
||
2809 | } |
||
2810 | if ( zNull != null && len > 0 ) |
||
2811 | { |
||
2812 | pDb.zNull = zNull; |
||
2813 | //pDb.zNull = TCL.Tcl_Alloc( len + 1 ); |
||
2814 | //memcpy(pDb->zNull, zNull, len); |
||
2815 | //pDb.zNull[len] = '\0'; |
||
2816 | } |
||
2817 | else |
||
2818 | { |
||
2819 | pDb.zNull = null; |
||
2820 | } |
||
2821 | } |
||
2822 | TCL.Tcl_SetObjResult( interp, dbTextToObj( pDb.zNull ) ); |
||
2823 | break; |
||
2824 | } |
||
2825 | |||
2826 | /* |
||
2827 | ** $db last_insert_rowid |
||
2828 | ** |
||
2829 | ** Return an integer which is the ROWID for the most recent insert. |
||
2830 | */ |
||
2831 | case (int)DB_enum.DB_LAST_INSERT_ROWID: |
||
2832 | { |
||
2833 | Tcl_Obj pResult; |
||
2834 | Tcl_WideInt rowid; |
||
2835 | if ( objc != 2 ) |
||
2836 | { |
||
2837 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
||
2838 | return TCL.TCL_ERROR; |
||
2839 | } |
||
2840 | rowid = sqlite3_last_insert_rowid( pDb.db ); |
||
2841 | pResult = TCL.Tcl_GetObjResult( interp ); |
||
2842 | TCL.Tcl_SetLongObj( pResult, rowid ); |
||
2843 | break; |
||
2844 | } |
||
2845 | |||
2846 | /* |
||
2847 | ** The DB_ONECOLUMN method is implemented together with DB_EXISTS. |
||
2848 | */ |
||
2849 | |||
2850 | /* $db progress ?N CALLBACK? |
||
2851 | ** |
||
2852 | ** Invoke the given callback every N virtual machine opcodes while executing |
||
2853 | ** queries. |
||
2854 | */ |
||
2855 | case (int)DB_enum.DB_PROGRESS: |
||
2856 | { |
||
2857 | if ( objc == 2 ) |
||
2858 | { |
||
2859 | if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
||
2860 | { |
||
2861 | TCL.Tcl_AppendResult( interp, pDb.zProgress ); |
||
2862 | } |
||
2863 | } |
||
2864 | else if ( objc == 4 ) |
||
2865 | { |
||
2866 | string zProgress; |
||
2867 | int len = 0; |
||
2868 | int N = 0; |
||
2869 | if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[2], out N ) ) |
||
2870 | { |
||
2871 | return TCL.TCL_ERROR; |
||
2872 | }; |
||
2873 | if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
||
2874 | { |
||
2875 | TCL.Tcl_Free( ref pDb.zProgress ); |
||
2876 | } |
||
2877 | zProgress = TCL.Tcl_GetStringFromObj( objv[3], len ); |
||
2878 | if ( !String.IsNullOrEmpty( zProgress ) ) |
||
2879 | { |
||
2880 | //pDb.zProgress = TCL.Tcl_Alloc( len + 1 ); |
||
2881 | //memcpy( pDb.zProgress, zProgress, len + 1 ); |
||
2882 | pDb.zProgress = zProgress; |
||
2883 | } |
||
2884 | else |
||
2885 | { |
||
2886 | pDb.zProgress = null; |
||
2887 | } |
||
2888 | #if !SQLITE_OMIT_PROGRESS_CALLBACK |
||
2889 | if ( !String.IsNullOrEmpty( pDb.zProgress ) ) |
||
2890 | { |
||
2891 | pDb.interp = interp; |
||
2892 | sqlite3_progress_handler( pDb.db, N, DbProgressHandler, pDb ); |
||
2893 | } |
||
2894 | else |
||
2895 | { |
||
2896 | sqlite3_progress_handler( pDb.db, 0, null, 0 ); |
||
2897 | } |
||
2898 | #endif |
||
2899 | } |
||
2900 | else |
||
2901 | { |
||
2902 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "N CALLBACK" ); |
||
2903 | return TCL.TCL_ERROR; |
||
2904 | } |
||
2905 | break; |
||
2906 | } |
||
2907 | |||
2908 | /* $db profile ?CALLBACK? |
||
2909 | ** |
||
2910 | ** Make arrangements to invoke the CALLBACK routine after each SQL statement |
||
2911 | ** that has run. The text of the SQL and the amount of elapse time are |
||
2912 | ** appended to CALLBACK before the script is run. |
||
2913 | */ |
||
2914 | case (int)DB_enum.DB_PROFILE: |
||
2915 | { |
||
2916 | if ( objc > 3 ) |
||
2917 | { |
||
2918 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
||
2919 | return TCL.TCL_ERROR; |
||
2920 | } |
||
2921 | else if ( objc == 2 ) |
||
2922 | { |
||
2923 | if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
||
2924 | { |
||
2925 | TCL.Tcl_AppendResult( interp, pDb.zProfile ); |
||
2926 | } |
||
2927 | } |
||
2928 | else |
||
2929 | { |
||
2930 | string zProfile; |
||
2931 | int len = 0; |
||
2932 | if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
||
2933 | { |
||
2934 | TCL.Tcl_Free( ref pDb.zProfile ); |
||
2935 | } |
||
2936 | zProfile = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
||
2937 | if ( !String.IsNullOrEmpty( zProfile ) && len > 0 ) |
||
2938 | { |
||
2939 | //pDb.zProfile = TCL.Tcl_Alloc( len + 1 ); |
||
2940 | //memcpy( pDb.zProfile, zProfile, len + 1 ); |
||
2941 | pDb.zProfile = zProfile; |
||
2942 | } |
||
2943 | else |
||
2944 | { |
||
2945 | pDb.zProfile = null; |
||
2946 | } |
||
2947 | #if !SQLITE_OMIT_TRACE && !(SQLITE_OMIT_FLOATING_POINT) |
||
2948 | if ( !String.IsNullOrEmpty( pDb.zProfile ) ) |
||
2949 | { |
||
2950 | pDb.interp = interp; |
||
2951 | sqlite3_profile( pDb.db, DbProfileHandler, pDb ); |
||
2952 | } |
||
2953 | else |
||
2954 | { |
||
2955 | sqlite3_profile( pDb.db, null, null ); |
||
2956 | } |
||
2957 | #endif |
||
2958 | } |
||
2959 | break; |
||
2960 | } |
||
2961 | |||
2962 | /* |
||
2963 | ** $db rekey KEY |
||
2964 | ** |
||
2965 | ** Change the encryption key on the currently open database. |
||
2966 | */ |
||
2967 | case (int)DB_enum.DB_REKEY: |
||
2968 | { |
||
2969 | int nKey = 0; |
||
2970 | string pKey; |
||
2971 | if ( objc != 3 ) |
||
2972 | { |
||
2973 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "KEY" ); |
||
2974 | return TCL.TCL_ERROR; |
||
2975 | } |
||
2976 | pKey = TCL.Tcl_GetStringFromObj( objv[2], out nKey ); |
||
2977 | #if SQLITE_HAS_CODEC |
||
2978 | rc = sqlite3_rekey( pDb.db, pKey, nKey ); |
||
2979 | if ( rc != 0 ) |
||
2980 | { |
||
2981 | TCL.Tcl_AppendResult( interp, sqlite3ErrStr( rc ) ); |
||
2982 | rc = TCL.TCL_ERROR; |
||
2983 | } |
||
2984 | #endif |
||
2985 | break; |
||
2986 | } |
||
2987 | |||
2988 | /* $db restore ?DATABASE? FILENAME |
||
2989 | ** |
||
2990 | ** Open a database file named FILENAME. Transfer the content |
||
2991 | ** of FILENAME into the local database DATABASE (default: "main"). |
||
2992 | */ |
||
2993 | case (int)DB_enum.DB_RESTORE: |
||
2994 | { |
||
2995 | string zSrcFile; |
||
2996 | string zDestDb; |
||
2997 | sqlite3 pSrc = null; |
||
2998 | sqlite3_backup pBackup; |
||
2999 | int nTimeout = 0; |
||
3000 | |||
3001 | if ( objc == 3 ) |
||
3002 | { |
||
3003 | zDestDb = "main"; |
||
3004 | zSrcFile = TCL.Tcl_GetString( objv[2] ); |
||
3005 | } |
||
3006 | else if ( objc == 4 ) |
||
3007 | { |
||
3008 | zDestDb = TCL.Tcl_GetString( objv[2] ); |
||
3009 | zSrcFile = TCL.Tcl_GetString( objv[3] ); |
||
3010 | } |
||
3011 | else |
||
3012 | { |
||
3013 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?DATABASE? FILENAME" ); |
||
3014 | return TCL.TCL_ERROR; |
||
3015 | } |
||
3016 | rc = sqlite3_open_v2( zSrcFile, out pSrc, SQLITE_OPEN_READONLY, null ); |
||
3017 | if ( rc != SQLITE_OK ) |
||
3018 | { |
||
3019 | TCL.Tcl_AppendResult( interp, "cannot open source database: ", |
||
3020 | sqlite3_errmsg( pSrc ) ); |
||
3021 | sqlite3_close( pSrc ); |
||
3022 | return TCL.TCL_ERROR; |
||
3023 | } |
||
3024 | pBackup = sqlite3_backup_init( pDb.db, zDestDb, pSrc, "main" ); |
||
3025 | if ( pBackup == null ) |
||
3026 | { |
||
3027 | TCL.Tcl_AppendResult( interp, "restore failed: ", |
||
3028 | sqlite3_errmsg( pDb.db ) ); |
||
3029 | sqlite3_close( pSrc ); |
||
3030 | return TCL.TCL_ERROR; |
||
3031 | } |
||
3032 | |||
3033 | #if SQLITE_HAS_CODEC |
||
3034 | if ( pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec != null ) |
||
3035 | { |
||
3036 | pBackup.pSrc.pBt.pPager.xCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodec; |
||
3037 | pBackup.pSrc.pBt.pPager.xCodecFree = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecFree; |
||
3038 | pBackup.pSrc.pBt.pPager.xCodecSizeChng = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecSizeChng; |
||
3039 | pBackup.pSrc.pBt.pPager.pCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec.Copy(); |
||
3040 | if ( pBackup.pDest.GetHashCode() != pBackup.pDestDb.aDb[0].GetHashCode() ) // Not Main Database |
||
3041 | { |
||
3042 | pBackup.pDest.pBt.pPager.xCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodec; |
||
3043 | pBackup.pDest.pBt.pPager.xCodecFree = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecFree; |
||
3044 | pBackup.pDest.pBt.pPager.xCodecSizeChng = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.xCodecSizeChng; |
||
3045 | pBackup.pDest.pBt.pPager.pCodec = pBackup.pDestDb.aDb[0].pBt.pBt.pPager.pCodec.Copy(); |
||
3046 | } |
||
3047 | } |
||
3048 | #endif |
||
3049 | while ( ( rc = sqlite3_backup_step( pBackup, 100 ) ) == SQLITE_OK |
||
3050 | || rc == SQLITE_BUSY ) |
||
3051 | { |
||
3052 | if ( rc == SQLITE_BUSY ) |
||
3053 | { |
||
3054 | if ( nTimeout++ >= 3 ) |
||
3055 | break; |
||
3056 | sqlite3_sleep( 100 ); |
||
3057 | } |
||
3058 | } |
||
3059 | sqlite3_backup_finish( pBackup ); |
||
3060 | if ( rc == SQLITE_DONE ) |
||
3061 | { |
||
3062 | rc = TCL.TCL_OK; |
||
3063 | } |
||
3064 | else if ( rc == SQLITE_BUSY || rc == SQLITE_LOCKED ) |
||
3065 | { |
||
3066 | TCL.Tcl_AppendResult( interp, "restore failed: source database busy" |
||
3067 | ); |
||
3068 | rc = TCL.TCL_ERROR; |
||
3069 | } |
||
3070 | else |
||
3071 | { |
||
3072 | TCL.Tcl_AppendResult( interp, "restore failed: ", |
||
3073 | sqlite3_errmsg( pDb.db ) ); |
||
3074 | rc = TCL.TCL_ERROR; |
||
3075 | } |
||
3076 | sqlite3_close( pSrc ); |
||
3077 | break; |
||
3078 | } |
||
3079 | |||
3080 | /* |
||
3081 | ** $db status (step|sort|autoindex) |
||
3082 | ** |
||
3083 | ** Display SQLITE_STMTSTATUS_FULLSCAN_STEP or |
||
3084 | ** SQLITE_STMTSTATUS_SORT for the most recent eval. |
||
3085 | */ |
||
3086 | case (int)DB_enum.DB_STATUS: |
||
3087 | { |
||
3088 | int v; |
||
3089 | string zOp; |
||
3090 | if ( objc != 3 ) |
||
3091 | { |
||
3092 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "(step|sort|autoindex)" ); |
||
3093 | return TCL.TCL_ERROR; |
||
3094 | } |
||
3095 | zOp = TCL.Tcl_GetString( objv[2] ); |
||
3096 | if ( zOp == "step" ) |
||
3097 | { |
||
3098 | v = pDb.nStep; |
||
3099 | } |
||
3100 | else if ( zOp == "sort" ) |
||
3101 | { |
||
3102 | v = pDb.nSort; |
||
3103 | } |
||
3104 | else if ( zOp == "autoindex" ) |
||
3105 | { |
||
3106 | v = pDb.nIndex; |
||
3107 | } |
||
3108 | else |
||
3109 | { |
||
3110 | TCL.Tcl_AppendResult( interp, "bad argument: should be autoindex, step or sort" ); |
||
3111 | return TCL.TCL_ERROR; |
||
3112 | } |
||
3113 | TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewIntObj( v ) ); |
||
3114 | break; |
||
3115 | } |
||
3116 | |||
3117 | /* |
||
3118 | ** $db timeout MILLESECONDS |
||
3119 | ** |
||
3120 | ** Delay for the number of milliseconds specified when a file is locked. |
||
3121 | */ |
||
3122 | case (int)DB_enum.DB_TIMEOUT: |
||
3123 | { |
||
3124 | int ms = 0; |
||
3125 | if ( objc != 3 ) |
||
3126 | { |
||
3127 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "MILLISECONDS" ); |
||
3128 | return TCL.TCL_ERROR; |
||
3129 | } |
||
3130 | if ( TCL.TCL_OK != TCL.Tcl_GetIntFromObj( interp, objv[2], out ms ) ) |
||
3131 | return TCL.TCL_ERROR; |
||
3132 | sqlite3_busy_timeout( pDb.db, ms ); |
||
3133 | break; |
||
3134 | } |
||
3135 | |||
3136 | /* |
||
3137 | ** $db total_changes |
||
3138 | ** |
||
3139 | ** Return the number of rows that were modified, inserted, or deleted |
||
3140 | ** since the database handle was created. |
||
3141 | */ |
||
3142 | case (int)DB_enum.DB_TOTAL_CHANGES: |
||
3143 | { |
||
3144 | Tcl_Obj pResult; |
||
3145 | if ( objc != 2 ) |
||
3146 | { |
||
3147 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "" ); |
||
3148 | return TCL.TCL_ERROR; |
||
3149 | } |
||
3150 | pResult = TCL.Tcl_GetObjResult( interp ); |
||
3151 | TCL.Tcl_SetIntObj( pResult, sqlite3_total_changes( pDb.db ) ); |
||
3152 | break; |
||
3153 | } |
||
3154 | |||
3155 | /* $db trace ?CALLBACK? |
||
3156 | ** |
||
3157 | ** Make arrangements to invoke the CALLBACK routine for each SQL statement |
||
3158 | ** that is executed. The text of the SQL is appended to CALLBACK before |
||
3159 | ** it is executed. |
||
3160 | */ |
||
3161 | case (int)DB_enum.DB_TRACE: |
||
3162 | { |
||
3163 | if ( objc > 3 ) |
||
3164 | { |
||
3165 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?CALLBACK?" ); |
||
3166 | return TCL.TCL_ERROR; |
||
3167 | } |
||
3168 | else if ( objc == 2 ) |
||
3169 | { |
||
3170 | if ( pDb.zTrace != null ) |
||
3171 | { |
||
3172 | TCL.Tcl_AppendResult( interp, pDb.zTrace ); |
||
3173 | } |
||
3174 | } |
||
3175 | else |
||
3176 | { |
||
3177 | string zTrace; |
||
3178 | int len = 0; |
||
3179 | if ( pDb.zTrace != null ) |
||
3180 | { |
||
3181 | TCL.Tcl_Free( ref pDb.zTrace ); |
||
3182 | } |
||
3183 | zTrace = TCL.Tcl_GetStringFromObj( objv[2], out len ); |
||
3184 | if ( zTrace != null && len > 0 ) |
||
3185 | { |
||
3186 | //pDb.zTrace = TCL.Tcl_Alloc( len + 1 ); |
||
3187 | pDb.zTrace = zTrace;//memcpy( pDb.zTrace, zTrace, len + 1 ); |
||
3188 | } |
||
3189 | else |
||
3190 | { |
||
3191 | pDb.zTrace = null; |
||
3192 | } |
||
3193 | #if !SQLITE_OMIT_TRACE && !(SQLITE_OMIT_FLOATING_POINT) |
||
3194 | if ( pDb.zTrace != null ) |
||
3195 | { |
||
3196 | pDb.interp = interp; |
||
3197 | sqlite3_trace( pDb.db, (dxTrace)DbTraceHandler, pDb ); |
||
3198 | } |
||
3199 | else |
||
3200 | { |
||
3201 | sqlite3_trace( pDb.db, null, null ); |
||
3202 | } |
||
3203 | #endif |
||
3204 | } |
||
3205 | break; |
||
3206 | } |
||
3207 | |||
3208 | // /* $db transaction [-deferred|-immediate|-exclusive] SCRIPT |
||
3209 | // ** |
||
3210 | // ** Start a new transaction (if we are not already in the midst of a |
||
3211 | // ** transaction) and execute the TCL script SCRIPT. After SCRIPT |
||
3212 | // ** completes, either commit the transaction or roll it back if SCRIPT |
||
3213 | // ** throws an exception. Or if no new transation was started, do nothing. |
||
3214 | // ** pass the exception on up the stack. |
||
3215 | // ** |
||
3216 | // ** This command was inspired by Dave Thomas's talk on Ruby at the |
||
3217 | // ** 2005 O'Reilly Open Source Convention (OSCON). |
||
3218 | // */ |
||
3219 | case (int)DB_enum.DB_TRANSACTION: |
||
3220 | { |
||
3221 | Tcl_Obj pScript; |
||
3222 | string zBegin = "SAVEPOINT _tcl_transaction"; |
||
3223 | if ( objc != 3 && objc != 4 ) |
||
3224 | { |
||
3225 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "[TYPE] SCRIPT" ); |
||
3226 | return TCL.TCL_ERROR; |
||
3227 | } |
||
3228 | if ( pDb.nTransaction == 0 && objc == 4 ) |
||
3229 | { |
||
3230 | string[] TTYPE_strs = { "deferred", "exclusive", "immediate", null }; |
||
3231 | |||
3232 | int ttype = 0; |
||
3233 | if ( TCL.Tcl_GetIndexFromObj( interp, objv[2], TTYPE_strs, "transaction type", |
||
3234 | 0, out ttype ) ) |
||
3235 | { |
||
3236 | return TCL.TCL_ERROR; |
||
3237 | } |
||
3238 | switch ( ttype ) |
||
3239 | { |
||
3240 | case (int)TTYPE_enum.TTYPE_DEFERRED: /* no-op */ |
||
3241 | ; |
||
3242 | break; |
||
3243 | case (int)TTYPE_enum.TTYPE_EXCLUSIVE: |
||
3244 | zBegin = "BEGIN EXCLUSIVE"; |
||
3245 | break; |
||
3246 | case (int)TTYPE_enum.TTYPE_IMMEDIATE: |
||
3247 | zBegin = "BEGIN IMMEDIATE"; |
||
3248 | break; |
||
3249 | } |
||
3250 | } |
||
3251 | pScript = objv[objc - 1]; |
||
3252 | |||
3253 | /* Run the SQLite BEGIN command to open a transaction or savepoint. */ |
||
3254 | pDb.disableAuth++; |
||
3255 | rc = sqlite3_exec( pDb.db, zBegin, 0, 0, 0 ); |
||
3256 | pDb.disableAuth--; |
||
3257 | if ( rc != SQLITE_OK ) |
||
3258 | { |
||
3259 | TCL.Tcl_AppendResult( interp, sqlite3_errmsg( pDb.db ) ); |
||
3260 | return TCL.TCL_ERROR; |
||
3261 | } |
||
3262 | pDb.nTransaction++; |
||
3263 | /* If using NRE, schedule a callback to invoke the script pScript, then |
||
3264 | ** a second callback to commit (or rollback) the transaction or savepoint |
||
3265 | ** opened above. If not using NRE, evaluate the script directly, then |
||
3266 | ** call function DbTransPostCmd() to commit (or rollback) the transaction |
||
3267 | ** or savepoint. */ |
||
3268 | if ( DbUseNre() ) |
||
3269 | { |
||
3270 | Debugger.Break(); |
||
3271 | //Tcl_NRAddCallback( interp, DbTransPostCmd, cd, 0, 0, 0 ); |
||
3272 | //Tcl_NREvalObj(interp, pScript, 0); |
||
3273 | } |
||
3274 | else |
||
3275 | { |
||
3276 | rc = DbTransPostCmd( cd, interp, TCL.Tcl_EvalObjEx( interp, pScript, 0 ) ); |
||
3277 | } |
||
3278 | break; |
||
3279 | } |
||
3280 | |||
3281 | /* |
||
3282 | ** $db unlock_notify ?script? |
||
3283 | */ |
||
3284 | case (int)DB_enum.DB_UNLOCK_NOTIFY: |
||
3285 | { |
||
3286 | #if !SQLITE_ENABLE_UNLOCK_NOTIFY |
||
3287 | TCL.Tcl_AppendResult( interp, "unlock_notify not available in this build", 0 ); |
||
3288 | rc = TCL.TCL_ERROR; |
||
3289 | #else |
||
3290 | if( objc!=2 && objc!=3 ){ |
||
3291 | Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?"); |
||
3292 | rc = TCL.Tcl_ERROR; |
||
3293 | }else{ |
||
3294 | void (*xNotify)(void **, int) = 0; |
||
3295 | void *pNotifyArg = 0; |
||
3296 | |||
3297 | if( pDb.pUnlockNotify ){ |
||
3298 | Tcl_DecrRefCount(pDb.pUnlockNotify); |
||
3299 | pDb.pUnlockNotify = 0; |
||
3300 | } |
||
3301 | |||
3302 | if( objc==3 ){ |
||
3303 | xNotify = DbUnlockNotify; |
||
3304 | pNotifyArg = (void )pDb; |
||
3305 | pDb.pUnlockNotify = objv[2]; |
||
3306 | Tcl_IncrRefCount(pDb.pUnlockNotify); |
||
3307 | } |
||
3308 | |||
3309 | if( sqlite3_unlock_notify(pDb.db, xNotify, pNotifyArg) ){ |
||
3310 | Tcl_AppendResult(interp, sqlite3_errmsg(pDb.db), 0); |
||
3311 | rc = TCL.Tcl_ERROR; |
||
3312 | } |
||
3313 | } |
||
3314 | #endif |
||
3315 | break; |
||
3316 | } |
||
3317 | /* |
||
3318 | ** $db wal_hook ?script? |
||
3319 | ** $db update_hook ?script? |
||
3320 | ** $db rollback_hook ?script? |
||
3321 | */ |
||
3322 | case (int)DB_enum.DB_WAL_HOOK: |
||
3323 | case (int)DB_enum.DB_UPDATE_HOOK: |
||
3324 | case (int)DB_enum.DB_ROLLBACK_HOOK: |
||
3325 | { |
||
3326 | |||
3327 | /* set ppHook to point at pUpdateHook or pRollbackHook, depending on |
||
3328 | ** whether [$db update_hook] or [$db rollback_hook] was invoked. |
||
3329 | */ |
||
3330 | Tcl_Obj ppHook; |
||
3331 | if ( choice == (int)DB_enum.DB_UPDATE_HOOK ) |
||
3332 | { |
||
3333 | ppHook = pDb.pUpdateHook; |
||
3334 | } |
||
3335 | else if ( choice == (int)DB_enum.DB_WAL_HOOK ) |
||
3336 | { |
||
3337 | ppHook = pDb.pWalHook; |
||
3338 | } |
||
3339 | else |
||
3340 | { |
||
3341 | ppHook = pDb.pRollbackHook; |
||
3342 | } |
||
3343 | |||
3344 | if ( objc != 2 && objc != 3 ) |
||
3345 | { |
||
3346 | TCL.Tcl_WrongNumArgs( interp, 2, objv, "?SCRIPT?" ); |
||
3347 | return TCL.TCL_ERROR; |
||
3348 | } |
||
3349 | if ( ppHook != null ) |
||
3350 | { |
||
3351 | TCL.Tcl_SetObjResult( interp, ppHook ); |
||
3352 | if ( objc == 3 ) |
||
3353 | { |
||
3354 | TCL.Tcl_DecrRefCount( ref ppHook ); |
||
3355 | ppHook = null; |
||
3356 | } |
||
3357 | } |
||
3358 | if ( objc == 3 ) |
||
3359 | { |
||
3360 | Debug.Assert( null == ppHook ); |
||
3361 | if ( objv[2] != null )//TCL.Tcl_GetCharLength( objv[2] ) > 0 ) |
||
3362 | { |
||
3363 | ppHook = objv[2]; |
||
3364 | TCL.Tcl_IncrRefCount( ppHook ); |
||
3365 | } |
||
3366 | } |
||
3367 | if ( choice == (int)DB_enum.DB_UPDATE_HOOK ) |
||
3368 | { |
||
3369 | pDb.pUpdateHook = ppHook; |
||
3370 | } |
||
3371 | else |
||
3372 | { |
||
3373 | pDb.pRollbackHook = ppHook; |
||
3374 | } |
||
3375 | sqlite3_update_hook( pDb.db, ( pDb.pUpdateHook != null ? (dxUpdateCallback)DbUpdateHandler : null ), pDb ); |
||
3376 | sqlite3_rollback_hook( pDb.db, ( pDb.pRollbackHook != null ? (dxRollbackCallback)DbRollbackHandler : null ), pDb ); |
||
3377 | sqlite3_wal_hook( pDb.db, ( pDb.pWalHook != null ? (dxWalCallback)DbWalHandler : null ), pDb ); |
||
3378 | |||
3379 | break; |
||
3380 | } |
||
3381 | |||
3382 | /* $db version |
||
3383 | ** |
||
3384 | ** Return the version string for this database. |
||
3385 | */ |
||
3386 | case (int)DB_enum.DB_VERSION: |
||
3387 | { |
||
3388 | TCL.Tcl_SetResult( interp, sqlite3_libversion(), TCL.TCL_STATIC ); |
||
3389 | break; |
||
3390 | } |
||
3391 | |||
3392 | default: |
||
3393 | Debug.Assert( false, "Missing switch:" + objv[1].ToString() ); |
||
3394 | break; |
||
3395 | } /* End of the SWITCH statement */ |
||
3396 | return rc; |
||
3397 | } |
||
3398 | |||
3399 | #if SQLITE_TCL_NRE |
||
3400 | /* |
||
3401 | ** Adaptor that provides an objCmd interface to the NRE-enabled |
||
3402 | ** interface implementation. |
||
3403 | */ |
||
3404 | static int DbObjCmdAdaptor( |
||
3405 | void *cd, |
||
3406 | Tcl_Interp interp, |
||
3407 | int objc, |
||
3408 | Tcl_Obj *const*objv |
||
3409 | ){ |
||
3410 | return TCL.TCL_NRCallObjProc(interp, DbObjCmd, cd, objc, objv); |
||
3411 | } |
||
3412 | #endif //* SQLITE_TCL_NRE */ |
||
3413 | /* |
||
3414 | ** sqlite3 DBNAME FILENAME ?-vfs VFSNAME? ?-key KEY? ?-readonly BOOLEAN? |
||
3415 | ** ?-create BOOLEAN? ?-nomutex BOOLEAN? |
||
3416 | ** |
||
3417 | ** This is the main Tcl command. When the "sqlite" Tcl command is |
||
3418 | ** invoked, this routine runs to process that command. |
||
3419 | ** |
||
3420 | ** The first argument, DBNAME, is an arbitrary name for a new |
||
3421 | ** database connection. This command creates a new command named |
||
3422 | ** DBNAME that is used to control that connection. The database |
||
3423 | ** connection is deleted when the DBNAME command is deleted. |
||
3424 | ** |
||
3425 | ** The second argument is the name of the database file. |
||
3426 | ** |
||
3427 | */ |
||
3428 | static int DbMain( object cd, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) |
||
3429 | { |
||
3430 | SqliteDb p; |
||
3431 | string pKey = null; |
||
3432 | int nKey = 0; |
||
3433 | string zArg; |
||
3434 | string zErrMsg; |
||
3435 | int i; |
||
3436 | string zFile; |
||
3437 | string zVfs = null; |
||
3438 | int flags; |
||
3439 | Tcl_DString translatedFilename; |
||
3440 | /* In normal use, each TCL interpreter runs in a single thread. So |
||
3441 | ** by default, we can turn of mutexing on SQLite database connections. |
||
3442 | ** However, for testing purposes it is useful to have mutexes turned |
||
3443 | ** on. So, by default, mutexes default off. But if compiled with |
||
3444 | ** SQLITE_TCL_DEFAULT_FULLMUTEX then mutexes default on. |
||
3445 | */ |
||
3446 | #if SQLITE_TCL_DEFAULT_FULLMUTEX |
||
3447 | flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; |
||
3448 | #else |
||
3449 | flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; |
||
3450 | #endif |
||
3451 | if ( objc == 2 ) |
||
3452 | { |
||
3453 | zArg = TCL.Tcl_GetStringFromObj( objv[1], 0 ); |
||
3454 | if ( zArg == "-version" ) |
||
3455 | { |
||
3456 | TCL.Tcl_AppendResult( interp, sqlite3_version, null ); |
||
3457 | return TCL.TCL_OK; |
||
3458 | } |
||
3459 | if ( zArg == "-has-codec" ) |
||
3460 | { |
||
3461 | #if SQLITE_HAS_CODEC |
||
3462 | TCL.Tcl_AppendResult( interp, "1" ); |
||
3463 | #else |
||
3464 | TCL.Tcl_AppendResult( interp, "0", null ); |
||
3465 | #endif |
||
3466 | return TCL.TCL_OK; |
||
3467 | } |
||
3468 | if ( zArg == "-tcl-uses-utf" ) |
||
3469 | { |
||
3470 | TCL.Tcl_AppendResult( interp, "1", null ); |
||
3471 | return TCL.TCL_OK; |
||
3472 | } |
||
3473 | } |
||
3474 | for ( i = 3; i + 1 < objc; i += 2 ) |
||
3475 | { |
||
3476 | zArg = TCL.Tcl_GetString( objv[i] ); |
||
3477 | if ( zArg == "-key" ) |
||
3478 | { |
||
3479 | pKey = TCL.Tcl_GetStringFromObj( objv[i + 1], out nKey ); |
||
3480 | } |
||
3481 | else if ( zArg == "-vfs" ) |
||
3482 | { |
||
3483 | zVfs = TCL.Tcl_GetString( objv[i + 1] ); |
||
3484 | } |
||
3485 | else if ( zArg == "-readonly" ) |
||
3486 | { |
||
3487 | bool b = false; |
||
3488 | if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
||
3489 | return TCL.TCL_ERROR; |
||
3490 | if ( b ) |
||
3491 | { |
||
3492 | flags &= ~( SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE ); |
||
3493 | flags |= SQLITE_OPEN_READONLY; |
||
3494 | } |
||
3495 | else |
||
3496 | { |
||
3497 | flags &= ~SQLITE_OPEN_READONLY; |
||
3498 | flags |= SQLITE_OPEN_READWRITE; |
||
3499 | } |
||
3500 | } |
||
3501 | else if ( zArg == "-create" ) |
||
3502 | { |
||
3503 | bool b = false; |
||
3504 | if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
||
3505 | return TCL.TCL_ERROR; |
||
3506 | if ( b && ( flags & SQLITE_OPEN_READONLY ) == 0 ) |
||
3507 | { |
||
3508 | flags |= SQLITE_OPEN_CREATE; |
||
3509 | } |
||
3510 | else |
||
3511 | { |
||
3512 | flags &= ~SQLITE_OPEN_CREATE; |
||
3513 | } |
||
3514 | } |
||
3515 | else if ( zArg == "-nomutex" ) |
||
3516 | { |
||
3517 | bool b = false; |
||
3518 | if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
||
3519 | return TCL.TCL_ERROR; |
||
3520 | if ( b ) |
||
3521 | { |
||
3522 | flags |= SQLITE_OPEN_NOMUTEX; |
||
3523 | flags &= ~SQLITE_OPEN_FULLMUTEX; |
||
3524 | } |
||
3525 | else |
||
3526 | { |
||
3527 | flags &= ~SQLITE_OPEN_NOMUTEX; |
||
3528 | } |
||
3529 | } |
||
3530 | else if ( zArg == "-fullmutex" )//strcmp( zArg, "-fullmutex" ) == 0 ) |
||
3531 | { |
||
3532 | bool b = false; |
||
3533 | if ( TCL.Tcl_GetBooleanFromObj( interp, objv[i + 1], out b ) ) |
||
3534 | return TCL.TCL_ERROR; |
||
3535 | if ( b ) |
||
3536 | { |
||
3537 | flags |= SQLITE_OPEN_FULLMUTEX; |
||
3538 | flags &= ~SQLITE_OPEN_NOMUTEX; |
||
3539 | } |
||
3540 | else |
||
3541 | { |
||
3542 | flags &= ~SQLITE_OPEN_FULLMUTEX; |
||
3543 | } |
||
3544 | } |
||
3545 | else |
||
3546 | { |
||
3547 | TCL.Tcl_AppendResult( interp, "unknown option: ", zArg, null ); |
||
3548 | return TCL.TCL_ERROR; |
||
3549 | } |
||
3550 | } |
||
3551 | if ( objc < 3 || ( objc & 1 ) != 1 ) |
||
3552 | { |
||
3553 | TCL.Tcl_WrongNumArgs( interp, 1, objv, |
||
3554 | "HANDLE FILENAME ?-vfs VFSNAME? ?-readonly BOOLEAN? ?-create BOOLEAN? ?-nomutex BOOLEAN? ?-fullmutex BOOLEAN?" |
||
3555 | #if SQLITE_HAS_CODEC |
||
3556 | + " ?-key CODECKEY?" |
||
3557 | #endif |
||
3558 | ); |
||
3559 | return TCL.TCL_ERROR; |
||
3560 | } |
||
3561 | zErrMsg = ""; |
||
3562 | p = new SqliteDb();//(SqliteDb)Tcl_Alloc( sizeof(*p) ); |
||
3563 | if ( p == null ) |
||
3564 | { |
||
3565 | TCL.Tcl_SetResult( interp, "malloc failed", TCL.TCL_STATIC ); |
||
3566 | return TCL.TCL_ERROR; |
||
3567 | } |
||
3568 | //memset(p, 0, sizeof(*p)); |
||
3569 | zFile = TCL.Tcl_GetStringFromObj( objv[2], 0 ); |
||
3570 | //zFile = TCL.Tcl_TranslateFileName( interp, zFile, ref translatedFilename ); |
||
3571 | sqlite3_open_v2( zFile, out p.db, flags, zVfs ); |
||
3572 | //Tcl_DStringFree( ref translatedFilename ); |
||
3573 | if ( SQLITE_OK != sqlite3_errcode( p.db ) ) |
||
3574 | { |
||
3575 | zErrMsg = sqlite3_errmsg( p.db );// sqlite3_mprintf( "%s", sqlite3_errmsg( p.db ) ); |
||
3576 | sqlite3_close( p.db ); |
||
3577 | p.db = null; |
||
3578 | } |
||
3579 | #if SQLITE_HAS_CODEC |
||
3580 | if ( p.db != null ) |
||
3581 | { |
||
3582 | sqlite3_key( p.db, pKey, nKey ); |
||
3583 | } |
||
3584 | #endif |
||
3585 | if ( p.db == null ) |
||
3586 | { |
||
3587 | TCL.Tcl_SetResult( interp, zErrMsg, TCL.TCL_VOLATILE ); |
||
3588 | TCL.Tcl_Free( ref p ); |
||
3589 | zErrMsg = "";// sqlite3DbFree( db, ref zErrMsg ); |
||
3590 | return TCL.TCL_ERROR; |
||
3591 | } |
||
3592 | p.maxStmt = NUM_PREPARED_STMTS; |
||
3593 | p.interp = interp; |
||
3594 | zArg = TCL.Tcl_GetStringFromObj( objv[1], 0 ); |
||
3595 | if ( DbUseNre() ) |
||
3596 | { |
||
3597 | Debugger.Break(); |
||
3598 | //Tcl_NRCreateCommand(interp, zArg, DbObjCmdAdaptor, DbObjCmd, |
||
3599 | // p, DbDeleteCmd); |
||
3600 | } |
||
3601 | else |
||
3602 | { |
||
3603 | TCL.Tcl_CreateObjCommand( interp, zArg, (Interp.dxObjCmdProc)DbObjCmd, p, (Interp.dxCmdDeleteProc)DbDeleteCmd ); |
||
3604 | } |
||
3605 | return TCL.TCL_OK; |
||
3606 | } |
||
3607 | |||
3608 | /* |
||
3609 | ** Provide a dummy TCL.Tcl_InitStubs if we are using this as a static |
||
3610 | ** library. |
||
3611 | */ |
||
3612 | #if !USE_TCL_STUBS |
||
3613 | //# undef TCL.Tcl_InitStubs |
||
3614 | static void Tcl_InitStubs( Tcl_Interp interp, string s, int i ) |
||
3615 | { |
||
3616 | } |
||
3617 | #endif |
||
3618 | |||
3619 | /* |
||
3620 | ** Make sure we have a PACKAGE_VERSION macro defined. This will be |
||
3621 | ** defined automatically by the TEA makefile. But other makefiles |
||
3622 | ** do not define it. |
||
3623 | */ |
||
3624 | #if !PACKAGE_VERSION |
||
3625 | public static string PACKAGE_VERSION;//# define PACKAGE_VERSION SQLITE_VERSION |
||
3626 | #endif |
||
3627 | |||
3628 | |||
3629 | /* |
||
3630 | ** Initialize this module. |
||
3631 | ** |
||
3632 | ** This Tcl module contains only a single new Tcl command named "sqlite". |
||
3633 | ** (Hence there is no namespace. There is no point in using a namespace |
||
3634 | ** if the extension only supplies one new name!) The "sqlite" command is |
||
3635 | ** used to open a new SQLite database. See the DbMain() routine above |
||
3636 | ** for additional information. |
||
3637 | ** |
||
3638 | ** The EXTERN macros are required by TCL in order to work on windows. |
||
3639 | */ |
||
3640 | //int Sqlite3_Init(Tcl_Interp interp){ |
||
3641 | static public int Sqlite3_Init( Tcl_Interp interp ) |
||
3642 | { |
||
3643 | PACKAGE_VERSION = SQLITE_VERSION; |
||
3644 | Tcl_InitStubs( interp, "tclsharp 8.4", 0 ); |
||
3645 | TCL.Tcl_CreateObjCommand( interp, "sqlite3", (Interp.dxObjCmdProc)DbMain, null, null ); |
||
3646 | TCL.Tcl_PkgProvide( interp, "sqlite3", PACKAGE_VERSION ); |
||
3647 | |||
3648 | #if !SQLITE_3_SUFFIX_ONLY |
||
3649 | /* The "sqlite" alias is undocumented. It is here only to support |
||
3650 | ** legacy scripts. All new scripts should use only the "sqlite3" |
||
3651 | ** command. |
||
3652 | */ |
||
3653 | TCL.Tcl_CreateObjCommand( interp, "sqlite", (Interp.dxObjCmdProc)DbMain, null, null ); |
||
3654 | #endif |
||
3655 | return TCL.TCL_OK; |
||
3656 | } |
||
3657 | //int Tclsqlite3_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
||
3658 | //int Sqlite3_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
||
3659 | //int Tclsqlite3_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
||
3660 | //int Sqlite3_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3661 | //int Tclsqlite3_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3662 | //int Sqlite3_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3663 | //int Tclsqlite3_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK;} |
||
3664 | |||
3665 | |||
3666 | #if !SQLITE_3_SUFFIX_ONLY |
||
3667 | //int Sqlite_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
||
3668 | //int Tclsqlite_Init(Tcl_Interp interp){ return Sqlite3_Init(interp); } |
||
3669 | //int Sqlite_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
||
3670 | //int Tclsqlite_SafeInit(Tcl_Interp interp){ return TCL.TCL_OK; } |
||
3671 | //int Sqlite_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3672 | //int Tclsqlite_Unload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3673 | //int Sqlite_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK; } |
||
3674 | //int Tclsqlite_SafeUnload(Tcl_Interp interp, int flags){ return TCL.TCL_OK;} |
||
3675 | #endif |
||
3676 | |||
3677 | #if TCLSH |
||
3678 | /***************************************************************************** |
||
3679 | ** All of the code that follows is used to build standalone TCL interpreters |
||
3680 | ** that are statically linked with SQLite. Enable these by compiling |
||
3681 | ** with -DTCLSH=n where n can be 1 or 2. An n of 1 generates a standard |
||
3682 | ** tclsh but with SQLite built in. An n of 2 generates the SQLite space |
||
3683 | ** analysis program. |
||
3684 | */ |
||
3685 | |||
3686 | #if (SQLITE_TEST) || (SQLITE_TCLMD5) |
||
3687 | /* |
||
3688 | * This code implements the MD5 message-digest algorithm. |
||
3689 | * The algorithm is due to Ron Rivest. This code was |
||
3690 | * written by Colin Plumb in 1993, no copyright is claimed. |
||
3691 | * This code is in the public domain; do with it what you wish. |
||
3692 | * |
||
3693 | * Equivalent code is available from RSA Data Security, Inc. |
||
3694 | * This code has been tested against that, and is equivalent, |
||
3695 | * except that you don't need to include two pages of legalese |
||
3696 | * with every copy. |
||
3697 | * |
||
3698 | * To compute the message digest of a chunk of bytes, declare an |
||
3699 | * MD5Context structure, pass it to MD5Init, call MD5Update as |
||
3700 | * needed on buffers full of bytes, and then call MD5Final, which |
||
3701 | * will fill a supplied 16-byte array with the digest. |
||
3702 | */ |
||
3703 | |||
3704 | /* |
||
3705 | * If compiled on a machine that doesn't have a 32-bit integer, |
||
3706 | * you just set "uint32" to the appropriate datatype for an |
||
3707 | * unsigned 32-bit integer. For example: |
||
3708 | * |
||
3709 | * cc -Duint32='unsigned long' md5.c |
||
3710 | * |
||
3711 | */ |
||
3712 | //#if !uint32 |
||
3713 | //# define uint32 unsigned int |
||
3714 | //#endif |
||
3715 | |||
3716 | //struct MD5Context { |
||
3717 | // int isInit; |
||
3718 | // uint32 buf[4]; |
||
3719 | // uint32 bits[2]; |
||
3720 | // unsigned char in[64]; |
||
3721 | //}; |
||
3722 | //typedef struct MD5Context MD5Context; |
||
3723 | class MD5Context |
||
3724 | { |
||
3725 | public bool isInit; |
||
3726 | public u32[] buf = new u32[4]; |
||
3727 | public u32[] bits = new u32[2]; |
||
3728 | public u32[] _in = new u32[64]; |
||
3729 | public Mem _Mem; |
||
3730 | }; |
||
3731 | |||
3732 | /* |
||
3733 | * Note: this code is harmless on little-endian machines. |
||
3734 | */ |
||
3735 | //static void byteReverse (unsigned char *buf, unsigned longs){ |
||
3736 | // uint32 t; |
||
3737 | // do { |
||
3738 | // t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | |
||
3739 | // ((unsigned)buf[1]<<8 | buf[0]); |
||
3740 | // *(uint32 )buf = t; |
||
3741 | // buf += 4; |
||
3742 | // } while (--longs); |
||
3743 | //} |
||
3744 | |||
3745 | /* The four core functions - F1 is optimized somewhat */ |
||
3746 | |||
3747 | delegate u32 dxF1234( u32 x, u32 y, u32 z ); |
||
3748 | |||
3749 | //* #define F1(x, y, z) (x & y | ~x & z) */ |
||
3750 | //#define F1(x, y, z) (z ^ (x & (y ^ z))) |
||
3751 | static u32 F1( u32 x, u32 y, u32 z ) |
||
3752 | { |
||
3753 | return ( z ^ ( x & ( y ^ z ) ) ); |
||
3754 | } |
||
3755 | |||
3756 | //#define F2(x, y, z) F1(z, x, y) |
||
3757 | static u32 F2( u32 x, u32 y, u32 z ) |
||
3758 | { |
||
3759 | return F1( z, x, y ); |
||
3760 | } |
||
3761 | |||
3762 | //#define F3(x, y, z) (x ^ y ^ z) |
||
3763 | static u32 F3( u32 x, u32 y, u32 z ) |
||
3764 | { |
||
3765 | return ( x ^ y ^ z ); |
||
3766 | } |
||
3767 | |||
3768 | //#define F4(x, y, z) (y ^ (x | ~z)) |
||
3769 | static u32 F4( u32 x, u32 y, u32 z ) |
||
3770 | { |
||
3771 | return ( y ^ ( x | ~z ) ); |
||
3772 | } |
||
3773 | |||
3774 | ///* This is the central step in the MD5 algorithm. */ |
||
3775 | //#define MD5STEP(f, w, x, y, z, data, s) \ |
||
3776 | // ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) |
||
3777 | static void MD5STEP( dxF1234 f, ref u32 w, u32 x, u32 y, u32 z, u32 data, byte s ) |
||
3778 | { |
||
3779 | w += f( x, y, z ) + data; |
||
3780 | w = w << s | w >> ( 32 - s ); |
||
3781 | w += x; |
||
3782 | } |
||
3783 | |||
3784 | /* |
||
3785 | * The core of the MD5 algorithm, this alters an existing MD5 hash to |
||
3786 | * reflect the addition of 16 longwords of new data. MD5Update blocks |
||
3787 | * the data and converts bytes into longwords for this routine. |
||
3788 | */ |
||
3789 | static void MD5Transform( u32[] buf, u32[] _in ) |
||
3790 | { |
||
3791 | u32 a, b, c, d; |
||
3792 | |||
3793 | a = buf[0]; |
||
3794 | b = buf[1]; |
||
3795 | c = buf[2]; |
||
3796 | d = buf[3]; |
||
3797 | |||
3798 | MD5STEP( F1, ref a, b, c, d, _in[0] + 0xd76aa478, 7 ); |
||
3799 | MD5STEP( F1, ref d, a, b, c, _in[1] + 0xe8c7b756, 12 ); |
||
3800 | MD5STEP( F1, ref c, d, a, b, _in[2] + 0x242070db, 17 ); |
||
3801 | MD5STEP( F1, ref b, c, d, a, _in[3] + 0xc1bdceee, 22 ); |
||
3802 | MD5STEP( F1, ref a, b, c, d, _in[4] + 0xf57c0faf, 7 ); |
||
3803 | MD5STEP( F1, ref d, a, b, c, _in[5] + 0x4787c62a, 12 ); |
||
3804 | MD5STEP( F1, ref c, d, a, b, _in[6] + 0xa8304613, 17 ); |
||
3805 | MD5STEP( F1, ref b, c, d, a, _in[7] + 0xfd469501, 22 ); |
||
3806 | MD5STEP( F1, ref a, b, c, d, _in[8] + 0x698098d8, 7 ); |
||
3807 | MD5STEP( F1, ref d, a, b, c, _in[9] + 0x8b44f7af, 12 ); |
||
3808 | MD5STEP( F1, ref c, d, a, b, _in[10] + 0xffff5bb1, 17 ); |
||
3809 | MD5STEP( F1, ref b, c, d, a, _in[11] + 0x895cd7be, 22 ); |
||
3810 | MD5STEP( F1, ref a, b, c, d, _in[12] + 0x6b901122, 7 ); |
||
3811 | MD5STEP( F1, ref d, a, b, c, _in[13] + 0xfd987193, 12 ); |
||
3812 | MD5STEP( F1, ref c, d, a, b, _in[14] + 0xa679438e, 17 ); |
||
3813 | MD5STEP( F1, ref b, c, d, a, _in[15] + 0x49b40821, 22 ); |
||
3814 | |||
3815 | MD5STEP( F2, ref a, b, c, d, _in[1] + 0xf61e2562, 5 ); |
||
3816 | MD5STEP( F2, ref d, a, b, c, _in[6] + 0xc040b340, 9 ); |
||
3817 | MD5STEP( F2, ref c, d, a, b, _in[11] + 0x265e5a51, 14 ); |
||
3818 | MD5STEP( F2, ref b, c, d, a, _in[0] + 0xe9b6c7aa, 20 ); |
||
3819 | MD5STEP( F2, ref a, b, c, d, _in[5] + 0xd62f105d, 5 ); |
||
3820 | MD5STEP( F2, ref d, a, b, c, _in[10] + 0x02441453, 9 ); |
||
3821 | MD5STEP( F2, ref c, d, a, b, _in[15] + 0xd8a1e681, 14 ); |
||
3822 | MD5STEP( F2, ref b, c, d, a, _in[4] + 0xe7d3fbc8, 20 ); |
||
3823 | MD5STEP( F2, ref a, b, c, d, _in[9] + 0x21e1cde6, 5 ); |
||
3824 | MD5STEP( F2, ref d, a, b, c, _in[14] + 0xc33707d6, 9 ); |
||
3825 | MD5STEP( F2, ref c, d, a, b, _in[3] + 0xf4d50d87, 14 ); |
||
3826 | MD5STEP( F2, ref b, c, d, a, _in[8] + 0x455a14ed, 20 ); |
||
3827 | MD5STEP( F2, ref a, b, c, d, _in[13] + 0xa9e3e905, 5 ); |
||
3828 | MD5STEP( F2, ref d, a, b, c, _in[2] + 0xfcefa3f8, 9 ); |
||
3829 | MD5STEP( F2, ref c, d, a, b, _in[7] + 0x676f02d9, 14 ); |
||
3830 | MD5STEP( F2, ref b, c, d, a, _in[12] + 0x8d2a4c8a, 20 ); |
||
3831 | |||
3832 | MD5STEP( F3, ref a, b, c, d, _in[5] + 0xfffa3942, 4 ); |
||
3833 | MD5STEP( F3, ref d, a, b, c, _in[8] + 0x8771f681, 11 ); |
||
3834 | MD5STEP( F3, ref c, d, a, b, _in[11] + 0x6d9d6122, 16 ); |
||
3835 | MD5STEP( F3, ref b, c, d, a, _in[14] + 0xfde5380c, 23 ); |
||
3836 | MD5STEP( F3, ref a, b, c, d, _in[1] + 0xa4beea44, 4 ); |
||
3837 | MD5STEP( F3, ref d, a, b, c, _in[4] + 0x4bdecfa9, 11 ); |
||
3838 | MD5STEP( F3, ref c, d, a, b, _in[7] + 0xf6bb4b60, 16 ); |
||
3839 | MD5STEP( F3, ref b, c, d, a, _in[10] + 0xbebfbc70, 23 ); |
||
3840 | MD5STEP( F3, ref a, b, c, d, _in[13] + 0x289b7ec6, 4 ); |
||
3841 | MD5STEP( F3, ref d, a, b, c, _in[0] + 0xeaa127fa, 11 ); |
||
3842 | MD5STEP( F3, ref c, d, a, b, _in[3] + 0xd4ef3085, 16 ); |
||
3843 | MD5STEP( F3, ref b, c, d, a, _in[6] + 0x04881d05, 23 ); |
||
3844 | MD5STEP( F3, ref a, b, c, d, _in[9] + 0xd9d4d039, 4 ); |
||
3845 | MD5STEP( F3, ref d, a, b, c, _in[12] + 0xe6db99e5, 11 ); |
||
3846 | MD5STEP( F3, ref c, d, a, b, _in[15] + 0x1fa27cf8, 16 ); |
||
3847 | MD5STEP( F3, ref b, c, d, a, _in[2] + 0xc4ac5665, 23 ); |
||
3848 | |||
3849 | MD5STEP( F4, ref a, b, c, d, _in[0] + 0xf4292244, 6 ); |
||
3850 | MD5STEP( F4, ref d, a, b, c, _in[7] + 0x432aff97, 10 ); |
||
3851 | MD5STEP( F4, ref c, d, a, b, _in[14] + 0xab9423a7, 15 ); |
||
3852 | MD5STEP( F4, ref b, c, d, a, _in[5] + 0xfc93a039, 21 ); |
||
3853 | MD5STEP( F4, ref a, b, c, d, _in[12] + 0x655b59c3, 6 ); |
||
3854 | MD5STEP( F4, ref d, a, b, c, _in[3] + 0x8f0ccc92, 10 ); |
||
3855 | MD5STEP( F4, ref c, d, a, b, _in[10] + 0xffeff47d, 15 ); |
||
3856 | MD5STEP( F4, ref b, c, d, a, _in[1] + 0x85845dd1, 21 ); |
||
3857 | MD5STEP( F4, ref a, b, c, d, _in[8] + 0x6fa87e4f, 6 ); |
||
3858 | MD5STEP( F4, ref d, a, b, c, _in[15] + 0xfe2ce6e0, 10 ); |
||
3859 | MD5STEP( F4, ref c, d, a, b, _in[6] + 0xa3014314, 15 ); |
||
3860 | MD5STEP( F4, ref b, c, d, a, _in[13] + 0x4e0811a1, 21 ); |
||
3861 | MD5STEP( F4, ref a, b, c, d, _in[4] + 0xf7537e82, 6 ); |
||
3862 | MD5STEP( F4, ref d, a, b, c, _in[11] + 0xbd3af235, 10 ); |
||
3863 | MD5STEP( F4, ref c, d, a, b, _in[2] + 0x2ad7d2bb, 15 ); |
||
3864 | MD5STEP( F4, ref b, c, d, a, _in[9] + 0xeb86d391, 21 ); |
||
3865 | |||
3866 | buf[0] += a; |
||
3867 | buf[1] += b; |
||
3868 | buf[2] += c; |
||
3869 | buf[3] += d; |
||
3870 | } |
||
3871 | |||
3872 | /* |
||
3873 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious |
||
3874 | * initialization constants. |
||
3875 | */ |
||
3876 | static void MD5Init( MD5Context ctx ) |
||
3877 | { |
||
3878 | ctx.isInit = true; |
||
3879 | ctx.buf[0] = 0x67452301; |
||
3880 | ctx.buf[1] = 0xefcdab89; |
||
3881 | ctx.buf[2] = 0x98badcfe; |
||
3882 | ctx.buf[3] = 0x10325476; |
||
3883 | ctx.bits[0] = 0; |
||
3884 | ctx.bits[1] = 0; |
||
3885 | } |
||
3886 | |||
3887 | /* |
||
3888 | * Update context to reflect the concatenation of another buffer full |
||
3889 | * of bytes. |
||
3890 | */ |
||
3891 | static void MD5Update( MD5Context pCtx, byte[] buf, int len ) |
||
3892 | { |
||
3893 | |||
3894 | MD5Context ctx = (MD5Context)pCtx; |
||
3895 | int t; |
||
3896 | |||
3897 | /* Update bitcount */ |
||
3898 | |||
3899 | t = (int)ctx.bits[0]; |
||
3900 | if ( ( ctx.bits[0] = (u32)( t + ( (u32)len << 3 ) ) ) < t ) |
||
3901 | ctx.bits[1]++; /* Carry from low to high */ |
||
3902 | ctx.bits[1] += (u32)( len >> 29 ); |
||
3903 | |||
3904 | t = ( t >> 3 ) & 0x3f; /* Bytes already in shsInfo.data */ |
||
3905 | |||
3906 | /* Handle any leading odd-sized chunks */ |
||
3907 | |||
3908 | int _buf = 0; // Offset into buffer |
||
3909 | int p = t; //Offset into ctx._in |
||
3910 | if ( t != 0 ) |
||
3911 | { |
||
3912 | //byte p = (byte)ctx._in + t; |
||
3913 | t = 64 - t; |
||
3914 | if ( len < t ) |
||
3915 | { |
||
3916 | Buffer.BlockCopy( buf, _buf, ctx._in, p, len );// memcpy( p, buf, len ); |
||
3917 | return; |
||
3918 | } |
||
3919 | Buffer.BlockCopy( buf, _buf, ctx._in, p, t ); //memcpy( p, buf, t ); |
||
3920 | //byteReverse(ctx._in, 16); |
||
3921 | MD5Transform( ctx.buf, ctx._in ); |
||
3922 | _buf += t;// buf += t; |
||
3923 | len -= t; |
||
3924 | } |
||
3925 | |||
3926 | /* Process data in 64-byte chunks */ |
||
3927 | |||
3928 | while ( len >= 64 ) |
||
3929 | { |
||
3930 | Buffer.BlockCopy( buf, _buf, ctx._in, 0, 64 );//memcpy( ctx._in, buf, 64 ); |
||
3931 | //byteReverse(ctx._in, 16); |
||
3932 | MD5Transform( ctx.buf, ctx._in ); |
||
3933 | _buf += 64;// buf += 64; |
||
3934 | len -= 64; |
||
3935 | } |
||
3936 | |||
3937 | /* Handle any remaining bytes of data. */ |
||
3938 | |||
3939 | Buffer.BlockCopy( buf, _buf, ctx._in, 0, len ); //memcpy( ctx._in, buf, len ); |
||
3940 | } |
||
3941 | |||
3942 | /* |
||
3943 | * Final wrapup - pad to 64-byte boundary with the bit pattern |
||
3944 | * 1 0* (64-bit count of bits processed, MSB-first) |
||
3945 | */ |
||
3946 | |||
3947 | static void MD5Final( byte[] digest, MD5Context pCtx ) |
||
3948 | { |
||
3949 | MD5Context ctx = pCtx; |
||
3950 | int count; |
||
3951 | int p; |
||
3952 | |||
3953 | /* Compute number of bytes mod 64 */ |
||
3954 | count = (int)( ctx.bits[0] >> 3 ) & 0x3F; |
||
3955 | |||
3956 | /* Set the first char of padding to 0x80. This is safe since there is |
||
3957 | always at least one byte free */ |
||
3958 | p = count; |
||
3959 | ctx._in[p++] = 0x80; |
||
3960 | |||
3961 | /* Bytes of padding needed to make 64 bytes */ |
||
3962 | count = 64 - 1 - count; |
||
3963 | |||
3964 | /* Pad out to 56 mod 64 */ |
||
3965 | if ( count < 8 ) |
||
3966 | { |
||
3967 | /* Two lots of padding: Pad the first block to 64 bytes */ |
||
3968 | Array.Clear( ctx._in, p, count );//memset(p, 0, count); |
||
3969 | //byteReverse( ctx._in, 16 ); |
||
3970 | MD5Transform( ctx.buf, ctx._in ); |
||
3971 | |||
3972 | /* Now fill the next block with 56 bytes */ |
||
3973 | Array.Clear( ctx._in, 0, 56 );//memset(ctx._in, 0, 56); |
||
3974 | } |
||
3975 | else |
||
3976 | { |
||
3977 | /* Pad block to 56 bytes */ |
||
3978 | Array.Clear( ctx._in, p, count - 8 );//memset(p, 0, count-8); |
||
3979 | } |
||
3980 | //byteReverse( ctx._in, 14 ); |
||
3981 | |||
3982 | /* Append length in bits and transform */ |
||
3983 | ctx._in[14] = (byte)ctx.bits[0]; |
||
3984 | ctx._in[15] = (byte)ctx.bits[1]; |
||
3985 | |||
3986 | MD5Transform( ctx.buf, ctx._in ); |
||
3987 | //byteReverse( ctx.buf, 4 ); |
||
3988 | Buffer.BlockCopy( ctx.buf, 0, digest, 0, 16 );//memcpy(digest, ctx.buf, 16); |
||
3989 | //memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */ |
||
3990 | Array.Clear( ctx._in, 0, ctx._in.Length ); |
||
3991 | Array.Clear( ctx.bits, 0, ctx.bits.Length ); |
||
3992 | Array.Clear( ctx.buf, 0, ctx.buf.Length ); |
||
3993 | ctx._Mem = null; |
||
3994 | } |
||
3995 | |||
3996 | /* |
||
3997 | ** Convert a 128-bit MD5 digest into a 32-digit base-16 number. |
||
3998 | */ |
||
3999 | static void DigestToBase16( byte[] digest, byte[] zBuf ) |
||
4000 | { |
||
4001 | string zEncode = "0123456789abcdef"; |
||
4002 | int i, j; |
||
4003 | |||
4004 | for ( j = i = 0; i < 16; i++ ) |
||
4005 | { |
||
4006 | int a = digest[i]; |
||
4007 | zBuf[j++] = (byte)zEncode[( a >> 4 ) & 0xf]; |
||
4008 | zBuf[j++] = (byte)zEncode[a & 0xf]; |
||
4009 | } |
||
4010 | if ( j < zBuf.Length ) |
||
4011 | zBuf[j] = 0; |
||
4012 | } |
||
4013 | |||
4014 | |||
4015 | /* |
||
4016 | ** Convert a 128-bit MD5 digest into sequency of eight 5-digit integers |
||
4017 | ** each representing 16 bits of the digest and separated from each |
||
4018 | ** other by a "-" character. |
||
4019 | */ |
||
4020 | //static void MD5DigestToBase10x8(unsigned char digest[16], char zDigest[50]){ |
||
4021 | // int i, j; |
||
4022 | // unsigned int x; |
||
4023 | // for(i=j=0; i<16; i+=2){ |
||
4024 | // x = digest[i]*256 + digest[i+1]; |
||
4025 | // if( i>0 ) zDigest[j++] = '-'; |
||
4026 | // sprintf(&zDigest[j], "%05u", x); |
||
4027 | // j += 5; |
||
4028 | // } |
||
4029 | // zDigest[j] = 0; |
||
4030 | //} |
||
4031 | |||
4032 | /* |
||
4033 | ** A TCL command for md5. The argument is the text to be hashed. The |
||
4034 | ** Result is the hash in base64. |
||
4035 | */ |
||
4036 | static int md5_cmd( object cd, Tcl_Interp interp, int argc, Tcl_Obj[] argv ) |
||
4037 | { |
||
4038 | MD5Context ctx = new MD5Context(); |
||
4039 | byte[] digest = new byte[16]; |
||
4040 | byte[] zBuf = new byte[32]; |
||
4041 | |||
4042 | |||
4043 | if ( argc != 2 ) |
||
4044 | { |
||
4045 | TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], |
||
4046 | " TEXT\"" ); |
||
4047 | return TCL.TCL_ERROR; |
||
4048 | } |
||
4049 | MD5Init( ctx ); |
||
4050 | MD5Update( ctx, Encoding.UTF8.GetBytes( argv[1].ToString() ), Encoding.UTF8.GetByteCount( argv[1].ToString() ) ); |
||
4051 | MD5Final( digest, ctx ); |
||
4052 | DigestToBase16( digest, zBuf ); |
||
4053 | TCL.Tcl_AppendResult( interp, Encoding.UTF8.GetString( zBuf, 0, zBuf.Length ) ); |
||
4054 | return TCL.TCL_OK; |
||
4055 | } |
||
4056 | |||
4057 | |||
4058 | /* |
||
4059 | ** A TCL command to take the md5 hash of a file. The argument is the |
||
4060 | ** name of the file. |
||
4061 | */ |
||
4062 | static int md5file_cmd( object cd, Tcl_Interp interp, int argc, Tcl_Obj[] argv ) |
||
4063 | { |
||
4064 | StreamReader _in = null; |
||
4065 | byte[] digest = new byte[16]; |
||
4066 | StringBuilder zBuf = new StringBuilder( 10240 ); |
||
4067 | |||
4068 | if ( argc != 2 ) |
||
4069 | { |
||
4070 | TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0], |
||
4071 | " FILENAME\"", 0 ); |
||
4072 | return TCL.TCL_ERROR; |
||
4073 | } |
||
4074 | Debugger.Break(); // TODO -- _in = fopen( argv[1], "rb" ); |
||
4075 | if ( _in == null ) |
||
4076 | { |
||
4077 | TCL.Tcl_AppendResult( interp, "unable to open file \"", argv[1], |
||
4078 | "\" for reading", 0 ); |
||
4079 | return TCL.TCL_ERROR; |
||
4080 | } |
||
4081 | Debugger.Break(); // TODO |
||
4082 | //MD5Init( ctx ); |
||
4083 | //for(;;){ |
||
4084 | // int n; |
||
4085 | // n = fread(zBuf, 1, zBuf.Capacity, _in); |
||
4086 | // if( n<=0 ) break; |
||
4087 | // MD5Update(ctx, zBuf.ToString(), (unsigned)n); |
||
4088 | //} |
||
4089 | //fclose(_in); |
||
4090 | //MD5Final(digest, ctx); |
||
4091 | // DigestToBase16(digest, zBuf); |
||
4092 | //Tcl_AppendResult( interp, zBuf ); |
||
4093 | return TCL.TCL_OK; |
||
4094 | } |
||
4095 | |||
4096 | /* |
||
4097 | ** Register the four new TCL commands for generating MD5 checksums |
||
4098 | ** with the TCL interpreter. |
||
4099 | */ |
||
4100 | static public int Md5_Init( Tcl_Interp interp ) |
||
4101 | { |
||
4102 | TCL.Tcl_CreateCommand( interp, "md5", md5_cmd, null, null ); |
||
4103 | //Tcl_CreateCommand(interp, "md5-10x8", (Tcl_CmdProc)md5_cmd, |
||
4104 | // MD5DigestToBase10x8, 0); |
||
4105 | |||
4106 | TCL.Tcl_CreateCommand( interp, "md5file", md5file_cmd, null, null ); |
||
4107 | |||
4108 | //Tcl_CreateCommand(interp, "md5file-10x8", (Tcl_CmdProc)md5file_cmd, |
||
4109 | // MD5DigestToBase10x8, 0); |
||
4110 | return TCL.TCL_OK; |
||
4111 | } |
||
4112 | #endif //* defined(SQLITE_TEST) || defined(SQLITE_TCLMD5) */ |
||
4113 | |||
4114 | #if (SQLITE_TEST) |
||
4115 | /* |
||
4116 | ** During testing, the special md5sum() aggregate function is available. |
||
4117 | ** inside SQLite. The following routines implement that function. |
||
4118 | */ |
||
4119 | static void md5step( sqlite3_context context, int argc, sqlite3_value[] argv ) |
||
4120 | { |
||
4121 | MD5Context p = null; |
||
4122 | int i; |
||
4123 | if ( argc < 1 ) |
||
4124 | return; |
||
4125 | Mem pMem = sqlite3_aggregate_context( context, 1 );//sizeof(*p)); |
||
4126 | if ( pMem._MD5Context == null ) |
||
4127 | { |
||
4128 | pMem._MD5Context = new MD5Context(); |
||
4129 | ( (MD5Context)pMem._MD5Context )._Mem = pMem; |
||
4130 | } |
||
4131 | p = (MD5Context)pMem._MD5Context; |
||
4132 | if ( p == null ) |
||
4133 | return; |
||
4134 | if ( !p.isInit ) |
||
4135 | { |
||
4136 | MD5Init( p ); |
||
4137 | } |
||
4138 | for ( i = 0; i < argc; i++ ) |
||
4139 | { |
||
4140 | byte[] zData = sqlite3_value_text( argv[i] ) == null ? null : Encoding.UTF8.GetBytes( sqlite3_value_text( argv[i] ) ); |
||
4141 | if ( zData != null ) |
||
4142 | { |
||
4143 | MD5Update( p, zData, zData.Length ); |
||
4144 | } |
||
4145 | } |
||
4146 | } |
||
4147 | |||
4148 | static void md5finalize( sqlite3_context context ) |
||
4149 | { |
||
4150 | MD5Context p; |
||
4151 | byte[] digest = new byte[16]; |
||
4152 | byte[] zBuf = new byte[33]; |
||
4153 | Mem pMem = sqlite3_aggregate_context( context, 0 ); |
||
4154 | if ( pMem != null ) |
||
4155 | { |
||
4156 | p = (MD5Context)pMem._MD5Context; |
||
4157 | MD5Final( digest, p ); |
||
4158 | } |
||
4159 | DigestToBase16( digest, zBuf ); |
||
4160 | sqlite3_result_text( context, Encoding.UTF8.GetString( zBuf, 0, zBuf.Length ), -1, SQLITE_TRANSIENT ); |
||
4161 | } |
||
4162 | |||
4163 | static int Md5_Register( sqlite3 db, ref string dummy1, sqlite3_api_routines dummy2 ) |
||
4164 | { |
||
4165 | int rc = sqlite3_create_function( db, "md5sum", -1, SQLITE_UTF8, 0, null, |
||
4166 | md5step, md5finalize ); |
||
4167 | sqlite3_overload_function( db, "md5sum", -1 ); /* To exercise this API */ |
||
4168 | return rc; |
||
4169 | } |
||
4170 | |||
4171 | #endif //* defined(SQLITE_TEST) */ |
||
4172 | |||
4173 | |||
4174 | /* |
||
4175 | ** If the macro TCLSH is one, then put in code this for the |
||
4176 | ** "main" routine that will initialize Tcl and take input from |
||
4177 | ** standard input, or if a file is named on the command line |
||
4178 | ** the TCL interpreter reads and evaluates that file. |
||
4179 | */ |
||
4180 | #if TCLSH//==1 |
||
4181 | //static char zMainloop[] = |
||
4182 | // "set line {}\n" |
||
4183 | // "while {![eof stdin]} {\n" |
||
4184 | // "if {$line!=\"\"} {\n" |
||
4185 | // "puts -nonewline \"> \"\n" |
||
4186 | // "} else {\n" |
||
4187 | // "puts -nonewline \"% \"\n" |
||
4188 | // "}\n" |
||
4189 | // "flush stdout\n" |
||
4190 | // "append line [gets stdin]\n" |
||
4191 | // "if {[info complete $line]} {\n" |
||
4192 | // "if {[catch {uplevel #0 $line} result]} {\n" |
||
4193 | // "puts stderr \"Error: $result\"\n" |
||
4194 | // "} elseif {$result!=\"\"} {\n" |
||
4195 | // "puts $result\n" |
||
4196 | // "}\n" |
||
4197 | // "set line {}\n" |
||
4198 | // "} else {\n" |
||
4199 | // "append line \\n\n" |
||
4200 | // "}\n" |
||
4201 | // "}\n" |
||
4202 | //; |
||
4203 | #endif |
||
4204 | |||
4205 | //#if TCLSH==2 |
||
4206 | //static char zMainloop[] = |
||
4207 | //#include "spaceanal_tcl.h" |
||
4208 | //; |
||
4209 | //#endif |
||
4210 | //#define TCLSH_MAIN main /* Needed to fake out mktclapp */ |
||
4211 | #if SQLITE_TEST |
||
4212 | //static void init_all(Tcl_Interp ); |
||
4213 | static int init_all_cmd( |
||
4214 | ClientData cd, |
||
4215 | Tcl_Interp interp, |
||
4216 | int objc, |
||
4217 | Tcl_Obj[] objv |
||
4218 | ) |
||
4219 | { |
||
4220 | |||
4221 | Tcl_Interp slave; |
||
4222 | if ( objc != 2 ) |
||
4223 | { |
||
4224 | TCL.Tcl_WrongNumArgs( interp, 1, objv, "SLAVE" ); |
||
4225 | return TCL.TCL_ERROR; |
||
4226 | } |
||
4227 | |||
4228 | slave = null;// TCL.Tcl_GetSlave( interp, TCL.Tcl_GetString( objv[1] ) ); |
||
4229 | if ( slave == null ) |
||
4230 | { |
||
4231 | return TCL.TCL_ERROR; |
||
4232 | } |
||
4233 | |||
4234 | init_all( slave ); |
||
4235 | return TCL.TCL_OK; |
||
4236 | } |
||
4237 | #endif |
||
4238 | |||
4239 | /* |
||
4240 | ** Configure the interpreter passed as the first argument to have access |
||
4241 | ** to the commands and linked variables that make up: |
||
4242 | ** |
||
4243 | ** * the [sqlite3] extension itself, |
||
4244 | ** |
||
4245 | ** * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and |
||
4246 | ** |
||
4247 | ** * If SQLITE_TEST is set, the various test interfaces used by the Tcl |
||
4248 | ** test suite. |
||
4249 | */ |
||
4250 | static void init_all( Tcl_Interp interp ) |
||
4251 | { |
||
4252 | Sqlite3_Init( interp ); |
||
4253 | #if (SQLITE_TEST) || (SQLITE_TCLMD5) |
||
4254 | Md5_Init( interp ); |
||
4255 | #endif |
||
4256 | #if SQLITE_TEST |
||
4257 | //{ |
||
4258 | //extern int Sqliteconfig_Init(Tcl_Interp); |
||
4259 | //extern int Sqlitetest1_Init(Tcl_Interp); |
||
4260 | //extern int Sqlitetest2_Init(Tcl_Interp); |
||
4261 | //extern int Sqlitetest3_Init(Tcl_Interp); |
||
4262 | //extern int Sqlitetest4_Init(Tcl_Interp); |
||
4263 | //extern int Sqlitetest5_Init(Tcl_Interp); |
||
4264 | //extern int Sqlitetest6_Init(Tcl_Interp); |
||
4265 | //extern int Sqlitetest7_Init(Tcl_Interp); |
||
4266 | //extern int Sqlitetest8_Init(Tcl_Interp); |
||
4267 | //extern int Sqlitetest9_Init(Tcl_Interp); |
||
4268 | //extern int Sqlitetestasync_Init(Tcl_Interp); |
||
4269 | //extern int Sqlitetest_autoext_Init(Tcl_Interp); |
||
4270 | //extern int Sqlitetest_demovfs_Init(Tcl_Interp ); |
||
4271 | //extern int Sqlitetest_func_Init(Tcl_Interp); |
||
4272 | //extern int Sqlitetest_hexio_Init(Tcl_Interp); |
||
4273 | //extern int Sqlitetest_malloc_Init(Tcl_Interp); |
||
4274 | //extern int Sqlitetest_mutex_Init(Tcl_Interp); |
||
4275 | //extern int Sqlitetestschema_Init(Tcl_Interp); |
||
4276 | //extern int Sqlitetestsse_Init(Tcl_Interp); |
||
4277 | //extern int Sqlitetesttclvar_Init(Tcl_Interp); |
||
4278 | //extern int SqlitetestThread_Init(Tcl_Interp); |
||
4279 | //extern int SqlitetestOnefile_Init(); |
||
4280 | //extern int SqlitetestOsinst_Init(Tcl_Interp); |
||
4281 | //extern int Sqlitetestbackup_Init(Tcl_Interp); |
||
4282 | //extern int Sqlitetestintarray_Init(Tcl_Interp); |
||
4283 | //extern int Sqlitetestvfs_Init(Tcl_Interp ); |
||
4284 | //extern int SqlitetestStat_Init(Tcl_Interp); |
||
4285 | //extern int Sqlitetestrtree_Init(Tcl_Interp); |
||
4286 | //extern int Sqlitequota_Init(Tcl_Interp); |
||
4287 | //extern int Sqlitemultiplex_Init(Tcl_Interp); |
||
4288 | //extern int SqliteSuperlock_Init(Tcl_Interp); |
||
4289 | //extern int SqlitetestSyscall_Init(Tcl_Interp); |
||
4290 | //extern int Sqlitetestfuzzer_Init(Tcl_Interp); |
||
4291 | //extern int Sqlitetestwholenumber_Init(Tcl_Interp); |
||
4292 | |||
4293 | #if (SQLITE_ENABLE_FTS3) || (SQLITE_ENABLE_FTS4) |
||
4294 | //extern int Sqlitetestfts3_Init(Tcl_Interp interp); |
||
4295 | #endif |
||
4296 | |||
4297 | #if SQLITE_ENABLE_ZIPVFS |
||
4298 | // extern int Zipvfs_Init(Tcl_Interp); |
||
4299 | // Zipvfs_Init(interp); |
||
4300 | #endif |
||
4301 | |||
4302 | Sqliteconfig_Init( interp ); |
||
4303 | Sqlitetest1_Init( interp ); |
||
4304 | Sqlitetest2_Init( interp ); |
||
4305 | Sqlitetest3_Init( interp ); |
||
4306 | |||
4307 | //Threads not implemented under C# |
||
4308 | //Sqlitetest4_Init(interp); |
||
4309 | |||
4310 | //TODO implemented under C# |
||
4311 | //Sqlitetest5_Init(interp); |
||
4312 | |||
4313 | //Simulated Crashtests not implemented under C# |
||
4314 | //Sqlitetest6_Init(interp); |
||
4315 | |||
4316 | //client/server version (Unix Only) not implemented under C# |
||
4317 | //Sqlitetest7_Init(interp); |
||
4318 | |||
4319 | //virtual table interface not implemented under C# |
||
4320 | //Sqlitetest8_Init(interp); |
||
4321 | |||
4322 | Sqlitetest9_Init( interp ); |
||
4323 | |||
4324 | //asynchronous IO extension interface not implemented under C# |
||
4325 | //Sqlitetestasync_Init(interp); |
||
4326 | |||
4327 | //sqlite3_auto_extension() function not implemented under C# |
||
4328 | //Sqlitetest_autoext_Init(interp); |
||
4329 | |||
4330 | //VFS not implemented under C# |
||
4331 | //Sqlitetest_demovfs_Init(interp); |
||
4332 | |||
4333 | Sqlitetest_func_Init( interp ); |
||
4334 | Sqlitetest_hexio_Init( interp ); |
||
4335 | Sqlitetest_malloc_Init( interp ); |
||
4336 | Sqlitetest_mutex_Init( interp ); |
||
4337 | |||
4338 | //virtual table interfaces not implemented under C# |
||
4339 | //Sqlitetestschema_Init(interp); |
||
4340 | |||
4341 | //virtual table interfaces not implemented under C# |
||
4342 | //Sqlitetesttclvar_Init(interp); |
||
4343 | |||
4344 | //Threads not implemented under C# |
||
4345 | //SqlitetestThread_Init(interp); |
||
4346 | |||
4347 | //VFS not implemented under C# |
||
4348 | //SqlitetestOnefile_Init(interp); |
||
4349 | |||
4350 | //VFS not implemented under C# |
||
4351 | //SqlitetestOsinst_Init(interp); |
||
4352 | |||
4353 | Sqlitetestbackup_Init( interp ); |
||
4354 | |||
4355 | //virtual table interfaces not implemented under C# |
||
4356 | //Sqlitetestintarray_Init(interp); |
||
4357 | |||
4358 | //VFS not implemented under C# |
||
4359 | //Sqlitetestvfs_Init(interp); |
||
4360 | |||
4361 | //virtual table interfaces not implemented under C# |
||
4362 | //SqlitetestStat_Init(interp); |
||
4363 | //Sqlitetestrtree_Init( interp ); |
||
4364 | //Sqlitequota_Init( interp ); |
||
4365 | //Sqlitemultiplex_Init( interp ); |
||
4366 | //SqliteSuperlock_Init( interp ); |
||
4367 | //SqlitetestSyscall_Init( interp ); |
||
4368 | Sqlitetestfuzzer_Init( interp ); |
||
4369 | //Sqlitetestwholenumber_Init( interp ); |
||
4370 | |||
4371 | #if (SQLITE_ENABLE_FTS3) || (SQLITE_ENABLE_FTS4) |
||
4372 | //Sqlitetestfts3_Init(interp); |
||
4373 | #endif |
||
4374 | TCL.Tcl_CreateObjCommand( interp, "load_testfixture_extensions", init_all_cmd, 0, null ); |
||
4375 | |||
4376 | #if SQLITE_SSE |
||
4377 | Sqlitetestsse_Init(interp); |
||
4378 | #endif |
||
4379 | } |
||
4380 | #endif |
||
4381 | } |
||
4382 | |||
4383 | #if FALSE |
||
4384 | //#define TCLSH_MAIN main /* Needed to fake out mktclapp */ |
||
4385 | int TCLSH_MAIN(int argc, char **argv){ |
||
4386 | Tcl_Interp interp; |
||
4387 | |||
4388 | /* Call sqlite3_shutdown() once before doing anything else. This is to |
||
4389 | ** test that sqlite3_shutdown() can be safely called by a process before |
||
4390 | ** sqlite3_initialize() is. */ |
||
4391 | sqlite3_shutdown(); |
||
4392 | |||
4393 | #if TCLSH//TCLSH==2 |
||
4394 | sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); |
||
4395 | #endif |
||
4396 | Tcl_FindExecutable(argv[0]); |
||
4397 | |||
4398 | interp = TCL.Tcl_CreateInterp(); |
||
4399 | init_all(interp); |
||
4400 | if( argc>=2 ){ |
||
4401 | int i; |
||
4402 | char zArgc[32]; |
||
4403 | sqlite3_snprintf(sizeof(zArgc), zArgc, "%d", argc-(3-TCLSH)); |
||
4404 | Tcl_SetVar(interp,"argc", zArgc, TCL_GLOBAL_ONLY); |
||
4405 | Tcl_SetVar(interp,"argv0",argv[1],TCL_GLOBAL_ONLY); |
||
4406 | Tcl_SetVar(interp,"argv", "", TCL_GLOBAL_ONLY); |
||
4407 | for(i=3-TCLSH; i<argc; i++){ |
||
4408 | Tcl_SetVar(interp, "argv", argv[i], |
||
4409 | TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT | TCL_APPEND_VALUE); |
||
4410 | } |
||
4411 | if( TCLSH==1 && Tcl_EvalFile(interp, argv[1])!=TCL_OK ){ |
||
4412 | string zInfo = TCL.Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY); |
||
4413 | if( zInfo==0 ) zInfo = TCL.Tcl_GetStringResult(interp); |
||
4414 | fprintf(stderr,"%s: %s\n", *argv, zInfo); |
||
4415 | return 1; |
||
4416 | } |
||
4417 | } |
||
4418 | if( TCLSH==2 || argc<=1 ){ |
||
4419 | Tcl_GlobalEval(interp, zMainloop); |
||
4420 | } |
||
4421 | return 0; |
||
4422 | } |
||
4423 | #endif |
||
4424 | #endif // * TCLSH */ |
||
4425 | #endif // NO_TCL |
||
4426 | } |
||
4427 |