wasCSharpSQLite – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Diagnostics; |
||
3 | using System.IO; |
||
4 | using System.Runtime.InteropServices; |
||
5 | using System.Text; |
||
6 | |||
7 | using FILE = System.IO.TextWriter; |
||
8 | using GETPROCTIMES = System.IntPtr; |
||
9 | using HANDLE = System.IntPtr; |
||
10 | using HINSTANCE = System.IntPtr; |
||
11 | using sqlite3_int64 = System.Int64; |
||
12 | using u32 = System.UInt32; |
||
13 | using va_list = System.Object; |
||
14 | |||
15 | namespace Community.CsharpSqlite |
||
16 | { |
||
17 | using dxCallback = Sqlite3.dxCallback; |
||
18 | using FILETIME = Sqlite3.FILETIME; |
||
19 | using sqlite3 = Sqlite3.sqlite3; |
||
20 | using sqlite3_stmt = Sqlite3.Vdbe; |
||
21 | using sqlite3_value = Sqlite3.Mem; |
||
22 | |||
23 | |||
24 | class Shell |
||
25 | { |
||
26 | |||
27 | /* |
||
28 | ** 2001 September 15 |
||
29 | ** |
||
30 | ** The author disclaims copyright to this source code. In place of |
||
31 | ** a legal notice, here is a blessing: |
||
32 | ** |
||
33 | ** May you do good and not evil. |
||
34 | ** May you find forgiveness for yourself and forgive others. |
||
35 | ** May you share freely, never taking more than you give. |
||
36 | ** |
||
37 | ************************************************************************* |
||
38 | ** This file contains code to implement the "sqlite" command line |
||
39 | ** utility for accessing SQLite databases. |
||
40 | ************************************************************************* |
||
41 | ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart |
||
42 | ** C#-SQLite is an independent reimplementation of the SQLite software library |
||
43 | ** |
||
44 | ************************************************************************* |
||
45 | */ |
||
46 | //#if defined(_WIN32) || defined(WIN32) |
||
47 | ///* This needs to come before any includes for MSVC compiler */ |
||
48 | //#define _CRT_SECURE_NO_WARNINGS |
||
49 | //#endif |
||
50 | |||
51 | //#include <stdlib.h> |
||
52 | //#include <string.h> |
||
53 | //#include <stdio.h> |
||
54 | //#include <Debug.Assert.h> |
||
55 | //#include "sqlite3.h" |
||
56 | //#include <ctype.h> |
||
57 | //#include <stdarg.h> |
||
58 | |||
59 | //#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) |
||
60 | //# include <signal.h> |
||
61 | //# if !defined(__RTP__) && !defined(_WRS_KERNEL) |
||
62 | //# include <pwd.h> |
||
63 | //# endif |
||
64 | //# include <unistd.h> |
||
65 | //# include <sys/types.h> |
||
66 | //#endif |
||
67 | |||
68 | //#if __OS2__ |
||
69 | //# include <unistd.h> |
||
70 | //#endif |
||
71 | |||
72 | //#if HAVE_EDITLINE |
||
73 | //# include <editline/editline.h> |
||
74 | //#endif |
||
75 | //#if defined(HAVE_READLINE) && HAVE_READLINE==1 |
||
76 | //# include <readline/readline.h> |
||
77 | //# include <readline/history.h> |
||
78 | //#endif |
||
79 | #if !(HAVE_EDITLINE) //&& (!(HAVE_READLINE) || HAVE_READLINE!=1) |
||
80 | //# define readline(p) local_getline(p,stdin) |
||
81 | static string readline(string p) |
||
82 | { |
||
83 | return local_getline(p, stdin); |
||
84 | } |
||
85 | //# define add_history(X) |
||
86 | static void add_history(object p) { } |
||
87 | //# define read_history(X) |
||
88 | static void read_history(object p) { } |
||
89 | //# define write_history(X) |
||
90 | static void write_history(object p) { } |
||
91 | //# define stifle_history(X) |
||
92 | static void stifle_history(object p) { } |
||
93 | #endif |
||
94 | |||
95 | #if (_WIN32) || (WIN32) |
||
96 | //# include <io.h> |
||
97 | //#define isatty(h) _isatty(h) |
||
98 | static bool isatty(object h) { return stdin.Equals(Console.In); } |
||
99 | //#define access(f,m) _access((f),(m)) |
||
100 | #else |
||
101 | /* Make sure isatty() has a prototype. |
||
102 | */ |
||
103 | extern int isatty(); |
||
104 | #endif |
||
105 | |||
106 | //#if defined(_WIN32_WCE) |
||
107 | ///* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() |
||
108 | // * thus we always assume that we have a console. That can be |
||
109 | // * overridden with the -batch command line option. |
||
110 | // */ |
||
111 | //#define isatty(x) 1 |
||
112 | //#endif |
||
113 | |||
114 | /* True if the timer is enabled */ |
||
115 | static bool enableTimer = false; |
||
116 | |||
117 | #if FALSE//!defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL) |
||
118 | //#include <sys/time.h> |
||
119 | //#include <sys/resource.h> |
||
120 | |||
121 | ///* Saved resource information for the beginning of an operation */ |
||
122 | //static struct rusage sBegin; |
||
123 | |||
124 | ///* |
||
125 | //** Begin timing an operation |
||
126 | //*/ |
||
127 | //static void beginTimer(){ |
||
128 | // if( enableTimer ){ |
||
129 | // getrusage(RUSAGE_SELF, sBegin); |
||
130 | // } |
||
131 | //} |
||
132 | |||
133 | ///* Return the difference of two time_structs in seconds */ |
||
134 | //static double timeDiff(timeval pStart, struct timeval pEnd){ |
||
135 | // return (pEnd.tv_usec - pStart.tv_usec)*0.000001 + |
||
136 | // (double)(pEnd.tv_sec - pStart.tv_sec); |
||
137 | //} |
||
138 | |||
139 | ///* |
||
140 | //** Print the timing results. |
||
141 | //*/ |
||
142 | //static void endTimer(){ |
||
143 | // if( enableTimer ){ |
||
144 | // struct rusage sEnd; |
||
145 | // getrusage(RUSAGE_SELF, sEnd); |
||
146 | // printf("CPU Time: user %f sys %f\n", |
||
147 | // timeDiff(sBegin.ru_utime, sEnd.ru_utime), |
||
148 | // timeDiff(sBegin.ru_stime, sEnd.ru_stime)); |
||
149 | // } |
||
150 | //} |
||
151 | |||
152 | //#define BEGIN_TIMER beginTimer() |
||
153 | //#define END_TIMER endTimer() |
||
154 | //#define HAS_TIMER 1 |
||
155 | |||
156 | #elif ((_WIN32) || (WIN32)) |
||
157 | |||
158 | //#include <windows.h> |
||
159 | |||
160 | /* Saved resource information for the beginning of an operation */ |
||
161 | static Process hProcess; |
||
162 | //static FILETIME ftKernelBegin; |
||
163 | //static FILETIME ftUserBegin; |
||
164 | static TimeSpan tsUserBegin; |
||
165 | static TimeSpan tsKernelBegin; |
||
166 | |||
167 | //typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME); |
||
168 | |||
169 | /* |
||
170 | ** Check to see if we have timer support. Return 1 if necessary |
||
171 | ** support found (or found previously). |
||
172 | */ |
||
173 | //static bool has_timer() |
||
174 | //{ |
||
175 | // if (getProcessTimesAddr != IntPtr.Zero) |
||
176 | // { |
||
177 | // return true; |
||
178 | // } |
||
179 | // else |
||
180 | // { |
||
181 | // /* GetProcessTimes() isn't supported in WIN95 and some other Windows versions. |
||
182 | // ** See if the version we are running on has it, and if it does, save off |
||
183 | // ** a pointer to it and the current process handle. |
||
184 | // */ |
||
185 | // hProcess = Process.GetCurrentProcess(); |
||
186 | // if (hProcess != null) |
||
187 | // { |
||
188 | // HINSTANCE hinstLib = LoadLibrary("Kernel32.dll"); |
||
189 | // if (null != hinstLib) |
||
190 | // { |
||
191 | // getProcessTimesAddr = (GETPROCTIMES)GetProcAddress(hinstLib, "GetProcessTimes"); |
||
192 | // if (null != getProcessTimesAddr) |
||
193 | // { |
||
194 | // return true; |
||
195 | // } |
||
196 | // FreeLibrary(hinstLib); |
||
197 | // } |
||
198 | // } |
||
199 | // } |
||
200 | // return true; |
||
201 | //} |
||
202 | |||
203 | /* |
||
204 | ** Begin timing an operation |
||
205 | */ |
||
206 | static void beginTimer() |
||
207 | { |
||
208 | if (enableTimer)//&& getProcessTimesAddr != IntPtr.Zero) |
||
209 | { |
||
210 | //FILETIME ftCreation, ftExit; |
||
211 | //getProcessTimesAddr(hProcess, ftCreation, ftExit, ftKernelBegin, ftUserBegin); |
||
212 | tsUserBegin = Process.GetCurrentProcess().UserProcessorTime; |
||
213 | tsKernelBegin = Process.GetCurrentProcess().TotalProcessorTime - Process.GetCurrentProcess().UserProcessorTime; |
||
214 | } |
||
215 | } |
||
216 | |||
217 | /* Return the difference of two TimeSpan structs in seconds */ |
||
218 | static double timeDiff(TimeSpan pStart, TimeSpan pEnd) |
||
219 | { |
||
220 | //sqlite3_int64 i64Start = ((sqlite3_int64)pStart.dwLowDateTime); |
||
221 | //sqlite3_int64 i64End = ((sqlite3_int64)pEnd.dwLowDateTime); |
||
222 | //return (double)((i64End - i64Start) / 10000000.0); |
||
223 | return timeDiff(pStart, pEnd) / 10000000.0; |
||
224 | } |
||
225 | |||
226 | /* |
||
227 | ** Print the timing results. |
||
228 | */ |
||
229 | static void endTimer() |
||
230 | { |
||
231 | if (enableTimer)// && getProcessTimesAddr != IntPtr.Zero) |
||
232 | { |
||
233 | //FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
||
234 | //getProcessTimesAddr(hProcess, ftCreation, ftExit, ftKernelEnd, ftUserEnd); |
||
235 | TimeSpan tsKernelEnd, tsUserEnd; |
||
236 | tsUserEnd = Process.GetCurrentProcess().UserProcessorTime; |
||
237 | tsKernelEnd = Process.GetCurrentProcess().TotalProcessorTime - Process.GetCurrentProcess().UserProcessorTime; |
||
238 | |||
239 | printf("CPU Time: user %f sys %f\n", |
||
240 | timeDiff(tsUserBegin, tsUserEnd), |
||
241 | timeDiff(tsKernelBegin, tsKernelEnd)); |
||
242 | } |
||
243 | } |
||
244 | |||
245 | //#define BEGIN_TIMER beginTimer() |
||
246 | //#define END_TIMER endTimer() |
||
247 | //#define HAS_TIMER HAS_TIMER |
||
248 | static bool HAS_TIMER = true; |
||
249 | #else |
||
250 | //#define BEGIN_TIMER |
||
251 | //#define END_TIMER |
||
252 | //#define HAS_TIMER 0 |
||
253 | #endif |
||
254 | |||
255 | /* |
||
256 | ** Used to prevent warnings about unused parameters |
||
257 | */ |
||
258 | //#define UNUSED_PARAMETER(x) ()(x) |
||
259 | static void UNUSED_PARAMETER<T>(T x) { } |
||
260 | |||
261 | /* |
||
262 | ** If the following flag is set, then command execution stops |
||
263 | ** at an error if we are not interactive. |
||
264 | */ |
||
265 | static bool bail_on_error = false; |
||
266 | |||
267 | /* |
||
268 | ** Threat stdin as an interactive input if the following variable |
||
269 | ** is true. Otherwise, assume stdin is connected to a file or pipe. |
||
270 | */ |
||
271 | static bool stdin_is_interactive = true; |
||
272 | |||
273 | /* |
||
274 | ** The following is the open SQLite database. We make a pointer |
||
275 | ** to this database a static variable so that it can be accessed |
||
276 | ** by the SIGINT handler to interrupt database processing. |
||
277 | */ |
||
278 | static sqlite3 db = null; |
||
279 | |||
280 | /* |
||
281 | ** True if an interrupt (Control-C) has been received. |
||
282 | */ |
||
283 | static bool seenInterrupt = false; |
||
284 | |||
285 | /* |
||
286 | ** This is the name of our program. It is set in main(), used |
||
287 | ** in a number of other places, mostly for error messages. |
||
288 | */ |
||
289 | static string Argv0; |
||
290 | |||
291 | /* |
||
292 | ** Prompt strings. Initialized in main. Settable with |
||
293 | ** .prompt main continue |
||
294 | */ |
||
295 | static string mainPrompt; /* First line prompt. default: "sqlite> "*/ |
||
296 | static string continuePrompt; /* Continuation prompt. default: " ...> " */ |
||
297 | |||
298 | /* |
||
299 | ** Write I/O traces to the following stream. |
||
300 | */ |
||
301 | #if SQLITE_ENABLE_IOTRACE |
||
302 | static FILE iotrace = null; |
||
303 | #endif |
||
304 | |||
305 | /* |
||
306 | ** This routine works like printf in that its first argument is a |
||
307 | ** format string and subsequent arguments are values to be substituted |
||
308 | ** in place of % fields. The result of formatting this string |
||
309 | ** is written to iotrace. |
||
310 | */ |
||
311 | #if SQLITE_ENABLE_IOTRACE |
||
312 | static void iotracePrintf(string zFormat, ...){ |
||
313 | va_list ap; |
||
314 | string z; |
||
315 | if( iotrace== null ) return; |
||
316 | va_start(ap, zFormat); |
||
317 | z = Sqlite3.SQLITE_vmprintf(zFormat, ap); |
||
318 | va_end(ap); |
||
319 | fprintf(iotrace, "%s", z); |
||
320 | Sqlite3.sqlite3_free(z); |
||
321 | } |
||
322 | #endif |
||
323 | |||
324 | |||
325 | /* |
||
326 | ** Determines if a string is a number of not. |
||
327 | */ |
||
328 | //static int isNumber(string z, ref int realnum){ |
||
329 | // if( *z=='-' || *z=='+' ) z++; |
||
330 | // if( !isdigit(*z) ){ |
||
331 | // return 0; |
||
332 | // } |
||
333 | // z++; |
||
334 | // //if( realnum ) *realnum = 0; |
||
335 | // realnum = 0; |
||
336 | // while( isdigit(*z) ){ z++; } |
||
337 | // if( *z=='.' ){ |
||
338 | // z++; |
||
339 | // if( !isdigit(*z) ) return 0; |
||
340 | // while( isdigit(*z) ){ z++; } |
||
341 | // //if( realnum ) *realnum = 1; |
||
342 | // realnum = 1; |
||
343 | // } |
||
344 | // if( *z=='e' || *z=='E' ){ |
||
345 | // z++; |
||
346 | // if( *z=='+' || *z=='-' ) z++; |
||
347 | // if( !isdigit(*z) ) return 0; |
||
348 | // while( isdigit(*z) ){ z++; } |
||
349 | // //if( realnum ) *realnum = 1; |
||
350 | // realnum = 1; |
||
351 | // } |
||
352 | // return *z== null; |
||
353 | //} |
||
354 | static bool isNumber(string z) |
||
355 | { |
||
356 | int i = 0; |
||
357 | return isNumber(z, ref i); |
||
358 | } |
||
359 | static bool isNumber(string z, int i) |
||
360 | { |
||
361 | return isNumber(z, ref i); |
||
362 | } |
||
363 | static bool isNumber(string z, ref int realnum) |
||
364 | { |
||
365 | int zIdx = 0; |
||
366 | if (z[zIdx] == '-' || z[zIdx] == '+') |
||
367 | zIdx++; |
||
368 | if (zIdx == z.Length || !isdigit(z[zIdx])) |
||
369 | { |
||
370 | return false; |
||
371 | } |
||
372 | zIdx++; |
||
373 | realnum = 0; |
||
374 | while (zIdx < z.Length && isdigit(z[zIdx])) |
||
375 | { |
||
376 | zIdx++; |
||
377 | } |
||
378 | if (z[zIdx] == '.') |
||
379 | { |
||
380 | zIdx++; |
||
381 | if (zIdx < z.Length && !isdigit(z[zIdx])) |
||
382 | return false; |
||
383 | while (zIdx < z.Length && isdigit(z[zIdx])) |
||
384 | { |
||
385 | zIdx++; |
||
386 | } |
||
387 | realnum = 1; |
||
388 | } |
||
389 | if (z[zIdx] == 'e' || z[zIdx] == 'E') |
||
390 | { |
||
391 | zIdx++; |
||
392 | if (zIdx < z.Length && (z[zIdx] == '+' || z[zIdx] == '-')) |
||
393 | zIdx++; |
||
394 | if (zIdx == z.Length || !isdigit(z[zIdx])) |
||
395 | return false; |
||
396 | while (zIdx < z.Length && isdigit(z[zIdx])) |
||
397 | { |
||
398 | zIdx++; |
||
399 | } |
||
400 | realnum = 1; |
||
401 | } |
||
402 | return zIdx == z.Length; |
||
403 | } |
||
404 | |||
405 | |||
406 | /* |
||
407 | ** A global char* and an SQL function to access its current value |
||
408 | ** from within an SQL statement. This program used to use the |
||
409 | ** Sqlite3.sqlite3_exec_printf() API to substitue a string into an SQL statement. |
||
410 | ** The correct way to do this with sqlite3 is to use the bind API, but |
||
411 | ** since the shell is built around the callback paradigm it would be a lot |
||
412 | ** of work. Instead just use this hack, which is quite harmless. |
||
413 | */ |
||
414 | static string zShellStatic = ""; |
||
415 | static void shellstaticFunc( |
||
416 | Sqlite3.sqlite3_context context, |
||
417 | int argc, |
||
418 | sqlite3_value[] argv |
||
419 | ) |
||
420 | { |
||
421 | Debug.Assert(0 == argc); |
||
422 | Debug.Assert(String.IsNullOrEmpty(zShellStatic)); |
||
423 | UNUSED_PARAMETER(argc); |
||
424 | UNUSED_PARAMETER(argv); |
||
425 | Sqlite3.sqlite3_result_text(context, zShellStatic, -1, Sqlite3.SQLITE_STATIC); |
||
426 | } |
||
427 | |||
428 | |||
429 | /* |
||
430 | ** This routine reads a line of text from FILE in, stores |
||
431 | ** the text in memory obtained from malloc() and returns a pointer |
||
432 | ** to the text. null is returned at end of file, or if malloc() |
||
433 | ** fails. |
||
434 | ** |
||
435 | ** The interface is like "readline" but no command-line editing |
||
436 | ** is done. |
||
437 | */ |
||
438 | static string local_getline(string zPrompt, TextReader In) |
||
439 | { |
||
440 | StringBuilder zIn = new StringBuilder(); |
||
441 | StringBuilder zLine; |
||
442 | int nLine; |
||
443 | int n; |
||
444 | bool eol; |
||
445 | |||
446 | if (zPrompt != null) |
||
447 | { |
||
448 | printf("%s", zPrompt); |
||
449 | fflush(stdout); |
||
450 | } |
||
451 | nLine = 100; |
||
452 | zLine = new StringBuilder(nLine);//malloc( nLine ); |
||
453 | //if( zLine== null ) return 0; |
||
454 | n = 0; |
||
455 | eol = false; |
||
456 | while (!eol) |
||
457 | { |
||
458 | if (n + 100 > nLine) |
||
459 | { |
||
460 | nLine = nLine * 2 + 100; |
||
461 | zLine.Capacity = (nLine);//= realloc(zLine, nLine); |
||
462 | //if (zLine == null) |
||
463 | // return null; |
||
464 | } |
||
465 | if (fgets(zIn, nLine - n, In) == 0) |
||
466 | { |
||
467 | if (zLine.Length == 0) |
||
468 | { |
||
469 | zLine = null;//free(zLine); |
||
470 | return null; |
||
471 | } |
||
472 | //zLine[n] = 0; |
||
473 | eol = true; |
||
474 | break; |
||
475 | } |
||
476 | n = 0; |
||
477 | while (n < zLine.Length && zLine[n] != '\0') { n++; } |
||
478 | n = zIn.Length - 1; |
||
479 | if (zIn[n] == '\n') |
||
480 | { |
||
481 | n--; |
||
482 | if (n > 0 && zIn[n - 1] == '\r') |
||
483 | n--; |
||
484 | |||
485 | zIn.Length = n + 1; |
||
486 | eol = true; |
||
487 | } |
||
488 | zLine.Append(zIn); |
||
489 | } |
||
490 | //zLine = realloc( zLine, n+1 ); |
||
491 | return zLine.ToString(); |
||
492 | } |
||
493 | |||
494 | /* |
||
495 | ** Retrieve a single line of input text. |
||
496 | ** |
||
497 | ** zPrior is a string of prior text retrieved. If not the empty |
||
498 | ** string, then issue a continuation prompt. |
||
499 | */ |
||
500 | static string one_input_line(string zPrior, TextReader In) |
||
501 | { |
||
502 | string zPrompt; |
||
503 | string zResult; |
||
504 | if (In != null) |
||
505 | { |
||
506 | return local_getline("", In).ToString(); |
||
507 | } |
||
508 | if (zPrior != null && zPrior.Length > 0) |
||
509 | { |
||
510 | zPrompt = continuePrompt; |
||
511 | } |
||
512 | else |
||
513 | { |
||
514 | zPrompt = mainPrompt; |
||
515 | } |
||
516 | zResult = readline(zPrompt); |
||
517 | #if (HAVE_READLINE) //&& HAVE_READLINE==1 |
||
518 | if( zResult && *zResult ) add_history(zResult); |
||
519 | #endif |
||
520 | return zResult; |
||
521 | } |
||
522 | |||
523 | class previous_mode_data |
||
524 | { |
||
525 | public bool valid; /* Is there legit data in here? */ |
||
526 | public int mode; |
||
527 | public bool showHeader; |
||
528 | public int[] colWidth = new int[200]; |
||
529 | }; |
||
530 | |||
531 | /* |
||
532 | ** An pointer to an instance of this structure is passed from |
||
533 | ** the main program to the callback. This is used to communicate |
||
534 | ** state and mode information. |
||
535 | */ |
||
536 | class callback_data |
||
537 | { |
||
538 | public sqlite3 db; /* The database */ |
||
539 | public bool echoOn; /* True to echo input commands */ |
||
540 | public bool statsOn; /* True to display memory stats before each finalize */ |
||
541 | public int cnt; /* Number of records displayed so far */ |
||
542 | public FILE Out; /* Write results here */ |
||
543 | public int mode; /* An output mode setting */ |
||
544 | public bool writableSchema; /* True if PRAGMA writable_schema=ON */ |
||
545 | public bool showHeader; /* True to show column names in List or Column mode */ |
||
546 | public string zDestTable; /* Name of destination table when MODE_Insert */ |
||
547 | public string separator = ""; /* Separator character for MODE_List */ |
||
548 | public int[] colWidth = new int[200]; /* Requested width of each column when in column mode*/ |
||
549 | public int[] actualWidth = new int[200]; /* Actual width of each column */ |
||
550 | public string nullvalue = "NULL"; /* The text to print when a null comes back from |
||
551 | ** the database */ |
||
552 | public previous_mode_data explainPrev = new previous_mode_data(); |
||
553 | /* Holds the mode information just before |
||
554 | ** .explain ON */ |
||
555 | public StringBuilder outfile = new StringBuilder(260); /* Filename for Out */ |
||
556 | public string zDbFilename; /* name of the database file */ |
||
557 | public string zVfs; /* Name of VFS to use */ |
||
558 | public sqlite3_stmt pStmt; /* Current statement if any. */ |
||
559 | public FILE pLog; /* Write log output here */ |
||
560 | |||
561 | internal callback_data Copy() |
||
562 | { |
||
563 | return (callback_data)this.MemberwiseClone(); |
||
564 | } |
||
565 | }; |
||
566 | // Store callback data variant |
||
567 | class callback_data_extra |
||
568 | { |
||
569 | public string[] azCols; //(string *)pData; /* Names of result columns */ |
||
570 | public string[] azVals;//azCols[nCol]; /* Results */ |
||
571 | public int[] aiTypes; //(int *)&azVals[nCol]; /* Result types */ |
||
572 | } |
||
573 | /* |
||
574 | ** These are the allowed modes. |
||
575 | */ |
||
576 | //#define MODE_Line 0 /* One column per line. Blank line between records */ |
||
577 | //#define MODE_Column 1 /* One record per line in neat columns */ |
||
578 | //#define MODE_List 2 /* One record per line with a separator */ |
||
579 | //#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ |
||
580 | //#define MODE_Html 4 /* Generate an XHTML table */ |
||
581 | //#define MODE_Insert 5 /* Generate SQL "insert" statements */ |
||
582 | //#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ |
||
583 | //#define MODE_Csv 7 /* Quote strings, numbers are plain */ |
||
584 | //#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ |
||
585 | const int MODE_Line = 0; |
||
586 | const int MODE_Column = 1; |
||
587 | const int MODE_List = 2; |
||
588 | const int MODE_Semi = 3; |
||
589 | const int MODE_Html = 4; |
||
590 | const int MODE_Insert = 5; |
||
591 | const int MODE_Tcl = 6; |
||
592 | const int MODE_Csv = 7; |
||
593 | const int MODE_Explain = 8; |
||
594 | |||
595 | static string[] modeDescr = new string[] { |
||
596 | "line", |
||
597 | "column", |
||
598 | "list", |
||
599 | "semi", |
||
600 | "html", |
||
601 | "insert", |
||
602 | "tcl", |
||
603 | "csv", |
||
604 | "explain", |
||
605 | }; |
||
606 | |||
607 | /* |
||
608 | ** Number of elements in an array |
||
609 | */ |
||
610 | //#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
||
611 | static int ArraySize<T>(T[] X) { return X.Length; } |
||
612 | |||
613 | /* |
||
614 | ** Compute a string length that is limited to what can be stored in |
||
615 | ** lower 30 bits of a 32-bit signed integer. |
||
616 | */ |
||
617 | static int strlen30(StringBuilder z) |
||
618 | { |
||
619 | //string z2 = z; |
||
620 | //while( *z2 ){ z2++; } |
||
621 | return 0x3fffffff & z.Length;//(int)(z2 - z); |
||
622 | } |
||
623 | static int strlen30(string z) |
||
624 | { |
||
625 | //string z2 = z; |
||
626 | //while( *z2 ){ z2++; } |
||
627 | return 0x3fffffff & z.Length;//(int)(z2 - z); |
||
628 | } |
||
629 | |||
630 | |||
631 | /* |
||
632 | ** A callback for the Sqlite3.SQLITE_log() interface. |
||
633 | */ |
||
634 | static void shellLog(object pArg, int iErrCode, string zMsg) |
||
635 | { |
||
636 | callback_data p = (callback_data)pArg; |
||
637 | if (p.pLog == null) |
||
638 | return; |
||
639 | fprintf(p.pLog, "(%d) %s\n", iErrCode, zMsg); |
||
640 | fflush(p.pLog); |
||
641 | } |
||
642 | |||
643 | /* |
||
644 | ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
||
645 | */ |
||
646 | static void output_hex_blob(FILE Out, byte[] pBlob, int nBlob) |
||
647 | { |
||
648 | int i; |
||
649 | //string zBlob = (string )pBlob; |
||
650 | fprintf(Out, "X'"); |
||
651 | for (i = 0; i < nBlob; i++) { fprintf(Out, "%02x", pBlob[i]); } |
||
652 | fprintf(Out, "'"); |
||
653 | } |
||
654 | |||
655 | /* |
||
656 | ** Output the given string as a quoted string using SQL quoting conventions. |
||
657 | */ |
||
658 | static void output_quoted_string(TextWriter Out, string z) |
||
659 | { |
||
660 | int i; |
||
661 | int nSingle = 0; |
||
662 | for (i = 0; z[i] != '\0'; i++) |
||
663 | { |
||
664 | if (z[i] == '\'') |
||
665 | nSingle++; |
||
666 | } |
||
667 | if (nSingle == 0) |
||
668 | { |
||
669 | fprintf(Out, "'%s'", z); |
||
670 | } |
||
671 | else |
||
672 | { |
||
673 | fprintf(Out, "'"); |
||
674 | while (z != "") |
||
675 | { |
||
676 | for (i = 0; i < z.Length && z[i] != '\''; i++) |
||
677 | { |
||
678 | } |
||
679 | if (i == 0) |
||
680 | { |
||
681 | fprintf(Out, "''"); |
||
682 | //z++; |
||
683 | } |
||
684 | else if (z[i] == '\'') |
||
685 | { |
||
686 | fprintf(Out, "%.*s''", i, z); |
||
687 | //z += i + 1; |
||
688 | } |
||
689 | else |
||
690 | { |
||
691 | fprintf(Out, "%s", z); |
||
692 | break; |
||
693 | } |
||
694 | } |
||
695 | fprintf(Out, "'"); |
||
696 | } |
||
697 | } |
||
698 | |||
699 | /* |
||
700 | ** Output the given string as a quoted according to C or TCL quoting rules. |
||
701 | */ |
||
702 | static void output_c_string(TextWriter Out, string z) |
||
703 | { |
||
704 | char c; |
||
705 | fputc('"', Out); |
||
706 | int zIdx = 0; |
||
707 | while (zIdx < z.Length && (c = z[zIdx++]) != '\0') |
||
708 | { |
||
709 | if (c == '\\') |
||
710 | { |
||
711 | fputc(c, Out); |
||
712 | fputc(c, Out); |
||
713 | } |
||
714 | else if (c == '\t') |
||
715 | { |
||
716 | fputc('\\', Out); |
||
717 | fputc('t', Out); |
||
718 | } |
||
719 | else if (c == '\n') |
||
720 | { |
||
721 | fputc('\\', Out); |
||
722 | fputc('n', Out); |
||
723 | } |
||
724 | else if (c == '\r') |
||
725 | { |
||
726 | fputc('\\', Out); |
||
727 | fputc('r', Out); |
||
728 | } |
||
729 | else if (!isprint(c)) |
||
730 | { |
||
731 | fprintf(Out, "\\%03o", c & 0xff); |
||
732 | } |
||
733 | else |
||
734 | { |
||
735 | fputc(c, Out); |
||
736 | } |
||
737 | } |
||
738 | fputc('"', Out); |
||
739 | } |
||
740 | |||
741 | |||
742 | /* |
||
743 | ** Output the given string with characters that are special to |
||
744 | ** HTML escaped. |
||
745 | */ |
||
746 | static void output_html_string(TextWriter Out, string z) |
||
747 | { |
||
748 | int i; |
||
749 | while (z != "") |
||
750 | { |
||
751 | for (i = 0; i < z.Length && z[i] != '<' && z[i] != '&'; i++) |
||
752 | { |
||
753 | } |
||
754 | if (i > 0) |
||
755 | { |
||
756 | fprintf(Out, "%.*s", i, z); |
||
757 | } |
||
758 | if (i < z.Length && z[i] == '<') |
||
759 | { |
||
760 | fprintf(Out, "<"); |
||
761 | } |
||
762 | else if (i < z.Length && z[i] == '&') |
||
763 | { |
||
764 | fprintf(Out, "&"); |
||
765 | } |
||
766 | else if (i < z.Length && z[i] == '\"') |
||
767 | { |
||
768 | fprintf(Out, """); |
||
769 | } |
||
770 | else if (i < z.Length && z[i] == '\'') |
||
771 | { |
||
772 | fprintf(Out, "'"); |
||
773 | } |
||
774 | else |
||
775 | { |
||
776 | break; |
||
777 | } |
||
778 | z += i + 1; |
||
779 | } |
||
780 | } |
||
781 | |||
782 | /* |
||
783 | ** If a field contains any character identified by a 1 in the following |
||
784 | ** array, then the string must be quoted for CSV. |
||
785 | */ |
||
786 | static byte[] needCsvQuote = new byte[] { |
||
787 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
788 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
789 | 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
||
790 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||
791 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||
792 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||
793 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
||
794 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
||
795 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
796 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
797 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
798 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
799 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
800 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
801 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
802 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
||
803 | }; |
||
804 | |||
805 | /* |
||
806 | ** Output a single term of CSV. Actually, p.separator is used for |
||
807 | ** the separator, which may or may not be a comma. p.nullvalue is |
||
808 | ** the null value. Strings are quoted using ANSI-C rules. Numbers |
||
809 | ** appear outside of quotes. |
||
810 | */ |
||
811 | static void output_csv(callback_data p, string z, bool bSep) |
||
812 | { |
||
813 | TextWriter Out = p.Out; |
||
814 | if (z == null) |
||
815 | { |
||
816 | fprintf(Out, "%s", p.nullvalue); |
||
817 | } |
||
818 | else |
||
819 | { |
||
820 | int i; |
||
821 | int nSep = strlen30(p.separator); |
||
822 | for (i = 0; i < z.Length; i++) |
||
823 | { |
||
824 | if (needCsvQuote[z[i]] != 0 |
||
825 | || (z[i] == p.separator[0] && |
||
826 | (nSep == 1 || z == p.separator))) |
||
827 | { |
||
828 | i = 0; |
||
829 | break; |
||
830 | } |
||
831 | } |
||
832 | if (i == 0) |
||
833 | { |
||
834 | putc('"', Out); |
||
835 | for (i = 0; i < z.Length; i++) |
||
836 | { |
||
837 | if (z[i] == '"') |
||
838 | putc('"', Out); |
||
839 | putc(z[i], Out); |
||
840 | } |
||
841 | putc('"', Out); |
||
842 | } |
||
843 | else |
||
844 | { |
||
845 | fprintf(Out, "%s", z); |
||
846 | } |
||
847 | } |
||
848 | if (bSep) |
||
849 | { |
||
850 | fprintf(p.Out, "%s", p.separator); |
||
851 | } |
||
852 | } |
||
853 | |||
854 | |||
855 | #if SIGINT |
||
856 | /* |
||
857 | ** This routine runs when the user presses Ctrl-C |
||
858 | */ |
||
859 | static void interrupt_handler(int NotUsed){ |
||
860 | UNUSED_PARAMETER(NotUsed); |
||
861 | seenInterrupt = 1; |
||
862 | if( db ) Sqlite3.SQLITE_interrupt(db); |
||
863 | } |
||
864 | #endif |
||
865 | |||
866 | /* |
||
867 | ** This is the callback routine that the shell |
||
868 | ** invokes for each row of a query result. |
||
869 | */ |
||
870 | static int shell_callback(object pArg, sqlite3_int64 nArg, object p2, object p3) |
||
871 | { |
||
872 | int i; |
||
873 | callback_data p = (callback_data)pArg; |
||
874 | |||
875 | //Unpack |
||
876 | string[] azArg = ((callback_data_extra)p2).azVals; |
||
877 | string[] azCol = ((callback_data_extra)p2).azCols; |
||
878 | int[] aiType = ((callback_data_extra)p2).aiTypes; |
||
879 | |||
880 | switch (p.mode) |
||
881 | { |
||
882 | case MODE_Line: |
||
883 | { |
||
884 | int w = 5; |
||
885 | if (azArg == null) |
||
886 | break; |
||
887 | for (i = 0; i < nArg; i++) |
||
888 | { |
||
889 | int len = strlen30(azCol[i] != null ? azCol[i] : ""); |
||
890 | if (len > w) |
||
891 | w = len; |
||
892 | } |
||
893 | if (p.cnt++ > 0) |
||
894 | fprintf(p.Out, "\n"); |
||
895 | for (i = 0; i < nArg; i++) |
||
896 | { |
||
897 | fprintf(p.Out, "%*s = %s\n", w, azCol[i], |
||
898 | azArg[i] != null ? azArg[i] : p.nullvalue); |
||
899 | } |
||
900 | break; |
||
901 | } |
||
902 | case MODE_Explain: |
||
903 | case MODE_Column: |
||
904 | { |
||
905 | if (p.cnt++ == 0) |
||
906 | { |
||
907 | for (i = 0; i < nArg; i++) |
||
908 | { |
||
909 | int w, n; |
||
910 | if (i < ArraySize(p.colWidth)) |
||
911 | { |
||
912 | w = p.colWidth[i]; |
||
913 | } |
||
914 | else |
||
915 | { |
||
916 | w = 0; |
||
917 | } |
||
918 | if (w <= 0) |
||
919 | { |
||
920 | w = strlen30(azCol[i] != null ? azCol[i] : ""); |
||
921 | if (w < 10) |
||
922 | w = 10; |
||
923 | n = strlen30(azArg != null && azArg[i] != null ? azArg[i] : p.nullvalue); |
||
924 | if (w < n) |
||
925 | w = n; |
||
926 | } |
||
927 | if (i < ArraySize(p.actualWidth)) |
||
928 | { |
||
929 | p.actualWidth[i] = w; |
||
930 | } |
||
931 | if (p.showHeader) |
||
932 | { |
||
933 | fprintf(p.Out, "%-*.*s%s", w, w, azCol[i], i == nArg - 1 ? "\n" : " "); |
||
934 | } |
||
935 | } |
||
936 | if (p.showHeader) |
||
937 | { |
||
938 | for (i = 0; i < nArg; i++) |
||
939 | { |
||
940 | int w; |
||
941 | if (i < ArraySize(p.actualWidth)) |
||
942 | { |
||
943 | w = p.actualWidth[i]; |
||
944 | } |
||
945 | else |
||
946 | { |
||
947 | w = 10; |
||
948 | } |
||
949 | fprintf(p.Out, "%-*.*s%s", w, w, "-----------------------------------" + |
||
950 | "----------------------------------------------------------", |
||
951 | i == nArg - 1 ? "\n" : " "); |
||
952 | } |
||
953 | } |
||
954 | } |
||
955 | if (azArg == null) |
||
956 | break; |
||
957 | for (i = 0; i < nArg; i++) |
||
958 | { |
||
959 | int w; |
||
960 | if (i < ArraySize(p.actualWidth)) |
||
961 | { |
||
962 | w = p.actualWidth[i]; |
||
963 | } |
||
964 | else |
||
965 | { |
||
966 | w = 10; |
||
967 | } |
||
968 | if (p.mode == MODE_Explain && azArg[i] != null && |
||
969 | strlen30(azArg[i]) > w) |
||
970 | { |
||
971 | w = strlen30(azArg[i]); |
||
972 | } |
||
973 | fprintf(p.Out, "%-*.*s%s", w, w, |
||
974 | azArg[i] != null ? azArg[i] : p.nullvalue, i == nArg - 1 ? "\n" : " "); |
||
975 | } |
||
976 | break; |
||
977 | } |
||
978 | case MODE_Semi: |
||
979 | case MODE_List: |
||
980 | { |
||
981 | if (p.cnt++ == null && p.showHeader) |
||
982 | { |
||
983 | for (i = 0; i < nArg; i++) |
||
984 | { |
||
985 | fprintf(p.Out, "%s%s", azCol[i], i == nArg - 1 ? "\n" : p.separator); |
||
986 | } |
||
987 | } |
||
988 | if (azArg == null) |
||
989 | break; |
||
990 | for (i = 0; i < nArg; i++) |
||
991 | { |
||
992 | string z = azArg[i]; |
||
993 | if (z == null) |
||
994 | z = p.nullvalue; |
||
995 | fprintf(p.Out, "%s", z); |
||
996 | if (i < nArg - 1) |
||
997 | { |
||
998 | fprintf(p.Out, "%s", p.separator); |
||
999 | } |
||
1000 | else if (p.mode == MODE_Semi) |
||
1001 | { |
||
1002 | fprintf(p.Out, ";\n"); |
||
1003 | } |
||
1004 | else |
||
1005 | { |
||
1006 | fprintf(p.Out, "\n"); |
||
1007 | } |
||
1008 | } |
||
1009 | break; |
||
1010 | } |
||
1011 | case MODE_Html: |
||
1012 | { |
||
1013 | if (p.cnt++ == null && p.showHeader) |
||
1014 | { |
||
1015 | fprintf(p.Out, "<TR>"); |
||
1016 | for (i = 0; i < nArg; i++) |
||
1017 | { |
||
1018 | fprintf(p.Out, "<TH>"); |
||
1019 | output_html_string(p.Out, azCol[i]); |
||
1020 | fprintf(p.Out, "</TH>\n"); |
||
1021 | } |
||
1022 | fprintf(p.Out, "</TR>\n"); |
||
1023 | } |
||
1024 | if (azArg == null) |
||
1025 | break; |
||
1026 | fprintf(p.Out, "<TR>"); |
||
1027 | for (i = 0; i < nArg; i++) |
||
1028 | { |
||
1029 | fprintf(p.Out, "<TD>"); |
||
1030 | output_html_string(p.Out, azArg[i] != null ? azArg[i] : p.nullvalue); |
||
1031 | fprintf(p.Out, "</TD>\n"); |
||
1032 | } |
||
1033 | fprintf(p.Out, "</TR>\n"); |
||
1034 | break; |
||
1035 | } |
||
1036 | case MODE_Tcl: |
||
1037 | { |
||
1038 | if (p.cnt++ == null && p.showHeader) |
||
1039 | { |
||
1040 | for (i = 0; i < nArg; i++) |
||
1041 | { |
||
1042 | output_c_string(p.Out, azCol[i] != null ? azCol[i] : ""); |
||
1043 | fprintf(p.Out, "%s", p.separator); |
||
1044 | } |
||
1045 | fprintf(p.Out, "\n"); |
||
1046 | } |
||
1047 | if (azArg == null) |
||
1048 | break; |
||
1049 | for (i = 0; i < nArg; i++) |
||
1050 | { |
||
1051 | output_c_string(p.Out, azArg[i] != null ? azArg[i] : p.nullvalue); |
||
1052 | fprintf(p.Out, "%s", p.separator); |
||
1053 | } |
||
1054 | fprintf(p.Out, "\n"); |
||
1055 | break; |
||
1056 | } |
||
1057 | case MODE_Csv: |
||
1058 | { |
||
1059 | if (p.cnt++ == null && p.showHeader) |
||
1060 | { |
||
1061 | for (i = 0; i < nArg; i++) |
||
1062 | { |
||
1063 | output_csv(p, azCol[i] != null ? azCol[i] : "", i < nArg - 1); |
||
1064 | } |
||
1065 | fprintf(p.Out, "\n"); |
||
1066 | } |
||
1067 | if (azArg == null) |
||
1068 | break; |
||
1069 | for (i = 0; i < nArg; i++) |
||
1070 | { |
||
1071 | output_csv(p, azArg[i], i < nArg - 1); |
||
1072 | } |
||
1073 | fprintf(p.Out, "\n"); |
||
1074 | break; |
||
1075 | } |
||
1076 | case MODE_Insert: |
||
1077 | { |
||
1078 | p.cnt++; |
||
1079 | if (azArg == null) |
||
1080 | break; |
||
1081 | fprintf(p.Out, "INSERT INTO %s VALUES(", p.zDestTable); |
||
1082 | for (i = 0; i < nArg; i++) |
||
1083 | { |
||
1084 | string zSep = i > 0 ? "," : ""; |
||
1085 | if ((azArg[i] == null) || (aiType != null && i < aiType.Length && aiType[i] == Sqlite3.SQLITE_NULL)) |
||
1086 | { |
||
1087 | fprintf(p.Out, "%snull", zSep); |
||
1088 | } |
||
1089 | else if (aiType != null && aiType[i] == Sqlite3.SQLITE_TEXT) |
||
1090 | { |
||
1091 | if (!String.IsNullOrEmpty(zSep)) |
||
1092 | fprintf(p.Out, "%s", zSep); |
||
1093 | output_quoted_string(p.Out, azArg[i]); |
||
1094 | } |
||
1095 | else if (aiType != null && (aiType[i] == Sqlite3.SQLITE_INTEGER || aiType[i] == Sqlite3.SQLITE_FLOAT)) |
||
1096 | { |
||
1097 | fprintf(p.Out, "%s%s", zSep, azArg[i]); |
||
1098 | } |
||
1099 | else if (aiType != null && aiType[i] == Sqlite3.SQLITE_BLOB && p.pStmt != null) |
||
1100 | { |
||
1101 | byte[] pBlob = Sqlite3.sqlite3_column_blob(p.pStmt, i); |
||
1102 | int nBlob = Sqlite3.sqlite3_column_bytes(p.pStmt, i); |
||
1103 | if (!String.IsNullOrEmpty(zSep)) |
||
1104 | fprintf(p.Out, "%s", zSep); |
||
1105 | output_hex_blob(p.Out, pBlob, nBlob); |
||
1106 | } |
||
1107 | else if (isNumber(azArg[i], 0)) |
||
1108 | { |
||
1109 | fprintf(p.Out, "%s%s", zSep, azArg[i]); |
||
1110 | } |
||
1111 | else |
||
1112 | { |
||
1113 | if (!String.IsNullOrEmpty(zSep)) |
||
1114 | fprintf(p.Out, "%s", zSep); |
||
1115 | output_quoted_string(p.Out, azArg[i]); |
||
1116 | } |
||
1117 | } |
||
1118 | fprintf(p.Out, ");\n"); |
||
1119 | break; |
||
1120 | } |
||
1121 | } |
||
1122 | return 0; |
||
1123 | } |
||
1124 | |||
1125 | /* |
||
1126 | ** This is the callback routine that the SQLite library |
||
1127 | ** invokes for each row of a query result. |
||
1128 | */ |
||
1129 | static int callback(object pArg, sqlite3_int64 nArg, object azArg, object azCol) |
||
1130 | { |
||
1131 | /* since we don't have type info, call the shell_callback with a null value */ |
||
1132 | callback_data_extra cde = new callback_data_extra(); |
||
1133 | cde.azVals = (string[])azArg; |
||
1134 | cde.azCols = (string[])azCol; |
||
1135 | cde.aiTypes = null; |
||
1136 | return shell_callback(pArg, (int)nArg, cde, null); |
||
1137 | } |
||
1138 | |||
1139 | /* |
||
1140 | ** Set the destination table field of the callback_data structure to |
||
1141 | ** the name of the table given. Escape any quote characters in the |
||
1142 | ** table name. |
||
1143 | */ |
||
1144 | static void set_table_name(callback_data p, string zName) |
||
1145 | { |
||
1146 | int i, n; |
||
1147 | bool needQuote; |
||
1148 | string z = ""; |
||
1149 | |||
1150 | if (p.zDestTable != null) |
||
1151 | { |
||
1152 | //free(ref p.zDestTable); |
||
1153 | p.zDestTable = null; |
||
1154 | } |
||
1155 | if (zName == null) |
||
1156 | return; |
||
1157 | needQuote = !isalpha(zName[0]) && zName != "_"; |
||
1158 | for (i = n = 0; i < zName.Length; i++, n++) |
||
1159 | { |
||
1160 | if (!isalnum(zName[i]) && zName[i] != '_') |
||
1161 | { |
||
1162 | needQuote = true; |
||
1163 | if (zName[i] == '\'') |
||
1164 | n++; |
||
1165 | } |
||
1166 | } |
||
1167 | if (needQuote) |
||
1168 | n += 2; |
||
1169 | //z = p.zDestTable = malloc( n + 1 ); |
||
1170 | //if ( z == 0 ) |
||
1171 | //{ |
||
1172 | // fprintf( stderr, "Out of memory!\n" ); |
||
1173 | // exit( 1 ); |
||
1174 | //} |
||
1175 | //n = 0; |
||
1176 | if (needQuote) |
||
1177 | z += '\''; |
||
1178 | for (i = 0; i < zName.Length; i++) |
||
1179 | { |
||
1180 | z += zName[i]; |
||
1181 | if (zName[i] == '\'') |
||
1182 | z += '\''; |
||
1183 | } |
||
1184 | if (needQuote) |
||
1185 | z += '\''; |
||
1186 | //z[n] = 0; |
||
1187 | p.zDestTable = z; |
||
1188 | } |
||
1189 | |||
1190 | /* zIn is either a pointer to a null-terminated string in memory obtained |
||
1191 | ** from malloc(), or a null pointer. The string pointed to by zAppend is |
||
1192 | ** added to zIn, and the result returned in memory obtained from malloc(). |
||
1193 | ** zIn, if it was not null, is freed. |
||
1194 | ** |
||
1195 | ** If the third argument, quote, is not '\0', then it is used as a |
||
1196 | ** quote character for zAppend. |
||
1197 | */ |
||
1198 | static void appendText(StringBuilder zIn, string zAppend, int noQuote) |
||
1199 | { appendText(zIn, zAppend, '\0'); } |
||
1200 | |||
1201 | static void appendText(StringBuilder zIn, string zAppend, char quote) |
||
1202 | { |
||
1203 | int len; |
||
1204 | int i; |
||
1205 | int nAppend = strlen30(zAppend); |
||
1206 | int nIn = (zIn != null ? strlen30(zIn) : 0); |
||
1207 | |||
1208 | len = nAppend + nIn; |
||
1209 | if (quote != '\0') |
||
1210 | { |
||
1211 | len += 2; |
||
1212 | for (i = 0; i < nAppend; i++) |
||
1213 | { |
||
1214 | if (zAppend[i] == quote) |
||
1215 | len++; |
||
1216 | } |
||
1217 | } |
||
1218 | |||
1219 | //zIn = realloc( zIn, len ); |
||
1220 | //if ( !zIn ) |
||
1221 | //{ |
||
1222 | // return 0; |
||
1223 | //} |
||
1224 | |||
1225 | if (quote != '\0') |
||
1226 | { |
||
1227 | zIn.Append(quote); |
||
1228 | for (i = 0; i < nAppend; i++) |
||
1229 | { |
||
1230 | zIn.Append(zAppend[i]); |
||
1231 | if (zAppend[i] == quote) |
||
1232 | zIn.Append(quote); |
||
1233 | } |
||
1234 | zIn.Append(quote); |
||
1235 | //zCsr++ = '\0'; |
||
1236 | Debug.Assert(zIn.Length == len); |
||
1237 | |||
1238 | } |
||
1239 | else |
||
1240 | { |
||
1241 | zIn.Append(zAppend);//memcpy( zIn[nIn], zAppend, nAppend ); |
||
1242 | //zIn[len - 1] = '\0'; |
||
1243 | } |
||
1244 | } |
||
1245 | |||
1246 | |||
1247 | |||
1248 | /* |
||
1249 | ** Execute a query statement that has a single result column. Print |
||
1250 | ** that result column on a line by itself with a semicolon terminator. |
||
1251 | ** |
||
1252 | ** This is used, for example, to show the schema of the database by |
||
1253 | ** querying the Sqlite3.SQLITE_MASTER table. |
||
1254 | */ |
||
1255 | static int run_table_dump_query( |
||
1256 | FILE Out, /* Send output here */ |
||
1257 | sqlite3 db, /* Database to query */ |
||
1258 | StringBuilder zSelect, /* SELECT statement to extract content */ |
||
1259 | string zFirstRow /* Print before first row, if not null */ |
||
1260 | ) |
||
1261 | { return run_table_dump_query(Out, db, zSelect.ToString(), zFirstRow); } |
||
1262 | |||
1263 | static int run_table_dump_query( |
||
1264 | FILE Out, /* Send output here */ |
||
1265 | sqlite3 db, /* Database to query */ |
||
1266 | string zSelect, /* SELECT statement to extract content */ |
||
1267 | string zFirstRow /* Print before first row, if not null */ |
||
1268 | ) |
||
1269 | { |
||
1270 | sqlite3_stmt pSelect = null; |
||
1271 | int rc; |
||
1272 | rc = Sqlite3.sqlite3_prepare(db, zSelect, -1, ref pSelect, 0); |
||
1273 | if (rc != Sqlite3.SQLITE_OK || null == pSelect) |
||
1274 | { |
||
1275 | return rc; |
||
1276 | } |
||
1277 | rc = Sqlite3.sqlite3_step(pSelect); |
||
1278 | while (rc == Sqlite3.SQLITE_ROW) |
||
1279 | { |
||
1280 | if (zFirstRow != null) |
||
1281 | { |
||
1282 | fprintf(Out, "%s", zFirstRow); |
||
1283 | zFirstRow = null; |
||
1284 | } |
||
1285 | fprintf(Out, "%s;\n", Sqlite3.sqlite3_column_text(pSelect, 0)); |
||
1286 | rc = Sqlite3.sqlite3_step(pSelect); |
||
1287 | } |
||
1288 | return Sqlite3.sqlite3_finalize(pSelect); |
||
1289 | } |
||
1290 | |||
1291 | /* |
||
1292 | ** Allocate space and save off current error string. |
||
1293 | */ |
||
1294 | static string save_err_msg( |
||
1295 | sqlite3 db /* Database to query */ |
||
1296 | ) |
||
1297 | { |
||
1298 | //int nErrMsg = 1 + strlen30(Sqlite3.sqlite3_errmsg(db)); |
||
1299 | //string zErrMsg = Sqlite3.sqlite3_malloc(nErrMsg); |
||
1300 | //if (zErrMsg != null) |
||
1301 | //{ |
||
1302 | // memcpy(zErrMsg, Sqlite3.sqlite3_errmsg(db), nErrMsg); |
||
1303 | //} |
||
1304 | return Sqlite3.sqlite3_errmsg(db); //zErrMsg; |
||
1305 | } |
||
1306 | |||
1307 | /* |
||
1308 | ** Display memory stats. |
||
1309 | */ |
||
1310 | static int display_stats( |
||
1311 | sqlite3 db, /* Database to query */ |
||
1312 | callback_data pArg, /* Pointer to struct callback_data */ |
||
1313 | int bReset /* True to reset the stats */ |
||
1314 | ) |
||
1315 | { |
||
1316 | int iCur; |
||
1317 | int iHiwtr; |
||
1318 | |||
1319 | if (pArg != null && pArg.Out != null) |
||
1320 | { |
||
1321 | |||
1322 | iHiwtr = iCur = -1; |
||
1323 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_MEMORY_USED, ref iCur, ref iHiwtr, bReset); |
||
1324 | fprintf(pArg.Out, "Memory Used: %d (max %d) bytes\n", iCur, iHiwtr); |
||
1325 | iHiwtr = iCur = -1; |
||
1326 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_MALLOC_COUNT, ref iCur, ref iHiwtr, bReset); |
||
1327 | fprintf(pArg.Out, "Number of Outstanding Allocations: %d (max %d)\n", iCur, iHiwtr); |
||
1328 | /* |
||
1329 | ** Not currently used by the CLI. |
||
1330 | ** iHiwtr = iCur = -1; |
||
1331 | ** Sqlite3.SQLITE_status(Sqlite3.SQLITE_STATUS_PAGECACHE_USED,ref iCur,ref iHiwtr, bReset); |
||
1332 | ** fprintf(pArg.Out, "Number of Pcache Pages Used: %d (max %d) pages\n", iCur, iHiwtr); |
||
1333 | */ |
||
1334 | iHiwtr = iCur = -1; |
||
1335 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_PAGECACHE_OVERFLOW, ref iCur, ref iHiwtr, bReset); |
||
1336 | fprintf(pArg.Out, "Number of Pcache Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); |
||
1337 | /* |
||
1338 | ** Not currently used by the CLI. |
||
1339 | ** iHiwtr = iCur = -1; |
||
1340 | ** Sqlite3.SQLITE_status(Sqlite3.SQLITE_STATUS_SCRATCH_USED,ref iCur,ref iHiwtr, bReset); |
||
1341 | ** fprintf(pArg.Out, "Number of Scratch Allocations Used: %d (max %d)\n", iCur, iHiwtr); |
||
1342 | */ |
||
1343 | iHiwtr = iCur = -1; |
||
1344 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_SCRATCH_OVERFLOW, ref iCur, ref iHiwtr, bReset); |
||
1345 | fprintf(pArg.Out, "Number of Scratch Overflow Bytes: %d (max %d) bytes\n", iCur, iHiwtr); |
||
1346 | iHiwtr = iCur = -1; |
||
1347 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_MALLOC_SIZE, ref iCur, ref iHiwtr, bReset); |
||
1348 | fprintf(pArg.Out, "Largest Allocation: %d bytes\n", iHiwtr); |
||
1349 | iHiwtr = iCur = -1; |
||
1350 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_PAGECACHE_SIZE, ref iCur, ref iHiwtr, bReset); |
||
1351 | fprintf(pArg.Out, "Largest Pcache Allocation: %d bytes\n", iHiwtr); |
||
1352 | iHiwtr = iCur = -1; |
||
1353 | Sqlite3.sqlite3_status(Sqlite3.SQLITE_STATUS_SCRATCH_SIZE, ref iCur, ref iHiwtr, bReset); |
||
1354 | fprintf(pArg.Out, "Largest Scratch Allocation: %d bytes\n", iHiwtr); |
||
1355 | #if YYTRACKMAXSTACKDEPTH |
||
1356 | iHiwtr = iCur = -1; |
||
1357 | Sqlite3.SQLITE_status(Sqlite3.SQLITE_STATUS_PARSER_STACK,ref iCur,ref iHiwtr, bReset); |
||
1358 | fprintf(pArg.Out, "Deepest Parser Stack: %d (max %d)\n", iCur, iHiwtr); |
||
1359 | #endif |
||
1360 | } |
||
1361 | |||
1362 | if (pArg != null && pArg.Out != null && db != null) |
||
1363 | { |
||
1364 | iHiwtr = iCur = -1; |
||
1365 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_LOOKASIDE_USED, ref iCur, ref iHiwtr, bReset); |
||
1366 | fprintf(pArg.Out, "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); |
||
1367 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_LOOKASIDE_HIT, ref iCur, ref iHiwtr, bReset); |
||
1368 | fprintf(pArg.Out, "Successful lookaside attempts: %d\n", iHiwtr); |
||
1369 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, ref iCur, ref iHiwtr, bReset); |
||
1370 | fprintf(pArg.Out, "Lookaside failures due to size: %d\n", iHiwtr); |
||
1371 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, ref iCur, ref iHiwtr, bReset); |
||
1372 | fprintf(pArg.Out, "Lookaside failures due to OOM: %d\n", iHiwtr); |
||
1373 | iHiwtr = iCur = -1; |
||
1374 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_CACHE_USED, ref iCur, ref iHiwtr, bReset); |
||
1375 | fprintf(pArg.Out, "Pager Heap Usage: %d bytes\n", iCur); |
||
1376 | iHiwtr = iCur = -1; |
||
1377 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_SCHEMA_USED, ref iCur, ref iHiwtr, bReset); |
||
1378 | fprintf(pArg.Out, "Schema Heap Usage: %d bytes\n", iCur); |
||
1379 | iHiwtr = iCur = -1; |
||
1380 | Sqlite3.sqlite3_db_status(db, Sqlite3.SQLITE_DBSTATUS_STMT_USED, ref iCur, ref iHiwtr, bReset); |
||
1381 | fprintf(pArg.Out, "Statement Heap/Lookaside Usage: %d bytes\n", iCur); |
||
1382 | } |
||
1383 | |||
1384 | if (pArg != null && pArg.Out != null && db != null && pArg.pStmt != null) |
||
1385 | { |
||
1386 | iCur = Sqlite3.sqlite3_stmt_status(pArg.pStmt, Sqlite3.SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); |
||
1387 | fprintf(pArg.Out, "Fullscan Steps: %d\n", iCur); |
||
1388 | iCur = Sqlite3.sqlite3_stmt_status(pArg.pStmt, Sqlite3.SQLITE_STMTSTATUS_SORT, bReset); |
||
1389 | fprintf(pArg.Out, "Sort Operations: %d\n", iCur); |
||
1390 | iCur = Sqlite3.sqlite3_stmt_status(pArg.pStmt, Sqlite3.SQLITE_STMTSTATUS_AUTOINDEX, bReset); |
||
1391 | fprintf(pArg.Out, "Autoindex Inserts: %d\n", iCur); |
||
1392 | } |
||
1393 | |||
1394 | return 0; |
||
1395 | } |
||
1396 | |||
1397 | /* |
||
1398 | ** Execute a statement or set of statements. Print |
||
1399 | ** any result rows/columns depending on the current mode |
||
1400 | ** set via the supplied callback. |
||
1401 | ** |
||
1402 | ** This is very similar to SQLite's built-in Sqlite3.sqlite3_exec() |
||
1403 | ** function except it takes a slightly different callback |
||
1404 | ** and callback data argument. |
||
1405 | */ |
||
1406 | static int shell_exec( |
||
1407 | sqlite3 db, /* An open database */ |
||
1408 | string zSql, /* SQL to be evaluated */ |
||
1409 | dxCallback xCallback, //int (*xCallback)(void*,int,char**,char**,int*), /* Callback function */ |
||
1410 | /* (not the same as Sqlite3.sqlite3_exec) */ |
||
1411 | callback_data pArg, /* Pointer to struct callback_data */ |
||
1412 | ref string pzErrMsg /* Error msg written here */ |
||
1413 | ) |
||
1414 | { |
||
1415 | sqlite3_stmt pStmt = null; /* Statement to execute. */ |
||
1416 | int rc = Sqlite3.SQLITE_OK; /* Return Code */ |
||
1417 | string zLeftover = null; /* Tail of unprocessed SQL */ |
||
1418 | |||
1419 | // if( pzErrMsg ) |
||
1420 | { |
||
1421 | pzErrMsg = null; |
||
1422 | } |
||
1423 | |||
1424 | while (!String.IsNullOrEmpty(zSql) && (Sqlite3.SQLITE_OK == rc)) |
||
1425 | { |
||
1426 | rc = Sqlite3.sqlite3_prepare_v2(db, zSql, -1, ref pStmt, ref zLeftover); |
||
1427 | if (Sqlite3.SQLITE_OK != rc) |
||
1428 | { |
||
1429 | //if (pzErrMsg != null) |
||
1430 | { |
||
1431 | pzErrMsg = save_err_msg(db); |
||
1432 | } |
||
1433 | } |
||
1434 | else |
||
1435 | { |
||
1436 | if (null == pStmt) |
||
1437 | { |
||
1438 | /* this happens for a comment or white-space */ |
||
1439 | zSql = zLeftover.TrimStart(); |
||
1440 | //while (isspace(zSql[0])) |
||
1441 | // zSql++; |
||
1442 | continue; |
||
1443 | } |
||
1444 | |||
1445 | /* save off the prepared statment handle and reset row count */ |
||
1446 | if (pArg != null) |
||
1447 | { |
||
1448 | pArg.pStmt = pStmt; |
||
1449 | pArg.cnt = 0; |
||
1450 | } |
||
1451 | |||
1452 | /* echo the sql statement if echo on */ |
||
1453 | if (pArg != null && pArg.echoOn) |
||
1454 | { |
||
1455 | string zStmtSql = Sqlite3.sqlite3_sql(pStmt); |
||
1456 | fprintf(pArg.Out, "%s\n", zStmtSql != null ? zStmtSql : zSql); |
||
1457 | } |
||
1458 | |||
1459 | /* perform the first step. this will tell us if we |
||
1460 | ** have a result set or not and how wide it is. |
||
1461 | */ |
||
1462 | rc = Sqlite3.sqlite3_step(pStmt); |
||
1463 | /* if we have a result set... */ |
||
1464 | if (Sqlite3.SQLITE_ROW == rc) |
||
1465 | { |
||
1466 | /* if we have a callback... */ |
||
1467 | if (xCallback != null) |
||
1468 | { |
||
1469 | /* allocate space for col name ptr, value ptr, and type */ |
||
1470 | int nCol = Sqlite3.sqlite3_column_count(pStmt); |
||
1471 | //void pData = Sqlite3.SQLITE_malloc(3*nCol*sizeof(const char*) + 1); |
||
1472 | //if( !pData ){ |
||
1473 | // rc = Sqlite3.SQLITE_NOMEM; |
||
1474 | //}else |
||
1475 | { |
||
1476 | string[] azCols = new string[nCol];//(string *)pData; /* Names of result columns */ |
||
1477 | string[] azVals = new string[nCol];//azCols[nCol]; /* Results */ |
||
1478 | int[] aiTypes = new int[nCol];//(int *)&azVals[nCol]; /* Result types */ |
||
1479 | int i; |
||
1480 | //Debug.Assert(sizeof(int) <= sizeof(string )); |
||
1481 | /* save off ptrs to column names */ |
||
1482 | for (i = 0; i < nCol; i++) |
||
1483 | { |
||
1484 | azCols[i] = (string)Sqlite3.sqlite3_column_name(pStmt, i); |
||
1485 | } |
||
1486 | do |
||
1487 | { |
||
1488 | /* extract the data and data types */ |
||
1489 | for (i = 0; i < nCol; i++) |
||
1490 | { |
||
1491 | azVals[i] = (string)Sqlite3.sqlite3_column_text(pStmt, i); |
||
1492 | aiTypes[i] = Sqlite3.sqlite3_column_type(pStmt, i); |
||
1493 | if (null == azVals[i] && (aiTypes[i] != Sqlite3.SQLITE_NULL)) |
||
1494 | { |
||
1495 | rc = Sqlite3.SQLITE_NOMEM; |
||
1496 | break; /* from for */ |
||
1497 | } |
||
1498 | } /* end for */ |
||
1499 | |||
1500 | /* if data and types extracted successfully... */ |
||
1501 | if (Sqlite3.SQLITE_ROW == rc) |
||
1502 | { |
||
1503 | /* call the supplied callback with the result row data */ |
||
1504 | callback_data_extra cde = new callback_data_extra(); |
||
1505 | cde.azVals = azVals; |
||
1506 | cde.azCols = azCols; |
||
1507 | cde.aiTypes = aiTypes; |
||
1508 | |||
1509 | if (xCallback(pArg, nCol, cde, null) != 0) |
||
1510 | { |
||
1511 | rc = Sqlite3.SQLITE_ABORT; |
||
1512 | } |
||
1513 | else |
||
1514 | { |
||
1515 | rc = Sqlite3.sqlite3_step(pStmt); |
||
1516 | } |
||
1517 | } |
||
1518 | } while (Sqlite3.SQLITE_ROW == rc); |
||
1519 | //Sqlite3.sqlite3_free(ref pData); |
||
1520 | } |
||
1521 | } |
||
1522 | else |
||
1523 | { |
||
1524 | do |
||
1525 | { |
||
1526 | rc = Sqlite3.sqlite3_step(pStmt); |
||
1527 | } while (rc == Sqlite3.SQLITE_ROW); |
||
1528 | } |
||
1529 | } |
||
1530 | |||
1531 | /* print usage stats if stats on */ |
||
1532 | if (pArg != null && pArg.statsOn) |
||
1533 | { |
||
1534 | display_stats(db, pArg, 0); |
||
1535 | } |
||
1536 | |||
1537 | /* Finalize the statement just executed. If this fails, save a |
||
1538 | ** copy of the error message. Otherwise, set zSql to point to the |
||
1539 | ** next statement to execute. */ |
||
1540 | rc = Sqlite3.sqlite3_finalize(pStmt); |
||
1541 | if (rc == Sqlite3.SQLITE_OK) |
||
1542 | { |
||
1543 | zSql = zLeftover.TrimStart(); |
||
1544 | //while (isspace(zSql[0])) |
||
1545 | // zSql++; |
||
1546 | } |
||
1547 | else //if (pzErrMsg) |
||
1548 | { |
||
1549 | pzErrMsg = save_err_msg(db); |
||
1550 | } |
||
1551 | |||
1552 | /* clear saved stmt handle */ |
||
1553 | if (pArg != null) |
||
1554 | { |
||
1555 | pArg.pStmt = null; |
||
1556 | } |
||
1557 | } |
||
1558 | } /* end while */ |
||
1559 | |||
1560 | return rc; |
||
1561 | } |
||
1562 | |||
1563 | |||
1564 | /* |
||
1565 | ** This is a different callback routine used for dumping the database. |
||
1566 | ** Each row received by this callback consists of a table name, |
||
1567 | ** the table type ("index" or "table") and SQL to create the table. |
||
1568 | ** This routine should print text sufficient to recreate the table. |
||
1569 | */ |
||
1570 | static int dump_callback(object pArg, sqlite3_int64 nArg, object pazArg, object pazCol) |
||
1571 | { |
||
1572 | int rc; |
||
1573 | string zTable; |
||
1574 | string zType; |
||
1575 | string zSql; |
||
1576 | string zPrepStmt = null; |
||
1577 | callback_data p = (callback_data)pArg; |
||
1578 | string[] azArg = (string[])pazArg; |
||
1579 | string[] azCol = (string[])pazCol; |
||
1580 | |||
1581 | UNUSED_PARAMETER(azCol); |
||
1582 | if (nArg != 3) |
||
1583 | return 1; |
||
1584 | zTable = azArg[0]; |
||
1585 | zType = azArg[1]; |
||
1586 | zSql = azArg[2]; |
||
1587 | |||
1588 | if (zTable.Equals("sqlite_sequence", StringComparison.InvariantCultureIgnoreCase)) |
||
1589 | { |
||
1590 | zPrepStmt = "DELETE FROM sqlite_sequence;\n"; |
||
1591 | } |
||
1592 | else if (zTable.Equals("sqlite_stat1", StringComparison.InvariantCultureIgnoreCase)) |
||
1593 | { |
||
1594 | fprintf(p.Out, "ANALYZE Sqlite3.SQLITE_master;\n"); |
||
1595 | } |
||
1596 | else if (zTable.StartsWith("SQLITE_", StringComparison.InvariantCultureIgnoreCase)) |
||
1597 | { |
||
1598 | return 0; |
||
1599 | } |
||
1600 | else if (zSql.StartsWith("CREATE VIRTUAL TABLE", StringComparison.InvariantCultureIgnoreCase)) |
||
1601 | { |
||
1602 | string zIns; |
||
1603 | if (!p.writableSchema) |
||
1604 | { |
||
1605 | fprintf(p.Out, "PRAGMA writable_schema=ON;\n"); |
||
1606 | p.writableSchema = true; |
||
1607 | } |
||
1608 | zIns = Sqlite3.sqlite3_mprintf( |
||
1609 | "INSERT INTO Sqlite3.SQLITE_master(type,name,tbl_name,rootpage,sql)" + |
||
1610 | "VALUES('table','%q','%q',0,'%q');", |
||
1611 | zTable, zTable, zSql); |
||
1612 | fprintf(p.Out, "%s\n", zIns); |
||
1613 | zIns = null;//Sqlite3.sqlite3_free(zIns); |
||
1614 | return 0; |
||
1615 | } |
||
1616 | else |
||
1617 | { |
||
1618 | fprintf(p.Out, "%s;\n", zSql); |
||
1619 | } |
||
1620 | |||
1621 | if (zType.Equals("table", StringComparison.InvariantCultureIgnoreCase)) |
||
1622 | { |
||
1623 | sqlite3_stmt pTableInfo = null; |
||
1624 | StringBuilder zSelect = new StringBuilder(); |
||
1625 | StringBuilder zTableInfo = new StringBuilder(); |
||
1626 | StringBuilder zTmp = new StringBuilder(); |
||
1627 | int nRow = 0; |
||
1628 | |||
1629 | appendText(zTableInfo, "PRAGMA table_info(", 0); |
||
1630 | appendText(zTableInfo, zTable, '"'); |
||
1631 | appendText(zTableInfo, ");", 0); |
||
1632 | |||
1633 | rc = Sqlite3.sqlite3_prepare(p.db, zTableInfo, -1, ref pTableInfo, 0); |
||
1634 | zTableInfo = null;//free(zTableInfo); |
||
1635 | if (rc != Sqlite3.SQLITE_OK || null == pTableInfo) |
||
1636 | { |
||
1637 | return 1; |
||
1638 | } |
||
1639 | |||
1640 | appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); |
||
1641 | appendText(zTmp, zTable, '"'); |
||
1642 | //if (zTmp!=null) |
||
1643 | { |
||
1644 | appendText(zSelect, zTmp.ToString(), '\''); |
||
1645 | } |
||
1646 | appendText(zSelect, " || ' VALUES(' || ", 0); |
||
1647 | rc = Sqlite3.sqlite3_step(pTableInfo); |
||
1648 | while (rc == Sqlite3.SQLITE_ROW) |
||
1649 | { |
||
1650 | string zText = (string)Sqlite3.sqlite3_column_text(pTableInfo, 1); |
||
1651 | appendText(zSelect, "quote(", 0); |
||
1652 | appendText(zSelect, zText, '"'); |
||
1653 | rc = Sqlite3.sqlite3_step(pTableInfo); |
||
1654 | if (rc == Sqlite3.SQLITE_ROW) |
||
1655 | { |
||
1656 | appendText(zSelect, ") || ',' || ", 0); |
||
1657 | } |
||
1658 | else |
||
1659 | { |
||
1660 | appendText(zSelect, ") ", 0); |
||
1661 | } |
||
1662 | nRow++; |
||
1663 | } |
||
1664 | rc = Sqlite3.sqlite3_finalize(pTableInfo); |
||
1665 | if (rc != Sqlite3.SQLITE_OK || nRow == 0) |
||
1666 | { |
||
1667 | zSelect = null;//free(zSelect); |
||
1668 | return 1; |
||
1669 | } |
||
1670 | appendText(zSelect, "|| ')' FROM ", 0); |
||
1671 | appendText(zSelect, zTable, '"'); |
||
1672 | |||
1673 | rc = run_table_dump_query(p.Out, p.db, zSelect, zPrepStmt); |
||
1674 | if (rc == Sqlite3.SQLITE_CORRUPT) |
||
1675 | { |
||
1676 | appendText(zSelect, " ORDER BY rowid DESC", 0); |
||
1677 | rc = run_table_dump_query(p.Out, p.db, zSelect, ""); |
||
1678 | } |
||
1679 | if (zSelect != null) |
||
1680 | zSelect = null;//free(zSelect); |
||
1681 | } |
||
1682 | return 0; |
||
1683 | } |
||
1684 | |||
1685 | /* |
||
1686 | ** Run zQuery. Use dump_callback() as the callback routine so that |
||
1687 | ** the contents of the query are output as SQL statements. |
||
1688 | ** |
||
1689 | ** If we get a Sqlite3.SQLITE_CORRUPT error, rerun the query after appending |
||
1690 | ** "ORDER BY rowid DESC" to the end. |
||
1691 | */ |
||
1692 | static int run_schema_dump_query( |
||
1693 | callback_data p, |
||
1694 | string zQuery, |
||
1695 | string pzErrMsg |
||
1696 | ) |
||
1697 | { |
||
1698 | int rc; |
||
1699 | rc = Sqlite3.sqlite3_exec(p.db, zQuery, dump_callback, p, ref pzErrMsg); |
||
1700 | if (rc == Sqlite3.SQLITE_CORRUPT) |
||
1701 | { |
||
1702 | StringBuilder zQ2; |
||
1703 | int len = strlen30(zQuery); |
||
1704 | if (pzErrMsg != null) |
||
1705 | pzErrMsg = null;//Sqlite3.sqlite3_free(pzErrMsg); |
||
1706 | zQ2 = new StringBuilder(len + 100);//zQ2 = malloc(len + 100); |
||
1707 | //if (zQ2 == null) |
||
1708 | // return rc; |
||
1709 | Sqlite3.sqlite3_snprintf(zQ2.Capacity, zQ2, "%s ORDER BY rowid DESC", zQuery); |
||
1710 | rc = Sqlite3.sqlite3_exec(p.db, zQ2.ToString(), dump_callback, p, ref pzErrMsg); |
||
1711 | zQ2 = null;//free(zQ2); |
||
1712 | } |
||
1713 | return rc; |
||
1714 | } |
||
1715 | |||
1716 | /* |
||
1717 | ** Text of a help message |
||
1718 | */ |
||
1719 | static string zHelp = |
||
1720 | ".backup ?DB? FILE Backup DB (default \"main\") to FILE\n" + |
||
1721 | ".bail ON|OFF Stop after hitting an error. Default OFF\n" + |
||
1722 | ".databases List names and files of attached databases\n" + |
||
1723 | ".dump ?TABLE? ... Dump the database in an SQL text format\n" + |
||
1724 | " If TABLE specified, only dump tables matching\n" + |
||
1725 | " LIKE pattern TABLE.\n" + |
||
1726 | ".echo ON|OFF Turn command echo on or off\n" + |
||
1727 | ".exit Exit this program\n" + |
||
1728 | ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n" + |
||
1729 | " With no args, it turns EXPLAIN on.\n" + |
||
1730 | ".header(s) ON|OFF Turn display of headers on or off\n" + |
||
1731 | ".help Show this message\n" + |
||
1732 | ".import FILE TABLE Import data from FILE into TABLE\n" + |
||
1733 | ".indices ?TABLE? Show names of all indices\n" + |
||
1734 | " If TABLE specified, only show indices for tables\n" + |
||
1735 | " matching LIKE pattern TABLE.\n" + |
||
1736 | #if SQLITE_ENABLE_IOTRACE |
||
1737 | ".iotrace FILE Enable I/O diagnostic logging to FILE\n" + |
||
1738 | #endif |
||
1739 | #if !SQLITE_OMIT_LOAD_EXTENSION |
||
1740 | ".load FILE ?ENTRY? Load an extension library\n" + |
||
1741 | #endif |
||
1742 | ".log FILE|off Turn logging on or off. FILE can be stderr/stdout\n" + |
||
1743 | ".mode MODE ?TABLE? Set output mode where MODE is one of:\n" + |
||
1744 | " csv Comma-separated values\n" + |
||
1745 | " column Left-aligned columns. (See .width)\n" + |
||
1746 | " html HTML <table> code\n" + |
||
1747 | " insert SQL insert statements for TABLE\n" + |
||
1748 | " line One value per line\n" + |
||
1749 | " list Values delimited by .separator string\n" + |
||
1750 | " tabs Tab-separated values\n" + |
||
1751 | " tcl TCL list elements\n" + |
||
1752 | ".nullvalue STRING Print STRING in place of null values\n" + |
||
1753 | ".output FILENAME Send output to FILENAME\n" + |
||
1754 | ".output stdout Send output to the screen\n" + |
||
1755 | ".prompt MAIN CONTINUE Replace the standard prompts\n" + |
||
1756 | ".quit Exit this program\n" + |
||
1757 | ".read FILENAME Execute SQL in FILENAME\n" + |
||
1758 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" + |
||
1759 | ".schema ?TABLE? Show the CREATE statements\n" + |
||
1760 | " If TABLE specified, only show tables matching\n" + |
||
1761 | " LIKE pattern TABLE.\n" + |
||
1762 | ".separator STRING Change separator used by output mode and .import\n" + |
||
1763 | ".show Show the current values for various settings\n" + |
||
1764 | ".stats ON|OFF Turn stats on or off\n" + |
||
1765 | ".tables ?TABLE? List names of tables\n" + |
||
1766 | " If TABLE specified, only list tables matching\n" + |
||
1767 | " LIKE pattern TABLE.\n" + |
||
1768 | ".timeout MS Try opening locked tables for MS milliseconds\n" + |
||
1769 | ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" |
||
1770 | ; |
||
1771 | |||
1772 | static string zTimerHelp = |
||
1773 | ".timer ON|OFF Turn the CPU timer measurement on or off\n" |
||
1774 | ; |
||
1775 | |||
1776 | /* Forward reference */ |
||
1777 | //static int process_input(callback_data p, FILE In); |
||
1778 | |||
1779 | /* |
||
1780 | ** Make sure the database is open. If it is not, then open it. If |
||
1781 | ** the database fails to open, print an error message and exit. |
||
1782 | */ |
||
1783 | static void open_db(callback_data p) |
||
1784 | { |
||
1785 | if (p.db == null) |
||
1786 | { |
||
1787 | Sqlite3.sqlite3_open(p.zDbFilename, out p.db); |
||
1788 | db = p.db; |
||
1789 | if (db != null && Sqlite3.sqlite3_errcode(db) == Sqlite3.SQLITE_OK) |
||
1790 | { |
||
1791 | Sqlite3.sqlite3_create_function(db, "shellstatic", 0, Sqlite3.SQLITE_UTF8, 0, |
||
1792 | shellstaticFunc, null, null); |
||
1793 | } |
||
1794 | if (db == null || Sqlite3.SQLITE_OK != Sqlite3.sqlite3_errcode(db)) |
||
1795 | { |
||
1796 | fprintf(stderr, "Error: unable to open database \"%s\": %s\n", |
||
1797 | p.zDbFilename, Sqlite3.sqlite3_errmsg(db)); |
||
1798 | exit(1); |
||
1799 | } |
||
1800 | #if !SQLITE_OMIT_LOAD_EXTENSION |
||
1801 | Sqlite3.sqlite3_enable_load_extension(p.db, 1); |
||
1802 | #endif |
||
1803 | } |
||
1804 | } |
||
1805 | |||
1806 | /* |
||
1807 | ** Do C-language style dequoting. |
||
1808 | ** |
||
1809 | ** \t . tab |
||
1810 | ** \n . newline |
||
1811 | ** \r . carriage return |
||
1812 | ** \NNN . ascii character NNN in octal |
||
1813 | ** \\ . backslash |
||
1814 | */ |
||
1815 | static void resolve_backslashes(ref string z) |
||
1816 | { |
||
1817 | StringBuilder sb = new StringBuilder(z); |
||
1818 | resolve_backslashes(sb); |
||
1819 | z = sb.ToString(); |
||
1820 | } |
||
1821 | static void resolve_backslashes(StringBuilder z) |
||
1822 | { |
||
1823 | int i, j; |
||
1824 | char c; |
||
1825 | for (i = j = 0; i < z.Length && (c = z[i]) != 0; i++, j++) |
||
1826 | { |
||
1827 | if (c == '\\') |
||
1828 | { |
||
1829 | c = z[++i]; |
||
1830 | if (c == 'n') |
||
1831 | { |
||
1832 | c = '\n'; |
||
1833 | } |
||
1834 | else if (c == 't') |
||
1835 | { |
||
1836 | c = '\t'; |
||
1837 | } |
||
1838 | else if (c == 'r') |
||
1839 | { |
||
1840 | c = '\r'; |
||
1841 | } |
||
1842 | else if (c >= '0' && c <= '7') |
||
1843 | { |
||
1844 | c -= '0'; |
||
1845 | if (z[i + 1] >= '0' && z[i + 1] <= '7') |
||
1846 | { |
||
1847 | i++; |
||
1848 | c = (char)((c << 3) + z[i] - '0'); |
||
1849 | if (z[i + 1] >= '0' && z[i + 1] <= '7') |
||
1850 | { |
||
1851 | i++; |
||
1852 | c = (char)((c << 3) + z[i] - '0'); |
||
1853 | } |
||
1854 | } |
||
1855 | } |
||
1856 | } |
||
1857 | z[j] = c; |
||
1858 | } |
||
1859 | z.Length = j;//z[j] = '\0'; |
||
1860 | } |
||
1861 | |||
1862 | |||
1863 | /* |
||
1864 | ** Interpret zArg as a boolean value. Return either 0 or 1. |
||
1865 | */ |
||
1866 | static bool booleanValue(StringBuilder zArg) |
||
1867 | { |
||
1868 | return booleanValue(zArg.ToString()); |
||
1869 | } |
||
1870 | |||
1871 | static bool booleanValue(string zArg) |
||
1872 | { |
||
1873 | if (String.IsNullOrEmpty(zArg)) |
||
1874 | return false; |
||
1875 | int val = Char.IsDigit(zArg[0]) ? Convert.ToInt32(zArg) : 0;// atoi( zArg ); |
||
1876 | int j; |
||
1877 | //for (j = 0; zArg[j]; j++) |
||
1878 | //{ |
||
1879 | // zArg[j] = (char)tolower(zArg[j]); |
||
1880 | //} |
||
1881 | if (zArg.Equals("on", StringComparison.InvariantCultureIgnoreCase)) |
||
1882 | { |
||
1883 | val = 1; |
||
1884 | } |
||
1885 | else if (zArg.Equals("yes", StringComparison.InvariantCultureIgnoreCase)) |
||
1886 | { |
||
1887 | val = 1; |
||
1888 | } |
||
1889 | return val != 0; |
||
1890 | } |
||
1891 | |||
1892 | class _aCtrl |
||
1893 | { |
||
1894 | public string zCtrlName; /* Name of a test-control option */ |
||
1895 | public int ctrlCode; /* Integer code for that option */ |
||
1896 | |||
1897 | public _aCtrl(string zCtrlName, int ctrlCode) |
||
1898 | { |
||
1899 | this.zCtrlName = zCtrlName; |
||
1900 | this.ctrlCode = ctrlCode; |
||
1901 | } |
||
1902 | } |
||
1903 | |||
1904 | /* |
||
1905 | ** If an input line begins with "." then invoke this routine to |
||
1906 | ** process that line. |
||
1907 | ** |
||
1908 | ** Return 1 on error, 2 to exit, and 0 otherwise. |
||
1909 | */ |
||
1910 | static int do_meta_command(StringBuilder zLine, callback_data p) |
||
1911 | { |
||
1912 | int i = 1; |
||
1913 | int i0 = 0; |
||
1914 | int nArg = 0; |
||
1915 | int n, c; |
||
1916 | int rc = 0; |
||
1917 | string[] azArg = new string[50]; |
||
1918 | |||
1919 | /* Parse the input line into tokens. |
||
1920 | */ |
||
1921 | while (i < zLine.Length && nArg < ArraySize(azArg)) |
||
1922 | { |
||
1923 | while (i < zLine.Length && Char.IsWhiteSpace(zLine[i])) { i++; } |
||
1924 | if (i == zLine.Length) |
||
1925 | break; |
||
1926 | if (zLine[i] == '\'' || zLine[i] == '"') |
||
1927 | { |
||
1928 | int delim = zLine[i++]; |
||
1929 | i0 = i; |
||
1930 | azArg[nArg++] = zLine[i].ToString(); |
||
1931 | while (zLine[i] != '\0' && zLine[i] != delim) { i++; } |
||
1932 | if (zLine[i] == delim) |
||
1933 | { |
||
1934 | zLine[i++] = '\0'; |
||
1935 | } |
||
1936 | if (delim == '"') |
||
1937 | resolve_backslashes(ref azArg[nArg - 1]); |
||
1938 | } |
||
1939 | else |
||
1940 | { |
||
1941 | i0 = i; |
||
1942 | while (i < zLine.Length && !isspace(zLine[i])) { i++; } |
||
1943 | azArg[nArg++] = zLine.ToString().Substring(i0, i - i0); |
||
1944 | //if (i < zLine.Length - 1) |
||
1945 | // zLine[i++] = '\0'; |
||
1946 | resolve_backslashes(ref azArg[nArg - 1]); |
||
1947 | } |
||
1948 | } |
||
1949 | |||
1950 | /* Process the input line. |
||
1951 | */ |
||
1952 | if (nArg == 0) |
||
1953 | return 0; /* no tokens, no error */ |
||
1954 | n = strlen30(azArg[0]); |
||
1955 | c = azArg[0][0]; |
||
1956 | if (c == 'b' && n >= 3 && azArg[0].StartsWith("backup", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 4) |
||
1957 | { |
||
1958 | string zDestFile; |
||
1959 | string zDb; |
||
1960 | sqlite3 pDest; |
||
1961 | Sqlite3.sqlite3_backup pBackup; |
||
1962 | if (nArg == 2) |
||
1963 | { |
||
1964 | zDestFile = azArg[1]; |
||
1965 | zDb = "main"; |
||
1966 | } |
||
1967 | else |
||
1968 | { |
||
1969 | zDestFile = azArg[2]; |
||
1970 | zDb = azArg[1]; |
||
1971 | } |
||
1972 | rc = Sqlite3.sqlite3_open(zDestFile, out pDest); |
||
1973 | if (rc != Sqlite3.SQLITE_OK) |
||
1974 | { |
||
1975 | fprintf(stderr, "Error: cannot open \"%s\"\n", zDestFile); |
||
1976 | Sqlite3.sqlite3_close(pDest); |
||
1977 | return 1; |
||
1978 | } |
||
1979 | open_db(p); |
||
1980 | pBackup = Sqlite3.sqlite3_backup_init(pDest, "main", p.db, zDb); |
||
1981 | if (pBackup == null) |
||
1982 | { |
||
1983 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(pDest)); |
||
1984 | Sqlite3.sqlite3_close(pDest); |
||
1985 | return 1; |
||
1986 | } |
||
1987 | while ((rc = Sqlite3.sqlite3_backup_step(pBackup, 100)) == Sqlite3.SQLITE_OK) { } |
||
1988 | Sqlite3.sqlite3_backup_finish(pBackup); |
||
1989 | if (rc == Sqlite3.SQLITE_DONE) |
||
1990 | { |
||
1991 | rc = 0; |
||
1992 | } |
||
1993 | else |
||
1994 | { |
||
1995 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(pDest)); |
||
1996 | rc = 1; |
||
1997 | } |
||
1998 | Sqlite3.sqlite3_close(pDest); |
||
1999 | } |
||
2000 | else |
||
2001 | |||
2002 | if (c == 'b' && n >= 3 && azArg[0].StartsWith("bail", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 3) |
||
2003 | { |
||
2004 | bail_on_error = booleanValue(azArg[1]); |
||
2005 | } |
||
2006 | else |
||
2007 | |||
2008 | if (c == 'd' && n > 1 && azArg[0].StartsWith("databases", StringComparison.InvariantCultureIgnoreCase) && nArg == 1) |
||
2009 | { |
||
2010 | callback_data data; |
||
2011 | string zErrMsg = null; |
||
2012 | open_db(p); |
||
2013 | data = p.Copy();// memcpy(data, p, sizeof(data)); |
||
2014 | data.showHeader = true; |
||
2015 | data.mode = MODE_Column; |
||
2016 | data.colWidth[0] = 3; |
||
2017 | data.colWidth[1] = 15; |
||
2018 | data.colWidth[2] = 58; |
||
2019 | data.cnt = 0; |
||
2020 | Sqlite3.sqlite3_exec(p.db, "PRAGMA database_list; ", callback, data, ref zErrMsg); |
||
2021 | if (!String.IsNullOrEmpty(zErrMsg)) |
||
2022 | { |
||
2023 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2024 | zErrMsg = null;//Sqlite3.sqlite3_free(zErrMsg); |
||
2025 | rc = 1; |
||
2026 | } |
||
2027 | } |
||
2028 | else |
||
2029 | |||
2030 | if (c == 'd' && azArg[0].StartsWith("dump", StringComparison.InvariantCultureIgnoreCase) && nArg < 3) |
||
2031 | { |
||
2032 | string zErrMsg = null; |
||
2033 | open_db(p); |
||
2034 | /* When playing back a "dump", the content might appear in an order |
||
2035 | ** which causes immediate foreign key constraints to be violated. |
||
2036 | ** So disable foreign-key constraint enforcement to prevent problems. */ |
||
2037 | fprintf(p.Out, "PRAGMA foreign_keys=OFF;\n"); |
||
2038 | fprintf(p.Out, "BEGIN TRANSACTION;\n"); |
||
2039 | p.writableSchema = false; |
||
2040 | Sqlite3.sqlite3_exec(p.db, "PRAGMA writable_schema=ON", 0, 0, 0); |
||
2041 | if (nArg == 1) |
||
2042 | { |
||
2043 | run_schema_dump_query(p, |
||
2044 | "SELECT name, type, sql FROM sqlite_master " + |
||
2045 | "WHERE sql NOT null AND type=='table' AND name!='Sqlite3.SQLITE_sequence'", null |
||
2046 | ); |
||
2047 | run_schema_dump_query(p, |
||
2048 | "SELECT name, type, sql FROM sqlite_master " + |
||
2049 | "WHERE name=='Sqlite3.SQLITE_sequence'", null |
||
2050 | ); |
||
2051 | run_table_dump_query(p.Out, p.db, |
||
2052 | "SELECT sql FROM sqlite_master " + |
||
2053 | "WHERE sql NOT null AND type IN ('index','trigger','view')", null |
||
2054 | ); |
||
2055 | } |
||
2056 | else |
||
2057 | { |
||
2058 | int ii; |
||
2059 | for (ii = 1; ii < nArg; ii++) |
||
2060 | { |
||
2061 | zShellStatic = azArg[ii]; |
||
2062 | run_schema_dump_query(p, |
||
2063 | "SELECT name, type, sql FROM sqlite_master " + |
||
2064 | "WHERE tbl_name LIKE shellstatic() AND type=='table'" + |
||
2065 | " AND sql NOT null", null); |
||
2066 | run_table_dump_query(p.Out, p.db, |
||
2067 | "SELECT sql FROM sqlite_master " + |
||
2068 | "WHERE sql NOT null" + |
||
2069 | " AND type IN ('index','trigger','view')" + |
||
2070 | " AND tbl_name LIKE shellstatic()", null |
||
2071 | ); |
||
2072 | zShellStatic = null; |
||
2073 | } |
||
2074 | } |
||
2075 | if (p.writableSchema) |
||
2076 | { |
||
2077 | fprintf(p.Out, "PRAGMA writable_schema=OFF;\n"); |
||
2078 | p.writableSchema = false; |
||
2079 | } |
||
2080 | Sqlite3.sqlite3_exec(p.db, "PRAGMA writable_schema=OFF", 0, 0, 0); |
||
2081 | if (!String.IsNullOrEmpty(zErrMsg)) |
||
2082 | { |
||
2083 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2084 | zErrMsg = null;//Sqlite3.sqlite3_free(zErrMsg); |
||
2085 | } |
||
2086 | else |
||
2087 | { |
||
2088 | fprintf(p.Out, "COMMIT;\n"); |
||
2089 | } |
||
2090 | } |
||
2091 | else |
||
2092 | |||
2093 | if (c == 'e' && azArg[0].StartsWith("echo", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 3) |
||
2094 | { |
||
2095 | p.echoOn = booleanValue(azArg[1]); |
||
2096 | } |
||
2097 | else |
||
2098 | |||
2099 | if (c == 'e' && azArg[0].StartsWith("exit", StringComparison.InvariantCultureIgnoreCase) && nArg == 1) |
||
2100 | { |
||
2101 | rc = 2; |
||
2102 | } |
||
2103 | else |
||
2104 | |||
2105 | if (c == 'e' && azArg[0].StartsWith("explain", StringComparison.InvariantCultureIgnoreCase) && nArg < 3) |
||
2106 | { |
||
2107 | int val = nArg >= 2 ? booleanValue(azArg[1]) ? 1 : 0 : 1; |
||
2108 | if (val == 1) |
||
2109 | { |
||
2110 | if (!p.explainPrev.valid) |
||
2111 | { |
||
2112 | p.explainPrev.valid = true; |
||
2113 | p.explainPrev.mode = p.mode; |
||
2114 | p.explainPrev.showHeader = p.showHeader; |
||
2115 | p.explainPrev.colWidth = new int[p.colWidth.Length]; |
||
2116 | Array.Copy(p.colWidth, p.explainPrev.colWidth, p.colWidth.Length);//memcpy( p.explainPrev.colWidth, p.colWidth, sizeof( p.colWidth ) ); |
||
2117 | } |
||
2118 | /* We could put this code under the !p.explainValid |
||
2119 | ** condition so that it does not execute if we are already in |
||
2120 | ** explain mode. However, always executing it allows us an easy |
||
2121 | ** was to reset to explain mode in case the user previously |
||
2122 | ** did an .explain followed by a .width, .mode or .header |
||
2123 | ** command. |
||
2124 | */ |
||
2125 | p.mode = MODE_Explain; |
||
2126 | p.showHeader = true; |
||
2127 | Array.Clear(p.colWidth, 0, p.colWidth.Length);//memset(p.colWidth, 0, ArraySize(p.colWidth)); |
||
2128 | p.colWidth[0] = 4; /* addr */ |
||
2129 | p.colWidth[1] = 13; /* opcode */ |
||
2130 | p.colWidth[2] = 4; /* P1 */ |
||
2131 | p.colWidth[3] = 4; /* P2 */ |
||
2132 | p.colWidth[4] = 4; /* P3 */ |
||
2133 | p.colWidth[5] = 13; /* P4 */ |
||
2134 | p.colWidth[6] = 2; /* P5 */ |
||
2135 | p.colWidth[7] = 13; /* Comment */ |
||
2136 | } |
||
2137 | else if (p.explainPrev.valid) |
||
2138 | { |
||
2139 | p.explainPrev.valid = false; |
||
2140 | p.mode = p.explainPrev.mode; |
||
2141 | p.showHeader = p.explainPrev.showHeader; |
||
2142 | Array.Copy(p.colWidth, p.explainPrev.colWidth, p.colWidth.Length);//memcpy(p.colWidth, p.explainPrev.colWidth, sizeof(p.colWidth)); |
||
2143 | } |
||
2144 | } |
||
2145 | else |
||
2146 | |||
2147 | if (c == 'h' && (azArg[0].StartsWith("header", StringComparison.InvariantCultureIgnoreCase) || |
||
2148 | azArg[0].StartsWith("headers", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 3)) |
||
2149 | { |
||
2150 | p.showHeader = booleanValue(azArg[1]); |
||
2151 | } |
||
2152 | else |
||
2153 | |||
2154 | if (c == 'h' && azArg[0].StartsWith("help", StringComparison.InvariantCultureIgnoreCase)) |
||
2155 | { |
||
2156 | fprintf(stderr, "%s", zHelp); |
||
2157 | if (HAS_TIMER) |
||
2158 | { |
||
2159 | fprintf(stderr, "%s", zTimerHelp); |
||
2160 | } |
||
2161 | } |
||
2162 | else |
||
2163 | |||
2164 | if (c == 'i' && azArg[0].StartsWith("import", StringComparison.InvariantCultureIgnoreCase) && nArg == 3) |
||
2165 | { |
||
2166 | string zTable = azArg[2]; /* Insert data into this table */ |
||
2167 | string zFile = azArg[1]; /* The file from which to extract data */ |
||
2168 | sqlite3_stmt pStmt = null; /* A statement */ |
||
2169 | int nCol; /* Number of columns in the table */ |
||
2170 | int nByte; /* Number of bytes in an SQL string */ |
||
2171 | int ii, j; /* Loop counters */ |
||
2172 | int nSep; /* Number of bytes in p.separator[] */ |
||
2173 | StringBuilder zSql; /* An SQL statement */ |
||
2174 | //string zLine; /* A single line of input from the file */ |
||
2175 | string[] azCol; /* zLine[] broken up into columns */ |
||
2176 | string zCommit; /* How to commit changes */ |
||
2177 | TextReader In; /* The input file */ |
||
2178 | int lineno = 0; /* Line number of input file */ |
||
2179 | |||
2180 | open_db(p); |
||
2181 | nSep = strlen30(p.separator); |
||
2182 | if (nSep == 0) |
||
2183 | { |
||
2184 | fprintf(stderr, "Error: non-null separator required for import\n"); |
||
2185 | return 1; |
||
2186 | } |
||
2187 | zSql = new StringBuilder(Sqlite3.sqlite3_mprintf("SELECT * FROM '%q'", zTable)); |
||
2188 | //if (zSql == null) |
||
2189 | //{ |
||
2190 | // fprintf(stderr, "Error: out of memory\n"); |
||
2191 | // return 1; |
||
2192 | //} |
||
2193 | nByte = strlen30(zSql); |
||
2194 | rc = Sqlite3.sqlite3_prepare(p.db, zSql, -1, ref pStmt, 0); |
||
2195 | zSql = null;//Sqlite3.sqlite3_free(zSql); |
||
2196 | if (rc != 0) |
||
2197 | { |
||
2198 | if (pStmt != null) |
||
2199 | Sqlite3.sqlite3_finalize(pStmt); |
||
2200 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(db)); |
||
2201 | return 1; |
||
2202 | } |
||
2203 | nCol = Sqlite3.sqlite3_column_count(pStmt); |
||
2204 | Sqlite3.sqlite3_finalize(pStmt); |
||
2205 | pStmt = null; |
||
2206 | if (nCol == 0) |
||
2207 | return 0; /* no columns, no error */ |
||
2208 | zSql = new StringBuilder(nByte + 20 + nCol * 2); |
||
2209 | //if (zSql == null) |
||
2210 | //{ |
||
2211 | // fprintf(stderr, "Error: out of memory\n"); |
||
2212 | // return 1; |
||
2213 | //} |
||
2214 | Sqlite3.sqlite3_snprintf(nByte + 20, zSql, "INSERT INTO '%q' VALUES(?", zTable); |
||
2215 | j = strlen30(zSql); |
||
2216 | for (ii = 1; ii < nCol; ii++) |
||
2217 | { |
||
2218 | zSql[j++] = ','; |
||
2219 | zSql[j++] = '?'; |
||
2220 | } |
||
2221 | zSql[j++] = ')'; |
||
2222 | zSql[j] = '\0'; |
||
2223 | rc = Sqlite3.sqlite3_prepare(p.db, zSql, -1, ref pStmt, 0); |
||
2224 | zSql = null;//free(zSql); |
||
2225 | if (rc != 0) |
||
2226 | { |
||
2227 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(db)); |
||
2228 | if (pStmt != null) |
||
2229 | Sqlite3.sqlite3_finalize(pStmt); |
||
2230 | return 1; |
||
2231 | } |
||
2232 | In = new StreamReader(zFile);// fopen(zFile, "rb"); |
||
2233 | if (In == null) |
||
2234 | { |
||
2235 | fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
||
2236 | Sqlite3.sqlite3_finalize(pStmt); |
||
2237 | return 1; |
||
2238 | } |
||
2239 | azCol = new string[nCol + 1];//malloc( sizeof(azCol[0])*(nCol+1) ); |
||
2240 | //if( azCol== null ){ |
||
2241 | //fprintf(stderr, "Error: out of memory\n"); |
||
2242 | //fclose(In ); |
||
2243 | //Sqlite3.sqlite3_finalize(pStmt!=null); |
||
2244 | //return 1; |
||
2245 | //} |
||
2246 | Sqlite3.sqlite3_exec(p.db, "BEGIN", 0, 0, 0); |
||
2247 | zCommit = "COMMIT"; |
||
2248 | while ((zLine.Append(local_getline(null, In).ToString())).Length != 0) |
||
2249 | { |
||
2250 | string z; |
||
2251 | //i = 0; |
||
2252 | lineno++; |
||
2253 | azCol = zLine.ToString().Split(p.separator.ToCharArray()); |
||
2254 | //azCol[0] = zLine; |
||
2255 | //for (i = 0, z = zLine; *z && *z != '\n' && *z != '\r'; z++) |
||
2256 | //{ |
||
2257 | // if (*z == p.separator[0] && z.StartsWith(p.separator)) |
||
2258 | // { |
||
2259 | // *z = 0; |
||
2260 | // i++; |
||
2261 | // if (i < nCol) |
||
2262 | // { |
||
2263 | // azCol[i] = z[nSep]; |
||
2264 | // z += nSep - 1; |
||
2265 | // } |
||
2266 | // } |
||
2267 | //} /* end for */ |
||
2268 | //*z = 0; |
||
2269 | if (azCol.Length != nCol) |
||
2270 | { |
||
2271 | fprintf(stderr, |
||
2272 | "Error: %s line %d: expected %d columns of data but found %d\n", |
||
2273 | zFile, lineno, nCol, azCol.Length); |
||
2274 | zCommit = "ROLLBACK"; |
||
2275 | zLine = null;//free(zLine); |
||
2276 | rc = 1; |
||
2277 | break; /* from while */ |
||
2278 | } |
||
2279 | for (ii = 0; ii < nCol; ii++) |
||
2280 | { |
||
2281 | Sqlite3.sqlite3_bind_text(pStmt, ii + 1, azCol[ii], -1, Sqlite3.SQLITE_STATIC); |
||
2282 | } |
||
2283 | Sqlite3.sqlite3_step(pStmt); |
||
2284 | rc = Sqlite3.sqlite3_reset(pStmt); |
||
2285 | zLine = null;//free(zLine); |
||
2286 | if (rc != Sqlite3.SQLITE_OK) |
||
2287 | { |
||
2288 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(db)); |
||
2289 | zCommit = "ROLLBACK"; |
||
2290 | rc = 1; |
||
2291 | break; /* from while */ |
||
2292 | } |
||
2293 | } /* end while */ |
||
2294 | azCol = null;//free(azCol); |
||
2295 | In.Close();//fclose(in); |
||
2296 | Sqlite3.sqlite3_finalize(pStmt); |
||
2297 | Sqlite3.sqlite3_exec(p.db, zCommit, 0, 0, 0); |
||
2298 | } |
||
2299 | else |
||
2300 | |||
2301 | if (c == 'i' && azArg[0].StartsWith("indices", StringComparison.InvariantCultureIgnoreCase) && nArg < 3) |
||
2302 | { |
||
2303 | callback_data data; |
||
2304 | string zErrMsg = null; |
||
2305 | open_db(p); |
||
2306 | data = p.Copy();// memcpy(data, p, sizeof(data)); |
||
2307 | data.showHeader = false; |
||
2308 | data.mode = MODE_List; |
||
2309 | if (nArg == 1) |
||
2310 | { |
||
2311 | rc = Sqlite3.sqlite3_exec(p.db, |
||
2312 | "SELECT name FROM sqlite_master " + |
||
2313 | "WHERE type='index' AND name NOT LIKE 'Sqlite3.SQLITE_%' " + |
||
2314 | "UNION ALL " + |
||
2315 | "SELECT name FROM sqlite_temp_master " + |
||
2316 | "WHERE type='index' " + |
||
2317 | "ORDER BY 1", |
||
2318 | callback, data, ref zErrMsg |
||
2319 | ); |
||
2320 | } |
||
2321 | else |
||
2322 | { |
||
2323 | zShellStatic = azArg[1]; |
||
2324 | rc = Sqlite3.sqlite3_exec(p.db, |
||
2325 | "SELECT name FROM sqlite_master " + |
||
2326 | "WHERE type='index' AND tbl_name LIKE shellstatic() " + |
||
2327 | "UNION ALL " + |
||
2328 | "SELECT name FROM sqlite_temp_master " + |
||
2329 | "WHERE type='index' AND tbl_name LIKE shellstatic() " + |
||
2330 | "ORDER BY 1", |
||
2331 | callback, data, ref zErrMsg |
||
2332 | ); |
||
2333 | zShellStatic = null; |
||
2334 | } |
||
2335 | if (!String.IsNullOrEmpty(zErrMsg)) |
||
2336 | { |
||
2337 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2338 | zErrMsg = null;//Sqlite3.sqlite3_free(zErrMsg); |
||
2339 | rc = 1; |
||
2340 | } |
||
2341 | else if (rc != Sqlite3.SQLITE_OK) |
||
2342 | { |
||
2343 | fprintf(stderr, "Error: querying Sqlite3.SQLITE_master and Sqlite3.SQLITE_temp_master\n"); |
||
2344 | rc = 1; |
||
2345 | } |
||
2346 | } |
||
2347 | else |
||
2348 | |||
2349 | #if SQLITE_ENABLE_IOTRACE |
||
2350 | if( c=='i' && "iotrace", n)== null ){ |
||
2351 | extern void (*sqlite3IoTrace)(const char*, ...); |
||
2352 | if( iotrace && iotrace!=stdout ) iotrace.Close();//fclose(iotrace); |
||
2353 | iotrace = null; |
||
2354 | if( nArg<2 ){ |
||
2355 | sqlite3IoTrace = 0; |
||
2356 | }else if( azArg[1].Equals("-") ){ |
||
2357 | sqlite3IoTrace = iotracePrintf; |
||
2358 | iotrace = stdout; |
||
2359 | }else{ |
||
2360 | iotrace = new StreamWriter(azArg[1].ToString());// fopen(azArg[1], "w"); |
||
2361 | if( iotrace== null ){ |
||
2362 | fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); |
||
2363 | sqlite3IoTrace = 0; |
||
2364 | rc = 1; |
||
2365 | }else{ |
||
2366 | sqlite3IoTrace = iotracePrintf; |
||
2367 | } |
||
2368 | } |
||
2369 | }else |
||
2370 | #endif |
||
2371 | |||
2372 | #if !SQLITE_OMIT_LOAD_EXTENSION |
||
2373 | if (c == 'l' && azArg[0].StartsWith("load", StringComparison.InvariantCultureIgnoreCase) && nArg >= 2) |
||
2374 | { |
||
2375 | string zFile, zProc; |
||
2376 | string zErrMsg = null; |
||
2377 | zFile = azArg[1]; |
||
2378 | zProc = nArg >= 3 ? azArg[2] : null; |
||
2379 | open_db(p); |
||
2380 | rc = Sqlite3.sqlite3_load_extension(p.db, zFile, zProc, ref zErrMsg); |
||
2381 | if (rc != Sqlite3.SQLITE_OK) |
||
2382 | { |
||
2383 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2384 | zErrMsg = null;//Sqlite3.sqlite3_free( zErrMsg ); |
||
2385 | rc = 1; |
||
2386 | } |
||
2387 | } |
||
2388 | else |
||
2389 | #endif |
||
2390 | |||
2391 | if (c == 'l' && azArg[0].StartsWith("log", StringComparison.InvariantCultureIgnoreCase) && nArg >= 2) |
||
2392 | { |
||
2393 | string zFile = azArg[1]; |
||
2394 | if (p.pLog != null && p.pLog != stdout && p.pLog != stderr) |
||
2395 | { |
||
2396 | p.pLog.Close();//fclose(p.pLog); |
||
2397 | p.pLog = null; |
||
2398 | } |
||
2399 | if (zFile.Equals("stdout", StringComparison.InvariantCultureIgnoreCase)) |
||
2400 | { |
||
2401 | p.pLog = stdout; |
||
2402 | } |
||
2403 | else if (zFile.Equals("stderr", StringComparison.InvariantCultureIgnoreCase)) |
||
2404 | { |
||
2405 | p.pLog = stderr; |
||
2406 | } |
||
2407 | else if (zFile.Equals("off", StringComparison.InvariantCultureIgnoreCase)) |
||
2408 | { |
||
2409 | p.pLog = null; |
||
2410 | } |
||
2411 | else |
||
2412 | { |
||
2413 | p.pLog = new StreamWriter(zFile);// fopen(zFile, "w"); |
||
2414 | if (p.pLog == null) |
||
2415 | { |
||
2416 | fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); |
||
2417 | } |
||
2418 | } |
||
2419 | } |
||
2420 | else |
||
2421 | |||
2422 | if (c == 'm' && azArg[0].StartsWith("mode", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2423 | { |
||
2424 | int n2 = strlen30(azArg[1]); |
||
2425 | if ((n2 == 4 && azArg[1].StartsWith("line", StringComparison.InvariantCultureIgnoreCase)) |
||
2426 | || |
||
2427 | (n2 == 5 && azArg[1].StartsWith("lines", StringComparison.InvariantCultureIgnoreCase))) |
||
2428 | { |
||
2429 | p.mode = MODE_Line; |
||
2430 | } |
||
2431 | else if ((n2 == 6 && azArg[1].StartsWith("column", StringComparison.InvariantCultureIgnoreCase)) |
||
2432 | || |
||
2433 | (n2 == 7 && azArg[1].StartsWith("columns", StringComparison.InvariantCultureIgnoreCase))) |
||
2434 | { |
||
2435 | p.mode = MODE_Column; |
||
2436 | } |
||
2437 | else if (n2 == 4 && azArg[1].StartsWith("list", StringComparison.InvariantCultureIgnoreCase)) |
||
2438 | { |
||
2439 | p.mode = MODE_List; |
||
2440 | } |
||
2441 | else if (n2 == 4 && azArg[1].StartsWith("html", StringComparison.InvariantCultureIgnoreCase)) |
||
2442 | { |
||
2443 | p.mode = MODE_Html; |
||
2444 | } |
||
2445 | else if (n2 == 3 && azArg[1].StartsWith("tcl", StringComparison.InvariantCultureIgnoreCase)) |
||
2446 | { |
||
2447 | p.mode = MODE_Tcl; |
||
2448 | } |
||
2449 | else if (n2 == 3 && azArg[1].StartsWith("csv", StringComparison.InvariantCultureIgnoreCase)) |
||
2450 | { |
||
2451 | p.mode = MODE_Csv; |
||
2452 | snprintf(2, ref p.separator, ","); |
||
2453 | } |
||
2454 | else if (n2 == 4 && azArg[1].StartsWith("tabs", StringComparison.InvariantCultureIgnoreCase)) |
||
2455 | { |
||
2456 | p.mode = MODE_List; |
||
2457 | snprintf(2, ref p.separator, "\t"); |
||
2458 | } |
||
2459 | else if (n2 == 6 && azArg[1].StartsWith("insert", StringComparison.InvariantCultureIgnoreCase)) |
||
2460 | { |
||
2461 | p.mode = MODE_Insert; |
||
2462 | set_table_name(p, "table"); |
||
2463 | } |
||
2464 | else |
||
2465 | { |
||
2466 | fprintf(stderr, "Error: mode should be one of: " + |
||
2467 | "column csv html insert line list tabs tcl\n"); |
||
2468 | rc = 1; |
||
2469 | } |
||
2470 | } |
||
2471 | else |
||
2472 | |||
2473 | if (c == 'm' && azArg[0].StartsWith("mode", StringComparison.InvariantCultureIgnoreCase) && nArg == 3) |
||
2474 | { |
||
2475 | int n2 = strlen30(azArg[1]); |
||
2476 | if (n2 == 6 && azArg[1].StartsWith("insert", StringComparison.InvariantCultureIgnoreCase)) |
||
2477 | { |
||
2478 | p.mode = MODE_Insert; |
||
2479 | set_table_name(p, azArg[2]); |
||
2480 | } |
||
2481 | else |
||
2482 | { |
||
2483 | fprintf(stderr, "Error: invalid arguments: " + |
||
2484 | " \"%s\". Enter \".help\" for help\n", azArg[2]); |
||
2485 | rc = 1; |
||
2486 | } |
||
2487 | } |
||
2488 | else |
||
2489 | |||
2490 | if (c == 'n' && azArg[0].StartsWith("nullvalue", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2491 | { |
||
2492 | snprintf(9, ref p.nullvalue, |
||
2493 | "%.*s", (int)p.nullvalue.Length - 1, azArg[1]); |
||
2494 | } |
||
2495 | else |
||
2496 | |||
2497 | if (c == 'o' && azArg[0].StartsWith("output", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2498 | { |
||
2499 | if (p.Out != stdout) |
||
2500 | { |
||
2501 | p.Out.Close();//fclose(p.Out); |
||
2502 | } |
||
2503 | if (azArg[1].Equals("stdout", StringComparison.InvariantCultureIgnoreCase)) |
||
2504 | { |
||
2505 | p.Out = stdout; |
||
2506 | Sqlite3.sqlite3_snprintf(p.outfile.Capacity, p.outfile, "stdout"); |
||
2507 | } |
||
2508 | else |
||
2509 | { |
||
2510 | p.Out = new StreamWriter(azArg[1].ToString());// fopen(azArg[1], "wb"); |
||
2511 | if (p.Out == null) |
||
2512 | { |
||
2513 | fprintf(stderr, "Error: cannot write to \"%s\"\n", azArg[1]); |
||
2514 | p.Out = stdout; |
||
2515 | rc = 1; |
||
2516 | } |
||
2517 | else |
||
2518 | { |
||
2519 | Sqlite3.sqlite3_snprintf(p.outfile.Capacity, p.outfile, "%s", azArg[1]); |
||
2520 | } |
||
2521 | } |
||
2522 | } |
||
2523 | else |
||
2524 | |||
2525 | if (c == 'p' && azArg[0].StartsWith("prompt", StringComparison.InvariantCultureIgnoreCase) && (nArg == 2 || nArg == 3)) |
||
2526 | { |
||
2527 | if (nArg >= 2) |
||
2528 | { |
||
2529 | mainPrompt = azArg[1].ToString();//strncpy(mainPrompt, azArg[1], (int)ArraySize(mainPrompt) - 1); |
||
2530 | } |
||
2531 | if (nArg >= 3) |
||
2532 | { |
||
2533 | continuePrompt = azArg[1].ToString();//strncpy(continuePrompt, azArg[2], (int)ArraySize(continuePrompt) - 1); |
||
2534 | } |
||
2535 | } |
||
2536 | else |
||
2537 | |||
2538 | if (c == 'q' && azArg[0].StartsWith("quit", StringComparison.InvariantCultureIgnoreCase) && nArg == 1) |
||
2539 | { |
||
2540 | rc = 2; |
||
2541 | } |
||
2542 | else |
||
2543 | |||
2544 | if (c == 'r' && n >= 3 && azArg[0].StartsWith("read", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2545 | { |
||
2546 | StreamReader alt = new StreamReader(azArg[1].ToString());// fopen(azArg[1], "rb"); |
||
2547 | if (alt == null) |
||
2548 | { |
||
2549 | fprintf(stderr, "Error: cannot open \"%s\"\n", azArg[1]); |
||
2550 | rc = 1; |
||
2551 | } |
||
2552 | else |
||
2553 | { |
||
2554 | rc = process_input(p, alt); |
||
2555 | alt.Close();//fclose(alt); |
||
2556 | } |
||
2557 | } |
||
2558 | else |
||
2559 | |||
2560 | if (c == 'r' && n >= 3 && azArg[0].StartsWith("restore", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 4) |
||
2561 | { |
||
2562 | string zSrcFile; |
||
2563 | string zDb; |
||
2564 | sqlite3 pSrc; |
||
2565 | Sqlite3.sqlite3_backup pBackup; |
||
2566 | int nTimeout = 0; |
||
2567 | |||
2568 | if (nArg == 2) |
||
2569 | { |
||
2570 | zSrcFile = azArg[1]; |
||
2571 | zDb = "main"; |
||
2572 | } |
||
2573 | else |
||
2574 | { |
||
2575 | zSrcFile = azArg[2]; |
||
2576 | zDb = azArg[1]; |
||
2577 | } |
||
2578 | rc = Sqlite3.sqlite3_open(zSrcFile, out pSrc); |
||
2579 | if (rc != Sqlite3.SQLITE_OK) |
||
2580 | { |
||
2581 | fprintf(stderr, "Error: cannot open \"%s\"\n", zSrcFile); |
||
2582 | Sqlite3.sqlite3_close(pSrc); |
||
2583 | return 1; |
||
2584 | } |
||
2585 | open_db(p); |
||
2586 | pBackup = Sqlite3.sqlite3_backup_init(p.db, zDb, pSrc, "main"); |
||
2587 | if (pBackup == null) |
||
2588 | { |
||
2589 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(p.db)); |
||
2590 | Sqlite3.sqlite3_close(pSrc); |
||
2591 | return 1; |
||
2592 | } |
||
2593 | while ((rc = Sqlite3.sqlite3_backup_step(pBackup, 100)) == Sqlite3.SQLITE_OK |
||
2594 | || rc == Sqlite3.SQLITE_BUSY) |
||
2595 | { |
||
2596 | if (rc == Sqlite3.SQLITE_BUSY) |
||
2597 | { |
||
2598 | if (nTimeout++ >= 3) |
||
2599 | break; |
||
2600 | Sqlite3.sqlite3_sleep(100); |
||
2601 | } |
||
2602 | } |
||
2603 | Sqlite3.sqlite3_backup_finish(pBackup); |
||
2604 | if (rc == Sqlite3.SQLITE_DONE) |
||
2605 | { |
||
2606 | rc = 0; |
||
2607 | } |
||
2608 | else if (rc == Sqlite3.SQLITE_BUSY || rc == Sqlite3.SQLITE_LOCKED) |
||
2609 | { |
||
2610 | fprintf(stderr, "Error: source database is busy\n"); |
||
2611 | rc = 1; |
||
2612 | } |
||
2613 | else |
||
2614 | { |
||
2615 | fprintf(stderr, "Error: %s\n", Sqlite3.sqlite3_errmsg(p.db)); |
||
2616 | rc = 1; |
||
2617 | } |
||
2618 | Sqlite3.sqlite3_close(pSrc); |
||
2619 | } |
||
2620 | else |
||
2621 | |||
2622 | if (c == 's' && azArg[0].StartsWith("schema", StringComparison.InvariantCultureIgnoreCase) && nArg < 3) |
||
2623 | { |
||
2624 | callback_data data; |
||
2625 | string zErrMsg = null; |
||
2626 | open_db(p); |
||
2627 | data = p.Copy();// memcpy(data, p, sizeof(data)); |
||
2628 | data.showHeader = false; |
||
2629 | data.mode = MODE_Semi; |
||
2630 | if (nArg > 1) |
||
2631 | { |
||
2632 | //int i; |
||
2633 | //for (i = 0; azArg[1][i]; i++) |
||
2634 | // azArg[1][i] = (char)tolower(azArg[1][i]); |
||
2635 | azArg[1] = azArg[1].ToLower(); |
||
2636 | if (azArg[1].Equals("sqlite_master", StringComparison.InvariantCultureIgnoreCase)) |
||
2637 | { |
||
2638 | string[] new_argv = new string[2]; |
||
2639 | string[] new_colv = new string[2]; |
||
2640 | new_argv[0] = "CREATE TABLE Sqlite3.SQLITE_master (\n" + |
||
2641 | " type text,\n" + |
||
2642 | " name text,\n" + |
||
2643 | " tbl_name text,\n" + |
||
2644 | " rootpage integer,\n" + |
||
2645 | " sql text\n" + |
||
2646 | ")"; |
||
2647 | new_argv[1] = null; |
||
2648 | new_colv[0] = "sql"; |
||
2649 | new_colv[1] = null; |
||
2650 | callback(data, 1, new_argv, new_colv); |
||
2651 | rc = Sqlite3.SQLITE_OK; |
||
2652 | } |
||
2653 | else if (azArg[1].Equals("sqlite_temp_master", StringComparison.InvariantCultureIgnoreCase)) |
||
2654 | { |
||
2655 | string[] new_argv = new string[2]; |
||
2656 | string[] new_colv = new string[2]; |
||
2657 | new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master(\n" + |
||
2658 | " type text,\n" + |
||
2659 | " name text,\n" + |
||
2660 | " tbl_name text,\n" + |
||
2661 | " rootpage integer,\n" + |
||
2662 | " sql text\n" + |
||
2663 | ")"; |
||
2664 | new_argv[1] = null; |
||
2665 | new_colv[0] = "sql"; |
||
2666 | new_colv[1] = null; |
||
2667 | callback(data, 1, new_argv, new_colv); |
||
2668 | rc = Sqlite3.SQLITE_OK; |
||
2669 | } |
||
2670 | else |
||
2671 | { |
||
2672 | zShellStatic = azArg[1]; |
||
2673 | rc = Sqlite3.sqlite3_exec(p.db, |
||
2674 | "SELECT sql FROM " + |
||
2675 | " (SELECT sql sql, type type, tbl_name tbl_name, name name" + |
||
2676 | " FROM sqlite_master UNION ALL" + |
||
2677 | " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " + |
||
2678 | "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTnull " + |
||
2679 | "ORDER BY substr(type,2,1), name", |
||
2680 | callback, data, ref zErrMsg); |
||
2681 | zShellStatic = null; |
||
2682 | } |
||
2683 | } |
||
2684 | else |
||
2685 | { |
||
2686 | rc = Sqlite3.sqlite3_exec(p.db, |
||
2687 | "SELECT sql FROM " + |
||
2688 | " (SELECT sql sql, type type, tbl_name tbl_name, name name" + |
||
2689 | " FROM sqlite_master UNION ALL" + |
||
2690 | " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " + |
||
2691 | "WHERE type!='meta' AND sql NOTnull AND name NOT LIKE 'Sqlite3.SQLITE_%'" + |
||
2692 | "ORDER BY substr(type,2,1), name", |
||
2693 | callback, data, ref zErrMsg |
||
2694 | ); |
||
2695 | } |
||
2696 | if (!String.IsNullOrEmpty(zErrMsg)) |
||
2697 | { |
||
2698 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2699 | zErrMsg = null;//Sqlite3.sqlite3_free(zErrMsg); |
||
2700 | rc = 1; |
||
2701 | } |
||
2702 | else if (rc != Sqlite3.SQLITE_OK) |
||
2703 | { |
||
2704 | fprintf(stderr, "Error: querying schema information\n"); |
||
2705 | rc = 1; |
||
2706 | } |
||
2707 | else |
||
2708 | { |
||
2709 | rc = 0; |
||
2710 | } |
||
2711 | } |
||
2712 | else |
||
2713 | |||
2714 | if (c == 's' && azArg[0].StartsWith("separator", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2715 | { |
||
2716 | snprintf(2, ref p.separator, "%.*s", 2 - 1, azArg[1]); |
||
2717 | } |
||
2718 | else |
||
2719 | |||
2720 | if (c == 's' && azArg[0].StartsWith("show", StringComparison.InvariantCultureIgnoreCase) && nArg == 1) |
||
2721 | { |
||
2722 | int ii; |
||
2723 | fprintf(p.Out, "%9.9s: %s\n", "echo", p.echoOn ? "on" : "off"); |
||
2724 | fprintf(p.Out, "%9.9s: %s\n", "explain", p.explainPrev.valid ? "on" : "off"); |
||
2725 | fprintf(p.Out, "%9.9s: %s\n", "headers", p.showHeader ? "on" : "off"); |
||
2726 | fprintf(p.Out, "%9.9s: %s\n", "mode", modeDescr[p.mode]); |
||
2727 | fprintf(p.Out, "%9.9s: ", "nullvalue"); |
||
2728 | output_c_string(p.Out, p.nullvalue); |
||
2729 | fprintf(p.Out, "\n"); |
||
2730 | fprintf(p.Out, "%9.9s: %s\n", "output", |
||
2731 | strlen30(p.outfile) != 0 ? p.outfile.ToString() : "stdout"); |
||
2732 | fprintf(p.Out, "%9.9s: ", "separator"); |
||
2733 | output_c_string(p.Out, p.separator); |
||
2734 | fprintf(p.Out, "\n"); |
||
2735 | fprintf(p.Out, "%9.9s: %s\n", "stats", p.statsOn ? "on" : "off"); |
||
2736 | fprintf(p.Out, "%9.9s: ", "width"); |
||
2737 | for (ii = 0; ii < (int)ArraySize(p.colWidth) && p.colWidth[ii] != 0; ii++) |
||
2738 | { |
||
2739 | fprintf(p.Out, "%d ", p.colWidth[ii]); |
||
2740 | } |
||
2741 | fprintf(p.Out, "\n"); |
||
2742 | } |
||
2743 | else |
||
2744 | |||
2745 | if (c == 's' && azArg[0].StartsWith("stats", StringComparison.InvariantCultureIgnoreCase) && nArg > 1 && nArg < 3) |
||
2746 | { |
||
2747 | p.statsOn = booleanValue(azArg[1]); |
||
2748 | } |
||
2749 | else |
||
2750 | |||
2751 | if (c == 't' && n > 1 && azArg[0].StartsWith("tables", StringComparison.InvariantCultureIgnoreCase) && nArg < 3) |
||
2752 | { |
||
2753 | string[] azResult = null; |
||
2754 | int nRow = 0; |
||
2755 | string zErrMsg = null; |
||
2756 | open_db(p); |
||
2757 | if (nArg == 1) |
||
2758 | { |
||
2759 | rc = Sqlite3.sqlite3_get_table(p.db, |
||
2760 | "SELECT name FROM sqlite_master " + |
||
2761 | "WHERE type IN ('table','view') AND name NOT LIKE 'Sqlite3.SQLITE_%' " + |
||
2762 | "UNION ALL " + |
||
2763 | "SELECT name FROM sqlite_temp_master " + |
||
2764 | "WHERE type IN ('table','view') " + |
||
2765 | "ORDER BY 1", |
||
2766 | ref azResult, ref nRow, null, ref zErrMsg |
||
2767 | ); |
||
2768 | } |
||
2769 | else |
||
2770 | { |
||
2771 | zShellStatic = azArg[1]; |
||
2772 | rc = Sqlite3.sqlite3_get_table(p.db, |
||
2773 | "SELECT name FROM sqlite_master " + |
||
2774 | "WHERE type IN ('table','view') AND name LIKE shellstatic() " + |
||
2775 | "UNION ALL " + |
||
2776 | "SELECT name FROM sqlite_temp_master " + |
||
2777 | "WHERE type IN ('table','view') AND name LIKE shellstatic() " + |
||
2778 | "ORDER BY 1", |
||
2779 | ref azResult, ref nRow, null, ref zErrMsg |
||
2780 | ); |
||
2781 | zShellStatic = null; |
||
2782 | } |
||
2783 | if (!String.IsNullOrEmpty(zErrMsg)) |
||
2784 | { |
||
2785 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
2786 | zErrMsg = null;//Sqlite3.sqlite3_free(zErrMsg); |
||
2787 | rc = 1; |
||
2788 | } |
||
2789 | else if (rc != Sqlite3.SQLITE_OK) |
||
2790 | { |
||
2791 | fprintf(stderr, "Error: querying Sqlite3.SQLITE_master and Sqlite3.SQLITE_temp_master\n"); |
||
2792 | rc = 1; |
||
2793 | } |
||
2794 | else |
||
2795 | { |
||
2796 | int len, maxlen = 0; |
||
2797 | int ii, j; |
||
2798 | int nPrintCol, nPrintRow; |
||
2799 | for (ii = 1; ii <= nRow; ii++) |
||
2800 | { |
||
2801 | if (azResult[ii] == null) |
||
2802 | continue; |
||
2803 | len = strlen30(azResult[ii]); |
||
2804 | if (len > maxlen) |
||
2805 | maxlen = len; |
||
2806 | } |
||
2807 | nPrintCol = 80 / (maxlen + 2); |
||
2808 | if (nPrintCol < 1) |
||
2809 | nPrintCol = 1; |
||
2810 | nPrintRow = (nRow + nPrintCol - 1) / nPrintCol; |
||
2811 | for (ii = 0; ii < nPrintRow; ii++) |
||
2812 | { |
||
2813 | for (j = ii + 1; j <= nRow; j += nPrintRow) |
||
2814 | { |
||
2815 | string zSp = j <= nPrintRow ? "" : " "; |
||
2816 | printf("%s%-*s", zSp, maxlen, !String.IsNullOrEmpty(azResult[j]) ? azResult[j] : ""); |
||
2817 | } |
||
2818 | printf("\n"); |
||
2819 | } |
||
2820 | } |
||
2821 | Sqlite3.sqlite3_free_table(ref azResult); |
||
2822 | } |
||
2823 | else |
||
2824 | |||
2825 | if (c == 't' && n >= 8 && azArg[0].StartsWith("testctrl", StringComparison.InvariantCultureIgnoreCase) && nArg >= 2) |
||
2826 | { |
||
2827 | //static const struct { |
||
2828 | // string zCtrlName; /* Name of a test-control option */ |
||
2829 | // int ctrlCode; /* Integer code for that option */ |
||
2830 | //} |
||
2831 | _aCtrl[] aCtrl = new _aCtrl[] { |
||
2832 | new _aCtrl( "prng_save", Sqlite3.SQLITE_TESTCTRL_PRNG_SAVE ), |
||
2833 | new _aCtrl( "prng_restore", Sqlite3.SQLITE_TESTCTRL_PRNG_RESTORE ), |
||
2834 | new _aCtrl( "prng_reset", Sqlite3.SQLITE_TESTCTRL_PRNG_RESET ), |
||
2835 | new _aCtrl( "bitvec_test", Sqlite3.SQLITE_TESTCTRL_BITVEC_TEST ), |
||
2836 | new _aCtrl( "fault_install", Sqlite3.SQLITE_TESTCTRL_FAULT_INSTALL ), |
||
2837 | new _aCtrl( "benign_malloc_hooks", Sqlite3.SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS ), |
||
2838 | new _aCtrl( "pending_byte", Sqlite3.SQLITE_TESTCTRL_PENDING_BYTE ), |
||
2839 | new _aCtrl( "Debug.Assert", Sqlite3.SQLITE_TESTCTRL_ASSERT ), |
||
2840 | new _aCtrl( "always", Sqlite3.SQLITE_TESTCTRL_ALWAYS ), |
||
2841 | new _aCtrl( "reserve", Sqlite3.SQLITE_TESTCTRL_RESERVE ), |
||
2842 | new _aCtrl( "optimizations", Sqlite3.SQLITE_TESTCTRL_OPTIMIZATIONS ), |
||
2843 | new _aCtrl( "iskeyword", Sqlite3.SQLITE_TESTCTRL_ISKEYWORD ), |
||
2844 | new _aCtrl( "pghdrsz", Sqlite3.SQLITE_TESTCTRL_PGHDRSZ ), |
||
2845 | new _aCtrl( "scratchmalloc", Sqlite3.SQLITE_TESTCTRL_SCRATCHMALLOC ), |
||
2846 | }; |
||
2847 | int testctrl = -1; |
||
2848 | //int rc = 0; |
||
2849 | int ii;//, n; |
||
2850 | open_db(p); |
||
2851 | |||
2852 | /* convert testctrl text option to value. allow any unique prefix |
||
2853 | ** of the option name, or a numerical value. */ |
||
2854 | //n = strlen30(azArg[1]); |
||
2855 | for (ii = 0; ii < aCtrl.Length; ii++)//(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++) |
||
2856 | { |
||
2857 | if (aCtrl[ii].zCtrlName.StartsWith(azArg[1], StringComparison.InvariantCultureIgnoreCase)) |
||
2858 | { |
||
2859 | if (testctrl < 0) |
||
2860 | { |
||
2861 | testctrl = aCtrl[ii].ctrlCode; |
||
2862 | } |
||
2863 | else |
||
2864 | { |
||
2865 | fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[ii]); |
||
2866 | testctrl = -1; |
||
2867 | break; |
||
2868 | } |
||
2869 | } |
||
2870 | } |
||
2871 | if (testctrl < 0) |
||
2872 | testctrl = Convert.ToInt32(azArg[1]);//atoi |
||
2873 | if ((testctrl < Sqlite3.SQLITE_TESTCTRL_FIRST) || (testctrl > Sqlite3.SQLITE_TESTCTRL_LAST)) |
||
2874 | { |
||
2875 | fprintf(stderr, "Error: invalid testctrl option: %s\n", azArg[1]); |
||
2876 | } |
||
2877 | else |
||
2878 | { |
||
2879 | switch (testctrl) |
||
2880 | { |
||
2881 | |||
2882 | /* Sqlite3.sqlite3_test_control(int, db, Int) */ |
||
2883 | case Sqlite3.SQLITE_TESTCTRL_OPTIMIZATIONS: |
||
2884 | case Sqlite3.SQLITE_TESTCTRL_RESERVE: |
||
2885 | if (nArg == 3) |
||
2886 | { |
||
2887 | int opt = (int)System.Int64.Parse(azArg[2]); |
||
2888 | rc = Sqlite3.sqlite3_test_control(testctrl, p.db, opt); |
||
2889 | printf("%d (0x%08x)\n", rc, rc); |
||
2890 | } |
||
2891 | else |
||
2892 | { |
||
2893 | fprintf(stderr, "Error: testctrl %s takes a single int option\n", |
||
2894 | azArg[1]); |
||
2895 | } |
||
2896 | break; |
||
2897 | |||
2898 | /* Sqlite3.sqlite3_test_control(int) */ |
||
2899 | case Sqlite3.SQLITE_TESTCTRL_PRNG_SAVE: |
||
2900 | case Sqlite3.SQLITE_TESTCTRL_PRNG_RESTORE: |
||
2901 | case Sqlite3.SQLITE_TESTCTRL_PRNG_RESET: |
||
2902 | case Sqlite3.SQLITE_TESTCTRL_PGHDRSZ: |
||
2903 | if (nArg == 2) |
||
2904 | { |
||
2905 | rc = Sqlite3.sqlite3_test_control(testctrl); |
||
2906 | printf("%d (0x%08x)\n", rc, rc); |
||
2907 | } |
||
2908 | else |
||
2909 | { |
||
2910 | fprintf(stderr, "Error: testctrl %s takes no options\n", azArg[1]); |
||
2911 | } |
||
2912 | break; |
||
2913 | |||
2914 | /* Sqlite3.sqlite3_test_control(int, uint) */ |
||
2915 | case Sqlite3.SQLITE_TESTCTRL_PENDING_BYTE: |
||
2916 | if (nArg == 3) |
||
2917 | { |
||
2918 | u32 opt = (u32)Convert.ToInt32(azArg[2]);//atoi |
||
2919 | rc = Sqlite3.sqlite3_test_control(testctrl, opt); |
||
2920 | printf("%d (0x%08x)\n", rc, rc); |
||
2921 | } |
||
2922 | else |
||
2923 | { |
||
2924 | fprintf(stderr, "Error: testctrl %s takes a single unsigned" + |
||
2925 | " int option\n", azArg[1]); |
||
2926 | } |
||
2927 | break; |
||
2928 | |||
2929 | /* Sqlite3.sqlite3_test_control(int, Int) */ |
||
2930 | case Sqlite3.SQLITE_TESTCTRL_ASSERT: |
||
2931 | case Sqlite3.SQLITE_TESTCTRL_ALWAYS: |
||
2932 | if (nArg == 3) |
||
2933 | { |
||
2934 | int opt = Convert.ToInt32(azArg[2]);//atoi |
||
2935 | rc = Sqlite3.sqlite3_test_control(testctrl, opt); |
||
2936 | printf("%d (0x%08x)\n", rc, rc); |
||
2937 | } |
||
2938 | else |
||
2939 | { |
||
2940 | fprintf(stderr, "Error: testctrl %s takes a single int option\n", |
||
2941 | azArg[1]); |
||
2942 | } |
||
2943 | break; |
||
2944 | |||
2945 | /* Sqlite3.sqlite3_test_control(int, string ) */ |
||
2946 | #if SQLITE_N_KEYWORD |
||
2947 | case Sqlite3.SQLITE_TESTCTRL_ISKEYWORD: |
||
2948 | if( nArg==3 ){ |
||
2949 | string opt = azArg[2]; |
||
2950 | rc = Sqlite3.sqlite3_test_control(testctrl, opt); |
||
2951 | printf("%d (0x%08x)\n", rc, rc); |
||
2952 | } else { |
||
2953 | fprintf(stderr,"Error: testctrl %s takes a single string option\n", |
||
2954 | azArg[1]); |
||
2955 | } |
||
2956 | break; |
||
2957 | #endif |
||
2958 | |||
2959 | case Sqlite3.SQLITE_TESTCTRL_BITVEC_TEST: |
||
2960 | case Sqlite3.SQLITE_TESTCTRL_FAULT_INSTALL: |
||
2961 | case Sqlite3.SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: |
||
2962 | case Sqlite3.SQLITE_TESTCTRL_SCRATCHMALLOC: |
||
2963 | default: |
||
2964 | fprintf(stderr, "Error: CLI support for testctrl %s not implemented\n", |
||
2965 | azArg[1]); |
||
2966 | break; |
||
2967 | } |
||
2968 | } |
||
2969 | } |
||
2970 | else |
||
2971 | |||
2972 | if (c == 't' && n > 4 && azArg[0].StartsWith("timeout", StringComparison.InvariantCultureIgnoreCase) && nArg == 2) |
||
2973 | { |
||
2974 | open_db(p); |
||
2975 | Sqlite3.sqlite3_busy_timeout(p.db, Convert.ToInt32(azArg[1]));//atoi |
||
2976 | } |
||
2977 | else |
||
2978 | |||
2979 | if (HAS_TIMER && c == 't' && n >= 5 && azArg[0].StartsWith("timer", StringComparison.InvariantCultureIgnoreCase) |
||
2980 | && nArg == 2 |
||
2981 | ) |
||
2982 | { |
||
2983 | enableTimer = booleanValue(azArg[1]); |
||
2984 | } |
||
2985 | else |
||
2986 | |||
2987 | if (c == 'v' && azArg[0].StartsWith("version", StringComparison.InvariantCultureIgnoreCase)) |
||
2988 | { |
||
2989 | printf("SQLite %s %s\n", |
||
2990 | Sqlite3.sqlite3_libversion(), Sqlite3.sqlite3_sourceid()); |
||
2991 | } |
||
2992 | else |
||
2993 | |||
2994 | if (c == 'w' && azArg[0].StartsWith("width", StringComparison.InvariantCultureIgnoreCase) && nArg > 1) |
||
2995 | { |
||
2996 | int j; |
||
2997 | Debug.Assert(nArg <= ArraySize(azArg)); |
||
2998 | for (j = 1; j < nArg && j < ArraySize(p.colWidth); j++) |
||
2999 | { |
||
3000 | p.colWidth[j - 1] = Convert.ToInt32(azArg[j]);//atoi |
||
3001 | } |
||
3002 | } |
||
3003 | else |
||
3004 | { |
||
3005 | fprintf(stderr, "Error: unknown command or invalid arguments: " + |
||
3006 | " \"%s\". Enter \".help\" for help\n", azArg[0]); |
||
3007 | rc = 1; |
||
3008 | } |
||
3009 | |||
3010 | return rc; |
||
3011 | } |
||
3012 | |||
3013 | /* |
||
3014 | ** Return TRUE if a semicolon occurs anywhere in the first N characters |
||
3015 | ** of string z[]. |
||
3016 | */ |
||
3017 | static bool _contains_semicolon(string z, int N) |
||
3018 | { |
||
3019 | //int i; |
||
3020 | //for (i = 0; i < N; i++) { if (z[i] == ';') return 1; } |
||
3021 | //return 0; |
||
3022 | return z.Contains(";"); |
||
3023 | } |
||
3024 | |||
3025 | /* |
||
3026 | ** Test to see if a line consists entirely of whitespace. |
||
3027 | */ |
||
3028 | static bool _all_whitespace(StringBuilder z) |
||
3029 | { |
||
3030 | return String.IsNullOrEmpty(z.ToString().Trim()); |
||
3031 | } |
||
3032 | static bool _all_whitespace(string z) |
||
3033 | { |
||
3034 | return String.IsNullOrEmpty(z.Trim()); |
||
3035 | } |
||
3036 | |||
3037 | /* |
||
3038 | ** Return TRUE if the line typed in is an SQL command terminator other |
||
3039 | ** than a semi-colon. The SQL Server style "go" command is understood |
||
3040 | ** as is the Oracle "/". |
||
3041 | */ |
||
3042 | static bool _is_command_terminator(StringBuilder zLine) |
||
3043 | { |
||
3044 | return _is_command_terminator(zLine.ToString()); |
||
3045 | } |
||
3046 | static bool _is_command_terminator(string zLine) |
||
3047 | { |
||
3048 | zLine = zLine.Trim();// while ( isspace( zLine ) ) { zLine++; }; |
||
3049 | if (zLine.Length == 0) |
||
3050 | return false; |
||
3051 | if (zLine[0] == '/')//&& _all_whitespace(zLine[1]) ) |
||
3052 | { |
||
3053 | return true; /* Oracle */ |
||
3054 | } |
||
3055 | if (Char.ToLower(zLine[0]) == 'g' && Char.ToLower(zLine[1]) == 'o') |
||
3056 | //&& _all_whitespace(&zLine[2]) ) |
||
3057 | { |
||
3058 | return true; /* SQL Server */ |
||
3059 | } |
||
3060 | return false; |
||
3061 | } |
||
3062 | /* |
||
3063 | ** Return true if zSql is a complete SQL statement. Return false if it |
||
3064 | ** ends in the middle of a string literal or C-style comment. |
||
3065 | */ |
||
3066 | static bool _is_complete(string zSql, int nSql) |
||
3067 | { |
||
3068 | int rc; |
||
3069 | if (zSql == null) |
||
3070 | return true; |
||
3071 | //zSql[nSql] = ';'; |
||
3072 | //zSql[nSql + 1] = 0; |
||
3073 | rc = Sqlite3.sqlite3_complete(zSql + ";\0"); |
||
3074 | //zSql[nSql] = 0; |
||
3075 | return rc != 0; |
||
3076 | } |
||
3077 | |||
3078 | /* |
||
3079 | ** Read input from In and process it. If In== null then input |
||
3080 | ** is interactive - the user is typing it it. Otherwise, Input |
||
3081 | ** is coming from a file or device. A prompt is issued and history |
||
3082 | ** is saved only if input is interactive. An interrupt signal will |
||
3083 | ** cause this routine to exit immediately, unless input is interactive. |
||
3084 | ** |
||
3085 | ** Return the number of errors. |
||
3086 | */ |
||
3087 | static int process_input(callback_data p, TextReader In) |
||
3088 | { |
||
3089 | StringBuilder zLine = new StringBuilder(1024); |
||
3090 | string zSql = null; |
||
3091 | int nSql = 0; |
||
3092 | int nSqlPrior = 0; |
||
3093 | string zErrMsg = null; |
||
3094 | int rc; |
||
3095 | int errCnt = 0; |
||
3096 | int lineno = 0; |
||
3097 | int startline = 0; |
||
3098 | |||
3099 | while (errCnt == null || !bail_on_error || (In == null && stdin_is_interactive)) |
||
3100 | { |
||
3101 | fflush(p.Out); |
||
3102 | //free(zLine); |
||
3103 | zLine.Length = 0; |
||
3104 | zLine.Append(one_input_line(zSql, In)); |
||
3105 | if (In != null && ((System.IO.StreamReader)(In)).EndOfStream) |
||
3106 | { |
||
3107 | break; /* We have reached EOF */ |
||
3108 | } |
||
3109 | //if (zLine == null) |
||
3110 | //{ |
||
3111 | // break; /* We have reached EOF */ |
||
3112 | //} |
||
3113 | if (seenInterrupt) |
||
3114 | { |
||
3115 | if (In != null) |
||
3116 | break; |
||
3117 | seenInterrupt = false; |
||
3118 | } |
||
3119 | lineno++; |
||
3120 | if (String.IsNullOrEmpty(zSql) && _all_whitespace(zLine)) |
||
3121 | continue; |
||
3122 | if (zLine.Length > 0 && zLine[0] == '.' && nSql == 0) |
||
3123 | { |
||
3124 | if (p.echoOn) |
||
3125 | printf("%s\n", zLine); |
||
3126 | rc = do_meta_command(zLine, p); |
||
3127 | if (rc == 2) |
||
3128 | { /* exit requested */ |
||
3129 | break; |
||
3130 | } |
||
3131 | else if (rc != 0) |
||
3132 | { |
||
3133 | errCnt++; |
||
3134 | } |
||
3135 | continue; |
||
3136 | } |
||
3137 | if (_is_command_terminator(zLine) && _is_complete(zSql, nSql)) |
||
3138 | { |
||
3139 | zLine.Append(";");// memcpy(zLine, ";", 2); |
||
3140 | } |
||
3141 | nSqlPrior = nSql; |
||
3142 | if (zSql == null) |
||
3143 | { |
||
3144 | int i; |
||
3145 | for (i = 0; i < zLine.Length && isspace(zLine[i]); i++) { } |
||
3146 | if (i < zLine.Length) |
||
3147 | { |
||
3148 | nSql = strlen30(zLine); |
||
3149 | //zSql = malloc(nSql + 3); |
||
3150 | //if (zSql == null) |
||
3151 | //{ |
||
3152 | // fprintf(stderr, "Error: out of memory\n"); |
||
3153 | // exit(1); |
||
3154 | //} |
||
3155 | zSql += zLine.ToString(0, nSql);//memcpy(zSql, zLine, nSql + 1); |
||
3156 | startline = lineno; |
||
3157 | } |
||
3158 | } |
||
3159 | else |
||
3160 | { |
||
3161 | int len = strlen30(zLine); |
||
3162 | //zSql = realloc(zSql, nSql + len + 4); |
||
3163 | //if (zSql == null) |
||
3164 | //{ |
||
3165 | // fprintf(stderr, "Error: out of memory\n"); |
||
3166 | // exit(1); |
||
3167 | //} |
||
3168 | zSql += '\n'; |
||
3169 | zSql += zLine;//memcpy(zSql[nSql], zLine, len + 1); |
||
3170 | nSql = zLine.Length; |
||
3171 | } |
||
3172 | if (!String.IsNullOrEmpty(zSql) && _contains_semicolon(zSql.Substring(nSqlPrior), nSql - nSqlPrior) |
||
3173 | && Sqlite3.sqlite3_complete(zSql) != 0) |
||
3174 | { |
||
3175 | p.cnt = 0; |
||
3176 | open_db(p); |
||
3177 | beginTimer(); |
||
3178 | rc = shell_exec(p.db, zSql, shell_callback, p, ref zErrMsg); |
||
3179 | endTimer(); |
||
3180 | if (rc != 0 || zErrMsg != null) |
||
3181 | { |
||
3182 | string zPrefix = null; |
||
3183 | if (In != null || !stdin_is_interactive) |
||
3184 | { |
||
3185 | snprintf(100, ref zPrefix, |
||
3186 | "Error: near line %d:", startline); |
||
3187 | } |
||
3188 | else |
||
3189 | { |
||
3190 | snprintf(100, ref zPrefix, "Error:"); |
||
3191 | } |
||
3192 | if (zErrMsg != null) |
||
3193 | { |
||
3194 | fprintf(stderr, "%s %s\n", zPrefix, zErrMsg); |
||
3195 | //Sqlite3.sqlite3_free(zErrMsg); |
||
3196 | zErrMsg = null; |
||
3197 | } |
||
3198 | else |
||
3199 | { |
||
3200 | fprintf(stderr, "%s %s\n", zPrefix, Sqlite3.sqlite3_errmsg(p.db)); |
||
3201 | } |
||
3202 | errCnt++; |
||
3203 | } |
||
3204 | //free(zSql); |
||
3205 | zSql = null; |
||
3206 | nSql = 0; |
||
3207 | } |
||
3208 | } |
||
3209 | if (zSql != null) |
||
3210 | { |
||
3211 | if (!_all_whitespace(zSql)) |
||
3212 | { |
||
3213 | fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); |
||
3214 | } |
||
3215 | zSql = null;//free(zSql); |
||
3216 | } |
||
3217 | zLine = null;//free(zLine); |
||
3218 | return errCnt; |
||
3219 | } |
||
3220 | |||
3221 | /* |
||
3222 | ** Return a pathname which is the user's home directory. A |
||
3223 | ** 0 return indicates an error of some kind. Space to hold the |
||
3224 | ** resulting string is obtained from malloc(). The calling |
||
3225 | ** function should free the result. |
||
3226 | */ |
||
3227 | static string find_home_dir() |
||
3228 | { |
||
3229 | string home_dir = null; |
||
3230 | |||
3231 | //#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) |
||
3232 | // struct passwd pwent; |
||
3233 | // uid_t uid = getuid(); |
||
3234 | // if( (pwent=getpwuid(uid)) != null) { |
||
3235 | // home_dir = pwent.pw_dir; |
||
3236 | // } |
||
3237 | //#endif |
||
3238 | |||
3239 | #if (_WIN32_WCE) |
||
3240 | /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() |
||
3241 | */ |
||
3242 | home_dir = strdup("/"); |
||
3243 | #else |
||
3244 | |||
3245 | #if (_WIN32) || (WIN32) || (__OS2__) |
||
3246 | //if (home_dir) |
||
3247 | { |
||
3248 | home_dir = getenv("USERPROFILE"); |
||
3249 | } |
||
3250 | #endif |
||
3251 | |||
3252 | if (!String.IsNullOrEmpty(home_dir)) |
||
3253 | { |
||
3254 | home_dir = getenv("HOME"); |
||
3255 | } |
||
3256 | |||
3257 | #if (_WIN32) || (WIN32) || (__OS2__) |
||
3258 | if (String.IsNullOrEmpty(home_dir)) |
||
3259 | { |
||
3260 | string zDrive, zPath; |
||
3261 | int n; |
||
3262 | zDrive = getenv("HOMEDRIVE"); |
||
3263 | zPath = getenv("HOMEPATH"); |
||
3264 | if (!String.IsNullOrEmpty(zDrive) && !String.IsNullOrEmpty(zPath)) |
||
3265 | { |
||
3266 | n = strlen30(zDrive) + strlen30(zPath) + 1; |
||
3267 | //home_dir = malloc(n); |
||
3268 | //if (home_dir == null) |
||
3269 | // return 0; |
||
3270 | snprintf(n, ref home_dir, "%s%s", zDrive, zPath); |
||
3271 | return home_dir; |
||
3272 | } |
||
3273 | home_dir = "c:\\"; |
||
3274 | } |
||
3275 | #endif |
||
3276 | |||
3277 | #endif //* !_WIN32_WCE */ |
||
3278 | |||
3279 | //if (home_dir) |
||
3280 | //{ |
||
3281 | // int n = strlen30(home_dir) + 1; |
||
3282 | // string z = malloc(n); |
||
3283 | // if (z) |
||
3284 | // memcpy(z, home_dir, n); |
||
3285 | // home_dir = z; |
||
3286 | //} |
||
3287 | |||
3288 | return home_dir; |
||
3289 | } |
||
3290 | |||
3291 | /* |
||
3292 | ** Read input from the file given by sqliterc_override. Or if that |
||
3293 | ** parameter is null, take input from ~/.sqliterc |
||
3294 | ** |
||
3295 | ** Returns the number of errors. |
||
3296 | */ |
||
3297 | static int process_sqliterc( |
||
3298 | callback_data p, /* Configuration data */ |
||
3299 | string sqliterc_override /* Name of config file. null to use default */ |
||
3300 | ) |
||
3301 | { |
||
3302 | string home_dir = null; |
||
3303 | string sqliterc = sqliterc_override; |
||
3304 | StringBuilder zBuf = null; |
||
3305 | StreamReader In = null; |
||
3306 | int nBuf; |
||
3307 | int rc = 0; |
||
3308 | |||
3309 | if (sqliterc == null) |
||
3310 | { |
||
3311 | home_dir = find_home_dir(); |
||
3312 | if (home_dir == null) |
||
3313 | { |
||
3314 | #if !(__RTP__) && !(_WRS_KERNEL) |
||
3315 | fprintf(stderr, "%s: Error: cannot locate your home directory\n", Argv0); |
||
3316 | #endif |
||
3317 | return 1; |
||
3318 | } |
||
3319 | nBuf = strlen30(home_dir) + 16; |
||
3320 | zBuf = new StringBuilder(nBuf); |
||
3321 | if (zBuf == null) |
||
3322 | { |
||
3323 | fprintf(stderr, "%s: Error: out of memory\n", Argv0); |
||
3324 | return 1; |
||
3325 | } |
||
3326 | Sqlite3.sqlite3_snprintf(nBuf, zBuf, "%s/.sqliterc", home_dir); |
||
3327 | home_dir = null;//free(home_dir); |
||
3328 | sqliterc = zBuf.ToString(); |
||
3329 | } |
||
3330 | if (File.Exists(sqliterc)) |
||
3331 | { |
||
3332 | try |
||
3333 | { |
||
3334 | In = new StreamReader(sqliterc);// fopen(sqliterc, "rb"); |
||
3335 | if (In != null) |
||
3336 | { |
||
3337 | if (stdin_is_interactive) |
||
3338 | { |
||
3339 | fprintf(stderr, "-- Loading resources from %s\n", sqliterc); |
||
3340 | } |
||
3341 | rc = process_input(p, In); |
||
3342 | In.Close();//fclose(In); |
||
3343 | } |
||
3344 | } catch |
||
3345 | { |
||
3346 | } |
||
3347 | } |
||
3348 | zBuf = null;//free(zBuf); |
||
3349 | return rc; |
||
3350 | } |
||
3351 | |||
3352 | /* |
||
3353 | ** Show available command line options |
||
3354 | */ |
||
3355 | static string zOptions = |
||
3356 | " -help show this message\n" + |
||
3357 | " -init filename read/process named file\n" + |
||
3358 | " -echo print commands before execution\n" + |
||
3359 | " -[no]header turn headers on or off\n" + |
||
3360 | " -bail stop after hitting an error\n" + |
||
3361 | " -interactive force interactive I/O\n" + |
||
3362 | " -batch force batch I/O\n" + |
||
3363 | " -column set output mode to 'column'\n" + |
||
3364 | " -csv set output mode to 'csv'\n" + |
||
3365 | " -html set output mode to HTML\n" + |
||
3366 | " -line set output mode to 'line'\n" + |
||
3367 | " -list set output mode to 'list'\n" + |
||
3368 | " -separator 'x' set output field separator (|)\n" + |
||
3369 | " -stats print memory stats before each finalize\n" + |
||
3370 | " -nullvalue 'text' set text string for null values\n" + |
||
3371 | " -version show SQLite version\n" + |
||
3372 | " -vfs NAME use NAME as the default VFS\n" + |
||
3373 | #if SQLITE_ENABLE_VFSTRACE |
||
3374 | " -vfstrace enable tracing of all VFS calls\n" + |
||
3375 | #endif |
||
3376 | ""; |
||
3377 | static void usage(bool showDetail) |
||
3378 | { |
||
3379 | fprintf(stderr, |
||
3380 | "Usage: %s [OPTIONS] FILENAME [SQL]\n" + |
||
3381 | "FILENAME is the name of an SQLite database. A new database is created\n" + |
||
3382 | "if the file does not previously exist.\n", Argv0); |
||
3383 | if (showDetail) |
||
3384 | { |
||
3385 | fprintf(stderr, "OPTIONS include:\n%s", zOptions); |
||
3386 | } |
||
3387 | else |
||
3388 | { |
||
3389 | fprintf(stderr, "Use the -help option for additional information\n"); |
||
3390 | } |
||
3391 | exit(1); |
||
3392 | } |
||
3393 | |||
3394 | /* |
||
3395 | ** Initialize the state information in data |
||
3396 | */ |
||
3397 | static void main_init(ref callback_data data) |
||
3398 | { |
||
3399 | data = new callback_data();//memset(data, 0, sizeof(*data)); |
||
3400 | data.mode = MODE_List; |
||
3401 | data.separator = "|";//memcpy(data.separator, "|", 2); |
||
3402 | data.showHeader = false; |
||
3403 | Sqlite3.sqlite3_initialize(); |
||
3404 | Sqlite3.sqlite3_config(Sqlite3.SQLITE_CONFIG_URI, 1); |
||
3405 | Sqlite3.sqlite3_config(Sqlite3.SQLITE_CONFIG_LOG, new object[] { (Sqlite3.dxLog)shellLog, data, null }); |
||
3406 | snprintf(10, ref mainPrompt, "sqlite> "); |
||
3407 | snprintf(10, ref continuePrompt, " ...> "); |
||
3408 | Sqlite3.sqlite3_config(Sqlite3.SQLITE_CONFIG_SINGLETHREAD); |
||
3409 | } |
||
3410 | |||
3411 | static int main(int argc, string[] argv) |
||
3412 | { |
||
3413 | string zErrMsg = null; |
||
3414 | callback_data data = null; |
||
3415 | string zInitFile = null; |
||
3416 | StringBuilder zFirstCmd = null; |
||
3417 | int i; |
||
3418 | int rc = 0; |
||
3419 | |||
3420 | if (!Sqlite3.sqlite3_sourceid().Equals(Sqlite3.SQLITE_SOURCE_ID, StringComparison.InvariantCultureIgnoreCase)) |
||
3421 | { |
||
3422 | fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n", |
||
3423 | Sqlite3.sqlite3_sourceid(), Sqlite3.SQLITE_SOURCE_ID); |
||
3424 | exit(1); |
||
3425 | } |
||
3426 | Argv0 = argv.Length == 0 ? null : argv[0]; |
||
3427 | main_init(ref data); |
||
3428 | stdin_is_interactive = isatty(0); |
||
3429 | |||
3430 | /* Make sure we have a valid signal handler early, before anything |
||
3431 | ** else is done. |
||
3432 | */ |
||
3433 | #if SIGINT |
||
3434 | signal(SIGINT, Interrupt_handler); |
||
3435 | #endif |
||
3436 | |||
3437 | /* Do an initial pass through the command-line argument to locate |
||
3438 | ** the name of the database file, the name of the initialization file, |
||
3439 | ** the size of the alternative malloc heap, |
||
3440 | ** and the first command to execute. |
||
3441 | */ |
||
3442 | for (i = 0; i < argc - 1; i++) |
||
3443 | { |
||
3444 | string z; |
||
3445 | if (argv[i][0] != '-') |
||
3446 | break; |
||
3447 | z = argv[i]; |
||
3448 | if (z[0] == '-' && z[1] == '-') |
||
3449 | z = z.Remove(0, 1);//z++; |
||
3450 | if (argv[i].Equals("-separator", StringComparison.InvariantCultureIgnoreCase) || argv[i].Equals("-nullvalue", StringComparison.InvariantCultureIgnoreCase)) |
||
3451 | { |
||
3452 | i++; |
||
3453 | } |
||
3454 | else if (argv[i].Equals("-init", StringComparison.InvariantCultureIgnoreCase)) |
||
3455 | { |
||
3456 | i++; |
||
3457 | zInitFile = argv[i]; |
||
3458 | /* Need to check for batch mode here to so we can avoid printing |
||
3459 | ** informational messages (like from process_sqliterc) before |
||
3460 | ** we do the actual processing of arguments later in a second pass. |
||
3461 | */ |
||
3462 | } |
||
3463 | else if (argv[i].Equals("-batch", StringComparison.InvariantCultureIgnoreCase)) |
||
3464 | { |
||
3465 | stdin_is_interactive = false; |
||
3466 | } |
||
3467 | else if (argv[i].Equals("-heap", StringComparison.InvariantCultureIgnoreCase)) |
||
3468 | { |
||
3469 | int j, c; |
||
3470 | string zSize; |
||
3471 | sqlite3_int64 szHeap; |
||
3472 | |||
3473 | zSize = argv[++i]; |
||
3474 | szHeap = Convert.ToInt32(zSize);//atoi |
||
3475 | for (j = 0; (c = zSize[j]) != null; j++) |
||
3476 | { |
||
3477 | if (c == 'M') { szHeap *= 1000000; break; } |
||
3478 | if (c == 'K') { szHeap *= 1000; break; } |
||
3479 | if (c == 'G') { szHeap *= 1000000000; break; } |
||
3480 | } |
||
3481 | if (szHeap > 0x7fff0000) |
||
3482 | szHeap = 0x7fff0000; |
||
3483 | #if (SQLITE_ENABLE_MEMSYS3) || (SQLITE_ENABLE_MEMSYS5) |
||
3484 | Sqlite3.SQLITE_config(Sqlite3.SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); |
||
3485 | #endif |
||
3486 | #if SQLITE_ENABLE_VFSTRACE |
||
3487 | }else if( argv[i].Equals("-vfstrace", StringComparison.InvariantCultureIgnoreCase) ){ |
||
3488 | extern int vfstrace_register( |
||
3489 | string zTraceName, |
||
3490 | string zOldVfsName, |
||
3491 | int (*xOut)(const char*,void*), |
||
3492 | void pOutArg, |
||
3493 | int makeDefault |
||
3494 | ); |
||
3495 | vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); |
||
3496 | #endif |
||
3497 | } |
||
3498 | else if (argv[i].Equals("-vfs", StringComparison.InvariantCultureIgnoreCase)) |
||
3499 | { |
||
3500 | Sqlite3.sqlite3_vfs pVfs = Sqlite3.sqlite3_vfs_find(argv[++i]); |
||
3501 | if (pVfs != null) |
||
3502 | { |
||
3503 | Sqlite3.sqlite3_vfs_register(pVfs, 1); |
||
3504 | } |
||
3505 | else |
||
3506 | { |
||
3507 | fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]); |
||
3508 | exit(1); |
||
3509 | } |
||
3510 | } |
||
3511 | } |
||
3512 | if (i < argc) |
||
3513 | { |
||
3514 | #if (SQLITE_OS_OS2) && SQLITE_OS_OS2 |
||
3515 | data.zDbFilename = (string )convertCpPathToUtf8( argv[i++] ); |
||
3516 | #else |
||
3517 | data.zDbFilename = argv[i++]; |
||
3518 | #endif |
||
3519 | } |
||
3520 | else |
||
3521 | { |
||
3522 | #if !SQLITE_OMIT_MEMORYDB |
||
3523 | data.zDbFilename = ":memory:"; |
||
3524 | #else |
||
3525 | data.zDbFilename = 0; |
||
3526 | #endif |
||
3527 | } |
||
3528 | if (i < argc) |
||
3529 | { |
||
3530 | zFirstCmd.Append(argv[i++]); |
||
3531 | } |
||
3532 | if (i < argc) |
||
3533 | { |
||
3534 | fprintf(stderr, "%s: Error: too many options: \"%s\"\n", Argv0, argv[i]); |
||
3535 | fprintf(stderr, "Use -help for a list of options.\n"); |
||
3536 | return 1; |
||
3537 | } |
||
3538 | data.Out = stdout; |
||
3539 | |||
3540 | #if SQLITE_OMIT_MEMORYDB |
||
3541 | if( data.zDbFilename== null ){ |
||
3542 | fprintf(stderr,"%s: Error: no database filename specified\n", Argv0); |
||
3543 | return 1; |
||
3544 | } |
||
3545 | #endif |
||
3546 | |||
3547 | /* Go ahead and open the database file if it already exists. If the |
||
3548 | ** file does not exist, delay opening it. This prevents empty database |
||
3549 | ** files from being created if a user mistypes the database name argument |
||
3550 | ** to the sqlite command-line tool. |
||
3551 | */ |
||
3552 | if (File.Exists(data.zDbFilename)) //(access(data.zDbFilename, 0) == 0) |
||
3553 | { |
||
3554 | open_db(data); |
||
3555 | } |
||
3556 | |||
3557 | /* Process the initialization file if there is one. If no -init option |
||
3558 | ** is given on the command line, look for a file named ~/.sqliterc and |
||
3559 | ** try to process it. |
||
3560 | */ |
||
3561 | rc = process_sqliterc(data, zInitFile); |
||
3562 | if (rc > 0) |
||
3563 | { |
||
3564 | return rc; |
||
3565 | } |
||
3566 | |||
3567 | /* Make a second pass through the command-line argument and set |
||
3568 | ** options. This second pass is delayed until after the initialization |
||
3569 | ** file is processed so that the command-line arguments will override |
||
3570 | ** settings in the initialization file. |
||
3571 | */ |
||
3572 | for (i = 0; i < argc && argv[i][0] == '-'; i++) |
||
3573 | { |
||
3574 | string z = argv[i]; |
||
3575 | if (z[1] == '-') { z = z.Remove(0, 1); } //z++; |
||
3576 | if (z.Equals("-init", StringComparison.InvariantCultureIgnoreCase)) |
||
3577 | { |
||
3578 | i++; |
||
3579 | } |
||
3580 | else if (z.Equals("-html", StringComparison.InvariantCultureIgnoreCase)) |
||
3581 | { |
||
3582 | data.mode = MODE_Html; |
||
3583 | } |
||
3584 | else if (z.Equals("-list", StringComparison.InvariantCultureIgnoreCase)) |
||
3585 | { |
||
3586 | data.mode = MODE_List; |
||
3587 | } |
||
3588 | else if (z.Equals("-line", StringComparison.InvariantCultureIgnoreCase)) |
||
3589 | { |
||
3590 | data.mode = MODE_Line; |
||
3591 | } |
||
3592 | else if (z.Equals("-column", StringComparison.InvariantCultureIgnoreCase)) |
||
3593 | { |
||
3594 | data.mode = MODE_Column; |
||
3595 | } |
||
3596 | else if (z.Equals("-csv", StringComparison.InvariantCultureIgnoreCase)) |
||
3597 | { |
||
3598 | data.mode = MODE_Csv; |
||
3599 | data.separator = ",";//memcpy(data.separator, ",", 2); |
||
3600 | } |
||
3601 | else if (z.Equals("-separator", StringComparison.InvariantCultureIgnoreCase)) |
||
3602 | { |
||
3603 | i++; |
||
3604 | if (i >= argc) |
||
3605 | { |
||
3606 | fprintf(stderr, "%s: Error: missing argument for option: %s\n", Argv0, z); |
||
3607 | fprintf(stderr, "Use -help for a list of options.\n"); |
||
3608 | return 1; |
||
3609 | } |
||
3610 | snprintf(data.separator.Length, ref data.separator, |
||
3611 | "%.*s", data.separator.Length - 1, argv[i]); |
||
3612 | } |
||
3613 | else if (z.Equals("-nullvalue", StringComparison.InvariantCultureIgnoreCase)) |
||
3614 | { |
||
3615 | i++; |
||
3616 | if (i >= argc) |
||
3617 | { |
||
3618 | fprintf(stderr, "%s: Error: missing argument for option: %s\n", Argv0, z); |
||
3619 | fprintf(stderr, "Use -help for a list of options.\n"); |
||
3620 | return 1; |
||
3621 | } |
||
3622 | snprintf(99, ref data.nullvalue, |
||
3623 | "%.*s", 99 - 1, argv[i]); |
||
3624 | } |
||
3625 | else if (z.Equals("-header", StringComparison.InvariantCultureIgnoreCase)) |
||
3626 | { |
||
3627 | data.showHeader = true; |
||
3628 | } |
||
3629 | else if (z.Equals("-noheader", StringComparison.InvariantCultureIgnoreCase)) |
||
3630 | { |
||
3631 | data.showHeader = false; |
||
3632 | } |
||
3633 | else if (z.Equals("-echo", StringComparison.InvariantCultureIgnoreCase)) |
||
3634 | { |
||
3635 | data.echoOn = true; |
||
3636 | } |
||
3637 | else if (z.Equals("-stats", StringComparison.InvariantCultureIgnoreCase)) |
||
3638 | { |
||
3639 | data.statsOn = true; |
||
3640 | } |
||
3641 | else if (z.Equals("-bail", StringComparison.InvariantCultureIgnoreCase)) |
||
3642 | { |
||
3643 | bail_on_error = true; |
||
3644 | } |
||
3645 | else if (z.Equals("-version", StringComparison.InvariantCultureIgnoreCase)) |
||
3646 | { |
||
3647 | printf("%s %s\n", Sqlite3.sqlite3_libversion(), Sqlite3.sqlite3_sourceid()); |
||
3648 | return 0; |
||
3649 | } |
||
3650 | else if (z.Equals("-interactive", StringComparison.InvariantCultureIgnoreCase)) |
||
3651 | { |
||
3652 | stdin_is_interactive = true; |
||
3653 | } |
||
3654 | else if (z.Equals("-batch", StringComparison.InvariantCultureIgnoreCase)) |
||
3655 | { |
||
3656 | stdin_is_interactive = false; |
||
3657 | } |
||
3658 | else if (z.Equals("-heap", StringComparison.InvariantCultureIgnoreCase)) |
||
3659 | { |
||
3660 | i++; |
||
3661 | } |
||
3662 | else if (z.Equals("-vfs", StringComparison.InvariantCultureIgnoreCase)) |
||
3663 | { |
||
3664 | i++; |
||
3665 | } |
||
3666 | else if (z.Equals("-vfstrace", StringComparison.InvariantCultureIgnoreCase)) |
||
3667 | { |
||
3668 | i++; |
||
3669 | } |
||
3670 | else if (z.Equals("-help", StringComparison.InvariantCultureIgnoreCase) || z.Equals("--help", StringComparison.InvariantCultureIgnoreCase)) |
||
3671 | { |
||
3672 | usage(true); |
||
3673 | } |
||
3674 | else |
||
3675 | { |
||
3676 | fprintf(stderr, "%s: Error: unknown option: %s\n", Argv0, z); |
||
3677 | fprintf(stderr, "Use -help for a list of options.\n"); |
||
3678 | return 1; |
||
3679 | } |
||
3680 | } |
||
3681 | |||
3682 | if (zFirstCmd != null && zFirstCmd.Length > 0) |
||
3683 | { |
||
3684 | /* Run just the command that follows the database name |
||
3685 | */ |
||
3686 | if (zFirstCmd[0] == '.') |
||
3687 | { |
||
3688 | rc = do_meta_command(zFirstCmd, data); |
||
3689 | } |
||
3690 | else |
||
3691 | { |
||
3692 | open_db(data); |
||
3693 | rc = shell_exec(data.db, zFirstCmd.ToString(), shell_callback, data, ref zErrMsg); |
||
3694 | if (zErrMsg != null) |
||
3695 | { |
||
3696 | fprintf(stderr, "Error: %s\n", zErrMsg); |
||
3697 | return rc != 0 ? rc : 1; |
||
3698 | } |
||
3699 | else if (rc != 0) |
||
3700 | { |
||
3701 | fprintf(stderr, "Error: unable to process SQL \"%s\"\n", zFirstCmd); |
||
3702 | return rc; |
||
3703 | } |
||
3704 | } |
||
3705 | } |
||
3706 | else |
||
3707 | { |
||
3708 | /* Run commands received from standard input |
||
3709 | */ |
||
3710 | if (stdin_is_interactive) |
||
3711 | { |
||
3712 | string zHome; |
||
3713 | string zHistory = null; |
||
3714 | int nHistory; |
||
3715 | printf( |
||
3716 | #if (SQLITE_HAS_CODEC) && (SQLITE_ENABLE_CEROD) |
||
3717 | "SQLite version %s with the CEROD Extension\n" + |
||
3718 | "Copyright 2006 Hipp, Wyrick & Company, Inc.\n" + |
||
3719 | #else |
||
3720 | "SQLite version %s\n" + |
||
3721 | #endif |
||
3722 | "(source %.19s)\n" + |
||
3723 | "Enter \".help\" for instructions\n" + |
||
3724 | "Enter SQL statements terminated with a \";\"\n", |
||
3725 | Sqlite3.sqlite3_libversion(), Sqlite3.sqlite3_sourceid() |
||
3726 | ); |
||
3727 | zHome = find_home_dir(); |
||
3728 | if (zHome != null) |
||
3729 | { |
||
3730 | nHistory = strlen30(zHome) + 20; |
||
3731 | //if ((zHistory = malloc(nHistory)) != null) |
||
3732 | { |
||
3733 | snprintf(nHistory, ref zHistory, "%s/.Sqlite3.SQLITE_history", zHome); |
||
3734 | } |
||
3735 | } |
||
3736 | #if (HAVE_READLINE)// && HAVE_READLINE==1 |
||
3737 | if( zHistory ) read_history(zHistory); |
||
3738 | #endif |
||
3739 | rc = process_input(data, null); |
||
3740 | if (zHistory != null) |
||
3741 | { |
||
3742 | stifle_history(100); |
||
3743 | write_history(zHistory); |
||
3744 | zHistory = null;//free(zHistory); |
||
3745 | } |
||
3746 | zHome = null;//free(zHome); |
||
3747 | } |
||
3748 | else |
||
3749 | { |
||
3750 | rc = process_input(data, stdin); |
||
3751 | } |
||
3752 | } |
||
3753 | set_table_name(data, null); |
||
3754 | if (data.db != null) |
||
3755 | { |
||
3756 | Sqlite3.sqlite3_close(data.db); |
||
3757 | } |
||
3758 | return rc; |
||
3759 | } |
||
3760 | // C# DllImports |
||
3761 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] |
||
3762 | internal static extern void FreeLibrary(IntPtr hModule); |
||
3763 | |||
3764 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] |
||
3765 | internal static extern IntPtr LoadLibrary(string lpFileName); |
||
3766 | |||
3767 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] |
||
3768 | internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName); |
||
3769 | |||
3770 | // Helper Variables for C# |
||
3771 | static TextReader stdin = Console.In; |
||
3772 | static TextWriter stdout = Console.Out; |
||
3773 | static TextWriter stderr = Console.Error; |
||
3774 | |||
3775 | // Helper Functions for C# |
||
3776 | private static void exit(int p) |
||
3777 | { |
||
3778 | if (p == 0) |
||
3779 | { |
||
3780 | Console.WriteLine("Enter to CONTINUE:"); |
||
3781 | } |
||
3782 | else |
||
3783 | { |
||
3784 | Console.WriteLine(String.Format("Error: {0}", p)); |
||
3785 | } |
||
3786 | Console.ReadKey(); |
||
3787 | } |
||
3788 | private static void fflush(TextWriter tw) |
||
3789 | { |
||
3790 | tw.Flush(); |
||
3791 | } |
||
3792 | private static int fgets(StringBuilder p, int p_2, TextReader In) |
||
3793 | { |
||
3794 | try |
||
3795 | { |
||
3796 | p.Length = 0; |
||
3797 | p.Append(In.ReadLine()); |
||
3798 | if (p.Length > 0) |
||
3799 | p.Append('\n'); |
||
3800 | return p.Length; |
||
3801 | } catch |
||
3802 | { |
||
3803 | return 0; |
||
3804 | } |
||
3805 | } |
||
3806 | private static void fputc(char c, TextWriter Out) |
||
3807 | { |
||
3808 | Out.Write(c); |
||
3809 | } |
||
3810 | private static string getenv(string p) |
||
3811 | { |
||
3812 | switch (p) |
||
3813 | { |
||
3814 | case "USERPROFILE": |
||
3815 | return Environment.GetEnvironmentVariable("UserProfile"); |
||
3816 | case "HOME": |
||
3817 | { |
||
3818 | return Environment.GetEnvironmentVariable("Home"); |
||
3819 | } |
||
3820 | case "HOMEDRIVE": |
||
3821 | { |
||
3822 | return Environment.GetEnvironmentVariable("HomeDrive"); |
||
3823 | } |
||
3824 | case "HOMEPATH": |
||
3825 | { |
||
3826 | return Environment.GetEnvironmentVariable("HomePath"); |
||
3827 | } |
||
3828 | default: |
||
3829 | throw new Exception("The method or operation is not implemented."); |
||
3830 | } |
||
3831 | } |
||
3832 | static bool isalnum(char c) |
||
3833 | { |
||
3834 | return char.IsLetterOrDigit(c); |
||
3835 | } |
||
3836 | static bool isalpha(char c) |
||
3837 | { |
||
3838 | return char.IsLetter(c); |
||
3839 | } |
||
3840 | static bool isdigit(char c) |
||
3841 | { |
||
3842 | return char.IsDigit(c); |
||
3843 | } |
||
3844 | static bool isprint(char c) |
||
3845 | { |
||
3846 | return !char.IsControl(c); |
||
3847 | } |
||
3848 | private static bool isspace(char c) |
||
3849 | { |
||
3850 | return char.IsWhiteSpace(c); |
||
3851 | } |
||
3852 | static void fprintf(TextWriter tw, string zFormat, params va_list[] ap) |
||
3853 | { |
||
3854 | tw.Write(Sqlite3.sqlite3_mprintf(zFormat, ap)); |
||
3855 | } |
||
3856 | public static void Main(string[] args) |
||
3857 | { |
||
3858 | main(args.Length, args); |
||
3859 | } |
||
3860 | |||
3861 | static void printf(string zFormat, params va_list[] ap) |
||
3862 | { |
||
3863 | stdout.Write(Sqlite3.sqlite3_mprintf(zFormat, ap)); |
||
3864 | } |
||
3865 | private static void putc(char c, TextWriter _out) |
||
3866 | { |
||
3867 | _out.Write(c); |
||
3868 | } |
||
3869 | private static void snprintf(int n, ref string zBuf, string zFormat, params va_list[] ap) |
||
3870 | { |
||
3871 | StringBuilder sbBuf = new StringBuilder(100); |
||
3872 | Sqlite3.sqlite3_snprintf(n, sbBuf, zFormat, ap); |
||
3873 | zBuf = sbBuf.ToString(); |
||
3874 | } |
||
3875 | } |
||
3876 | } |