summaryrefslogtreecommitdiff
path: root/shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c14839
1 files changed, 9064 insertions, 5775 deletions
diff --git a/shell.c b/shell.c
index 0d5bf19e74d5..57faceb4691f 100644
--- a/shell.c
+++ b/shell.c
@@ -1,21 +1,45 @@
-/* DO NOT EDIT!
-** This file is automatically generated by the script in the canonical
-** SQLite source tree at tool/mkshellc.tcl. That script combines source
-** code from various constituent source files of SQLite into this single
-** "shell.c" file used to implement the SQLite command-line shell.
-**
-** Most of the code found below comes from the "src/shell.c.in" file in
-** the canonical SQLite source tree. That main file contains "INCLUDE"
-** lines that specify other files in the canonical source tree that are
-** inserted to getnerate this complete program source file.
+/*
+** This is the amalgamated source code to the "sqlite3" or "sqlite3.exe"
+** command-line shell (CLI) for SQLite. This file is automatically
+** generated by the tool/mkshellc.tcl script from the following sources:
**
-** The code from multiple files is combined into this single "shell.c"
-** source file to help make the command-line program easier to compile.
+** ext/expert/sqlite3expert.c
+** ext/expert/sqlite3expert.h
+** ext/intck/sqlite3intck.c
+** ext/intck/sqlite3intck.h
+** ext/misc/appendvfs.c
+** ext/misc/base64.c
+** ext/misc/base85.c
+** ext/misc/completion.c
+** ext/misc/decimal.c
+** ext/misc/fileio.c
+** ext/misc/ieee754.c
+** ext/misc/memtrace.c
+** ext/misc/pcachetrace.c
+** ext/misc/regexp.c
+** ext/misc/series.c
+** ext/misc/sha1.c
+** ext/misc/shathree.c
+** ext/misc/sqlar.c
+** ext/misc/sqlite3_stdio.c
+** ext/misc/sqlite3_stdio.h
+** ext/misc/stmtrand.c
+** ext/misc/uint.c
+** ext/misc/vfstrace.c
+** ext/misc/windirent.h
+** ext/misc/zipfile.c
+** ext/qrf/qrf.c
+** ext/qrf/qrf.h
+** ext/recover/dbdata.c
+** ext/recover/sqlite3recover.c
+** ext/recover/sqlite3recover.h
+** src/shell.c.in
**
** To modify this program, get a copy of the canonical SQLite source tree,
-** edit the src/shell.c.in" and/or some of the other files that are included
-** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
+** edit the src/shell.c.in file and/or some of the other files that are
+** listed above, then rerun the command "make shell.c".
*/
+/************************* Begin src/shell.c.in ******************/
/*
** 2001 September 15
**
@@ -27,7 +51,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains code to implement the "sqlite" command line
+** This file contains code to implement the "sqlite3" command line
** utility for accessing SQLite databases.
*/
#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
@@ -38,6 +62,22 @@ typedef unsigned int u32;
typedef unsigned short int u16;
/*
+** Limit input nesting via .read or any other input redirect.
+** It's not too expensive, so a generous allowance can be made.
+*/
+#define MAX_INPUT_NESTING 25
+
+/*
+** Used to prevent warnings about unused parameters
+*/
+#define UNUSED_PARAMETER(x) (void)(x)
+
+/*
+** Number of elements in an array
+*/
+#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
+
+/*
** Optionally #include a user-defined header, whereby compilation options
** may be set prior to where they take effect, but after platform setup.
** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
@@ -50,14 +90,6 @@ typedef unsigned short int u16;
#endif
/*
-** Determine if we are dealing with WinRT, which provides only a subset of
-** the full Win32 API.
-*/
-#if !defined(SQLITE_OS_WINRT)
-# define SQLITE_OS_WINRT 0
-#endif
-
-/*
** If SQLITE_SHELL_FIDDLE is defined then the shell is modified
** somewhat for use as a WASM module in a web browser. This flag
** should only be used when building the "fiddle" web application, as
@@ -65,6 +97,10 @@ typedef unsigned short int u16;
** and this build mode rewires the user input subsystem to account for
** that.
*/
+#if defined(SQLITE_SHELL_FIDDLE)
+# undef SQLITE_OMIT_LOAD_EXTENSION
+# define SQLITE_OMIT_LOAD_EXTENSION 1
+#endif
/*
** Warning pragmas copied from msvc.h in the core.
@@ -118,12 +154,17 @@ typedef unsigned short int u16;
#include <stdio.h>
#include <assert.h>
#include <math.h>
+#include <stdint.h>
#include "sqlite3.h"
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef unsigned char u8;
#include <ctype.h>
#include <stdarg.h>
+#ifndef _WIN32
+# include <sys/time.h>
+# include <sys/ioctl.h>
+#endif
#if !defined(_WIN32) && !defined(WIN32)
# include <signal.h>
@@ -192,9 +233,6 @@ typedef unsigned char u8;
#endif
#if defined(_WIN32) || defined(WIN32)
-# if SQLITE_OS_WINRT
-# define SQLITE_OMIT_POPEN 1
-# else
# include <io.h>
# include <fcntl.h>
# define isatty(h) _isatty(h)
@@ -209,7 +247,6 @@ typedef unsigned char u8;
# endif
# undef pclose
# define pclose _pclose
-# endif
#else
/* Make sure isatty() has a prototype. */
extern int isatty(int);
@@ -240,9 +277,6 @@ typedef unsigned char u8;
#define IsAlpha(X) isalpha((unsigned char)X)
#if defined(_WIN32) || defined(WIN32)
-#if SQLITE_OS_WINRT
-#include <intrin.h>
-#endif
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
@@ -252,7 +286,7 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
#endif
-/************************* Begin ../ext/misc/sqlite3_stdio.h ******************/
+/************************* Begin ext/misc/sqlite3_stdio.h ******************/
/*
** 2024-09-24
**
@@ -286,6 +320,7 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
#ifdef _WIN32
/**** Definitions For Windows ****/
#include <stdio.h>
+#include <stdarg.h>
#include <windows.h>
FILE *sqlite3_fopen(const char *zFilename, const char *zMode);
@@ -293,6 +328,7 @@ FILE *sqlite3_popen(const char *zCommand, const char *type);
char *sqlite3_fgets(char *s, int size, FILE *stream);
int sqlite3_fputs(const char *s, FILE *stream);
int sqlite3_fprintf(FILE *stream, const char *format, ...);
+int sqlite3_vfprintf(FILE *stream, const char *format, va_list);
void sqlite3_fsetmode(FILE *stream, int mode);
@@ -304,13 +340,14 @@ void sqlite3_fsetmode(FILE *stream, int mode);
#define sqlite3_fgets fgets
#define sqlite3_fputs fputs
#define sqlite3_fprintf fprintf
+#define sqlite3_vfprintf vfprintf
#define sqlite3_fsetmode(F,X) /*no-op*/
#endif
#endif /* _SQLITE3_STDIO_H_ */
-/************************* End ../ext/misc/sqlite3_stdio.h ********************/
-/************************* Begin ../ext/misc/sqlite3_stdio.c ******************/
+/************************* End ext/misc/sqlite3_stdio.h ********************/
+/************************* Begin ext/misc/sqlite3_stdio.c ******************/
/*
** 2024-09-24
**
@@ -414,8 +451,8 @@ FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
- b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
- b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
+ b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
+ b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
b1[sz1] = 0;
@@ -440,8 +477,8 @@ FILE *sqlite3_popen(const char *zCommand, const char *zMode){
sz1 = (int)strlen(zCommand);
sz2 = (int)strlen(zMode);
- b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
- b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
+ b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) );
+ b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
b1[sz1] = 0;
@@ -464,7 +501,7 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
- wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
+ wchar_t *b1 = sqlite3_malloc64( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
DWORD nRead = 0;
@@ -539,7 +576,7 @@ int sqlite3_fputs(const char *z, FILE *out){
** to the console on Windows.
*/
int sz = (int)strlen(z);
- wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
+ wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
@@ -571,7 +608,7 @@ int sqlite3_fputs(const char *z, FILE *out){
/*
-** Work-alike for fprintf() from the standard C library.
+** Work-alikes for fprintf() and vfprintf() from the standard C library.
*/
int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
int rc;
@@ -598,6 +635,24 @@ int sqlite3_fprintf(FILE *out, const char *zFormat, ...){
}
return rc;
}
+int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){
+ int rc;
+ if( UseWtextForOutput(out) ){
+ /* When writing to the command-prompt in Windows, it is necessary
+ ** to use _O_WTEXT input mode and write UTF-16 characters.
+ */
+ char *z;
+ z = sqlite3_vmprintf(zFormat, ap);
+ sqlite3_fputs(z, out);
+ rc = (int)strlen(z);
+ sqlite3_free(z);
+ }else{
+ /* Writing to a file or other destination, just write bytes without
+ ** any translation. */
+ rc = vfprintf(out, zFormat, ap);
+ }
+ return rc;
+}
/*
** Set the mode for an output stream. mode argument is typically _O_BINARY or
@@ -616,393 +671,694 @@ void sqlite3_fsetmode(FILE *fp, int mode){
#endif /* defined(_WIN32) */
-/************************* End ../ext/misc/sqlite3_stdio.c ********************/
-
-/* Use console I/O package as a direct INCLUDE. */
-#define SQLITE_INTERNAL_LINKAGE static
-
-#ifdef SQLITE_SHELL_FIDDLE
-/* Deselect most features from the console I/O package for Fiddle. */
-# define SQLITE_CIO_NO_REDIRECT
-# define SQLITE_CIO_NO_CLASSIFY
-# define SQLITE_CIO_NO_TRANSLATE
-# define SQLITE_CIO_NO_SETMODE
-# define SQLITE_CIO_NO_FLUSH
+/************************* End ext/misc/sqlite3_stdio.c ********************/
+/************************* Begin ext/qrf/qrf.h ******************/
+/*
+** 2025-10-20
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Header file for the Query Result-Format or "qrf" utility library for
+** SQLite. See the README.md documentation for additional information.
+*/
+#ifndef SQLITE_QRF_H
+#define SQLITE_QRF_H
+#ifdef __cplusplus
+extern "C" {
#endif
+#include <stdlib.h>
+/* #include "sqlite3.h" */
-#define eputz(z) sqlite3_fputs(z,stderr)
-#define sputz(fp,z) sqlite3_fputs(z,fp)
-
-/* True if the timer is enabled */
-static int enableTimer = 0;
-
-/* A version of strcmp() that works with NULL values */
-static int cli_strcmp(const char *a, const char *b){
- if( a==0 ) a = "";
- if( b==0 ) b = "";
- return strcmp(a,b);
-}
-static int cli_strncmp(const char *a, const char *b, size_t n){
- if( a==0 ) a = "";
- if( b==0 ) b = "";
- return strncmp(a,b,n);
-}
-
-/* Return the current wall-clock time */
-static sqlite3_int64 timeOfDay(void){
- static sqlite3_vfs *clockVfs = 0;
- sqlite3_int64 t;
- if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
- if( clockVfs==0 ) return 0; /* Never actually happens */
- if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
- clockVfs->xCurrentTimeInt64(clockVfs, &t);
- }else{
- double r;
- clockVfs->xCurrentTime(clockVfs, &r);
- t = (sqlite3_int64)(r*86400000.0);
- }
- return t;
-}
-
-#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
-#include <sys/time.h>
-#include <sys/resource.h>
-
-/* VxWorks does not support getrusage() as far as we can determine */
-#if defined(_WRS_KERNEL) || defined(__RTP__)
-struct rusage {
- struct timeval ru_utime; /* user CPU time used */
- struct timeval ru_stime; /* system CPU time used */
+/*
+** Specification used by clients to define the output format they want
+*/
+typedef struct sqlite3_qrf_spec sqlite3_qrf_spec;
+struct sqlite3_qrf_spec {
+ unsigned char iVersion; /* Version number of this structure */
+ unsigned char eStyle; /* Formatting style. "box", "csv", etc... */
+ unsigned char eEsc; /* How to escape control characters in text */
+ unsigned char eText; /* Quoting style for text */
+ unsigned char eTitle; /* Quating style for the text of column names */
+ unsigned char eBlob; /* Quoting style for BLOBs */
+ unsigned char bTitles; /* True to show column names */
+ unsigned char bWordWrap; /* Try to wrap on word boundaries */
+ unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */
+ unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */
+ unsigned char eTitleAlign; /* Alignment for column headers */
+ unsigned char bSplitColumn; /* Wrap single-column output into many columns */
+ unsigned char bBorder; /* Show outer border in Box and Table styles */
+ short int nWrap; /* Wrap columns wider than this */
+ short int nScreenWidth; /* Maximum overall table width */
+ short int nLineLimit; /* Maximum number of lines for any row */
+ short int nTitleLimit; /* Maximum number of characters in a title */
+ unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */
+ int nCharLimit; /* Maximum number of characters in a cell */
+ int nWidth; /* Number of entries in aWidth[] */
+ int nAlign; /* Number of entries in aAlignment[] */
+ short int *aWidth; /* Column widths */
+ unsigned char *aAlign; /* Column alignments */
+ char *zColumnSep; /* Alternative column separator */
+ char *zRowSep; /* Alternative row separator */
+ char *zTableName; /* Output table name */
+ char *zNull; /* Rendering of NULL */
+ char *(*xRender)(void*,sqlite3_value*); /* Render a value */
+ int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */
+ void *pRenderArg; /* First argument to the xRender callback */
+ void *pWriteArg; /* First argument to the xWrite callback */
+ char **pzOutput; /* Storage location for output string */
+ /* Additional fields may be added in the future */
};
-#define getrusage(A,B) memset(B,0,sizeof(*B))
-#endif
+/*
+** Interfaces
+*/
+int sqlite3_format_query_result(
+ sqlite3_stmt *pStmt, /* SQL statement to run */
+ const sqlite3_qrf_spec *pSpec, /* Result format specification */
+ char **pzErr /* OUT: Write error message here */
+);
-/* Saved resource information for the beginning of an operation */
-static struct rusage sBegin; /* CPU time at start */
-static sqlite3_int64 iBegin; /* Wall-clock time at start */
+/*
+** Range of values for sqlite3_qrf_spec.aWidth[] entries and for
+** sqlite3_qrf_spec.mxColWidth and .nScreenWidth
+*/
+#define QRF_MAX_WIDTH 10000
+#define QRF_MIN_WIDTH 0
/*
-** Begin timing an operation
+** Output styles:
*/
-static void beginTimer(void){
- if( enableTimer ){
- getrusage(RUSAGE_SELF, &sBegin);
- iBegin = timeOfDay();
- }
-}
+#define QRF_STYLE_Auto 0 /* Choose a style automatically */
+#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */
+#define QRF_STYLE_Column 2 /* One record per line in neat columns */
+#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */
+#define QRF_STYLE_Csv 4 /* Comma-separated-value */
+#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */
+#define QRF_STYLE_Explain 6 /* EXPLAIN output */
+#define QRF_STYLE_Html 7 /* Generate an XHTML table */
+#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */
+#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */
+#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */
+#define QRF_STYLE_Line 11 /* One column per line. */
+#define QRF_STYLE_List 12 /* One record per line with a separator */
+#define QRF_STYLE_Markdown 13 /* Markdown formatting */
+#define QRF_STYLE_Off 14 /* No query output shown */
+#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */
+#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */
+#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */
+#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */
+#define QRF_STYLE_Table 19 /* MySQL-style table formatting */
-/* Return the difference of two time_structs in seconds */
-static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
- return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
- (double)(pEnd->tv_sec - pStart->tv_sec);
-}
+/*
+** Quoting styles for text.
+** Allowed values for sqlite3_qrf_spec.eText
+*/
+#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */
+#define QRF_TEXT_Plain 1 /* Literal text */
+#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */
+#define QRF_TEXT_Csv 3 /* CSV-style quoting */
+#define QRF_TEXT_Html 4 /* HTML-style quoting */
+#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */
+#define QRF_TEXT_Json 6 /* JSON quoting */
+#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */
/*
-** Print the timing results.
+** Quoting styles for BLOBs
+** Allowed values for sqlite3_qrf_spec.eBlob
*/
-static void endTimer(FILE *out){
- if( enableTimer ){
- sqlite3_int64 iEnd = timeOfDay();
- struct rusage sEnd;
- getrusage(RUSAGE_SELF, &sEnd);
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
- (iEnd - iBegin)*0.001,
- timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
- timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
- }
-}
+#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */
+#define QRF_BLOB_Text 1 /* Display content exactly as it is */
+#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */
+#define QRF_BLOB_Hex 3 /* Hexadecimal representation */
+#define QRF_BLOB_Tcl 4 /* "\000" notation */
+#define QRF_BLOB_Json 5 /* A JSON string */
+#define QRF_BLOB_Size 6 /* Display the blob size only */
-#define BEGIN_TIMER beginTimer()
-#define END_TIMER(X) endTimer(X)
-#define HAS_TIMER 1
+/*
+** Control-character escape modes.
+** Allowed values for sqlite3_qrf_spec.eEsc
+*/
+#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */
+#define QRF_ESC_Off 1 /* Do not escape control characters */
+#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */
+#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */
-#elif (defined(_WIN32) || defined(WIN32))
+/*
+** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap",
+** and "bTextJsonb". There is an extra "auto" variants so these are actually
+** tri-state settings, not booleans.
+*/
+#define QRF_SW_Auto 0 /* Let QRF choose the best value */
+#define QRF_SW_Off 1 /* This setting is forced off */
+#define QRF_SW_On 2 /* This setting is forced on */
+#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */
+#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */
+#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */
-/* Saved resource information for the beginning of an operation */
-static HANDLE hProcess;
-static FILETIME ftKernelBegin;
-static FILETIME ftUserBegin;
-static sqlite3_int64 ftWallBegin;
-typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME,
- LPFILETIME, LPFILETIME);
-static GETPROCTIMES getProcessTimesAddr = NULL;
+/*
+** Possible alignment values alignment settings
+**
+** Horizontal Vertial
+** ---------- -------- */
+#define QRF_ALIGN_Auto 0 /* auto auto */
+#define QRF_ALIGN_Left 1 /* left auto */
+#define QRF_ALIGN_Center 2 /* center auto */
+#define QRF_ALIGN_Right 3 /* right auto */
+#define QRF_ALIGN_Top 4 /* auto top */
+#define QRF_ALIGN_NW 5 /* left top */
+#define QRF_ALIGN_N 6 /* center top */
+#define QRF_ALIGN_NE 7 /* right top */
+#define QRF_ALIGN_Middle 8 /* auto middle */
+#define QRF_ALIGN_W 9 /* left middle */
+#define QRF_ALIGN_C 10 /* center middle */
+#define QRF_ALIGN_E 11 /* right middle */
+#define QRF_ALIGN_Bottom 12 /* auto bottom */
+#define QRF_ALIGN_SW 13 /* left bottom */
+#define QRF_ALIGN_S 14 /* center bottom */
+#define QRF_ALIGN_SE 15 /* right bottom */
+#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */
+#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */
/*
-** Check to see if we have timer support. Return 1 if necessary
-** support found (or found previously).
+** Auxiliary routines contined within this module that might be useful
+** in other contexts, and which are therefore exported.
*/
-static int hasTimer(void){
- if( getProcessTimesAddr ){
- return 1;
- } else {
-#if !SQLITE_OS_WINRT
- /* GetProcessTimes() isn't supported in WIN95 and some other Windows
- ** versions. See if the version we are running on has it, and if it
- ** does, save off a pointer to it and the current process handle.
- */
- hProcess = GetCurrentProcess();
- if( hProcess ){
- HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
- if( NULL != hinstLib ){
- getProcessTimesAddr =
- (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
- if( NULL != getProcessTimesAddr ){
- return 1;
- }
- FreeLibrary(hinstLib);
- }
- }
-#endif
- }
- return 0;
-}
+/*
+** Return an estimate of the width, in columns, for the single Unicode
+** character c. For normal characters, the answer is always 1. But the
+** estimate might be 0 or 2 for zero-width and double-width characters.
+**
+** Different devices display unicode using different widths. So
+** it is impossible to know that true display width with 100% accuracy.
+** Inaccuracies in the width estimates might cause columns to be misaligned.
+** Unfortunately, there is nothing we can do about that.
+*/
+int sqlite3_qrf_wcwidth(int c);
/*
-** Begin timing an operation
+** Return an estimate of the number of display columns used by the
+** string in the argument. The width of individual characters is
+** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences
+** are assigned a width of zero.
*/
-static void beginTimer(void){
- if( enableTimer && getProcessTimesAddr ){
- FILETIME ftCreation, ftExit;
- getProcessTimesAddr(hProcess,&ftCreation,&ftExit,
- &ftKernelBegin,&ftUserBegin);
- ftWallBegin = timeOfDay();
- }
-}
+size_t sqlite3_qrf_wcswidth(const char*);
-/* Return the difference of two FILETIME structs in seconds */
-static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
- sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
- sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
- return (double) ((i64End - i64Start) / 10000000.0);
+
+#ifdef __cplusplus
}
+#endif
+#endif /* !defined(SQLITE_QRF_H) */
+/************************* End ext/qrf/qrf.h ********************/
+/************************* Begin ext/qrf/qrf.c ******************/
/*
-** Print the timing results.
+** 2025-10-20
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Implementation of the Query Result-Format or "qrf" utility library for
+** SQLite. See the README.md documentation for additional information.
*/
-static void endTimer(FILE *out){
- if( enableTimer && getProcessTimesAddr){
- FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
- sqlite3_int64 ftWallEnd = timeOfDay();
- getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
- sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n",
- (ftWallEnd - ftWallBegin)*0.001,
- timeDiff(&ftUserBegin, &ftUserEnd),
- timeDiff(&ftKernelBegin, &ftKernelEnd));
- }
-}
-
-#define BEGIN_TIMER beginTimer()
-#define END_TIMER(X) endTimer(X)
-#define HAS_TIMER hasTimer()
+#ifndef SQLITE_QRF_H
+#include "qrf.h"
+#endif
+#include <string.h>
+#include <assert.h>
+#include <stdint.h>
-#else
-#define BEGIN_TIMER
-#define END_TIMER(X) /*no-op*/
-#define HAS_TIMER 0
+#ifndef SQLITE_AMALGAMATION
+/* typedef sqlite3_int64 i64; */
#endif
+/* A single line in the EQP output */
+typedef struct qrfEQPGraphRow qrfEQPGraphRow;
+struct qrfEQPGraphRow {
+ int iEqpId; /* ID for this row */
+ int iParentId; /* ID of the parent row */
+ qrfEQPGraphRow *pNext; /* Next row in sequence */
+ char zText[1]; /* Text to display for this row */
+};
+
+/* All EQP output is collected into an instance of the following */
+typedef struct qrfEQPGraph qrfEQPGraph;
+struct qrfEQPGraph {
+ qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
+ qrfEQPGraphRow *pLast; /* Last element of the pRow list */
+ int nWidth; /* Width of the graph */
+ char zPrefix[400]; /* Graph prefix */
+};
+
/*
-** Used to prevent warnings about unused parameters
+** Private state information. Subject to change from one release to the
+** next.
*/
-#define UNUSED_PARAMETER(x) (void)(x)
+typedef struct Qrf Qrf;
+struct Qrf {
+ sqlite3_stmt *pStmt; /* The statement whose output is to be rendered */
+ sqlite3 *db; /* The corresponding database connection */
+ sqlite3_stmt *pJTrans; /* JSONB to JSON translator statement */
+ char **pzErr; /* Write error message here, if not NULL */
+ sqlite3_str *pOut; /* Accumulated output */
+ int iErr; /* Error code */
+ int nCol; /* Number of output columns */
+ int expMode; /* Original sqlite3_stmt_isexplain() plus 1 */
+ int mxWidth; /* Screen width */
+ int mxHeight; /* nLineLimit */
+ union {
+ struct { /* Content for QRF_STYLE_Line */
+ int mxColWth; /* Maximum display width of any column */
+ char **azCol; /* Names of output columns (MODE_Line) */
+ } sLine;
+ qrfEQPGraph *pGraph; /* EQP graph (Eqp, Stats, and StatsEst) */
+ struct { /* Content for QRF_STYLE_Explain */
+ int nIndent; /* Slots allocated for aiIndent */
+ int iIndent; /* Current slot */
+ int *aiIndent; /* Indentation for each opcode */
+ } sExpln;
+ unsigned int nIns; /* Bytes used for current INSERT stmt */
+ } u;
+ sqlite3_int64 nRow; /* Number of rows handled so far */
+ int *actualWidth; /* Actual width of each column */
+ sqlite3_qrf_spec spec; /* Copy of the original spec */
+};
/*
-** Number of elements in an array
+** Data for substitute ctype.h functions. Used for x-platform
+** consistency and so that '_' is counted as an alphabetic
+** character.
+**
+** 0x01 - space
+** 0x02 - digit
+** 0x04 - alphabetic, including '_'
*/
-#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
+static const char qrfCType[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4,
+ 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+#define qrfSpace(x) ((qrfCType[(unsigned char)x]&1)!=0)
+#define qrfDigit(x) ((qrfCType[(unsigned char)x]&2)!=0)
+#define qrfAlpha(x) ((qrfCType[(unsigned char)x]&4)!=0)
+#define qrfAlnum(x) ((qrfCType[(unsigned char)x]&6)!=0)
+
+#ifndef deliberate_fall_through
+/* Quiet some compilers about some of our intentional code. */
+# if defined(GCC_VERSION) && GCC_VERSION>=7000000
+# define deliberate_fall_through __attribute__((fallthrough));
+# else
+# define deliberate_fall_through
+# endif
+#endif
/*
-** If the following flag is set, then command execution stops
-** at an error if we are not interactive.
+** Set an error code and error message.
*/
-static int bail_on_error = 0;
+static void qrfError(
+ Qrf *p, /* Query result state */
+ int iCode, /* Error code */
+ const char *zFormat, /* Message format (or NULL) */
+ ...
+){
+ p->iErr = iCode;
+ if( p->pzErr!=0 ){
+ sqlite3_free(*p->pzErr);
+ *p->pzErr = 0;
+ if( zFormat ){
+ va_list ap;
+ va_start(ap, zFormat);
+ *p->pzErr = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ }
+ }
+}
/*
-** Treat stdin as an interactive input if the following variable
-** is true. Otherwise, assume stdin is connected to a file or pipe.
+** Out-of-memory error.
*/
-static int stdin_is_interactive = 1;
+static void qrfOom(Qrf *p){
+ qrfError(p, SQLITE_NOMEM, "out of memory");
+}
/*
-** On Windows systems we need to know if standard output is a console
-** in order to show that UTF-16 translation is done in the sign-on
-** banner. The following variable is true if it is the console.
+** Transfer any error in pStr over into p.
*/
-static int stdout_is_console = 1;
+static void qrfStrErr(Qrf *p, sqlite3_str *pStr){
+ int rc = pStr ? sqlite3_str_errcode(pStr) : 0;
+ if( rc ){
+ qrfError(p, rc, sqlite3_errstr(rc));
+ }
+}
+
/*
-** The following is the open SQLite database. We make a pointer
-** to this database a static variable so that it can be accessed
-** by the SIGINT handler to interrupt database processing.
+** Add a new entry to the EXPLAIN QUERY PLAN data
*/
-static sqlite3 *globalDb = 0;
+static void qrfEqpAppend(Qrf *p, int iEqpId, int p2, const char *zText){
+ qrfEQPGraphRow *pNew;
+ sqlite3_int64 nText;
+ if( zText==0 ) return;
+ if( p->u.pGraph==0 ){
+ p->u.pGraph = sqlite3_malloc64( sizeof(qrfEQPGraph) );
+ if( p->u.pGraph==0 ){
+ qrfOom(p);
+ return;
+ }
+ memset(p->u.pGraph, 0, sizeof(qrfEQPGraph) );
+ }
+ nText = strlen(zText);
+ pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
+ if( pNew==0 ){
+ qrfOom(p);
+ return;
+ }
+ pNew->iEqpId = iEqpId;
+ pNew->iParentId = p2;
+ memcpy(pNew->zText, zText, nText+1);
+ pNew->pNext = 0;
+ if( p->u.pGraph->pLast ){
+ p->u.pGraph->pLast->pNext = pNew;
+ }else{
+ p->u.pGraph->pRow = pNew;
+ }
+ p->u.pGraph->pLast = pNew;
+}
/*
-** True if an interrupt (Control-C) has been received.
+** Free and reset the EXPLAIN QUERY PLAN data that has been collected
+** in p->u.pGraph.
*/
-static volatile int seenInterrupt = 0;
+static void qrfEqpReset(Qrf *p){
+ qrfEQPGraphRow *pRow, *pNext;
+ if( p->u.pGraph ){
+ for(pRow = p->u.pGraph->pRow; pRow; pRow = pNext){
+ pNext = pRow->pNext;
+ sqlite3_free(pRow);
+ }
+ sqlite3_free(p->u.pGraph);
+ p->u.pGraph = 0;
+ }
+}
-/*
-** This is the name of our program. It is set in main(), used
-** in a number of other places, mostly for error messages.
+/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
+** pOld, or return the first such line if pOld is NULL
*/
-static char *Argv0;
+static qrfEQPGraphRow *qrfEqpNextRow(Qrf *p, int iEqpId, qrfEQPGraphRow *pOld){
+ qrfEQPGraphRow *pRow = pOld ? pOld->pNext : p->u.pGraph->pRow;
+ while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
+ return pRow;
+}
+
+/* Render a single level of the graph that has iEqpId as its parent. Called
+** recursively to render sublevels.
+*/
+static void qrfEqpRenderLevel(Qrf *p, int iEqpId){
+ qrfEQPGraphRow *pRow, *pNext;
+ i64 n = strlen(p->u.pGraph->zPrefix);
+ char *z;
+ for(pRow = qrfEqpNextRow(p, iEqpId, 0); pRow; pRow = pNext){
+ pNext = qrfEqpNextRow(p, iEqpId, pRow);
+ z = pRow->zText;
+ sqlite3_str_appendf(p->pOut, "%s%s%s\n", p->u.pGraph->zPrefix,
+ pNext ? "|--" : "`--", z);
+ if( n<(i64)sizeof(p->u.pGraph->zPrefix)-7 ){
+ memcpy(&p->u.pGraph->zPrefix[n], pNext ? "| " : " ", 4);
+ qrfEqpRenderLevel(p, pRow->iEqpId);
+ p->u.pGraph->zPrefix[n] = 0;
+ }
+ }
+}
/*
-** Prompt strings. Initialized in main. Settable with
-** .prompt main continue
+** Render the 64-bit value N in a more human-readable format into
+** pOut.
+**
+** + Only show the first three significant digits.
+** + Append suffixes K, M, G, T, P, and E for 1e3, 1e6, ... 1e18
*/
-#define PROMPT_LEN_MAX 128
-/* First line prompt. default: "sqlite> " */
-static char mainPrompt[PROMPT_LEN_MAX];
-/* Continuation prompt. default: " ...> " */
-static char continuePrompt[PROMPT_LEN_MAX];
+static void qrfApproxInt64(sqlite3_str *pOut, i64 N){
+ static const char aSuffix[] = { 'K', 'M', 'G', 'T', 'P', 'E' };
+ int i;
+ if( N<0 ){
+ N = N==INT64_MIN ? INT64_MAX : -N;
+ sqlite3_str_append(pOut, "-", 1);
+ }
+ if( N<10000 ){
+ sqlite3_str_appendf(pOut, "%4lld ", N);
+ return;
+ }
+ for(i=1; i<=18; i++){
+ N = (N+5)/10;
+ if( N<10000 ){
+ int n = (int)N;
+ switch( i%3 ){
+ case 0:
+ sqlite3_str_appendf(pOut, "%d.%02d", n/1000, (n%1000)/10);
+ break;
+ case 1:
+ sqlite3_str_appendf(pOut, "%2d.%d", n/100, (n%100)/10);
+ break;
+ case 2:
+ sqlite3_str_appendf(pOut, "%4d", n/10);
+ break;
+ }
+ sqlite3_str_append(pOut, &aSuffix[i/3], 1);
+ break;
+ }
+ }
+}
-/* This is variant of the standard-library strncpy() routine with the
-** one change that the destination string is always zero-terminated, even
-** if there is no zero-terminator in the first n-1 characters of the source
-** string.
+/*
+** Display and reset the EXPLAIN QUERY PLAN data
*/
-static char *shell_strncpy(char *dest, const char *src, size_t n){
- size_t i;
- for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i];
- dest[i] = 0;
- return dest;
+static void qrfEqpRender(Qrf *p, i64 nCycle){
+ qrfEQPGraphRow *pRow;
+ if( p->u.pGraph!=0 && (pRow = p->u.pGraph->pRow)!=0 ){
+ if( pRow->zText[0]=='-' ){
+ if( pRow->pNext==0 ){
+ qrfEqpReset(p);
+ return;
+ }
+ sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3);
+ p->u.pGraph->pRow = pRow->pNext;
+ sqlite3_free(pRow);
+ }else if( nCycle>0 ){
+ int nSp = p->u.pGraph->nWidth - 2;
+ if( p->spec.eStyle==QRF_STYLE_StatsEst ){
+ sqlite3_str_appendchar(p->pOut, nSp, ' ');
+ sqlite3_str_appendall(p->pOut,
+ "Cycles Loops (est) Rows (est)\n");
+ sqlite3_str_appendchar(p->pOut, nSp, ' ');
+ sqlite3_str_appendall(p->pOut,
+ "---------- ------------ ------------\n");
+ }else{
+ sqlite3_str_appendchar(p->pOut, nSp, ' ');
+ sqlite3_str_appendall(p->pOut,
+ "Cycles Loops Rows \n");
+ sqlite3_str_appendchar(p->pOut, nSp, ' ');
+ sqlite3_str_appendall(p->pOut,
+ "---------- ----- -----\n");
+ }
+ sqlite3_str_appendall(p->pOut, "QUERY PLAN");
+ sqlite3_str_appendchar(p->pOut, nSp - 10, ' ');
+ qrfApproxInt64(p->pOut, nCycle);
+ sqlite3_str_appendall(p->pOut, " 100%\n");
+ }else{
+ sqlite3_str_appendall(p->pOut, "QUERY PLAN\n");
+ }
+ p->u.pGraph->zPrefix[0] = 0;
+ qrfEqpRenderLevel(p, 0);
+ qrfEqpReset(p);
+ }
}
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
-** strcpy() workalike to squelch an unwarranted link-time warning
-** from OpenBSD.
+** Helper function for qrfExpStats().
+**
*/
-static void shell_strcpy(char *dest, const char *src){
- while( (*(dest++) = *(src++))!=0 ){}
+static int qrfStatsHeight(sqlite3_stmt *p, int iEntry){
+ int iPid = 0;
+ int ret = 1;
+ sqlite3_stmt_scanstatus_v2(p, iEntry,
+ SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
+ );
+ while( iPid!=0 ){
+ int ii;
+ for(ii=0; 1; ii++){
+ int iId;
+ int res;
+ res = sqlite3_stmt_scanstatus_v2(p, ii,
+ SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId
+ );
+ if( res ) break;
+ if( iId==iPid ){
+ sqlite3_stmt_scanstatus_v2(p, ii,
+ SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
+ );
+ }
+ }
+ ret++;
+ }
+ return ret;
}
+#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
+
/*
-** Optionally disable dynamic continuation prompt.
-** Unless disabled, the continuation prompt shows open SQL lexemes if any,
-** or open parentheses level if non-zero, or continuation prompt as set.
-** This facility interacts with the scanner and process_input() where the
-** below 5 macros are used.
+** Generate ".scanstatus est" style of EQP output.
*/
-#ifdef SQLITE_OMIT_DYNAPROMPT
-# define CONTINUATION_PROMPT continuePrompt
-# define CONTINUE_PROMPT_RESET
-# define CONTINUE_PROMPT_AWAITS(p,s)
-# define CONTINUE_PROMPT_AWAITC(p,c)
-# define CONTINUE_PAREN_INCR(p,n)
-# define CONTINUE_PROMPT_PSTATE 0
-typedef void *t_NoDynaPrompt;
-# define SCAN_TRACKER_REFTYPE t_NoDynaPrompt
+static void qrfEqpStats(Qrf *p){
+#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
+ qrfError(p, SQLITE_ERROR, "not available in this build");
#else
-# define CONTINUATION_PROMPT dynamicContinuePrompt()
-# define CONTINUE_PROMPT_RESET \
- do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0)
-# define CONTINUE_PROMPT_AWAITS(p,s) \
- if(p && stdin_is_interactive) setLexemeOpen(p, s, 0)
-# define CONTINUE_PROMPT_AWAITC(p,c) \
- if(p && stdin_is_interactive) setLexemeOpen(p, 0, c)
-# define CONTINUE_PAREN_INCR(p,n) \
- if(p && stdin_is_interactive) (trackParenLevel(p,n))
-# define CONTINUE_PROMPT_PSTATE (&dynPrompt)
-typedef struct DynaPrompt *t_DynaPromptRef;
-# define SCAN_TRACKER_REFTYPE t_DynaPromptRef
+ static const int f = SQLITE_SCANSTAT_COMPLEX;
+ sqlite3_stmt *pS = p->pStmt;
+ int i = 0;
+ i64 nTotal = 0;
+ int nWidth = 0;
+ int prevPid = -1; /* Previous iPid */
+ double rEstCum = 1.0; /* Cumulative row estimate */
+ sqlite3_str *pLine = sqlite3_str_new(p->db);
+ sqlite3_str *pStats = sqlite3_str_new(p->db);
+ qrfEqpReset(p);
-static struct DynaPrompt {
- char dynamicPrompt[PROMPT_LEN_MAX];
- char acAwait[2];
- int inParenLevel;
- char *zScannerAwaits;
-} dynPrompt = { {0}, {0}, 0, 0 };
+ for(i=0; 1; i++){
+ const char *z = 0;
+ int n = 0;
+ if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
+ break;
+ }
+ n = (int)strlen(z) + qrfStatsHeight(pS,i)*3;
+ if( n>nWidth ) nWidth = n;
+ }
+ nWidth += 2;
-/* Record parenthesis nesting level change, or force level to 0. */
-static void trackParenLevel(struct DynaPrompt *p, int ni){
- p->inParenLevel += ni;
- if( ni==0 ) p->inParenLevel = 0;
- p->zScannerAwaits = 0;
-}
+ sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal);
+ for(i=0; 1; i++){
+ i64 nLoop = 0;
+ i64 nRow = 0;
+ i64 nCycle = 0;
+ int iId = 0;
+ int iPid = 0;
+ const char *zo = 0;
+ const char *zName = 0;
+ double rEst = 0.0;
-/* Record that a lexeme is opened, or closed with args==0. */
-static void setLexemeOpen(struct DynaPrompt *p, char *s, char c){
- if( s!=0 || c==0 ){
- p->zScannerAwaits = s;
- p->acAwait[0] = 0;
- }else{
- p->acAwait[0] = c;
- p->zScannerAwaits = p->acAwait;
- }
-}
+ if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){
+ break;
+ }
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
+ if( iPid!=prevPid ){
+ prevPid = iPid;
+ rEstCum = 1.0;
+ }
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst);
+ rEstCum *= rEst;
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop);
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow);
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
+ sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
-/* Upon demand, derive the continuation prompt to display. */
-static char *dynamicContinuePrompt(void){
- if( continuePrompt[0]==0
- || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
- return continuePrompt;
- }else{
- if( dynPrompt.zScannerAwaits ){
- size_t ncp = strlen(continuePrompt);
- size_t ndp = strlen(dynPrompt.zScannerAwaits);
- if( ndp > ncp-3 ) return continuePrompt;
- shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
- while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
- shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
- PROMPT_LEN_MAX-4);
- }else{
- if( dynPrompt.inParenLevel>9 ){
- shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4);
- }else if( dynPrompt.inParenLevel<0 ){
- shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
- }else{
- shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
- dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
+ if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
+ int nSp = 0;
+ sqlite3_str_reset(pStats);
+ if( nCycle>=0 && nTotal>0 ){
+ qrfApproxInt64(pStats, nCycle);
+ sqlite3_str_appendf(pStats, " %3d%%",
+ ((nCycle*100)+nTotal/2) / nTotal
+ );
+ nSp = 2;
}
- shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
- PROMPT_LEN_MAX-4);
+ if( nLoop>=0 ){
+ if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' ');
+ qrfApproxInt64(pStats, nLoop);
+ nSp = 2;
+ if( p->spec.eStyle==QRF_STYLE_StatsEst ){
+ sqlite3_str_appendf(pStats, " ");
+ qrfApproxInt64(pStats, (i64)(rEstCum/rEst));
+ }
+ }
+ if( nRow>=0 ){
+ if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' ');
+ qrfApproxInt64(pStats, nRow);
+ nSp = 2;
+ if( p->spec.eStyle==QRF_STYLE_StatsEst ){
+ sqlite3_str_appendf(pStats, " ");
+ qrfApproxInt64(pStats, (i64)rEstCum);
+ }
+ }
+ sqlite3_str_appendf(pLine,
+ "% *s %s", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo,
+ sqlite3_str_value(pStats)
+ );
+ sqlite3_str_reset(pStats);
+ qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine));
+ sqlite3_str_reset(pLine);
+ }else{
+ qrfEqpAppend(p, iId, iPid, zo);
}
}
- return dynPrompt.dynamicPrompt;
-}
-#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
-
-/* Indicate out-of-memory and exit. */
-static void shell_out_of_memory(void){
- eputz("Error: out of memory\n");
- exit(1);
+ if( p->u.pGraph ) p->u.pGraph->nWidth = nWidth;
+ qrfStrErr(p, pLine);
+ sqlite3_free(sqlite3_str_finish(pLine));
+ qrfStrErr(p, pStats);
+ sqlite3_free(sqlite3_str_finish(pStats));
+#endif
}
-/* Check a pointer to see if it is NULL. If it is NULL, exit with an
-** out-of-memory error.
-*/
-static void shell_check_oom(const void *p){
- if( p==0 ) shell_out_of_memory();
-}
/*
-** Write I/O traces to the following stream.
+** Reset the prepared statement.
*/
-#ifdef SQLITE_ENABLE_IOTRACE
-static FILE *iotrace = 0;
-#endif
+static void qrfResetStmt(Qrf *p){
+ int rc = sqlite3_reset(p->pStmt);
+ if( rc!=SQLITE_OK && p->iErr==SQLITE_OK ){
+ qrfError(p, rc, "%s", sqlite3_errmsg(p->db));
+ }
+}
/*
-** This routine works like printf in that its first argument is a
-** format string and subsequent arguments are values to be substituted
-** in place of % fields. The result of formatting this string
-** is written to iotrace.
+** If xWrite is defined, send all content of pOut to xWrite and
+** reset pOut.
*/
-#ifdef SQLITE_ENABLE_IOTRACE
-static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
- va_list ap;
- char *z;
- if( iotrace==0 ) return;
- va_start(ap, zFormat);
- z = sqlite3_vmprintf(zFormat, ap);
- va_end(ap);
- sqlite3_fprintf(iotrace, "%s", z);
- sqlite3_free(z);
+static void qrfWrite(Qrf *p){
+ int n;
+ if( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){
+ int rc = p->spec.xWrite(p->spec.pWriteArg,
+ sqlite3_str_value(p->pOut),
+ (sqlite3_int64)n);
+ sqlite3_str_reset(p->pOut);
+ if( rc ){
+ qrfError(p, rc, "Failed to write %d bytes of output", n);
+ }
+ }
}
-#endif
/* Lookup table to estimate the number of columns consumed by a Unicode
** character.
@@ -1010,7 +1366,7 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
static const struct {
unsigned char w; /* Width of the character in columns */
int iFirst; /* First character in a span having this width */
-} aUWidth[] = {
+} aQrfUWidth[] = {
/* {1, 0x00000}, */
{0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488},
{1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0},
@@ -1046,7 +1402,7 @@ static const struct {
{1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88},
{0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6},
{1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033},
- {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058},
+ {0, 0x01036}, {1, 0x0103b}, {0, 0x01058},
{1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f},
{1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735},
{0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4},
@@ -1085,37 +1441,38 @@ static const struct {
** Inaccuracies in the width estimates might cause columns to be misaligned.
** Unfortunately, there is nothing we can do about that.
*/
-int cli_wcwidth(int c){
+int sqlite3_qrf_wcwidth(int c){
int iFirst, iLast;
/* Fast path for common characters */
- if( c<=0x300 ) return 1;
+ if( c<0x300 ) return 1;
/* The general case */
iFirst = 0;
- iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1;
+ iLast = sizeof(aQrfUWidth)/sizeof(aQrfUWidth[0]) - 1;
while( iFirst<iLast-1 ){
int iMid = (iFirst+iLast)/2;
- int cMid = aUWidth[iMid].iFirst;
+ int cMid = aQrfUWidth[iMid].iFirst;
if( cMid < c ){
iFirst = iMid;
}else if( cMid > c ){
iLast = iMid - 1;
}else{
- return aUWidth[iMid].w;
+ return aQrfUWidth[iMid].w;
}
}
- if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w;
- return aUWidth[iLast].w;
+ if( aQrfUWidth[iLast].iFirst > c ) return aQrfUWidth[iFirst].w;
+ return aQrfUWidth[iLast].w;
}
/*
** Compute the value and length of a multi-byte UTF-8 character that
-** begins at z[0]. Return the length. Write the Unicode value into *pU.
+** begins at z[0]. Return the length. Write the Unicode value into *pU.
**
-** This routine only works for *multi-byte* UTF-8 characters.
+** This routine only works for *multi-byte* UTF-8 characters. It does
+** not attempt to detect illegal characters.
*/
-static int decodeUtf8(const unsigned char *z, int *pU){
+int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){
if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){
*pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f);
return 2;
@@ -1128,89 +1485,686 @@ static int decodeUtf8(const unsigned char *z, int *pU){
&& (z[3] & 0xc0)==0x80
){
*pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
- | (z[4] & 0x3f);
+ | (z[3] & 0x3f);
return 4;
}
*pU = 0;
return 1;
}
+/*
+** Check to see if z[] is a valid VT100 escape. If it is, then
+** return the number of bytes in the escape sequence. Return 0 if
+** z[] is not a VT100 escape.
+**
+** This routine assumes that z[0] is \033 (ESC).
+*/
+static int qrfIsVt100(const unsigned char *z){
+ int i;
+ if( z[1]!='[' ) return 0;
+ i = 2;
+ while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
+ while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
+ if( z[i]<0x40 || z[i]>0x7e ) return 0;
+ return i+1;
+}
+
+/*
+** Return the length of a string in display characters.
+**
+** Most characters of the input string count as 1, including
+** multi-byte UTF8 characters. However, zero-width unicode
+** characters and VT100 escape sequences count as zero, and
+** double-width characters count as two.
+**
+** The definition of "zero-width" and "double-width" characters
+** is not precise. It depends on the output device, to some extent,
+** and it varies according to the Unicode version. This routine
+** makes the best guess that it can.
+*/
+size_t sqlite3_qrf_wcswidth(const char *zIn){
+ const unsigned char *z = (const unsigned char*)zIn;
+ size_t n = 0;
+ while( *z ){
+ if( z[0]<' ' ){
+ int k;
+ if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){
+ z += k;
+ }else{
+ z++;
+ }
+ }else if( (0x80&z[0])==0 ){
+ n++;
+ z++;
+ }else{
+ int u = 0;
+ int len = sqlite3_qrf_decode_utf8(z, &u);
+ z += len;
+ n += sqlite3_qrf_wcwidth(u);
+ }
+ }
+ return n;
+}
-#if 0 /* NOT USED */
/*
-** Return the width, in display columns, of a UTF-8 string.
+** Return the display width of the longest line of text
+** in the (possibly) multi-line input string zIn[0..nByte].
+** zIn[] is not necessarily zero-terminated. Take
+** into account tab characters, zero- and double-width
+** characters, CR and NL, and VT100 escape codes.
**
-** Each normal character counts as 1. Zero-width characters count
-** as zero, and double-width characters count as 2.
+** Write the number of newlines into *pnNL. So, *pnNL will
+** return 0 if everything fits on one line, or positive it
+** it will need to be split.
*/
-int cli_wcswidth(const char *z){
- const unsigned char *a = (const unsigned char*)z;
+static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){
+ const unsigned char *z;
+ const unsigned char *zEnd;
+ int mx = 0;
int n = 0;
- int i = 0;
- unsigned char c;
- while( (c = a[i])!=0 ){
- if( c>=0xc0 ){
- int u;
- int len = decodeUtf8(&a[i], &u);
- i += len;
- n += cli_wcwidth(u);
- }else if( c>=' ' ){
+ int nNL = 0;
+ if( zIn==0 ) zIn = "";
+ z = (const unsigned char*)zIn;
+ zEnd = &z[nByte];
+ while( z<zEnd ){
+ if( z[0]<' ' ){
+ int k;
+ if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){
+ z += k;
+ }else{
+ if( z[0]=='\t' ){
+ n = (n+8)&~7;
+ }else if( z[0]=='\n' || z[0]=='\r' ){
+ nNL++;
+ if( n>mx ) mx = n;
+ n = 0;
+ }
+ z++;
+ }
+ }else if( (0x80&z[0])==0 ){
n++;
- i++;
+ z++;
}else{
- i++;
+ int u = 0;
+ int len = sqlite3_qrf_decode_utf8(z, &u);
+ z += len;
+ n += sqlite3_qrf_wcwidth(u);
}
}
+ if( mx>n ) n = mx;
+ if( pnNL ) *pnNL = nNL;
return n;
}
-#endif
/*
-** Check to see if z[] is a valid VT100 escape. If it is, then
-** return the number of bytes in the escape sequence. Return 0 if
-** z[] is not a VT100 escape.
+** Escape the input string if it is needed and in accordance with
+** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol.
**
-** This routine assumes that z[0] is \033 (ESC).
+** Escaping is needed if the string contains any control characters
+** other than \t, \n, and \r\n
+**
+** If no escaping is needed (the common case) then set *ppOut to NULL
+** and return 0. If escaping is needed, write the escaped string into
+** memory obtained from sqlite3_malloc64() and make *ppOut point to that
+** memory and return 0. If an error occurs, return non-zero.
+**
+** The caller is responsible for freeing *ppFree if it is non-NULL in order
+** to reclaim memory.
*/
-static int isVt100(const unsigned char *z){
- int i;
- if( z[1]!='[' ) return 0;
- i = 2;
- while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
- while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
- if( z[i]<0x40 || z[i]>0x7e ) return 0;
- return i+1;
+static void qrfEscape(
+ int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */
+ sqlite3_str *pStr, /* String to be escaped */
+ int iStart /* Begin escapding on this byte of pStr */
+){
+ sqlite3_int64 i, j; /* Loop counters */
+ sqlite3_int64 sz; /* Size of the string prior to escaping */
+ sqlite3_int64 nCtrl = 0;/* Number of control characters to escape */
+ unsigned char *zIn; /* Text to be escaped */
+ unsigned char c; /* A single character of the text */
+ unsigned char *zOut; /* Where to write the results */
+
+ /* Find the text to be escaped */
+ zIn = (unsigned char*)sqlite3_str_value(pStr);
+ if( zIn==0 ) return;
+ zIn += iStart;
+
+ /* Count the control characters */
+ for(i=0; (c = zIn[i])!=0; i++){
+ if( c<=0x1f
+ && c!='\t'
+ && c!='\n'
+ && (c!='\r' || zIn[i+1]!='\n')
+ ){
+ nCtrl++;
+ }
+ }
+ if( nCtrl==0 ) return; /* Early out if no control characters */
+
+ /* Make space to hold the escapes. Copy the original text to the end
+ ** of the available space. */
+ sz = sqlite3_str_length(pStr) - iStart;
+ if( eEsc==QRF_ESC_Symbol ) nCtrl *= 2;
+ sqlite3_str_appendchar(pStr, nCtrl, ' ');
+ zOut = (unsigned char*)sqlite3_str_value(pStr);
+ if( zOut==0 ) return;
+ zOut += iStart;
+ zIn = zOut + nCtrl;
+ memmove(zIn,zOut,sz);
+
+ /* Convert the control characters */
+ for(i=j=0; (c = zIn[i])!=0; i++){
+ if( c>0x1f
+ || c=='\t'
+ || c=='\n'
+ || (c=='\r' && zIn[i+1]=='\n')
+ ){
+ continue;
+ }
+ if( i>0 ){
+ memmove(&zOut[j], zIn, i);
+ j += i;
+ }
+ zIn += i+1;
+ i = -1;
+ if( eEsc==QRF_ESC_Symbol ){
+ zOut[j++] = 0xe2;
+ zOut[j++] = 0x90;
+ zOut[j++] = 0x80+c;
+ }else{
+ zOut[j++] = '^';
+ zOut[j++] = 0x40+c;
+ }
+ }
}
/*
-** Output string zUtf to stdout as w characters. If w is negative,
-** then right-justify the text. W is the width in UTF-8 characters, not
-** in bytes. This is different from the %*.*s specification in printf
-** since with %*.*s the width is measured in bytes, not characters.
+** Determine if the string z[] can be shown as plain text. Return true
+** if z[] is unambiguously text. Return false if z[] needs to be
+** quoted.
+**
+** All of the following must be true in order for z[] to be relaxable:
**
-** Take into account zero-width and double-width Unicode characters.
-** In other words, a zero-width character does not count toward the
-** the w limit. A double-width character counts as two.
+** (1) z[] does not begin or end with ' or whitespace
+** (2) z[] is not the same as the NULL rendering
+** (3) z[] does not looks like a numeric literal
*/
-static void utf8_width_print(FILE *out, int w, const char *zUtf){
+static int qrfRelaxable(Qrf *p, const char *z){
+ size_t i, n;
+ if( z[0]=='\'' || qrfSpace(z[0]) ) return 0;
+ if( z[0]==0 ){
+ return (p->spec.zNull!=0 && p->spec.zNull[0]!=0);
+ }
+ n = strlen(z);
+ if( n==0 || z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0;
+ if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0;
+ i = (z[0]=='-' || z[0]=='+');
+ if( strcmp(z+i,"Inf")==0 ) return 0;
+ if( !qrfDigit(z[i]) ) return 1;
+ i++;
+ while( qrfDigit(z[i]) ){ i++; }
+ if( z[i]==0 ) return 0;
+ if( z[i]=='.' ){
+ i++;
+ while( qrfDigit(z[i]) ){ i++; }
+ if( z[i]==0 ) return 0;
+ }
+ if( z[i]=='e' || z[i]=='E' ){
+ i++;
+ if( z[i]=='+' || z[i]=='-' ){ i++; }
+ if( !qrfDigit(z[i]) ) return 1;
+ i++;
+ while( qrfDigit(z[i]) ){ i++; }
+ }
+ return z[i]!=0;
+}
+
+/*
+** If a field contains any character identified by a 1 in the following
+** array, then the string must be quoted for CSV.
+*/
+static const char qrfCsvQuote[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+};
+
+/*
+** Encode text appropriately and append it to pOut.
+*/
+static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){
+ int iStart = sqlite3_str_length(pOut);
+ switch( p->spec.eText ){
+ case QRF_TEXT_Relaxed:
+ if( qrfRelaxable(p, zTxt) ){
+ sqlite3_str_appendall(pOut, zTxt);
+ break;
+ }
+ deliberate_fall_through; /* FALLTHRU */
+ case QRF_TEXT_Sql: {
+ if( p->spec.eEsc==QRF_ESC_Off ){
+ sqlite3_str_appendf(pOut, "%Q", zTxt);
+ }else{
+ sqlite3_str_appendf(pOut, "%#Q", zTxt);
+ }
+ break;
+ }
+ case QRF_TEXT_Csv: {
+ unsigned int i;
+ for(i=0; zTxt[i]; i++){
+ if( qrfCsvQuote[((const unsigned char*)zTxt)[i]] ){
+ i = 0;
+ break;
+ }
+ }
+ if( i==0 || strstr(zTxt, p->spec.zColumnSep)!=0 ){
+ sqlite3_str_appendf(pOut, "\"%w\"", zTxt);
+ }else{
+ sqlite3_str_appendall(pOut, zTxt);
+ }
+ break;
+ }
+ case QRF_TEXT_Html: {
+ const unsigned char *z = (const unsigned char*)zTxt;
+ while( *z ){
+ unsigned int i = 0;
+ unsigned char c;
+ while( (c=z[i])>'>'
+ || (c && c!='<' && c!='>' && c!='&' && c!='\"' && c!='\'')
+ ){
+ i++;
+ }
+ if( i>0 ){
+ sqlite3_str_append(pOut, (const char*)z, i);
+ }
+ switch( z[i] ){
+ case '>': sqlite3_str_append(pOut, "&lt;", 4); break;
+ case '&': sqlite3_str_append(pOut, "&amp;", 5); break;
+ case '<': sqlite3_str_append(pOut, "&lt;", 4); break;
+ case '"': sqlite3_str_append(pOut, "&quot;", 6); break;
+ case '\'': sqlite3_str_append(pOut, "&#39;", 5); break;
+ default: i--;
+ }
+ z += i + 1;
+ }
+ break;
+ }
+ case QRF_TEXT_Tcl:
+ case QRF_TEXT_Json: {
+ const unsigned char *z = (const unsigned char*)zTxt;
+ sqlite3_str_append(pOut, "\"", 1);
+ while( *z ){
+ unsigned int i;
+ for(i=0; z[i]>=0x20 && z[i]!='\\' && z[i]!='"'; i++){}
+ if( i>0 ){
+ sqlite3_str_append(pOut, (const char*)z, i);
+ }
+ if( z[i]==0 ) break;
+ switch( z[i] ){
+ case '"': sqlite3_str_append(pOut, "\\\"", 2); break;
+ case '\\': sqlite3_str_append(pOut, "\\\\", 2); break;
+ case '\b': sqlite3_str_append(pOut, "\\b", 2); break;
+ case '\f': sqlite3_str_append(pOut, "\\f", 2); break;
+ case '\n': sqlite3_str_append(pOut, "\\n", 2); break;
+ case '\r': sqlite3_str_append(pOut, "\\r", 2); break;
+ case '\t': sqlite3_str_append(pOut, "\\t", 2); break;
+ default: {
+ if( p->spec.eText==QRF_TEXT_Json ){
+ sqlite3_str_appendf(pOut, "\\u%04x", z[i]);
+ }else{
+ sqlite3_str_appendf(pOut, "\\%03o", z[i]);
+ }
+ break;
+ }
+ }
+ z += i + 1;
+ }
+ sqlite3_str_append(pOut, "\"", 1);
+ break;
+ }
+ default: {
+ sqlite3_str_appendall(pOut, zTxt);
+ break;
+ }
+ }
+ if( p->spec.eEsc!=QRF_ESC_Off ){
+ qrfEscape(p->spec.eEsc, pOut, iStart);
+ }
+}
+
+/*
+** Do a quick sanity check to see aBlob[0..nBlob-1] is valid JSONB
+** return true if it is and false if it is not.
+**
+** False positives are possible, but not false negatives.
+*/
+static int qrfJsonbQuickCheck(unsigned char *aBlob, int nBlob){
+ unsigned char x; /* Payload size half-byte */
+ int i; /* Loop counter */
+ int n; /* Bytes in the payload size integer */
+ sqlite3_uint64 sz; /* value of the payload size integer */
+
+ if( nBlob==0 ) return 0;
+ x = aBlob[0]>>4;
+ if( x<=11 ) return nBlob==(1+x);
+ n = x<14 ? x-11 : 4*(x-13);
+ if( nBlob<1+n ) return 0;
+ sz = aBlob[1];
+ for(i=1; i<n; i++) sz = (sz<<8) + aBlob[i+1];
+ return sz+n+1==(sqlite3_uint64)nBlob;
+}
+
+/*
+** The current iCol-th column of p->pStmt is known to be a BLOB. Check
+** to see if that BLOB is really a JSONB blob. If it is, then translate
+** it into a text JSON representation and return a pointer to that text JSON.
+** If the BLOB is not JSONB, then return a NULL pointer.
+**
+** The memory used to hold the JSON text is managed internally by the
+** "p" object and is overwritten and/or deallocated upon the next call
+** to this routine (with the same p argument) or when the p object is
+** finailized.
+*/
+static const char *qrfJsonbToJson(Qrf *p, int iCol){
+ int nByte;
+ const void *pBlob;
+ int rc;
+ nByte = sqlite3_column_bytes(p->pStmt, iCol);
+ pBlob = sqlite3_column_blob(p->pStmt, iCol);
+ if( qrfJsonbQuickCheck((unsigned char*)pBlob, nByte)==0 ){
+ return 0;
+ }
+ if( p->pJTrans==0 ){
+ sqlite3 *db;
+ rc = sqlite3_open(":memory:",&db);
+ if( rc ){
+ sqlite3_close(db);
+ return 0;
+ }
+ rc = sqlite3_prepare_v2(db, "SELECT json(?1)", -1, &p->pJTrans, 0);
+ if( rc ){
+ sqlite3_finalize(p->pJTrans);
+ p->pJTrans = 0;
+ sqlite3_close(db);
+ return 0;
+ }
+ }else{
+ sqlite3_reset(p->pJTrans);
+ }
+ sqlite3_bind_blob(p->pJTrans, 1, (void*)pBlob, nByte, SQLITE_STATIC);
+ rc = sqlite3_step(p->pJTrans);
+ if( rc==SQLITE_ROW ){
+ return (const char*)sqlite3_column_text(p->pJTrans, 0);
+ }else{
+ return 0;
+ }
+}
+
+/*
+** Adjust the input string zIn[] such that it is no more than N display
+** characters wide. If it is wider than that, then truncate and add
+** ellipsis. Or if zIn[] contains a \r or \n, truncate at that point,
+** adding ellipsis. Embedded tabs in zIn[] are converted into ordinary
+** spaces.
+**
+** Return this display width of the modified title string.
+*/
+static int qrfTitleLimit(char *zIn, int N){
+ unsigned char *z = (unsigned char*)zIn;
+ int n = 0;
+ unsigned char *zEllipsis = 0;
+ while( 1 /*exit-by-break*/ ){
+ if( z[0]<' ' ){
+ int k;
+ if( z[0]==0 ){
+ zEllipsis = 0;
+ break;
+ }else if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){
+ z += k;
+ }else if( z[0]=='\t' ){
+ z[0] = ' ';
+ }else if( z[0]=='\n' || z[0]=='\r' ){
+ z[0] = ' ';
+ }else{
+ z++;
+ }
+ }else if( (0x80&z[0])==0 ){
+ if( n>=(N-3) && zEllipsis==0 ) zEllipsis = z;
+ if( n==N ){ z[0] = 0; break; }
+ n++;
+ z++;
+ }else{
+ int u = 0;
+ int len = sqlite3_qrf_decode_utf8(z, &u);
+ if( n+len>(N-3) && zEllipsis==0 ) zEllipsis = z;
+ if( n+len>N ){ z[0] = 0; break; }
+ z += len;
+ n += sqlite3_qrf_wcwidth(u);
+ }
+ }
+ if( zEllipsis && N>=3 ) memcpy(zEllipsis,"...",4);
+ return n;
+}
+
+
+/*
+** Render value pVal into pOut
+*/
+static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){
+#if SQLITE_VERSION_NUMBER>=3052000
+ int iStartLen = sqlite3_str_length(pOut);
+#endif
+ if( p->spec.xRender ){
+ sqlite3_value *pVal;
+ char *z;
+ pVal = sqlite3_value_dup(sqlite3_column_value(p->pStmt,iCol));
+ z = p->spec.xRender(p->spec.pRenderArg, pVal);
+ sqlite3_value_free(pVal);
+ if( z ){
+ sqlite3_str_appendall(pOut, z);
+ sqlite3_free(z);
+ return;
+ }
+ }
+ switch( sqlite3_column_type(p->pStmt,iCol) ){
+ case SQLITE_INTEGER: {
+ sqlite3_str_appendf(pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
+ sqlite3_str_appendall(pOut, zTxt);
+ break;
+ }
+ case SQLITE_BLOB: {
+ if( p->spec.bTextJsonb==QRF_Yes ){
+ const char *zJson = qrfJsonbToJson(p, iCol);
+ if( zJson ){
+ if( p->spec.eText==QRF_TEXT_Sql ){
+ sqlite3_str_append(pOut,"jsonb(",6);
+ qrfEncodeText(p, pOut, zJson);
+ sqlite3_str_append(pOut,")",1);
+ }else{
+ qrfEncodeText(p, pOut, zJson);
+ }
+ break;
+ }
+ }
+ switch( p->spec.eBlob ){
+ case QRF_BLOB_Hex:
+ case QRF_BLOB_Sql: {
+ int iStart;
+ int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
+ int i, j;
+ char *zVal;
+ const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
+ if( p->spec.eBlob==QRF_BLOB_Sql ){
+ sqlite3_str_append(pOut, "x'", 2);
+ }
+ iStart = sqlite3_str_length(pOut);
+ sqlite3_str_appendchar(pOut, nBlob, ' ');
+ sqlite3_str_appendchar(pOut, nBlob, ' ');
+ if( p->spec.eBlob==QRF_BLOB_Sql ){
+ sqlite3_str_appendchar(pOut, 1, '\'');
+ }
+ if( sqlite3_str_errcode(pOut) ) return;
+ zVal = sqlite3_str_value(pOut);
+ for(i=0, j=iStart; i<nBlob; i++, j+=2){
+ unsigned char c = a[i];
+ zVal[j] = "0123456789abcdef"[(c>>4)&0xf];
+ zVal[j+1] = "0123456789abcdef"[(c)&0xf];
+ }
+ break;
+ }
+ case QRF_BLOB_Tcl:
+ case QRF_BLOB_Json: {
+ int iStart;
+ int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
+ int i, j;
+ char *zVal;
+ const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol);
+ int szC = p->spec.eBlob==QRF_BLOB_Json ? 6 : 4;
+ sqlite3_str_append(pOut, "\"", 1);
+ iStart = sqlite3_str_length(pOut);
+ for(i=szC; i>0; i--){
+ sqlite3_str_appendchar(pOut, nBlob, ' ');
+ }
+ sqlite3_str_appendchar(pOut, 1, '"');
+ if( sqlite3_str_errcode(pOut) ) return;
+ zVal = sqlite3_str_value(pOut);
+ for(i=0, j=iStart; i<nBlob; i++, j+=szC){
+ unsigned char c = a[i];
+ zVal[j] = '\\';
+ if( szC==4 ){
+ zVal[j+1] = '0' + ((c>>6)&3);
+ zVal[j+2] = '0' + ((c>>3)&7);
+ zVal[j+3] = '0' + (c&7);
+ }else{
+ zVal[j+1] = 'u';
+ zVal[j+2] = '0';
+ zVal[j+3] = '0';
+ zVal[j+4] = "0123456789abcdef"[(c>>4)&0xf];
+ zVal[j+5] = "0123456789abcdef"[(c)&0xf];
+ }
+ }
+ break;
+ }
+ case QRF_BLOB_Size: {
+ int nBlob = sqlite3_column_bytes(p->pStmt,iCol);
+ sqlite3_str_appendf(pOut, "(%d-byte blob)", nBlob);
+ break;
+ }
+ default: {
+ const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
+ qrfEncodeText(p, pOut, zTxt);
+ }
+ }
+ break;
+ }
+ case SQLITE_NULL: {
+ sqlite3_str_appendall(pOut, p->spec.zNull);
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol);
+ qrfEncodeText(p, pOut, zTxt);
+ break;
+ }
+ }
+#if SQLITE_VERSION_NUMBER>=3052000
+ if( p->spec.nCharLimit>0
+ && (sqlite3_str_length(pOut) - iStartLen) > p->spec.nCharLimit
+ ){
+ const unsigned char *z;
+ int ii = 0, w = 0, limit = p->spec.nCharLimit;
+ z = (const unsigned char*)sqlite3_str_value(pOut) + iStartLen;
+ if( limit<4 ) limit = 4;
+ while( 1 ){
+ if( z[ii]<' ' ){
+ int k;
+ if( z[ii]=='\033' && (k = qrfIsVt100(z+ii))>0 ){
+ ii += k;
+ }else if( z[ii]==0 ){
+ break;
+ }else{
+ ii++;
+ }
+ }else if( (0x80&z[ii])==0 ){
+ w++;
+ if( w>limit ) break;
+ ii++;
+ }else{
+ int u = 0;
+ int len = sqlite3_qrf_decode_utf8(&z[ii], &u);
+ w += sqlite3_qrf_wcwidth(u);
+ if( w>limit ) break;
+ ii += len;
+ }
+ }
+ if( w>limit ){
+ sqlite3_str_truncate(pOut, iStartLen+ii);
+ sqlite3_str_append(pOut, "...", 3);
+ }
+ }
+#endif
+}
+
+/* Trim spaces of the end if pOut
+*/
+static void qrfRTrim(sqlite3_str *pOut){
+#if SQLITE_VERSION_NUMBER>=3052000
+ int nByte = sqlite3_str_length(pOut);
+ const char *zOut = sqlite3_str_value(pOut);
+ while( nByte>0 && zOut[nByte-1]==' ' ){ nByte--; }
+ sqlite3_str_truncate(pOut, nByte);
+#endif
+}
+
+/*
+** Store string zUtf to pOut as w characters. If w is negative,
+** then right-justify the text. W is the width in display characters, not
+** in bytes. Double-width unicode characters count as two characters.
+** VT100 escape sequences count as zero. And so forth.
+*/
+static void qrfWidthPrint(Qrf *p, sqlite3_str *pOut, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
+ static const int mxW = 10000000;
unsigned char c;
int i = 0;
int n = 0;
int k;
- int aw = w<0 ? -w : w;
- if( zUtf==0 ) zUtf = "";
+ int aw;
+ (void)p;
+ if( w<-mxW ){
+ w = -mxW;
+ }else if( w>mxW ){
+ w= mxW;
+ }
+ aw = w<0 ? -w : w;
+ if( a==0 ) a = (const unsigned char*)"";
while( (c = a[i])!=0 ){
if( (c&0xc0)==0xc0 ){
int u;
- int len = decodeUtf8(a+i, &u);
- int x = cli_wcwidth(u);
+ int len = sqlite3_qrf_decode_utf8(a+i, &u);
+ int x = sqlite3_qrf_wcwidth(u);
if( x+n>aw ){
break;
}
i += len;
n += x;
- }else if( c==0x1b && (k = isVt100(&a[i]))>0 ){
+ }else if( c==0x1b && (k = qrfIsVt100(&a[i]))>0 ){
i += k;
}else if( n>=aw ){
break;
@@ -1220,303 +2174,1185 @@ static void utf8_width_print(FILE *out, int w, const char *zUtf){
}
}
if( n>=aw ){
- sqlite3_fprintf(out, "%.*s", i, zUtf);
+ sqlite3_str_append(pOut, zUtf, i);
}else if( w<0 ){
- sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf);
+ if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' ');
+ sqlite3_str_append(pOut, zUtf, i);
}else{
- sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, "");
+ sqlite3_str_append(pOut, zUtf, i);
+ if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' ');
}
}
-
/*
-** Determines if a string is a number of not.
+** (*pz)[] is a line of text that is to be displayed the box or table or
+** similar tabular formats. z[] contain newlines or might be too wide
+** to fit in the columns so will need to be split into multiple line.
+**
+** This routine determines:
+**
+** * How many bytes of z[] should be shown on the current line.
+** * How many character positions those bytes will cover.
+** * The byte offset to the start of the next line.
*/
-static int isNumber(const char *z, int *realnum){
- if( *z=='-' || *z=='+' ) z++;
- if( !IsDigit(*z) ){
- return 0;
+static void qrfWrapLine(
+ const char *zIn, /* Input text to be displayed */
+ int w, /* Column width in characters (not bytes) */
+ int bWrap, /* True if we should do word-wrapping */
+ int *pnThis, /* OUT: How many bytes of z[] for the current line */
+ int *pnWide, /* OUT: How wide is the text of this line */
+ int *piNext /* OUT: Offset into z[] to start of the next line */
+){
+ int i; /* Input bytes consumed */
+ int k; /* Bytes in a VT100 code */
+ int n; /* Output column number */
+ const unsigned char *z = (const unsigned char*)zIn;
+ unsigned char c = 0;
+
+ if( z[0]==0 ){
+ *pnThis = 0;
+ *pnWide = 0;
+ *piNext = 0;
+ return;
+ }
+ n = 0;
+ for(i=0; n<=w; i++){
+ c = z[i];
+ if( c>=0xc0 ){
+ int u;
+ int len = sqlite3_qrf_decode_utf8(&z[i], &u);
+ int wcw = sqlite3_qrf_wcwidth(u);
+ if( wcw+n>w ) break;
+ i += len-1;
+ n += wcw;
+ continue;
+ }
+ if( c>=' ' ){
+ if( n==w ) break;
+ n++;
+ continue;
+ }
+ if( c==0 || c=='\n' ) break;
+ if( c=='\r' && z[i+1]=='\n' ){ c = z[++i]; break; }
+ if( c=='\t' ){
+ int wcw = 8 - (n&7);
+ if( n+wcw>w ) break;
+ n += wcw;
+ continue;
+ }
+ if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){
+ i += k-1;
+ }else if( n==w ){
+ break;
+ }else{
+ n++;
+ }
}
- z++;
- if( realnum ) *realnum = 0;
- while( IsDigit(*z) ){ z++; }
- if( *z=='.' ){
- z++;
- if( !IsDigit(*z) ) return 0;
- while( IsDigit(*z) ){ z++; }
- if( realnum ) *realnum = 1;
+ if( c==0 ){
+ *pnThis = i;
+ *pnWide = n;
+ *piNext = i;
+ return;
}
- if( *z=='e' || *z=='E' ){
- z++;
- if( *z=='+' || *z=='-' ) z++;
- if( !IsDigit(*z) ) return 0;
- while( IsDigit(*z) ){ z++; }
- if( realnum ) *realnum = 1;
+ if( c=='\n' ){
+ *pnThis = i;
+ *pnWide = n;
+ *piNext = i+1;
+ return;
}
- return *z==0;
+
+ /* If we get this far, that means the current line will end at some
+ ** point that is neither a "\n" or a 0x00. Figure out where that
+ ** split should occur
+ */
+ if( bWrap && z[i]!=0 && !qrfSpace(z[i]) && qrfAlnum(c)==qrfAlnum(z[i]) ){
+ /* Perhaps try to back up to a better place to break the line */
+ for(k=i-1; k>=i/2; k--){
+ if( qrfSpace(z[k]) ) break;
+ }
+ if( k<i/2 ){
+ for(k=i; k>=i/2; k--){
+ if( qrfAlnum(z[k-1])!=qrfAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
+ }
+ }
+ if( k>=i/2 ){
+ i = k;
+ n = qrfDisplayWidth((const char*)z, k, 0);
+ }
+ }
+ *pnThis = i;
+ *pnWide = n;
+ while( zIn[i]==' ' || zIn[i]=='\t' || zIn[i]=='\r' ){ i++; }
+ *piNext = i;
}
/*
-** Compute a string length that is limited to what can be stored in
-** lower 30 bits of a 32-bit signed integer.
+** Append nVal bytes of text from zVal onto the end of pOut.
+** Convert tab characters in zVal to the appropriate number of
+** spaces.
*/
-static int strlen30(const char *z){
- const char *z2 = z;
- while( *z2 ){ z2++; }
- return 0x3fffffff & (int)(z2 - z);
-}
+static void qrfAppendWithTabs(
+ sqlite3_str *pOut, /* Append text here */
+ const char *zVal, /* Text to append */
+ int nVal /* Use only the first nVal bytes of zVal[] */
+){
+ int i = 0;
+ unsigned int col = 0;
+ unsigned char *z = (unsigned char *)zVal;
+ while( i<nVal ){
+ unsigned char c = z[i];
+ if( c<' ' ){
+ int k;
+ sqlite3_str_append(pOut, (const char*)z, i);
+ nVal -= i;
+ z += i;
+ i = 0;
+ if( c=='\033' && (k = qrfIsVt100(z))>0 ){
+ sqlite3_str_append(pOut, (const char*)z, k);
+ z += k;
+ nVal -= k;
+ }else if( c=='\t' ){
+ k = 8 - (col&7);
+ sqlite3_str_appendchar(pOut, k, ' ');
+ col += k;
+ z++;
+ nVal--;
+ }else if( c=='\r' && nVal==1 ){
+ z++;
+ nVal--;
+ }else{
+ char zCtrlPik[4];
+ col++;
+ zCtrlPik[0] = 0xe2;
+ zCtrlPik[1] = 0x90;
+ zCtrlPik[2] = 0x80+c;
+ sqlite3_str_append(pOut, zCtrlPik, 3);
+ z++;
+ nVal--;
+ }
+ }else if( (0x80&c)==0 ){
+ i++;
+ col++;
+ }else{
+ int u = 0;
+ int len = sqlite3_qrf_decode_utf8(&z[i], &u);
+ i += len;
+ col += sqlite3_qrf_wcwidth(u);
+ }
+ }
+ sqlite3_str_append(pOut, (const char*)z, i);
+}
/*
-** Return the length of a string in characters. Multibyte UTF8 characters
-** count as a single character.
+** GCC does not define the offsetof() macro so we'll have to do it
+** ourselves.
*/
-static int strlenChar(const char *z){
- int n = 0;
- while( *z ){
- if( (0xc0&*(z++))!=0x80 ) n++;
+#ifndef offsetof
+# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0))
+#endif
+
+/*
+** Data for columnar layout, collected into a single object so
+** that it can be more easily passed into subroutines.
+*/
+typedef struct qrfColData qrfColData;
+struct qrfColData {
+ Qrf *p; /* The QRF instance */
+ int nCol; /* Number of columns in the table */
+ unsigned char bMultiRow; /* One or more cells will span multiple lines */
+ unsigned char nMargin; /* Width of column margins */
+ sqlite3_int64 nRow; /* Number of rows */
+ sqlite3_int64 nAlloc; /* Number of cells allocated */
+ sqlite3_int64 n; /* Number of cells. nCol*nRow */
+ char **az; /* Content of all cells */
+ int *aiWth; /* Width of each cell */
+ unsigned char *abNum; /* True for each numeric cell */
+ struct qrfPerCol { /* Per-column data */
+ char *z; /* Cache of text for current row */
+ int w; /* Computed width of this column */
+ int mxW; /* Maximum natural (unwrapped) width */
+ unsigned char e; /* Alignment */
+ unsigned char fx; /* Width is fixed */
+ unsigned char bNum; /* True if is numeric */
+ } *a; /* One per column */
+};
+
+/*
+** Output horizontally justified text into pOut. The text is the
+** first nVal bytes of zVal. Include nWS bytes of whitespace, either
+** split between both sides, or on the left, or on the right, depending
+** on eAlign.
+*/
+static void qrfPrintAligned(
+ sqlite3_str *pOut, /* Append text here */
+ struct qrfPerCol *pCol, /* Information about the text to print */
+ int nVal, /* Use only the first nVal bytes of zVal[] */
+ int nWS /* Whitespace for horizonal alignment */
+){
+ unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK;
+ if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right;
+ if( eAlign==QRF_ALIGN_Center ){
+ /* Center the text */
+ sqlite3_str_appendchar(pOut, nWS/2, ' ');
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
+ sqlite3_str_appendchar(pOut, nWS - nWS/2, ' ');
+ }else if( eAlign==QRF_ALIGN_Right ){
+ /* Right justify the text */
+ sqlite3_str_appendchar(pOut, nWS, ' ');
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
+ }else{
+ /* Left justify the text */
+ qrfAppendWithTabs(pOut, pCol->z, nVal);
+ sqlite3_str_appendchar(pOut, nWS, ' ');
}
- return n;
}
/*
-** Return open FILE * if zFile exists, can be opened for read
-** and is an ordinary file or a character stream source.
-** Otherwise return 0.
+** Free all the memory allocates in the qrfColData object
*/
-static FILE * openChrSource(const char *zFile){
-#if defined(_WIN32) || defined(WIN32)
- struct __stat64 x = {0};
-# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
- /* On Windows, open first, then check the stream nature. This order
- ** is necessary because _stat() and sibs, when checking a named pipe,
- ** effectively break the pipe as its supplier sees it. */
- FILE *rv = sqlite3_fopen(zFile, "rb");
- if( rv==0 ) return 0;
- if( _fstat64(_fileno(rv), &x) != 0
- || !STAT_CHR_SRC(x.st_mode)){
- fclose(rv);
- rv = 0;
+static void qrfColDataFree(qrfColData *p){
+ sqlite3_int64 i;
+ for(i=0; i<p->n; i++) sqlite3_free(p->az[i]);
+ sqlite3_free(p->az);
+ sqlite3_free(p->aiWth);
+ sqlite3_free(p->abNum);
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+}
+
+/*
+** Allocate space for more cells in the qrfColData object.
+** Return non-zero if a memory allocation fails.
+*/
+static int qrfColDataEnlarge(qrfColData *p){
+ char **azData;
+ int *aiWth;
+ unsigned char *abNum;
+ p->nAlloc = 2*p->nAlloc + 10*p->nCol;
+ azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*));
+ if( azData==0 ){
+ qrfOom(p->p);
+ qrfColDataFree(p);
+ return 1;
}
- return rv;
-#else
- struct stat x = {0};
- int rc = stat(zFile, &x);
-# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
- if( rc!=0 ) return 0;
- if( STAT_CHR_SRC(x.st_mode) ){
- return sqlite3_fopen(zFile, "rb");
- }else{
- return 0;
+ p->az = azData;
+ aiWth = sqlite3_realloc64(p->aiWth, p->nAlloc*sizeof(int));
+ if( aiWth==0 ){
+ qrfOom(p->p);
+ qrfColDataFree(p);
+ return 1;
}
-#endif
-#undef STAT_CHR_SRC
+ p->aiWth = aiWth;
+ abNum = sqlite3_realloc64(p->abNum, p->nAlloc);
+ if( abNum==0 ){
+ qrfOom(p->p);
+ qrfColDataFree(p);
+ return 1;
+ }
+ p->abNum = abNum;
+ return 0;
}
/*
-** This routine reads a line of text from FILE in, stores
-** the text in memory obtained from malloc() and returns a pointer
-** to the text. NULL is returned at end of file, or if malloc()
-** fails.
+** Print a markdown or table-style row separator using ascii-art
+*/
+static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){
+ int i;
+ if( p->nCol>0 ){
+ int useBorder = p->p->spec.bBorder!=QRF_No;
+ if( useBorder ){
+ sqlite3_str_append(pOut, &cSep, 1);
+ }
+ sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-');
+ for(i=1; i<p->nCol; i++){
+ sqlite3_str_append(pOut, &cSep, 1);
+ sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-');
+ }
+ if( useBorder ){
+ sqlite3_str_append(pOut, &cSep, 1);
+ }
+ }
+ sqlite3_str_append(pOut, "\n", 1);
+}
+
+/*
+** UTF8 box-drawing characters. Imagine box lines like this:
**
-** If zLine is not NULL then it is a malloced buffer returned from
-** a previous call to this routine that may be reused.
+** 1
+** |
+** 4 --+-- 2
+** |
+** 3
+**
+** Each box characters has between 2 and 4 of the lines leading from
+** the center. The characters are here identified by the numbers of
+** their corresponding lines.
*/
-static char *local_getline(char *zLine, FILE *in){
- int nLine = zLine==0 ? 0 : 100;
- int n = 0;
+#define BOX_24 "\342\224\200" /* U+2500 --- */
+#define BOX_13 "\342\224\202" /* U+2502 | */
+#define BOX_23 "\342\224\214" /* U+250c ,- */
+#define BOX_34 "\342\224\220" /* U+2510 -, */
+#define BOX_12 "\342\224\224" /* U+2514 '- */
+#define BOX_14 "\342\224\230" /* U+2518 -' */
+#define BOX_123 "\342\224\234" /* U+251c |- */
+#define BOX_134 "\342\224\244" /* U+2524 -| */
+#define BOX_234 "\342\224\254" /* U+252c -,- */
+#define BOX_124 "\342\224\264" /* U+2534 -'- */
+#define BOX_1234 "\342\224\274" /* U+253c -|- */
- while( 1 ){
- if( n+100>nLine ){
- nLine = nLine*2 + 100;
- zLine = realloc(zLine, nLine);
- shell_check_oom(zLine);
+/* Rounded corners: */
+#define BOX_R12 "\342\225\260" /* U+2570 '- */
+#define BOX_R23 "\342\225\255" /* U+256d ,- */
+#define BOX_R34 "\342\225\256" /* U+256e -, */
+#define BOX_R14 "\342\225\257" /* U+256f -' */
+
+/* Doubled horizontal lines: */
+#define DBL_24 "\342\225\220" /* U+2550 === */
+#define DBL_123 "\342\225\236" /* U+255e |= */
+#define DBL_134 "\342\225\241" /* U+2561 =| */
+#define DBL_1234 "\342\225\252" /* U+256a =|= */
+
+/* Draw horizontal line N characters long using unicode box
+** characters
+*/
+static void qrfBoxLine(sqlite3_str *pOut, int N, int bDbl){
+ const char *azDash[2] = {
+ BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24,
+ DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24
+ };/* 0 1 2 3 4 5 6 7 8 9 */
+ const int nDash = 30;
+ N *= 3;
+ while( N>nDash ){
+ sqlite3_str_append(pOut, azDash[bDbl], nDash);
+ N -= nDash;
+ }
+ sqlite3_str_append(pOut, azDash[bDbl], N);
+}
+
+/*
+** Draw a horizontal separator for a QRF_STYLE_Box table.
+*/
+static void qrfBoxSeparator(
+ sqlite3_str *pOut,
+ qrfColData *p,
+ const char *zSep1,
+ const char *zSep2,
+ const char *zSep3,
+ int bDbl
+){
+ int i;
+ if( p->nCol>0 ){
+ int useBorder = p->p->spec.bBorder!=QRF_No;
+ if( useBorder ){
+ sqlite3_str_appendall(pOut, zSep1);
}
- if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
- if( n==0 ){
- free(zLine);
- return 0;
- }
- zLine[n] = 0;
- break;
+ qrfBoxLine(pOut, p->a[0].w+p->nMargin, bDbl);
+ for(i=1; i<p->nCol; i++){
+ sqlite3_str_appendall(pOut, zSep2);
+ qrfBoxLine(pOut, p->a[i].w+p->nMargin, bDbl);
}
- while( zLine[n] ) n++;
- if( n>0 && zLine[n-1]=='\n' ){
- n--;
- if( n>0 && zLine[n-1]=='\r' ) n--;
- zLine[n] = 0;
- break;
+ if( useBorder ){
+ sqlite3_str_appendall(pOut, zSep3);
}
}
- return zLine;
+ sqlite3_str_append(pOut, "\n", 1);
}
/*
-** Retrieve a single line of input text.
-**
-** If in==0 then read from standard input and prompt before each line.
-** If isContinuation is true, then a continuation prompt is appropriate.
-** If isContinuation is zero, then the main prompt should be used.
+** Load into pData the default alignment for the body of a table.
+*/
+static void qrfLoadAlignment(qrfColData *pData, Qrf *p){
+ sqlite3_int64 i;
+ for(i=0; i<pData->nCol; i++){
+ pData->a[i].e = p->spec.eDfltAlign;
+ if( i<p->spec.nAlign ){
+ unsigned char ax = p->spec.aAlign[i];
+ if( (ax & QRF_ALIGN_HMASK)!=0 ){
+ pData->a[i].e = (ax & QRF_ALIGN_HMASK) |
+ (pData->a[i].e & QRF_ALIGN_VMASK);
+ }
+ }else if( i<p->spec.nWidth ){
+ if( p->spec.aWidth[i]<0 ){
+ pData->a[i].e = QRF_ALIGN_Right |
+ (pData->a[i].e & QRF_ALIGN_VMASK);
+ }
+ }
+ }
+}
+
+/*
+** If the single column in pData->a[] with pData->n entries can be
+** laid out as nCol columns with a 2-space gap between each such
+** that all columns fit within nSW, then return a pointer to an array
+** of integers which is the width of each column from left to right.
**
-** If zPrior is not NULL then it is a buffer from a prior call to this
-** routine that can be reused.
+** If the layout is not possible, return a NULL pointer.
**
-** The result is stored in space obtained from malloc() and must either
-** be freed by the caller or else passed back into this routine via the
-** zPrior argument for reuse.
+** Space to hold the returned array is from sqlite_malloc64().
*/
-#ifndef SQLITE_SHELL_FIDDLE
-static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
- char *zPrompt;
- char *zResult;
- if( in!=0 ){
- zResult = local_getline(zPrior, in);
- }else{
- zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
-#if SHELL_USE_LOCAL_GETLINE
- sputz(stdout, zPrompt);
- fflush(stdout);
- do{
- zResult = local_getline(zPrior, stdin);
- zPrior = 0;
- /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
- if( zResult==0 ) sqlite3_sleep(50);
- }while( zResult==0 && seenInterrupt>0 );
-#else
- free(zPrior);
- zResult = shell_readline(zPrompt);
- while( zResult==0 ){
- /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
- sqlite3_sleep(50);
- if( seenInterrupt==0 ) break;
- zResult = shell_readline("");
+static int *qrfValidLayout(
+ qrfColData *pData, /* Collected query results */
+ Qrf *p, /* On which to report an OOM */
+ int nCol, /* Attempt this many columns */
+ int nSW /* Screen width */
+){
+ int i; /* Loop counter */
+ int nr; /* Number of rows */
+ int w = 0; /* Width of the current column */
+ int t; /* Total width of all columns */
+ int *aw; /* Array of individual column widths */
+
+ aw = sqlite3_malloc64( sizeof(int)*nCol );
+ if( aw==0 ){
+ qrfOom(p);
+ return 0;
+ }
+ nr = (pData->n + nCol - 1)/nCol;
+ for(i=0; i<pData->n; i++){
+ if( (i%nr)==0 ){
+ if( i>0 ) aw[i/nr-1] = w;
+ w = pData->aiWth[i];
+ }else if( pData->aiWth[i]>w ){
+ w = pData->aiWth[i];
}
- if( zResult && *zResult ) shell_add_history(zResult);
-#endif
}
- return zResult;
+ aw[nCol-1] = w;
+ for(t=i=0; i<nCol; i++) t += aw[i];
+ t += 2*(nCol-1);
+ if( t>nSW ){
+ sqlite3_free(aw);
+ return 0;
+ }
+ return aw;
}
-#endif /* !SQLITE_SHELL_FIDDLE */
/*
-** Return the value of a hexadecimal digit. Return -1 if the input
-** is not a hex digit.
+** The output is single-column and the bSplitColumn flag is set.
+** Check to see if the single-column output can be split into multiple
+** columns that appear side-by-side. Adjust pData appropriately.
*/
-static int hexDigitValue(char c){
- if( c>='0' && c<='9' ) return c - '0';
- if( c>='a' && c<='f' ) return c - 'a' + 10;
- if( c>='A' && c<='F' ) return c - 'A' + 10;
- return -1;
+static void qrfSplitColumn(qrfColData *pData, Qrf *p){
+ int nCol = 1;
+ int *aw = 0;
+ char **az = 0;
+ int *aiWth = 0;
+ unsigned char *abNum = 0;
+ int nColNext = 2;
+ int w;
+ struct qrfPerCol *a = 0;
+ sqlite3_int64 nRow = 1;
+ sqlite3_int64 i;
+ while( 1/*exit-by-break*/ ){
+ int *awNew = qrfValidLayout(pData, p, nColNext, p->spec.nScreenWidth);
+ if( awNew==0 ) break;
+ sqlite3_free(aw);
+ aw = awNew;
+ nCol = nColNext;
+ nRow = (pData->n + nCol - 1)/nCol;
+ if( nRow==1 ) break;
+ nColNext++;
+ while( (pData->n + nColNext - 1)/nColNext == nRow ) nColNext++;
+ }
+ if( nCol==1 ){
+ sqlite3_free(aw);
+ return; /* Cannot do better than 1 column */
+ }
+ az = sqlite3_malloc64( nRow*nCol*sizeof(char*) );
+ if( az==0 ){
+ qrfOom(p);
+ return;
+ }
+ aiWth = sqlite3_malloc64( nRow*nCol*sizeof(int) );
+ if( aiWth==0 ){
+ sqlite3_free(az);
+ qrfOom(p);
+ return;
+ }
+ a = sqlite3_malloc64( nCol*sizeof(struct qrfPerCol) );
+ if( a==0 ){
+ sqlite3_free(az);
+ sqlite3_free(aiWth);
+ qrfOom(p);
+ return;
+ }
+ abNum = sqlite3_malloc64( nRow*nCol );
+ if( abNum==0 ){
+ sqlite3_free(az);
+ sqlite3_free(aiWth);
+ sqlite3_free(a);
+ qrfOom(p);
+ return;
+ }
+ for(i=0; i<pData->n; i++){
+ sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
+ az[j] = pData->az[i];
+ abNum[j]= pData->abNum[i];
+ pData->az[i] = 0;
+ aiWth[j] = pData->aiWth[i];
+ }
+ while( i<nRow*nCol ){
+ sqlite3_int64 j = (i%nRow)*nCol + (i/nRow);
+ az[j] = sqlite3_mprintf("");
+ if( az[j]==0 ) qrfOom(p);
+ aiWth[j] = 0;
+ abNum[j] = 0;
+ i++;
+ }
+ for(i=0; i<nCol; i++){
+ a[i].fx = a[i].mxW = a[i].w = aw[i];
+ a[i].e = pData->a[0].e;
+ }
+ sqlite3_free(pData->az);
+ sqlite3_free(pData->aiWth);
+ sqlite3_free(pData->a);
+ sqlite3_free(pData->abNum);
+ sqlite3_free(aw);
+ pData->az = az;
+ pData->aiWth = aiWth;
+ pData->a = a;
+ pData->abNum = abNum;
+ pData->nCol = nCol;
+ pData->n = pData->nAlloc = nRow*nCol;
+ for(i=w=0; i<nCol; i++) w += a[i].w;
+ pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1);
+ if( pData->nMargin>5 ) pData->nMargin = 5;
}
/*
-** Interpret zArg as an integer value, possibly with suffixes.
+** Adjust the layout for the screen width restriction
*/
-static sqlite3_int64 integerValue(const char *zArg){
- sqlite3_int64 v = 0;
- static const struct { char *zSuffix; int iMult; } aMult[] = {
- { "KiB", 1024 },
- { "MiB", 1024*1024 },
- { "GiB", 1024*1024*1024 },
- { "KB", 1000 },
- { "MB", 1000000 },
- { "GB", 1000000000 },
- { "K", 1000 },
- { "M", 1000000 },
- { "G", 1000000000 },
- };
- int i;
- int isNeg = 0;
- if( zArg[0]=='-' ){
- isNeg = 1;
- zArg++;
- }else if( zArg[0]=='+' ){
- zArg++;
+static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){
+ int sepW; /* Width of all box separators and margins */
+ int sumW; /* Total width of data area over all columns */
+ int targetW; /* Desired total data area */
+ int i; /* Loop counters */
+ int nCol; /* Number of columns */
+
+ pData->nMargin = 2; /* Default to normal margins */
+ if( p->spec.nScreenWidth==0 ) return;
+ if( p->spec.eStyle==QRF_STYLE_Column ){
+ sepW = pData->nCol*2 - 2;
+ }else{
+ sepW = pData->nCol*3 + 1;
+ if( p->spec.bBorder==QRF_No ) sepW -= 2;
}
- if( zArg[0]=='0' && zArg[1]=='x' ){
- int x;
- zArg += 2;
- while( (x = hexDigitValue(zArg[0]))>=0 ){
- v = (v<<4) + x;
- zArg++;
- }
+ nCol = pData->nCol;
+ for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w;
+ if( p->spec.nScreenWidth >= sumW+sepW ) return;
+
+ /* First thing to do is reduce the separation between columns */
+ pData->nMargin = 0;
+ if( p->spec.eStyle==QRF_STYLE_Column ){
+ sepW = pData->nCol - 1;
}else{
- while( IsDigit(zArg[0]) ){
- v = v*10 + zArg[0] - '0';
- zArg++;
- }
+ sepW = pData->nCol + 1;
+ if( p->spec.bBorder==QRF_No ) sepW -= 2;
}
- for(i=0; i<ArraySize(aMult); i++){
- if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
- v *= aMult[i].iMult;
- break;
+ targetW = p->spec.nScreenWidth - sepW;
+
+#define MIN_SQUOZE 8
+#define MIN_EX_SQUOZE 16
+ /* Reduce the width of the widest eligible column. A column is
+ ** eligible for narrowing if:
+ **
+ ** * It is not a fixed-width column (a[0].fx is false)
+ ** * The current width is more than MIN_SQUOZE
+ ** * Either:
+ ** + The current width is more then MIN_EX_SQUOZE, or
+ ** + The current width is more than half the max width (a[].mxW)
+ **
+ ** Keep making reductions until either no more reductions are
+ ** possible or until the size target is reached.
+ */
+ while( sumW > targetW ){
+ int gain, w;
+ int ix = -1;
+ int mx = 0;
+ for(i=0; i<nCol; i++){
+ if( pData->a[i].fx==0
+ && (w = pData->a[i].w)>mx
+ && w>MIN_SQUOZE
+ && (w>MIN_EX_SQUOZE || w*2>pData->a[i].mxW)
+ ){
+ ix = i;
+ mx = w;
+ }
}
+ if( ix<0 ) break;
+ if( mx>=MIN_SQUOZE*2 ){
+ gain = mx/2;
+ }else{
+ gain = mx - MIN_SQUOZE;
+ }
+ if( sumW - gain < targetW ){
+ gain = sumW - targetW;
+ }
+ sumW -= gain;
+ pData->a[ix].w -= gain;
+ pData->bMultiRow = 1;
}
- return isNeg? -v : v;
}
/*
-** A variable length string to which one can append text.
+** Columnar modes require that the entire query be evaluated first, with
+** results written into memory, so that we can compute appropriate column
+** widths.
*/
-typedef struct ShellText ShellText;
-struct ShellText {
- char *z;
- int n;
- int nAlloc;
-};
+static void qrfColumnar(Qrf *p){
+ sqlite3_int64 i, j; /* Loop counters */
+ const char *colSep = 0; /* Column separator text */
+ const char *rowSep = 0; /* Row terminator text */
+ const char *rowStart = 0; /* Row start text */
+ int szColSep, szRowSep, szRowStart; /* Size in bytes of previous 3 */
+ int rc; /* Result code */
+ int nColumn = p->nCol; /* Number of columns */
+ int bWW; /* True to do word-wrap */
+ sqlite3_str *pStr; /* Temporary rendering */
+ qrfColData data; /* Columnar layout data */
+ int bRTrim; /* Trim trailing space */
+
+ rc = sqlite3_step(p->pStmt);
+ if( rc!=SQLITE_ROW || nColumn==0 ){
+ return; /* No output */
+ }
+
+ /* Initialize the data container */
+ memset(&data, 0, sizeof(data));
+ data.nCol = p->nCol;
+ data.p = p;
+ data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) );
+ if( data.a==0 ){
+ qrfOom(p);
+ return;
+ }
+ memset(data.a, 0, nColumn*sizeof(struct qrfPerCol) );
+ if( qrfColDataEnlarge(&data) ) return;
+ assert( data.az!=0 );
+
+ /* Load the column header names and all cell content into data */
+ if( p->spec.bTitles==QRF_Yes ){
+ unsigned char saved_eText = p->spec.eText;
+ p->spec.eText = p->spec.eTitle;
+ memset(data.abNum, 0, nColumn);
+ for(i=0; i<nColumn; i++){
+ const char *z = (const char*)sqlite3_column_name(p->pStmt,i);
+ int nNL = 0;
+ int n, w;
+ pStr = sqlite3_str_new(p->db);
+ qrfEncodeText(p, pStr, z ? z : "");
+ n = sqlite3_str_length(pStr);
+ qrfStrErr(p, pStr);
+ z = data.az[data.n] = sqlite3_str_finish(pStr);
+ if( p->spec.nTitleLimit ){
+ nNL = 0;
+ data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n],
+ p->spec.nTitleLimit );
+ }else{
+ data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
+ }
+ data.n++;
+ if( w>data.a[i].mxW ) data.a[i].mxW = w;
+ if( nNL ) data.bMultiRow = 1;
+ }
+ p->spec.eText = saved_eText;
+ p->nRow++;
+ }
+ do{
+ if( data.n+nColumn > data.nAlloc ){
+ if( qrfColDataEnlarge(&data) ) return;
+ }
+ for(i=0; i<nColumn; i++){
+ char *z;
+ int nNL = 0;
+ int n, w;
+ int eType = sqlite3_column_type(p->pStmt,i);
+ pStr = sqlite3_str_new(p->db);
+ qrfRenderValue(p, pStr, i);
+ n = sqlite3_str_length(pStr);
+ qrfStrErr(p, pStr);
+ z = data.az[data.n] = sqlite3_str_finish(pStr);
+ data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT;
+ data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL);
+ data.n++;
+ if( w>data.a[i].mxW ) data.a[i].mxW = w;
+ if( nNL ) data.bMultiRow = 1;
+ }
+ p->nRow++;
+ }while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK );
+ if( p->iErr ){
+ qrfColDataFree(&data);
+ return;
+ }
+
+ /* Compute the width and alignment of every column */
+ if( p->spec.bTitles==QRF_No ){
+ qrfLoadAlignment(&data, p);
+ }else{
+ unsigned char e;
+ if( p->spec.eTitleAlign==QRF_Auto ){
+ e = QRF_ALIGN_Center;
+ }else{
+ e = p->spec.eTitleAlign;
+ }
+ for(i=0; i<nColumn; i++) data.a[i].e = e;
+ }
+
+ for(i=0; i<nColumn; i++){
+ int w = 0;
+ if( i<p->spec.nWidth ){
+ w = p->spec.aWidth[i];
+ if( w==(-32768) ){
+ w = 0;
+ if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){
+ data.a[i].e |= QRF_ALIGN_Right;
+ }
+ }else if( w<0 ){
+ w = -w;
+ if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){
+ data.a[i].e |= QRF_ALIGN_Right;
+ }
+ }
+ if( w ) data.a[i].fx = 1;
+ }
+ if( w==0 ){
+ w = data.a[i].mxW;
+ if( p->spec.nWrap>0 && w>p->spec.nWrap ){
+ w = p->spec.nWrap;
+ data.bMultiRow = 1;
+ }
+ }else if( (data.bMultiRow==0 || w==1) && data.a[i].mxW>w ){
+ data.bMultiRow = 1;
+ if( w==1 ){
+ /* If aiWth[j] is 2 or more, then there might be a double-wide
+ ** character somewhere. So make the column width at least 2. */
+ w = 2;
+ }
+ }
+ data.a[i].w = w;
+ }
+
+ if( nColumn==1
+ && data.n>1
+ && p->spec.bSplitColumn==QRF_Yes
+ && p->spec.eStyle==QRF_STYLE_Column
+ && p->spec.bTitles==QRF_No
+ && p->spec.nScreenWidth>data.a[0].w+3
+ ){
+ /* Attempt to convert single-column tables into multi-column by
+ ** verticle wrapping, if the screen is wide enough and if the
+ ** bSplitColumn flag is set. */
+ qrfSplitColumn(&data, p);
+ nColumn = data.nCol;
+ }else{
+ /* Adjust the column widths due to screen width restrictions */
+ qrfRestrictScreenWidth(&data, p);
+ }
+
+ /* Draw the line across the top of the table. Also initialize
+ ** the row boundary and column separator texts. */
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ if( data.nMargin ){
+ rowStart = BOX_13 " ";
+ colSep = " " BOX_13 " ";
+ rowSep = " " BOX_13 "\n";
+ }else{
+ rowStart = BOX_13;
+ colSep = BOX_13;
+ rowSep = BOX_13 "\n";
+ }
+ if( p->spec.bBorder==QRF_No){
+ rowStart += 3;
+ rowSep = "\n";
+ }else{
+ qrfBoxSeparator(p->pOut, &data, BOX_R23, BOX_234, BOX_R34, 0);
+ }
+ break;
+ case QRF_STYLE_Table:
+ if( data.nMargin ){
+ rowStart = "| ";
+ colSep = " | ";
+ rowSep = " |\n";
+ }else{
+ rowStart = "|";
+ colSep = "|";
+ rowSep = "|\n";
+ }
+ if( p->spec.bBorder==QRF_No ){
+ rowStart += 1;
+ rowSep = "\n";
+ }else{
+ qrfRowSeparator(p->pOut, &data, '+');
+ }
+ break;
+ case QRF_STYLE_Column: {
+ static const char zSpace[] = " ";
+ rowStart = "";
+ if( data.nMargin<2 ){
+ colSep = " ";
+ }else if( data.nMargin<=5 ){
+ colSep = &zSpace[5-data.nMargin];
+ }else{
+ colSep = zSpace;
+ }
+ rowSep = "\n";
+ break;
+ }
+ default: /*case QRF_STYLE_Markdown:*/
+ if( data.nMargin ){
+ rowStart = "| ";
+ colSep = " | ";
+ rowSep = " |\n";
+ }else{
+ rowStart = "|";
+ colSep = "|";
+ rowSep = "|\n";
+ }
+ break;
+ }
+ szRowStart = (int)strlen(rowStart);
+ szRowSep = (int)strlen(rowSep);
+ szColSep = (int)strlen(colSep);
+
+ bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow);
+ if( p->spec.eStyle==QRF_STYLE_Column
+ || (p->spec.bBorder==QRF_No
+ && (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table)
+ )
+ ){
+ bRTrim = 1;
+ }else{
+ bRTrim = 0;
+ }
+ for(i=0; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){
+ int bMore;
+ int nRow = 0;
+
+ /* Draw a single row of the table. This might be the title line
+ ** (if there is a title line) or a row in the body of the table.
+ ** The column number will be j. The row number is i/nColumn.
+ */
+ for(j=0; j<nColumn; j++){
+ data.a[j].z = data.az[i+j];
+ if( data.a[j].z==0 ) data.a[j].z = "";
+ data.a[j].bNum = data.abNum[i+j];
+ }
+ do{
+ sqlite3_str_append(p->pOut, rowStart, szRowStart);
+ bMore = 0;
+ for(j=0; j<nColumn; j++){
+ int nThis = 0;
+ int nWide = 0;
+ int iNext = 0;
+ int nWS;
+ qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext);
+ nWS = data.a[j].w - nWide;
+ qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS);
+ data.a[j].z += iNext;
+ if( data.a[j].z[0]!=0 ){
+ bMore = 1;
+ }
+ if( j<nColumn-1 ){
+ sqlite3_str_append(p->pOut, colSep, szColSep);
+ }else{
+ if( bRTrim ) qrfRTrim(p->pOut);
+ sqlite3_str_append(p->pOut, rowSep, szRowSep);
+ }
+ }
+ }while( bMore && ++nRow < p->mxHeight );
+ if( bMore ){
+ /* This row was terminated by nLineLimit. Show ellipsis. */
+ sqlite3_str_append(p->pOut, rowStart, szRowStart);
+ for(j=0; j<nColumn; j++){
+ if( data.a[j].z[0]==0 ){
+ sqlite3_str_appendchar(p->pOut, data.a[j].w, ' ');
+ }else{
+ int nE = 3;
+ if( nE>data.a[j].w ) nE = data.a[j].w;
+ data.a[j].z = "...";
+ qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE);
+ }
+ if( j<nColumn-1 ){
+ sqlite3_str_append(p->pOut, colSep, szColSep);
+ }else{
+ if( bRTrim ) qrfRTrim(p->pOut);
+ sqlite3_str_append(p->pOut, rowSep, szRowSep);
+ }
+ }
+ }
+
+ /* Draw either (1) the separator between the title line and the body
+ ** of the table, or (2) separators between individual rows of the table
+ ** body. isTitleDataSeparator will be true if we are doing (1).
+ */
+ if( (i==0 || data.bMultiRow) && i+nColumn<data.n ){
+ int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes);
+ if( isTitleDataSeparator ){
+ qrfLoadAlignment(&data, p);
+ }
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Table: {
+ if( isTitleDataSeparator || data.bMultiRow ){
+ qrfRowSeparator(p->pOut, &data, '+');
+ }
+ break;
+ }
+ case QRF_STYLE_Box: {
+ if( isTitleDataSeparator ){
+ qrfBoxSeparator(p->pOut, &data, DBL_123, DBL_1234, DBL_134, 1);
+ }else if( data.bMultiRow ){
+ qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134, 0);
+ }
+ break;
+ }
+ case QRF_STYLE_Markdown: {
+ if( isTitleDataSeparator ){
+ qrfRowSeparator(p->pOut, &data, '|');
+ }
+ break;
+ }
+ case QRF_STYLE_Column: {
+ if( isTitleDataSeparator ){
+ for(j=0; j<nColumn; j++){
+ sqlite3_str_appendchar(p->pOut, data.a[j].w, '-');
+ if( j<nColumn-1 ){
+ sqlite3_str_append(p->pOut, colSep, szColSep);
+ }else{
+ qrfRTrim(p->pOut);
+ sqlite3_str_append(p->pOut, rowSep, szRowSep);
+ }
+ }
+ }else if( data.bMultiRow ){
+ qrfRTrim(p->pOut);
+ sqlite3_str_append(p->pOut, "\n", 1);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* Draw the line across the bottom of the table */
+ if( p->spec.bBorder!=QRF_No ){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0);
+ break;
+ case QRF_STYLE_Table:
+ qrfRowSeparator(p->pOut, &data, '+');
+ break;
+ }
+ }
+ qrfWrite(p);
+
+ qrfColDataFree(&data);
+ return;
+}
/*
-** Initialize and destroy a ShellText object
+** Parameter azArray points to a zero-terminated array of strings. zStr
+** points to a single nul-terminated string. Return non-zero if zStr
+** is equal, according to strcmp(), to any of the strings in the array.
+** Otherwise, return zero.
*/
-static void initText(ShellText *p){
- memset(p, 0, sizeof(*p));
-}
-static void freeText(ShellText *p){
- free(p->z);
- initText(p);
+static int qrfStringInArray(const char *zStr, const char **azArray){
+ int i;
+ if( zStr==0 ) return 0;
+ for(i=0; azArray[i]; i++){
+ if( 0==strcmp(zStr, azArray[i]) ) return 1;
+ }
+ return 0;
}
-/* zIn is either a pointer to a NULL-terminated string in memory obtained
-** from malloc(), or a NULL pointer. The string pointed to by zAppend is
-** added to zIn, and the result returned in memory obtained from malloc().
-** zIn, if it was not NULL, is freed.
+/*
+** Print out an EXPLAIN with indentation. This is a two-pass algorithm.
**
-** If the third argument, quote, is not '\0', then it is used as a
-** quote character for zAppend.
+** On the first pass, we compute aiIndent[iOp] which is the amount of
+** indentation to apply to the iOp-th opcode. The output actually occurs
+** on the second pass.
+**
+** The indenting rules are:
+**
+** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
+** all opcodes that occur between the p2 jump destination and the opcode
+** itself by 2 spaces.
+**
+** * Do the previous for "Return" instructions for when P2 is positive.
+** See tag-20220407a in wherecode.c and vdbe.c.
+**
+** * For each "Goto", if the jump destination is earlier in the program
+** and ends on one of:
+** Yield SeekGt SeekLt RowSetRead Rewind
+** or if the P1 parameter is one instead of zero,
+** then indent all opcodes between the earlier instruction
+** and "Goto" by 2 spaces.
*/
-static void appendText(ShellText *p, const char *zAppend, char quote){
- i64 len;
- i64 i;
- i64 nAppend = strlen30(zAppend);
+static void qrfExplain(Qrf *p){
+ int *abYield = 0; /* abYield[iOp] is rue if opcode iOp is an OP_Yield */
+ int *aiIndent = 0; /* Indent the iOp-th opcode by aiIndent[iOp] */
+ i64 nAlloc = 0; /* Allocated size of aiIndent[], abYield */
+ int nIndent = 0; /* Number of entries in aiIndent[] */
+ int iOp; /* Opcode number */
+ int i; /* Column loop counter */
- len = nAppend+p->n+1;
- if( quote ){
- len += 2;
- for(i=0; i<nAppend; i++){
- if( zAppend[i]==quote ) len++;
+ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
+ "Return", 0 };
+ const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
+ "Rewind", 0 };
+ const char *azGoto[] = { "Goto", 0 };
+
+ /* The caller guarantees that the leftmost 4 columns of the statement
+ ** passed to this function are equivalent to the leftmost 4 columns
+ ** of EXPLAIN statement output. In practice the statement may be
+ ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */
+ assert( sqlite3_column_count(p->pStmt)>=4 );
+ assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) );
+ assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) );
+ assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) );
+ assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) );
+
+ for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){
+ int iAddr = sqlite3_column_int(p->pStmt, 0);
+ const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1);
+ int p1 = sqlite3_column_int(p->pStmt, 2);
+ int p2 = sqlite3_column_int(p->pStmt, 3);
+
+ /* Assuming that p2 is an instruction address, set variable p2op to the
+ ** index of that instruction in the aiIndent[] array. p2 and p2op may be
+ ** different if the current instruction is part of a sub-program generated
+ ** by an SQL trigger or foreign key. */
+ int p2op = (p2 + (iOp-iAddr));
+
+ /* Grow the aiIndent array as required */
+ if( iOp>=nAlloc ){
+ nAlloc += 100;
+ aiIndent = (int*)sqlite3_realloc64(aiIndent, nAlloc*sizeof(int));
+ abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
+ if( aiIndent==0 || abYield==0 ){
+ qrfOom(p);
+ sqlite3_free(aiIndent);
+ sqlite3_free(abYield);
+ return;
+ }
}
- }
- if( p->z==0 || p->n+len>=p->nAlloc ){
- p->nAlloc = p->nAlloc*2 + len + 20;
- p->z = realloc(p->z, p->nAlloc);
- shell_check_oom(p->z);
+ abYield[iOp] = qrfStringInArray(zOp, azYield);
+ aiIndent[iOp] = 0;
+ nIndent = iOp+1;
+ if( qrfStringInArray(zOp, azNext) && p2op>0 ){
+ for(i=p2op; i<iOp; i++) aiIndent[i] += 2;
+ }
+ if( qrfStringInArray(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){
+ for(i=p2op; i<iOp; i++) aiIndent[i] += 2;
+ }
}
+ sqlite3_free(abYield);
- if( quote ){
- char *zCsr = p->z+p->n;
- *zCsr++ = quote;
- for(i=0; i<nAppend; i++){
- *zCsr++ = zAppend[i];
- if( zAppend[i]==quote ) *zCsr++ = quote;
+ /* Second pass. Actually generate output */
+ sqlite3_reset(p->pStmt);
+ if( p->iErr==SQLITE_OK ){
+ static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};
+ static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 };
+ static const int aScanExpWidth[] = {4,15, 6, 13, 4, 4, 4, 13, 2, 13};
+ static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 };
+ const int *aWidth = aExplainWidth;
+ const int *aMap = aExplainMap;
+ int nWidth = sizeof(aExplainWidth)/sizeof(int);
+ int iIndent = 1;
+ int nArg = p->nCol;
+ if( p->spec.eStyle==QRF_STYLE_StatsVm ){
+ aWidth = aScanExpWidth;
+ aMap = aScanExpMap;
+ nWidth = sizeof(aScanExpWidth)/sizeof(int);
+ iIndent = 3;
}
- *zCsr++ = quote;
- p->n = (int)(zCsr - p->z);
- *zCsr = '\0';
- }else{
- memcpy(p->z+p->n, zAppend, nAppend);
- p->n += nAppend;
- p->z[p->n] = '\0';
+ if( nArg>nWidth ) nArg = nWidth;
+
+ for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){
+ /* If this is the first row seen, print out the headers */
+ if( iOp==0 ){
+ for(i=0; i<nArg; i++){
+ const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]);
+ qrfWidthPrint(p,p->pOut, aWidth[i], zCol);
+ if( i==nArg-1 ){
+ sqlite3_str_append(p->pOut, "\n", 1);
+ }else{
+ sqlite3_str_append(p->pOut, " ", 2);
+ }
+ }
+ for(i=0; i<nArg; i++){
+ sqlite3_str_appendf(p->pOut, "%.*c", aWidth[i], '-');
+ if( i==nArg-1 ){
+ sqlite3_str_append(p->pOut, "\n", 1);
+ }else{
+ sqlite3_str_append(p->pOut, " ", 2);
+ }
+ }
+ }
+
+ for(i=0; i<nArg; i++){
+ const char *zSep = " ";
+ int w = aWidth[i];
+ const char *zVal = (const char*)sqlite3_column_text(p->pStmt, aMap[i]);
+ int len;
+ if( i==nArg-1 ) w = 0;
+ if( zVal==0 ) zVal = "";
+ len = (int)sqlite3_qrf_wcswidth(zVal);
+ if( len>w ){
+ w = len;
+ zSep = " ";
+ }
+ if( i==iIndent && aiIndent && iOp<nIndent ){
+ sqlite3_str_appendchar(p->pOut, aiIndent[iOp], ' ');
+ }
+ qrfWidthPrint(p, p->pOut, w, zVal);
+ if( i==nArg-1 ){
+ sqlite3_str_append(p->pOut, "\n", 1);
+ }else{
+ sqlite3_str_appendall(p->pOut, zSep);
+ }
+ }
+ p->nRow++;
+ }
+ qrfWrite(p);
+ }
+ sqlite3_free(aiIndent);
+}
+
+/*
+** Do a "scanstatus vm" style EXPLAIN listing on p->pStmt.
+**
+** p->pStmt is probably not an EXPLAIN query. Instead, construct a
+** new query that is a bytecode() rendering of p->pStmt with extra
+** columns for the "scanstatus vm" outputs, and run the results of
+** that new query through the normal EXPLAIN formatting.
+*/
+static void qrfScanStatusVm(Qrf *p){
+ sqlite3_stmt *pOrigStmt = p->pStmt;
+ sqlite3_stmt *pExplain;
+ int rc;
+ static const char *zSql =
+ " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
+ " format('% 6s (%.2f%%)',"
+ " CASE WHEN ncycle<100_000 THEN ncycle || ' '"
+ " WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'"
+ " WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'"
+ " ELSE (ncycle/1000_000_000) || 'G' END,"
+ " ncycle*100.0/(sum(ncycle) OVER ())"
+ " ) AS cycles"
+ " FROM bytecode(?1)";
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pExplain, 0);
+ if( rc ){
+ qrfError(p, rc, "%s", sqlite3_errmsg(p->db));
+ sqlite3_finalize(pExplain);
+ return;
}
+ sqlite3_bind_pointer(pExplain, 1, pOrigStmt, "stmt-pointer", 0);
+ p->pStmt = pExplain;
+ p->nCol = 10;
+ qrfExplain(p);
+ sqlite3_finalize(pExplain);
+ p->pStmt = pOrigStmt;
}
/*
@@ -1525,178 +3361,544 @@ static void appendText(ShellText *p, const char *zAppend, char quote){
** SQLite keyword. Be conservative in this estimate: When in doubt assume
** that quoting is required.
**
-** Return '"' if quoting is required. Return 0 if no quoting is required.
+** Return 1 if quoting is required. Return 0 if no quoting is required.
*/
-static char quoteChar(const char *zName){
+
+static int qrf_need_quote(const char *zName){
int i;
- if( zName==0 ) return '"';
- if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"';
- for(i=0; zName[i]; i++){
- if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"';
+ const unsigned char *z = (const unsigned char*)zName;
+ if( z==0 ) return 1;
+ if( !qrfAlpha(z[0]) ) return 1;
+ for(i=0; z[i]; i++){
+ if( !qrfAlnum(z[i]) ) return 1;
}
- return sqlite3_keyword_check(zName, i) ? '"' : 0;
+ return sqlite3_keyword_check(zName, i)!=0;
}
/*
-** Construct a fake object name and column list to describe the structure
-** of the view, virtual table, or table valued function zSchema.zName.
+** Helper function for QRF_STYLE_Json and QRF_STYLE_JObject.
+** The initial "{" for a JSON object that will contain row content
+** has been output. Now output all the content.
*/
-static char *shellFakeSchema(
- sqlite3 *db, /* The database connection containing the vtab */
- const char *zSchema, /* Schema of the database holding the vtab */
- const char *zName /* The name of the virtual table */
-){
- sqlite3_stmt *pStmt = 0;
- char *zSql;
- ShellText s;
- char cQuote;
- char *zDiv = "(";
- int nRow = 0;
-
- zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;",
- zSchema ? zSchema : "main", zName);
- shell_check_oom(zSql);
- sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- initText(&s);
- if( zSchema ){
- cQuote = quoteChar(zSchema);
- if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0;
- appendText(&s, zSchema, cQuote);
- appendText(&s, ".", 0);
- }
- cQuote = quoteChar(zName);
- appendText(&s, zName, cQuote);
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
- const char *zCol = (const char*)sqlite3_column_text(pStmt, 1);
- nRow++;
- appendText(&s, zDiv, 0);
- zDiv = ",";
- if( zCol==0 ) zCol = "";
- cQuote = quoteChar(zCol);
- appendText(&s, zCol, cQuote);
+static void qrfOneJsonRow(Qrf *p){
+ int i, nItem;
+ for(nItem=i=0; i<p->nCol; i++){
+ const char *zCName;
+ zCName = sqlite3_column_name(p->pStmt, i);
+ if( nItem>0 ) sqlite3_str_append(p->pOut, ",", 1);
+ nItem++;
+ qrfEncodeText(p, p->pOut, zCName);
+ sqlite3_str_append(p->pOut, ":", 1);
+ qrfRenderValue(p, p->pOut, i);
}
- appendText(&s, ")", 0);
- sqlite3_finalize(pStmt);
- if( nRow==0 ){
- freeText(&s);
- s.z = 0;
+ qrfWrite(p);
+}
+
+/*
+** Render a single row of output for non-columnar styles - any
+** style that lets us render row by row as the content is received
+** from the query.
+*/
+static void qrfOneSimpleRow(Qrf *p){
+ int i;
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Off:
+ case QRF_STYLE_Count: {
+ /* No-op */
+ break;
+ }
+ case QRF_STYLE_Json: {
+ if( p->nRow==0 ){
+ sqlite3_str_append(p->pOut, "[{", 2);
+ }else{
+ sqlite3_str_append(p->pOut, "},\n{", 4);
+ }
+ qrfOneJsonRow(p);
+ break;
+ }
+ case QRF_STYLE_JObject: {
+ if( p->nRow==0 ){
+ sqlite3_str_append(p->pOut, "{", 1);
+ }else{
+ sqlite3_str_append(p->pOut, "}\n{", 3);
+ }
+ qrfOneJsonRow(p);
+ break;
+ }
+ case QRF_STYLE_Html: {
+ if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
+ sqlite3_str_append(p->pOut, "<TR>", 4);
+ for(i=0; i<p->nCol; i++){
+ const char *zCName = sqlite3_column_name(p->pStmt, i);
+ sqlite3_str_append(p->pOut, "\n<TH>", 5);
+ qrfEncodeText(p, p->pOut, zCName);
+ }
+ sqlite3_str_append(p->pOut, "\n</TR>\n", 7);
+ }
+ sqlite3_str_append(p->pOut, "<TR>", 4);
+ for(i=0; i<p->nCol; i++){
+ sqlite3_str_append(p->pOut, "\n<TD>", 5);
+ qrfRenderValue(p, p->pOut, i);
+ }
+ sqlite3_str_append(p->pOut, "\n</TR>\n", 7);
+ qrfWrite(p);
+ break;
+ }
+ case QRF_STYLE_Insert: {
+ unsigned int mxIns = p->spec.nMultiInsert;
+ int szStart = sqlite3_str_length(p->pOut);
+ if( p->u.nIns==0 || p->u.nIns>=mxIns ){
+ if( p->u.nIns ){
+ sqlite3_str_append(p->pOut, ";\n", 2);
+ p->u.nIns = 0;
+ }
+ if( qrf_need_quote(p->spec.zTableName) ){
+ sqlite3_str_appendf(p->pOut,"INSERT INTO \"%w\"",p->spec.zTableName);
+ }else{
+ sqlite3_str_appendf(p->pOut,"INSERT INTO %s",p->spec.zTableName);
+ }
+ if( p->spec.bTitles==QRF_Yes ){
+ for(i=0; i<p->nCol; i++){
+ const char *zCName = sqlite3_column_name(p->pStmt, i);
+ if( qrf_need_quote(zCName) ){
+ sqlite3_str_appendf(p->pOut, "%c\"%w\"",
+ i==0 ? '(' : ',', zCName);
+ }else{
+ sqlite3_str_appendf(p->pOut, "%c%s",
+ i==0 ? '(' : ',', zCName);
+ }
+ }
+ sqlite3_str_append(p->pOut, ")", 1);
+ }
+ sqlite3_str_append(p->pOut," VALUES(", 8);
+ }else{
+ sqlite3_str_append(p->pOut,",\n (", 5);
+ }
+ for(i=0; i<p->nCol; i++){
+ if( i>0 ) sqlite3_str_append(p->pOut, ",", 1);
+ qrfRenderValue(p, p->pOut, i);
+ }
+ p->u.nIns += sqlite3_str_length(p->pOut) + 2 - szStart;
+ if( p->u.nIns>=mxIns ){
+ sqlite3_str_append(p->pOut, ");\n", 3);
+ p->u.nIns = 0;
+ }else{
+ sqlite3_str_append(p->pOut, ")", 1);
+ }
+ qrfWrite(p);
+ break;
+ }
+ case QRF_STYLE_Line: {
+ sqlite3_str *pVal;
+ int mxW;
+ int bWW;
+ int nSep;
+ if( p->u.sLine.azCol==0 ){
+ p->u.sLine.azCol = sqlite3_malloc64( p->nCol*sizeof(char*) );
+ if( p->u.sLine.azCol==0 ){
+ qrfOom(p);
+ break;
+ }
+ p->u.sLine.mxColWth = 0;
+ for(i=0; i<p->nCol; i++){
+ int sz;
+ const char *zCName = sqlite3_column_name(p->pStmt, i);
+ if( zCName==0 ) zCName = "unknown";
+ p->u.sLine.azCol[i] = sqlite3_mprintf("%s", zCName);
+ if( p->spec.nTitleLimit>0 ){
+ (void)qrfTitleLimit(p->u.sLine.azCol[i], p->spec.nTitleLimit);
+ }
+ sz = (int)sqlite3_qrf_wcswidth(p->u.sLine.azCol[i]);
+ if( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz;
+ }
+ }
+ if( p->nRow ) sqlite3_str_append(p->pOut, "\n", 1);
+ pVal = sqlite3_str_new(p->db);
+ nSep = (int)strlen(p->spec.zColumnSep);
+ mxW = p->mxWidth - (nSep + p->u.sLine.mxColWth);
+ bWW = p->spec.bWordWrap==QRF_Yes;
+ for(i=0; i<p->nCol; i++){
+ const char *zVal;
+ int cnt = 0;
+ qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth, p->u.sLine.azCol[i]);
+ sqlite3_str_append(p->pOut, p->spec.zColumnSep, nSep);
+ qrfRenderValue(p, pVal, i);
+ zVal = sqlite3_str_value(pVal);
+ if( zVal==0 ) zVal = "";
+ do{
+ int nThis, nWide, iNext;
+ qrfWrapLine(zVal, mxW, bWW, &nThis, &nWide, &iNext);
+ if( cnt ){
+ sqlite3_str_appendchar(p->pOut,p->u.sLine.mxColWth+nSep,' ');
+ }
+ cnt++;
+ if( cnt>p->mxHeight ){
+ zVal = "...";
+ nThis = iNext = 3;
+ }
+ sqlite3_str_append(p->pOut, zVal, nThis);
+ sqlite3_str_append(p->pOut, "\n", 1);
+ zVal += iNext;
+ }while( zVal[0] );
+ sqlite3_str_reset(pVal);
+ }
+ qrfStrErr(p, pVal);
+ sqlite3_free(sqlite3_str_finish(pVal));
+ qrfWrite(p);
+ break;
+ }
+ case QRF_STYLE_Eqp: {
+ const char *zEqpLine = (const char*)sqlite3_column_text(p->pStmt,3);
+ int iEqpId = sqlite3_column_int(p->pStmt, 0);
+ int iParentId = sqlite3_column_int(p->pStmt, 1);
+ if( zEqpLine==0 ) zEqpLine = "";
+ if( zEqpLine[0]=='-' ) qrfEqpRender(p, 0);
+ qrfEqpAppend(p, iEqpId, iParentId, zEqpLine);
+ break;
+ }
+ default: { /* QRF_STYLE_List */
+ if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){
+ int saved_eText = p->spec.eText;
+ p->spec.eText = p->spec.eTitle;
+ for(i=0; i<p->nCol; i++){
+ const char *zCName = sqlite3_column_name(p->pStmt, i);
+ if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
+ qrfEncodeText(p, p->pOut, zCName);
+ }
+ sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
+ qrfWrite(p);
+ p->spec.eText = saved_eText;
+ }
+ for(i=0; i<p->nCol; i++){
+ if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep);
+ qrfRenderValue(p, p->pOut, i);
+ }
+ sqlite3_str_appendall(p->pOut, p->spec.zRowSep);
+ qrfWrite(p);
+ break;
+ }
}
- return s.z;
+ p->nRow++;
}
/*
-** SQL function: strtod(X)
-**
-** Use the C-library strtod() function to convert string X into a double.
-** Used for comparing the accuracy of SQLite's internal text-to-float conversion
-** routines against the C-library.
+** Initialize the internal Qrf object.
*/
-static void shellStrtod(
- sqlite3_context *pCtx,
- int nVal,
- sqlite3_value **apVal
+static void qrfInitialize(
+ Qrf *p, /* State object to be initialized */
+ sqlite3_stmt *pStmt, /* Query whose output to be formatted */
+ const sqlite3_qrf_spec *pSpec, /* Format specification */
+ char **pzErr /* Write errors here */
){
- char *z = (char*)sqlite3_value_text(apVal[0]);
- UNUSED_PARAMETER(nVal);
- if( z==0 ) return;
- sqlite3_result_double(pCtx, strtod(z,0));
+ size_t sz; /* Size of pSpec[], based on pSpec->iVersion */
+ memset(p, 0, sizeof(*p));
+ p->pzErr = pzErr;
+ if( pSpec->iVersion>1 ){
+ qrfError(p, SQLITE_ERROR,
+ "unusable sqlite3_qrf_spec.iVersion (%d)",
+ pSpec->iVersion);
+ return;
+ }
+ p->pStmt = pStmt;
+ p->db = sqlite3_db_handle(pStmt);
+ p->pOut = sqlite3_str_new(p->db);
+ if( p->pOut==0 ){
+ qrfOom(p);
+ return;
+ }
+ p->iErr = SQLITE_OK;
+ p->nCol = sqlite3_column_count(p->pStmt);
+ p->nRow = 0;
+ sz = sizeof(sqlite3_qrf_spec);
+ memcpy(&p->spec, pSpec, sz);
+ if( p->spec.zNull==0 ) p->spec.zNull = "";
+ p->mxWidth = p->spec.nScreenWidth;
+ if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH;
+ p->mxHeight = p->spec.nLineLimit;
+ if( p->mxHeight<=0 ) p->mxHeight = 2147483647;
+ if( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto;
+ if( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto;
+ if( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto;
+ if( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto;
+ if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto;
+qrf_reinit:
+ switch( p->spec.eStyle ){
+ case QRF_Auto: {
+ switch( sqlite3_stmt_isexplain(pStmt) ){
+ case 0: p->spec.eStyle = QRF_STYLE_Box; break;
+ case 1: p->spec.eStyle = QRF_STYLE_Explain; break;
+ default: p->spec.eStyle = QRF_STYLE_Eqp; break;
+ }
+ goto qrf_reinit;
+ }
+ case QRF_STYLE_List: {
+ if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|";
+ if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
+ break;
+ }
+ case QRF_STYLE_JObject:
+ case QRF_STYLE_Json: {
+ p->spec.eText = QRF_TEXT_Json;
+ p->spec.zNull = "null";
+ break;
+ }
+ case QRF_STYLE_Html: {
+ p->spec.eText = QRF_TEXT_Html;
+ p->spec.zNull = "null";
+ break;
+ }
+ case QRF_STYLE_Insert: {
+ p->spec.eText = QRF_TEXT_Sql;
+ p->spec.zNull = "NULL";
+ if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){
+ p->spec.zTableName = "tab";
+ }
+ p->u.nIns = 0;
+ break;
+ }
+ case QRF_STYLE_Line: {
+ if( p->spec.zColumnSep==0 ){
+ p->spec.zColumnSep = ": ";
+ }
+ break;
+ }
+ case QRF_STYLE_Csv: {
+ p->spec.eStyle = QRF_STYLE_List;
+ p->spec.eText = QRF_TEXT_Csv;
+ p->spec.zColumnSep = ",";
+ p->spec.zRowSep = "\r\n";
+ p->spec.zNull = "";
+ break;
+ }
+ case QRF_STYLE_Quote: {
+ p->spec.eText = QRF_TEXT_Sql;
+ p->spec.zNull = "NULL";
+ p->spec.zColumnSep = ",";
+ p->spec.zRowSep = "\n";
+ break;
+ }
+ case QRF_STYLE_Eqp: {
+ int expMode = sqlite3_stmt_isexplain(p->pStmt);
+ if( expMode!=2 ){
+ sqlite3_stmt_explain(p->pStmt, 2);
+ p->expMode = expMode+1;
+ }
+ break;
+ }
+ case QRF_STYLE_Explain: {
+ int expMode = sqlite3_stmt_isexplain(p->pStmt);
+ if( expMode!=1 ){
+ sqlite3_stmt_explain(p->pStmt, 1);
+ p->expMode = expMode+1;
+ }
+ break;
+ }
+ }
+ if( p->spec.eEsc==QRF_Auto ){
+ p->spec.eEsc = QRF_ESC_Ascii;
+ }
+ if( p->spec.eText==QRF_Auto ){
+ p->spec.eText = QRF_TEXT_Plain;
+ }
+ if( p->spec.eTitle==QRF_Auto ){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ case QRF_STYLE_Column:
+ case QRF_STYLE_Table:
+ p->spec.eTitle = QRF_TEXT_Plain;
+ break;
+ default:
+ p->spec.eTitle = p->spec.eText;
+ break;
+ }
+ }
+ if( p->spec.eBlob==QRF_Auto ){
+ switch( p->spec.eText ){
+ case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break;
+ case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break;
+ case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break;
+ case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break;
+ default: p->spec.eBlob = QRF_BLOB_Text; break;
+ }
+ }
+ if( p->spec.bTitles==QRF_Auto ){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Box:
+ case QRF_STYLE_Csv:
+ case QRF_STYLE_Column:
+ case QRF_STYLE_Table:
+ case QRF_STYLE_Markdown:
+ p->spec.bTitles = QRF_Yes;
+ break;
+ default:
+ p->spec.bTitles = QRF_No;
+ break;
+ }
+ }
+ if( p->spec.bWordWrap==QRF_Auto ){
+ p->spec.bWordWrap = QRF_Yes;
+ }
+ if( p->spec.bTextJsonb==QRF_Auto ){
+ p->spec.bTextJsonb = QRF_No;
+ }
+ if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ",";
+ if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n";
}
/*
-** SQL function: dtostr(X)
-**
-** Use the C-library printf() function to convert real value X into a string.
-** Used for comparing the accuracy of SQLite's internal float-to-text conversion
-** routines against the C-library.
+** Finish rendering the results
*/
-static void shellDtostr(
- sqlite3_context *pCtx,
- int nVal,
- sqlite3_value **apVal
-){
- double r = sqlite3_value_double(apVal[0]);
- int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26;
- char z[400];
- if( n<1 ) n = 1;
- if( n>350 ) n = 350;
- sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r);
- sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
+static void qrfFinalize(Qrf *p){
+ switch( p->spec.eStyle ){
+ case QRF_STYLE_Count: {
+ sqlite3_str_appendf(p->pOut, "%lld\n", p->nRow);
+ break;
+ }
+ case QRF_STYLE_Json: {
+ if( p->nRow>0 ){
+ sqlite3_str_append(p->pOut, "}]\n", 3);
+ }
+ break;
+ }
+ case QRF_STYLE_JObject: {
+ if( p->nRow>0 ){
+ sqlite3_str_append(p->pOut, "}\n", 2);
+ }
+ break;
+ }
+ case QRF_STYLE_Insert: {
+ if( p->u.nIns ){
+ sqlite3_str_append(p->pOut, ";\n", 2);
+ }
+ break;
+ }
+ case QRF_STYLE_Line: {
+ if( p->u.sLine.azCol ){
+ int i;
+ for(i=0; i<p->nCol; i++) sqlite3_free(p->u.sLine.azCol[i]);
+ sqlite3_free(p->u.sLine.azCol);
+ }
+ break;
+ }
+ case QRF_STYLE_Stats:
+ case QRF_STYLE_StatsEst: {
+ i64 nCycle = 0;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ sqlite3_stmt_scanstatus_v2(p->pStmt, -1, SQLITE_SCANSTAT_NCYCLE,
+ SQLITE_SCANSTAT_COMPLEX, (void*)&nCycle);
+#endif
+ qrfEqpRender(p, nCycle);
+ break;
+ }
+ case QRF_STYLE_Eqp: {
+ qrfEqpRender(p, 0);
+ break;
+ }
+ }
+ qrfWrite(p);
+ qrfStrErr(p, p->pOut);
+ if( p->spec.pzOutput ){
+ if( p->spec.pzOutput[0] ){
+ sqlite3_int64 n, sz;
+ char *zCombined;
+ sz = strlen(p->spec.pzOutput[0]);
+ n = sqlite3_str_length(p->pOut);
+ zCombined = sqlite3_realloc64(p->spec.pzOutput[0], sz+n+1);
+ if( zCombined==0 ){
+ sqlite3_free(p->spec.pzOutput[0]);
+ p->spec.pzOutput[0] = 0;
+ qrfOom(p);
+ }else{
+ p->spec.pzOutput[0] = zCombined;
+ memcpy(zCombined+sz, sqlite3_str_value(p->pOut), n+1);
+ }
+ sqlite3_free(sqlite3_str_finish(p->pOut));
+ }else{
+ p->spec.pzOutput[0] = sqlite3_str_finish(p->pOut);
+ }
+ }else if( p->pOut ){
+ sqlite3_free(sqlite3_str_finish(p->pOut));
+ }
+ if( p->expMode>0 ){
+ sqlite3_stmt_explain(p->pStmt, p->expMode-1);
+ }
+ if( p->actualWidth ){
+ sqlite3_free(p->actualWidth);
+ }
+ if( p->pJTrans ){
+ sqlite3 *db = sqlite3_db_handle(p->pJTrans);
+ sqlite3_finalize(p->pJTrans);
+ sqlite3_close(db);
+ }
}
/*
-** SQL function: shell_add_schema(S,X)
-**
-** Add the schema name X to the CREATE statement in S and return the result.
-** Examples:
-**
-** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x);
-**
-** Also works on
-**
-** CREATE INDEX
-** CREATE UNIQUE INDEX
-** CREATE VIEW
-** CREATE TRIGGER
-** CREATE VIRTUAL TABLE
-**
-** This UDF is used by the .schema command to insert the schema name of
-** attached databases into the middle of the sqlite_schema.sql field.
+** Run the prepared statement pStmt and format the results according
+** to the specification provided in pSpec. Return an error code.
+** If pzErr is not NULL and if an error occurs, write an error message
+** into *pzErr.
*/
-static void shellAddSchemaName(
- sqlite3_context *pCtx,
- int nVal,
- sqlite3_value **apVal
+int sqlite3_format_query_result(
+ sqlite3_stmt *pStmt, /* Statement to evaluate */
+ const sqlite3_qrf_spec *pSpec, /* Format specification */
+ char **pzErr /* Write error message here */
){
- static const char *aPrefix[] = {
- "TABLE",
- "INDEX",
- "UNIQUE INDEX",
- "VIEW",
- "TRIGGER",
- "VIRTUAL TABLE"
- };
- int i = 0;
- const char *zIn = (const char*)sqlite3_value_text(apVal[0]);
- const char *zSchema = (const char*)sqlite3_value_text(apVal[1]);
- const char *zName = (const char*)sqlite3_value_text(apVal[2]);
- sqlite3 *db = sqlite3_context_db_handle(pCtx);
- UNUSED_PARAMETER(nVal);
- if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){
- for(i=0; i<ArraySize(aPrefix); i++){
- int n = strlen30(aPrefix[i]);
- if( cli_strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
- char *z = 0;
- char *zFake = 0;
- if( zSchema ){
- char cQuote = quoteChar(zSchema);
- if( cQuote && sqlite3_stricmp(zSchema,"temp")!=0 ){
- z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8);
- }else{
- z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8);
- }
- }
- if( zName
- && aPrefix[i][0]=='V'
- && (zFake = shellFakeSchema(db, zSchema, zName))!=0
- ){
- if( z==0 ){
- z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
- }else{
- z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
- }
- free(zFake);
- }
- if( z ){
- sqlite3_result_text(pCtx, z, -1, sqlite3_free);
- return;
- }
+ Qrf qrf; /* The new Qrf being created */
+
+ if( pStmt==0 ) return SQLITE_OK; /* No-op */
+ if( pSpec==0 ) return SQLITE_MISUSE;
+ qrfInitialize(&qrf, pStmt, pSpec, pzErr);
+ switch( qrf.spec.eStyle ){
+ case QRF_STYLE_Box:
+ case QRF_STYLE_Column:
+ case QRF_STYLE_Markdown:
+ case QRF_STYLE_Table: {
+ /* Columnar modes require that the entire query be evaluated and the
+ ** results stored in memory, so that we can compute column widths */
+ qrfColumnar(&qrf);
+ break;
+ }
+ case QRF_STYLE_Explain: {
+ qrfExplain(&qrf);
+ break;
+ }
+ case QRF_STYLE_StatsVm: {
+ qrfScanStatusVm(&qrf);
+ break;
+ }
+ case QRF_STYLE_Stats:
+ case QRF_STYLE_StatsEst: {
+ qrfEqpStats(&qrf);
+ break;
+ }
+ default: {
+ /* Non-columnar modes where the output can occur after each row
+ ** of result is received */
+ while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
+ qrfOneSimpleRow(&qrf);
}
+ break;
}
}
- sqlite3_result_value(pCtx, apVal[0]);
+ qrfResetStmt(&qrf);
+ qrfFinalize(&qrf);
+ return qrf.iErr;
}
+/************************* End ext/qrf/qrf.c ********************/
+
+/* Use console I/O package as a direct INCLUDE. */
+#define SQLITE_INTERNAL_LINKAGE static
+
+#ifdef SQLITE_SHELL_FIDDLE
+/* Deselect most features from the console I/O package for Fiddle. */
+# define SQLITE_CIO_NO_REDIRECT
+# define SQLITE_CIO_NO_CLASSIFY
+# define SQLITE_CIO_NO_TRANSLATE
+# define SQLITE_CIO_NO_SETMODE
+# define SQLITE_CIO_NO_FLUSH
+#endif
+
/*
** The source code for several run-time loadable extensions is inserted
** below by the ../tool/mkshellc.tcl script. Before processing that included
@@ -1706,10 +3908,9 @@ static void shellAddSchemaName(
#define SQLITE_EXTENSION_INIT1
#define SQLITE_EXTENSION_INIT2(X) (void)(X)
-#if defined(_WIN32) && defined(_MSC_VER)
-/************************* Begin test_windirent.h ******************/
+/************************* Begin ext/misc/windirent.h ******************/
/*
-** 2015 November 30
+** 2025-06-05
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -1719,323 +3920,161 @@ static void shellAddSchemaName(
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains declarations for most of the opendir() family of
-** POSIX functions on Win32 using the MSVCRT.
+**
+** An implementation of opendir(), readdir(), and closedir() for Windows,
+** based on the FindFirstFile(), FindNextFile(), and FindClose() APIs
+** of Win32.
+**
+** #include this file inside any C-code module that needs to use
+** opendir()/readdir()/closedir(). This file is a no-op on non-Windows
+** machines. On Windows, static functions are defined that implement
+** those standard interfaces.
*/
-
#if defined(_WIN32) && defined(_MSC_VER) && !defined(SQLITE_WINDIRENT_H)
#define SQLITE_WINDIRENT_H
-/*
-** We need several data types from the Windows SDK header.
-*/
-
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
-
-#include "windows.h"
-
-/*
-** We need several support functions from the SQLite core.
-*/
-
-/* #include "sqlite3.h" */
-
-/*
-** We need several things from the ANSI and MSVCRT headers.
-*/
-
+#include <windows.h>
+#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
-#include <io.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
-
-/*
-** We may need several defines that should have been in "sys/stat.h".
-*/
-
+#include <string.h>
+#ifndef FILENAME_MAX
+# define FILENAME_MAX (260)
+#endif
#ifndef S_ISREG
-#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
-
#ifndef S_ISDIR
-#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
-
#ifndef S_ISLNK
-#define S_ISLNK(mode) (0)
-#endif
-
-/*
-** We may need to provide the "mode_t" type.
-*/
-
-#ifndef MODE_T_DEFINED
- #define MODE_T_DEFINED
- typedef unsigned short mode_t;
+#define S_ISLNK(m) (0)
#endif
+typedef unsigned short mode_t;
-/*
-** We may need to provide the "ino_t" type.
+/* The dirent object for Windows is abbreviated. The only field really
+** usable by applications is d_name[].
*/
-
-#ifndef INO_T_DEFINED
- #define INO_T_DEFINED
- typedef unsigned short ino_t;
-#endif
-
-/*
-** We need to define "NAME_MAX" if it was not present in "limits.h".
-*/
-
-#ifndef NAME_MAX
-# ifdef FILENAME_MAX
-# define NAME_MAX (FILENAME_MAX)
-# else
-# define NAME_MAX (260)
-# endif
-# define DIRENT_NAME_MAX (NAME_MAX)
-#endif
-
-/*
-** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
-*/
-
-#ifndef NULL_INTPTR_T
-# define NULL_INTPTR_T ((intptr_t)(0))
-#endif
-
-#ifndef BAD_INTPTR_T
-# define BAD_INTPTR_T ((intptr_t)(-1))
-#endif
-
-/*
-** We need to provide the necessary structures and related types.
-*/
-
-#ifndef DIRENT_DEFINED
-#define DIRENT_DEFINED
-typedef struct DIRENT DIRENT;
-typedef DIRENT *LPDIRENT;
-struct DIRENT {
- ino_t d_ino; /* Sequence number, do not use. */
- unsigned d_attributes; /* Win32 file attributes. */
- char d_name[NAME_MAX + 1]; /* Name within the directory. */
+struct dirent {
+ int d_ino; /* Inode number (synthesized) */
+ unsigned d_attributes; /* File attributes */
+ char d_name[FILENAME_MAX]; /* Null-terminated filename */
};
-#endif
-#ifndef DIR_DEFINED
-#define DIR_DEFINED
+/* The internals of DIR are opaque according to standards. So it
+** does not matter what we put here. */
typedef struct DIR DIR;
-typedef DIR *LPDIR;
struct DIR {
- intptr_t d_handle; /* Value returned by "_findfirst". */
- DIRENT d_first; /* DIRENT constructed based on "_findfirst". */
- DIRENT d_next; /* DIRENT constructed based on "_findnext". */
+ intptr_t d_handle; /* Handle for findfirst()/findnext() */
+ struct dirent cur; /* Current entry */
};
-#endif
-
-/*
-** Provide a macro, for use by the implementation, to determine if a
-** particular directory entry should be skipped over when searching for
-** the next directory entry that should be returned by the readdir().
-*/
-
-#ifndef is_filtered
-# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
-#endif
-
-/*
-** Provide the function prototype for the POSIX compatible getenv()
-** function. This function is not thread-safe.
-*/
-
-extern const char *windirent_getenv(const char *name);
-
-/*
-** Finally, we can provide the function prototypes for the opendir(),
-** readdir(), and closedir() POSIX functions.
-*/
-
-extern LPDIR opendir(const char *dirname);
-extern LPDIRENT readdir(LPDIR dirp);
-extern INT closedir(LPDIR dirp);
-
-#endif /* defined(WIN32) && defined(_MSC_VER) */
-
-/************************* End test_windirent.h ********************/
-/************************* Begin test_windirent.c ******************/
-/*
-** 2015 November 30
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file contains code to implement most of the opendir() family of
-** POSIX functions on Win32 using the MSVCRT.
-*/
-#if defined(_WIN32) && defined(_MSC_VER)
-/* #include "test_windirent.h" */
+/* Ignore hidden and system files */
+#define WindowsFileToIgnore(a) \
+ ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
/*
-** Implementation of the POSIX getenv() function using the Win32 API.
-** This function is not thread-safe.
+** Close a previously opened directory
*/
-const char *windirent_getenv(
- const char *name
-){
- static char value[32768]; /* Maximum length, per MSDN */
- DWORD dwSize = sizeof(value) / sizeof(char); /* Size in chars */
- DWORD dwRet; /* Value returned by GetEnvironmentVariableA() */
-
- memset(value, 0, sizeof(value));
- dwRet = GetEnvironmentVariableA(name, value, dwSize);
- if( dwRet==0 || dwRet>dwSize ){
- /*
- ** The function call to GetEnvironmentVariableA() failed -OR-
- ** the buffer is not large enough. Either way, return NULL.
- */
- return 0;
- }else{
- /*
- ** The function call to GetEnvironmentVariableA() succeeded
- ** -AND- the buffer contains the entire value.
- */
- return value;
+static int closedir(DIR *pDir){
+ int rc = 0;
+ if( pDir==0 ){
+ return EINVAL;
+ }
+ if( pDir->d_handle!=0 && pDir->d_handle!=(-1) ){
+ rc = _findclose(pDir->d_handle);
}
+ sqlite3_free(pDir);
+ return rc;
}
/*
-** Implementation of the POSIX opendir() function using the MSVCRT.
+** Open a new directory. The directory name should be UTF-8 encoded.
+** appropriate translations happen automatically.
*/
-LPDIR opendir(
- const char *dirname /* Directory name, UTF8 encoding */
-){
- struct _wfinddata_t data;
- LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
- SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
+static DIR *opendir(const char *zDirName){
+ DIR *pDir;
wchar_t *b1;
sqlite3_int64 sz;
+ struct _wfinddata_t data;
- if( dirp==NULL ) return NULL;
- memset(dirp, 0, sizeof(DIR));
-
- /* TODO: Remove this if Unix-style root paths are not used. */
- if( sqlite3_stricmp(dirname, "/")==0 ){
- dirname = windirent_getenv("SystemDrive");
- }
-
+ pDir = sqlite3_malloc64( sizeof(DIR) );
+ if( pDir==0 ) return 0;
+ memset(pDir, 0, sizeof(DIR));
memset(&data, 0, sizeof(data));
- sz = strlen(dirname);
+ sz = strlen(zDirName);
b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
if( b1==0 ){
- closedir(dirp);
+ closedir(pDir);
return NULL;
}
- sz = MultiByteToWideChar(CP_UTF8, 0, dirname, sz, b1, sz);
+ sz = MultiByteToWideChar(CP_UTF8, 0, zDirName, sz, b1, sz);
b1[sz++] = '\\';
b1[sz++] = '*';
b1[sz] = 0;
- if( sz+1>(sqlite3_int64)namesize ){
- closedir(dirp);
+ if( sz+1>sizeof(data.name)/sizeof(data.name[0]) ){
+ closedir(pDir);
sqlite3_free(b1);
return NULL;
}
memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
sqlite3_free(b1);
- dirp->d_handle = _wfindfirst(data.name, &data);
-
- if( dirp->d_handle==BAD_INTPTR_T ){
- closedir(dirp);
+ pDir->d_handle = _wfindfirst(data.name, &data);
+ if( pDir->d_handle<0 ){
+ closedir(pDir);
return NULL;
}
-
- /* TODO: Remove this block to allow hidden and/or system files. */
- if( is_filtered(data) ){
-next:
-
+ while( WindowsFileToIgnore(data) ){
memset(&data, 0, sizeof(data));
- if( _wfindnext(dirp->d_handle, &data)==-1 ){
- closedir(dirp);
+ if( _wfindnext(pDir->d_handle, &data)==-1 ){
+ closedir(pDir);
return NULL;
}
-
- /* TODO: Remove this block to allow hidden and/or system files. */
- if( is_filtered(data) ) goto next;
}
-
- dirp->d_first.d_attributes = data.attrib;
+ pDir->cur.d_ino = 0;
+ pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
- dirp->d_first.d_name, DIRENT_NAME_MAX, 0, 0);
- return dirp;
+ pDir->cur.d_name, FILENAME_MAX, 0, 0);
+ return pDir;
}
/*
-** Implementation of the POSIX readdir() function using the MSVCRT.
+** Read the next entry from a directory.
+**
+** The returned struct-dirent object is managed by DIR. It is only
+** valid until the next readdir() or closedir() call. Only the
+** d_name[] field is meaningful. The d_name[] value has been
+** translated into UTF8.
*/
-LPDIRENT readdir(
- LPDIR dirp
-){
+static struct dirent *readdir(DIR *pDir){
struct _wfinddata_t data;
-
- if( dirp==NULL ) return NULL;
-
- if( dirp->d_first.d_ino==0 ){
- dirp->d_first.d_ino++;
- dirp->d_next.d_ino++;
-
- return &dirp->d_first;
+ if( pDir==0 ) return 0;
+ if( (pDir->cur.d_ino++)==0 ){
+ return &pDir->cur;
}
-
-next:
-
- memset(&data, 0, sizeof(data));
- if( _wfindnext(dirp->d_handle, &data)==-1 ) return NULL;
-
- /* TODO: Remove this block to allow hidden and/or system files. */
- if( is_filtered(data) ) goto next;
-
- dirp->d_next.d_ino++;
- dirp->d_next.d_attributes = data.attrib;
+ do{
+ memset(&data, 0, sizeof(data));
+ if( _wfindnext(pDir->d_handle, &data)==-1 ){
+ return NULL;
+ }
+ }while( WindowsFileToIgnore(data) );
+ pDir->cur.d_attributes = data.attrib;
WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
- dirp->d_next.d_name, DIRENT_NAME_MAX, 0, 0);
- return &dirp->d_next;
-}
-
-/*
-** Implementation of the POSIX closedir() function using the MSVCRT.
-*/
-INT closedir(
- LPDIR dirp
-){
- INT result = 0;
-
- if( dirp==NULL ) return EINVAL;
-
- if( dirp->d_handle!=NULL_INTPTR_T && dirp->d_handle!=BAD_INTPTR_T ){
- result = _findclose(dirp->d_handle);
- }
-
- sqlite3_free(dirp);
- return result;
+ pDir->cur.d_name, FILENAME_MAX, 0, 0);
+ return &pDir->cur;
}
-#endif /* defined(WIN32) && defined(_MSC_VER) */
+#endif /* defined(_WIN32) && defined(_MSC_VER) */
-/************************* End test_windirent.c ********************/
-#define dirent DIRENT
-#endif
-/************************* Begin ../ext/misc/memtrace.c ******************/
+/************************* End ext/misc/windirent.h ********************/
+/************************* Begin ext/misc/memtrace.c ******************/
/*
** 2019-01-21
**
@@ -2145,8 +4184,8 @@ int sqlite3MemTraceDeactivate(void){
return rc;
}
-/************************* End ../ext/misc/memtrace.c ********************/
-/************************* Begin ../ext/misc/pcachetrace.c ******************/
+/************************* End ext/misc/memtrace.c ********************/
+/************************* Begin ext/misc/pcachetrace.c ******************/
/*
** 2023-06-21
**
@@ -2327,8 +4366,8 @@ int sqlite3PcacheTraceDeactivate(void){
return rc;
}
-/************************* End ../ext/misc/pcachetrace.c ********************/
-/************************* Begin ../ext/misc/shathree.c ******************/
+/************************* End ext/misc/pcachetrace.c ********************/
+/************************* Begin ext/misc/shathree.c ******************/
/*
** 2017-03-08
**
@@ -3184,8 +5223,8 @@ int sqlite3_shathree_init(
return rc;
}
-/************************* End ../ext/misc/shathree.c ********************/
-/************************* Begin ../ext/misc/sha1.c ******************/
+/************************* End ext/misc/shathree.c ********************/
+/************************* Begin ext/misc/sha1.c ******************/
/*
** 2017-01-27
**
@@ -3418,13 +5457,16 @@ static void hash_finish(
*****************************************************************************/
/*
-** Implementation of the sha1(X) function.
+** Two SQL functions: sha1(X) and sha1b(X).
**
-** Return a lower-case hexadecimal rendering of the SHA1 hash of the
-** argument X. If X is a BLOB, it is hashed as is. For all other
+** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash
+** of the argument X. If X is a BLOB, it is hashed as is. For all other
** types of input, X is converted into a UTF-8 string and the string
-** is hash without the trailing 0x00 terminator. The hash of a NULL
+** is hashed without the trailing 0x00 terminator. The hash of a NULL
** value is NULL.
+**
+** sha1b(X) is the same except that it returns a 20-byte BLOB containing
+** the binary hash instead of a hexadecimal string.
*/
static void sha1Func(
sqlite3_context *context,
@@ -3434,22 +5476,27 @@ static void sha1Func(
SHA1Context cx;
int eType = sqlite3_value_type(argv[0]);
int nByte = sqlite3_value_bytes(argv[0]);
+ const unsigned char *pData;
char zOut[44];
assert( argc==1 );
if( eType==SQLITE_NULL ) return;
hash_init(&cx);
if( eType==SQLITE_BLOB ){
- hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
+ pData = (const unsigned char*)sqlite3_value_blob(argv[0]);
}else{
- hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
+ pData = (const unsigned char*)sqlite3_value_text(argv[0]);
}
+ if( pData==0 ) return;
+ hash_step(&cx, pData, nByte);
if( sqlite3_user_data(context)!=0 ){
+ /* sha1b() - binary result */
hash_finish(&cx, zOut, 1);
sqlite3_result_blob(context, zOut, 20, SQLITE_TRANSIENT);
}else{
+ /* sha1() - hexadecimal text result */
hash_finish(&cx, zOut, 0);
- sqlite3_result_blob(context, zOut, 40, SQLITE_TRANSIENT);
+ sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
}
}
@@ -3503,6 +5550,7 @@ static void sha1QueryFunc(
}
nCol = sqlite3_column_count(pStmt);
z = sqlite3_sql(pStmt);
+ if( z==0 ) z = "";
n = (int)strlen(z);
hash_step_vformat(&cx,"S%d:",n);
hash_step(&cx,(unsigned char*)z,n);
@@ -3596,8 +5644,8 @@ int sqlite3_sha_init(
return rc;
}
-/************************* End ../ext/misc/sha1.c ********************/
-/************************* Begin ../ext/misc/uint.c ******************/
+/************************* End ext/misc/sha1.c ********************/
+/************************* Begin ext/misc/uint.c ******************/
/*
** 2020-04-14
**
@@ -3691,8 +5739,8 @@ int sqlite3_uint_init(
return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
}
-/************************* End ../ext/misc/uint.c ********************/
-/************************* Begin ../ext/misc/decimal.c ******************/
+/************************* End ext/misc/uint.c ********************/
+/************************* Begin ext/misc/decimal.c ******************/
/*
** 2020-06-22
**
@@ -3726,6 +5774,10 @@ SQLITE_EXTENSION_INIT1
#define IsSpace(X) isspace((unsigned char)X)
#endif
+#ifndef SQLITE_DECIMAL_MAX_DIGIT
+# define SQLITE_DECIMAL_MAX_DIGIT 10000000
+#endif
+
/* A decimal object */
typedef struct Decimal Decimal;
struct Decimal {
@@ -3764,7 +5816,8 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
int i;
int iExp = 0;
- p = sqlite3_malloc( sizeof(*p) );
+ if( zIn==0 ) goto new_from_text_failed;
+ p = sqlite3_malloc64( sizeof(*p) );
if( p==0 ) goto new_from_text_failed;
p->sign = 0;
p->oom = 0;
@@ -3823,8 +5876,10 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
- if( p->a==0 ) goto new_from_text_failed;
+ signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ + (sqlite3_int64)iExp + 1 );
+ if( a==0 ) goto new_from_text_failed;
+ p->a = a;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
@@ -3842,14 +5897,21 @@ static Decimal *decimalNewFromText(const char *zIn, int n){
}
}
if( iExp>0 ){
- p->a = sqlite3_realloc64(p->a, p->nDigit + iExp + 1 );
- if( p->a==0 ) goto new_from_text_failed;
+ signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ + (sqlite3_int64)iExp + 1 );
+ if( a==0 ) goto new_from_text_failed;
+ p->a = a;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
p->nFrac += iExp;
}
}
+ if( p->sign ){
+ for(i=0; i<p->nDigit && p->a[i]==0; i++){}
+ if( i>=p->nDigit ) p->sign = 0;
+ }
+ if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed;
return p;
new_from_text_failed:
@@ -3942,7 +6004,7 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_null(pCtx);
return;
}
- z = sqlite3_malloc( p->nDigit+4 );
+ z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
if( z==0 ){
sqlite3_result_error_nomem(pCtx);
return;
@@ -3981,11 +6043,37 @@ static void decimal_result(sqlite3_context *pCtx, Decimal *p){
}
/*
+** Round a decimal value to N significant digits. N must be positive.
+*/
+static void decimal_round(Decimal *p, int N){
+ int i;
+ int nZero;
+ if( N<1 ) return;
+ if( p==0 ) return;
+ if( p->nDigit<=N ) return;
+ for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){}
+ N += nZero;
+ if( p->nDigit<=N ) return;
+ if( p->a[N]>4 ){
+ p->a[N-1]++;
+ for(i=N-1; i>0 && p->a[i]>9; i--){
+ p->a[i] = 0;
+ p->a[i-1]++;
+ }
+ if( p->a[0]>9 ){
+ p->a[0] = 1;
+ p->nFrac--;
+ }
+ }
+ memset(&p->a[N], 0, p->nDigit - N);
+}
+
+/*
** Make the given Decimal the result in an format similar to '%+#e'.
** In other words, show exponential notation with leading and trailing
** zeros omitted.
*/
-static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
+static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){
char *z; /* The output buffer */
int i; /* Loop counter */
int nZero; /* Number of leading zeros */
@@ -4003,11 +6091,12 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
sqlite3_result_null(pCtx);
return;
}
- for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
+ if( N<1 ) N = 0;
+ for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
- z = sqlite3_malloc( nDigit+20 );
+ z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 );
if( z==0 ){
sqlite3_result_error_nomem(pCtx);
return;
@@ -4052,13 +6141,21 @@ static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p){
** pB!=0
** pB->isNull==0
*/
-static int decimal_cmp(const Decimal *pA, const Decimal *pB){
+static int decimal_cmp(Decimal *pA, Decimal *pB){
int nASig, nBSig, rc, n;
+ while( pA->nFrac>0 && pA->a[pA->nDigit-1]==0 ){
+ pA->nDigit--;
+ pA->nFrac--;
+ }
+ while( pB->nFrac>0 && pB->a[pB->nDigit-1]==0 ){
+ pB->nDigit--;
+ pB->nFrac--;
+ }
if( pA->sign!=pB->sign ){
return pA->sign ? -1 : +1;
}
if( pA->sign ){
- const Decimal *pTemp = pA;
+ Decimal *pTemp = pA;
pA = pB;
pB = pTemp;
}
@@ -4111,15 +6208,18 @@ cmp_done:
static void decimal_expand(Decimal *p, int nDigit, int nFrac){
int nAddSig;
int nAddFrac;
+ signed char *a;
if( p==0 ) return;
nAddFrac = nFrac - p->nFrac;
nAddSig = (nDigit - p->nDigit) - nAddFrac;
if( nAddFrac==0 && nAddSig==0 ) return;
- p->a = sqlite3_realloc64(p->a, nDigit+1);
- if( p->a==0 ){
+ if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; }
+ a = sqlite3_realloc64(p->a, nDigit+1);
+ if( a==0 ){
p->oom = 1;
return;
}
+ p->a = a;
if( nAddSig ){
memmove(p->a+nAddSig, p->a, p->nDigit);
memset(p->a, 0, nAddSig);
@@ -4214,13 +6314,18 @@ static void decimalMul(Decimal *pA, Decimal *pB){
signed char *acc = 0;
int i, j, k;
int minFrac;
+ sqlite3_int64 sumDigit;
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
- acc = sqlite3_malloc64( pA->nDigit + pB->nDigit + 2 );
+ sumDigit = pA->nDigit;
+ sumDigit += pB->nDigit;
+ sumDigit += 2;
+ if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; }
+ acc = sqlite3_malloc64( sumDigit );
if( acc==0 ){
pA->oom = 1;
goto mul_end;
@@ -4307,7 +6412,7 @@ static Decimal *decimalFromDouble(double r){
isNeg = 0;
}
memcpy(&a,&r,sizeof(a));
- if( a==0 ){
+ if( a==0 || a==(sqlite3_int64)0x8000000000000000LL){
e = 0;
m = 0;
}else{
@@ -4357,10 +6462,16 @@ static void decimalFunc(
sqlite3_value **argv
){
Decimal *p = decimal_new(context, argv[0], 0);
- UNUSED_PARAMETER(argc);
+ int N;
+ if( argc==2 ){
+ N = sqlite3_value_int(argv[1]);
+ if( N>0 ) decimal_round(p, N);
+ }else{
+ N = 0;
+ }
if( p ){
if( sqlite3_user_data(context)!=0 ){
- decimal_result_sci(context, p);
+ decimal_result_sci(context, p, N);
}else{
decimal_result(context, p);
}
@@ -4446,7 +6557,7 @@ static void decimalSumStep(
if( p==0 ) return;
if( !p->isInit ){
p->isInit = 1;
- p->a = sqlite3_malloc(2);
+ p->a = sqlite3_malloc64(2);
if( p->a==0 ){
p->oom = 1;
}else{
@@ -4530,7 +6641,7 @@ static void decimalPow2Func(
UNUSED_PARAMETER(argc);
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
Decimal *pA = decimalPow2(sqlite3_value_int(argv[0]));
- decimal_result_sci(context, pA);
+ decimal_result_sci(context, pA, 0);
decimal_free(pA);
}
}
@@ -4551,7 +6662,9 @@ int sqlite3_decimal_init(
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} aFunc[] = {
{ "decimal", 1, 0, decimalFunc },
+ { "decimal", 2, 0, decimalFunc },
{ "decimal_exp", 1, 1, decimalFunc },
+ { "decimal_exp", 2, 1, decimalFunc },
{ "decimal_cmp", 2, 0, decimalCmpFunc },
{ "decimal_add", 2, 0, decimalAddFunc },
{ "decimal_sub", 2, 0, decimalSubFunc },
@@ -4581,516 +6694,8 @@ int sqlite3_decimal_init(
return rc;
}
-/************************* End ../ext/misc/decimal.c ********************/
-/************************* Begin ../ext/misc/percentile.c ******************/
-/*
-** 2013-05-28
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains code to implement the percentile(Y,P) SQL function
-** and similar as described below:
-**
-** (1) The percentile(Y,P) function is an aggregate function taking
-** exactly two arguments.
-**
-** (2) If the P argument to percentile(Y,P) is not the same for every
-** row in the aggregate then an error is thrown. The word "same"
-** in the previous sentence means that the value differ by less
-** than 0.001.
-**
-** (3) If the P argument to percentile(Y,P) evaluates to anything other
-** than a number in the range of 0.0 to 100.0 inclusive then an
-** error is thrown.
-**
-** (4) If any Y argument to percentile(Y,P) evaluates to a value that
-** is not NULL and is not numeric then an error is thrown.
-**
-** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
-** infinity then an error is thrown. (SQLite always interprets NaN
-** values as NULL.)
-**
-** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
-** including CASE WHEN expressions.
-**
-** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
-** one million (1,000,000) rows.
-**
-** (8) If there are no non-NULL values for Y, then percentile(Y,P)
-** returns NULL.
-**
-** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
-** returns the one Y value.
-**
-** (10) If there N non-NULL values of Y where N is two or more and
-** the Y values are ordered from least to greatest and a graph is
-** drawn from 0 to N-1 such that the height of the graph at J is
-** the J-th Y value and such that straight lines are drawn between
-** adjacent Y values, then the percentile(Y,P) function returns
-** the height of the graph at P*(N-1)/100.
-**
-** (11) The percentile(Y,P) function always returns either a floating
-** point number or NULL.
-**
-** (12) The percentile(Y,P) is implemented as a single C99 source-code
-** file that compiles into a shared-library or DLL that can be loaded
-** into SQLite using the sqlite3_load_extension() interface.
-**
-** (13) A separate median(Y) function is the equivalent percentile(Y,50).
-**
-** (14) A separate percentile_cont(Y,P) function is equivalent to
-** percentile(Y,P/100.0). In other words, the fraction value in
-** the second argument is in the range of 0 to 1 instead of 0 to 100.
-**
-** (15) A separate percentile_disc(Y,P) function is like
-** percentile_cont(Y,P) except that instead of returning the weighted
-** average of the nearest two input values, it returns the next lower
-** value. So the percentile_disc(Y,P) will always return a value
-** that was one of the inputs.
-**
-** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
-** percentile_disc(Y,P) can be used as window functions.
-**
-** Differences from standard SQL:
-**
-** * The percentile_cont(X,P) function is equivalent to the following in
-** standard SQL:
-**
-** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
-**
-** The SQLite syntax is much more compact. The standard SQL syntax
-** is also supported if SQLite is compiled with the
-** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
-**
-** * No median(X) function exists in the SQL standard. App developers
-** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
-**
-** * No percentile(Y,P) function exists in the SQL standard. Instead of
-** percential(Y,P), developers must write this:
-** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
-** the fraction parameter to percentile() goes from 0 to 100 whereas
-** the fraction parameter in SQL standard percentile_cont() goes from
-** 0 to 1.
-**
-** Implementation notes as of 2024-08-31:
-**
-** * The regular aggregate-function versions of these routines work
-** by accumulating all values in an array of doubles, then sorting
-** that array using quicksort before computing the answer. Thus
-** the runtime is O(NlogN) where N is the number of rows of input.
-**
-** * For the window-function versions of these routines, the array of
-** inputs is sorted as soon as the first value is computed. Thereafter,
-** the array is kept in sorted order using an insert-sort. This
-** results in O(N*K) performance where K is the size of the window.
-** One can imagine alternative implementations that give O(N*logN*logK)
-** performance, but they require more complex logic and data structures.
-** The developers have elected to keep the asymptotically slower
-** algorithm for now, for simplicity, under the theory that window
-** functions are seldom used and when they are, the window size K is
-** often small. The developers might revisit that decision later,
-** should the need arise.
-*/
-#if defined(SQLITE3_H)
- /* no-op */
-#elif defined(SQLITE_STATIC_PERCENTILE)
-/* # include "sqlite3.h" */
-#else
-/* # include "sqlite3ext.h" */
- SQLITE_EXTENSION_INIT1
-#endif
-#include <assert.h>
-#include <string.h>
-#include <stdlib.h>
-
-/* The following object is the group context for a single percentile()
-** aggregate. Remember all input Y values until the very end.
-** Those values are accumulated in the Percentile.a[] array.
-*/
-typedef struct Percentile Percentile;
-struct Percentile {
- unsigned nAlloc; /* Number of slots allocated for a[] */
- unsigned nUsed; /* Number of slots actually used in a[] */
- char bSorted; /* True if a[] is already in sorted order */
- char bKeepSorted; /* True if advantageous to keep a[] sorted */
- char bPctValid; /* True if rPct is valid */
- double rPct; /* Fraction. 0.0 to 1.0 */
- double *a; /* Array of Y values */
-};
-
-/* Details of each function in the percentile family */
-typedef struct PercentileFunc PercentileFunc;
-struct PercentileFunc {
- const char *zName; /* Function name */
- char nArg; /* Number of arguments */
- char mxFrac; /* Maximum value of the "fraction" input */
- char bDiscrete; /* True for percentile_disc() */
-};
-static const PercentileFunc aPercentFunc[] = {
- { "median", 1, 1, 0 },
- { "percentile", 2, 100, 0 },
- { "percentile_cont", 2, 1, 0 },
- { "percentile_disc", 2, 1, 1 },
-};
-
-/*
-** Return TRUE if the input floating-point number is an infinity.
-*/
-static int percentIsInfinity(double r){
- sqlite3_uint64 u;
- assert( sizeof(u)==sizeof(r) );
- memcpy(&u, &r, sizeof(u));
- return ((u>>52)&0x7ff)==0x7ff;
-}
-
-/*
-** Return TRUE if two doubles differ by 0.001 or less.
-*/
-static int percentSameValue(double a, double b){
- a -= b;
- return a>=-0.001 && a<=0.001;
-}
-
-/*
-** Search p (which must have p->bSorted) looking for an entry with
-** value y. Return the index of that entry.
-**
-** If bExact is true, return -1 if the entry is not found.
-**
-** If bExact is false, return the index at which a new entry with
-** value y should be insert in order to keep the values in sorted
-** order. The smallest return value in this case will be 0, and
-** the largest return value will be p->nUsed.
-*/
-static int percentBinarySearch(Percentile *p, double y, int bExact){
- int iFirst = 0; /* First element of search range */
- int iLast = p->nUsed - 1; /* Last element of search range */
- while( iLast>=iFirst ){
- int iMid = (iFirst+iLast)/2;
- double x = p->a[iMid];
- if( x<y ){
- iFirst = iMid + 1;
- }else if( x>y ){
- iLast = iMid - 1;
- }else{
- return iMid;
- }
- }
- if( bExact ) return -1;
- return iFirst;
-}
-
-/*
-** Generate an error for a percentile function.
-**
-** The error format string must have exactly one occurrence of "%%s()"
-** (with two '%' characters). That substring will be replaced by the name
-** of the function.
-*/
-static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
- char *zMsg1;
- char *zMsg2;
- va_list ap;
-
- va_start(ap, zFormat);
- zMsg1 = sqlite3_vmprintf(zFormat, ap);
- va_end(ap);
- zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, pFunc->zName) : 0;
- sqlite3_result_error(pCtx, zMsg2, -1);
- sqlite3_free(zMsg1);
- sqlite3_free(zMsg2);
-}
-
-/*
-** The "step" function for percentile(Y,P) is called once for each
-** input row.
-*/
-static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
- Percentile *p;
- double rPct;
- int eType;
- double y;
- assert( argc==2 || argc==1 );
-
- if( argc==1 ){
- /* Requirement 13: median(Y) is the same as percentile(Y,50). */
- rPct = 0.5;
- }else{
- /* Requirement 3: P must be a number between 0 and 100 */
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
- eType = sqlite3_value_numeric_type(argv[1]);
- rPct = sqlite3_value_double(argv[1])/(double)pFunc->mxFrac;
- if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
- || rPct<0.0 || rPct>1.0
- ){
- percentError(pCtx, "the fraction argument to %%s()"
- " is not between 0.0 and %.1f",
- (double)pFunc->mxFrac);
- return;
- }
- }
-
- /* Allocate the session context. */
- p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
- if( p==0 ) return;
-
- /* Remember the P value. Throw an error if the P value is different
- ** from any prior row, per Requirement (2). */
- if( !p->bPctValid ){
- p->rPct = rPct;
- p->bPctValid = 1;
- }else if( !percentSameValue(p->rPct,rPct) ){
- percentError(pCtx, "the fraction argument to %%s()"
- " is not the same for all input rows");
- return;
- }
-
- /* Ignore rows for which Y is NULL */
- eType = sqlite3_value_type(argv[0]);
- if( eType==SQLITE_NULL ) return;
-
- /* If not NULL, then Y must be numeric. Otherwise throw an error.
- ** Requirement 4 */
- if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
- percentError(pCtx, "input to %%s() is not numeric");
- return;
- }
-
- /* Throw an error if the Y value is infinity or NaN */
- y = sqlite3_value_double(argv[0]);
- if( percentIsInfinity(y) ){
- percentError(pCtx, "Inf input to %%s()");
- return;
- }
-
- /* Allocate and store the Y */
- if( p->nUsed>=p->nAlloc ){
- unsigned n = p->nAlloc*2 + 250;
- double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
- if( a==0 ){
- sqlite3_free(p->a);
- memset(p, 0, sizeof(*p));
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- p->nAlloc = n;
- p->a = a;
- }
- if( p->nUsed==0 ){
- p->a[p->nUsed++] = y;
- p->bSorted = 1;
- }else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
- p->a[p->nUsed++] = y;
- }else if( p->bKeepSorted ){
- int i;
- i = percentBinarySearch(p, y, 0);
- if( i<(int)p->nUsed ){
- memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
- }
- p->a[i] = y;
- p->nUsed++;
- }else{
- p->a[p->nUsed++] = y;
- p->bSorted = 0;
- }
-}
-
-/*
-** Interchange two doubles.
-*/
-#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
-
-/*
-** Sort an array of doubles.
-**
-** Algorithm: quicksort
-**
-** This is implemented separately rather than using the qsort() routine
-** from the standard library because:
-**
-** (1) To avoid a dependency on qsort()
-** (2) To avoid the function call to the comparison routine for each
-** comparison.
-*/
-static void percentSort(double *a, unsigned int n){
- int iLt; /* Entries before a[iLt] are less than rPivot */
- int iGt; /* Entries at or after a[iGt] are greater than rPivot */
- int i; /* Loop counter */
- double rPivot; /* The pivot value */
-
- assert( n>=2 );
- if( a[0]>a[n-1] ){
- SWAP_DOUBLE(a[0],a[n-1])
- }
- if( n==2 ) return;
- iGt = n-1;
- i = n/2;
- if( a[0]>a[i] ){
- SWAP_DOUBLE(a[0],a[i])
- }else if( a[i]>a[iGt] ){
- SWAP_DOUBLE(a[i],a[iGt])
- }
- if( n==3 ) return;
- rPivot = a[i];
- iLt = i = 1;
- do{
- if( a[i]<rPivot ){
- if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
- iLt++;
- i++;
- }else if( a[i]>rPivot ){
- do{
- iGt--;
- }while( iGt>i && a[iGt]>rPivot );
- SWAP_DOUBLE(a[i],a[iGt])
- }else{
- i++;
- }
- }while( i<iGt );
- if( iLt>=2 ) percentSort(a, iLt);
- if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
-
-/* Uncomment for testing */
-#if 0
- for(i=0; i<n-1; i++){
- assert( a[i]<=a[i+1] );
- }
-#endif
-}
-
-
-/*
-** The "inverse" function for percentile(Y,P) is called to remove a
-** row that was previously inserted by "step".
-*/
-static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
- Percentile *p;
- int eType;
- double y;
- int i;
- assert( argc==2 || argc==1 );
-
- /* Allocate the session context. */
- p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
- assert( p!=0 );
-
- /* Ignore rows for which Y is NULL */
- eType = sqlite3_value_type(argv[0]);
- if( eType==SQLITE_NULL ) return;
-
- /* If not NULL, then Y must be numeric. Otherwise throw an error.
- ** Requirement 4 */
- if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
- return;
- }
-
- /* Ignore the Y value if it is infinity or NaN */
- y = sqlite3_value_double(argv[0]);
- if( percentIsInfinity(y) ){
- return;
- }
- if( p->bSorted==0 ){
- assert( p->nUsed>1 );
- percentSort(p->a, p->nUsed);
- p->bSorted = 1;
- }
- p->bKeepSorted = 1;
-
- /* Find and remove the row */
- i = percentBinarySearch(p, y, 1);
- if( i>=0 ){
- p->nUsed--;
- if( i<(int)p->nUsed ){
- memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
- }
- }
-}
-
-/*
-** Compute the final output of percentile(). Clean up all allocated
-** memory if and only if bIsFinal is true.
-*/
-static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
- Percentile *p;
- PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
- unsigned i1, i2;
- double v1, v2;
- double ix, vx;
- p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
- if( p==0 ) return;
- if( p->a==0 ) return;
- if( p->nUsed ){
- if( p->bSorted==0 ){
- assert( p->nUsed>1 );
- percentSort(p->a, p->nUsed);
- p->bSorted = 1;
- }
- ix = p->rPct*(p->nUsed-1);
- i1 = (unsigned)ix;
- if( pFunc->bDiscrete ){
- vx = p->a[i1];
- }else{
- i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
- v1 = p->a[i1];
- v2 = p->a[i2];
- vx = v1 + (v2-v1)*(ix-i1);
- }
- sqlite3_result_double(pCtx, vx);
- }
- if( bIsFinal ){
- sqlite3_free(p->a);
- memset(p, 0, sizeof(*p));
- }else{
- p->bKeepSorted = 1;
- }
-}
-static void percentFinal(sqlite3_context *pCtx){
- percentCompute(pCtx, 1);
-}
-static void percentValue(sqlite3_context *pCtx){
- percentCompute(pCtx, 0);
-}
-
-#if defined(_WIN32) && !defined(SQLITE3_H) && !defined(SQLITE_STATIC_PERCENTILE)
-
-#endif
-int sqlite3_percentile_init(
- sqlite3 *db,
- char **pzErrMsg,
- const sqlite3_api_routines *pApi
-){
- int rc = SQLITE_OK;
- unsigned int i;
-#ifdef SQLITE3EXT_H
- SQLITE_EXTENSION_INIT2(pApi);
-#else
- (void)pApi; /* Unused parameter */
-#endif
- (void)pzErrMsg; /* Unused parameter */
- for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
- rc = sqlite3_create_window_function(db,
- aPercentFunc[i].zName,
- aPercentFunc[i].nArg,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
- (void*)&aPercentFunc[i],
- percentStep, percentFinal, percentValue, percentInverse, 0);
- if( rc ) break;
- }
- return rc;
-}
-
-/************************* End ../ext/misc/percentile.c ********************/
-#undef sqlite3_base_init
-#define sqlite3_base_init sqlite3_base64_init
-/************************* Begin ../ext/misc/base64.c ******************/
+/************************* End ext/misc/decimal.c ********************/
+/************************* Begin ext/misc/base64.c ******************/
/*
** 2022-11-18
**
@@ -5300,7 +6905,9 @@ static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){
/* This function does the work for the SQLite base64(x) UDF. */
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
- int nb, nc, nv = sqlite3_value_bytes(av[0]);
+ sqlite3_int64 nb;
+ sqlite3_int64 nv = sqlite3_value_bytes(av[0]);
+ sqlite3_int64 nc;
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
@@ -5309,7 +6916,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
switch( sqlite3_value_type(av[0]) ){
case SQLITE_BLOB:
nb = nv;
- nc = 4*(nv+2/3); /* quads needed */
+ nc = 4*((nv+2)/3); /* quads needed */
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
@@ -5323,7 +6930,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
- cBuf = sqlite3_malloc(nc);
+ cBuf = sqlite3_malloc64(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase64(bBuf, nb, cBuf) - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -5345,7 +6952,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
- bBuf = sqlite3_malloc(nb);
+ bBuf = sqlite3_malloc64(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase64(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -5366,7 +6973,7 @@ static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
#endif
-int sqlite3_base_init
+int sqlite3_base64_init
#else
static int sqlite3_base64_init
#endif
@@ -5387,11 +6994,8 @@ static int sqlite3_base64_init
#define BASE64_INIT(db) sqlite3_base64_init(db, 0, 0)
#define BASE64_EXPOSE(db, pzErr) /* Not needed, ..._init() does this. */
-/************************* End ../ext/misc/base64.c ********************/
-#undef sqlite3_base_init
-#define sqlite3_base_init sqlite3_base85_init
-#define OMIT_BASE85_CHECKER
-/************************* Begin ../ext/misc/base85.c ******************/
+/************************* End ext/misc/base64.c ********************/
+/************************* Begin ext/misc/base85.c ******************/
/*
** 2022-11-16
**
@@ -5656,7 +7260,7 @@ static int allBase85( char *p, int len ){
#ifndef BASE85_STANDALONE
-# ifndef OMIT_BASE85_CHECKER
+#ifndef OMIT_BASE85_CHECKER
/* This function does the work for the SQLite is_base85(t) UDF. */
static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
assert(na==1);
@@ -5676,11 +7280,11 @@ static void is_base85(sqlite3_context *context, int na, sqlite3_value *av[]){
return;
}
}
-# endif
+#endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
- int nb, nc, nv = sqlite3_value_bytes(av[0]);
+ sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0]);
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
@@ -5703,7 +7307,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_text(context,"",-1,SQLITE_STATIC);
break;
}
- cBuf = sqlite3_malloc(nc);
+ cBuf = sqlite3_malloc64(nc);
if( !cBuf ) goto memFail;
nc = (int)(toBase85(bBuf, nb, cBuf, "\n") - cBuf);
sqlite3_result_text(context, cBuf, nc, sqlite3_free);
@@ -5725,7 +7329,7 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_result_zeroblob(context, 0);
break;
}
- bBuf = sqlite3_malloc(nb);
+ bBuf = sqlite3_malloc64(nb);
if( !bBuf ) goto memFail;
nb = (int)(fromBase85(cBuf, nc, bBuf) - bBuf);
sqlite3_result_blob(context, bBuf, nb, sqlite3_free);
@@ -5746,14 +7350,14 @@ static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
#ifdef _WIN32
#endif
-int sqlite3_base_init
+int sqlite3_base85_init
#else
static int sqlite3_base85_init
#endif
(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi);
(void)pzErr;
-# ifndef OMIT_BASE85_CHECKER
+#ifndef OMIT_BASE85_CHECKER
{
int rc = sqlite3_create_function
(db, "is_base85", 1,
@@ -5761,7 +7365,7 @@ static int sqlite3_base85_init
0, is_base85, 0, 0);
if( rc!=SQLITE_OK ) return rc;
}
-# endif
+#endif
return sqlite3_create_function
(db, "base85", 1,
SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS|SQLITE_DIRECTONLY|SQLITE_UTF8,
@@ -5826,9 +7430,9 @@ int main(int na, char *av[]){
int nc = strlen(cBuf);
size_t nbo = fromBase85( cBuf, nc, bBuf ) - bBuf;
if( 1 != fwrite(bBuf, nbo, 1, fb) ) rc = 1;
-# ifndef OMIT_BASE85_CHECKER
+#ifndef OMIT_BASE85_CHECKER
b85Clean &= allBase85( cBuf, nc );
-# endif
+#endif
}
break;
default:
@@ -5847,8 +7451,8 @@ int main(int na, char *av[]){
#endif
-/************************* End ../ext/misc/base85.c ********************/
-/************************* Begin ../ext/misc/ieee754.c ******************/
+/************************* End ext/misc/base85.c ********************/
+/************************* Begin ext/misc/ieee754.c ******************/
/*
** 2013-04-17
**
@@ -5985,6 +7589,9 @@ static void ieee754func(
if( a==0 ){
e = 0;
m = 0;
+ }else if( a==(sqlite3_int64)0x8000000000000000LL ){
+ e = -1996;
+ m = -1;
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
@@ -6027,9 +7634,9 @@ static void ieee754func(
}
if( m<0 ){
+ if( m<(-9223372036854775807LL) ) return;
isNeg = 1;
m = -m;
- if( m<0 ) return;
}else if( m==0 && e>-1000 && e<1000 ){
sqlite3_result_double(context, 0.0);
return;
@@ -6108,6 +7715,38 @@ static void ieee754func_to_blob(
}
/*
+** Functions to convert between 64-bit integers and floats.
+**
+** The bit patterns are copied. The numeric values are different.
+*/
+static void ieee754func_from_int(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){
+ double r;
+ sqlite3_int64 v = sqlite3_value_int64(argv[0]);
+ memcpy(&r, &v, sizeof(r));
+ sqlite3_result_double(context, r);
+ }
+}
+static void ieee754func_to_int(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAMETER(argc);
+ if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){
+ double r = sqlite3_value_double(argv[0]);
+ sqlite3_uint64 v;
+ memcpy(&v, &r, sizeof(v));
+ sqlite3_result_int64(context, v);
+ }
+}
+
+/*
** SQL Function: ieee754_inc(r,N)
**
** Move the floating point value r by N quantums and return the new
@@ -6159,6 +7798,8 @@ int sqlite3_ieee_init(
{ "ieee754_exponent", 1, 2, ieee754func },
{ "ieee754_to_blob", 1, 0, ieee754func_to_blob },
{ "ieee754_from_blob", 1, 0, ieee754func_from_blob },
+ { "ieee754_to_int", 1, 0, ieee754func_to_int },
+ { "ieee754_from_int", 1, 0, ieee754func_from_int },
{ "ieee754_inc", 2, 0, ieee754inc },
};
unsigned int i;
@@ -6174,8 +7815,8 @@ int sqlite3_ieee_init(
return rc;
}
-/************************* End ../ext/misc/ieee754.c ********************/
-/************************* Begin ../ext/misc/series.c ******************/
+/************************* End ext/misc/ieee754.c ********************/
+/************************* Begin ext/misc/series.c ******************/
/*
** 2015-08-18, 2023-04-28
**
@@ -6208,19 +7849,20 @@ int sqlite3_ieee_init(
** SELECT * FROM generate_series(0,100,5);
**
** The query above returns integers from 0 through 100 counting by steps
-** of 5.
+** of 5. In other words, 0, 5, 10, 15, ..., 90, 95, 100. There are a total
+** of 21 rows.
**
** SELECT * FROM generate_series(0,100);
**
-** Integers from 0 through 100 with a step size of 1.
+** Integers from 0 through 100 with a step size of 1. 101 rows.
**
** SELECT * FROM generate_series(20) LIMIT 10;
**
-** Integers 20 through 29.
+** Integers 20 through 29. 10 rows.
**
** SELECT * FROM generate_series(0,-100,-5);
**
-** Integers 0 -5 -10 ... -100.
+** Integers 0 -5 -10 ... -100. 21 rows.
**
** SELECT * FROM generate_series(0,-1);
**
@@ -6296,139 +7938,88 @@ SQLITE_EXTENSION_INIT1
#include <math.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/*
-** Return that member of a generate_series(...) sequence whose 0-based
-** index is ix. The 0th member is given by smBase. The sequence members
-** progress per ix increment by smStep.
-*/
-static sqlite3_int64 genSeqMember(
- sqlite3_int64 smBase,
- sqlite3_int64 smStep,
- sqlite3_uint64 ix
-){
- static const sqlite3_uint64 mxI64 =
- ((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
- if( ix>=mxI64 ){
- /* Get ix into signed i64 range. */
- ix -= mxI64;
- /* With 2's complement ALU, this next can be 1 step, but is split into
- * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
- smBase += (mxI64/2) * smStep;
- smBase += (mxI64 - mxI64/2) * smStep;
- }
- /* Under UBSAN (or on 1's complement machines), must do this last term
- * in steps to avoid the dreaded (and harmless) signed multiply overflow. */
- if( ix>=2 ){
- sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
- smBase += ix2*smStep;
- ix -= ix2;
- }
- return smBase + ((sqlite3_int64)ix)*smStep;
-}
+/* series_cursor is a subclass of sqlite3_vtab_cursor which will
+** serve as the underlying representation of a cursor that scans
+** over rows of the result.
+**
+** iOBase, iOTerm, and iOStep are the original values of the
+** start=, stop=, and step= constraints on the query. These are
+** the values reported by the start, stop, and step columns of the
+** virtual table.
+**
+** iBase, iTerm, iStep, and bDescp are the actual values used to generate
+** the sequence. These might be different from the iOxxxx values.
+** For example in
+**
+** SELECT value FROM generate_series(1,11,2)
+** WHERE value BETWEEN 4 AND 8;
+**
+** The iOBase is 1, but the iBase is 5. iOTerm is 11 but iTerm is 7.
+** Another example:
+**
+** SELECT value FROM generate_series(1,15,3) ORDER BY value DESC;
+**
+** The cursor initialization for the above query is:
+**
+** iOBase = 1 iBase = 13
+** iOTerm = 15 iTerm = 1
+** iOStep = 3 iStep = 3 bDesc = 1
+**
+** The actual step size is unsigned so that can have a value of
+** +9223372036854775808 which is needed for querys like this:
+**
+** SELECT value
+** FROM generate_series(9223372036854775807,
+** -9223372036854775808,
+** -9223372036854775808)
+** ORDER BY value ASC;
+**
+** The setup for the previous query will be:
+**
+** iOBase = 9223372036854775807 iBase = -1
+** iOTerm = -9223372036854775808 iTerm = 9223372036854775807
+** iOStep = -9223372036854775808 iStep = 9223372036854775808 bDesc = 0
+*/
/* typedef unsigned char u8; */
-
-typedef struct SequenceSpec {
- sqlite3_int64 iOBase; /* Original starting value ("start") */
- sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
- sqlite3_int64 iBase; /* Starting value to actually use */
- sqlite3_int64 iTerm; /* Terminal value to actually use */
- sqlite3_int64 iStep; /* Increment ("step") */
- sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
- sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
- sqlite3_int64 iValueNow; /* Current value during generation */
- u8 isNotEOF; /* Sequence generation not exhausted */
- u8 isReversing; /* Sequence is being reverse generated */
-} SequenceSpec;
+typedef struct series_cursor series_cursor;
+struct series_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ sqlite3_int64 iOBase; /* Original starting value ("start") */
+ sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
+ sqlite3_int64 iOStep; /* Original step value */
+ sqlite3_int64 iBase; /* Starting value to actually use */
+ sqlite3_int64 iTerm; /* Terminal value to actually use */
+ sqlite3_uint64 iStep; /* The step size */
+ sqlite3_int64 iValue; /* Current value */
+ u8 bDesc; /* iStep is really negative */
+ u8 bDone; /* True if stepped past last element */
+};
/*
-** Prepare a SequenceSpec for use in generating an integer series
-** given initialized iBase, iTerm and iStep values. Sequence is
-** initialized per given isReversing. Other members are computed.
+** Computed the difference between two 64-bit signed integers using a
+** convoluted computation designed to work around the silly restriction
+** against signed integer overflow in C.
*/
-static void setupSequence( SequenceSpec *pss ){
- int bSameSigns;
- pss->uSeqIndexMax = 0;
- pss->isNotEOF = 0;
- bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
- if( pss->iTerm < pss->iBase ){
- sqlite3_uint64 nuspan = 0;
- if( bSameSigns ){
- nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
- }else{
- /* Under UBSAN (or on 1's complement machines), must do this in steps.
- * In this clause, iBase>=0 and iTerm<0 . */
- nuspan = 1;
- nuspan += pss->iBase;
- nuspan += -(pss->iTerm+1);
- }
- if( pss->iStep<0 ){
- pss->isNotEOF = 1;
- if( nuspan==ULONG_MAX ){
- pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
- }else if( pss->iStep>LLONG_MIN ){
- pss->uSeqIndexMax = nuspan/-pss->iStep;
- }
- }
- }else if( pss->iTerm > pss->iBase ){
- sqlite3_uint64 puspan = 0;
- if( bSameSigns ){
- puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
- }else{
- /* Under UBSAN (or on 1's complement machines), must do this in steps.
- * In this clause, iTerm>=0 and iBase<0 . */
- puspan = 1;
- puspan += pss->iTerm;
- puspan += -(pss->iBase+1);
- }
- if( pss->iStep>0 ){
- pss->isNotEOF = 1;
- pss->uSeqIndexMax = puspan/pss->iStep;
- }
- }else if( pss->iTerm == pss->iBase ){
- pss->isNotEOF = 1;
- pss->uSeqIndexMax = 0;
- }
- pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
- pss->iValueNow = (pss->isReversing)
- ? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
- : pss->iBase;
-}
+static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
+ assert( a>=b );
+ return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
+}
/*
-** Progress sequence generator to yield next value, if any.
-** Leave its state to either yield next value or be at EOF.
-** Return whether there is a next value, or 0 at EOF.
+** Add or substract an unsigned 64-bit integer from a signed 64-bit integer
+** and return the new signed 64-bit integer.
*/
-static int progressSequence( SequenceSpec *pss ){
- if( !pss->isNotEOF ) return 0;
- if( pss->isReversing ){
- if( pss->uSeqIndexNow > 0 ){
- pss->uSeqIndexNow--;
- pss->iValueNow -= pss->iStep;
- }else{
- pss->isNotEOF = 0;
- }
- }else{
- if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
- pss->uSeqIndexNow++;
- pss->iValueNow += pss->iStep;
- }else{
- pss->isNotEOF = 0;
- }
- }
- return pss->isNotEOF;
+static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
+ sqlite3_uint64 x = *(sqlite3_uint64*)&a;
+ x += b;
+ return *(sqlite3_int64*)&x;
+}
+static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
+ sqlite3_uint64 x = *(sqlite3_uint64*)&a;
+ x -= b;
+ return *(sqlite3_int64*)&x;
}
-
-/* series_cursor is a subclass of sqlite3_vtab_cursor which will
-** serve as the underlying representation of a cursor that scans
-** over rows of the result
-*/
-typedef struct series_cursor series_cursor;
-struct series_cursor {
- sqlite3_vtab_cursor base; /* Base class - must be first */
- SequenceSpec ss; /* (this) Derived class data */
-};
/*
** The seriesConnect() method is invoked to create a new
@@ -6467,7 +8058,7 @@ static int seriesConnect(
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
@@ -6489,7 +8080,7 @@ static int seriesDisconnect(sqlite3_vtab *pVtab){
static int seriesOpen(sqlite3_vtab *pUnused, sqlite3_vtab_cursor **ppCursor){
series_cursor *pCur;
(void)pUnused;
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3_malloc64( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
@@ -6510,7 +8101,15 @@ static int seriesClose(sqlite3_vtab_cursor *cur){
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
- progressSequence( & pCur->ss );
+ if( pCur->iValue==pCur->iTerm ){
+ pCur->bDone = 1;
+ }else if( pCur->bDesc ){
+ pCur->iValue = sub64(pCur->iValue, pCur->iStep);
+ assert( pCur->iValue>=pCur->iTerm );
+ }else{
+ pCur->iValue = add64(pCur->iValue, pCur->iStep);
+ assert( pCur->iValue<=pCur->iTerm );
+ }
return SQLITE_OK;
}
@@ -6526,19 +8125,19 @@ static int seriesColumn(
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
- case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
- case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
- case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
- default: x = pCur->ss.iValueNow; break;
+ case SERIES_COLUMN_START: x = pCur->iOBase; break;
+ case SERIES_COLUMN_STOP: x = pCur->iOTerm; break;
+ case SERIES_COLUMN_STEP: x = pCur->iOStep; break;
+ default: x = pCur->iValue; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
#ifndef LARGEST_UINT64
-#define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
-#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
-#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#define LARGEST_INT64 ((sqlite3_int64)0x7fffffffffffffffLL)
+#define LARGEST_UINT64 ((sqlite3_uint64)0xffffffffffffffffULL)
+#define SMALLEST_INT64 ((sqlite3_int64)0x8000000000000000LL)
#endif
/*
@@ -6546,7 +8145,7 @@ static int seriesColumn(
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
- *pRowid = pCur->ss.iValueNow;
+ *pRowid = pCur->iValue;
return SQLITE_OK;
}
@@ -6556,7 +8155,7 @@ static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
- return !pCur->ss.isNotEOF;
+ return pCur->bDone;
}
/* True to cause run-time checking of the start=, stop=, and/or step=
@@ -6568,6 +8167,59 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
#endif
/*
+** Return the number of steps between pCur->iBase and pCur->iTerm if
+** the step width is pCur->iStep.
+*/
+static sqlite3_uint64 seriesSteps(series_cursor *pCur){
+ if( pCur->bDesc ){
+ assert( pCur->iBase >= pCur->iTerm );
+ return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
+ }else{
+ assert( pCur->iBase <= pCur->iTerm );
+ return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
+ }
+}
+
+#if defined(SQLITE_ENABLE_MATH_FUNCTIONS) || defined(_WIN32)
+/*
+** Case 1 (the most common case):
+** The standard math library is available so use ceil() and floor() from there.
+*/
+static double seriesCeil(double r){ return ceil(r); }
+static double seriesFloor(double r){ return floor(r); }
+#elif defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
+/*
+** Case 2 (2nd most common): Use GCC/Clang builtins
+*/
+static double seriesCeil(double r){ return __builtin_ceil(r); }
+static double seriesFloor(double r){ return __builtin_floor(r); }
+#else
+/*
+** Case 3 (rarely happens): Use home-grown ceil() and floor() routines.
+*/
+static double seriesCeil(double r){
+ sqlite3_int64 x;
+ if( r!=r ) return r;
+ if( r<=(-4503599627370496.0) ) return r;
+ if( r>=(+4503599627370496.0) ) return r;
+ x = (sqlite3_int64)r;
+ if( r==(double)x ) return r;
+ if( r>(double)x ) x++;
+ return (double)x;
+}
+static double seriesFloor(double r){
+ sqlite3_int64 x;
+ if( r!=r ) return r;
+ if( r<=(-4503599627370496.0) ) return r;
+ if( r>=(+4503599627370496.0) ) return r;
+ x = (sqlite3_int64)r;
+ if( r==(double)x ) return r;
+ if( r<(double)x ) x--;
+ return (double)x;
+}
+#endif
+
+/*
** This method is called to "rewind" the series_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to seriesColumn() or seriesRowid() or
@@ -6600,33 +8252,42 @@ static int seriesFilter(
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
- int i = 0;
- int returnNoRows = 0;
- sqlite3_int64 iMin = SMALLEST_INT64;
- sqlite3_int64 iMax = LARGEST_INT64;
- sqlite3_int64 iLimit = 0;
- sqlite3_int64 iOffset = 0;
+ int iArg = 0; /* Arguments used so far */
+ int i; /* Loop counter */
+ sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
+ sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
+ sqlite3_int64 iLimit = 0; /* if >0, the value of the LIMIT */
+ sqlite3_int64 iOffset = 0; /* if >0, the value of the OFFSET */
(void)idxStrUnused;
+
+ /* If any constraints have a NULL value, then return no rows.
+ ** See ticket https://sqlite.org/src/info/fac496b61722daf2
+ */
+ for(i=0; i<argc; i++){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
+ goto series_no_rows;
+ }
+ }
+
+ /* Capture the three HIDDEN parameters to the virtual table and insert
+ ** default values for any parameters that are omitted.
+ */
if( idxNum & 0x01 ){
- pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
+ pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
}else{
- pCur->ss.iBase = 0;
+ pCur->iOBase = 0;
}
if( idxNum & 0x02 ){
- pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
+ pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
}else{
- pCur->ss.iTerm = 0xffffffff;
+ pCur->iOTerm = 0xffffffff;
}
if( idxNum & 0x04 ){
- pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
- if( pCur->ss.iStep==0 ){
- pCur->ss.iStep = 1;
- }else if( pCur->ss.iStep<0 ){
- if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
- }
+ pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
+ if( pCur->iOStep==0 ) pCur->iOStep = 1;
}else{
- pCur->ss.iStep = 1;
+ pCur->iOStep = 1;
}
/* If there are constraints on the value column but there are
@@ -6636,72 +8297,94 @@ static int seriesFilter(
** further below.
*/
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
- pCur->ss.iBase = SMALLEST_INT64;
+ pCur->iOBase = SMALLEST_INT64;
}
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
- pCur->ss.iTerm = LARGEST_INT64;
+ pCur->iOTerm = LARGEST_INT64;
+ }
+ pCur->iBase = pCur->iOBase;
+ pCur->iTerm = pCur->iOTerm;
+ if( pCur->iOStep>0 ){
+ pCur->iStep = pCur->iOStep;
+ }else if( pCur->iOStep>SMALLEST_INT64 ){
+ pCur->iStep = -pCur->iOStep;
+ }else{
+ pCur->iStep = LARGEST_INT64;
+ pCur->iStep++;
+ }
+ pCur->bDesc = pCur->iOStep<0;
+ if( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
+ goto series_no_rows;
+ }
+ if( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
+ goto series_no_rows;
}
- pCur->ss.iOBase = pCur->ss.iBase;
- pCur->ss.iOTerm = pCur->ss.iTerm;
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
** The range must first be constrained by the limits on value.
*/
if( idxNum & 0x20 ){
- iLimit = sqlite3_value_int64(argv[i++]);
+ iLimit = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x40 ){
- iOffset = sqlite3_value_int64(argv[i++]);
+ iOffset = sqlite3_value_int64(argv[iArg++]);
}
}
+ /* Narrow the range of iMin and iMax (the minimum and maximum outputs)
+ ** based on equality and inequality constraints on the "value" column.
+ */
if( idxNum & 0x3380 ){
- /* Extract the maximum range of output values determined by
- ** constraints on the "value" column.
- */
- if( idxNum & 0x0080 ){
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
- double r = sqlite3_value_double(argv[i++]);
- if( r==ceil(r) ){
+ if( idxNum & 0x0080 ){ /* value=X */
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
+ double r = sqlite3_value_double(argv[iArg++]);
+ if( r==seriesCeil(r)
+ && r>=(double)SMALLEST_INT64
+ && r<=(double)LARGEST_INT64
+ ){
iMin = iMax = (sqlite3_int64)r;
}else{
- returnNoRows = 1;
+ goto series_no_rows;
}
}else{
- iMin = iMax = sqlite3_value_int64(argv[i++]);
+ iMin = iMax = sqlite3_value_int64(argv[iArg++]);
}
}else{
- if( idxNum & 0x0300 ){
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
- double r = sqlite3_value_double(argv[i++]);
- if( idxNum & 0x0200 && r==ceil(r) ){
- iMin = (sqlite3_int64)ceil(r+1.0);
+ if( idxNum & 0x0300 ){ /* value>X or value>=X */
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
+ double r = sqlite3_value_double(argv[iArg++]);
+ if( r<(double)SMALLEST_INT64 ){
+ iMin = SMALLEST_INT64;
+ }else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
+ iMin = (sqlite3_int64)seriesCeil(r+1.0);
}else{
- iMin = (sqlite3_int64)ceil(r);
+ iMin = (sqlite3_int64)seriesCeil(r);
}
}else{
- iMin = sqlite3_value_int64(argv[i++]);
- if( idxNum & 0x0200 ){
+ iMin = sqlite3_value_int64(argv[iArg++]);
+ if( (idxNum & 0x0200)!=0 ){
if( iMin==LARGEST_INT64 ){
- returnNoRows = 1;
+ goto series_no_rows;
}else{
iMin++;
}
}
}
}
- if( idxNum & 0x3000 ){
- if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
- double r = sqlite3_value_double(argv[i++]);
- if( (idxNum & 0x2000)!=0 && r==floor(r) ){
+ if( idxNum & 0x3000 ){ /* value<X or value<=X */
+ if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
+ double r = sqlite3_value_double(argv[iArg++]);
+ if( r>(double)LARGEST_INT64 ){
+ iMax = LARGEST_INT64;
+ }else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
iMax = (sqlite3_int64)(r-1.0);
}else{
- iMax = (sqlite3_int64)floor(r);
+ iMax = (sqlite3_int64)seriesFloor(r);
}
}else{
- iMax = sqlite3_value_int64(argv[i++]);
+ iMax = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x2000 ){
if( iMax==SMALLEST_INT64 ){
- returnNoRows = 1;
+ goto series_no_rows;
}else{
iMax--;
}
@@ -6709,72 +8392,99 @@ static int seriesFilter(
}
}
if( iMin>iMax ){
- returnNoRows = 1;
+ goto series_no_rows;
}
}
/* Try to reduce the range of values to be generated based on
** constraints on the "value" column.
*/
- if( pCur->ss.iStep>0 ){
- sqlite3_int64 szStep = pCur->ss.iStep;
- if( pCur->ss.iBase<iMin ){
- sqlite3_uint64 d = iMin - pCur->ss.iBase;
- pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
+ if( pCur->bDesc==0 ){
+ if( pCur->iBase<iMin ){
+ sqlite3_uint64 span = span64(iMin,pCur->iBase);
+ pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
+ if( pCur->iBase<iMin ){
+ if( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
+ goto series_no_rows;
+ }
+ pCur->iBase = add64(pCur->iBase, pCur->iStep);
+ }
}
- if( pCur->ss.iTerm>iMax ){
- pCur->ss.iTerm = iMax;
+ if( pCur->iTerm>iMax ){
+ pCur->iTerm = iMax;
}
}else{
- sqlite3_int64 szStep = -pCur->ss.iStep;
- assert( szStep>0 );
- if( pCur->ss.iBase>iMax ){
- sqlite3_uint64 d = pCur->ss.iBase - iMax;
- pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
+ if( pCur->iBase>iMax ){
+ sqlite3_uint64 span = span64(pCur->iBase,iMax);
+ pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
+ if( pCur->iBase>iMax ){
+ if( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
+ goto series_no_rows;
+ }
+ pCur->iBase = sub64(pCur->iBase, pCur->iStep);
+ }
}
- if( pCur->ss.iTerm<iMin ){
- pCur->ss.iTerm = iMin;
+ if( pCur->iTerm<iMin ){
+ pCur->iTerm = iMin;
}
}
}
+ /* Adjust iTerm so that it is exactly the last value of the series.
+ */
+ if( pCur->bDesc==0 ){
+ if( pCur->iBase>pCur->iTerm ){
+ goto series_no_rows;
+ }
+ pCur->iTerm = sub64(pCur->iTerm,
+ span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
+ }else{
+ if( pCur->iBase<pCur->iTerm ){
+ goto series_no_rows;
+ }
+ pCur->iTerm = add64(pCur->iTerm,
+ span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
+ }
+
+ /* Transform the series generator to output values in the requested
+ ** order.
+ */
+ if( ((idxNum & 0x0008)!=0 && pCur->bDesc==0)
+ || ((idxNum & 0x0010)!=0 && pCur->bDesc!=0)
+ ){
+ sqlite3_int64 tmp = pCur->iBase;
+ pCur->iBase = pCur->iTerm;
+ pCur->iTerm = tmp;
+ pCur->bDesc = !pCur->bDesc;
+ }
+
/* Apply LIMIT and OFFSET constraints, if any */
+ assert( pCur->iStep!=0 );
if( idxNum & 0x20 ){
if( iOffset>0 ){
- pCur->ss.iBase += pCur->ss.iStep*iOffset;
- }
- if( iLimit>=0 ){
- sqlite3_int64 iTerm;
- iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
- if( pCur->ss.iStep<0 ){
- if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
+ if( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
+ goto series_no_rows;
+ }else if( pCur->bDesc ){
+ pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
}else{
- if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
+ pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
}
}
- }
-
-
- for(i=0; i<argc; i++){
- if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
- /* If any of the constraints have a NULL value, then return no rows.
- ** See ticket https://sqlite.org/src/info/fac496b61722daf2 */
- returnNoRows = 1;
- break;
+ if( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){
+ pCur->iTerm = add64(pCur->iBase, (iLimit - 1)*pCur->iStep);
}
}
- if( returnNoRows ){
- pCur->ss.iBase = 1;
- pCur->ss.iTerm = 0;
- pCur->ss.iStep = 1;
- }
- if( idxNum & 0x08 ){
- pCur->ss.isReversing = pCur->ss.iStep > 0;
- }else{
- pCur->ss.isReversing = pCur->ss.iStep < 0;
- }
- setupSequence( &pCur->ss );
+ pCur->iValue = pCur->iBase;
+ pCur->bDone = 0;
return SQLITE_OK;
+
+series_no_rows:
+ pCur->iBase = 0;
+ pCur->iTerm = 0;
+ pCur->iStep = 1;
+ pCur->bDesc = 0;
+ pCur->bDone = 1;
+ return SQLITE_OK;
}
/*
@@ -7045,8 +8755,8 @@ int sqlite3_series_init(
return rc;
}
-/************************* End ../ext/misc/series.c ********************/
-/************************* Begin ../ext/misc/regexp.c ******************/
+/************************* End ext/misc/series.c ********************/
+/************************* Begin ext/misc/regexp.c ******************/
/*
** 2012-11-13
**
@@ -7081,7 +8791,7 @@ int sqlite3_series_init(
** ^X X occurring at the beginning of the string
** X$ X occurring at the end of the string
** . Match any single character
-** \c Character c where c is one of \{}()[]|*+?.
+** \c Character c where c is one of \{}()[]|*+?-.
** \c C-language escapes for c in afnrtv. ex: \t or \n
** \uXXXX Where XXXX is exactly 4 hex digits, unicode value XXXX
** \xXX Where XX is exactly 2 hex digits, unicode value XX
@@ -7104,6 +8814,8 @@ int sqlite3_series_init(
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
+**
+** To help prevent DoS attacks, the maximum size of the NFA is restricted.
*/
#include <string.h>
#include <stdlib.h>
@@ -7145,32 +8857,6 @@ SQLITE_EXTENSION_INIT1
#define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */
#define RE_OP_ATSTART 18 /* Currently at the start of the string */
-#if defined(SQLITE_DEBUG)
-/* Opcode names used for symbolic debugging */
-static const char *ReOpName[] = {
- "EOF",
- "MATCH",
- "ANY",
- "ANYSTAR",
- "FORK",
- "GOTO",
- "ACCEPT",
- "CC_INC",
- "CC_EXC",
- "CC_VALUE",
- "CC_RANGE",
- "WORD",
- "NOTWORD",
- "DIGIT",
- "NOTDIGIT",
- "SPACE",
- "NOTSPACE",
- "BOUNDARY",
- "ATSTART",
-};
-#endif /* SQLITE_DEBUG */
-
-
/* Each opcode is a "state" in the NFA */
typedef unsigned short ReStateNumber;
@@ -7207,6 +8893,7 @@ struct ReCompiled {
int nInit; /* Number of bytes in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
+ unsigned mxAlloc; /* Complexity limit */
};
/* Add a state to the given state set if it is not already there */
@@ -7421,14 +9108,15 @@ re_match_end:
/* Resize the opcode and argument arrays for an RE under construction.
*/
-static int re_resize(ReCompiled *p, int N){
+static int re_resize(ReCompiled *p, unsigned int N){
char *aOp;
int *aArg;
+ if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
- if( aOp==0 ) return 1;
+ if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
p->aOp = aOp;
aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
- if( aArg==0 ) return 1;
+ if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
p->aArg = aArg;
p->nAlloc = N;
return 0;
@@ -7459,7 +9147,7 @@ static int re_append(ReCompiled *p, int op, int arg){
/* Make a copy of N opcodes starting at iStart onto the end of the RE
** under construction.
*/
-static void re_copy(ReCompiled *p, int iStart, int N){
+static void re_copy(ReCompiled *p, int iStart, unsigned int N){
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
@@ -7488,7 +9176,7 @@ static int re_hex(int c, int *pV){
** return its interpretation.
*/
static unsigned re_esc_char(ReCompiled *p){
- static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]";
+ static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-";
static const char zTrans[] = "\a\f\n\r\t\v";
int i, v = 0;
char c;
@@ -7612,18 +9300,26 @@ static const char *re_subcompile_string(ReCompiled *p){
break;
}
case '{': {
- int m = 0, n = 0;
- int sz, j;
+ unsigned int m = 0, n = 0;
+ unsigned int sz, j;
if( iPrev<0 ) return "'{m,n}' without operand";
- while( (c=rePeek(p))>='0' && c<='9' ){ m = m*10 + c - '0'; p->sIn.i++; }
+ while( (c=rePeek(p))>='0' && c<='9' ){
+ m = m*10 + c - '0';
+ if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
+ p->sIn.i++;
+ }
n = m;
if( c==',' ){
p->sIn.i++;
n = 0;
- while( (c=rePeek(p))>='0' && c<='9' ){ n = n*10 + c-'0'; p->sIn.i++; }
+ while( (c=rePeek(p))>='0' && c<='9' ){
+ n = n*10 + c-'0';
+ if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
+ p->sIn.i++;
+ }
}
if( c!='}' ) return "unmatched '{'";
- if( n>0 && n<m ) return "n less than m in '{m,n}'";
+ if( n<m ) return "n less than m in '{m,n}'";
p->sIn.i++;
sz = p->nState - iPrev;
if( m==0 ){
@@ -7639,7 +9335,7 @@ static const char *re_subcompile_string(ReCompiled *p){
re_copy(p, iPrev, sz);
}
if( n==0 && m>0 ){
- re_append(p, RE_OP_FORK, -sz);
+ re_append(p, RE_OP_FORK, -(int)sz);
}
break;
}
@@ -7705,8 +9401,7 @@ static const char *re_subcompile_string(ReCompiled *p){
** regular expression. Applications should invoke this routine once
** for every call to re_compile() to avoid memory leaks.
*/
-static void re_free(void *p){
- ReCompiled *pRe = (ReCompiled*)p;
+static void re_free(ReCompiled *pRe){
if( pRe ){
sqlite3_free(pRe->aOp);
sqlite3_free(pRe->aArg);
@@ -7715,26 +9410,42 @@ static void re_free(void *p){
}
/*
+** Version of re_free() that accepts a pointer of type (void*). Required
+** to satisfy sanitizers when the re_free() function is called via a
+** function pointer.
+*/
+static void re_free_voidptr(void *p){
+ re_free((ReCompiled*)p);
+}
+
+/*
** Compile a textual regular expression in zIn[] into a compiled regular
** expression suitable for us by re_match() and return a pointer to the
** compiled regular expression in *ppRe. Return NULL on success or an
** error message if something goes wrong.
*/
-static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
+static const char *re_compile(
+ ReCompiled **ppRe, /* OUT: write compiled NFA here */
+ const char *zIn, /* Input regular expression */
+ int mxRe, /* Complexity limit */
+ int noCase /* True for caseless comparisons */
+){
ReCompiled *pRe;
const char *zErr;
int i, j;
*ppRe = 0;
- pRe = sqlite3_malloc( sizeof(*pRe) );
+ pRe = sqlite3_malloc64( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
memset(pRe, 0, sizeof(*pRe));
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
+ pRe->mxAlloc = mxRe;
if( re_resize(pRe, 30) ){
+ zErr = pRe->zErr;
re_free(pRe);
- return "out of memory";
+ return zErr;
}
if( zIn[0]=='^' ){
zIn++;
@@ -7788,6 +9499,21 @@ static const char *re_compile(ReCompiled **ppRe, const char *zIn, int noCase){
}
/*
+** The value of LIMIT_MAX_PATTERN_LENGTH.
+*/
+static int re_maxlen(sqlite3_context *context){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1);
+}
+
+/*
+** Maximum NFA size given a maximum pattern length.
+*/
+static int re_maxnfa(int mxlen){
+ return 75+mxlen/2;
+}
+
+/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
@@ -7810,9 +9536,17 @@ static void re_sql_func(
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
+ int mxLen = re_maxlen(context);
+ int nPattern;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
+ nPattern = sqlite3_value_bytes(argv[0]);
+ if( nPattern>mxLen ){
+ zErr = "REGEXP pattern too big";
+ }else{
+ zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen),
+ sqlite3_user_data(context)!=0);
+ }
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
@@ -7829,7 +9563,7 @@ static void re_sql_func(
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
if( setAux ){
- sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ sqlite3_set_auxdata(context, 0, pRe, re_free_voidptr);
}
}
@@ -7853,11 +9587,33 @@ static void re_bytecode_func(
int i;
int n;
char *z;
- (void)argc;
+ static const char *ReOpName[] = {
+ "EOF",
+ "MATCH",
+ "ANY",
+ "ANYSTAR",
+ "FORK",
+ "GOTO",
+ "ACCEPT",
+ "CC_INC",
+ "CC_EXC",
+ "CC_VALUE",
+ "CC_RANGE",
+ "WORD",
+ "NOTWORD",
+ "DIGIT",
+ "NOTDIGIT",
+ "SPACE",
+ "NOTSPACE",
+ "BOUNDARY",
+ "ATSTART",
+ };
+ (void)argc;
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
- zErr = re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
+ zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)),
+ sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
@@ -7930,9 +9686,9 @@ int sqlite3_regexp_init(
return rc;
}
-/************************* End ../ext/misc/regexp.c ********************/
+/************************* End ext/misc/regexp.c ********************/
#ifndef SQLITE_SHELL_FIDDLE
-/************************* Begin ../ext/misc/fileio.c ******************/
+/************************* Begin ext/misc/fileio.c ******************/
/*
** 2014-06-13
**
@@ -8002,16 +9758,12 @@ int sqlite3_regexp_init(
** data: For a regular file, a blob containing the file data. For a
** symlink, a text value containing the text of the link. For a
** directory, NULL.
+** level: Directory hierarchy level. Topmost is 1.
**
** If a non-NULL value is specified for the optional $dir parameter and
** $path is a relative path, then $path is interpreted relative to $dir.
** And the paths returned in the "name" column of the table are also
** relative to directory $dir.
-**
-** Notes on building this extension for Windows:
-** Unless linked statically with the SQLite library, a preprocessor
-** symbol, FILEIO_WIN32_DLL, must be #define'd to create a stand-alone
-** DLL form of this extension for WIN32. See its use below for details.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
@@ -8028,15 +9780,16 @@ SQLITE_EXTENSION_INIT1
# include <utime.h>
# include <sys/time.h>
# define STRUCT_STAT struct stat
+# include <limits.h>
+# include <stdlib.h>
#else
-# include "windows.h"
-# include <io.h>
+/* # include "windirent.h" */
# include <direct.h>
-/* # include "test_windirent.h" */
-# define dirent DIRENT
# define STRUCT_STAT struct _stat
# define chmod(path,mode) fileio_chmod(path,mode)
# define mkdir(path,mode) fileio_mkdir(path)
+ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
+ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
#endif
#include <time.h>
#include <errno.h>
@@ -8052,26 +9805,25 @@ SQLITE_EXTENSION_INIT1
/*
** Structure of the fsdir() table-valued function
*/
- /* 0 1 2 3 4 5 */
-#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
+ /* 0 1 2 3 4 5 6 */
+#define FSDIR_SCHEMA "(name,mode,mtime,data,level,path HIDDEN,dir HIDDEN)"
+
#define FSDIR_COLUMN_NAME 0 /* Name of the file */
#define FSDIR_COLUMN_MODE 1 /* Access mode */
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
#define FSDIR_COLUMN_DATA 3 /* File content */
-#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
-#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
+#define FSDIR_COLUMN_LEVEL 4 /* Level. Topmost is 1 */
+#define FSDIR_COLUMN_PATH 5 /* Path to top of search */
+#define FSDIR_COLUMN_DIR 6 /* Path is relative to this directory */
/*
** UTF8 chmod() function for Windows
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_chmod(const char *zPath, int pmode){
- sqlite3_int64 sz = strlen(zPath);
- wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
+ wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
- sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
- b1[sz] = 0;
rc = _wchmod(b1, pmode);
sqlite3_free(b1);
return rc;
@@ -8083,12 +9835,9 @@ static int fileio_chmod(const char *zPath, int pmode){
*/
#if defined(_WIN32) || defined(WIN32)
static int fileio_mkdir(const char *zPath){
- sqlite3_int64 sz = strlen(zPath);
- wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
+ wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return -1;
- sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
- b1[sz] = 0;
rc = _wmkdir(b1);
sqlite3_free(b1);
return rc;
@@ -8201,50 +9950,7 @@ static sqlite3_uint64 fileTimeToUnixTime(
return (fileIntervals.QuadPart - epochIntervals.QuadPart) / 10000000;
}
-
-
-#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
-# /* To allow a standalone DLL, use this next replacement function: */
-# undef sqlite3_win32_utf8_to_unicode
-# define sqlite3_win32_utf8_to_unicode utf8_to_utf16
-#
-LPWSTR utf8_to_utf16(const char *z){
- int nAllot = MultiByteToWideChar(CP_UTF8, 0, z, -1, NULL, 0);
- LPWSTR rv = sqlite3_malloc(nAllot * sizeof(WCHAR));
- if( rv!=0 && 0 < MultiByteToWideChar(CP_UTF8, 0, z, -1, rv, nAllot) )
- return rv;
- sqlite3_free(rv);
- return 0;
-}
-#endif
-
-/*
-** This function attempts to normalize the time values found in the stat()
-** buffer to UTC. This is necessary on Win32, where the runtime library
-** appears to return these values as local times.
-*/
-static void statTimesToUtc(
- const char *zPath,
- STRUCT_STAT *pStatBuf
-){
- HANDLE hFindFile;
- WIN32_FIND_DATAW fd;
- LPWSTR zUnicodeName;
- extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*);
- zUnicodeName = sqlite3_win32_utf8_to_unicode(zPath);
- if( zUnicodeName ){
- memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
- hFindFile = FindFirstFileW(zUnicodeName, &fd);
- if( hFindFile!=NULL ){
- pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
- pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
- pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
- FindClose(hFindFile);
- }
- sqlite3_free(zUnicodeName);
- }
-}
-#endif
+#endif /* _WIN32 */
/*
** This function is used in place of stat(). On Windows, special handling
@@ -8256,14 +9962,23 @@ static int fileStat(
STRUCT_STAT *pStatBuf
){
#if defined(_WIN32)
- sqlite3_int64 sz = strlen(zPath);
- wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
int rc;
+ wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath);
if( b1==0 ) return 1;
- sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
- b1[sz] = 0;
rc = _wstat(b1, pStatBuf);
- if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
+ if( rc==0 ){
+ HANDLE hFindFile;
+ WIN32_FIND_DATAW fd;
+ memset(&fd, 0, sizeof(WIN32_FIND_DATAW));
+ hFindFile = FindFirstFileW(b1, &fd);
+ if( hFindFile!=NULL ){
+ pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime);
+ pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime);
+ pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime);
+ FindClose(hFindFile);
+ }
+ }
+ sqlite3_free(b1);
return rc;
#else
return stat(zPath, pStatBuf);
@@ -8394,7 +10109,6 @@ static int writeFile(
if( mtime>=0 ){
#if defined(_WIN32)
-#if !SQLITE_OS_WINRT
/* Windows */
FILETIME lastAccess;
FILETIME lastWrite;
@@ -8425,7 +10139,6 @@ static int writeFile(
}else{
return 1;
}
-#endif
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
/* Recent unix */
struct timespec times[2];
@@ -8556,6 +10269,7 @@ struct fsdir_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
int nLvl; /* Number of entries in aLvl[] array */
+ int mxLvl; /* Maximum level */
int iLvl; /* Index of current entry */
FsdirLevel *aLvl; /* Hierarchy of directories being traversed */
@@ -8590,7 +10304,7 @@ static int fsdirConnect(
(void)pzErr;
rc = sqlite3_declare_vtab(db, "CREATE TABLE x" FSDIR_SCHEMA);
if( rc==SQLITE_OK ){
- pNew = (fsdir_tab*)sqlite3_malloc( sizeof(*pNew) );
+ pNew = (fsdir_tab*)sqlite3_malloc64( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
@@ -8613,7 +10327,7 @@ static int fsdirDisconnect(sqlite3_vtab *pVtab){
static int fsdirOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
fsdir_cursor *pCur;
(void)p;
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3_malloc64( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->iLvl = -1;
@@ -8674,7 +10388,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
mode_t m = pCur->sStat.st_mode;
pCur->iRowid++;
- if( S_ISDIR(m) ){
+ if( S_ISDIR(m) && pCur->iLvl+3<pCur->mxLvl ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
@@ -8694,7 +10408,7 @@ static int fsdirNext(sqlite3_vtab_cursor *cur){
pCur->zPath = 0;
pLvl->pDir = opendir(pLvl->zDir);
if( pLvl->pDir==0 ){
- fsdirSetErrmsg(pCur, "cannot read directory: %s", pCur->zPath);
+ fsdirSetErrmsg(pCur, "cannot read directory: %s", pLvl->zDir);
return SQLITE_ERROR;
}
}
@@ -8782,7 +10496,11 @@ static int fsdirColumn(
}else{
readFileContents(ctx, pCur->zPath);
}
+ break;
}
+ case FSDIR_COLUMN_LEVEL:
+ sqlite3_result_int(ctx, pCur->iLvl+2);
+ break;
case FSDIR_COLUMN_PATH:
default: {
/* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters.
@@ -8816,8 +10534,11 @@ static int fsdirEof(sqlite3_vtab_cursor *cur){
/*
** xFilter callback.
**
-** idxNum==1 PATH parameter only
-** idxNum==2 Both PATH and DIR supplied
+** idxNum bit Meaning
+** 0x01 PATH=N
+** 0x02 DIR=N
+** 0x04 LEVEL<N
+** 0x08 LEVEL<=N
*/
static int fsdirFilter(
sqlite3_vtab_cursor *cur,
@@ -8826,6 +10547,7 @@ static int fsdirFilter(
){
const char *zDir = 0;
fsdir_cursor *pCur = (fsdir_cursor*)cur;
+ int i;
(void)idxStr;
fsdirResetCursor(pCur);
@@ -8834,14 +10556,24 @@ static int fsdirFilter(
return SQLITE_ERROR;
}
- assert( argc==idxNum && (argc==1 || argc==2) );
+ assert( (idxNum & 0x01)!=0 && argc>0 );
zDir = (const char*)sqlite3_value_text(argv[0]);
if( zDir==0 ){
fsdirSetErrmsg(pCur, "table function fsdir requires a non-NULL argument");
return SQLITE_ERROR;
}
- if( argc==2 ){
- pCur->zBase = (const char*)sqlite3_value_text(argv[1]);
+ i = 1;
+ if( (idxNum & 0x02)!=0 ){
+ assert( argc>i );
+ pCur->zBase = (const char*)sqlite3_value_text(argv[i++]);
+ }
+ if( (idxNum & 0x0c)!=0 ){
+ assert( argc>i );
+ pCur->mxLvl = sqlite3_value_int(argv[i++]);
+ if( idxNum & 0x08 ) pCur->mxLvl++;
+ if( pCur->mxLvl<=0 ) pCur->mxLvl = 1000000000;
+ }else{
+ pCur->mxLvl = 1000000000;
}
if( pCur->zBase ){
pCur->nBase = (int)strlen(pCur->zBase)+1;
@@ -8870,10 +10602,11 @@ static int fsdirFilter(
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
-** The query plan is represented by values of idxNum:
+** The query plan is represented by bits in idxNum:
**
-** (1) The path value is supplied by argv[0]
-** (2) Path is in argv[0] and dir is in argv[1]
+** 0x01 The path value is supplied by argv[0]
+** 0x02 dir is in argv[1]
+** 0x04 maxdepth is in argv[1] or [2]
*/
static int fsdirBestIndex(
sqlite3_vtab *tab,
@@ -8882,6 +10615,9 @@ static int fsdirBestIndex(
int i; /* Loop over constraints */
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
+ int idxLevel = -1; /* Index in pIdxInfo->aConstraint of LEVEL< or <= */
+ int idxLevelEQ = 0; /* 0x08 for LEVEL<= or LEVEL=. 0x04 for LEVEL< */
+ int omitLevel = 0; /* omit the LEVEL constraint */
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
@@ -8889,25 +10625,48 @@ static int fsdirBestIndex(
(void)tab;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
- switch( pConstraint->iColumn ){
- case FSDIR_COLUMN_PATH: {
- if( pConstraint->usable ){
- idxPath = i;
- seenPath = 0;
- }else if( idxPath<0 ){
- seenPath = 1;
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ switch( pConstraint->iColumn ){
+ case FSDIR_COLUMN_PATH: {
+ if( pConstraint->usable ){
+ idxPath = i;
+ seenPath = 0;
+ }else if( idxPath<0 ){
+ seenPath = 1;
+ }
+ break;
}
- break;
- }
- case FSDIR_COLUMN_DIR: {
- if( pConstraint->usable ){
- idxDir = i;
- seenDir = 0;
- }else if( idxDir<0 ){
- seenDir = 1;
+ case FSDIR_COLUMN_DIR: {
+ if( pConstraint->usable ){
+ idxDir = i;
+ seenDir = 0;
+ }else if( idxDir<0 ){
+ seenDir = 1;
+ }
+ break;
}
- break;
+ case FSDIR_COLUMN_LEVEL: {
+ if( pConstraint->usable && idxLevel<0 ){
+ idxLevel = i;
+ idxLevelEQ = 0x08;
+ omitLevel = 0;
+ }
+ break;
+ }
+ }
+ }else
+ if( pConstraint->iColumn==FSDIR_COLUMN_LEVEL
+ && pConstraint->usable
+ && idxLevel<0
+ ){
+ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
+ idxLevel = i;
+ idxLevelEQ = 0x08;
+ omitLevel = 1;
+ }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
+ idxLevel = i;
+ idxLevelEQ = 0x04;
+ omitLevel = 1;
}
}
}
@@ -8924,14 +10683,20 @@ static int fsdirBestIndex(
}else{
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
+ pIdxInfo->idxNum = 0x01;
+ pIdxInfo->estimatedCost = 1.0e9;
+ i = 2;
if( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
- pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
- pIdxInfo->idxNum = 2;
- pIdxInfo->estimatedCost = 10.0;
- }else{
- pIdxInfo->idxNum = 1;
- pIdxInfo->estimatedCost = 100.0;
+ pIdxInfo->aConstraintUsage[idxDir].argvIndex = i++;
+ pIdxInfo->idxNum |= 0x02;
+ pIdxInfo->estimatedCost /= 1.0e4;
+ }
+ if( idxLevel>=0 ){
+ pIdxInfo->aConstraintUsage[idxLevel].omit = omitLevel;
+ pIdxInfo->aConstraintUsage[idxLevel].argvIndex = i++;
+ pIdxInfo->idxNum |= idxLevelEQ;
+ pIdxInfo->estimatedCost /= 1.0e4;
}
}
@@ -8977,6 +10742,154 @@ static int fsdirRegister(sqlite3 *db){
# define fsdirRegister(x) SQLITE_OK
#endif
+/*
+** This version of realpath() works on any system. The string
+** returned is held in memory allocated using sqlite3_malloc64().
+** The caller is responsible for calling sqlite3_free().
+*/
+static char *portable_realpath(const char *zPath){
+#if !defined(_WIN32) /* BEGIN unix */
+
+ char *zOut = 0; /* Result */
+ char *z; /* Temporary buffer */
+#if defined(PATH_MAX)
+ char zBuf[PATH_MAX+1]; /* Space for the temporary buffer */
+#endif
+
+ if( zPath==0 ) return 0;
+#if defined(PATH_MAX)
+ z = realpath(zPath, zBuf);
+ if( z ){
+ zOut = sqlite3_mprintf("%s", zBuf);
+ }
+#endif /* defined(PATH_MAX) */
+ if( zOut==0 ){
+ /* Try POSIX.1-2008 malloc behavior */
+ z = realpath(zPath, NULL);
+ if( z ){
+ zOut = sqlite3_mprintf("%s", z);
+ free(z);
+ }
+ }
+ return zOut;
+
+#else /* End UNIX, Begin WINDOWS */
+
+ wchar_t *zPath16; /* UTF16 translation of zPath */
+ char *zOut = 0; /* Result */
+ wchar_t *z = 0; /* Temporary buffer */
+
+ if( zPath==0 ) return 0;
+
+ zPath16 = sqlite3_win32_utf8_to_unicode(zPath);
+ if( zPath16==0 ) return 0;
+ z = _wfullpath(NULL, zPath16, 0);
+ sqlite3_free(zPath16);
+ if( z ){
+ zOut = sqlite3_win32_unicode_to_utf8(z);
+ free(z);
+ }
+ return zOut;
+
+#endif /* End WINDOWS, Begin common code */
+}
+
+/*
+** SQL function: realpath(X)
+**
+** Try to convert file or pathname X into its real, absolute pathname.
+** Return NULL if unable.
+**
+** The file or directory X is not required to exist. The answer is formed
+** by calling system realpath() on the prefix of X that does exist and
+** appending the tail of X that does not (yet) exist.
+*/
+static void realpathFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *zPath; /* Original input path */
+ char *zCopy; /* An editable copy of zPath */
+ char *zOut; /* The result */
+ char cSep = 0; /* Separator turned into \000 */
+ size_t len; /* Prefix length before cSep */
+#ifdef _WIN32
+ const int isWin = 1;
+#else
+ const int isWin = 0;
+#endif
+
+ (void)argc;
+ zPath = (const char*)sqlite3_value_text(argv[0]);
+ if( zPath==0 ) return;
+ if( zPath[0]==0 ) zPath = ".";
+ zCopy = sqlite3_mprintf("%s",zPath);
+ len = strlen(zCopy);
+ while( len>1 && (zCopy[len-1]=='/' || (isWin && zCopy[len-1]=='\\')) ){
+ len--;
+ }
+ zCopy[len] = 0;
+ while( 1 /*exit-by-break*/ ){
+ zOut = portable_realpath(zCopy);
+ zCopy[len] = cSep;
+ if( zOut ){
+ if( cSep ){
+ zOut = sqlite3_mprintf("%z%s",zOut,&zCopy[len]);
+ }
+ break;
+ }else{
+ size_t i = len-1;
+ while( i>0 ){
+ if( zCopy[i]=='/' || (isWin && zCopy[i]=='\\') ) break;
+ i--;
+ }
+ if( i<=0 ){
+ if( zCopy[0]=='/' ){
+ zOut = zCopy;
+ zCopy = 0;
+ }else if( (zOut = portable_realpath("."))!=0 ){
+ zOut = sqlite3_mprintf("%z/%s", zOut, zCopy);
+ }
+ break;
+ }
+ cSep = zCopy[i];
+ zCopy[i] = 0;
+ len = i;
+ }
+ }
+ sqlite3_free(zCopy);
+ if( zOut ){
+ /* Simplify any "/./" or "/../" that might have snuck into the
+ ** pathname due to appending of zCopy. We only have to consider
+ ** unix "/" separators, because the _wfilepath() system call on
+ ** Windows will have already done this simplification for us. */
+ size_t i, j, n;
+ n = strlen(zOut);
+ for(i=j=0; i<n; i++){
+ if( zOut[i]=='/' ){
+ if( zOut[i+1]=='/' ) continue;
+ if( zOut[i+1]=='.' && i+2<n && zOut[i+2]=='/' ){
+ i += 1;
+ continue;
+ }
+ if( zOut[i+1]=='.' && i+3<n && zOut[i+2]=='.' && zOut[i+3]=='/' ){
+ while( j>0 && zOut[j-1]!='/' ){ j--; }
+ if( j>0 ){ j--; }
+ i += 2;
+ continue;
+ }
+ }
+ zOut[j++] = zOut[i];
+ }
+ zOut[j] = 0;
+
+ /* Return the result */
+ sqlite3_result_text(context, zOut, -1, sqlite3_free);
+ }
+}
+
+
#ifdef _WIN32
#endif
@@ -9003,19 +10916,16 @@ int sqlite3_fileio_init(
if( rc==SQLITE_OK ){
rc = fsdirRegister(db);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "realpath", 1,
+ SQLITE_UTF8, 0,
+ realpathFunc, 0, 0);
+ }
return rc;
}
-#if defined(FILEIO_WIN32_DLL) && (defined(_WIN32) || defined(WIN32))
-/* To allow a standalone DLL, make test_windirent.c use the same
- * redefined SQLite API calls as the above extension code does.
- * Just pull in this .c to accomplish this. As a beneficial side
- * effect, this extension becomes a single translation unit. */
-# include "test_windirent.c"
-#endif
-
-/************************* End ../ext/misc/fileio.c ********************/
-/************************* Begin ../ext/misc/completion.c ******************/
+/************************* End ext/misc/fileio.c ********************/
+/************************* Begin ext/misc/completion.c ******************/
/*
** 2017-07-10
**
@@ -9150,7 +11060,7 @@ static int completionConnect(
" phase INT HIDDEN" /* Used for debugging only */
")");
if( rc==SQLITE_OK ){
- pNew = sqlite3_malloc( sizeof(*pNew) );
+ pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
@@ -9172,7 +11082,7 @@ static int completionDisconnect(sqlite3_vtab *pVtab){
*/
static int completionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
completion_cursor *pCur;
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3_malloc64( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->db = ((completion_vtab*)p)->db;
@@ -9217,6 +11127,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
completion_cursor *pCur = (completion_cursor*)cur;
int eNextPhase = 0; /* Next phase to try if current phase reaches end */
int iCol = -1; /* If >=0, step pCur->pStmt and use the i-th column */
+ int rc;
pCur->iRowid++;
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
@@ -9242,22 +11153,27 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_TABLES: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
+ sqlite3_str* pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
- zSql = sqlite3_mprintf(
- "%z%s"
+ sqlite3_str_appendf(pStr,
+ "%s"
"SELECT name FROM \"%w\".sqlite_schema",
- zSql, zSep, zDb
+ zSep, zDb
);
- if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
- sqlite3_finalize(pS2);
- sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
+ rc = sqlite3_finalize(pS2);
+ zSql = sqlite3_str_finish(pStr);
+ if( zSql==0 ) return SQLITE_NOMEM;
+ if( rc==SQLITE_OK ){
+ sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
+ }
sqlite3_free(zSql);
+ if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_COLUMNS;
@@ -9266,24 +11182,29 @@ static int completionNext(sqlite3_vtab_cursor *cur){
case COMPLETION_COLUMNS: {
if( pCur->pStmt==0 ){
sqlite3_stmt *pS2;
+ sqlite3_str *pStr = sqlite3_str_new(pCur->db);
char *zSql = 0;
const char *zSep = "";
sqlite3_prepare_v2(pCur->db, "PRAGMA database_list", -1, &pS2, 0);
while( sqlite3_step(pS2)==SQLITE_ROW ){
const char *zDb = (const char*)sqlite3_column_text(pS2, 1);
- zSql = sqlite3_mprintf(
- "%z%s"
+ sqlite3_str_appendf(pStr,
+ "%s"
"SELECT pti.name FROM \"%w\".sqlite_schema AS sm"
" JOIN pragma_table_xinfo(sm.name,%Q) AS pti"
" WHERE sm.type='table'",
- zSql, zSep, zDb, zDb
+ zSep, zDb, zDb
);
- if( zSql==0 ) return SQLITE_NOMEM;
zSep = " UNION ";
}
- sqlite3_finalize(pS2);
- sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
+ rc = sqlite3_finalize(pS2);
+ zSql = sqlite3_str_finish(pStr);
+ if( zSql==0 ) return SQLITE_NOMEM;
+ if( rc==SQLITE_OK ){
+ sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0);
+ }
sqlite3_free(zSql);
+ if( rc ) return rc;
}
iCol = 0;
eNextPhase = COMPLETION_EOF;
@@ -9300,9 +11221,10 @@ static int completionNext(sqlite3_vtab_cursor *cur){
pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
- sqlite3_finalize(pCur->pStmt);
+ rc = sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0;
pCur->ePhase = eNextPhase;
+ if( rc ) return rc;
continue;
}
}
@@ -9388,6 +11310,7 @@ static int completionFilter(
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
+ pCur->nPrefix = (int)strlen(pCur->zPrefix);
}
iArg = 1;
}
@@ -9396,6 +11319,7 @@ static int completionFilter(
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
+ pCur->nLine = (int)strlen(pCur->zLine);
}
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
@@ -9407,6 +11331,7 @@ static int completionFilter(
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
+ pCur->nPrefix = (int)strlen(pCur->zPrefix);
}
}
pCur->iRowid = 0;
@@ -9524,8 +11449,8 @@ int sqlite3_completion_init(
return rc;
}
-/************************* End ../ext/misc/completion.c ********************/
-/************************* Begin ../ext/misc/appendvfs.c ******************/
+/************************* End ext/misc/completion.c ********************/
+/************************* Begin ext/misc/appendvfs.c ******************/
/*
** 2017-10-20
**
@@ -10199,10 +12124,10 @@ int sqlite3_appendvfs_init(
return rc;
}
-/************************* End ../ext/misc/appendvfs.c ********************/
+/************************* End ext/misc/appendvfs.c ********************/
#endif
#ifdef SQLITE_HAVE_ZLIB
-/************************* Begin ../ext/misc/zipfile.c ******************/
+/************************* Begin ext/misc/zipfile.c ******************/
/*
** 2017-12-26
**
@@ -10320,7 +12245,13 @@ static const char ZIPFILE_SCHEMA[] =
") WITHOUT ROWID;";
#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
-#define ZIPFILE_BUFFER_SIZE (64*1024)
+#define ZIPFILE_MX_NAME (250) /* Windows limitation on filename size */
+
+/*
+** The buffer should be large enough to contain 3 65536 byte strings - the
+** filename, the extra field and the file comment.
+*/
+#define ZIPFILE_BUFFER_SIZE (200*1024)
/*
@@ -10592,7 +12523,7 @@ static int zipfileConnect(
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if( rc==SQLITE_OK ){
- pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
+ pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile);
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte+nFile);
pNew->db = db;
@@ -10655,7 +12586,7 @@ static int zipfileDisconnect(sqlite3_vtab *pVtab){
static int zipfileOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCsr){
ZipfileTab *pTab = (ZipfileTab*)p;
ZipfileCsr *pCsr;
- pCsr = sqlite3_malloc(sizeof(*pCsr));
+ pCsr = sqlite3_malloc64(sizeof(*pCsr));
*ppCsr = (sqlite3_vtab_cursor*)pCsr;
if( pCsr==0 ){
return SQLITE_NOMEM;
@@ -10738,14 +12669,15 @@ static void zipfileCursorErr(ZipfileCsr *pCsr, const char *zFmt, ...){
static int zipfileReadData(
FILE *pFile, /* Read from this file */
u8 *aRead, /* Read into this buffer */
- int nRead, /* Number of bytes to read */
+ i64 nRead, /* Number of bytes to read */
i64 iOff, /* Offset to read from */
char **pzErrmsg /* OUT: Error message (from sqlite3_malloc) */
){
size_t n;
fseek(pFile, (long)iOff, SEEK_SET);
- n = fread(aRead, 1, nRead, pFile);
- if( (int)n!=nRead ){
+ n = fread(aRead, 1, (long)nRead, pFile);
+ if( n!=(size_t)nRead ){
+ sqlite3_free(*pzErrmsg);
*pzErrmsg = sqlite3_mprintf("error in fread()");
return SQLITE_ERROR;
}
@@ -10762,7 +12694,7 @@ static int zipfileAppendData(
fseek(pTab->pWriteFd, (long)pTab->szCurrent, SEEK_SET);
n = fwrite(aWrite, 1, nWrite, pTab->pWriteFd);
if( (int)n!=nWrite ){
- pTab->base.zErrMsg = sqlite3_mprintf("error in fwrite()");
+ zipfileTableErr(pTab,"error in fwrite()");
return SQLITE_ERROR;
}
pTab->szCurrent += nWrite;
@@ -10877,6 +12809,7 @@ static int zipfileReadLFH(
pLFH->szUncompressed = zipfileRead32(aRead);
pLFH->nFile = zipfileRead16(aRead);
pLFH->nExtra = zipfileRead16(aRead);
+ if( pLFH->nFile>ZIPFILE_MX_NAME ) rc = SQLITE_ERROR;
}
return rc;
}
@@ -10902,7 +12835,12 @@ static int zipfileScanExtra(u8 *aExtra, int nExtra, u32 *pmTime){
u8 *p = aExtra;
u8 *pEnd = &aExtra[nExtra];
- while( p<pEnd ){
+ /* Stop when there are less than 9 bytes left to scan in the buffer. This
+ ** is because the timestamp field requires exactly 9 bytes - 4 bytes of
+ ** header fields and 5 bytes of data. If there are less than 9 bytes
+ ** remaining, either it is some other field or else the extra data
+ ** is corrupt. Either way, do not process it. */
+ while( p+(2*sizeof(u16) + 1 + sizeof(u32))<=pEnd ){
u16 id = zipfileRead16(p);
u16 nByte = zipfileRead16(p);
@@ -11004,6 +12942,16 @@ static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
}
/*
+** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a
+** generic corruption message and return SQLITE_CORRUPT;
+*/
+static int zipfileCorrupt(char **pzErr){
+ sqlite3_free(*pzErr);
+ *pzErr = sqlite3_mprintf("zip archive is corrupt");
+ return SQLITE_CORRUPT;
+}
+
+/*
** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
** size) containing an entire zip archive image. Or, if aBlob is NULL,
** then pFile is a file-handle open on a zip file. In either case, this
@@ -11017,7 +12965,7 @@ static void zipfileMtimeToDos(ZipfileCDS *pCds, u32 mUnixTime){
static int zipfileGetEntry(
ZipfileTab *pTab, /* Store any error message here */
const u8 *aBlob, /* Pointer to in-memory file image */
- int nBlob, /* Size of aBlob[] in bytes */
+ i64 nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
@@ -11025,12 +12973,15 @@ static int zipfileGetEntry(
u8 *aRead;
char **pzErr = &pTab->base.zErrMsg;
int rc = SQLITE_OK;
- (void)nBlob;
if( aBlob==0 ){
aRead = pTab->aBuffer;
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
}else{
+ if( (iOff+ZIPFILE_CDS_FIXED_SZ)>nBlob ){
+ /* Not enough data for the CDS structure. Corruption. */
+ return zipfileCorrupt(pzErr);
+ }
aRead = (u8*)&aBlob[iOff];
}
@@ -11054,13 +13005,16 @@ static int zipfileGetEntry(
memset(pNew, 0, sizeof(ZipfileEntry));
rc = zipfileReadCDS(aRead, &pNew->cds);
if( rc!=SQLITE_OK ){
- *pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
+ zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff);
}else if( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else{
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
+ if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){
+ rc = zipfileCorrupt(pzErr);
+ }
}
}
@@ -11083,18 +13037,26 @@ static int zipfileGetEntry(
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
+ if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
+ rc = zipfileCorrupt(pzErr);
+ }
}
+ memset(&lfh, 0, sizeof(lfh));
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
- pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
+ pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
- pNew->aData = &pNew->aExtra[nExtra];
- memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
+ if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
+ rc = zipfileCorrupt(pzErr);
+ }else{
+ pNew->aData = &pNew->aExtra[nExtra];
+ memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
+ }
}
}else{
- *pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
+ zipfileTableErr(pTab, "failed to read LFH at offset %d",
(int)pNew->cds.iOffset
);
}
@@ -11118,7 +13080,7 @@ static int zipfileNext(sqlite3_vtab_cursor *cur){
int rc = SQLITE_OK;
if( pCsr->pFile ){
- i64 iEof = pCsr->eocd.iOffset + pCsr->eocd.nSize;
+ i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize;
zipfileEntryFree(pCsr->pCurrent);
pCsr->pCurrent = 0;
if( pCsr->iNextOff>=iEof ){
@@ -11163,7 +13125,7 @@ static void zipfileInflate(
int nIn, /* Size of buffer aIn[] in bytes */
int nOut /* Expected output size */
){
- u8 *aRes = sqlite3_malloc(nOut);
+ u8 *aRes = sqlite3_malloc64(nOut);
if( aRes==0 ){
sqlite3_result_error_nomem(pCtx);
}else{
@@ -11184,7 +13146,7 @@ static void zipfileInflate(
if( err!=Z_STREAM_END ){
zipfileCtxErrorMsg(pCtx, "inflate() failed (%d)", err);
}else{
- sqlite3_result_blob(pCtx, aRes, nOut, zipfileFree);
+ sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree);
aRes = 0;
}
}
@@ -11356,12 +13318,12 @@ static int zipfileEof(sqlite3_vtab_cursor *cur){
static int zipfileReadEOCD(
ZipfileTab *pTab, /* Return errors here */
const u8 *aBlob, /* Pointer to in-memory file image */
- int nBlob, /* Size of aBlob[] in bytes */
+ i64 nBlob, /* Size of aBlob[] in bytes */
FILE *pFile, /* Read from this file if aBlob==0 */
ZipfileEOCD *pEOCD /* Object to populate */
){
u8 *aRead = pTab->aBuffer; /* Temporary buffer */
- int nRead; /* Bytes to read from file */
+ i64 nRead; /* Bytes to read from file */
int rc = SQLITE_OK;
memset(pEOCD, 0, sizeof(ZipfileEOCD));
@@ -11382,7 +13344,7 @@ static int zipfileReadEOCD(
}
if( rc==SQLITE_OK ){
- int i;
+ i64 i;
/* Scan backwards looking for the signature bytes */
for(i=nRead-20; i>=0; i--){
@@ -11393,9 +13355,7 @@ static int zipfileReadEOCD(
}
}
if( i<0 ){
- pTab->base.zErrMsg = sqlite3_mprintf(
- "cannot find end of central directory record"
- );
+ zipfileTableErr(pTab, "cannot find end of central directory record");
return SQLITE_ERROR;
}
@@ -11440,7 +13400,7 @@ static void zipfileAddEntry(
}
}
-static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, int nBlob){
+static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){
ZipfileEOCD eocd;
int rc;
int i;
@@ -11488,7 +13448,7 @@ static int zipfileFilter(
}else if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){
static const u8 aEmptyBlob = 0;
const u8 *aBlob = (const u8*)sqlite3_value_blob(argv[0]);
- int nBlob = sqlite3_value_bytes(argv[0]);
+ i64 nBlob = sqlite3_value_bytes(argv[0]);
assert( pTab->pFirstEntry==0 );
if( aBlob==0 ){
aBlob = &aEmptyBlob;
@@ -11562,7 +13522,7 @@ static int zipfileBestIndex(
static ZipfileEntry *zipfileNewEntry(const char *zPath){
ZipfileEntry *pNew;
- pNew = sqlite3_malloc(sizeof(ZipfileEntry));
+ pNew = sqlite3_malloc64(sizeof(ZipfileEntry));
if( pNew ){
memset(pNew, 0, sizeof(ZipfileEntry));
pNew->cds.zFile = sqlite3_mprintf("%s", zPath);
@@ -11686,7 +13646,7 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
assert( pTab->pWriteFd==0 );
if( pTab->zFile==0 || pTab->zFile[0]==0 ){
- pTab->base.zErrMsg = sqlite3_mprintf("zipfile: missing filename");
+ zipfileTableErr(pTab, "zipfile: missing filename");
return SQLITE_ERROR;
}
@@ -11696,9 +13656,9 @@ static int zipfileBegin(sqlite3_vtab *pVtab){
** in main-memory until the transaction is committed. */
pTab->pWriteFd = sqlite3_fopen(pTab->zFile, "ab+");
if( pTab->pWriteFd==0 ){
- pTab->base.zErrMsg = sqlite3_mprintf(
- "zipfile: failed to open file %s for writing", pTab->zFile
- );
+ zipfileTableErr(pTab,
+ "zipfile: failed to open file %s for writing", pTab->zFile
+ );
rc = SQLITE_ERROR;
}else{
fseek(pTab->pWriteFd, 0, SEEK_END);
@@ -11878,6 +13838,11 @@ static int zipfileUpdate(
zPath = (const char*)sqlite3_value_text(apVal[2]);
if( zPath==0 ) zPath = "";
nPath = (int)strlen(zPath);
+ if( nPath>ZIPFILE_MX_NAME ){
+ zipfileTableErr(pTab, "filename too long; max: %d bytes",
+ ZIPFILE_MX_NAME);
+ rc = SQLITE_CONSTRAINT;
+ }
mTime = zipfileGetTime(apVal[4]);
}
@@ -12158,7 +14123,7 @@ struct ZipfileCtx {
ZipfileBuffer cds;
};
-static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
+static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){
if( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
@@ -12207,7 +14172,7 @@ static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
char *zName = 0; /* Path (name) of new entry */
int nName = 0; /* Size of zName in bytes */
char *zFree = 0; /* Free this before returning */
- int nByte;
+ i64 nByte;
memset(&e, 0, sizeof(e));
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
@@ -12239,6 +14204,13 @@ static void zipfileStep(sqlite3_context *pCtx, int nVal, sqlite3_value **apVal){
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
+ if( nName>ZIPFILE_MX_NAME ){
+ zErr = sqlite3_mprintf(
+ "filename argument to zipfile() too big; max: %d bytes",
+ ZIPFILE_MX_NAME);
+ rc = SQLITE_ERROR;
+ goto zipfile_step_out;
+ }
/* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
** deflate compression) or NULL (choose automatically). */
@@ -12450,8 +14422,8 @@ int sqlite3_zipfile_init(
return zipfileRegister(db);
}
-/************************* End ../ext/misc/zipfile.c ********************/
-/************************* Begin ../ext/misc/sqlar.c ******************/
+/************************* End ext/misc/zipfile.c ********************/
+/************************* Begin ext/misc/sqlar.c ******************/
/*
** 2017-12-17
**
@@ -12500,7 +14472,7 @@ static void sqlarCompressFunc(
uLongf nOut = compressBound(nData);
Bytef *pOut;
- pOut = (Bytef*)sqlite3_malloc(nOut);
+ pOut = (Bytef*)sqlite3_malloc64(nOut);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -12538,14 +14510,14 @@ static void sqlarUncompressFunc(
sqlite3_int64 sz;
assert( argc==2 );
- sz = sqlite3_value_int(argv[1]);
+ sz = sqlite3_value_int64(argv[1]);
if( sz<=0 || sz==(nData = sqlite3_value_bytes(argv[0])) ){
sqlite3_result_value(context, argv[0]);
}else{
uLongf szf = sz;
const Bytef *pData= sqlite3_value_blob(argv[0]);
- Bytef *pOut = sqlite3_malloc(sz);
+ Bytef *pOut = sqlite3_malloc64(sz);
if( pOut==0 ){
sqlite3_result_error_nomem(context);
}else if( Z_OK!=uncompress(pOut, &szf, pData, nData) ){
@@ -12579,9 +14551,10 @@ int sqlite3_sqlar_init(
return rc;
}
-/************************* End ../ext/misc/sqlar.c ********************/
+/************************* End ext/misc/sqlar.c ********************/
#endif
-/************************* Begin ../ext/expert/sqlite3expert.h ******************/
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
+/************************* Begin ext/expert/sqlite3expert.h ******************/
/*
** 2017 April 07
**
@@ -12751,8 +14724,8 @@ void sqlite3_expert_destroy(sqlite3expert*);
#endif /* !defined(SQLITEEXPERT_H) */
-/************************* End ../ext/expert/sqlite3expert.h ********************/
-/************************* Begin ../ext/expert/sqlite3expert.c ******************/
+/************************* End ext/expert/sqlite3expert.h ********************/
+/************************* Begin ext/expert/sqlite3expert.c ******************/
/*
** 2017 April 09
**
@@ -12927,11 +14900,11 @@ struct sqlite3expert {
** Allocate and return nByte bytes of zeroed memory using sqlite3_malloc().
** If the allocation fails, set *pRc to SQLITE_NOMEM and return NULL.
*/
-static void *idxMalloc(int *pRc, int nByte){
+static void *idxMalloc(int *pRc, i64 nByte){
void *pRet;
assert( *pRc==SQLITE_OK );
assert( nByte>0 );
- pRet = sqlite3_malloc(nByte);
+ pRet = sqlite3_malloc64(nByte);
if( pRet ){
memset(pRet, 0, nByte);
}else{
@@ -12998,7 +14971,7 @@ static int idxHashAdd(
return 1;
}
}
- pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + nKey+1 + nVal+1);
+ pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + (i64)nKey+1 + (i64)nVal+1);
if( pEntry ){
pEntry->zKey = (char*)&pEntry[1];
memcpy(pEntry->zKey, zKey, nKey);
@@ -13133,15 +15106,15 @@ struct ExpertCsr {
};
static char *expertDequote(const char *zIn){
- int n = STRLEN(zIn);
- char *zRet = sqlite3_malloc(n);
+ i64 n = STRLEN(zIn);
+ char *zRet = sqlite3_malloc64(n);
assert( zIn[0]=='\'' );
assert( zIn[n-1]=='\'' );
if( zRet ){
- int iOut = 0;
- int iIn = 0;
+ i64 iOut = 0;
+ i64 iIn = 0;
for(iIn=1; iIn<(n-1); iIn++){
if( zIn[iIn]=='\'' ){
assert( zIn[iIn+1]=='\'' );
@@ -13454,7 +15427,7 @@ static int idxGetTableInfo(
sqlite3_stmt *p1 = 0;
int nCol = 0;
int nTab;
- int nByte;
+ i64 nByte;
IdxTable *pNew = 0;
int rc, rc2;
char *pCsr = 0;
@@ -13546,14 +15519,14 @@ static char *idxAppendText(int *pRc, char *zIn, const char *zFmt, ...){
va_list ap;
char *zAppend = 0;
char *zRet = 0;
- int nIn = zIn ? STRLEN(zIn) : 0;
- int nAppend = 0;
+ i64 nIn = zIn ? STRLEN(zIn) : 0;
+ i64 nAppend = 0;
va_start(ap, zFmt);
if( *pRc==SQLITE_OK ){
zAppend = sqlite3_vmprintf(zFmt, ap);
if( zAppend ){
nAppend = STRLEN(zAppend);
- zRet = (char*)sqlite3_malloc(nIn + nAppend + 1);
+ zRet = (char*)sqlite3_malloc64(nIn + nAppend + 1);
}
if( zAppend && zRet ){
if( nIn ) memcpy(zRet, zIn, nIn);
@@ -14317,8 +16290,8 @@ struct IdxRemCtx {
int eType; /* SQLITE_NULL, INTEGER, REAL, TEXT, BLOB */
i64 iVal; /* SQLITE_INTEGER value */
double rVal; /* SQLITE_FLOAT value */
- int nByte; /* Bytes of space allocated at z */
- int n; /* Size of buffer z */
+ i64 nByte; /* Bytes of space allocated at z */
+ i64 n; /* Size of buffer z */
char *z; /* SQLITE_TEXT/BLOB value */
} aSlot[1];
};
@@ -14354,11 +16327,13 @@ static void idxRemFunc(
break;
case SQLITE_BLOB:
- sqlite3_result_blob(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
+ assert( pSlot->n <= 0x7fffffff );
+ sqlite3_result_blob(pCtx, pSlot->z, (int)pSlot->n, SQLITE_TRANSIENT);
break;
case SQLITE_TEXT:
- sqlite3_result_text(pCtx, pSlot->z, pSlot->n, SQLITE_TRANSIENT);
+ assert( pSlot->n <= 0x7fffffff );
+ sqlite3_result_text(pCtx, pSlot->z, (int)pSlot->n, SQLITE_TRANSIENT);
break;
}
@@ -14378,10 +16353,10 @@ static void idxRemFunc(
case SQLITE_BLOB:
case SQLITE_TEXT: {
- int nByte = sqlite3_value_bytes(argv[1]);
+ i64 nByte = sqlite3_value_bytes(argv[1]);
const void *pData = 0;
if( nByte>pSlot->nByte ){
- char *zNew = (char*)sqlite3_realloc(pSlot->z, nByte*2);
+ char *zNew = (char*)sqlite3_realloc64(pSlot->z, nByte*2);
if( zNew==0 ){
sqlite3_result_error_nomem(pCtx);
return;
@@ -14436,7 +16411,7 @@ static int idxPopulateOneStat1(
int nCol = 0;
int i;
sqlite3_stmt *pQuery = 0;
- int *aStat = 0;
+ i64 *aStat = 0;
int rc = SQLITE_OK;
assert( p->iSample>0 );
@@ -14482,7 +16457,7 @@ static int idxPopulateOneStat1(
sqlite3_free(zQuery);
if( rc==SQLITE_OK ){
- aStat = (int*)idxMalloc(&rc, sizeof(int)*(nCol+1));
+ aStat = (i64*)idxMalloc(&rc, sizeof(i64)*(nCol+1));
}
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pQuery) ){
IdxHashEntry *pEntry;
@@ -14499,11 +16474,11 @@ static int idxPopulateOneStat1(
}
if( rc==SQLITE_OK ){
- int s0 = aStat[0];
- zStat = sqlite3_mprintf("%d", s0);
+ i64 s0 = aStat[0];
+ zStat = sqlite3_mprintf("%lld", s0);
if( zStat==0 ) rc = SQLITE_NOMEM;
for(i=1; rc==SQLITE_OK && i<=nCol; i++){
- zStat = idxAppendText(&rc, zStat, " %d", (s0+aStat[i]/2) / aStat[i]);
+ zStat = idxAppendText(&rc, zStat, " %lld", (s0+aStat[i]/2) / aStat[i]);
}
}
@@ -14582,7 +16557,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
rc = sqlite3_exec(p->dbm, "ANALYZE; PRAGMA writable_schema=1", 0, 0, 0);
if( rc==SQLITE_OK ){
- int nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
+ i64 nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax);
pCtx = (struct IdxRemCtx*)idxMalloc(&rc, nByte);
}
@@ -14599,7 +16574,7 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
}
if( rc==SQLITE_OK ){
- pCtx->nSlot = nMax+1;
+ pCtx->nSlot = (i64)nMax+1;
rc = idxPrepareStmt(p->dbm, &pAllIndex, pzErr, zAllIndex);
}
if( rc==SQLITE_OK ){
@@ -14866,7 +16841,7 @@ int sqlite3_expert_sql(
if( pStmt ){
IdxStatement *pNew;
const char *z = sqlite3_sql(pStmt);
- int n = STRLEN(z);
+ i64 n = STRLEN(z);
pNew = (IdxStatement*)idxMalloc(&rc, sizeof(IdxStatement) + n+1);
if( rc==SQLITE_OK ){
pNew->zSql = (char*)&pNew[1];
@@ -14988,8 +16963,9 @@ void sqlite3_expert_destroy(sqlite3expert *p){
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
-/************************* End ../ext/expert/sqlite3expert.c ********************/
-/************************* Begin ../ext/intck/sqlite3intck.h ******************/
+/************************* End ext/expert/sqlite3expert.c ********************/
+#endif
+/************************* Begin ext/intck/sqlite3intck.h ******************/
/*
** 2024-02-08
**
@@ -15162,8 +17138,8 @@ const char *sqlite3_intck_test_sql(sqlite3_intck *pCk, const char *zObj);
#endif /* ifndef _SQLITE_INTCK_H */
-/************************* End ../ext/intck/sqlite3intck.h ********************/
-/************************* Begin ../ext/intck/sqlite3intck.c ******************/
+/************************* End ext/intck/sqlite3intck.h ********************/
+/************************* Begin ext/intck/sqlite3intck.c ******************/
/*
** 2024-02-08
**
@@ -15326,6 +17302,7 @@ static char *intckMprintf(sqlite3_intck *p, const char *zFmt, ...){
sqlite3_free(zRet);
zRet = 0;
}
+ va_end(ap);
return zRet;
}
@@ -15484,7 +17461,7 @@ static int intckGetToken(const char *z){
char c = z[0];
int iRet = 1;
if( c=='\'' || c=='"' || c=='`' ){
- while( 1 ){
+ while( z[iRet] ){
if( z[iRet]==c ){
iRet++;
if( z[iRet]!=c ) break;
@@ -16105,8 +18082,8 @@ const char *sqlite3_intck_test_sql(sqlite3_intck *p, const char *zObj){
return p->zTestSql;
}
-/************************* End ../ext/intck/sqlite3intck.c ********************/
-/************************* Begin ../ext/misc/stmtrand.c ******************/
+/************************* End ext/intck/sqlite3intck.c ********************/
+/************************* Begin ext/misc/stmtrand.c ******************/
/*
** 2024-05-24
**
@@ -16161,7 +18138,7 @@ static void stmtrandFunc(
p = (Stmtrand*)sqlite3_get_auxdata(context, STMTRAND_KEY);
if( p==0 ){
unsigned int seed;
- p = sqlite3_malloc( sizeof(*p) );
+ p = sqlite3_malloc64( sizeof(*p) );
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -16205,8 +18182,8 @@ int sqlite3_stmtrand_init(
return rc;
}
-/************************* End ../ext/misc/stmtrand.c ********************/
-/************************* Begin ../ext/misc/vfstrace.c ******************/
+/************************* End ext/misc/stmtrand.c ********************/
+/************************* Begin ext/misc/vfstrace.c ******************/
/*
** 2011 March 16
**
@@ -17101,7 +19078,7 @@ static int vfstraceOpen(
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
- sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
+ sqlite3_io_methods *pNew = sqlite3_malloc64( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
@@ -17419,7 +19396,7 @@ void vfstrace_unregister(const char *zTraceName){
sqlite3_free(pVfs);
}
-/************************* End ../ext/misc/vfstrace.c ********************/
+/************************* End ext/misc/vfstrace.c ********************/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
@@ -17427,7 +19404,7 @@ void vfstrace_unregister(const char *zTraceName){
#define SQLITE_SHELL_HAVE_RECOVER 0
#endif
#if SQLITE_SHELL_HAVE_RECOVER
-/************************* Begin ../ext/recover/sqlite3recover.h ******************/
+/************************* Begin ext/recover/sqlite3recover.h ******************/
/*
** 2022-08-27
**
@@ -17678,9 +19655,9 @@ int sqlite3_recover_finish(sqlite3_recover*);
#endif /* ifndef _SQLITE_RECOVER_H */
-/************************* End ../ext/recover/sqlite3recover.h ********************/
+/************************* End ext/recover/sqlite3recover.h ********************/
# ifndef SQLITE_HAVE_SQLITE3R
-/************************* Begin ../ext/recover/dbdata.c ******************/
+/************************* Begin ext/recover/dbdata.c ******************/
/*
** 2019-04-17
**
@@ -18705,8 +20682,8 @@ int sqlite3_dbdata_init(
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
-/************************* End ../ext/recover/dbdata.c ********************/
-/************************* Begin ../ext/recover/sqlite3recover.c ******************/
+/************************* End ext/recover/dbdata.c ********************/
+/************************* Begin ext/recover/sqlite3recover.c ******************/
/*
** 2022-08-27
**
@@ -19242,17 +21219,38 @@ static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){
}
/*
+** Run a single SQL statement in zSql. If zSql contains two or more
+** SQL statements separated by ';', only the first is run.
+**
+** Return the sqlite3_finalizer() or sqlite3_prepare() result code
+** from running the zSql statement.
+*/
+static int recoverOneStmt(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ if( zSql==0 ) return SQLITE_OK;
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ if( rc ){
+ sqlite3_finalize(pStmt);
+ return rc;
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){}
+ return sqlite3_finalize(pStmt);
+}
+
+/*
** This function is a no-op if recover handle p already contains an error
** (if p->errCode!=SQLITE_OK). A copy of p->errCode is returned in this
** case.
**
-** Otherwise, execute SQL script zSql. If successful, return SQLITE_OK.
-** Or, if an error occurs, leave an error code and message in the recover
-** handle and return a copy of the error code.
+** Otherwise, execute a single SQL statment in zSql. Even if zSql contains
+** two or more SQL statements separated by ';', only execute the first one.
+** If successful, return SQLITE_OK. Or, if an error occurs, leave an error
+** code and message in the recover handle and return a copy of the error code.
*/
static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){
if( p->errCode==SQLITE_OK ){
- int rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ int rc = recoverOneStmt(db, zSql);
if( rc ){
recoverDbError(p, db);
}
@@ -19652,7 +21650,8 @@ static void recoverTransferSettings(sqlite3_recover *p){
}
recoverFinalize(p, p1);
}
- recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;");
+ recoverExec(p, db2, "CREATE TABLE t1(a)");
+ recoverExec(p, db2, "DROP TABLE t1");
if( p->errCode==SQLITE_OK ){
sqlite3 *db = p->dbOut;
@@ -19734,12 +21733,12 @@ static int recoverOpenOutput(sqlite3_recover *p){
static void recoverOpenRecovery(sqlite3_recover *p){
char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb);
recoverExec(p, p->dbOut, zSql);
- recoverExec(p, p->dbOut,
- "PRAGMA writable_schema = 1;"
- "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);"
- "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);"
- );
sqlite3_free(zSql);
+ recoverExec(p, p->dbOut, "PRAGMA writable_schema = 1");
+ recoverExec(p, p->dbOut,
+ "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT)");
+ recoverExec(p, p->dbOut,
+ "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql)");
}
@@ -19879,7 +21878,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){
")"
"SELECT rootpage, tbl, isVirtual, name, sql"
" FROM dbschema "
- " WHERE tbl OR isIndex"
+ " WHERE (tbl OR isIndex) AND sql GLOB 'CREATE *'"
" ORDER BY tbl DESC, name=='sqlite_sequence' DESC"
);
@@ -19905,7 +21904,7 @@ static int recoverWriteSchema1(sqlite3_recover *p){
zName, zName, zSql
));
}
- rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
+ rc = recoverOneStmt(p->dbOut, zSql);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
if( bTable && !bVirtual ){
@@ -19947,15 +21946,17 @@ static int recoverWriteSchema2(sqlite3_recover *p){
p->bSlowIndexes ?
"SELECT rootpage, sql FROM recovery.schema "
" WHERE type!='table' AND type!='index'"
+ " AND sql GLOB 'CREATE *'"
:
"SELECT rootpage, sql FROM recovery.schema "
" WHERE type!='table' AND (type!='index' OR sql NOT LIKE '%unique%')"
+ " AND sql GLOB 'CREATE *'"
);
if( pSelect ){
while( sqlite3_step(pSelect)==SQLITE_ROW ){
const char *zSql = (const char*)sqlite3_column_text(pSelect, 1);
- int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0);
+ int rc = recoverOneStmt(p->dbOut, zSql);
if( rc==SQLITE_OK ){
recoverSqlCallback(p, zSql);
}else if( rc!=SQLITE_ERROR ){
@@ -21333,7 +23334,7 @@ static void recoverStep(sqlite3_recover *p){
if( bUseWrapper ) recoverUninstallWrapper(p);
}while( p->errCode==SQLITE_NOTADB
&& (bUseWrapper--)
- && SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0)
+ && SQLITE_OK==recoverOneStmt(p->dbIn, "ROLLBACK")
);
}
@@ -21398,7 +23399,7 @@ static void recoverStep(sqlite3_recover *p){
** database. Regardless of whether or not an error has occurred, make
** an attempt to end the read transaction on the input database. */
recoverExec(p, p->dbOut, "COMMIT");
- rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0);
+ rc = recoverOneStmt(p->dbIn, "END");
if( p->errCode==SQLITE_OK ) p->errCode = rc;
recoverSqlCallback(p, "PRAGMA writable_schema = off");
@@ -21594,7 +23595,7 @@ int sqlite3_recover_finish(sqlite3_recover *p){
}else{
recoverFinalCleanup(p);
if( p->bCloseTransaction && sqlite3_get_autocommit(p->dbIn)==0 ){
- rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0);
+ rc = recoverOneStmt(p->dbIn, "END");
if( p->errCode==SQLITE_OK ) p->errCode = rc;
}
rc = p->errCode;
@@ -21609,7 +23610,7 @@ int sqlite3_recover_finish(sqlite3_recover *p){
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
-/************************* End ../ext/recover/sqlite3recover.c ********************/
+/************************* End ext/recover/sqlite3recover.c ********************/
# endif /* SQLITE_HAVE_SQLITE3R */
#endif
#ifdef SQLITE_SHELL_EXTSRC
@@ -21629,37 +23630,32 @@ struct OpenSession {
};
#endif
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
sqlite3expert *pExpert;
int bVerbose;
};
+#endif
-/* A single line in the EQP output */
-typedef struct EQPGraphRow EQPGraphRow;
-struct EQPGraphRow {
- int iEqpId; /* ID for this row */
- int iParentId; /* ID of the parent row */
- EQPGraphRow *pNext; /* Next row in sequence */
- char zText[1]; /* Text to display for this row */
-};
+/* All the parameters that determine how to render query results.
+*/
+typedef struct Mode {
+ u8 autoExplain; /* Automatically turn on .explain mode */
+ u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */
+ u8 autoEQPtrace; /* autoEQP is in trace mode */
+ u8 scanstatsOn; /* True to display scan stats before each finalize */
+ u8 bAutoScreenWidth; /* Using the TTY to determine screen width */
+ u8 mFlags; /* MFLG_ECHO, MFLG_CRLF, etc. */
+ u8 eMode; /* One of the MODE_ values */
+ sqlite3_qrf_spec spec; /* Spec to be passed into QRF */
+} Mode;
-/* All EQP output is collected into an instance of the following */
-typedef struct EQPGraph EQPGraph;
-struct EQPGraph {
- EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
- EQPGraphRow *pLast; /* Last element of the pRow list */
- char zPrefix[100]; /* Graph prefix */
-};
+/* Flags for Mode.mFlags */
+#define MFLG_ECHO 0x01 /* Echo inputs to output */
+#define MFLG_CRLF 0x02 /* Use CR/LF output line endings */
+#define MFLG_HDR 0x04 /* .header used to change headers on/off */
-/* Parameters affecting columnar mode result display (defaulting together) */
-typedef struct ColModeOpts {
- int iWrap; /* In columnar modes, wrap lines reaching this limit */
- u8 bQuote; /* Quote results for .mode box and table */
- u8 bWordWrap; /* In columnar modes, wrap at word boundaries */
-} ColModeOpts;
-#define ColModeOpts_default { 60, 0, 0 }
-#define ColModeOpts_default_qbox { 60, 1, 0 }
/*
** State information about the database connection is contained in an
@@ -21668,11 +23664,6 @@ typedef struct ColModeOpts {
typedef struct ShellState ShellState;
struct ShellState {
sqlite3 *db; /* The database */
- u8 autoExplain; /* Automatically turn on .explain mode */
- u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */
- u8 autoEQPtest; /* autoEQP is in test mode */
- u8 autoEQPtrace; /* autoEQP is in trace mode */
- u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
@@ -21680,48 +23671,44 @@ struct ShellState {
u8 bSafeMode; /* True to prohibit unsafe operations */
u8 bSafeModePersist; /* The long-term value of bSafeMode */
u8 eRestoreState; /* See comments above doAutoDetectRestore() */
- u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */
- u8 eEscMode; /* Escape mode for text output */
- ColModeOpts cmOpts; /* Option values affecting columnar mode output */
unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */
+ u8 nPopOutput; /* Revert .output settings when reaching zero */
+ u8 nPopMode; /* Revert .mode settings when reaching zero */
+ u8 enableTimer; /* Enable the timer. 2: permanently 1: only once */
int inputNesting; /* Track nesting level of .read and other redirects */
- int outCount; /* Revert to stdout when reaching zero */
- int cnt; /* Number of records displayed so far */
- int lineno; /* Line number of last line read from in */
+ double prevTimer; /* Last reported timer value */
+ double tmProgress; /* --timeout option for .progress */
+ i64 lineno; /* Line number of last line read from in */
+ const char *zInFile; /* Name of the input file */
int openFlags; /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */
FILE *in; /* Read commands from this stream */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */
- int mode; /* An output mode setting */
- int modePrior; /* Saved mode */
- int cMode; /* temporary output mode for the current query */
- int normalMode; /* Output mode before ".explain on" */
int writableSchema; /* True if PRAGMA writable_schema=ON */
- int showHeader; /* True to show column names in List or Column mode */
int nCheck; /* Number of ".check" commands run */
unsigned nProgress; /* Number of progress callbacks encountered */
unsigned mxProgress; /* Maximum progress callbacks before failing */
unsigned flgProgress; /* Flags for the progress callback */
unsigned shellFlgs; /* Various flags */
- unsigned priorShFlgs; /* Saved copy of flags */
+ unsigned nTestRun; /* Number of test cases run */
+ unsigned nTestErr; /* Number of test cases that failed */
sqlite3_int64 szMax; /* --maxsize argument to .open */
char *zDestTable; /* Name of destination table when MODE_Insert */
char *zTempFile; /* Temporary file that might need deleting */
+ char *zErrPrefix; /* Alternative error message prefix */
char zTestcase[30]; /* Name of current test case */
- char colSeparator[20]; /* Column separator character for several modes */
- char rowSeparator[20]; /* Row separator character for MODE_Ascii */
- char colSepPrior[20]; /* Saved column separator */
- char rowSepPrior[20]; /* Saved row separator */
- int *colWidth; /* Requested width of each column in columnar modes */
- int *actualWidth; /* Actual width of each column */
- int nWidth; /* Number of slots in colWidth[] and actualWidth[] */
- char nullValue[20]; /* The text to print when a NULL comes back from
- ** the database */
char outfile[FILENAME_MAX]; /* Filename for *out */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
+ Mode mode; /* Current display mode */
+ Mode modePrior; /* Backup */
+ struct SavedMode { /* Ability to define custom mode configurations */
+ char *zTag; /* Name of this saved mode */
+ Mode mode; /* The saved mode */
+ } *aSavedModes; /* Array of saved .mode settings. system malloc() */
+ int nSavedModes; /* Number of saved .mode settings */
struct AuxDb { /* Storage space for auxiliary database connections */
sqlite3 *db; /* Connection pointer */
const char *zDbFilename; /* Filename used to open the connection */
@@ -21732,12 +23719,19 @@ struct ShellState {
#endif
} aAuxDb[5], /* Array of all database connections */
*pAuxDb; /* Currently active database connection */
- int *aiIndent; /* Array of indents used in MODE_Explain */
- int nIndent; /* Size of array aiIndent[] */
- int iIndent; /* Index of current op in aiIndent[] */
char *zNonce; /* Nonce for temporary safe-mode escapes */
- EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
+#endif
+ struct DotCmdLine { /* Info about arguments to a dot-command */
+ const char *zOrig; /* Original text of the dot-command */
+ char *zCopy; /* Copy of zOrig, from malloc() */
+ int nAlloc; /* Size of allocates for arrays below */
+ int nArg; /* Number of argument slots actually used */
+ char **azArg; /* Pointer to each argument, dequoted */
+ int *aiOfst; /* Offset into zOrig[] for start of each arg */
+ char *abQuot; /* True if the argment was originally quoted */
+ } dot;
#ifdef SQLITE_SHELL_FIDDLE
struct {
const char * zInput; /* Input string from wasm/JS proxy */
@@ -21752,7 +23746,7 @@ static ShellState shellState;
#endif
-/* Allowed values for ShellState.autoEQP
+/* Allowed values for ShellState.mode.autoEQP
*/
#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
#define AUTOEQP_on 1 /* Automatic EQP is on */
@@ -21765,9 +23759,8 @@ static ShellState shellState;
#define SHELL_OPEN_NORMAL 1 /* Normal database file */
#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
-#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
-#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
-#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
+#define SHELL_OPEN_DESERIALIZE 4 /* Open using sqlite3_deserialize() */
+#define SHELL_OPEN_HEXDB 5 /* Use "dbtotxt" output as data source */
/* Allowed values for ShellState.eTraceType
*/
@@ -21781,15 +23774,13 @@ static ShellState shellState;
** callback limit is reached, and for each
** top-level SQL statement */
#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */
+#define SHELL_PROGRESS_TMOUT 0x08 /* Stop after tmProgress seconds */
-/* Allowed values for ShellState.eEscMode. The default value should
-** be 0, so to change the default, reorder the names.
+/* Names of values for Mode.spec.eEsc and Mode.spec.eText
*/
-#define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */
-#define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */
-#define SHELL_ESC_OFF 2 /* Send characters verbatim */
-
-static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" };
+static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" };
+static const char *qrfQuoteNames[] =
+ { "off","off","sql","hex","csv","tcl","json","relaxed"};
/*
** These are the allowed shellFlgs values
@@ -21798,10 +23789,8 @@ static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" };
#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */
#define SHFLG_PreserveRowid 0x00000008 /* .dump preserves rowid values */
-#define SHFLG_Newlines 0x00000010 /* .dump --newline flag */
+#define SHFLG_NoErrLineno 0x00000010 /* Omit line numbers from error msgs */
#define SHFLG_CountChanges 0x00000020 /* .changes setting */
-#define SHFLG_Echo 0x00000040 /* .echo on/off, or --echo setting */
-#define SHFLG_HeaderSet 0x00000080 /* showHeader has been specified */
#define SHFLG_DumpDataOnly 0x00000100 /* .dump show data only */
#define SHFLG_DumpNoSys 0x00000200 /* .dump omits system tables */
#define SHFLG_TestingMode 0x00000400 /* allow unsafe testing features */
@@ -21814,54 +23803,107 @@ static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" };
#define ShellClearFlag(P,X) ((P)->shellFlgs&=(~(X)))
/*
-** These are the allowed modes.
+** These are the allowed values for Mode.eMode. There is a lot of overlap
+** between these values and the Mode.spec.eStyle values, but they are not
+** one-to-one, and thus need to be tracked separately.
*/
-#define MODE_Line 0 /* One column per line. Blank line between records */
-#define MODE_Column 1 /* One record per line in neat columns */
-#define MODE_List 2 /* One record per line with a separator */
-#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
-#define MODE_Html 4 /* Generate an XHTML table */
-#define MODE_Insert 5 /* Generate SQL "insert" statements */
-#define MODE_Quote 6 /* Quote values as for SQL */
-#define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */
-#define MODE_Csv 8 /* Quote strings, numbers are plain */
-#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
-#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */
-#define MODE_Pretty 11 /* Pretty-print schemas */
-#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */
-#define MODE_Json 13 /* Output JSON */
-#define MODE_Markdown 14 /* Markdown formatting */
-#define MODE_Table 15 /* MySQL-style table formatting */
-#define MODE_Box 16 /* Unicode box-drawing characters */
-#define MODE_Count 17 /* Output only a count of the rows of output */
-#define MODE_Off 18 /* No query output shown */
-#define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */
-#define MODE_Www 20 /* Full web-page output */
+#define MODE_Ascii 0 /* Use ASCII unit and record separators (0x1F/0x1E) */
+#define MODE_Box 1 /* Unicode box-drawing characters */
+#define MODE_C 2 /* Comma-separated list of C-strings */
+#define MODE_Column 3 /* One record per line in neat columns */
+#define MODE_Count 4 /* Output only a count of the rows of output */
+#define MODE_Csv 5 /* Quote strings, numbers are plain */
+#define MODE_Html 6 /* Generate an XHTML table */
+#define MODE_Insert 7 /* Generate SQL "insert" statements */
+#define MODE_JAtom 8 /* Comma-separated list of JSON atoms */
+#define MODE_JObject 9 /* One JSON object per row */
+#define MODE_Json 10 /* Output JSON */
+#define MODE_Line 11 /* One column per line. Blank line between records */
+#define MODE_List 12 /* One record per line with a separator */
+#define MODE_Markdown 13 /* Markdown formatting */
+#define MODE_Off 14 /* No query output shown */
+#define MODE_Psql 15 /* Similar to psql */
+#define MODE_QBox 16 /* BOX with SQL-quoted content */
+#define MODE_Quote 17 /* Quote values as for SQL */
+#define MODE_Split 18 /* Split-column mode */
+#define MODE_Table 19 /* MySQL-style table formatting */
+#define MODE_Tabs 20 /* Tab-separated values */
+#define MODE_Tcl 21 /* Space-separated list of TCL strings */
+#define MODE_Www 22 /* Full web-page output */
+
+#define MODE_BUILTIN 22 /* Maximum built-in mode */
+#define MODE_BATCH 50 /* Default mode for batch processing */
+#define MODE_TTY 51 /* Default mode for interactive processing */
+#define MODE_USER 75 /* First user-defined mode */
+#define MODE_N_USER 25 /* Maximum number of user-defined modes */
-static const char *modeDescr[] = {
- "line",
- "column",
- "list",
- "semi",
- "html",
- "insert",
- "quote",
- "tcl",
- "csv",
- "explain",
- "ascii",
- "prettyprint",
- "eqp",
- "json",
- "markdown",
- "table",
- "box",
- "count",
- "off",
- "scanexp",
- "www",
+/*
+** Information about built-in display modes
+*/
+typedef struct ModeInfo ModeInfo;
+struct ModeInfo {
+ char zName[9]; /* Symbolic name of the mode */
+ unsigned char eCSep; /* Column separator */
+ unsigned char eRSep; /* Row separator */
+ unsigned char eNull; /* Null representation */
+ unsigned char eText; /* Default text encoding */
+ unsigned char eHdr; /* Default header encoding. */
+ unsigned char eBlob; /* Default blob encoding. */
+ unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */
+ unsigned char eStyle; /* Underlying QRF style */
+ unsigned char eCx; /* 0: other, 1: line, 2: columnar */
+ unsigned char mFlg; /* Flags. 1=border-off 2=split-column */
};
+/* String constants used by built-in modes */
+static const char *aModeStr[] =
+ /* 0 1 2 3 4 5 6 7 8 */
+ { 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t",
+ "", "NULL", "null", "\"\"", ": ", };
+ /* 9 10 11 12 13 */
+
+static const ModeInfo aModeInfo[] = {
+/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */
+ { "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 },
+ { "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 },
+ { "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 },
+ { "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 },
+ { "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 },
+ { "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 },
+ { "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 },
+ { "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 },
+ { "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 },
+ { "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 },
+ { "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 },
+ { "line", 13, 1, 9, 1, 1, 0, 0, 11, 1, 0 },
+ { "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 },
+ { "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 },
+ { "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 },
+ { "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 },
+ { "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 },
+ { "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 },
+ { "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 },
+ { "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 },
+ { "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 },
+ { "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 },
+ { "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }
+}; /* | / / | / / | | \
+ ** | / / | / / | | \_ 2: columnar
+ ** Index into aModeStr[] | / / | | 1: line
+ ** | / / | | 0: other
+ ** | / / | \
+ ** text encoding |/ | show | \
+ ** v-------------------' | hdrs? | The QRF style
+ ** 0: n/a blob | v-----'
+ ** 1: plain v_---------' 0: n/a
+ ** 2: sql 0: auto 1: no
+ ** 3: csv 1: as-text 2: yes
+ ** 4: html 2: sql
+ ** 5: c 3: hex
+ ** 6: json 4: c
+ ** 5: json
+ ** 6: size
+ ******************************************************************/
/*
** These are the column/row/line separators used by the various
** import/export modes.
@@ -21876,10 +23918,1133 @@ static const char *modeDescr[] = {
#define SEP_Record "\x1E"
/*
-** Limit input nesting via .read or any other input redirect.
-** It's not too expensive, so a generous allowance can be made.
+** Default values for the various QRF limits
*/
-#define MAX_INPUT_NESTING 25
+#ifndef DFLT_CHAR_LIMIT
+# define DFLT_CHAR_LIMIT 300
+#endif
+#ifndef DFLT_LINE_LIMIT
+# define DFLT_LINE_LIMIT 5
+#endif
+#ifndef DFLT_TITLE_LIMIT
+# define DFLT_TITLE_LIMIT 20
+#endif
+#ifndef DFLT_MULTI_INSERT
+# define DFLT_MULTI_INSERT 3000
+#endif
+
+/*
+** If the following flag is set, then command execution stops
+** at an error if we are not interactive.
+*/
+static int bail_on_error = 0;
+
+/*
+** Treat stdin as an interactive input if the following variable
+** is true. Otherwise, assume stdin is connected to a file or pipe.
+*/
+static int stdin_is_interactive = 1;
+
+/*
+** Treat stdout like a TTY if true.
+*/
+static int stdout_is_console = 1;
+
+/*
+** Use this value as the width of the output device. Or, figure it
+** out at runtime if the value is negative. Or use a default width
+** if this value is zero.
+*/
+static int stdout_tty_width = -1;
+
+/*
+** The following is the open SQLite database. We make a pointer
+** to this database a static variable so that it can be accessed
+** by the SIGINT handler to interrupt database processing.
+*/
+static sqlite3 *globalDb = 0;
+
+/*
+** True if an interrupt (Control-C) has been received.
+*/
+static volatile int seenInterrupt = 0;
+
+/*
+** This is the name of our program. It is set in main(), used
+** in a number of other places, mostly for error messages.
+*/
+static char *Argv0;
+
+/*
+** Prompt strings. Initialized in main. Settable with
+** .prompt main continue
+*/
+#define PROMPT_LEN_MAX 128
+/* First line prompt. default: "sqlite> " */
+static char mainPrompt[PROMPT_LEN_MAX];
+/* Continuation prompt. default: " ...> " */
+static char continuePrompt[PROMPT_LEN_MAX];
+
+/*
+** Write I/O traces to the following stream.
+*/
+#ifdef SQLITE_ENABLE_IOTRACE
+static FILE *iotrace = 0;
+#endif
+
+/*
+** Output routines that are able to redirect to memory rather than
+** doing actually I/O.
+** Works like.
+** --------------
+** cli_printf(FILE*, const char*, ...); fprintf()
+** cli_puts(const char*, FILE*); fputs()
+** cli_vprintf(FILE*, const char*, va_list); vfprintf()
+**
+** These are just thin wrappers with the following added semantics:
+** If the file-scope variable cli_output_capture is not NULL, and
+** if the FILE* argument is stdout or stderr, then rather than
+** writing to stdout/stdout, append the text to the cli_output_capture
+** variable.
+**
+** The cli_exit(int) routine works like exit() except that it
+** first dumps any capture output to stdout.
+*/
+static sqlite3_str *cli_output_capture = 0;
+static int cli_printf(FILE *out, const char *zFormat, ...){
+ va_list ap;
+ int rc;
+ va_start(ap,zFormat);
+ if( cli_output_capture && (out==stdout || out==stderr) ){
+ sqlite3_str_vappendf(cli_output_capture, zFormat, ap);
+ rc = 1;
+ }else{
+ rc = sqlite3_vfprintf(out, zFormat, ap);
+ }
+ va_end(ap);
+ return rc;
+}
+static int cli_puts(const char *zText, FILE *out){
+ if( cli_output_capture && (out==stdout || out==stderr) ){
+ sqlite3_str_appendall(cli_output_capture, zText);
+ return 1;
+ }
+ return sqlite3_fputs(zText, out);
+}
+#if 0 /* Not currently used - available if we need it later */
+static int cli_vprintf(FILE *out, const char *zFormat, va_list ap){
+ if( cli_output_capture && (out==stdout || out==stderr) ){
+ sqlite3_str_vappendf(cli_output_capture, zFormat, ap);
+ return 1;
+ }else{
+ return sqlite3_vfprintf(out, zFormat, ap);
+ }
+}
+#endif
+static void cli_exit(int rc){
+ if( cli_output_capture ){
+ char *z = sqlite3_str_finish(cli_output_capture);
+ sqlite3_fputs(z, stdout);
+ fflush(stdout);
+ }
+ exit(rc);
+}
+
+
+#define eputz(z) cli_puts(z,stderr)
+#define sputz(fp,z) cli_puts(z,fp)
+
+/* A version of strcmp() that works with NULL values */
+static int cli_strcmp(const char *a, const char *b){
+ if( a==0 ) a = "";
+ if( b==0 ) b = "";
+ return strcmp(a,b);
+}
+static int cli_strncmp(const char *a, const char *b, size_t n){
+ if( a==0 ) a = "";
+ if( b==0 ) b = "";
+ return strncmp(a,b,n);
+}
+
+/* Return the current wall-clock time in microseconds since the
+** Unix epoch (1970-01-01T00:00:00Z)
+*/
+static sqlite3_int64 timeOfDay(void){
+#if defined(_WIN64) && _WIN32_WINNT >= _WIN32_WINNT_WIN8
+ sqlite3_uint64 t;
+ FILETIME tm;
+ GetSystemTimePreciseAsFileTime(&tm);
+ t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
+ t += 116444736000000000LL;
+ t /= 10;
+ return t;
+#elif defined(_WIN32)
+ static sqlite3_vfs *clockVfs = 0;
+ sqlite3_int64 t;
+ if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
+ if( clockVfs==0 ) return 0; /* Never actually happens */
+ if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
+ clockVfs->xCurrentTimeInt64(clockVfs, &t);
+ }else{
+ double r;
+ clockVfs->xCurrentTime(clockVfs, &r);
+ t = (sqlite3_int64)(r*86400000.0);
+ }
+ return t*1000;
+#else
+ struct timeval sNow;
+ (void)gettimeofday(&sNow,0);
+ return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
+#endif
+}
+
+
+
+/* This is variant of the standard-library strncpy() routine with the
+** one change that the destination string is always zero-terminated, even
+** if there is no zero-terminator in the first n-1 characters of the source
+** string.
+*/
+static char *shell_strncpy(char *dest, const char *src, size_t n){
+ size_t i;
+ for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i];
+ dest[i] = 0;
+ return dest;
+}
+
+/*
+** strcpy() workalike to squelch an unwarranted link-time warning
+** from OpenBSD.
+*/
+static void shell_strcpy(char *dest, const char *src){
+ while( (*(dest++) = *(src++))!=0 ){}
+}
+
+/*
+** Optionally disable dynamic continuation prompt.
+** Unless disabled, the continuation prompt shows open SQL lexemes if any,
+** or open parentheses level if non-zero, or continuation prompt as set.
+** This facility interacts with the scanner and process_input() where the
+** below 5 macros are used.
+*/
+#ifdef SQLITE_OMIT_DYNAPROMPT
+# define CONTINUATION_PROMPT continuePrompt
+# define CONTINUE_PROMPT_RESET
+# define CONTINUE_PROMPT_AWAITS(p,s)
+# define CONTINUE_PROMPT_AWAITC(p,c)
+# define CONTINUE_PAREN_INCR(p,n)
+# define CONTINUE_PROMPT_PSTATE 0
+typedef void *t_NoDynaPrompt;
+# define SCAN_TRACKER_REFTYPE t_NoDynaPrompt
+#else
+# define CONTINUATION_PROMPT dynamicContinuePrompt()
+# define CONTINUE_PROMPT_RESET \
+ do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0)
+# define CONTINUE_PROMPT_AWAITS(p,s) \
+ if(p && stdin_is_interactive) setLexemeOpen(p, s, 0)
+# define CONTINUE_PROMPT_AWAITC(p,c) \
+ if(p && stdin_is_interactive) setLexemeOpen(p, 0, c)
+# define CONTINUE_PAREN_INCR(p,n) \
+ if(p && stdin_is_interactive) (trackParenLevel(p,n))
+# define CONTINUE_PROMPT_PSTATE (&dynPrompt)
+typedef struct DynaPrompt *t_DynaPromptRef;
+# define SCAN_TRACKER_REFTYPE t_DynaPromptRef
+
+static struct DynaPrompt {
+ char dynamicPrompt[PROMPT_LEN_MAX];
+ char acAwait[2];
+ int inParenLevel;
+ char *zScannerAwaits;
+} dynPrompt = { {0}, {0}, 0, 0 };
+
+/* Record parenthesis nesting level change, or force level to 0. */
+static void trackParenLevel(struct DynaPrompt *p, int ni){
+ p->inParenLevel += ni;
+ if( ni==0 ) p->inParenLevel = 0;
+ p->zScannerAwaits = 0;
+}
+
+/* Record that a lexeme is opened, or closed with args==0. */
+static void setLexemeOpen(struct DynaPrompt *p, char *s, char c){
+ if( s!=0 || c==0 ){
+ p->zScannerAwaits = s;
+ p->acAwait[0] = 0;
+ }else{
+ p->acAwait[0] = c;
+ p->zScannerAwaits = p->acAwait;
+ }
+}
+
+/* Upon demand, derive the continuation prompt to display. */
+static char *dynamicContinuePrompt(void){
+ if( continuePrompt[0]==0
+ || (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
+ return continuePrompt;
+ }else{
+ if( dynPrompt.zScannerAwaits ){
+ size_t ncp = strlen(continuePrompt);
+ size_t ndp = strlen(dynPrompt.zScannerAwaits);
+ if( ndp > ncp-3 ) return continuePrompt;
+ shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
+ while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
+ shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
+ PROMPT_LEN_MAX-4);
+ }else{
+ if( dynPrompt.inParenLevel>9 ){
+ shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4);
+ }else if( dynPrompt.inParenLevel<0 ){
+ shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
+ }else{
+ shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
+ dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
+ }
+ shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
+ PROMPT_LEN_MAX-4);
+ }
+ }
+ return dynPrompt.dynamicPrompt;
+}
+#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
+
+/* Indicate out-of-memory and exit. */
+static void shell_out_of_memory(void){
+ eputz("Error: out of memory\n");
+ cli_exit(1);
+}
+
+/* Check a pointer to see if it is NULL. If it is NULL, exit with an
+** out-of-memory error.
+*/
+static void shell_check_oom(const void *p){
+ if( p==0 ) shell_out_of_memory();
+}
+
+/*
+** This routine works like printf in that its first argument is a
+** format string and subsequent arguments are values to be substituted
+** in place of % fields. The result of formatting this string
+** is written to iotrace.
+*/
+#ifdef SQLITE_ENABLE_IOTRACE
+static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
+ va_list ap;
+ char *z;
+ if( iotrace==0 ) return;
+ va_start(ap, zFormat);
+ z = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ cli_printf(iotrace, "%s", z);
+ sqlite3_free(z);
+}
+#endif
+
+/*
+** Compute a string length that is limited to what can be stored in
+** lower 30 bits of a 32-bit signed integer.
+*/
+static int strlen30(const char *z){
+ size_t n;
+ if( z==0 ) return 0;
+ n = strlen(z);
+ return n>0x3fffffff ? 0x3fffffff : (int)n;
+}
+
+/*
+** Return open FILE * if zFile exists, can be opened for read
+** and is an ordinary file or a character stream source.
+** Otherwise return 0.
+*/
+static FILE * openChrSource(const char *zFile){
+#if defined(_WIN32) || defined(WIN32)
+ struct __stat64 x = {0};
+# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
+ /* On Windows, open first, then check the stream nature. This order
+ ** is necessary because _stat() and sibs, when checking a named pipe,
+ ** effectively break the pipe as its supplier sees it. */
+ FILE *rv = sqlite3_fopen(zFile, "rb");
+ if( rv==0 ) return 0;
+ if( _fstat64(_fileno(rv), &x) != 0
+ || !STAT_CHR_SRC(x.st_mode)){
+ fclose(rv);
+ rv = 0;
+ }
+ return rv;
+#else
+ struct stat x = {0};
+ int rc = stat(zFile, &x);
+# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode))
+ if( rc!=0 ) return 0;
+ if( STAT_CHR_SRC(x.st_mode) ){
+ return sqlite3_fopen(zFile, "rb");
+ }else{
+ return 0;
+ }
+#endif
+#undef STAT_CHR_SRC
+}
+
+/*
+** This routine reads a line of text from FILE in, stores
+** the text in memory obtained from malloc() and returns a pointer
+** to the text. NULL is returned at end of file, or if malloc()
+** fails, or if the length of the line is longer than about a gigabyte.
+**
+** If zLine is not NULL then it is a malloced buffer returned from
+** a previous call to this routine that may be reused.
+*/
+static char *local_getline(char *zLine, FILE *in){
+ int nLine = zLine==0 ? 0 : 100;
+ int n = 0;
+
+ while( 1 ){
+ if( n+100>nLine ){
+ if( nLine>=1073741773 ){
+ free(zLine);
+ return 0;
+ }
+ nLine = nLine*2 + 100;
+ zLine = realloc(zLine, nLine);
+ shell_check_oom(zLine);
+ }
+ if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
+ if( n==0 ){
+ free(zLine);
+ return 0;
+ }
+ zLine[n] = 0;
+ break;
+ }
+ while( zLine[n] ) n++;
+ if( n>0 && zLine[n-1]=='\n' ){
+ n--;
+ if( n>0 && zLine[n-1]=='\r' ) n--;
+ zLine[n] = 0;
+ break;
+ }
+ }
+ return zLine;
+}
+
+/*
+** Retrieve a single line of input text.
+**
+** If in==0 then read from standard input and prompt before each line.
+** If isContinuation is true, then a continuation prompt is appropriate.
+** If isContinuation is zero, then the main prompt should be used.
+**
+** If zPrior is not NULL then it is a buffer from a prior call to this
+** routine that can be reused.
+**
+** The result is stored in space obtained from malloc() and must either
+** be freed by the caller or else passed back into this routine via the
+** zPrior argument for reuse.
+*/
+#ifndef SQLITE_SHELL_FIDDLE
+static char *one_input_line(ShellState *p, char *zPrior, int isContinuation){
+ char *zPrompt;
+ char *zResult;
+ FILE *in = p->in;
+ if( in!=0 ){
+ zResult = local_getline(zPrior, in);
+ }else{
+ zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
+#if SHELL_USE_LOCAL_GETLINE
+ sputz(stdout, zPrompt);
+ fflush(stdout);
+ do{
+ zResult = local_getline(zPrior, stdin);
+ zPrior = 0;
+ /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
+ if( zResult==0 ) sqlite3_sleep(50);
+ }while( zResult==0 && seenInterrupt>0 );
+#else
+ free(zPrior);
+ zResult = shell_readline(zPrompt);
+ while( zResult==0 ){
+ /* ^C trap creates a false EOF, so let "interrupt" thread catch up. */
+ sqlite3_sleep(50);
+ if( seenInterrupt==0 ) break;
+ zResult = shell_readline("");
+ }
+ if( zResult && *zResult ) shell_add_history(zResult);
+#endif
+ }
+ return zResult;
+}
+#endif /* !SQLITE_SHELL_FIDDLE */
+
+/*
+** Return the value of a hexadecimal digit. Return -1 if the input
+** is not a hex digit.
+*/
+static int hexDigitValue(char c){
+ if( c>='0' && c<='9' ) return c - '0';
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
+ return -1;
+}
+
+/*
+** Interpret zArg as an integer value, possibly with suffixes.
+**
+** If the value specified by zArg is outside the range of values that
+** can be represented using a 64-bit twos-complement integer, then return
+** the nearest representable value.
+*/
+static sqlite3_int64 integerValue(const char *zArg){
+ sqlite3_uint64 v = 0;
+ static const struct { char *zSuffix; unsigned int iMult; } aMult[] = {
+ { "KiB", 1024 },
+ { "MiB", 1024*1024 },
+ { "GiB", 1024*1024*1024 },
+ { "KB", 1000 },
+ { "MB", 1000000 },
+ { "GB", 1000000000 },
+ { "K", 1000 },
+ { "M", 1000000 },
+ { "G", 1000000000 },
+ };
+ int i;
+ int isNeg = 0;
+ if( zArg[0]=='-' ){
+ isNeg = 1;
+ zArg++;
+ }else if( zArg[0]=='+' ){
+ zArg++;
+ }
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ int x;
+ zArg += 2;
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
+ if( v > 0x0fffffffffffffffULL ) goto integer_overflow;
+ v = (v<<4) + x;
+ zArg++;
+ }
+ }else{
+ while( IsDigit(zArg[0]) ){
+ if( v>=922337203685477580LL ){
+ if( v>922337203685477580LL || zArg[0]>='8' ) goto integer_overflow;
+ }
+ v = v*10 + (zArg[0] - '0');
+ zArg++;
+ }
+ }
+ for(i=0; i<ArraySize(aMult); i++){
+ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
+ if( 0x7fffffffffffffffULL/aMult[i].iMult < v ) goto integer_overflow;
+ v *= aMult[i].iMult;
+ break;
+ }
+ }
+ if( isNeg && v>0x7fffffffffffffffULL ) goto integer_overflow;
+ return isNeg? -(sqlite3_int64)v : (sqlite3_int64)v;
+integer_overflow:
+ return isNeg ? (i64)0x8000000000000000LL : 0x7fffffffffffffffLL;
+}
+
+/*
+** A variable length string to which one can append text.
+*/
+typedef struct ShellText ShellText;
+struct ShellText {
+ char *zTxt; /* The text */
+ i64 n; /* Number of bytes of zTxt[] actually used */
+ i64 nAlloc; /* Number of bytes allocated for zTxt[] */
+};
+
+/*
+** Initialize and destroy a ShellText object
+*/
+static void initText(ShellText *p){
+ memset(p, 0, sizeof(*p));
+}
+static void freeText(ShellText *p){
+ sqlite3_free(p->zTxt);
+ initText(p);
+}
+
+/* zIn is either a pointer to a NULL-terminated string in memory obtained
+** from malloc(), or a NULL pointer. The string pointed to by zAppend is
+** added to zIn, and the result returned in memory obtained from malloc().
+** zIn, if it was not NULL, is freed.
+**
+** If the third argument, quote, is not '\0', then it is used as a
+** quote character for zAppend.
+*/
+static void appendText(ShellText *p, const char *zAppend, char quote){
+ i64 len;
+ i64 i;
+ i64 nAppend = strlen30(zAppend);
+
+ len = nAppend+p->n+1;
+ if( quote ){
+ len += 2;
+ for(i=0; i<nAppend; i++){
+ if( zAppend[i]==quote ) len++;
+ }
+ }
+
+ if( p->zTxt==0 || p->n+len>=p->nAlloc ){
+ p->nAlloc = p->nAlloc*2 + len + 20;
+ p->zTxt = sqlite3_realloc64(p->zTxt, p->nAlloc);
+ shell_check_oom(p->zTxt);
+ }
+
+ if( quote ){
+ char *zCsr = p->zTxt+p->n;
+ *zCsr++ = quote;
+ for(i=0; i<nAppend; i++){
+ *zCsr++ = zAppend[i];
+ if( zAppend[i]==quote ) *zCsr++ = quote;
+ }
+ *zCsr++ = quote;
+ p->n = (i64)(zCsr - p->zTxt);
+ *zCsr = '\0';
+ }else{
+ memcpy(p->zTxt+p->n, zAppend, nAppend);
+ p->n += nAppend;
+ p->zTxt[p->n] = '\0';
+ }
+}
+
+/*
+** Attempt to determine if identifier zName needs to be quoted, either
+** because it contains non-alphanumeric characters, or because it is an
+** SQLite keyword. Be conservative in this estimate: When in doubt assume
+** that quoting is required.
+**
+** Return '"' if quoting is required. Return 0 if no quoting is required.
+*/
+static char quoteChar(const char *zName){
+ int i;
+ if( zName==0 ) return '"';
+ if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"';
+ for(i=0; zName[i]; i++){
+ if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"';
+ }
+ return sqlite3_keyword_check(zName, i) ? '"' : 0;
+}
+
+/*
+** Construct a fake object name and column list to describe the structure
+** of the view, virtual table, or table valued function zSchema.zName.
+**
+** The returned string comes from sqlite3_mprintf() and should be freed
+** by the caller using sqlite3_free().
+*/
+static char *shellFakeSchema(
+ sqlite3 *db, /* The database connection containing the vtab */
+ const char *zSchema, /* Schema of the database holding the vtab */
+ const char *zName /* The name of the virtual table */
+){
+ sqlite3_stmt *pStmt = 0;
+ char *zSql;
+ ShellText s;
+ char cQuote;
+ char *zDiv = "(";
+ int nRow = 0;
+
+ zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;",
+ zSchema ? zSchema : "main", zName);
+ shell_check_oom(zSql);
+ sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ initText(&s);
+ if( zSchema ){
+ cQuote = quoteChar(zSchema);
+ if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0;
+ appendText(&s, zSchema, cQuote);
+ appendText(&s, ".", 0);
+ }
+ cQuote = quoteChar(zName);
+ appendText(&s, zName, cQuote);
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ const char *zCol = (const char*)sqlite3_column_text(pStmt, 1);
+ nRow++;
+ appendText(&s, zDiv, 0);
+ zDiv = ",";
+ if( zCol==0 ) zCol = "";
+ cQuote = quoteChar(zCol);
+ appendText(&s, zCol, cQuote);
+ }
+ appendText(&s, ")", 0);
+ sqlite3_finalize(pStmt);
+ if( nRow==0 ){
+ freeText(&s);
+ s.zTxt = 0;
+ }
+ return s.zTxt;
+}
+
+/*
+** SQL function: strtod(X)
+**
+** Use the C-library strtod() function to convert string X into a double.
+** Used for comparing the accuracy of SQLite's internal text-to-float conversion
+** routines against the C-library.
+*/
+static void shellStrtod(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ char *z = (char*)sqlite3_value_text(apVal[0]);
+ UNUSED_PARAMETER(nVal);
+ if( z==0 ) return;
+ sqlite3_result_double(pCtx, strtod(z,0));
+}
+
+/*
+** SQL function: dtostr(X)
+**
+** Use the C-library printf() function to convert real value X into a string.
+** Used for comparing the accuracy of SQLite's internal float-to-text conversion
+** routines against the C-library.
+*/
+static void shellDtostr(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ double r = sqlite3_value_double(apVal[0]);
+ int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26;
+ char z[400];
+ if( n<1 ) n = 1;
+ if( n>350 ) n = 350;
+ sprintf(z, "%#+.*e", n, r);
+ sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
+}
+
+/*
+** SQL function: shell_add_schema(S,X)
+**
+** Add the schema name X to the CREATE statement in S and return the result.
+** Examples:
+**
+** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x);
+**
+** Also works on
+**
+** CREATE INDEX
+** CREATE UNIQUE INDEX
+** CREATE VIEW
+** CREATE TRIGGER
+** CREATE VIRTUAL TABLE
+**
+** This UDF is used by the .schema command to insert the schema name of
+** attached databases into the middle of the sqlite_schema.sql field.
+*/
+static void shellAddSchemaName(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ static const char *aPrefix[] = {
+ "TABLE",
+ "INDEX",
+ "UNIQUE INDEX",
+ "VIEW",
+ "TRIGGER",
+ "VIRTUAL TABLE"
+ };
+ int i = 0;
+ const char *zIn = (const char*)sqlite3_value_text(apVal[0]);
+ const char *zSchema = (const char*)sqlite3_value_text(apVal[1]);
+ const char *zName = (const char*)sqlite3_value_text(apVal[2]);
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+ UNUSED_PARAMETER(nVal);
+ if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){
+ for(i=0; i<ArraySize(aPrefix); i++){
+ int n = strlen30(aPrefix[i]);
+ if( cli_strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
+ char *z = 0;
+ char *zFake = 0;
+ if( zSchema ){
+ char cQuote = quoteChar(zSchema);
+ if( cQuote && sqlite3_stricmp(zSchema,"temp")!=0 ){
+ z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8);
+ }else{
+ z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8);
+ }
+ }
+ if( zName
+ && aPrefix[i][0]=='V'
+ && (zFake = shellFakeSchema(db, zSchema, zName))!=0
+ ){
+ if( z==0 ){
+ z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
+ }else{
+ z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
+ }
+ sqlite3_free(zFake);
+ }
+ if( z ){
+ sqlite3_result_text(pCtx, z, -1, sqlite3_free);
+ return;
+ }
+ }
+ }
+ }
+ sqlite3_result_value(pCtx, apVal[0]);
+}
+
+
+/************************* BEGIN PERFORMANCE TIMER *****************************/
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
+#include <sys/time.h>
+#include <sys/resource.h>
+/* VxWorks does not support getrusage() as far as we can determine */
+#if defined(_WRS_KERNEL) || defined(__RTP__)
+struct rusage {
+ struct timeval ru_utime; /* user CPU time used */
+ struct timeval ru_stime; /* system CPU time used */
+};
+#define getrusage(A,B) memset(B,0,sizeof(*B))
+#endif
+
+/* Saved resource information for the beginning of an operation */
+static struct rusage sBegin; /* CPU time at start */
+static sqlite3_int64 iBegin; /* Wall-clock time at start */
+
+/*
+** Begin timing an operation
+*/
+static void beginTimer(ShellState *p){
+ if( p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 ){
+ getrusage(RUSAGE_SELF, &sBegin);
+ iBegin = timeOfDay();
+ }
+}
+
+/* Return the difference of two time_structs in seconds */
+static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
+ return (pEnd->tv_usec - pStart->tv_usec)*0.000001 +
+ (double)(pEnd->tv_sec - pStart->tv_sec);
+}
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+/* Return the time since the start of the timer in
+** seconds. */
+static double elapseTime(ShellState *NotUsed){
+ (void)NotUsed;
+ if( iBegin==0 ) return 0.0;
+ return (timeOfDay() - iBegin)*0.000001;
+}
+#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
+
+/*
+** Print the timing results.
+*/
+static void endTimer(ShellState *p){
+ if( p->enableTimer ){
+ sqlite3_int64 iEnd = timeOfDay();
+ struct rusage sEnd;
+ getrusage(RUSAGE_SELF, &sEnd);
+ p->prevTimer = (iEnd - iBegin)*0.000001;
+ cli_printf(p->out, "Run Time: real %.6f user %.6f sys %.6f\n",
+ p->prevTimer,
+ timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
+ timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
+ if( p->enableTimer==1 ) p->enableTimer = 0;
+ iBegin = 0;
+ }
+}
+
+#define BEGIN_TIMER(X) beginTimer(X)
+#define END_TIMER(X) endTimer(X)
+#define ELAPSE_TIME(X) elapseTime(X)
+#define HAS_TIMER 1
+
+#elif (defined(_WIN32) || defined(WIN32))
+
+/* Saved resource information for the beginning of an operation */
+static HANDLE hProcess;
+static FILETIME ftKernelBegin;
+static FILETIME ftUserBegin;
+static sqlite3_int64 ftWallBegin;
+typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME,
+ LPFILETIME, LPFILETIME);
+static GETPROCTIMES getProcessTimesAddr = NULL;
+
+/*
+** Check to see if we have timer support. Return 1 if necessary
+** support found (or found previously).
+*/
+static int hasTimer(void){
+ if( getProcessTimesAddr ){
+ return 1;
+ } else {
+ /* GetProcessTimes() isn't supported in WIN95 and some other Windows
+ ** versions. See if the version we are running on has it, and if it
+ ** does, save off a pointer to it and the current process handle.
+ */
+ hProcess = GetCurrentProcess();
+ if( hProcess ){
+ HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll"));
+ if( NULL != hinstLib ){
+ getProcessTimesAddr =
+ (GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes");
+ if( NULL != getProcessTimesAddr ){
+ return 1;
+ }
+ FreeLibrary(hinstLib);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Begin timing an operation
+*/
+static void beginTimer(ShellState *p){
+ if( (p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0)
+ && getProcessTimesAddr
+ ){
+ FILETIME ftCreation, ftExit;
+ getProcessTimesAddr(hProcess,&ftCreation,&ftExit,
+ &ftKernelBegin,&ftUserBegin);
+ ftWallBegin = timeOfDay();
+ }
+}
+
+/* Return the difference of two FILETIME structs in seconds */
+static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
+ sqlite_int64 i64Start = *((sqlite_int64 *) pStart);
+ sqlite_int64 i64End = *((sqlite_int64 *) pEnd);
+ return (double) ((i64End - i64Start) / 10000000.0);
+}
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+/* Return the time since the start of the timer in
+** seconds. */
+static double elapseTime(ShellState *NotUsed){
+ (void)NotUsed;
+ if( ftWallBegin==0 ) return 0.0;
+ return (timeOfDay() - ftWallBegin)*0.000001;
+}
+#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
+
+/*
+** Print the timing results.
+*/
+static void endTimer(ShellState *p){
+ if( p->enableTimer && getProcessTimesAddr){
+ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
+ sqlite3_int64 ftWallEnd = timeOfDay();
+ getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
+ p->prevTimer = (ftWallEnd - ftWallBegin)*0.000001;
+#ifdef _WIN64
+ /* microsecond precision on 64-bit windows */
+ cli_printf(p->out, "Run Time: real %.6f user %f sys %f\n",
+ p->prevTimer,
+ timeDiff(&ftUserBegin, &ftUserEnd),
+ timeDiff(&ftKernelBegin, &ftKernelEnd));
+#else
+ /* millisecond precisino on 32-bit windows */
+ cli_printf(p->out, "Run Time: real %.3f user %.3f sys %.3f\n",
+ p->prevTimer,
+ timeDiff(&ftUserBegin, &ftUserEnd),
+ timeDiff(&ftKernelBegin, &ftKernelEnd));
+#endif
+ if( p->enableTimer==1 ) p->enableTimer = 0;
+ ftWallBegin = 0;
+ }
+}
+
+#define BEGIN_TIMER(X) beginTimer(X)
+#define ELAPSE_TIME(X) elapseTime(X)
+#define END_TIMER(X) endTimer(X)
+#define HAS_TIMER hasTimer()
+
+#else
+#define BEGIN_TIMER(X) /* no-op */
+#define ELAPSE_TIME(X) 0.0
+#define END_TIMER(X) /*no-op*/
+#define HAS_TIMER 0
+#endif
+/************************* END PERFORMANCE TIMER ******************************/
+
+/*
+** Clear a display mode, freeing any allocated memory that it
+** contains.
+*/
+static void modeFree(Mode *p){
+ u8 autoExplain = p->autoExplain;
+ free(p->spec.aWidth);
+ free(p->spec.aAlign);
+ free(p->spec.zColumnSep);
+ free(p->spec.zRowSep);
+ free(p->spec.zTableName);
+ free(p->spec.zNull);
+ memset(p, 0, sizeof(*p));
+ p->spec.iVersion = 1;
+ p->autoExplain = autoExplain;
+}
+
+/*
+** Duplicate Mode pSrc into pDest. pDest is assumed to be
+** uninitialized prior to invoking this routine.
+*/
+static void modeDup(Mode *pDest, Mode *pSrc){
+ memcpy(pDest, pSrc, sizeof(*pDest));
+ if( pDest->spec.aWidth ){
+ size_t sz = sizeof(pSrc->spec.aWidth[0]) * pSrc->spec.nWidth;
+ pDest->spec.aWidth = malloc( sz );
+ if( pDest->spec.aWidth ){
+ memcpy(pDest->spec.aWidth, pSrc->spec.aWidth, sz);
+ }else{
+ pDest->spec.nWidth = 0;
+ }
+ }
+ if( pDest->spec.aAlign ){
+ size_t sz = sizeof(pSrc->spec.aAlign[0]) * pSrc->spec.nAlign;
+ pDest->spec.aAlign = malloc( sz );
+ if( pDest->spec.aAlign ){
+ memcpy(pDest->spec.aAlign, pSrc->spec.aAlign, sz);
+ }else{
+ pDest->spec.nAlign = 0;
+ }
+ }
+ if( pDest->spec.zColumnSep ){
+ pDest->spec.zColumnSep = strdup(pSrc->spec.zColumnSep);
+ }
+ if( pDest->spec.zRowSep ){
+ pDest->spec.zRowSep = strdup(pSrc->spec.zRowSep);
+ }
+ if( pDest->spec.zTableName ){
+ pDest->spec.zTableName = strdup(pSrc->spec.zTableName);
+ }
+ if( pDest->spec.zNull ){
+ pDest->spec.zNull = strdup(pSrc->spec.zNull);
+ }
+}
+
+/*
+** Set a string value to a copy of the zNew string in memory
+** obtained from system malloc().
+*/
+static void modeSetStr(char **az, const char *zNew){
+ free(*az);
+ if( zNew==0 ){
+ *az = 0;
+ }else{
+ size_t n = strlen(zNew);
+ *az = malloc( n+1 );
+ if( *az ){
+ memcpy(*az, zNew, n+1 );
+ }
+ }
+}
+
+/*
+** Change the mode to eMode
+*/
+static void modeChange(ShellState *p, unsigned char eMode){
+ const ModeInfo *pI;
+ if( eMode<ArraySize(aModeInfo) ){
+ Mode *pM = &p->mode;
+ pI = &aModeInfo[eMode];
+ pM->eMode = eMode;
+ if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]);
+ if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]);
+ if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]);
+ pM->spec.eText = pI->eText;
+ pM->spec.eBlob = pI->eBlob;
+ if( (pM->mFlags & MFLG_HDR)==0 ){
+ pM->spec.bTitles = pI->bHdr;
+ }
+ pM->spec.eTitle = pI->eHdr;
+ if( pI->mFlg & 0x01 ){
+ pM->spec.bBorder = QRF_No;
+ }else{
+ pM->spec.bBorder = QRF_Auto;
+ }
+ if( pI->mFlg & 0x02 ){
+ pM->spec.bSplitColumn = QRF_Yes;
+ pM->bAutoScreenWidth = 1;
+ }else{
+ pM->spec.bSplitColumn = QRF_No;
+ }
+ }else if( eMode>=MODE_USER && eMode-MODE_USER<p->nSavedModes ){
+ modeFree(&p->mode);
+ modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode);
+ }else if( eMode==MODE_BATCH ){
+ u8 mFlags = p->mode.mFlags;
+ modeFree(&p->mode);
+ modeChange(p, MODE_List);
+ p->mode.mFlags = mFlags;
+ }else if( eMode==MODE_TTY ){
+ u8 mFlags = p->mode.mFlags;
+ modeFree(&p->mode);
+ modeChange(p, MODE_QBox);
+ p->mode.bAutoScreenWidth = 1;
+ p->mode.spec.eText = QRF_TEXT_Relaxed;
+ p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT;
+ p->mode.spec.nLineLimit = DFLT_LINE_LIMIT;
+ p->mode.spec.bTextJsonb = QRF_Yes;
+ p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT;
+ p->mode.spec.nMultiInsert = DFLT_MULTI_INSERT;
+ p->mode.mFlags = mFlags;
+ }
+}
+
+/*
+** Set the mode to the default. It assumed that the mode has
+** already been freed and zeroed prior to calling this routine.
+*/
+static void modeDefault(ShellState *p){
+ p->mode.spec.iVersion = 1;
+ p->mode.autoExplain = 1;
+ if( stdin_is_interactive || stdout_is_console ){
+ modeChange(p, MODE_TTY);
+ }else{
+ modeChange(p, MODE_BATCH);
+ }
+}
+
+/*
+** Find the number of a display mode given its name. Return -1 if
+** the name does not match any mode.
+**
+** Saved modes are also searched if p!=NULL. The number returned
+** for a saved mode is the index into the p->aSavedModes[] array
+** plus MODE_USER.
+**
+** Two special mode names are also available: "batch" and "tty".
+** evaluate to the default mode for batch operation and interactive
+** operation on a TTY, respectively.
+*/
+static int modeFind(ShellState *p, const char *zName){
+ int i;
+ for(i=0; i<ArraySize(aModeInfo); i++){
+ if( cli_strcmp(aModeInfo[i].zName,zName)==0 ) return i;
+ }
+ for(i=0; i<p->nSavedModes; i++){
+ if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+MODE_USER;
+ }
+ if( strcmp(zName,"batch")==0 ) return MODE_BATCH;
+ if( strcmp(zName,"tty")==0 ) return MODE_TTY;
+ return -1;
+}
+
+/*
+** Save or restore the current output mode
+*/
+static void modePush(ShellState *p){
+ if( p->nPopMode==0 ){
+ modeFree(&p->modePrior);
+ modeDup(&p->modePrior,&p->mode);
+ }
+}
+static void modePop(ShellState *p){
+ if( p->modePrior.spec.iVersion>0 ){
+ modeFree(&p->mode);
+ p->mode = p->modePrior;
+ memset(&p->modePrior, 0, sizeof(p->modePrior));
+ }
+}
+
/*
** A callback for the sqlite3_log() interface.
@@ -21887,7 +25052,7 @@ static const char *modeDescr[] = {
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
ShellState *p = (ShellState*)pArg;
if( p->pLog==0 ) return;
- sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
+ cli_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
fflush(p->pLog);
}
@@ -21904,11 +25069,27 @@ static void shellPutsFunc(
){
ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
(void)nVal;
- sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
+ cli_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
sqlite3_result_value(pCtx, apVal[0]);
}
/*
+** Compute the name of the location of an input error in memory
+** obtained from sqlite3_malloc().
+*/
+static char *shellErrorLocation(ShellState *p){
+ char *zLoc;
+ if( p->zErrPrefix ){
+ zLoc = sqlite3_mprintf("%s", p->zErrPrefix);
+ }else if( p->zInFile==0 || strcmp(p->zInFile,"<stdin>")==0){
+ zLoc = sqlite3_mprintf("line %lld:", p->lineno);
+ }else{
+ zLoc = sqlite3_mprintf("%s:%lld:", p->zInFile, p->lineno);
+ }
+ return zLoc;
+}
+
+/*
** If in safe mode, print an error message described by the arguments
** and exit immediately.
*/
@@ -21920,15 +25101,51 @@ static void failIfSafeMode(
if( p->bSafeMode ){
va_list ap;
char *zMsg;
+ char *zLoc = shellErrorLocation(p);
va_start(ap, zErrMsg);
zMsg = sqlite3_vmprintf(zErrMsg, ap);
va_end(ap);
- sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg);
- exit(1);
+ cli_printf(stderr, "%s %s\n", zLoc, zMsg);
+ cli_exit(1);
}
}
/*
+** Issue an error message from a dot-command.
+*/
+static void dotCmdError(
+ ShellState *p, /* Shell state */
+ int iArg, /* Index of argument on which error occurred */
+ const char *zBrief, /* Brief (<20 character) error description */
+ const char *zDetail, /* Error details */
+ ...
+){
+ FILE *out = stderr;
+ char *zLoc = shellErrorLocation(p);
+ if( zBrief!=0 && iArg>=0 && iArg<p->dot.nArg ){
+ int i = p->dot.aiOfst[iArg];
+ int nPrompt = strlen30(zBrief) + 5;
+ cli_printf(out, "%s %s\n", zLoc, p->dot.zOrig);
+ if( i > nPrompt ){
+ cli_printf(out, "%s %*s%s ---^\n", zLoc, 1+i-nPrompt, "", zBrief);
+ }else{
+ cli_printf(out, "%s %*s^--- %s\n", zLoc, i, "", zBrief);
+ }
+ }
+ if( zDetail ){
+ char *zMsg;
+ va_list ap;
+ va_start(ap, zDetail);
+ zMsg = sqlite3_vmprintf(zDetail,ap);
+ va_end(ap);
+ cli_printf(out,"%s %s\n", zLoc, zMsg);
+ sqlite3_free(zMsg);
+ }
+ sqlite3_free(zLoc);
+}
+
+
+/*
** SQL function: edit(VALUE)
** edit(VALUE,EDITOR)
**
@@ -22073,27 +25290,11 @@ edit_func_end:
#endif /* SQLITE_NOHAVE_SYSTEM */
/*
-** Save or restore the current output mode
-*/
-static void outputModePush(ShellState *p){
- p->modePrior = p->mode;
- p->priorShFlgs = p->shellFlgs;
- memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
- memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
-}
-static void outputModePop(ShellState *p){
- p->mode = p->modePrior;
- p->shellFlgs = p->priorShFlgs;
- memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
- memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
-}
-
-/*
** Set output mode to text or binary for Windows.
*/
static void setCrlfMode(ShellState *p){
#ifdef _WIN32
- if( p->crlfMode ){
+ if( p->mode.mFlags & MFLG_CRLF ){
sqlite3_fsetmode(p->out, _O_TEXT);
}else{
sqlite3_fsetmode(p->out, _O_BINARY);
@@ -22104,126 +25305,6 @@ static void setCrlfMode(ShellState *p){
}
/*
-** Output the given string as a hex-encoded blob (eg. X'1234' )
-*/
-static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
- int i;
- unsigned char *aBlob = (unsigned char*)pBlob;
-
- char *zStr = sqlite3_malloc(nBlob*2 + 1);
- shell_check_oom(zStr);
-
- for(i=0; i<nBlob; i++){
- static const char aHex[] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
- };
- zStr[i*2] = aHex[ (aBlob[i] >> 4) ];
- zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ];
- }
- zStr[i*2] = '\0';
-
- sqlite3_fprintf(out, "X'%s'", zStr);
- sqlite3_free(zStr);
-}
-
-/*
-** Output the given string as a quoted string using SQL quoting conventions:
-**
-** (1) Single quotes (') within the string are doubled
-** (2) The whle string is enclosed in '...'
-** (3) Control characters other than \n, \t, and \r\n are escaped
-** using \u00XX notation and if such substitutions occur,
-** the whole string is enclosed in unistr('...') instead of '...'.
-**
-** Step (3) is omitted if the control-character escape mode is OFF.
-**
-** See also: output_quoted_escaped_string() which does the same except
-** that it does not make exceptions for \n, \t, and \r\n in step (3).
-*/
-static void output_quoted_string(ShellState *p, const char *zInX){
- int i;
- int needUnistr = 0;
- int needDblQuote = 0;
- const unsigned char *z = (const unsigned char*)zInX;
- unsigned char c;
- FILE *out = p->out;
- sqlite3_fsetmode(out, _O_BINARY);
- if( z==0 ) return;
- for(i=0; (c = z[i])!=0; i++){
- if( c=='\'' ){ needDblQuote = 1; }
- if( c>0x1f ) continue;
- if( c=='\t' || c=='\n' ) continue;
- if( c=='\r' && z[i+1]=='\n' ) continue;
- needUnistr = 1;
- break;
- }
- if( (needDblQuote==0 && needUnistr==0)
- || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF)
- ){
- sqlite3_fprintf(out, "'%s'",z);
- }else if( p->eEscMode==SHELL_ESC_OFF ){
- char *zEncoded = sqlite3_mprintf("%Q", z);
- sqlite3_fputs(zEncoded, out);
- sqlite3_free(zEncoded);
- }else{
- if( needUnistr ){
- sqlite3_fputs("unistr('", out);
- }else{
- sqlite3_fputs("'", out);
- }
- while( *z ){
- for(i=0; (c = z[i])!=0; i++){
- if( c=='\'' ) break;
- if( c>0x1f ) continue;
- if( c=='\t' || c=='\n' ) continue;
- if( c=='\r' && z[i+1]=='\n' ) continue;
- break;
- }
- if( i ){
- sqlite3_fprintf(out, "%.*s", i, z);
- z += i;
- }
- if( c==0 ) break;
- if( c=='\'' ){
- sqlite3_fputs("''", out);
- }else{
- sqlite3_fprintf(out, "\\u%04x", c);
- }
- z++;
- }
- if( needUnistr ){
- sqlite3_fputs("')", out);
- }else{
- sqlite3_fputs("'", out);
- }
- }
- setCrlfMode(p);
-}
-
-/*
-** Output the given string as a quoted string using SQL quoting conventions.
-** Additionallly , escape the "\n" and "\r" characters so that they do not
-** get corrupted by end-of-line translation facilities in some operating
-** systems.
-**
-** This is like output_quoted_string() but with the addition of the \r\n
-** escape mechanism.
-*/
-static void output_quoted_escaped_string(ShellState *p, const char *z){
- char *zEscaped;
- sqlite3_fsetmode(p->out, _O_BINARY);
- if( p->eEscMode==SHELL_ESC_OFF ){
- zEscaped = sqlite3_mprintf("%Q", z);
- }else{
- zEscaped = sqlite3_mprintf("%#Q", z);
- }
- sqlite3_fputs(zEscaped, p->out);
- sqlite3_free(zEscaped);
- setCrlfMode(p);
-}
-
-/*
** Find earliest of chars within s specified in zAny.
** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
*/
@@ -22286,13 +25367,14 @@ static void output_c_string(FILE *out, const char *z){
static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */
char ace[3] = "\\?";
char cbsSay;
- sqlite3_fputs(zq, out);
+ cli_puts(zq, out);
+ if( z==0 ) z = "";
while( *z!=0 ){
const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0);
const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask);
const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast;
if( pcEnd > z ){
- sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z);
+ cli_printf(out, "%.*s", (int)(pcEnd-z), z);
}
if( (c = *pcEnd)==0 ) break;
++pcEnd;
@@ -22308,250 +25390,106 @@ static void output_c_string(FILE *out, const char *z){
}
if( cbsSay ){
ace[1] = cbsSay;
- sqlite3_fputs(ace, out);
+ cli_puts(ace, out);
}else if( !isprint(c&0xff) ){
- sqlite3_fprintf(out, "\\%03o", c&0xff);
+ cli_printf(out, "\\%03o", c&0xff);
}else{
ace[1] = (char)c;
- sqlite3_fputs(ace+1, out);
+ cli_puts(ace+1, out);
}
z = pcEnd;
}
- sqlite3_fputs(zq, out);
+ cli_puts(zq, out);
}
-/*
-** Output the given string as quoted according to JSON quoting rules.
+/* Encode input string z[] as a C-language string literal and
+** append it to the sqlite3_str. If z is NULL render and empty string.
*/
-static void output_json_string(FILE *out, const char *z, i64 n){
- unsigned char c;
+static void append_c_string(sqlite3_str *out, const char *z){
+ char c;
static const char *zq = "\"";
static long ctrlMask = ~0L;
- static const char *zDQBS = "\"\\";
- const char *pcLimit;
+ static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */
char ace[3] = "\\?";
char cbsSay;
-
if( z==0 ) z = "";
- pcLimit = z + ((n<0)? strlen(z) : (size_t)n);
- sqlite3_fputs(zq, out);
- while( z < pcLimit ){
- const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z);
- const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask);
- const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast;
+ sqlite3_str_appendall(out,zq);
+ while( *z!=0 ){
+ const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0);
+ const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask);
+ const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast;
if( pcEnd > z ){
- sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z);
- z = pcEnd;
+ sqlite3_str_appendf(out, "%.*s", (int)(pcEnd-z), z);
}
- if( z >= pcLimit ) break;
- c = (unsigned char)*(z++);
+ if( (c = *pcEnd)==0 ) break;
+ ++pcEnd;
switch( c ){
- case '"': case '\\':
+ case '\\': case '"':
cbsSay = (char)c;
break;
- case '\b': cbsSay = 'b'; break;
- case '\f': cbsSay = 'f'; break;
+ case '\t': cbsSay = 't'; break;
case '\n': cbsSay = 'n'; break;
case '\r': cbsSay = 'r'; break;
- case '\t': cbsSay = 't'; break;
+ case '\f': cbsSay = 'f'; break;
default: cbsSay = 0; break;
}
if( cbsSay ){
ace[1] = cbsSay;
- sqlite3_fputs(ace, out);
- }else if( c<=0x1f || c>=0x7f ){
- sqlite3_fprintf(out, "\\u%04x", c);
+ sqlite3_str_appendall(out,ace);
+ }else if( !isprint(c&0xff) ){
+ sqlite3_str_appendf(out, "\\%03o", c&0xff);
}else{
ace[1] = (char)c;
- sqlite3_fputs(ace+1, out);
- }
- }
- sqlite3_fputs(zq, out);
-}
-
-/*
-** Escape the input string if it is needed and in accordance with
-** eEscMode.
-**
-** Escaping is needed if the string contains any control characters
-** other than \t, \n, and \r\n
-**
-** If no escaping is needed (the common case) then set *ppFree to NULL
-** and return the original string. If escapingn is needed, write the
-** escaped string into memory obtained from sqlite3_malloc64() or the
-** equivalent, and return the new string and set *ppFree to the new string
-** as well.
-**
-** The caller is responsible for freeing *ppFree if it is non-NULL in order
-** to reclaim memory.
-*/
-static const char *escapeOutput(
- ShellState *p,
- const char *zInX,
- char **ppFree
-){
- i64 i, j;
- i64 nCtrl = 0;
- unsigned char *zIn;
- unsigned char c;
- unsigned char *zOut;
-
-
- /* No escaping if disabled */
- if( p->eEscMode==SHELL_ESC_OFF ){
- *ppFree = 0;
- return zInX;
- }
-
- /* Count the number of control characters in the string. */
- zIn = (unsigned char*)zInX;
- for(i=0; (c = zIn[i])!=0; i++){
- if( c<=0x1f
- && c!='\t'
- && c!='\n'
- && (c!='\r' || zIn[i+1]!='\n')
- ){
- nCtrl++;
- }
- }
- if( nCtrl==0 ){
- *ppFree = 0;
- return zInX;
- }
- if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2;
- zOut = sqlite3_malloc64( i + nCtrl + 1 );
- shell_check_oom(zOut);
- for(i=j=0; (c = zIn[i])!=0; i++){
- if( c>0x1f
- || c=='\t'
- || c=='\n'
- || (c=='\r' && zIn[i+1]=='\n')
- ){
- continue;
- }
- if( i>0 ){
- memcpy(&zOut[j], zIn, i);
- j += i;
+ sqlite3_str_appendall(out, ace+1);
}
- zIn += i+1;
- i = -1;
- switch( p->eEscMode ){
- case SHELL_ESC_SYMBOL:
- zOut[j++] = 0xe2;
- zOut[j++] = 0x90;
- zOut[j++] = 0x80+c;
- break;
- case SHELL_ESC_ASCII:
- zOut[j++] = '^';
- zOut[j++] = 0x40+c;
- break;
- }
- }
- if( i>0 ){
- memcpy(&zOut[j], zIn, i);
- j += i;
+ z = pcEnd;
}
- zOut[j] = 0;
- *ppFree = (char*)zOut;
- return (char*)zOut;
+ sqlite3_str_appendall(out, zq);
}
/*
-** Output the given string with characters that are special to
-** HTML escaped.
+** This routine runs when the user presses Ctrl-C
*/
-static void output_html_string(FILE *out, const char *z){
- int i;
- if( z==0 ) z = "";
- while( *z ){
- for(i=0; z[i]
- && z[i]!='<'
- && z[i]!='&'
- && z[i]!='>'
- && z[i]!='\"'
- && z[i]!='\'';
- i++){}
- if( i>0 ){
- sqlite3_fprintf(out, "%.*s",i,z);
- }
- if( z[i]=='<' ){
- sqlite3_fputs("&lt;", out);
- }else if( z[i]=='&' ){
- sqlite3_fputs("&amp;", out);
- }else if( z[i]=='>' ){
- sqlite3_fputs("&gt;", out);
- }else if( z[i]=='\"' ){
- sqlite3_fputs("&quot;", out);
- }else if( z[i]=='\'' ){
- sqlite3_fputs("&#39;", out);
- }else{
- break;
- }
- z += i + 1;
- }
+static void interrupt_handler(int NotUsed){
+ UNUSED_PARAMETER(NotUsed);
+ if( ++seenInterrupt>1 ) cli_exit(1);
+ if( globalDb ) sqlite3_interrupt(globalDb);
}
-/*
-** If a field contains any character identified by a 1 in the following
-** array, then the string must be quoted for CSV.
+/* Try to determine the screen width. Use the default if unable.
*/
-static const char needCsvQuote[] = {
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-};
-
-/*
-** Output a single term of CSV. Actually, p->colSeparator is used for
-** the separator, which may or may not be a comma. p->nullValue is
-** the null value. Strings are quoted if necessary. The separator
-** is only issued if bSep is true.
-*/
-static void output_csv(ShellState *p, const char *z, int bSep){
- if( z==0 ){
- sqlite3_fprintf(p->out, "%s",p->nullValue);
+int shellScreenWidth(void){
+ if( stdout_tty_width>0 ){
+ return stdout_tty_width;
}else{
- unsigned i;
- for(i=0; z[i]; i++){
- if( needCsvQuote[((unsigned char*)z)[i]] ){
- i = 0;
- break;
- }
+#if defined(TIOCGSIZE)
+ struct ttysize ts;
+ if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0
+ || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0
+ || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0
+ ){
+ return ts.ts_cols;
}
- if( i==0 || strstr(z, p->colSeparator)!=0 ){
- char *zQuoted = sqlite3_mprintf("\"%w\"", z);
- shell_check_oom(zQuoted);
- sqlite3_fputs(zQuoted, p->out);
- sqlite3_free(zQuoted);
- }else{
- sqlite3_fputs(z, p->out);
+#elif defined(TIOCGWINSZ)
+ struct winsize ws;
+ if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0
+ || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0
+ || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0
+ ){
+ return ws.ws_col;
}
+#elif defined(_WIN32)
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)
+ || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi)
+ || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi)
+ ){
+ return csbi.srWindow.Right - csbi.srWindow.Left + 1;
+ }
+#endif
+#define DEFAULT_SCREEN_WIDTH 80
+ return DEFAULT_SCREEN_WIDTH;
}
- if( bSep ){
- sqlite3_fputs(p->colSeparator, p->out);
- }
-}
-
-/*
-** This routine runs when the user presses Ctrl-C
-*/
-static void interrupt_handler(int NotUsed){
- UNUSED_PARAMETER(NotUsed);
- if( ++seenInterrupt>1 ) exit(1);
- if( globalDb ) sqlite3_interrupt(globalDb);
}
#if (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
@@ -22587,6 +25525,7 @@ static int safeModeAuth(
"fts3_tokenizer",
"load_extension",
"readfile",
+ "realpath",
"writefile",
"zipfile",
"zipfile_cds",
@@ -22649,23 +25588,23 @@ static int shellAuth(
az[1] = zA2;
az[2] = zA3;
az[3] = zA4;
- sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]);
+ cli_printf(p->out, "authorizer: %s", azAction[op]);
for(i=0; i<4; i++){
- sqlite3_fputs(" ", p->out);
+ cli_puts(" ", p->out);
if( az[i] ){
output_c_string(p->out, az[i]);
}else{
- sqlite3_fputs("NULL", p->out);
+ cli_puts("NULL", p->out);
}
}
- sqlite3_fputs("\n", p->out);
+ cli_puts("\n", p->out);
if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
#endif
/*
-** Print a schema statement. Part of MODE_Semi and MODE_Pretty output.
+** Print a schema statement. This is helper routine to dump_callbac().
**
** This routine converts some CREATE TABLE statements for shadow tables
** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements.
@@ -22696,18 +25635,12 @@ static void printSchemaLine(FILE *out, const char *z, const char *zTail){
}
}
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
- sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
+ cli_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
}else{
- sqlite3_fprintf(out, "%s%s", z, zTail);
+ cli_printf(out, "%s%s", z, zTail);
}
sqlite3_free(zToFree);
}
-static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){
- char c = z[n];
- z[n] = 0;
- printSchemaLine(out, z, zTail);
- z[n] = c;
-}
/*
** Return true if string z[] has nothing but whitespace and comments to the
@@ -22725,95 +25658,130 @@ static int wsToEol(const char *z){
}
/*
-** Add a new entry to the EXPLAIN QUERY PLAN data
+** SQL Function: shell_format_schema(SQL,FLAGS)
+**
+** This function is internally by the CLI to assist with the
+** ".schema", ".fullschema", and ".dump" commands. The first
+** argument is the value from sqlite_schema.sql. The value returned
+** is a modification of the input that can actually be run as SQL
+** to recreate the schema object.
+**
+** When FLAGS is zero, the only changes is to append ";". If the
+** 0x01 bit of FLAGS is set, then transformations are made to implement
+** ".schema --indent".
*/
-static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
- EQPGraphRow *pNew;
- i64 nText;
- if( zText==0 ) return;
- nText = strlen(zText);
- if( p->autoEQPtest ){
- sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
+static void shellFormatSchema(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ int flags; /* Value of 2nd parameter */
+ const char *zSql; /* Value of 1st parameter */
+ int nSql; /* Bytes of text in zSql[] */
+ sqlite3_str *pOut; /* Output buffer */
+ char *z; /* Writable copy of zSql */
+ int i, j; /* Loop counters */
+ int nParen = 0;
+ char cEnd = 0;
+ char c;
+ int nLine = 0;
+ int isIndex;
+ int isWhere = 0;
+
+ assert( nVal==2 );
+ pOut = sqlite3_str_new(sqlite3_context_db_handle(pCtx));
+ nSql = sqlite3_value_bytes(apVal[0]);
+ zSql = (const char*)sqlite3_value_text(apVal[0]);
+ if( zSql==0 || zSql[0]==0 ) goto shellFormatSchema_finish;
+ flags = sqlite3_value_int(apVal[1]);
+ if( (flags & 0x01)==0 ){
+ sqlite3_str_append(pOut, zSql, nSql);
+ sqlite3_str_append(pOut, ";", 1);
+ goto shellFormatSchema_finish;
}
- pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
- shell_check_oom(pNew);
- pNew->iEqpId = iEqpId;
- pNew->iParentId = p2;
- memcpy(pNew->zText, zText, nText+1);
- pNew->pNext = 0;
- if( p->sGraph.pLast ){
- p->sGraph.pLast->pNext = pNew;
- }else{
- p->sGraph.pRow = pNew;
+ if( sqlite3_strlike("CREATE VIEW%", zSql, 0)==0
+ || sqlite3_strlike("CREATE TRIG%", zSql, 0)==0
+ ){
+ sqlite3_str_append(pOut, zSql, nSql);
+ sqlite3_str_append(pOut, ";", 1);
+ goto shellFormatSchema_finish;
}
- p->sGraph.pLast = pNew;
-}
-
-/*
-** Free and reset the EXPLAIN QUERY PLAN data that has been collected
-** in p->sGraph.
-*/
-static void eqp_reset(ShellState *p){
- EQPGraphRow *pRow, *pNext;
- for(pRow = p->sGraph.pRow; pRow; pRow = pNext){
- pNext = pRow->pNext;
- sqlite3_free(pRow);
+ isIndex = sqlite3_strlike("CREATE INDEX%", zSql, 0)==0
+ || sqlite3_strlike("CREATE UNIQUE INDEX%", zSql, 0)==0;
+ z = sqlite3_mprintf("%s", zSql);
+ if( z==0 ){
+ sqlite3_str_free(pOut);
+ sqlite3_result_error_nomem(pCtx);
+ return;
}
- memset(&p->sGraph, 0, sizeof(p->sGraph));
-}
-
-/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
-** pOld, or return the first such line if pOld is NULL
-*/
-static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){
- EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
- while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
- return pRow;
-}
-
-/* Render a single level of the graph that has iEqpId as its parent. Called
-** recursively to render sublevels.
-*/
-static void eqp_render_level(ShellState *p, int iEqpId){
- EQPGraphRow *pRow, *pNext;
- i64 n = strlen(p->sGraph.zPrefix);
- char *z;
- for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
- pNext = eqp_next_row(p, iEqpId, pRow);
- z = pRow->zText;
- sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix,
- pNext ? "|--" : "`--", z);
- if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){
- memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
- eqp_render_level(p, pRow->iEqpId);
- p->sGraph.zPrefix[n] = 0;
+ j = 0;
+ for(i=0; IsSpace(z[i]); i++){}
+ for(; (c = z[i])!=0; i++){
+ if( IsSpace(c) ){
+ if( z[j-1]=='\r' ) z[j-1] = '\n';
+ if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue;
+ }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){
+ j--;
}
+ z[j++] = c;
}
-}
-
-/*
-** Display and reset the EXPLAIN QUERY PLAN data
-*/
-static void eqp_render(ShellState *p, i64 nCycle){
- EQPGraphRow *pRow = p->sGraph.pRow;
- if( pRow ){
- if( pRow->zText[0]=='-' ){
- if( pRow->pNext==0 ){
- eqp_reset(p);
- return;
+ while( j>0 && IsSpace(z[j-1]) ){ j--; }
+ z[j] = 0;
+ if( strlen30(z)>=79 ){
+ for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */
+ if( c==cEnd ){
+ cEnd = 0;
+ }else if( cEnd!=0){
+ /* No-op */
+ }else if( c=='"' || c=='\'' || c=='`' ){
+ cEnd = c;
+ }else if( c=='[' ){
+ cEnd = ']';
+ }else if( c=='-' && z[i+1]=='-' ){
+ cEnd = '\n';
+ }else if( c=='(' ){
+ nParen++;
+ }else if( c==')' ){
+ nParen--;
+ if( nLine>0 && nParen==0 && j>0 && !isWhere ){
+ sqlite3_str_append(pOut, z, j);
+ sqlite3_str_append(pOut, "\n", 1);
+ j = 0;
+ }
+ }else if( (c=='w' || c=='W')
+ && nParen==0 && isIndex
+ && sqlite3_strnicmp("WHERE",&z[i],5)==0
+ && !IsAlnum(z[i+5]) && z[i+5]!='_' ){
+ isWhere = 1;
+ }else if( isWhere && (c=='A' || c=='a')
+ && nParen==0
+ && sqlite3_strnicmp("AND",&z[i],3)==0
+ && !IsAlnum(z[i+3]) && z[i+3]!='_' ){
+ sqlite3_str_append(pOut, z, j);
+ sqlite3_str_append(pOut, "\n ", 5);
+ j = 0;
+ }
+ z[j++] = c;
+ if( nParen==1 && cEnd==0
+ && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
+ && !isWhere
+ ){
+ if( c=='\n' ) j--;
+ sqlite3_str_append(pOut, z, j);
+ sqlite3_str_append(pOut, "\n ", 3);
+ j = 0;
+ nLine++;
+ while( IsSpace(z[i+1]) ){ i++; }
}
- sqlite3_fprintf(p->out, "%s\n", pRow->zText+3);
- p->sGraph.pRow = pRow->pNext;
- sqlite3_free(pRow);
- }else if( nCycle>0 ){
- sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
- }else{
- sqlite3_fputs("QUERY PLAN\n", p->out);
}
- p->sGraph.zPrefix[0] = 0;
- eqp_render_level(p, 0);
- eqp_reset(p);
+ z[j] = 0;
}
+ sqlite3_str_appendall(pOut, z);
+ sqlite3_str_append(pOut, ";", 1);
+ sqlite3_free(z);
+
+shellFormatSchema_finish:
+ sqlite3_result_text(pCtx, sqlite3_str_finish(pOut), -1, sqlite3_free);
}
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
@@ -22823,494 +25791,27 @@ static void eqp_render(ShellState *p, i64 nCycle){
static int progress_handler(void *pClientData) {
ShellState *p = (ShellState*)pClientData;
p->nProgress++;
+ if( (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0
+ && ELAPSE_TIME(p)>=p->tmProgress
+ ){
+ cli_printf(p->out, "Progress timeout after %.6f seconds\n",
+ ELAPSE_TIME(p));
+ return 1;
+ }
if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){
- sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress);
+ cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0;
return 1;
}
if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
- sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress);
+ cli_printf(p->out, "Progress %u\n", p->nProgress);
}
return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
/*
-** Print N dashes
-*/
-static void print_dashes(FILE *out, int N){
- const char zDash[] = "--------------------------------------------------";
- const int nDash = sizeof(zDash) - 1;
- while( N>nDash ){
- sqlite3_fputs(zDash, out);
- N -= nDash;
- }
- sqlite3_fprintf(out, "%.*s", N, zDash);
-}
-
-/*
-** Print a markdown or table-style row separator using ascii-art
-*/
-static void print_row_separator(
- ShellState *p,
- int nArg,
- const char *zSep
-){
- int i;
- if( nArg>0 ){
- sqlite3_fputs(zSep, p->out);
- print_dashes(p->out, p->actualWidth[0]+2);
- for(i=1; i<nArg; i++){
- sqlite3_fputs(zSep, p->out);
- print_dashes(p->out, p->actualWidth[i]+2);
- }
- sqlite3_fputs(zSep, p->out);
- }
- sqlite3_fputs("\n", p->out);
-}
-
-/*
-** This is the callback routine that the shell
-** invokes for each row of a query result.
-*/
-static int shell_callback(
- void *pArg,
- int nArg, /* Number of result columns */
- char **azArg, /* Text of each result column */
- char **azCol, /* Column names */
- int *aiType /* Column types. Might be NULL */
-){
- int i;
- ShellState *p = (ShellState*)pArg;
-
- if( azArg==0 ) return 0;
- switch( p->cMode ){
- case MODE_Count:
- case MODE_Off: {
- break;
- }
- case MODE_Line: {
- int w = 5;
- if( azArg==0 ) break;
- for(i=0; i<nArg; i++){
- int len = strlen30(azCol[i] ? azCol[i] : "");
- if( len>w ) w = len;
- }
- if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
- for(i=0; i<nArg; i++){
- char *pFree = 0;
- const char *pDisplay;
- pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree);
- sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i],
- pDisplay, p->rowSeparator);
- if( pFree ) sqlite3_free(pFree);
- }
- break;
- }
- case MODE_ScanExp:
- case MODE_Explain: {
- static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13};
- static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 };
- static const int aScanExpWidth[] = {4, 15, 6, 13, 4, 4, 4, 13, 2, 13};
- static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 };
-
- const int *aWidth = aExplainWidth;
- const int *aMap = aExplainMap;
- int nWidth = ArraySize(aExplainWidth);
- int iIndent = 1;
-
- if( p->cMode==MODE_ScanExp ){
- aWidth = aScanExpWidth;
- aMap = aScanExpMap;
- nWidth = ArraySize(aScanExpWidth);
- iIndent = 3;
- }
- if( nArg>nWidth ) nArg = nWidth;
-
- /* If this is the first row seen, print out the headers */
- if( p->cnt++==0 ){
- for(i=0; i<nArg; i++){
- utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]);
- sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out);
- }
- for(i=0; i<nArg; i++){
- print_dashes(p->out, aWidth[i]);
- sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out);
- }
- }
-
- /* If there is no data, exit early. */
- if( azArg==0 ) break;
-
- for(i=0; i<nArg; i++){
- const char *zSep = " ";
- int w = aWidth[i];
- const char *zVal = azArg[ aMap[i] ];
- if( i==nArg-1 ) w = 0;
- if( zVal && strlenChar(zVal)>w ){
- w = strlenChar(zVal);
- zSep = " ";
- }
- if( i==iIndent && p->aiIndent && p->pStmt ){
- if( p->iIndent<p->nIndent ){
- sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
- }
- p->iIndent++;
- }
- utf8_width_print(p->out, w, zVal ? zVal : p->nullValue);
- sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out);
- }
- break;
- }
- case MODE_Semi: { /* .schema and .fullschema output */
- printSchemaLine(p->out, azArg[0], ";\n");
- break;
- }
- case MODE_Pretty: { /* .schema and .fullschema with --indent */
- char *z;
- int j;
- int nParen = 0;
- char cEnd = 0;
- char c;
- int nLine = 0;
- int isIndex;
- int isWhere = 0;
- assert( nArg==1 );
- if( azArg[0]==0 ) break;
- if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
- || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
- ){
- sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
- break;
- }
- isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0
- || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0;
- z = sqlite3_mprintf("%s", azArg[0]);
- shell_check_oom(z);
- j = 0;
- for(i=0; IsSpace(z[i]); i++){}
- for(; (c = z[i])!=0; i++){
- if( IsSpace(c) ){
- if( z[j-1]=='\r' ) z[j-1] = '\n';
- if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue;
- }else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){
- j--;
- }
- z[j++] = c;
- }
- while( j>0 && IsSpace(z[j-1]) ){ j--; }
- z[j] = 0;
- if( strlen30(z)>=79 ){
- for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */
- if( c==cEnd ){
- cEnd = 0;
- }else if( c=='"' || c=='\'' || c=='`' ){
- cEnd = c;
- }else if( c=='[' ){
- cEnd = ']';
- }else if( c=='-' && z[i+1]=='-' ){
- cEnd = '\n';
- }else if( c=='(' ){
- nParen++;
- }else if( c==')' ){
- nParen--;
- if( nLine>0 && nParen==0 && j>0 && !isWhere ){
- printSchemaLineN(p->out, z, j, "\n");
- j = 0;
- }
- }else if( (c=='w' || c=='W')
- && nParen==0 && isIndex
- && sqlite3_strnicmp("WHERE",&z[i],5)==0
- && !IsAlnum(z[i+5]) && z[i+5]!='_' ){
- isWhere = 1;
- }else if( isWhere && (c=='A' || c=='a')
- && nParen==0
- && sqlite3_strnicmp("AND",&z[i],3)==0
- && !IsAlnum(z[i+3]) && z[i+3]!='_' ){
- printSchemaLineN(p->out, z, j, "\n ");
- j = 0;
- }
- z[j++] = c;
- if( nParen==1 && cEnd==0
- && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
- && !isWhere
- ){
- if( c=='\n' ) j--;
- printSchemaLineN(p->out, z, j, "\n ");
- j = 0;
- nLine++;
- while( IsSpace(z[i+1]) ){ i++; }
- }
- }
- z[j] = 0;
- }
- printSchemaLine(p->out, z, ";\n");
- sqlite3_free(z);
- break;
- }
- case MODE_List: {
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; i<nArg; i++){
- char *z = azCol[i];
- char *pFree;
- const char *zOut = escapeOutput(p, z, &pFree);
- sqlite3_fprintf(p->out, "%s%s", zOut,
- i==nArg-1 ? p->rowSeparator : p->colSeparator);
- if( pFree ) sqlite3_free(pFree);
- }
- }
- if( azArg==0 ) break;
- for(i=0; i<nArg; i++){
- char *z = azArg[i];
- char *pFree;
- const char *zOut;
- if( z==0 ) z = p->nullValue;
- zOut = escapeOutput(p, z, &pFree);
- sqlite3_fputs(zOut, p->out);
- if( pFree ) sqlite3_free(pFree);
- sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
- }
- break;
- }
- case MODE_Www:
- case MODE_Html: {
- if( p->cnt==0 && p->cMode==MODE_Www ){
- sqlite3_fputs(
- "</PRE>\n"
- "<TABLE border='1' cellspacing='0' cellpadding='2'>\n"
- ,p->out
- );
- }
- if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){
- sqlite3_fputs("<TR>", p->out);
- for(i=0; i<nArg; i++){
- sqlite3_fputs("<TH>", p->out);
- output_html_string(p->out, azCol[i]);
- sqlite3_fputs("</TH>\n", p->out);
- }
- sqlite3_fputs("</TR>\n", p->out);
- }
- p->cnt++;
- if( azArg==0 ) break;
- sqlite3_fputs("<TR>", p->out);
- for(i=0; i<nArg; i++){
- sqlite3_fputs("<TD>", p->out);
- output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
- sqlite3_fputs("</TD>\n", p->out);
- }
- sqlite3_fputs("</TR>\n", p->out);
- break;
- }
- case MODE_Tcl: {
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; i<nArg; i++){
- output_c_string(p->out, azCol[i] ? azCol[i] : "");
- if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- if( azArg==0 ) break;
- for(i=0; i<nArg; i++){
- output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
- if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_Csv: {
- sqlite3_fsetmode(p->out, _O_BINARY);
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; i<nArg; i++){
- output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- if( nArg>0 ){
- for(i=0; i<nArg; i++){
- output_csv(p, azArg[i], i<nArg-1);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- setCrlfMode(p);
- break;
- }
- case MODE_Insert: {
- if( azArg==0 ) break;
- sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable);
- if( p->showHeader ){
- sqlite3_fputs("(", p->out);
- for(i=0; i<nArg; i++){
- if( i>0 ) sqlite3_fputs(",", p->out);
- if( quoteChar(azCol[i]) ){
- char *z = sqlite3_mprintf("\"%w\"", azCol[i]);
- shell_check_oom(z);
- sqlite3_fputs(z, p->out);
- sqlite3_free(z);
- }else{
- sqlite3_fprintf(p->out, "%s", azCol[i]);
- }
- }
- sqlite3_fputs(")", p->out);
- }
- p->cnt++;
- for(i=0; i<nArg; i++){
- sqlite3_fputs(i>0 ? "," : " VALUES(", p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("NULL", p->out);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p, azArg[i]);
- }else{
- output_quoted_escaped_string(p, azArg[i]);
- }
- }else if( aiType && aiType[i]==SQLITE_INTEGER ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_uint64 ur;
- memcpy(&ur,&r,sizeof(r));
- if( ur==0x7ff0000000000000LL ){
- sqlite3_fputs("9.0e+999", p->out);
- }else if( ur==0xfff0000000000000LL ){
- sqlite3_fputs("-9.0e+999", p->out);
- }else{
- sqlite3_int64 ir = (sqlite3_int64)r;
- if( r==(double)ir ){
- sqlite3_snprintf(50,z,"%lld.0", ir);
- }else{
- sqlite3_snprintf(50,z,"%!.20g", r);
- }
- sqlite3_fputs(z, p->out);
- }
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
- }else if( isNumber(azArg[i], 0) ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p, azArg[i]);
- }else{
- output_quoted_escaped_string(p, azArg[i]);
- }
- }
- sqlite3_fputs(");\n", p->out);
- break;
- }
- case MODE_Json: {
- if( azArg==0 ) break;
- if( p->cnt==0 ){
- sqlite3_fputs("[{", p->out);
- }else{
- sqlite3_fputs(",\n{", p->out);
- }
- p->cnt++;
- for(i=0; i<nArg; i++){
- output_json_string(p->out, azCol[i], -1);
- sqlite3_fputs(":", p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("null", p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_uint64 ur;
- memcpy(&ur,&r,sizeof(r));
- if( ur==0x7ff0000000000000LL ){
- sqlite3_fputs("9.0e+999", p->out);
- }else if( ur==0xfff0000000000000LL ){
- sqlite3_fputs("-9.0e+999", p->out);
- }else{
- sqlite3_snprintf(50,z,"%!.20g", r);
- sqlite3_fputs(z, p->out);
- }
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_json_string(p->out, pBlob, nBlob);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_json_string(p->out, azArg[i], -1);
- }else{
- sqlite3_fputs(azArg[i], p->out);
- }
- if( i<nArg-1 ){
- sqlite3_fputs(",", p->out);
- }
- }
- sqlite3_fputs("}", p->out);
- break;
- }
- case MODE_Quote: {
- if( azArg==0 ) break;
- if( p->cnt==0 && p->showHeader ){
- for(i=0; i<nArg; i++){
- if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
- output_quoted_string(p, azCol[i]);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- p->cnt++;
- for(i=0; i<nArg; i++){
- if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("NULL", p->out);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_quoted_string(p, azArg[i]);
- }else if( aiType && aiType[i]==SQLITE_INTEGER ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_snprintf(50,z,"%!.20g", r);
- sqlite3_fputs(z, p->out);
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
- }else if( isNumber(azArg[i], 0) ){
- sqlite3_fputs(azArg[i], p->out);
- }else{
- output_quoted_string(p, azArg[i]);
- }
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_Ascii: {
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; i<nArg; i++){
- if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
- sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- if( azArg==0 ) break;
- for(i=0; i<nArg; i++){
- if( i>0 ) sqlite3_fputs(p->colSeparator, p->out);
- sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_EQP: {
- eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
- break;
- }
- }
- return 0;
-}
-
-/*
-** This is the callback routine that the SQLite library
-** invokes for each row of a query result.
-*/
-static int callback(void *pArg, int nArg, char **azArg, char **azCol){
- /* since we don't have type info, call the shell_callback with a NULL value */
- return shell_callback(pArg, nArg, azArg, azCol, NULL);
-}
-
-/*
** This is the callback routine from sqlite3_exec() that appends all
** output onto the end of a ShellText object.
*/
@@ -23369,7 +25870,7 @@ static void createSelftestTable(ShellState *p){
"DROP TABLE [_shell$self];"
,0,0,&zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
+ cli_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0);
@@ -23382,28 +25883,13 @@ static void createSelftestTable(ShellState *p){
** table name.
*/
static void set_table_name(ShellState *p, const char *zName){
- int i, n;
- char cQuote;
- char *z;
-
if( p->zDestTable ){
- free(p->zDestTable);
+ sqlite3_free(p->zDestTable);
p->zDestTable = 0;
}
if( zName==0 ) return;
- cQuote = quoteChar(zName);
- n = strlen30(zName);
- if( cQuote ) n += n+2;
- z = p->zDestTable = malloc( n+1 );
- shell_check_oom(z);
- n = 0;
- if( cQuote ) z[n++] = cQuote;
- for(i=0; zName[i]; i++){
- z[n++] = zName[i];
- if( zName[i]==cQuote ) z[n++] = cQuote;
- }
- if( cQuote ) z[n++] = cQuote;
- z[n] = 0;
+ p->zDestTable = sqlite3_mprintf("%s", zName);
+ shell_check_oom(p->zDestTable);
}
/*
@@ -23472,7 +25958,7 @@ static int run_table_dump_query(
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
char *zContext = shell_error_context(zSelect, p->db);
- sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s",
+ cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s",
rc, sqlite3_errmsg(p->db), zContext);
sqlite3_free(zContext);
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
@@ -23482,22 +25968,22 @@ static int run_table_dump_query(
nResult = sqlite3_column_count(pSelect);
while( rc==SQLITE_ROW ){
z = (const char*)sqlite3_column_text(pSelect, 0);
- sqlite3_fprintf(p->out, "%s", z);
+ cli_printf(p->out, "%s", z);
for(i=1; i<nResult; i++){
- sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i));
+ cli_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
}
if( z==0 ) z = "";
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
if( z[0] ){
- sqlite3_fputs("\n;\n", p->out);
+ cli_puts("\n;\n", p->out);
}else{
- sqlite3_fputs(";\n", p->out);
+ cli_puts(";\n", p->out);
}
rc = sqlite3_step(pSelect);
}
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n",
+ cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n",
rc, sqlite3_errmsg(p->db));
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
@@ -23557,7 +26043,7 @@ static void displayLinuxIoStats(FILE *out){
for(i=0; i<ArraySize(aTrans); i++){
int n = strlen30(aTrans[i].zPattern);
if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){
- sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
+ cli_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
break;
}
}
@@ -23589,7 +26075,7 @@ static void displayStatLine(
}else{
sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr);
}
- sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine);
+ cli_printf(out, "%-36s %s\n", zLabel, zLine);
}
/*
@@ -23600,8 +26086,8 @@ static int display_stats(
ShellState *pArg, /* Pointer to ShellState */
int bReset /* True to reset the stats */
){
- int iCur;
- int iHiwtr;
+ int iCur, iHiwtr;
+ sqlite3_int64 iCur64, iHiwtr64;
FILE *out;
if( pArg==0 || pArg->out==0 ) return 0;
out = pArg->out;
@@ -23611,22 +26097,22 @@ static int display_stats(
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
nCol = sqlite3_column_count(pStmt);
- sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol);
+ cli_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
for(i=0; i<nCol; i++){
sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
- sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
+ cli_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
#ifndef SQLITE_OMIT_DECLTYPE
sqlite3_snprintf(30, z+x, "declared type:");
- sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
+ cli_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
#endif
#ifdef SQLITE_ENABLE_COLUMN_METADATA
sqlite3_snprintf(30, z+x, "database name:");
- sqlite3_fprintf(out, "%-36s %s\n", z,
+ cli_printf(out, "%-36s %s\n", z,
sqlite3_column_database_name(pStmt,i));
sqlite3_snprintf(30, z+x, "table name:");
- sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
+ cli_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
sqlite3_snprintf(30, z+x, "origin name:");
- sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i));
+ cli_printf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i));
#endif
}
}
@@ -23634,7 +26120,7 @@ static int display_stats(
if( pArg->statsOn==3 ){
if( pArg->pStmt ){
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset);
- sqlite3_fprintf(out, "VM-steps: %d\n", iCur);
+ cli_printf(out, "VM-steps: %d\n", iCur);
}
return 0;
}
@@ -23663,48 +26149,55 @@ static int display_stats(
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Successful lookaside attempts: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside failures due to size: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside failures due to OOM: %d\n", iHiwtr);
}
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Pager Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache misses: %d\n", iCur);
+ iHiwtr64 = iCur64 = -1;
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
+ 0);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache spills: %d\n", iCur);
+ cli_printf(out,
+ "Temporary data spilled to disk: %lld\n", iCur64);
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
+ 1);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Statement Heap/Lookaside Usage: %d bytes\n", iCur);
}
@@ -23712,33 +26205,33 @@ static int display_stats(
int iHit, iMiss;
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Fullscan Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Autoindex Inserts: %d\n", iCur);
iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT,
bReset);
iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS,
bReset);
if( iHit || iMiss ){
- sqlite3_fprintf(out,
+ cli_printf(out,
"Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss);
}
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Virtual Machine Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Reprepare operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Number of times run: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Memory used by prepared stmt: %d\n", iCur);
}
@@ -23751,267 +26244,6 @@ static int display_stats(
return 0;
}
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
-static int scanStatsHeight(sqlite3_stmt *p, int iEntry){
- int iPid = 0;
- int ret = 1;
- sqlite3_stmt_scanstatus_v2(p, iEntry,
- SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
- );
- while( iPid!=0 ){
- int ii;
- for(ii=0; 1; ii++){
- int iId;
- int res;
- res = sqlite3_stmt_scanstatus_v2(p, ii,
- SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId
- );
- if( res ) break;
- if( iId==iPid ){
- sqlite3_stmt_scanstatus_v2(p, ii,
- SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
- );
- }
- }
- ret++;
- }
- return ret;
-}
-#endif
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
-static void display_explain_scanstats(
- sqlite3 *db, /* Database to query */
- ShellState *pArg /* Pointer to ShellState */
-){
- static const int f = SQLITE_SCANSTAT_COMPLEX;
- sqlite3_stmt *p = pArg->pStmt;
- int ii = 0;
- i64 nTotal = 0;
- int nWidth = 0;
- eqp_reset(pArg);
-
- for(ii=0; 1; ii++){
- const char *z = 0;
- int n = 0;
- if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
- break;
- }
- n = (int)strlen(z) + scanStatsHeight(p, ii)*3;
- if( n>nWidth ) nWidth = n;
- }
- nWidth += 4;
-
- sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal);
- for(ii=0; 1; ii++){
- i64 nLoop = 0;
- i64 nRow = 0;
- i64 nCycle = 0;
- int iId = 0;
- int iPid = 0;
- const char *zo = 0;
- const char *zName = 0;
- char *zText = 0;
- double rEst = 0.0;
-
- if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){
- break;
- }
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
-
- zText = sqlite3_mprintf("%s", zo);
- if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
- char *z = 0;
- if( nCycle>=0 && nTotal>0 ){
- z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z,
- nCycle, ((nCycle*100)+nTotal/2) / nTotal
- );
- }
- if( nLoop>=0 ){
- z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop);
- }
- if( nRow>=0 ){
- z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
- }
-
- if( zName && pArg->scanstatsOn>1 ){
- double rpl = (double)nRow / (double)nLoop;
- z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst);
- }
-
- zText = sqlite3_mprintf(
- "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
- );
- }
-
- eqp_append(pArg, iId, iPid, zText);
- sqlite3_free(zText);
- }
-
- eqp_render(pArg, nTotal);
-}
-#endif
-
-
-/*
-** Parameter azArray points to a zero-terminated array of strings. zStr
-** points to a single nul-terminated string. Return non-zero if zStr
-** is equal, according to strcmp(), to any of the strings in the array.
-** Otherwise, return zero.
-*/
-static int str_in_array(const char *zStr, const char **azArray){
- int i;
- for(i=0; azArray[i]; i++){
- if( 0==cli_strcmp(zStr, azArray[i]) ) return 1;
- }
- return 0;
-}
-
-/*
-** If compiled statement pSql appears to be an EXPLAIN statement, allocate
-** and populate the ShellState.aiIndent[] array with the number of
-** spaces each opcode should be indented before it is output.
-**
-** The indenting rules are:
-**
-** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
-** all opcodes that occur between the p2 jump destination and the opcode
-** itself by 2 spaces.
-**
-** * Do the previous for "Return" instructions for when P2 is positive.
-** See tag-20220407a in wherecode.c and vdbe.c.
-**
-** * For each "Goto", if the jump destination is earlier in the program
-** and ends on one of:
-** Yield SeekGt SeekLt RowSetRead Rewind
-** or if the P1 parameter is one instead of zero,
-** then indent all opcodes between the earlier instruction
-** and "Goto" by 2 spaces.
-*/
-static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
- int *abYield = 0; /* True if op is an OP_Yield */
- int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
- int iOp; /* Index of operation in p->aiIndent[] */
-
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
- "Return", 0 };
- const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
- "Rewind", 0 };
- const char *azGoto[] = { "Goto", 0 };
-
- /* The caller guarantees that the leftmost 4 columns of the statement
- ** passed to this function are equivalent to the leftmost 4 columns
- ** of EXPLAIN statement output. In practice the statement may be
- ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */
- assert( sqlite3_column_count(pSql)>=4 );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 0), "addr" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 1), "opcode" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 2), "p1" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 3), "p2" ) );
-
- for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
- int i;
- int iAddr = sqlite3_column_int(pSql, 0);
- const char *zOp = (const char*)sqlite3_column_text(pSql, 1);
- int p1 = sqlite3_column_int(pSql, 2);
- int p2 = sqlite3_column_int(pSql, 3);
-
- /* Assuming that p2 is an instruction address, set variable p2op to the
- ** index of that instruction in the aiIndent[] array. p2 and p2op may be
- ** different if the current instruction is part of a sub-program generated
- ** by an SQL trigger or foreign key. */
- int p2op = (p2 + (iOp-iAddr));
-
- /* Grow the p->aiIndent array as required */
- if( iOp>=nAlloc ){
- nAlloc += 100;
- p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
- shell_check_oom(p->aiIndent);
- abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
- shell_check_oom(abYield);
- }
-
- abYield[iOp] = str_in_array(zOp, azYield);
- p->aiIndent[iOp] = 0;
- p->nIndent = iOp+1;
- if( str_in_array(zOp, azNext) && p2op>0 ){
- for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
- }
- if( str_in_array(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){
- for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
- }
- }
-
- p->iIndent = 0;
- sqlite3_free(abYield);
- sqlite3_reset(pSql);
-}
-
-/*
-** Free the array allocated by explain_data_prepare().
-*/
-static void explain_data_delete(ShellState *p){
- sqlite3_free(p->aiIndent);
- p->aiIndent = 0;
- p->nIndent = 0;
- p->iIndent = 0;
-}
-
-static void exec_prepared_stmt(ShellState*, sqlite3_stmt*);
-
-/*
-** Display scan stats.
-*/
-static void display_scanstats(
- sqlite3 *db, /* Database to query */
- ShellState *pArg /* Pointer to ShellState */
-){
-#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
- UNUSED_PARAMETER(db);
- UNUSED_PARAMETER(pArg);
-#else
- if( pArg->scanstatsOn==3 ){
- const char *zSql =
- " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
- " format('% 6s (%.2f%%)',"
- " CASE WHEN ncycle<100_000 THEN ncycle || ' '"
- " WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'"
- " WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'"
- " ELSE (ncycle/1000_000_000) || 'G' END,"
- " ncycle*100.0/(sum(ncycle) OVER ())"
- " ) AS cycles"
- " FROM bytecode(?)";
-
- int rc = SQLITE_OK;
- sqlite3_stmt *pStmt = 0;
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- if( rc==SQLITE_OK ){
- sqlite3_stmt *pSave = pArg->pStmt;
- pArg->pStmt = pStmt;
- sqlite3_bind_pointer(pStmt, 1, pSave, "stmt-pointer", 0);
-
- pArg->cnt = 0;
- pArg->cMode = MODE_ScanExp;
- explain_data_prepare(pArg, pStmt);
- exec_prepared_stmt(pArg, pStmt);
- explain_data_delete(pArg);
-
- sqlite3_finalize(pStmt);
- pArg->pStmt = pSave;
- }
- }else{
- display_explain_scanstats(db, pArg);
- }
-#endif
-}
-
/*
** Disable and restore .wheretrace and .treetrace/.selecttrace settings.
*/
@@ -24103,6 +26335,38 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
memcpy(zBuf, &zVar[6], szVar-5);
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
}
+ }else if( strcmp(zVar, "$TIMER")==0 ){
+ sqlite3_bind_double(pStmt, i, pArg->prevTimer);
+#ifdef SQLITE_ENABLE_CARRAY
+ }else if( strncmp(zVar, "$carray_", 8)==0 ){
+ static char *azColorNames[] = {
+ "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold",
+ "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon",
+ "navy", "olive", "orange", "pink", "purple", "red", "silver",
+ "tan", "teal", "violet", "white", "yellow"
+ };
+ static int aPrimes[] = {
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
+ 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
+ };
+ /* Special bindings: carray($carray_clr), carray($carray_primes)
+ ** with --unsafe-testing: carray($carray_clr_p,26,'char*'),
+ ** carray($carray_primes_p,26,'int32')
+ */
+ if( strcmp(zVar+8,"clr")==0 ){
+ sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);
+ }else if( strcmp(zVar+8,"primes")==0 ){
+ sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);
+ }else if( strcmp(zVar+8,"clr_p")==0
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
+ sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0);
+ }else if( strcmp(zVar+8,"primes_p")==0
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
+ sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0);
+ }else{
+ sqlite3_bind_null(pStmt, i);
+ }
+#endif
}else{
sqlite3_bind_null(pStmt, i);
}
@@ -24111,581 +26375,7 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
sqlite3_finalize(pQ);
}
-/*
-** UTF8 box-drawing characters. Imagine box lines like this:
-**
-** 1
-** |
-** 4 --+-- 2
-** |
-** 3
-**
-** Each box characters has between 2 and 4 of the lines leading from
-** the center. The characters are here identified by the numbers of
-** their corresponding lines.
-*/
-#define BOX_24 "\342\224\200" /* U+2500 --- */
-#define BOX_13 "\342\224\202" /* U+2502 | */
-#define BOX_23 "\342\224\214" /* U+250c ,- */
-#define BOX_34 "\342\224\220" /* U+2510 -, */
-#define BOX_12 "\342\224\224" /* U+2514 '- */
-#define BOX_14 "\342\224\230" /* U+2518 -' */
-#define BOX_123 "\342\224\234" /* U+251c |- */
-#define BOX_134 "\342\224\244" /* U+2524 -| */
-#define BOX_234 "\342\224\254" /* U+252c -,- */
-#define BOX_124 "\342\224\264" /* U+2534 -'- */
-#define BOX_1234 "\342\224\274" /* U+253c -|- */
-
-/* Draw horizontal line N characters long using unicode box
-** characters
-*/
-static void print_box_line(FILE *out, int N){
- const char zDash[] =
- BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
- BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
- const int nDash = sizeof(zDash) - 1;
- N *= 3;
- while( N>nDash ){
- sqlite3_fputs(zDash, out);
- N -= nDash;
- }
- sqlite3_fprintf(out, "%.*s", N, zDash);
-}
-
-/*
-** Draw a horizontal separator for a MODE_Box table.
-*/
-static void print_box_row_separator(
- ShellState *p,
- int nArg,
- const char *zSep1,
- const char *zSep2,
- const char *zSep3
-){
- int i;
- if( nArg>0 ){
- sqlite3_fputs(zSep1, p->out);
- print_box_line(p->out, p->actualWidth[0]+2);
- for(i=1; i<nArg; i++){
- sqlite3_fputs(zSep2, p->out);
- print_box_line(p->out, p->actualWidth[i]+2);
- }
- sqlite3_fputs(zSep3, p->out);
- }
- sqlite3_fputs("\n", p->out);
-}
-
-/*
-** z[] is a line of text that is to be displayed the .mode box or table or
-** similar tabular formats. z[] might contain control characters such
-** as \n, \t, \f, or \r.
-**
-** Compute characters to display on the first line of z[]. Stop at the
-** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
-** from malloc()) of that first line, which caller should free sometime.
-** Write anything to display on the next line into *pzTail. If this is
-** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
-*/
-static char *translateForDisplayAndDup(
- ShellState *p, /* To access current settings */
- const unsigned char *z, /* Input text to be transformed */
- const unsigned char **pzTail, /* OUT: Tail of the input for next line */
- int mxWidth, /* Max width. 0 means no limit */
- u8 bWordWrap /* If true, avoid breaking mid-word */
-){
- int i; /* Input bytes consumed */
- int j; /* Output bytes generated */
- int k; /* Input bytes to be displayed */
- int n; /* Output column number */
- unsigned char *zOut; /* Output text */
-
- if( z==0 ){
- *pzTail = 0;
- return 0;
- }
- if( mxWidth<0 ) mxWidth = -mxWidth;
- if( mxWidth==0 ) mxWidth = 1000000;
- i = j = n = 0;
- while( n<mxWidth ){
- unsigned char c = z[i];
- if( c>=0xc0 ){
- int u;
- int len = decodeUtf8(&z[i], &u);
- i += len;
- j += len;
- n += cli_wcwidth(u);
- continue;
- }
- if( c>=' ' ){
- n++;
- i++;
- j++;
- continue;
- }
- if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break;
- if( c=='\t' ){
- do{
- n++;
- j++;
- }while( (n&7)!=0 && n<mxWidth );
- i++;
- continue;
- }
- if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){
- i += k;
- j += k;
- }else{
- n++;
- j += 3;
- i++;
- }
- }
- if( n>=mxWidth && bWordWrap ){
- /* Perhaps try to back up to a better place to break the line */
- for(k=i; k>i/2; k--){
- if( IsSpace(z[k-1]) ) break;
- }
- if( k<=i/2 ){
- for(k=i; k>i/2; k--){
- if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
- }
- }
- if( k<=i/2 ){
- k = i;
- }else{
- i = k;
- while( z[i]==' ' ) i++;
- }
- }else{
- k = i;
- }
- if( n>=mxWidth && z[i]>=' ' ){
- *pzTail = &z[i];
- }else if( z[i]=='\r' && z[i+1]=='\n' ){
- *pzTail = z[i+2] ? &z[i+2] : 0;
- }else if( z[i]==0 || z[i+1]==0 ){
- *pzTail = 0;
- }else{
- *pzTail = &z[i+1];
- }
- zOut = malloc( j+1 );
- shell_check_oom(zOut);
- i = j = n = 0;
- while( i<k ){
- unsigned char c = z[i];
- if( c>=0xc0 ){
- int u;
- int len = decodeUtf8(&z[i], &u);
- do{ zOut[j++] = z[i++]; }while( (--len)>0 );
- n += cli_wcwidth(u);
- continue;
- }
- if( c>=' ' ){
- n++;
- zOut[j++] = z[i++];
- continue;
- }
- if( c==0 ) break;
- if( z[i]=='\t' ){
- do{
- n++;
- zOut[j++] = ' ';
- }while( (n&7)!=0 && n<mxWidth );
- i++;
- continue;
- }
- switch( p->eEscMode ){
- case SHELL_ESC_SYMBOL:
- zOut[j++] = 0xe2;
- zOut[j++] = 0x90;
- zOut[j++] = 0x80 + c;
- break;
- case SHELL_ESC_ASCII:
- zOut[j++] = '^';
- zOut[j++] = 0x40 + c;
- break;
- case SHELL_ESC_OFF: {
- int nn;
- if( c==0x1b && (nn = isVt100(&z[i]))>0 ){
- memcpy(&zOut[j], &z[i], nn);
- j += nn;
- i += nn - 1;
- }else{
- zOut[j++] = c;
- }
- break;
- }
- }
- i++;
- }
- zOut[j] = 0;
- return (char*)zOut;
-}
-
-/* Return true if the text string z[] contains characters that need
-** unistr() escaping.
-*/
-static int needUnistr(const unsigned char *z){
- unsigned char c;
- if( z==0 ) return 0;
- while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; }
- return c!=0;
-}
-
-/* Extract the value of the i-th current column for pStmt as an SQL literal
-** value. Memory is obtained from sqlite3_malloc64() and must be freed by
-** the caller.
-*/
-static char *quoted_column(sqlite3_stmt *pStmt, int i){
- switch( sqlite3_column_type(pStmt, i) ){
- case SQLITE_NULL: {
- return sqlite3_mprintf("NULL");
- }
- case SQLITE_INTEGER:
- case SQLITE_FLOAT: {
- return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
- }
- case SQLITE_TEXT: {
- const unsigned char *zText = sqlite3_column_text(pStmt,i);
- return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText);
- }
- case SQLITE_BLOB: {
- int j;
- sqlite3_str *pStr = sqlite3_str_new(0);
- const unsigned char *a = sqlite3_column_blob(pStmt,i);
- int n = sqlite3_column_bytes(pStmt,i);
- sqlite3_str_append(pStr, "x'", 2);
- for(j=0; j<n; j++){
- sqlite3_str_appendf(pStr, "%02x", a[j]);
- }
- sqlite3_str_append(pStr, "'", 1);
- return sqlite3_str_finish(pStr);
- }
- }
- return 0; /* Not reached */
-}
-
-/*
-** Run a prepared statement and output the result in one of the
-** table-oriented formats: MODE_Column, MODE_Markdown, MODE_Table,
-** or MODE_Box.
-**
-** This is different from ordinary exec_prepared_stmt() in that
-** it has to run the entire query and gather the results into memory
-** first, in order to determine column widths, before providing
-** any output.
-*/
-static void exec_prepared_stmt_columnar(
- ShellState *p, /* Pointer to ShellState */
- sqlite3_stmt *pStmt /* Statement to run */
-){
- sqlite3_int64 nRow = 0;
- int nColumn = 0;
- char **azData = 0;
- sqlite3_int64 nAlloc = 0;
- char *abRowDiv = 0;
- const unsigned char *uz;
- const char *z;
- char **azQuoted = 0;
- int rc;
- sqlite3_int64 i, nData;
- int j, nTotal, w, n;
- const char *colSep = 0;
- const char *rowSep = 0;
- const unsigned char **azNextLine = 0;
- int bNextLine = 0;
- int bMultiLineRowExists = 0;
- int bw = p->cmOpts.bWordWrap;
- const char *zEmpty = "";
- const char *zShowNull = p->nullValue;
-
- rc = sqlite3_step(pStmt);
- if( rc!=SQLITE_ROW ) return;
- nColumn = sqlite3_column_count(pStmt);
- if( nColumn==0 ) goto columnar_end;
- nAlloc = nColumn*4;
- if( nAlloc<=0 ) nAlloc = 1;
- azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
- shell_check_oom(azData);
- azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
- shell_check_oom(azNextLine);
- memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
- if( p->cmOpts.bQuote ){
- azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
- shell_check_oom(azQuoted);
- memset(azQuoted, 0, nColumn*sizeof(char*) );
- }
- abRowDiv = sqlite3_malloc64( nAlloc/nColumn );
- shell_check_oom(abRowDiv);
- if( nColumn>p->nWidth ){
- p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
- shell_check_oom(p->colWidth);
- for(i=p->nWidth; i<nColumn; i++) p->colWidth[i] = 0;
- p->nWidth = nColumn;
- p->actualWidth = &p->colWidth[nColumn];
- }
- memset(p->actualWidth, 0, nColumn*sizeof(int));
- for(i=0; i<nColumn; i++){
- w = p->colWidth[i];
- if( w<0 ) w = -w;
- p->actualWidth[i] = w;
- }
- for(i=0; i<nColumn; i++){
- const unsigned char *zNotUsed;
- int wx = p->colWidth[i];
- if( wx==0 ){
- wx = p->cmOpts.iWrap;
- }
- if( wx<0 ) wx = -wx;
- uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
- if( uz==0 ) uz = (u8*)"";
- azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw);
- }
- do{
- int useNextLine = bNextLine;
- bNextLine = 0;
- if( (nRow+2)*nColumn >= nAlloc ){
- nAlloc *= 2;
- azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
- shell_check_oom(azData);
- abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn);
- shell_check_oom(abRowDiv);
- }
- abRowDiv[nRow] = 1;
- nRow++;
- for(i=0; i<nColumn; i++){
- int wx = p->colWidth[i];
- if( wx==0 ){
- wx = p->cmOpts.iWrap;
- }
- if( wx<0 ) wx = -wx;
- if( useNextLine ){
- uz = azNextLine[i];
- if( uz==0 ) uz = (u8*)zEmpty;
- }else if( p->cmOpts.bQuote ){
- assert( azQuoted!=0 );
- sqlite3_free(azQuoted[i]);
- azQuoted[i] = quoted_column(pStmt,i);
- uz = (const unsigned char*)azQuoted[i];
- }else{
- uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
- if( uz==0 ) uz = (u8*)zShowNull;
- }
- azData[nRow*nColumn + i]
- = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw);
- if( azNextLine[i] ){
- bNextLine = 1;
- abRowDiv[nRow-1] = 0;
- bMultiLineRowExists = 1;
- }
- }
- }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
- nTotal = nColumn*(nRow+1);
- for(i=0; i<nTotal; i++){
- z = azData[i];
- if( z==0 ) z = (char*)zEmpty;
- n = strlenChar(z);
- j = i%nColumn;
- if( n>p->actualWidth[j] ) p->actualWidth[j] = n;
- }
- if( seenInterrupt ) goto columnar_end;
- switch( p->cMode ){
- case MODE_Column: {
- colSep = " ";
- rowSep = "\n";
- if( p->showHeader ){
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- if( p->colWidth[i]<0 ) w = -w;
- utf8_width_print(p->out, w, azData[i]);
- sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
- }
- for(i=0; i<nColumn; i++){
- print_dashes(p->out, p->actualWidth[i]);
- sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
- }
- }
- break;
- }
- case MODE_Table: {
- colSep = " | ";
- rowSep = " |\n";
- print_row_separator(p, nColumn, "+");
- sqlite3_fputs("| ", p->out);
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
- azData[i], (w-n+1)/2, "");
- sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
- }
- print_row_separator(p, nColumn, "+");
- break;
- }
- case MODE_Markdown: {
- colSep = " | ";
- rowSep = " |\n";
- sqlite3_fputs("| ", p->out);
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
- azData[i], (w-n+1)/2, "");
- sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
- }
- print_row_separator(p, nColumn, "|");
- break;
- }
- case MODE_Box: {
- colSep = " " BOX_13 " ";
- rowSep = " " BOX_13 "\n";
- print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34);
- sqlite3_fputs(BOX_13 " ", p->out);
- for(i=0; i<nColumn; i++){
- w = p->actualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s%s",
- (w-n)/2, "", azData[i], (w-n+1)/2, "",
- i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
- }
- print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
- break;
- }
- }
- for(i=nColumn, j=0; i<nTotal; i++, j++){
- if( j==0 && p->cMode!=MODE_Column ){
- sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out);
- }
- z = azData[i];
- if( z==0 ) z = p->nullValue;
- w = p->actualWidth[j];
- if( p->colWidth[j]<0 ) w = -w;
- utf8_width_print(p->out, w, z);
- if( j==nColumn-1 ){
- sqlite3_fputs(rowSep, p->out);
- if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){
- if( p->cMode==MODE_Table ){
- print_row_separator(p, nColumn, "+");
- }else if( p->cMode==MODE_Box ){
- print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
- }else if( p->cMode==MODE_Column ){
- sqlite3_fputs("\n", p->out);
- }
- }
- j = -1;
- if( seenInterrupt ) goto columnar_end;
- }else{
- sqlite3_fputs(colSep, p->out);
- }
- }
- if( p->cMode==MODE_Table ){
- print_row_separator(p, nColumn, "+");
- }else if( p->cMode==MODE_Box ){
- print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14);
- }
-columnar_end:
- if( seenInterrupt ){
- sqlite3_fputs("Interrupt\n", p->out);
- }
- nData = (nRow+1)*nColumn;
- for(i=0; i<nData; i++){
- z = azData[i];
- if( z!=zEmpty && z!=zShowNull ) free(azData[i]);
- }
- sqlite3_free(azData);
- sqlite3_free((void*)azNextLine);
- sqlite3_free(abRowDiv);
- if( azQuoted ){
- for(i=0; i<nColumn; i++) sqlite3_free(azQuoted[i]);
- sqlite3_free(azQuoted);
- }
-}
-
-/*
-** Run a prepared statement
-*/
-static void exec_prepared_stmt(
- ShellState *pArg, /* Pointer to ShellState */
- sqlite3_stmt *pStmt /* Statement to run */
-){
- int rc;
- sqlite3_uint64 nRow = 0;
-
- if( pArg->cMode==MODE_Column
- || pArg->cMode==MODE_Table
- || pArg->cMode==MODE_Box
- || pArg->cMode==MODE_Markdown
- ){
- exec_prepared_stmt_columnar(pArg, pStmt);
- return;
- }
-
- /* perform the first step. this will tell us if we
- ** have a result set or not and how wide it is.
- */
- rc = sqlite3_step(pStmt);
- /* if we have a result set... */
- if( SQLITE_ROW == rc ){
- /* allocate space for col name ptr, value ptr, and type */
- int nCol = sqlite3_column_count(pStmt);
- void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
- if( !pData ){
- shell_out_of_memory();
- }else{
- char **azCols = (char **)pData; /* Names of result columns */
- char **azVals = &azCols[nCol]; /* Results */
- int *aiTypes = (int *)&azVals[nCol]; /* Result types */
- int i, x;
- assert(sizeof(int) <= sizeof(char *));
- /* save off ptrs to column names */
- for(i=0; i<nCol; i++){
- azCols[i] = (char *)sqlite3_column_name(pStmt, i);
- }
- do{
- nRow++;
- /* extract the data and data types */
- for(i=0; i<nCol; i++){
- aiTypes[i] = x = sqlite3_column_type(pStmt, i);
- if( x==SQLITE_BLOB
- && pArg
- && (pArg->cMode==MODE_Insert || pArg->cMode==MODE_Quote)
- ){
- azVals[i] = "";
- }else{
- azVals[i] = (char*)sqlite3_column_text(pStmt, i);
- }
- if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
- rc = SQLITE_NOMEM;
- break; /* from for */
- }
- } /* end for */
-
- /* if data and types extracted successfully... */
- if( SQLITE_ROW == rc ){
- /* call the supplied callback with the result row data */
- if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){
- rc = SQLITE_ABORT;
- }else{
- rc = sqlite3_step(pStmt);
- }
- }
- } while( SQLITE_ROW == rc );
- sqlite3_free(pData);
- if( pArg->cMode==MODE_Json ){
- sqlite3_fputs("]\n", pArg->out);
- }else if( pArg->cMode==MODE_Www ){
- sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out);
- }else if( pArg->cMode==MODE_Count ){
- char zBuf[200];
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n",
- nRow, nRow!=1 ? "s" : "");
- printf("%s", zBuf);
- }
- }
- }
-}
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
/*
** This function is called to process SQL if the previous shell command
** was ".expert". It passes the SQL in the second argument directly to
@@ -24736,8 +26426,8 @@ static int expertFinish(
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
- sqlite3_fputs("-- Candidates -----------------------------\n", out);
- sqlite3_fprintf(out, "%s\n", zCand);
+ cli_puts("-- Candidates -----------------------------\n", out);
+ cli_printf(out, "%s\n", zCand);
}
for(i=0; i<nQuery; i++){
const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
@@ -24745,12 +26435,12 @@ static int expertFinish(
const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
if( zIdx==0 ) zIdx = "(no new indexes)\n";
if( bVerbose ){
- sqlite3_fprintf(out,
+ cli_printf(out,
"-- Query %d --------------------------------\n"
"%s\n\n"
,i+1, zSql);
}
- sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP);
+ cli_printf(out, "%s\n%s\n", zIdx, zEQP);
}
}
}
@@ -24785,18 +26475,18 @@ static int expertDotCommand(
}
else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
- sqlite3_fprintf(stderr, "option requires an argument: %s\n", z);
+ cli_printf(stderr, "option requires an argument: %s\n", z);
rc = SQLITE_ERROR;
}else{
iSample = (int)integerValue(azArg[++i]);
if( iSample<0 || iSample>100 ){
- sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]);
+ cli_printf(stderr,"value out of range: %s\n", azArg[i]);
rc = SQLITE_ERROR;
}
}
}
else{
- sqlite3_fprintf(stderr,"unknown option: %s\n", z);
+ cli_printf(stderr,"unknown option: %s\n", z);
rc = SQLITE_ERROR;
}
}
@@ -24804,7 +26494,7 @@ static int expertDotCommand(
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory");
rc = SQLITE_ERROR;
}else{
@@ -24817,7 +26507,16 @@ static int expertDotCommand(
return rc;
}
-#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+#endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */
+
+/*
+** QRF write callback
+*/
+static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){
+ ShellState *pArg = (ShellState*)pX;
+ cli_printf(pArg->out, "%.*s", (int)n, z);
+ return SQLITE_OK;
+}
/*
** Execute a statement or set of statements. Print
@@ -24838,19 +26537,42 @@ static int shell_exec(
int rc2;
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3 *db = pArg->db;
+ unsigned char eStyle;
+ sqlite3_qrf_spec spec;
if( pzErrMsg ){
*pzErrMsg = NULL;
}
+ memcpy(&spec, &pArg->mode.spec, sizeof(spec));
+ spec.xWrite = shellWriteQR;
+ spec.pWriteArg = (void*)pArg;
+ if( pArg->mode.eMode==MODE_Insert && ShellHasFlag(pArg,SHFLG_PreserveRowid) ){
+ spec.bTitles = QRF_SW_On;
+ }
+ /* ,- This is true, but it is omitted
+ ** vvvvvvvvvvvvvvvvvvv ----- to avoid compiler warnings. */
+ assert( /*pArg->mode.eMode>=0 &&*/ pArg->mode.eMode<ArraySize(aModeInfo) );
+ eStyle = aModeInfo[pArg->mode.eMode].eStyle;
+ if( pArg->mode.bAutoScreenWidth ){
+ spec.nScreenWidth = shellScreenWidth();
+ }
+ if( spec.eBlob==QRF_BLOB_Auto ){
+ switch( spec.eText ){
+ case QRF_TEXT_Relaxed: /* fall through */
+ case QRF_TEXT_Sql: spec.eBlob = QRF_BLOB_Sql; break;
+ case QRF_TEXT_Json: spec.eBlob = QRF_BLOB_Json; break;
+ default: spec.eBlob = QRF_BLOB_Text; break;
+ }
+ }
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( pArg->expert.pExpert ){
rc = expertHandleSQL(pArg, zSql, pzErrMsg);
return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
}
#endif
- while( zSql[0] && (SQLITE_OK == rc) ){
+ while( zSql && zSql[0] && (SQLITE_OK == rc) ){
static const char *zStmtSql;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
if( SQLITE_OK != rc ){
@@ -24858,6 +26580,7 @@ static int shell_exec(
*pzErrMsg = save_err_msg(db, "in prepare", rc, zSql);
}
}else{
+ int isExplain;
if( !pStmt ){
/* this happens for a comment or white-space */
zSql = zLeftover;
@@ -24868,80 +26591,58 @@ static int shell_exec(
if( zStmtSql==0 ) zStmtSql = "";
while( IsSpace(zStmtSql[0]) ) zStmtSql++;
- /* save off the prepared statement handle and reset row count */
+ /* save off the prepared statement handle */
if( pArg ){
pArg->pStmt = pStmt;
- pArg->cnt = 0;
}
-
+
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
- if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
- sqlite3_stmt *pExplain;
+ isExplain = sqlite3_stmt_isexplain(pStmt);
+ if( pArg && pArg->mode.autoEQP && isExplain==0 && pArg->dot.nArg==0 ){
int triggerEQP = 0;
+ u8 savedEnableTimer = pArg->enableTimer;
+ pArg->enableTimer = 0;
disable_debug_trace_modes();
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
- if( pArg->autoEQP>=AUTOEQP_trigger ){
+ if( pArg->mode.autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
- pExplain = pStmt;
- sqlite3_reset(pExplain);
- rc = sqlite3_stmt_explain(pExplain, 2);
- if( rc==SQLITE_OK ){
- bind_prepared_stmt(pArg, pExplain);
- while( sqlite3_step(pExplain)==SQLITE_ROW ){
- const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
- int iEqpId = sqlite3_column_int(pExplain, 0);
- int iParentId = sqlite3_column_int(pExplain, 1);
- if( zEQPLine==0 ) zEQPLine = "";
- if( zEQPLine[0]=='-' ) eqp_render(pArg, 0);
- eqp_append(pArg, iEqpId, iParentId, zEQPLine);
- }
- eqp_render(pArg, 0);
- }
- if( pArg->autoEQP>=AUTOEQP_full ){
- /* Also do an EXPLAIN for ".eqp full" mode */
- sqlite3_reset(pExplain);
- rc = sqlite3_stmt_explain(pExplain, 1);
- if( rc==SQLITE_OK ){
- pArg->cMode = MODE_Explain;
- assert( sqlite3_stmt_isexplain(pExplain)==1 );
- bind_prepared_stmt(pArg, pExplain);
- explain_data_prepare(pArg, pExplain);
- exec_prepared_stmt(pArg, pExplain);
- explain_data_delete(pArg);
- }
+ sqlite3_reset(pStmt);
+ spec.eStyle = QRF_STYLE_Auto;
+ sqlite3_stmt_explain(pStmt, 2);
+ sqlite3_format_query_result(pStmt, &spec, 0);
+ if( pArg->mode.autoEQP>=AUTOEQP_full ){
+ sqlite3_reset(pStmt);
+ sqlite3_stmt_explain(pStmt, 1);
+ sqlite3_format_query_result(pStmt, &spec, 0);
}
- if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
+
+ if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
}
sqlite3_reset(pStmt);
sqlite3_stmt_explain(pStmt, 0);
restore_debug_trace_modes();
- }
-
- if( pArg ){
- int bIsExplain = (sqlite3_stmt_isexplain(pStmt)==1);
- pArg->cMode = pArg->mode;
- if( pArg->autoExplain ){
- if( bIsExplain ){
- pArg->cMode = MODE_Explain;
- }
- if( sqlite3_stmt_isexplain(pStmt)==2 ){
- pArg->cMode = MODE_EQP;
- }
- }
-
- /* If the shell is currently in ".explain" mode, gather the extra
- ** data required to add indents to the output.*/
- if( pArg->cMode==MODE_Explain && bIsExplain ){
- explain_data_prepare(pArg, pStmt);
- }
+ pArg->enableTimer = savedEnableTimer;
}
bind_prepared_stmt(pArg, pStmt);
- exec_prepared_stmt(pArg, pStmt);
- explain_data_delete(pArg);
- eqp_render(pArg, 0);
+ if( isExplain && pArg->mode.autoExplain ){
+ spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ }else if( pArg->mode.eMode==MODE_Www ){
+ cli_printf(pArg->out,
+ "</PRE>\n"
+ "<TABLE border='1' cellspacing='0' cellpadding='2'>\n");
+ spec.eStyle = QRF_STYLE_Html;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ cli_printf(pArg->out,
+ "</TABLE>\n"
+ "<PRE>");
+ }else{
+ spec.eStyle = eStyle;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ }
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
@@ -24949,8 +26650,19 @@ static int shell_exec(
}
/* print loop-counters if required */
- if( pArg && pArg->scanstatsOn ){
- display_scanstats(db, pArg);
+ if( pArg && pArg->mode.scanstatsOn ){
+ char *zErr = 0;
+ switch( pArg->mode.scanstatsOn ){
+ case 1: spec.eStyle = QRF_STYLE_Stats; break;
+ case 2: spec.eStyle = QRF_STYLE_StatsEst; break;
+ default: spec.eStyle = QRF_STYLE_StatsVm; break;
+ }
+ sqlite3_reset(pStmt);
+ rc = sqlite3_format_query_result(pStmt, &spec, &zErr);
+ if( rc ){
+ cli_printf(stderr, "Stats query failed: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
}
/* Finalize the statement just executed. If this fails, save a
@@ -25005,7 +26717,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
sqlite3_stmt *pStmt;
char *zSql;
int nCol = 0;
- int nAlloc = 0;
+ i64 nAlloc = 0;
int nPK = 0; /* Number of PRIMARY KEY columns seen */
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
@@ -25019,7 +26731,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
- azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
+ azCol = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
shell_check_oom(azCol);
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
@@ -25108,6 +26820,9 @@ static void toggleSelectOrder(sqlite3 *db){
sqlite3_exec(db, zStmt, 0, 0, 0);
}
+/* Forward reference */
+static int db_int(sqlite3 *db, const char *zSql, ...);
+
/*
** This is a different callback routine used for dumping the database.
** Each row received by this callback consists of a table name,
@@ -25134,9 +26849,23 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
- /* no-op */
+ /* The sqlite_sequence table is repopulated last. Delete content
+ ** in the sqlite_sequence table added by prior repopulations prior to
+ ** repopulating sqlite_sequence itself. But only do this if the
+ ** table is non-empty, because if it is empty the table might not
+ ** have been recreated by prior repopulations. See forum posts:
+ ** 2024-10-13T17:10:01z and 2025-10-29T19:38:43z
+ */
+ if( db_int(p->db, "SELECT count(*) FROM sqlite_sequence")>0 ){
+ if( !p->writableSchema ){
+ cli_puts("PRAGMA writable_schema=ON;\n", p->out);
+ p->writableSchema = 1;
+ }
+ cli_puts("CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq);\n"
+ "DELETE FROM sqlite_sequence;\n", p->out);
+ }
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
- if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
+ if( !dataOnly ) cli_puts("ANALYZE sqlite_schema;\n", p->out);
}else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( dataOnly ){
@@ -25144,7 +26873,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
}else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !p->writableSchema ){
- sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out);
+ cli_puts("PRAGMA writable_schema=ON;\n", p->out);
p->writableSchema = 1;
}
zIns = sqlite3_mprintf(
@@ -25152,7 +26881,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
shell_check_oom(zIns);
- sqlite3_fprintf(p->out, "%s\n", zIns);
+ cli_printf(p->out, "%s\n", zIns);
sqlite3_free(zIns);
return 0;
}else{
@@ -25164,8 +26893,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
ShellText sTable;
char **azCol;
int i;
- char *savedDestTable;
- int savedMode;
+ Mode savedMode;
azCol = tableColumnList(p, zTable);
if( azCol==0 ){
@@ -25208,18 +26936,21 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
appendText(&sSelect, " FROM ", 0);
appendText(&sSelect, zTable, quoteChar(zTable));
- savedDestTable = p->zDestTable;
+
savedMode = p->mode;
- p->zDestTable = sTable.z;
- p->mode = p->cMode = MODE_Insert;
- rc = shell_exec(p, sSelect.z, 0);
+ p->mode.spec.zTableName = (char*)zTable;
+ p->mode.eMode = MODE_Insert;
+ p->mode.spec.eText = QRF_TEXT_Sql;
+ p->mode.spec.eBlob = QRF_BLOB_Sql;
+ p->mode.spec.bTitles = QRF_No;
+ p->mode.spec.nCharLimit = 0;
+ rc = shell_exec(p, sSelect.zTxt, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
- sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
+ cli_puts("/****** CORRUPTION ERROR *******/\n", p->out);
toggleSelectOrder(p->db);
- shell_exec(p, sSelect.z, 0);
+ shell_exec(p, sSelect.zTxt, 0);
toggleSelectOrder(p->db);
}
- p->zDestTable = savedDestTable;
p->mode = savedMode;
freeText(&sTable);
freeText(&sSelect);
@@ -25245,9 +26976,9 @@ static int run_schema_dump_query(
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
- sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
+ cli_puts("/****** CORRUPTION ERROR *******/\n", p->out);
if( zErr ){
- sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr);
+ cli_printf(p->out, "/****** %s ******/\n", zErr);
sqlite3_free(zErr);
zErr = 0;
}
@@ -25256,7 +26987,7 @@ static int run_schema_dump_query(
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
- sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
+ cli_printf(p->out, "/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
@@ -25314,8 +27045,8 @@ static const char *(azHelp[]) = {
".cd DIRECTORY Change the working directory to DIRECTORY",
#endif
".changes on|off Show number of rows changed by SQL",
+ ".check OPTIONS ... Verify the results of a .testcase",
#ifndef SQLITE_SHELL_FIDDLE
- ".check GLOB Fail if output since .testcase does not match",
".clone NEWDB Clone data into NEWDB from the existing database",
#endif
".connection [close] [#] Open or close an auxiliary database connection",
@@ -25349,7 +27080,9 @@ static const char *(azHelp[]) = {
#ifndef SQLITE_SHELL_FIDDLE
".exit ?CODE? Exit this program with return-code CODE",
#endif
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
".expert EXPERIMENTAL. Suggest indexes for queries",
+#endif
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
".filectrl CMD ... Run various sqlite3_file_control() operations",
" --schema SCHEMA Use SCHEMA instead of \"main\"",
@@ -25359,26 +27092,14 @@ static const char *(azHelp[]) = {
".help ?-all? ?PATTERN? Show help text for PATTERN",
#ifndef SQLITE_SHELL_FIDDLE
".import FILE TABLE Import data from FILE into TABLE",
- " Options:",
- " --ascii Use \\037 and \\036 as column and row separators",
- " --csv Use , and \\n as column and row separators",
- " --skip N Skip the first N rows of input",
- " --schema S Target table to be S.TABLE",
- " -v \"Verbose\" - increase auxiliary output",
- " Notes:",
- " * If TABLE does not exist, it is created. The first row of input",
- " determines the column names.",
- " * If neither --csv or --ascii are used, the input mode is derived",
- " from the \".mode\" output mode",
- " * If FILE begins with \"|\" then it is a command that generates the",
- " input text.",
#endif
#ifndef SQLITE_OMIT_TEST_CONTROL
- ",imposter INDEX TABLE Create imposter table TABLE on index INDEX",
+ ".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
#endif
- ".indexes ?TABLE? Show names of indexes",
- " If TABLE is specified, only show indexes for",
- " tables matching TABLE using the LIKE operator.",
+ ".indexes ?PATTERN? Show names of indexes matching PATTERN",
+ " -a|--all Also show system-generated indexes",
+ " --expr Show only expression indexes",
+ " --sys Show only system-generated indexes",
".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
#ifdef SQLITE_ENABLE_IOTRACE
",iotrace FILE Enable I/O diagnostic logging to FILE",
@@ -25396,42 +27117,12 @@ static const char *(azHelp[]) = {
".log on|off Turn logging on or off.",
#endif
".mode ?MODE? ?OPTIONS? Set output mode",
- " MODE is one of:",
- " ascii Columns/rows delimited by 0x1F and 0x1E",
- " box Tables using unicode box-drawing characters",
- " csv Comma-separated values",
- " column Output in columns. (See .width)",
- " html HTML <table> code",
- " insert SQL insert statements for TABLE",
- " json Results in a JSON array",
- " line One value per line",
- " list Values delimited by \"|\"",
- " markdown Markdown table format",
- " qbox Shorthand for \"box --wrap 60 --quote\"",
- " quote Escape answers as for SQL",
- " table ASCII-art table",
- " tabs Tab-separated values",
- " tcl TCL list elements",
- " OPTIONS: (for columnar modes or insert mode):",
- " --escape T ctrl-char escape; T is one of: symbol, ascii, off",
- " --wrap N Wrap output lines to no longer than N characters",
- " --wordwrap B Wrap or not at word boundaries per B (on/off)",
- " --ww Shorthand for \"--wordwrap 1\"",
- " --quote Quote output text as SQL literals",
- " --noquote Do not quote output text",
- " TABLE The name of SQL table used for \"insert\" mode",
#ifndef SQLITE_SHELL_FIDDLE
".nonce STRING Suspend safe mode for one command if nonce matches",
#endif
".nullvalue STRING Use STRING in place of NULL values",
#ifndef SQLITE_SHELL_FIDDLE
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
- " If FILE begins with '|' then open as a pipe",
- " --bom Put a UTF8 byte-order mark at the beginning",
- " -e Send output to the system text editor",
- " --plain Use text/plain output instead of HTML for -w option",
- " -w Send output as HTML to a web browser (same as \".www\")",
- " -x Send output as CSV to a spreadsheet (same as \".excel\")",
/* Note that .open is (partially) available in WASM builds but is
** currently only intended to be used by the fiddle tool, not
** end users, so is "undocumented." */
@@ -25441,23 +27132,22 @@ static const char *(azHelp[]) = {
#endif
#ifndef SQLITE_OMIT_DESERIALIZE
" --deserialize Load into memory using sqlite3_deserialize()",
+#endif
+/*" --exclusive Set the SQLITE_OPEN_EXCLUSIVE flag", UNDOCUMENTED */
+#ifndef SQLITE_OMIT_DESERIALIZE
" --hexdb Load the output of \"dbtotxt\" as an in-memory db",
+#endif
+ " --ifexist Only open if FILE already exists",
+#ifndef SQLITE_OMIT_DESERIALIZE
" --maxsize N Maximum size for --hexdb or --deserialized database",
#endif
" --new Initialize FILE to an empty database",
+ " --normal FILE is an ordinary SQLite database",
" --nofollow Do not follow symbolic links",
" --readonly Open FILE readonly",
" --zip FILE is a ZIP archive",
#ifndef SQLITE_SHELL_FIDDLE
".output ?FILE? Send output to FILE or stdout if FILE is omitted",
- " If FILE begins with '|' then open it as a pipe.",
- " If FILE is 'off' then output is disabled.",
- " Options:",
- " --bom Prefix output with a UTF8 byte-order mark",
- " -e Send output to the system text editor",
- " --plain Use text/plain for -w option",
- " -w Send output to a web browser",
- " -x Send output as CSV to a spreadsheet",
#endif
".parameter CMD ... Manage SQL parameter bindings",
" clear Erase all bindings",
@@ -25473,6 +27163,7 @@ static const char *(azHelp[]) = {
" --once Do no more than one progress interrupt",
" --quiet|-q No output except at interrupts",
" --reset Reset the count for each input and interrupt",
+ " --timeout S Halt after running for S seconds",
#endif
".prompt MAIN CONTINUE Replace the standard prompts",
#ifndef SQLITE_SHELL_FIDDLE
@@ -25500,7 +27191,7 @@ static const char *(azHelp[]) = {
" Options:",
" --init Create a new SELFTEST table",
" -v Verbose output",
- ".separator COL ?ROW? Change the column and row separators",
+ ",separator COL ?ROW? Change the column and row separators",
#if defined(SQLITE_ENABLE_SESSION)
".session ?NAME? CMD ... Create or control sessions",
" Subcommands:",
@@ -25527,7 +27218,7 @@ static const char *(azHelp[]) = {
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE)
".shell CMD ARGS... Run CMD ARGS... in a system shell",
#endif
- ".show Show the current values for various settings",
+ ",show Show the current values for various settings",
".stats ?ARG? Show stats or turn stats on or off",
" off Turn off automatic stat display",
" on Turn on automatic stat display",
@@ -25537,13 +27228,11 @@ static const char *(azHelp[]) = {
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
-#ifndef SQLITE_SHELL_FIDDLE
- ",testcase NAME Begin redirecting output to 'testcase-out.txt'",
-#endif
+ ".testcase NAME Begin a test case.",
",testctrl CMD ... Run various sqlite3_test_control() operations",
" Run \".testctrl\" with no arguments for details",
".timeout MS Try opening locked tables for MS milliseconds",
- ".timer on|off Turn SQL timer on or off",
+ ".timer on|off|once Turn SQL timer on or off.",
#ifndef SQLITE_OMIT_TRACE
".trace ?OPTIONS? Output each SQL statement as it is run",
" FILE Send output to FILE",
@@ -25568,7 +27257,7 @@ static const char *(azHelp[]) = {
".vfsinfo ?AUX? Information about the top-level VFS",
".vfslist List all available VFSes",
".vfsname ?AUX? Print the name of the VFS stack",
- ".width NUM1 NUM2 ... Set minimum column widths for columnar output",
+ ",width NUM1 NUM2 ... Set minimum column widths for columnar output",
" Negative values right-justify",
#ifndef SQLITE_SHELL_FIDDLE
".www Display output of the next command in web browser",
@@ -25576,6 +27265,204 @@ static const char *(azHelp[]) = {
#endif
};
+/**************************************************************
+** "Usage" help text automatically generated from comments */
+static const struct {
+ const char *zCmd; /* Name of the dot-command */
+ const char *zUsage; /* Documentation */
+} aUsage[] = {
+ { ".import",
+"USAGE: .import [OPTIONS] FILE TABLE\n"
+"\n"
+"Import CSV or similar text from FILE into TABLE. If TABLE does\n"
+"not exist, it is created using the first row of FILE as the column\n"
+"names. If FILE begins with \"|\" then it is a command that is run\n"
+"and the output from the command is used as the input data. If\n"
+"FILE begins with \"<<\" followed by a label, then content is read from\n"
+"the script until the first line that matches the label.\n"
+"\n"
+"The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n"
+"rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n"
+"the --ascii option is used.\n"
+"\n"
+"The column and row separators must be single ASCII characters. If\n"
+"multiple characters or a Unicode character are specified for the\n"
+"separators, then only the first byte of the separator is used. Except,\n"
+"if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n"
+"understood as a row separator too.\n"
+"\n"
+"Options:\n"
+" --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n"
+" as column and row separators on input, unless other\n"
+" delimiters are specified using --colsep and/or --rowsep\n"
+" --colsep CHAR Use CHAR as the column separator.\n"
+" --csv Input is standard RFC-4180 CSV.\n"
+" --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.\n"
+" --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.\n"
+" --rowsep CHAR Use CHAR as the row separator.\n"
+" --schema S When creating TABLE, put it in schema S\n"
+" --skip N Ignore the first N rows of input\n"
+" -v Verbose mode\n"
+ },
+ { ".mode",
+"USAGE: .mode [MODE] [OPTIONS]\n"
+"\n"
+"Change the output mode to MODE and/or apply OPTIONS to the output mode.\n"
+"Arguments are processed from left to right. If no arguments, show the\n"
+"current output mode and relevant options.\n"
+"\n"
+"Options:\n"
+" --align STRING Set the alignment of text in columnar modes\n"
+" String consists of characters 'L', 'C', 'R'\n"
+" meaning \"left\", \"centered\", and \"right\", with\n"
+" one letter per column starting from the left.\n"
+" Unspecified alignment defaults to 'L'.\n"
+" --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n"
+" \"json\", or \"size\". Default is \"auto\".\n"
+" --border on|off Show outer border on \"box\" and \"table\" modes.\n"
+" --charlimit N Set the maximum number of output characters to\n"
+" show for any single SQL value to N. Longer values\n"
+" truncated. Zero means \"no limit\".\n"
+" --colsep STRING Use STRING as the column separator\n"
+" --escape ESC Enable/disable escaping of control characters\n"
+" found in the output. ESC can be \"off\", \"ascii\",\n"
+" or \"symbol\".\n"
+" --linelimit N Set the maximum number of output lines to show for\n"
+" any single SQL value to N. Longer values are\n"
+" truncated. Zero means \"no limit\". Only works\n"
+" in \"line\" mode and in columnar modes.\n"
+" --limits L,C,T Shorthand for \"--linelimit L --charlimit C\n"
+" --titlelimit T\". The \",T\" can be omitted in which\n"
+" case the --titlelimit is unchanged. The argument\n"
+" can also be \"off\" to mean \"0,0,0\" or \"on\" to\n"
+" mean \"5,300,20\".\n"
+" --list List available modes\n"
+" --multiinsert N In \"insert\" mode, put multiple rows on a single\n"
+" INSERT statement until the size exceeds N bytes.\n"
+" --null STRING Render SQL NULL values as the given string\n"
+" --once Setting changes to the right are reverted after\n"
+" the next SQL command.\n"
+" --quote ARG Enable/disable quoting of text. ARG can be\n"
+" \"off\", \"on\", \"sql\", \"relaxed\", \"csv\", \"html\",\n"
+" \"tcl\", or \"json\". \"off\" means show the text as-is.\n"
+" \"on\" is an alias for \"sql\".\n"
+" --reset Changes all mode settings back to their default.\n"
+" --rowsep STRING Use STRING as the row separator\n"
+" --sw|--screenwidth N Declare the screen width of the output device\n"
+" to be N characters. An attempt may be made to\n"
+" wrap output text to fit within this limit. Zero\n"
+" means \"no limit\". Or N can be \"auto\" to set the\n"
+" width automatically.\n"
+" --tablename NAME Set the name of the table for \"insert\" mode.\n"
+" --tag NAME Save mode to the left as NAME.\n"
+" --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n"
+" --title ARG Whether or not to show column headers, and if so\n"
+" how to encode them. ARG can be \"off\", \"on\",\n"
+" \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n"
+" --titlelimit N Limit the length of column titles to N characters.\n"
+" -v|--verbose Verbose output\n"
+" --widths LIST Set the columns widths for columnar modes. The\n"
+" argument is a list of integers, one for each\n"
+" column. A \"0\" width means use a dynamic width\n"
+" based on the actual width of data. If there are\n"
+" fewer entries in LIST than columns, \"0\" is used\n"
+" for the unspecified widths.\n"
+" --wordwrap BOOLEAN Enable/disable word wrapping\n"
+" --wrap N Wrap columns wider than N characters\n"
+" --ww Shorthand for \"--wordwrap on\"\n"
+ },
+ { ".output",
+"USAGE: .output [OPTIONS] [FILE]\n"
+"\n"
+"Begin redirecting output to FILE. Or if FILE is omitted, revert\n"
+"to sending output to the console. If FILE begins with \"|\" then\n"
+"the remainder of file is taken as a pipe and output is directed\n"
+"into that pipe. If FILE is \"memory\" then output is captured in an\n"
+"internal memory buffer. If FILE is \"off\" then output is redirected\n"
+"into /dev/null or the equivalent.\n"
+"\n"
+"Options:\n"
+" --bom Prepend a byte-order mark to the output\n"
+" -e Accumulate output in a temporary text file then\n"
+" launch a text editor when the redirection ends.\n"
+" --error-prefix X Use X as the left-margin prefix for error messages.\n"
+" Set to an empty string to restore the default.\n"
+" --keep Keep redirecting output to its current destination.\n"
+" Use this option in combination with --show or\n"
+" with --error-prefix when you do not want to stop\n"
+" a current redirection.\n"
+" --plain Use plain text rather than HTML tables with -w\n"
+" --show Show output text captured by .testcase or by\n"
+" redirecting to \"memory\".\n"
+" -w Show the output in a web browser. Output is\n"
+" written into a temporary HTML file until the\n"
+" redirect ends, then the web browser is launched.\n"
+" Query results are shown as HTML tables, unless\n"
+" the --plain is used too.\n"
+" -x Show the output in a spreadsheet. Output is\n"
+" written to a temp file as CSV then the spreadsheet\n"
+" is launched when\n"
+ },
+ { ".once",
+"USAGE: .once [OPTIONS] FILE ...\n"
+"\n"
+"Write the output for the next line of SQL or the next dot-command into\n"
+"FILE. If FILE begins with \"|\" then it is a program into which output\n"
+"is written. The FILE argument should be omitted if one of the -e, -w,\n"
+"or -x options is used.\n"
+"\n"
+"Options:\n"
+" -e Capture output into a temporary file then bring up\n"
+" a text editor on that temporary file.\n"
+" --plain Use plain text rather than HTML tables with -w\n"
+" -w Capture output into an HTML file then bring up that\n"
+" file in a web browser\n"
+" -x Show the output in a spreadsheet. Output is\n"
+" written to a temp file as CSV then the spreadsheet\n"
+" is launched when\n"
+ },
+ { ".check",
+"USAGE: .check [OPTIONS] PATTERN\n"
+"\n"
+"Verify results of commands since the most recent .testcase command.\n"
+"Restore output to the console, unless --keep is used.\n"
+"\n"
+"If PATTERN starts with \"<<ENDMARK\" then the actual pattern is taken from\n"
+"subsequent lines of text up to the first line that begins with ENDMARK.\n"
+"All pattern lines and the ENDMARK are discarded.\n"
+"\n"
+"Options:\n"
+" --exact Do an exact comparison including leading and\n"
+" trailing whitespace.\n"
+" --glob Treat PATTERN as a GLOB\n"
+" --keep Do not reset the testcase. More .check commands\n"
+" will follow.\n"
+" --notglob Output should not match PATTERN\n"
+" --show Write testcase output to the screen, for debugging.\n"
+ },
+ { ".testcase",
+"USAGE: .testcase [OPTIONS] NAME\n"
+"\n"
+"Start a new test case identified by NAME. All output\n"
+"through the next \".check\" command is captured for comparison. See the\n"
+"\".check\" commandn for additional informatioon.\n"
+"\n"
+"Options:\n"
+" --error-prefix TEXT Change error message prefix text to TEXT\n"
+ },
+};
+
+/*
+** Return a pointer to usage text for zCmd, or NULL if none exists.
+*/
+static const char *findUsage(const char *zCmd){
+ int i;
+ for(i=0; i<ArraySize(aUsage); i++){
+ if( sqlite3_strglob(zCmd, aUsage[i].zCmd)==0 ) return aUsage[i].zUsage;
+ }
+ return 0;
+}
+
/*
** Output help text for commands that match zPattern.
**
@@ -25605,6 +27492,7 @@ static int showHelp(FILE *out, const char *zPattern){
int j = 0;
int n = 0;
char *zPat;
+ const char *zHit = 0;
if( zPattern==0 ){
/* Show just the first line for all help topics */
zPattern = "[a-z]";
@@ -25622,37 +27510,46 @@ static int showHelp(FILE *out, const char *zPattern){
show = 0;
}else if( azHelp[i][0]==',' ){
show = 1;
- sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]);
+ cli_printf(out, ".%s\n", &azHelp[i][1]);
n++;
}else if( show ){
- sqlite3_fprintf(out, "%s\n", azHelp[i]);
+ cli_printf(out, "%s\n", azHelp[i]);
}
}
return n;
}
/* Seek documented commands for which zPattern is an exact prefix */
- zPat = sqlite3_mprintf(".%s*", zPattern);
+ zPat = sqlite3_mprintf(".%s*", zPattern[0]=='.' ? &zPattern[1] : zPattern);
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
- sqlite3_fprintf(out, "%s\n", azHelp[i]);
+ if( zHit ) cli_printf(out, "%s\n", zHit);
+ zHit = azHelp[i];
j = i+1;
n++;
}
}
- sqlite3_free(zPat);
if( n ){
if( n==1 ){
- /* when zPattern is a prefix of exactly one command, then include
- ** the details of that command, which should begin at offset j */
- while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
- j++;
+ const char *zUsage = findUsage(zPat);
+ if( zUsage ){
+ cli_puts(zUsage, out);
+ }else{
+ /* when zPattern is a prefix of exactly one command, then include
+ ** the details of that command, which should begin at offset j */
+ cli_printf(out, "%s\n", zHit);
+ while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
+ cli_printf(out, "%s\n", azHelp[j]);
+ j++;
+ }
}
+ }else{
+ cli_printf(out, "%s\n", zHit);
}
- return n;
}
+ sqlite3_free(zPat);
+ if( n ) return n;
/* Look for documented commands that contain zPattern anywhere.
** Show complete text of all documented commands that match. */
@@ -25665,10 +27562,10 @@ static int showHelp(FILE *out, const char *zPattern){
}
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
+ cli_printf(out, "%s\n", azHelp[j]);
while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
j++;
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
+ cli_printf(out, "%s\n", azHelp[j]);
}
i = j;
n++;
@@ -25679,7 +27576,7 @@ static int showHelp(FILE *out, const char *zPattern){
}
/* Forward reference */
-static int process_input(ShellState *p);
+static int process_input(ShellState *p, const char*);
/*
** Read the content of file zName into memory obtained from sqlite3_malloc64()
@@ -25705,7 +27602,7 @@ static char *readFile(const char *zName, int *pnByte){
if( in==0 ) return 0;
rc = fseek(in, 0, SEEK_END);
if( rc!=0 ){
- sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName);
+ cli_printf(stderr,"Error: '%s' not seekable\n", zName);
fclose(in);
return 0;
}
@@ -25713,7 +27610,7 @@ static char *readFile(const char *zName, int *pnByte){
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
if( pBuf==0 ){
- sqlite3_fputs("Error: out of memory\n", stderr);
+ cli_puts("Error: out of memory\n", stderr);
fclose(in);
return 0;
}
@@ -25721,7 +27618,7 @@ static char *readFile(const char *zName, int *pnByte){
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
- sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName);
+ cli_printf(stderr,"Error: cannot read '%s'\n", zName);
return 0;
}
pBuf[nIn] = 0;
@@ -25778,6 +27675,52 @@ static int session_filter(void *pCtx, const char *zTab){
#endif
/*
+** Return the size of the named file in bytes. Or return a negative
+** number if the file does not exist.
+*/
+static sqlite3_int64 fileSize(const char *zFile){
+#if defined(_WIN32) || defined(WIN32)
+ struct _stat64 x;
+ if( _stat64(zFile, &x)!=0 ) return -1;
+ return (sqlite3_int64)x.st_size;
+#else
+ struct stat x;
+ if( stat(zFile, &x)!=0 ) return -1;
+ return (sqlite3_int64)x.st_size;
+#endif
+}
+
+/*
+** Return true if zFile is an SQLite database.
+**
+** Algorithm:
+** * If the file does not exist -> return false
+** * If the size of the file is not a multiple of 512 -> return false
+** * If sqlite3_open() fails -> return false
+** * if sqlite3_prepare() or sqlite3_step() fails -> return false
+** * Otherwise -> return true
+*/
+static int isDatabaseFile(const char *zFile, int openFlags){
+ sqlite3 *db = 0;
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ sqlite3_int64 sz = fileSize(zFile);
+ if( sz<512 || (sz%512)!=0 ) return 0;
+ if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK
+ && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0)
+ ==SQLITE_OK
+ && sqlite3_step(pStmt)==SQLITE_ROW
+ ){
+ rc = 1;
+ }else{
+ rc = 0;
+ }
+ sqlite3_finalize(pStmt);
+ sqlite3_close(db);
+ return rc;
+}
+
+/*
** Try to deduce the type of file for zName based on its content. Return
** one of the SHELL_OPEN_* constants.
**
@@ -25786,18 +27729,18 @@ static int session_filter(void *pCtx, const char *zTab){
** Otherwise, assume an ordinary database regardless of the filename if
** the type cannot be determined from content.
*/
-int deduceDatabaseType(const char *zName, int dfltZip){
- FILE *f = sqlite3_fopen(zName, "rb");
+int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){
+ FILE *f;
size_t n;
int rc = SHELL_OPEN_UNSPEC;
char zBuf[100];
- if( f==0 ){
- if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
- return SHELL_OPEN_ZIPFILE;
- }else{
- return SHELL_OPEN_NORMAL;
- }
+ if( access(zName,0)!=0 ) goto database_type_by_name;
+ if( isDatabaseFile(zName, openFlags) ){
+ rc = SHELL_OPEN_NORMAL;
}
+ if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL;
+ f = sqlite3_fopen(zName, "rb");
+ if( f==0 ) goto database_type_by_name;
n = fread(zBuf, 16, 1, f);
if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){
fclose(f);
@@ -25819,6 +27762,43 @@ int deduceDatabaseType(const char *zName, int dfltZip){
}
fclose(f);
return rc;
+
+database_type_by_name:
+ if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
+ rc = SHELL_OPEN_ZIPFILE;
+ }else{
+ rc = SHELL_OPEN_NORMAL;
+ }
+ return rc;
+}
+
+/*
+** If the text in z[] is the name of a readable file and that file appears
+** to contain SQL text and/or dot-commands, then return true. If z[] is
+** not a file, or if the file is unreadable, or if the file is a database
+** or anything else that is not SQL text and dot-commands, then return false.
+**
+** If the bLeaveUninit flag is set, then be sure to leave SQLite in an
+** uninitialized state. This means invoking sqlite3_shutdown() after any
+** SQLite API is used.
+**
+** Some amount of guesswork is involved in this decision.
+*/
+static int isScriptFile(const char *z, int bLeaveUninit){
+ sqlite3_int64 sz = fileSize(z);
+ if( sz<=0 ) return 0;
+ if( (sz%512)==0 ){
+ int rc;
+ sqlite3_initialize();
+ rc = isDatabaseFile(z, SQLITE_OPEN_READONLY);
+ if( bLeaveUninit ){
+ sqlite3_shutdown();
+ }
+ if( rc ) return 0; /* Is a database */
+ }
+ if( sqlite3_strlike("%.sql",z,0)==0 ) return 1;
+ if( sqlite3_strlike("%.txt",z,0)==0 ) return 1;
+ return 0;
}
#ifndef SQLITE_OMIT_DESERIALIZE
@@ -25829,11 +27809,11 @@ int deduceDatabaseType(const char *zName, int dfltZip){
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
- int nLine;
- int n = 0;
+ i64 nLine;
+ int n = 0; /* Size of db per first line of hex dump */
+ i64 sz = 0; /* n rounded up to nearest page boundary */
int pgsz = 0;
- int iOffset = 0;
- int j, k;
+ i64 iOffset = 0;
int rc;
FILE *in;
const char *zDbFilename = p->pAuxDb->zDbFilename;
@@ -25842,7 +27822,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
if( zDbFilename ){
in = sqlite3_fopen(zDbFilename, "r");
if( in==0 ){
- sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename);
+ cli_printf(stderr,"cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
@@ -25857,16 +27837,21 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<0 ) goto readHexDb_error;
- if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
- n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
- a = sqlite3_malloc( n ? n : 1 );
- shell_check_oom(a);
- memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
- sqlite3_fputs("invalid pagesize\n", stderr);
+ cli_puts("invalid pagesize\n", stderr);
goto readHexDb_error;
}
+ sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */
+ a = sqlite3_malloc64( sz ? sz : 1 );
+ shell_check_oom(a);
+ memset(a, 0, sz);
for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
+ int j = 0; /* Page number from "| page" line */
+ int k = 0; /* Offset from "| page" line */
+ if( nLine>=2000000000 ){
+ cli_printf(stderr, "input too big\n");
+ goto readHexDb_error;
+ }
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
@@ -25879,14 +27864,14 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
- k = iOffset+j;
- if( k+16<=n && k>=0 ){
+ i64 iOff = iOffset+j;
+ if( iOff+16<=sz && iOff>=0 ){
int ii;
- for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
+ for(ii=0; ii<16; ii++) a[iOff+ii] = x[ii]&0xff;
}
}
}
- *pnData = n;
+ *pnData = sz;
if( in!=p->in ){
fclose(in);
}else{
@@ -25905,7 +27890,7 @@ readHexDb_error:
p->lineno = nLine;
}
sqlite3_free(a);
- sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine);
+ cli_printf(stderr,"Error on line %lld of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
@@ -25953,7 +27938,7 @@ static void shellModuleSchema(
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
- free(zFake);
+ sqlite3_free(zFake);
}
}
@@ -25982,13 +27967,16 @@ static void open_db(ShellState *p, int openFlags){
p->openMode = SHELL_OPEN_NORMAL;
}else{
p->openMode = (u8)deduceDatabaseType(zDbFilename,
- (openFlags & OPEN_DB_ZIPFILE)!=0);
+ (openFlags & OPEN_DB_ZIPFILE)!=0, p->openFlags);
}
}
+ if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
+ if( p->openFlags==0 ) p->openFlags = SQLITE_OPEN_CREATE;
+ p->openFlags |= SQLITE_OPEN_READWRITE;
+ }
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, "apndvfs");
break;
}
case SHELL_OPEN_HEXDB:
@@ -26000,32 +27988,26 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_open(":memory:", &p->db);
break;
}
- case SHELL_OPEN_READONLY: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READONLY|p->openFlags, 0);
- break;
- }
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
+ cli_printf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
- exit(1);
+ cli_exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- sqlite3_fputs("Also: unable to open substitute in-memory database.\n",
+ cli_puts("Also: unable to open substitute in-memory database.\n",
stderr);
- exit(1);
+ cli_exit(1);
}else{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Notice: using substitute in-memory database instead of \"%s\"\n",
zDbFilename);
}
@@ -26048,7 +28030,6 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
- sqlite3_percentile_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
@@ -26104,6 +28085,8 @@ static void open_db(ShellState *p, int openFlags){
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
+ sqlite3_create_function(p->db, "shell_format_schema", 2, SQLITE_UTF8, p,
+ shellFormatSchema, 0, 0);
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
#ifndef SQLITE_NOHAVE_SYSTEM
@@ -26138,7 +28121,7 @@ static void open_db(ShellState *p, int openFlags){
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
- sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc);
+ cli_printf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
@@ -26147,11 +28130,13 @@ static void open_db(ShellState *p, int openFlags){
#endif
}
if( p->db!=0 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
+#endif
sqlite3_db_config(
- p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
+ p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0
);
}
}
@@ -26162,7 +28147,7 @@ static void open_db(ShellState *p, int openFlags){
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
@@ -26335,7 +28320,7 @@ static int booleanValue(const char *zArg){
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
return 0;
}
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
return 0;
}
@@ -26363,18 +28348,18 @@ static void output_file_close(FILE *f){
** recognized and do the right thing. NULL is returned if the output
** filename is "off".
*/
-static FILE *output_file_open(const char *zFile){
+static FILE *output_file_open(ShellState *p, const char *zFile){
FILE *f;
if( cli_strcmp(zFile,"stdout")==0 ){
f = stdout;
}else if( cli_strcmp(zFile, "stderr")==0 ){
f = stderr;
- }else if( cli_strcmp(zFile, "off")==0 ){
+ }else if( cli_strcmp(zFile, "off")==0 || p->bSafeMode ){
f = 0;
}else{
f = sqlite3_fopen(zFile, "w");
if( f==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile);
}
}
return f;
@@ -26427,12 +28412,12 @@ static int sql_trace_callback(
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
- sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
+ cli_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
- sqlite3_fprintf(p->traceOut,
+ cli_printf(p->traceOut,
"%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
break;
}
@@ -26461,9 +28446,11 @@ struct ImportCtx {
const char *zFile; /* Name of the input file */
FILE *in; /* Read the CSV text from this input stream */
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
+ char *zIn; /* Input text */
char *z; /* Accumulated text for a field */
- int n; /* Number of bytes in z */
- int nAlloc; /* Space allocated for z[] */
+ i64 nUsed; /* Bytes of zIn[] used so far */
+ i64 n; /* Number of bytes in z */
+ i64 nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int nRow; /* Number of rows imported */
int nErr; /* Number of errors encountered */
@@ -26471,6 +28458,8 @@ struct ImportCtx {
int cTerm; /* Character that terminated the most recent field */
int cColSep; /* The column separator character. (Usually ",") */
int cRowSep; /* The row separator character. (Usually "\n") */
+ int cQEscape; /* Escape character with "...". 0 for none */
+ int cUQEscape; /* Escape character not with "...". 0 for none */
};
/* Clean up resourced used by an ImportCtx */
@@ -26481,9 +28470,28 @@ static void import_cleanup(ImportCtx *p){
}
sqlite3_free(p->z);
p->z = 0;
+ if( p->zIn ){
+ sqlite3_free(p->zIn);
+ p->zIn = 0;
+ }
+}
+
+/* Read a single character of the .import input text. Return EOF
+** at end-of-file.
+*/
+static int import_getc(ImportCtx *p){
+ if( p->in ){
+ return fgetc(p->in);
+ }else if( p->zIn && p->zIn[p->nUsed]!=0 ){
+ return p->zIn[p->nUsed++];
+ }else{
+ return EOF;
+ }
}
-/* Append a single byte to z[] */
+/* Append a single byte to the field value begin constructed
+** in the p->z[] buffer
+*/
static void import_append_char(ImportCtx *p, int c){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
@@ -26499,8 +28507,8 @@ static void import_append_char(ImportCtx *p, int c){
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc64().
-** + Use p->cSep as the column separator. The default is ",".
-** + Use p->rSep as the row separator. The default is "\n".
+** + Use p->cColSep as the column separator. The default is ",".
+** + Use p->cRowSep as the row separator. The default is "\n".
** + Keep track of the line number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
@@ -26511,7 +28519,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
@@ -26520,10 +28528,17 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int pc, ppc;
int startLine = p->nLine;
int cQuote = c;
+ int cEsc = (u8)p->cQEscape;
pc = ppc = 0;
while( 1 ){
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==rSep ) p->nLine++;
+ if( c==cEsc && cEsc!=0 ){
+ c = import_getc(p);
+ import_append_char(p, c);
+ ppc = pc = 0;
+ continue;
+ }
if( c==cQuote ){
if( pc==cQuote ){
pc = 0;
@@ -26540,11 +28555,11 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
break;
}
if( pc==cQuote && c!='\r' ){
- sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n",
- p->zFile, p->nLine, cQuote);
+ cli_printf(stderr,"%s:%d: unescaped %c character\n",
+ p->zFile, p->nLine, cQuote);
}
if( c==EOF ){
- sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n",
+ cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n",
p->zFile, startLine, cQuote);
p->cTerm = c;
break;
@@ -26556,12 +28571,13 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
}else{
/* If this is the first field being parsed and it begins with the
** UTF-8 BOM (0xEF BB BF) then skip the BOM */
+ int cEsc = p->cUQEscape;
if( (c&0xff)==0xef && p->bNotFirst==0 ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
if( (c&0xff)==0xbb ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
if( (c&0xff)==0xbf ){
p->bNotFirst = 1;
p->n = 0;
@@ -26570,8 +28586,9 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
}
}
while( c!=EOF && c!=cSep && c!=rSep ){
+ if( c==cEsc && cEsc!=0 ) c = import_getc(p);
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
}
if( c==rSep ){
p->nLine++;
@@ -26589,8 +28606,8 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc64().
-** + Use p->cSep as the column separator. The default is "\x1F".
-** + Use p->rSep as the row separator. The default is "\x1E".
+** + Use p->cColSep as the column separator. The default is "\x1F".
+** + Use p->cRowSep as the row separator. The default is "\x1E".
** + Keep track of the row number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
@@ -26601,14 +28618,14 @@ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
}
while( c!=EOF && c!=cSep && c!=rSep ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
}
if( c==rSep ){
p->nLine++;
@@ -26643,7 +28660,7 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
+ cli_printf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_data_xfer;
}
@@ -26660,7 +28677,7 @@ static void tryToCloneData(
memcpy(zInsert+i, ");", 3);
rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
+ cli_printf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert);
goto end_data_xfer;
}
@@ -26696,7 +28713,7 @@ static void tryToCloneData(
} /* End for */
rc = sqlite3_step(pInsert);
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
- sqlite3_fprintf(stderr,"Error %d: %s\n",
+ cli_printf(stderr,"Error %d: %s\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb));
}
sqlite3_reset(pInsert);
@@ -26714,7 +28731,7 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable);
+ cli_printf(stderr,"Warning: cannot step \"%s\" backwards", zTable);
break;
}
} /* End for(k=0...) */
@@ -26751,7 +28768,7 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db),
sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
@@ -26761,10 +28778,10 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){
- sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
+ cli_printf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
@@ -26782,7 +28799,7 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n",
+ cli_printf(stderr,"Error: (%d) %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
@@ -26791,10 +28808,10 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue;
- sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
+ cli_printf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
@@ -26818,12 +28835,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){
int rc;
sqlite3 *newDb = 0;
if( access(zNewDb,0)==0 ){
- sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb);
+ cli_printf(stderr,"File \"%s\" already exists.\n", zNewDb);
return;
}
rc = sqlite3_open(zNewDb, &newDb);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Cannot create output database: %s\n", sqlite3_errmsg(newDb));
}else{
sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
@@ -26842,12 +28859,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){
*/
static void output_redir(ShellState *p, FILE *pfNew){
if( p->out != stdout ){
- sqlite3_fputs("Output already redirected.\n", stderr);
+ cli_puts("Output already redirected.\n", stderr);
}else{
p->out = pfNew;
setCrlfMode(p);
- if( p->mode==MODE_Www ){
- sqlite3_fputs(
+ if( p->mode.eMode==MODE_Www ){
+ cli_puts(
"<!DOCTYPE html>\n"
"<HTML><BODY><PRE>\n",
p->out
@@ -26869,8 +28886,8 @@ static void output_reset(ShellState *p){
pclose(p->out);
#endif
}else{
- if( p->mode==MODE_Www ){
- sqlite3_fputs("</PRE></BODY></HTML>\n", p->out);
+ if( p->mode.eMode==MODE_Www ){
+ cli_puts("</PRE></BODY></HTML>\n", p->out);
}
output_file_close(p->out);
#ifndef SQLITE_NOHAVE_SYSTEM
@@ -26886,7 +28903,7 @@ static void output_reset(ShellState *p){
char *zCmd;
zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
if( system(zCmd) ){
- sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd);
+ cli_printf(stderr,"Failed: [%s]\n", zCmd);
}else{
/* Give the start/open/xdg-open command some time to get
** going before we continue, and potential delete the
@@ -26894,7 +28911,7 @@ static void output_reset(ShellState *p){
sqlite3_sleep(2000);
}
sqlite3_free(zCmd);
- outputModePop(p);
+ modePop(p);
p->doXdgOpen = 0;
}
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
@@ -26902,6 +28919,10 @@ static void output_reset(ShellState *p){
p->outfile[0] = 0;
p->out = stdout;
setCrlfMode(p);
+ if( cli_output_capture ){
+ sqlite3_str_free(cli_output_capture);
+ cli_output_capture = 0;
+ }
}
#else
# define output_redir(SS,pfO)
@@ -26933,10 +28954,13 @@ static int db_int(sqlite3 *db, const char *zSql, ...){
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
static unsigned int get2byteInt(unsigned char *a){
- return (a[0]<<8) + a[1];
+ return ((unsigned int)a[0]<<8) + (unsigned int)a[1];
}
static unsigned int get4byteInt(unsigned char *a){
- return (a[0]<<24) + (a[1]<<16) + (a[2]<<8) + a[3];
+ return ((unsigned int)a[0]<<24)
+ + ((unsigned int)a[1]<<16)
+ + ((unsigned int)a[2]<<8)
+ + (unsigned int)a[3];
}
/*
@@ -26983,7 +29007,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
-1, &pStmt, 0);
if( rc ){
- sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db));
+ cli_printf(stderr,"error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
return 1;
}
@@ -26996,28 +29020,28 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
memcpy(aHdr, pb, 100);
sqlite3_finalize(pStmt);
}else{
- sqlite3_fputs("unable to read database header\n", stderr);
+ cli_puts("unable to read database header\n", stderr);
sqlite3_finalize(pStmt);
return 1;
}
i = get2byteInt(aHdr+16);
if( i==1 ) i = 65536;
- sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i);
- sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
- sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
- sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
+ cli_printf(p->out, "%-20s %d\n", "database page size:", i);
+ cli_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
+ cli_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
+ cli_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
for(i=0; i<ArraySize(aField); i++){
int ofst = aField[i].ofst;
unsigned int val = get4byteInt(aHdr + ofst);
- sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val);
+ cli_printf(p->out, "%-20s %u", aField[i].zName, val);
switch( ofst ){
case 56: {
- if( val==1 ) sqlite3_fputs(" (utf8)", p->out);
- if( val==2 ) sqlite3_fputs(" (utf16le)", p->out);
- if( val==3 ) sqlite3_fputs(" (utf16be)", p->out);
+ if( val==1 ) cli_puts(" (utf8)", p->out);
+ if( val==2 ) cli_puts(" (utf16le)", p->out);
+ if( val==3 ) cli_puts(" (utf16be)", p->out);
}
}
- sqlite3_fputs("\n", p->out);
+ cli_puts("\n", p->out);
}
if( zDb==0 ){
zSchemaTab = sqlite3_mprintf("main.sqlite_schema");
@@ -27028,11 +29052,11 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
}
for(i=0; i<ArraySize(aQuery); i++){
int val = db_int(p->db, aQuery[i].zSql, zSchemaTab);
- sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
+ cli_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
}
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
- sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
+ cli_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
@@ -27073,7 +29097,7 @@ static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
sqlite3_finalize(pStmt);
pStmt = 0;
if( nPage<1 ) goto dbtotxt_error;
- rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
+ rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
zTail = "unk.db";
@@ -27084,10 +29108,15 @@ static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
#if defined(_WIN32)
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
#endif
+ if( zTail==0 ){
+ zTail = zFilename;
+ }else if( zTail[1]!=0 ){
+ zTail++;
+ }
}
zName = strdup(zTail);
shell_check_oom(zName);
- sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
+ cli_printf(p->out, "| size %lld pagesize %d filename %s\n",
nPage*pgSz, pgSz, zName);
sqlite3_finalize(pStmt);
pStmt = 0;
@@ -27103,27 +29132,27 @@ static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
for(j=0; j<16 && aLine[j]==0; j++){}
if( j==16 ) continue;
if( !seenPageLabel ){
- sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz);
+ cli_printf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz);
seenPageLabel = 1;
}
- sqlite3_fprintf(p->out, "| %5d:", i);
- for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
- sqlite3_fprintf(p->out, " ");
+ cli_printf(p->out, "| %5d:", i);
+ for(j=0; j<16; j++) cli_printf(p->out, " %02x", aLine[j]);
+ cli_printf(p->out, " ");
for(j=0; j<16; j++){
unsigned char c = (unsigned char)aLine[j];
- sqlite3_fprintf(p->out, "%c", bShow[c]);
+ cli_printf(p->out, "%c", bShow[c]);
}
- sqlite3_fprintf(p->out, "\n");
+ cli_printf(p->out, "\n");
}
}
sqlite3_finalize(pStmt);
- sqlite3_fprintf(p->out, "| end %s\n", zName);
+ cli_printf(p->out, "| end %s\n", zName);
free(zName);
return 0;
dbtotxt_error:
if( rc ){
- sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db));
+ cli_printf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db));
}
sqlite3_finalize(pStmt);
free(zName);
@@ -27134,7 +29163,7 @@ dbtotxt_error:
** Print the given string as an error message.
*/
static void shellEmitError(const char *zErr){
- sqlite3_fprintf(stderr,"Error: %s\n", zErr);
+ cli_printf(stderr,"Error: %s\n", zErr);
}
/*
** Print the current sqlite3_errmsg() value to stderr and return 1.
@@ -27254,6 +29283,43 @@ static int optionMatch(const char *zStr, const char *zOpt){
}
/*
+** The input zFN is guaranteed to start with "file:" and is thus a URI
+** filename. Extract the actual filename and return a pointer to that
+** filename in spaced obtained from sqlite3_malloc().
+**
+** The caller is responsible for freeing space using sqlite3_free() when
+** it has finished with the filename.
+*/
+static char *shellFilenameFromUri(const char *zFN){
+ char *zOut;
+ int i, j, d1, d2;
+
+ assert( cli_strncmp(zFN,"file:",5)==0 );
+ zOut = sqlite3_mprintf("%s", zFN+5);
+ shell_check_oom(zOut);
+ for(i=j=0; zOut[i]!=0 && zOut[i]!='?'; i++){
+ if( zOut[i]!='%' ){
+ zOut[j++] = zOut[i];
+ continue;
+ }
+ d1 = hexDigitValue(zOut[i+1]);
+ if( d1<0 ){
+ zOut[j] = 0;
+ break;
+ }
+ d2 = hexDigitValue(zOut[i+2]);
+ if( d2<0 ){
+ zOut[j] = 0;
+ break;
+ }
+ zOut[j++] = d1*16 + d2;
+ i += 2;
+ }
+ zOut[j] = 0;
+ return zOut;
+}
+
+/*
** Delete a file.
*/
int shellDeleteFile(const char *zFilename){
@@ -27280,39 +29346,42 @@ static void clearTempFile(ShellState *p){
p->zTempFile = 0;
}
+/* Forward reference */
+static char *find_home_dir(int clearFlag);
+
/*
** Create a new temp file name with the given suffix.
+**
+** Because the classic temp folders like /tmp are no longer
+** accessible to web browsers, for security reasons, create the
+** temp file in the user's home directory.
*/
static void newTempFile(ShellState *p, const char *zSuffix){
- clearTempFile(p);
- sqlite3_free(p->zTempFile);
- p->zTempFile = 0;
- if( p->db ){
- sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile);
- }
- if( p->zTempFile==0 ){
- /* If p->db is an in-memory database then the TEMPFILENAME file-control
- ** will not work and we will need to fallback to guessing */
- char *zTemp;
- sqlite3_uint64 r;
- sqlite3_randomness(sizeof(r), &r);
- zTemp = getenv("TEMP");
- if( zTemp==0 ) zTemp = getenv("TMP");
- if( zTemp==0 ){
+ char *zHome; /* Home directory */
+ int i; /* Loop counter */
+ sqlite3_uint64 r = 0; /* Integer with 64 bits of randomness */
+ char zRand[32]; /* Text string with 160 bits of randomness */
#ifdef _WIN32
- zTemp = "\\tmp";
+ const char cDirSep = '\\';
#else
- zTemp = "/tmp";
+ const char cDirSep = '/';
#endif
- }
- p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix);
- }else{
- p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
+
+ for(i=0; i<31; i++){
+ if( (i%12)==0 ) sqlite3_randomness(sizeof(r),&r);
+ zRand[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[r%36];
+ r /= 36;
}
+ zRand[i] = 0;
+ clearTempFile(p);
+ sqlite3_free(p->zTempFile);
+ p->zTempFile = 0;
+ zHome = find_home_dir(0);
+ p->zTempFile = sqlite3_mprintf("%s%ctemp-%s.%s",
+ zHome,cDirSep,zRand,zSuffix);
shell_check_oom(p->zTempFile);
}
-
/*
** The implementation of SQL scalar function fkey_collate_clause(), used
** by the ".lint fkey-indexes" command. This scalar function is always
@@ -27457,7 +29526,7 @@ static int lintFkeyIndexes(
zIndent = " ";
}
else{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]);
return SQLITE_ERROR;
}
@@ -27502,22 +29571,22 @@ static int lintFkeyIndexes(
if( rc!=SQLITE_OK ) break;
if( res<0 ){
- sqlite3_fputs("Error: internal error", stderr);
+ cli_puts("Error: internal error", stderr);
break;
}else{
if( bGroupByParent
&& (bVerbose || res==0)
&& (zPrev==0 || sqlite3_stricmp(zParent, zPrev))
){
- sqlite3_fprintf(out, "-- Parent table %s\n", zParent);
+ cli_printf(out, "-- Parent table %s\n", zParent);
sqlite3_free(zPrev);
zPrev = sqlite3_mprintf("%s", zParent);
}
if( res==0 ){
- sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget);
+ cli_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget);
}else if( bVerbose ){
- sqlite3_fprintf(out,
+ cli_printf(out,
"%s/* no extra indexes required for %s -> %s */\n",
zIndent, zFrom, zTarget
);
@@ -27527,16 +29596,16 @@ static int lintFkeyIndexes(
sqlite3_free(zPrev);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
+ cli_printf(stderr,"%s\n", sqlite3_errmsg(db));
}
rc2 = sqlite3_finalize(pSql);
if( rc==SQLITE_OK && rc2!=SQLITE_OK ){
rc = rc2;
- sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
+ cli_printf(stderr,"%s\n", sqlite3_errmsg(db));
}
}else{
- sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db));
+ cli_printf(stderr,"%s\n", sqlite3_errmsg(db));
}
return rc;
@@ -27556,9 +29625,9 @@ static int lintDotCommand(
return lintFkeyIndexes(pState, azArg, nArg);
usage:
- sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]);
- sqlite3_fprintf(stderr, "Where sub-commands are:\n");
- sqlite3_fprintf(stderr, " fkey-indexes\n");
+ cli_printf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]);
+ cli_printf(stderr, "Where sub-commands are:\n");
+ cli_printf(stderr, " fkey-indexes\n");
return SQLITE_ERROR;
}
@@ -27572,7 +29641,7 @@ static void shellPrepare(
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
*pRc = rc;
}
@@ -27617,7 +29686,7 @@ static void shellFinalize(
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
+ cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
@@ -27639,7 +29708,7 @@ void shellReset(
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
sqlite3 *db = sqlite3_db_handle(pStmt);
- sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
+ cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
@@ -27692,9 +29761,9 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
va_end(ap);
shellEmitError(z);
if( pAr->fromCmdLine ){
- sqlite3_fputs("Use \"-A\" for more help\n", stderr);
+ cli_puts("Use \"-A\" for more help\n", stderr);
}else{
- sqlite3_fputs("Use \".archive --help\" for more help\n", stderr);
+ cli_puts("Use \".archive --help\" for more help\n", stderr);
}
sqlite3_free(z);
return SQLITE_ERROR;
@@ -27794,7 +29863,7 @@ static int arParseCommand(
struct ArSwitch *pEnd = &aSwitch[nSwitch];
if( nArg<=1 ){
- sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n");
+ cli_printf(stderr, "Wrong number of arguments. Usage:\n");
return arUsage(stderr);
}else{
char *z = azArg[1];
@@ -27900,7 +29969,7 @@ static int arParseCommand(
}
}
if( pAr->eCmd==0 ){
- sqlite3_fprintf(stderr, "Required argument missing. Usage:\n");
+ cli_printf(stderr, "Required argument missing. Usage:\n");
return arUsage(stderr);
}
return SQLITE_OK;
@@ -27943,7 +30012,7 @@ static int arCheckEntries(ArCommand *pAr){
}
shellReset(&rc, pTest);
if( rc==SQLITE_OK && bOk==0 ){
- sqlite3_fprintf(stderr,"not found in archive: %s\n", z);
+ cli_printf(stderr,"not found in archive: %s\n", z);
rc = SQLITE_ERROR;
}
}
@@ -27966,25 +30035,41 @@ static void arWhereClause(
char **pzWhere /* OUT: New WHERE clause */
){
char *zWhere = 0;
- const char *zSameOp = (pAr->bGlob)? "GLOB" : "=";
if( *pRc==SQLITE_OK ){
if( pAr->nArg==0 ){
zWhere = sqlite3_mprintf("1");
}else{
+ char *z1 = sqlite3_mprintf(pAr->bGlob ? "" : "name IN(");
+ char *z2 = sqlite3_mprintf("");
+ const char *zSep1 = "";
+ const char *zSep2 = "";
+
int i;
- const char *zSep = "";
- for(i=0; i<pAr->nArg; i++){
+ for(i=0; i<pAr->nArg && z1 && z2; i++){
const char *z = pAr->azArg[i];
- zWhere = sqlite3_mprintf(
- "%z%s name %s '%q' OR substr(name,1,%d) %s '%q/'",
- zWhere, zSep, zSameOp, z, strlen30(z)+1, zSameOp, z
- );
- if( zWhere==0 ){
- *pRc = SQLITE_NOMEM;
- break;
+ int n = strlen30(z);
+
+ if( pAr->bGlob ){
+ z1 = sqlite3_mprintf("%z%sname GLOB '%q'", z1, zSep2, z);
+ z2 = sqlite3_mprintf(
+ "%z%ssubstr(name,1,%d) GLOB '%q/'", z2, zSep2, n+1,z
+ );
+ }else{
+ z1 = sqlite3_mprintf("%z%s'%q'", z1, zSep1, z);
+ z2 = sqlite3_mprintf("%z%ssubstr(name,1,%d) = '%q/'",z2,zSep2,n+1,z);
}
- zSep = " OR ";
+ zSep1 = ", ";
+ zSep2 = " OR ";
+ }
+ if( z1==0 || z2==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ zWhere = sqlite3_mprintf("(%s%s OR (name GLOB '*/*' AND (%s))) ",
+ z1, pAr->bGlob==0 ? ")" : "", z2
+ );
}
+ sqlite3_free(z1);
+ sqlite3_free(z2);
}
}
*pzWhere = zWhere;
@@ -28010,15 +30095,15 @@ static int arListCommand(ArCommand *pAr){
shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose],
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
- sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql));
+ cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql));
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( pAr->bVerbose ){
- sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n",
+ cli_printf(pAr->out, "%s % 10d %s %s\n",
sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1),
sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3));
}else{
- sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
+ cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
}
}
}
@@ -28045,7 +30130,7 @@ static int arRemoveCommand(ArCommand *pAr){
zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
- sqlite3_fprintf(pAr->out, "%s\n", zSql);
+ cli_printf(pAr->out, "%s\n", zSql);
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0);
@@ -28058,7 +30143,7 @@ static int arRemoveCommand(ArCommand *pAr){
}
}
if( zErr ){
- sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */
+ cli_printf(stdout, "ERROR: %s\n", zErr); /* stdout? */
sqlite3_free(zErr);
}
}
@@ -28073,11 +30158,15 @@ static int arRemoveCommand(ArCommand *pAr){
*/
static int arExtractCommand(ArCommand *pAr){
const char *zSql1 =
- "SELECT "
- " ($dir || name),"
- " writefile(($dir || name), %s, mode, mtime) "
- "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)"
- " AND name NOT GLOB '*..[/\\]*'";
+ "WITH dest(dpath,dlen) AS (SELECT realpath($dir),length(realpath($dir)))\n"
+ "SELECT ($dir || name),\n"
+ " CASE WHEN $dryrun THEN 0\n"
+ " ELSE writefile($dir||name, %s, mode, mtime) END\n"
+ " FROM dest CROSS JOIN %s\n"
+ " WHERE (%s)\n"
+ " AND (data IS NULL OR $pass==0)\n" /* Dirs both passes */
+ " AND dpath=substr(realpath($dir||name),1,dlen)\n" /* No escapes */
+ " AND name NOT GLOB '*..[/\\]*'\n"; /* No /../ in paths */
const char *azExtraArg[] = {
"sqlar_uncompress(data, sz)",
@@ -28112,24 +30201,28 @@ static int arExtractCommand(ArCommand *pAr){
if( rc==SQLITE_OK ){
j = sqlite3_bind_parameter_index(pSql, "$dir");
sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC);
+ j = sqlite3_bind_parameter_index(pSql, "$dryrun");
+ sqlite3_bind_int(pSql, j, pAr->bDryRun);
- /* Run the SELECT statement twice. The first time, writefile() is called
- ** for all archive members that should be extracted. The second time,
- ** only for the directories. This is because the timestamps for
- ** extracted directories must be reset after they are populated (as
- ** populating them changes the timestamp). */
+ /* Run the SELECT statement twice
+ ** (0) writefile() all files and directories
+ ** (1) writefile() for directory again
+ ** The second pass is so that the timestamps for extracted directories
+ ** will be reset to the value in the archive, since populating them
+ ** in the first pass will have changed the timestamp. */
for(i=0; i<2; i++){
- j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
+ j = sqlite3_bind_parameter_index(pSql, "$pass");
sqlite3_bind_int(pSql, j, i);
if( pAr->bDryRun ){
- sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql));
- }else{
- while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
- if( i==0 && pAr->bVerbose ){
- sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
- }
+ cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql));
+ if( pAr->bVerbose==0 ) break;
+ }
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
+ if( i==0 && pAr->bVerbose ){
+ cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0));
}
}
+ if( pAr->bDryRun ) break;
shellReset(&rc, pSql);
}
shellFinalize(&rc, pSql);
@@ -28146,13 +30239,13 @@ static int arExtractCommand(ArCommand *pAr){
static int arExecSql(ArCommand *pAr, const char *zSql){
int rc;
if( pAr->bDryRun ){
- sqlite3_fprintf(pAr->out, "%s\n", zSql);
+ cli_printf(pAr->out, "%s\n", zSql);
rc = SQLITE_OK;
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
if( zErr ){
- sqlite3_fprintf(stdout, "ERROR: %s\n", zErr);
+ cli_printf(stdout, "ERROR: %s\n", zErr);
sqlite3_free(zErr);
}
}
@@ -28304,7 +30397,7 @@ static int arDotCommand(
cmd.out = pState->out;
cmd.db = pState->db;
if( cmd.zFile ){
- eDbType = deduceDatabaseType(cmd.zFile, 1);
+ eDbType = deduceDatabaseType(cmd.zFile, 1, 0);
}else{
eDbType = pState->openMode;
}
@@ -28328,13 +30421,13 @@ static int arDotCommand(
}
cmd.db = 0;
if( cmd.bDryRun ){
- sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile,
+ cli_printf(cmd.out, "-- open database '%s'%s\n", cmd.zFile,
eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
}
rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n",
+ cli_printf(stderr, "cannot open file: %s (%s)\n",
cmd.zFile, sqlite3_errmsg(cmd.db));
goto end_ar_command;
}
@@ -28348,7 +30441,7 @@ static int arDotCommand(
if( cmd.eCmd!=AR_CMD_CREATE
&& sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
){
- sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n");
+ cli_printf(stderr, "database does not contain an 'sqlar' table\n");
rc = SQLITE_ERROR;
goto end_ar_command;
}
@@ -28406,7 +30499,7 @@ end_ar_command:
*/
static int recoverSqlCb(void *pCtx, const char *zSql){
ShellState *pState = (ShellState*)pCtx;
- sqlite3_fprintf(pState->out, "%s;\n", zSql);
+ cli_printf(pState->out, "%s;\n", zSql);
return SQLITE_OK;
}
@@ -28449,7 +30542,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
bRowids = 0;
}
else{
- sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]);
+ cli_printf(stderr,"unexpected option: %s\n", azArg[i]);
showHelp(pState->out, azArg[0]);
return 1;
}
@@ -28459,17 +30552,19 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
pState->db, "main", recoverSqlCb, (void*)pState
);
- sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
+ if( !pState->bSafeMode ){
+ sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
+ }
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
- sqlite3_fprintf(pState->out, ".dbconfig defensive off\n");
+ cli_printf(pState->out, ".dbconfig defensive off\n");
sqlite3_recover_run(p);
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(p);
int errCode = sqlite3_recover_errcode(p);
- sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
+ cli_printf(stderr,"sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
return rc;
@@ -28491,7 +30586,7 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){
while( SQLITE_OK==sqlite3_intck_step(p) ){
const char *zMsg = sqlite3_intck_message(p);
if( zMsg ){
- sqlite3_fprintf(pState->out, "%s\n", zMsg);
+ cli_printf(pState->out, "%s\n", zMsg);
nError++;
}
nStep++;
@@ -28501,11 +30596,11 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){
}
rc = sqlite3_intck_error(p, &zErr);
if( zErr ){
- sqlite3_fprintf(stderr,"%s\n", zErr);
+ cli_printf(stderr,"%s\n", zErr);
}
sqlite3_intck_close(p);
- sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError);
+ cli_printf(pState->out, "%lld steps, %lld errors\n", nStep, nError);
}
return rc;
@@ -28528,7 +30623,7 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){
#define rc_err_oom_die(rc) \
if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
- sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0)
+ cli_printf(stderr,"E:%d\n",rc), assert(0)
#else
static void rc_err_oom_die(int rc){
if( rc==SQLITE_NOMEM ) shell_check_oom(0);
@@ -28643,7 +30738,7 @@ SELECT CASE WHEN (nc < 10) THEN 1 WHEN (nc < 100) THEN 2 \
SELECT\
'('||x'0a'\
|| group_concat(\
- cname||' TEXT',\
+ cname||' ANY',\
','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\
||')' AS ColsSpec \
FROM (\
@@ -28742,10 +30837,10 @@ static int outputDumpWarning(ShellState *p, const char *zLike){
sqlite3_stmt *pStmt = 0;
shellPreparePrintf(p->db, &rc, &pStmt,
"SELECT 1 FROM sqlite_schema o WHERE "
- "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
+ "sql LIKE 'CREATE VIRTUAL TABLE%%' AND (%s)", zLike ? zLike : "true"
);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
- sqlite3_fputs("/* WARNING: "
+ cli_puts("/* WARNING: "
"Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n",
p->out
);
@@ -28778,13 +30873,13 @@ static int faultsim_callback(int iArg){
if( faultsim_state.iCnt ){
if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--;
if( faultsim_state.eVerbose>=2 ){
- sqlite3_fprintf(stdout,
+ cli_printf(stdout,
"FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt);
}
return SQLITE_OK;
}
if( faultsim_state.eVerbose>=1 ){
- sqlite3_fprintf(stdout,
+ cli_printf(stdout,
"FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr);
}
faultsim_state.iCnt = faultsim_state.iInterval;
@@ -28796,47 +30891,1595 @@ static int faultsim_callback(int iArg){
}
/*
+** pickStr(zArg, &zErr, zS1, zS2, ..., "");
+**
+** Try to match zArg against zS1, zS2, and so forth until the first
+** emptry string. Return the index of the match or -1 if none is found.
+** If no match is found, and &zErr is not NULL, then write into
+** zErr a message describing the valid choices.
+*/
+static int pickStr(const char *zArg, char **pzErr, ...){
+ int i, n;
+ const char *z;
+ sqlite3_str *pMsg;
+ va_list ap;
+ va_start(ap, pzErr);
+ i = 0;
+ while( (z = va_arg(ap,const char*))!=0 && z[0]!=0 ){
+ if( cli_strcmp(zArg, z)==0 ) return i;
+ i++;
+ }
+ va_end(ap);
+ if( pzErr==0 ) return -1;
+ n = i;
+ pMsg = sqlite3_str_new(0);
+ va_start(ap, pzErr);
+ sqlite3_str_appendall(pMsg, "should be");
+ i = 0;
+ while( (z = va_arg(ap, const char*))!=0 && z[0]!=0 ){
+ if( i==n-1 ){
+ sqlite3_str_append(pMsg,", or",4);
+ }else if( i>0 ){
+ sqlite3_str_append(pMsg, ",", 1);
+ }
+ sqlite3_str_appendf(pMsg, " %s", z);
+ i++;
+ }
+ va_end(ap);
+ *pzErr = sqlite3_str_finish(pMsg);
+ return -1;
+}
+
+/*
+** DOT-COMMAND: .import
+**
+** USAGE: .import [OPTIONS] FILE TABLE
+**
+** Import CSV or similar text from FILE into TABLE. If TABLE does
+** not exist, it is created using the first row of FILE as the column
+** names. If FILE begins with "|" then it is a command that is run
+** and the output from the command is used as the input data. If
+** FILE begins with "<<" followed by a label, then content is read from
+** the script until the first line that matches the label.
+**
+** The content of FILE is interpreted using RFC-4180 ("CSV") quoting
+** rules unless the current mode is "ascii" or "tabs" or unless one
+** the --ascii option is used.
+**
+** The column and row separators must be single ASCII characters. If
+** multiple characters or a Unicode character are specified for the
+** separators, then only the first byte of the separator is used. Except,
+** if the row separator is \n and the mode is not --ascii, then \r\n is
+** understood as a row separator too.
+**
+** Options:
+** --ascii Do not use RFC-4180 quoting. Use \037 and \036
+** as column and row separators on input, unless other
+** delimiters are specified using --colsep and/or --rowsep
+** --colsep CHAR Use CHAR as the column separator.
+** --csv Input is standard RFC-4180 CSV.
+** --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.
+** --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.
+** --rowsep CHAR Use CHAR as the row separator.
+** --schema S When creating TABLE, put it in schema S
+** --skip N Ignore the first N rows of input
+** -v Verbose mode
+*/
+static int dotCmdImport(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg;/* Argument list */
+ char *zTable = 0; /* Insert data into this table */
+ char *zSchema = 0; /* Schema of zTable */
+ char *zFile = 0; /* Name of file to extra content from */
+ sqlite3_stmt *pStmt = NULL; /* A statement */
+ int nCol; /* Number of columns in the table */
+ i64 nByte; /* Number of bytes in an SQL string */
+ int i, j; /* Loop counters */
+ int needCommit; /* True to COMMIT or ROLLBACK at end */
+ char *zSql = 0; /* An SQL statement */
+ ImportCtx sCtx; /* Reader context */
+ char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
+ int eVerbose = 0; /* Larger for more console output */
+ i64 nSkip = 0; /* Initial lines to skip */
+ i64 iLineOffset = 0; /* Offset to the first line of input */
+ char *zCreate = 0; /* CREATE TABLE statement text */
+ int rc; /* Result code */
+
+ failIfSafeMode(p, "cannot run .import in safe mode");
+ memset(&sCtx, 0, sizeof(sCtx));
+ if( p->mode.eMode==MODE_Ascii ){
+ xRead = ascii_read_one_field;
+ }else{
+ xRead = csv_read_one_field;
+ }
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ if( z[0]!='-' ){
+ if( zFile==0 ){
+ zFile = z;
+ }else if( zTable==0 ){
+ zTable = z;
+ }else{
+ dotCmdError(p, i, "unknown argument", 0);
+ return 1;
+ }
+ }else if( cli_strcmp(z,"-v")==0 ){
+ eVerbose++;
+ }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){
+ zSchema = azArg[++i];
+ }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){
+ nSkip = integerValue(azArg[++i]);
+ }else if( cli_strcmp(z,"-ascii")==0 ){
+ if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0];
+ if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0];
+ xRead = ascii_read_one_field;
+ }else if( cli_strcmp(z,"-csv")==0 ){
+ if( sCtx.cColSep==0 ) sCtx.cColSep = ',';
+ if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n';
+ xRead = csv_read_one_field;
+ }else if( cli_strcmp(z,"-esc")==0 ){
+ sCtx.cUQEscape = azArg[++i][0];
+ }else if( cli_strcmp(z,"-qesc")==0 ){
+ sCtx.cQEscape = azArg[++i][0];
+ }else if( cli_strcmp(z,"-colsep")==0 ){
+ if( i==nArg-1 ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ i++;
+ sCtx.cColSep = azArg[i][0];
+ }else if( cli_strcmp(z,"-rowsep")==0 ){
+ if( i==nArg-1 ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ i++;
+ sCtx.cRowSep = azArg[i][0];
+ }else{
+ dotCmdError(p, i, "unknown option", 0);
+ return 1;
+ }
+ }
+ if( zTable==0 ){
+ dotCmdError(p, nArg, 0, "Missing %s argument\n",
+ zFile==0 ? "FILE" : "TABLE");
+ return 1;
+ }
+ seenInterrupt = 0;
+ open_db(p, 0);
+ if( sCtx.cColSep==0 ){
+ if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){
+ sCtx.cColSep = p->mode.spec.zColumnSep[0];
+ }else{
+ sCtx.cColSep = ',';
+ }
+ }
+ if( (sCtx.cColSep & 0x80)!=0 ){
+ eputz("Error: .import column separator must be ASCII\n");
+ return 1;
+ }
+ if( sCtx.cRowSep==0 ){
+ if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){
+ sCtx.cRowSep = p->mode.spec.zRowSep[0];
+ }else{
+ sCtx.cRowSep = '\n';
+ }
+ }
+ if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){
+ sCtx.cRowSep = '\n';
+ }
+ if( (sCtx.cRowSep & 0x80)!=0 ){
+ eputz("Error: .import row separator must be ASCII\n");
+ return 1;
+ }
+ sCtx.zFile = zFile;
+ sCtx.nLine = 1;
+ if( sCtx.zFile[0]=='|' ){
+#ifdef SQLITE_OMIT_POPEN
+ eputz("Error: pipes are not supported in this OS\n");
+ return 1;
+#else
+ sCtx.in = sqlite3_popen(sCtx.zFile+1, "r");
+ sCtx.zFile = "<pipe>";
+ sCtx.xCloser = pclose;
+#endif
+ }else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){
+ /* Input text comes from subsequent lines of script until the zFile
+ ** delimiter */
+ int nEndMark = strlen30(zFile)-2;
+ char *zEndMark = &zFile[2];
+ sqlite3_str *pContent = sqlite3_str_new(p->db);
+ int ckEnd = 1;
+ i64 iStart = p->lineno;
+ char zLine[2000];
+ sCtx.zFile = p->zInFile;
+ sCtx.nLine = p->lineno+1;
+ iLineOffset = p->lineno;
+ while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){
+ if( ckEnd && cli_strncmp(zLine,zEndMark,nEndMark)==0 ){
+ ckEnd = 2;
+ if( strchr(zLine,'\n') ) p->lineno++;
+ break;
+ }
+ if( strchr(zLine,'\n') ){
+ p->lineno++;
+ ckEnd = 1;
+ }else{
+ ckEnd = 0;
+ }
+ sqlite3_str_appendall(pContent, zLine);
+ }
+ sCtx.zIn = sqlite3_str_finish(pContent);
+ if( sCtx.zIn==0 ){
+ sCtx.zIn = sqlite3_mprintf("");
+ }
+ if( ckEnd<2 ){
+ i64 savedLn = p->lineno;
+ p->lineno = iStart;
+ dotCmdError(p, 0, 0,"Content terminator \"%s\" not found.",zEndMark);
+ p->lineno = savedLn;
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ }else{
+ sCtx.in = sqlite3_fopen(sCtx.zFile, "rb");
+ sCtx.xCloser = fclose;
+ }
+ if( sCtx.in==0 && sCtx.zIn==0 ){
+ dotCmdError(p, 0, 0, "cannot open \"%s\"", zFile);
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ if( eVerbose>=1 ){
+ char zSep[2];
+ zSep[1] = 0;
+ zSep[0] = sCtx.cColSep;
+ cli_puts("Column separator ", p->out);
+ output_c_string(p->out, zSep);
+ cli_puts(", row separator ", p->out);
+ zSep[0] = sCtx.cRowSep;
+ output_c_string(p->out, zSep);
+ cli_puts("\n", p->out);
+ }
+ sCtx.z = sqlite3_malloc64(120);
+ if( sCtx.z==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ /* Below, resources must be freed before exit. */
+ while( nSkip>0 ){
+ nSkip--;
+ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
+ }
+ import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
+ if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
+ && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
+ " WHERE name=%Q AND type='view'",
+ zSchema ? zSchema : "main", zTable)
+ ){
+ /* Table does not exist. Create it. */
+ sqlite3 *dbCols = 0;
+ char *zRenames = 0;
+ char *zColDefs;
+ zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
+ zSchema ? zSchema : "main", zTable);
+ while( xRead(&sCtx) ){
+ zAutoColumn(sCtx.z, &dbCols, 0);
+ if( sCtx.cTerm!=sCtx.cColSep ) break;
+ }
+ zColDefs = zAutoColumn(0, &dbCols, &zRenames);
+ if( zRenames!=0 ){
+ cli_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
+ "Columns renamed during .import %s due to duplicates:\n"
+ "%s\n", sCtx.zFile, zRenames);
+ sqlite3_free(zRenames);
+ }
+ assert(dbCols==0);
+ if( zColDefs==0 ){
+ cli_printf(stderr,"%s: empty file\n", sCtx.zFile);
+ import_cleanup(&sCtx);
+ sqlite3_free(zCreate);
+ return 1;
+ }
+ zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
+ if( zCreate==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ if( eVerbose>=1 ){
+ cli_printf(p->out, "%s\n", zCreate);
+ }
+ rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+ if( rc ){
+ cli_printf(stderr,
+ "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
+ }
+ sqlite3_free(zCreate);
+ zCreate = 0;
+ if( rc ){
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ }
+ zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
+ zTable, zSchema);
+ if( zSql==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ zSql = 0;
+ if( rc ){
+ if (pStmt) sqlite3_finalize(pStmt);
+ shellDatabaseError(p->db);
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ if( sqlite3_step(pStmt)==SQLITE_ROW ){
+ nCol = sqlite3_column_int(pStmt, 0);
+ }else{
+ nCol = 0;
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( nCol==0 ) return 0; /* no columns, no error */
+
+ nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
+ + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
+ + strlen(zTable)*2 + 2 /* Quoted table name */
+ + nCol*2; /* Space for ",?" for each column */
+ zSql = sqlite3_malloc64( nByte );
+ if( zSql==0 ){
+ import_cleanup(&sCtx);
+ shell_out_of_memory();
+ }
+ if( zSchema ){
+ sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
+ zSchema, zTable);
+ }else{
+ sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
+ }
+ j = strlen30(zSql);
+ for(i=1; i<nCol; i++){
+ zSql[j++] = ',';
+ zSql[j++] = '?';
+ }
+ zSql[j++] = ')';
+ zSql[j] = 0;
+ assert( j<nByte );
+ if( eVerbose>=2 ){
+ cli_printf(p->out, "Insert using: %s\n", zSql);
+ }
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ zSql = 0;
+ if( rc ){
+ shellDatabaseError(p->db);
+ if (pStmt) sqlite3_finalize(pStmt);
+ import_cleanup(&sCtx);
+ return 1;
+ }
+ needCommit = sqlite3_get_autocommit(p->db);
+ if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
+ do{
+ int startLine = sCtx.nLine;
+ for(i=0; i<nCol; i++){
+ char *z = xRead(&sCtx);
+ /*
+ ** Did we reach end-of-file before finding any columns?
+ ** If so, stop instead of NULL filling the remaining columns.
+ */
+ if( z==0 && i==0 ) break;
+ /*
+ ** Did we reach end-of-file OR end-of-line before finding any
+ ** columns in ASCII mode? If so, stop instead of NULL filling
+ ** the remaining columns.
+ */
+ if( p->mode.eMode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
+ /*
+ ** For CSV mode, per RFC 4180, accept EOF in lieu of final
+ ** record terminator but only for last field of multi-field row.
+ ** (If there are too few fields, it's not valid CSV anyway.)
+ */
+ if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
+ z = "";
+ }
+ sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
+ if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
+ if( i==0 && (strcmp(z,"\n")==0 || strcmp(z,"\r\n")==0) ){
+ /* Ignore trailing \n or \r\n when some other row separator */
+ break;
+ }
+ cli_printf(stderr,"%s:%d: expected %d columns but found %d"
+ " - filling the rest with NULL\n",
+ sCtx.zFile, startLine, nCol, i+1);
+ i += 2;
+ while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
+ }
+ }
+ if( sCtx.cTerm==sCtx.cColSep ){
+ do{
+ xRead(&sCtx);
+ i++;
+ }while( sCtx.cTerm==sCtx.cColSep );
+ cli_printf(stderr,
+ "%s:%d: expected %d columns but found %d - extras ignored\n",
+ sCtx.zFile, startLine, nCol, i);
+ }
+ if( i>=nCol ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ cli_printf(stderr,"%s:%d: INSERT failed: %s\n",
+ sCtx.zFile, startLine, sqlite3_errmsg(p->db));
+ sCtx.nErr++;
+ if( bail_on_error ) break;
+ }else{
+ sCtx.nRow++;
+ }
+ }
+ }while( sCtx.cTerm!=EOF );
+
+ import_cleanup(&sCtx);
+ sqlite3_finalize(pStmt);
+ if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
+ if( eVerbose>0 ){
+ cli_printf(p->out,
+ "Added %d rows with %d errors using %d lines of input\n",
+ sCtx.nRow, sCtx.nErr, sCtx.nLine-1-iLineOffset);
+ }
+ return sCtx.nErr ? 1 : 0;
+}
+
+
+/*
+** This function computes what to show the user about the configured
+** titles (or column-names). Output is an integer between 0 and 3:
+**
+** 0: The titles do not matter. Never show anything.
+** 1: Show "--titles off"
+** 2: Show "--titles on"
+** 3: Show "--title VALUE" where VALUE is an encoding method
+** to use, one of: plain sql csv html tcl json
+**
+** Inputs are:
+**
+** spec.bTitles (bT) Whether or not to show the titles
+** spec.eTitle (eT) The actual encoding to be used for titles
+** ModeInfo.bHdr (bH) Default value for spec.bTitles
+** ModeInfo.eHdr (eH) Default value for spec.eTitle
+** bAll Whether the -v option is used
+*/
+static int modeTitleDsply(ShellState *p, int bAll){
+ int eMode = p->mode.eMode;
+ const ModeInfo *pI = &aModeInfo[eMode];
+ int bT = p->mode.spec.bTitles;
+ int eT = p->mode.spec.eTitle;
+ int bH = pI->bHdr;
+ int eH = pI->eHdr;
+
+ /* Variable "v" is the truth table that will determine the answer
+ **
+ ** Actual encoding is different from default
+ ** vvvvvvvv */
+ sqlite3_uint64 v = UINT64_C(0x0133013311220102);
+ /* ^^^^ ^^^^
+ ** Upper 2-byte groups for when ON/OFF disagrees with
+ ** the default. */
+
+ if( bH==0 ) return 0; /* Header not appliable. Ex: off, count */
+
+ if( eT==0 ) eT = eH; /* Fill in missing spec.eTitle */
+ if( bT==0 ) bT = bH; /* Fill in missing spec.bTitles */
+
+ if( eT!=eH ) v >>= 32; /* Encoding disagree in upper 4-bytes */
+ if( bT!=bH ) v >>= 16; /* ON/OFF disagree in upper 2-byte pairs */
+ if( bT<2 ) v >>= 8; /* ON in even bytes, OFF in odd bytes (1st byte 0) */
+ if( !bAll ) v >>= 4; /* bAll values are in the lower half-byte */
+
+ return v & 3; /* Return the selected truth-table entry */
+}
+
+/*
+** DOT-COMMAND: .mode
+**
+** USAGE: .mode [MODE] [OPTIONS]
+**
+** Change the output mode to MODE and/or apply OPTIONS to the output mode.
+** Arguments are processed from left to right. If no arguments, show the
+** current output mode and relevant options.
+**
+** Options:
+** --align STRING Set the alignment of text in columnar modes
+** String consists of characters 'L', 'C', 'R'
+** meaning "left", "centered", and "right", with
+** one letter per column starting from the left.
+** Unspecified alignment defaults to 'L'.
+** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl",
+** "json", or "size". Default is "auto".
+** --border on|off Show outer border on "box" and "table" modes.
+** --charlimit N Set the maximum number of output characters to
+** show for any single SQL value to N. Longer values
+** truncated. Zero means "no limit".
+** --colsep STRING Use STRING as the column separator
+** --escape ESC Enable/disable escaping of control characters
+** found in the output. ESC can be "off", "ascii",
+** or "symbol".
+** --linelimit N Set the maximum number of output lines to show for
+** any single SQL value to N. Longer values are
+** truncated. Zero means "no limit". Only works
+** in "line" mode and in columnar modes.
+** --limits L,C,T Shorthand for "--linelimit L --charlimit C
+** --titlelimit T". The ",T" can be omitted in which
+** case the --titlelimit is unchanged. The argument
+** can also be "off" to mean "0,0,0" or "on" to
+** mean "5,300,20".
+** --list List available modes
+** --multiinsert N In "insert" mode, put multiple rows on a single
+** INSERT statement until the size exceeds N bytes.
+** --null STRING Render SQL NULL values as the given string
+** --once Setting changes to the right are reverted after
+** the next SQL command.
+** --quote ARG Enable/disable quoting of text. ARG can be
+** "off", "on", "sql", "relaxed", "csv", "html",
+** "tcl", or "json". "off" means show the text as-is.
+** "on" is an alias for "sql".
+** --reset Changes all mode settings back to their default.
+** --rowsep STRING Use STRING as the row separator
+** --sw|--screenwidth N Declare the screen width of the output device
+** to be N characters. An attempt may be made to
+** wrap output text to fit within this limit. Zero
+** means "no limit". Or N can be "auto" to set the
+** width automatically.
+** --tablename NAME Set the name of the table for "insert" mode.
+** --tag NAME Save mode to the left as NAME.
+** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.
+** --title ARG Whether or not to show column headers, and if so
+** how to encode them. ARG can be "off", "on",
+** "sql", "csv", "html", "tcl", or "json".
+** --titlelimit N Limit the length of column titles to N characters.
+** -v|--verbose Verbose output
+** --widths LIST Set the columns widths for columnar modes. The
+** argument is a list of integers, one for each
+** column. A "0" width means use a dynamic width
+** based on the actual width of data. If there are
+** fewer entries in LIST than columns, "0" is used
+** for the unspecified widths.
+** --wordwrap BOOLEAN Enable/disable word wrapping
+** --wrap N Wrap columns wider than N characters
+** --ww Shorthand for "--wordwrap on"
+*/
+static int dotCmdMode(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg;/* Argument list */
+ int eMode = -1; /* New mode value, or -1 for none */
+ int iMode = -1; /* Index of the argument that is the mode name */
+ int i; /* Loop counter */
+ int k; /* Misc index variable */
+ int chng = 0; /* True if anything has changed */
+ int bAll = 0; /* Show all details of the mode */
+
+ for(i=1; i<nArg; i++){
+ const char *z = azArg[i];
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ if( z[0]!='-'
+ && iMode<0
+ && (eMode = modeFind(p, azArg[i]))>=0
+ && eMode!=MODE_Www
+ ){
+ iMode = i;
+ modeChange(p, eMode);
+ /* (Legacy) If the mode is MODE_Insert and the next argument
+ ** is not an option, then the next argument must be the table
+ ** name.
+ */
+ if( i+1<nArg && azArg[i+1][0]!='-' ){
+ i++;
+ modeSetStr(&p->mode.spec.zTableName, azArg[i]);
+ }
+ chng = 1;
+ }else if( optionMatch(z,"align") ){
+ char *zAlign;
+ int nAlign;
+ int nErr = 0;
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ i++;
+ zAlign = azArg[i];
+ nAlign = 0x3fff & strlen(zAlign);
+ free(p->mode.spec.aAlign);
+ p->mode.spec.aAlign = malloc(nAlign);
+ shell_check_oom(p->mode.spec.aAlign);
+ for(k=0; k<nAlign; k++){
+ unsigned char c = 0;
+ switch( zAlign[k] ){
+ case 'l': case 'L': c = QRF_ALIGN_Left; break;
+ case 'c': case 'C': c = QRF_ALIGN_Center; break;
+ case 'r': case 'R': c = QRF_ALIGN_Right; break;
+ default: nErr++; break;
+ }
+ p->mode.spec.aAlign[k] = c;
+ }
+ p->mode.spec.nAlign = nAlign;
+ chng = 1;
+ if( nErr ){
+ dotCmdError(p, i, "bad alignment string",
+ "Should contain only characters L, C, and R.");
+ return 1;
+ }
+ }else if( pickStr(z,0,"-blob","-blob-quote","")>=0 ){
+ if( (++i)>=nArg ){
+ dotCmdError(p, i-1, "missing argument", 0);
+ return 1;
+ }
+ k = pickStr(azArg[i], 0,
+ "auto", "text", "sql", "hex", "tcl", "json", "size", "");
+ /* 0 1 2 3 4 5 6
+ ** Must match QRF_BLOB_xxxx values. See also tag-20251124a */
+ if( k>=0 ){
+ p->mode.spec.eBlob = k & 0xff;
+ }
+ chng = 1;
+ }else if( optionMatch(z,"border") ){
+ if( (++i)>=nArg ){
+ dotCmdError(p, i-1, "missing argument", 0);
+ return 1;
+ }
+ k = pickStr(azArg[i], 0, "auto", "off", "on", "");
+ if( k>=0 ){
+ p->mode.spec.bBorder = k & 0x3;
+ }
+ chng = 1;
+ }else if( 0<=(k=pickStr(z,0,
+ "-charlimit","-linelimit","-titlelimit","-multiinsert","")) ){
+ int w; /* 0 1 2 3 */
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ w = integerValue(azArg[++i]);
+ switch( k ){
+ case 0: p->mode.spec.nCharLimit = w; break;
+ case 1: p->mode.spec.nLineLimit = w; break;
+ case 2: p->mode.spec.nTitleLimit = w; break;
+ default: p->mode.spec.nMultiInsert = w; break;
+ }
+ chng = 1;
+ }else if( 0<=(k=pickStr(z,0,"-tablename","-rowsep","-colsep","-null","")) ){
+ /* 0 1 2 3 */
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ i++;
+ switch( k ){
+ case 0: modeSetStr(&p->mode.spec.zTableName, azArg[i]); break;
+ case 1: modeSetStr(&p->mode.spec.zRowSep, azArg[i]); break;
+ case 2: modeSetStr(&p->mode.spec.zColumnSep, azArg[i]); break;
+ case 3: modeSetStr(&p->mode.spec.zNull, azArg[i]); break;
+ }
+ chng = 1;
+ }else if( optionMatch(z,"escape") ){
+ /* See similar code at tag-20250224-1 */
+ char *zErr = 0;
+ if( (++i)>=nArg ){
+ dotCmdError(p, i-1, "missing argument", 0);
+ return 1;
+ } /* 0 1 2 <-- One less than QRF_ESC_ */
+ k = pickStr(azArg[i],&zErr,"off","ascii","symbol","");
+ if( k<0 ){
+ dotCmdError(p, i, "unknown escape type", "%s", zErr);
+ sqlite3_free(zErr);
+ return 1;
+ }
+ p->mode.spec.eEsc = k+1;
+ chng = 1;
+ }else if( optionMatch(z,"limits") ){
+ if( (++i)>=nArg ){
+ dotCmdError(p, i-1, "missing argument", 0);
+ return 1;
+ }
+ k = pickStr(azArg[i],0,"on","off","");
+ if( k==0 ){
+ p->mode.spec.nLineLimit = DFLT_LINE_LIMIT;
+ p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT;
+ p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT;
+ }else if( k==1 ){
+ p->mode.spec.nLineLimit = 0;
+ p->mode.spec.nCharLimit = 0;
+ p->mode.spec.nTitleLimit = 0;
+ }else{
+ int L, C, T = 0;
+ int nNum = sscanf(azArg[i], "%d,%d,%d", &L, &C, &T);
+ if( nNum<2 || L<0 || C<0 || T<0){
+ dotCmdError(p, i, "bad argument", "Should be \"L,C,T\" where L, C"
+ " and T are unsigned integers");
+ return 1;
+ }
+ p->mode.spec.nLineLimit = L;
+ p->mode.spec.nCharLimit = C;
+ if( nNum==3 ) p->mode.spec.nTitleLimit = T;
+ }
+ chng = 1;
+ }else if( optionMatch(z,"list") ){
+ int ii;
+ cli_puts("available modes:", p->out);
+ for(ii=0; ii<ArraySize(aModeInfo); ii++){
+ if( ii==MODE_Www ) continue;
+ cli_printf(p->out, " %s", aModeInfo[ii].zName);
+ }
+ for(ii=0; ii<p->nSavedModes; ii++){
+ cli_printf(p->out, " %s", p->aSavedModes[ii].zTag);
+ }
+ cli_puts(" batch tty\n", p->out);
+ chng = 1; /* Not really a change, but we still want to suppress the
+ ** "current mode" output */
+ }else if( optionMatch(z,"once") ){
+ p->nPopMode = 0;
+ modePush(p);
+ p->nPopMode = 1;
+ }else if( optionMatch(z,"noquote") ){
+ /* (undocumented legacy) --noquote always turns quoting off */
+ p->mode.spec.eText = QRF_TEXT_Plain;
+ p->mode.spec.eBlob = QRF_BLOB_Auto;
+ chng = 1;
+ }else if( optionMatch(z,"quote") ){
+ if( i+1<nArg
+ && azArg[i+1][0]!='-'
+ && (iMode>0 || strcmp(azArg[i+1],"off")==0 || modeFind(p, azArg[i+1])<0)
+ ){
+ /* --quote is followed by an argument other that is not an option
+ ** or a mode name. See it must be a boolean or a keyword to describe
+ ** how to set quoting. */
+ i++;
+ if( (k = pickStr(azArg[i],0,"no","yes","0","1",""))>=0 ){
+ k &= 1; /* 0 for "off". 1 for "on". */
+ }else{
+ char *zErr = 0;
+ k = pickStr(azArg[i],&zErr,
+ "off","on","sql","csv","html","tcl","json","relaxed","");
+ /* 0 1 2 3 4 5 6 7 */
+ if( k<0 ){
+ dotCmdError(p, i, "unknown", "%z", zErr);
+ return 1;
+ }
+ }
+ }else{
+ /* (Legacy) no following boolean argument. Turn quoting on */
+ k = 1;
+ }
+ switch( k ){
+ case 1: /* on */
+ modeSetStr(&p->mode.spec.zNull, "NULL");
+ /* Fall through */
+ case 2: /* sql */
+ p->mode.spec.eText = QRF_TEXT_Sql;
+ break;
+ case 3: /* csv */
+ p->mode.spec.eText = QRF_TEXT_Csv;
+ break;
+ case 4: /* html */
+ p->mode.spec.eText = QRF_TEXT_Html;
+ break;
+ case 5: /* tcl */
+ p->mode.spec.eText = QRF_TEXT_Tcl;
+ break;
+ case 6: /* json */
+ p->mode.spec.eText = QRF_TEXT_Json;
+ break;
+ case 7: /* relaxed */
+ p->mode.spec.eText = QRF_TEXT_Relaxed;
+ break;
+ default: /* off */
+ p->mode.spec.eText = QRF_TEXT_Plain;
+ break;
+ }
+ chng = 1;
+ }else if( optionMatch(z,"reset") ){
+ int saved_eMode = p->mode.eMode;
+ modeFree(&p->mode);
+ modeChange(p, saved_eMode);
+ }else if( optionMatch(z,"screenwidth") || optionMatch(z,"sw") ){
+ if( (++i)>=nArg ){
+ dotCmdError(p, i-1, "missing argument", 0);
+ return 1;
+ }
+ k = pickStr(azArg[i],0,"off","auto","");
+ if( k==0 ){
+ p->mode.bAutoScreenWidth = 0;
+ p->mode.spec.nScreenWidth = 0;
+ }else if( k==1 ){
+ p->mode.bAutoScreenWidth = 1;
+ }else{
+ i64 w = integerValue(azArg[i]);
+ p->mode.bAutoScreenWidth = 0;
+ if( w<0 ) w = 0;
+ if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH;
+ p->mode.spec.nScreenWidth = w;
+ }
+ chng = 1;
+ }else if( optionMatch(z,"tag") ){
+ size_t nByte;
+ int n;
+ const char *zTag;
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ zTag = azArg[++i];
+ if( modeFind(p, zTag)>=0 ){
+ dotCmdError(p, i, "mode already exists", 0);
+ return 1;
+ }
+ if( p->nSavedModes > MODE_N_USER ){
+ dotCmdError(p, i-1, "cannot add more modes", 0);
+ return 1;
+ }
+ n = p->nSavedModes++;
+ nByte = sizeof(p->aSavedModes[0]);
+ nByte *= n+1;
+ p->aSavedModes = realloc( p->aSavedModes, nByte );
+ shell_check_oom(p->aSavedModes);
+ p->aSavedModes[n].zTag = strdup(zTag);
+ shell_check_oom(p->aSavedModes[n].zTag);
+ modeDup(&p->aSavedModes[n].mode, &p->mode);
+ chng = 1;
+ }else if( optionMatch(z,"textjsonb") ){
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ p->mode.spec.bTextJsonb = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No;
+ chng = 1;
+ }else if( optionMatch(z,"titles") || optionMatch(z,"title") ){
+ char *zErr = 0;
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ k = pickStr(azArg[++i],&zErr,
+ "off","on","plain","sql","csv","html","tcl","json","");
+ /* 0 1 2 3 4 5 6 7 */
+ if( k<0 ){
+ dotCmdError(p, i, "bad --titles value","%z", zErr);
+ return 1;
+ }
+ p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No;
+ p->mode.mFlags &= ~MFLG_HDR;
+ p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr;
+ chng = 1;
+ }else if( optionMatch(z,"widths") || optionMatch(z,"width") ){
+ int nWidth = 0;
+ short int *aWidth;
+ const char *zW;
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ zW = azArg[++i];
+ /* Every width value takes at least 2 bytes in the input string to
+ ** specify, so strlen(zW) bytes should be plenty of space to hold the
+ ** result. */
+ aWidth = malloc( strlen(zW) );
+ while( IsSpace(zW[0]) ) zW++;
+ while( zW[0] ){
+ int w = 0;
+ int nDigit = 0;
+ k = zW[0]=='-' && IsDigit(zW[1]);
+ while( IsDigit(zW[k]) ){
+ w = w*10 + zW[k] - '0';
+ if( w>QRF_MAX_WIDTH ){
+ dotCmdError(p,i+1,"width too big",
+ "Maximum column width is %d", QRF_MAX_WIDTH);
+ free(aWidth);
+ return 1;
+ }
+ nDigit++;
+ k++;
+ }
+ if( nDigit==0 ){
+ dotCmdError(p,i+1,"syntax error",
+ "should be a comma-separated list if integers");
+ free(aWidth);
+ return 1;
+ }
+ if( zW[0]=='-' ) w = -w;
+ aWidth[nWidth++] = w;
+ zW += k;
+ if( zW[0]==',' ) zW++;
+ while( IsSpace(zW[0]) ) zW++;
+ }
+ free(p->mode.spec.aWidth);
+ p->mode.spec.aWidth = aWidth;
+ p->mode.spec.nWidth = nWidth;
+ chng = 1;
+ }else if( optionMatch(z,"wrap") ){
+ int w;
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ w = integerValue(azArg[++i]);
+ if( w<(-QRF_MAX_WIDTH) ) w = -QRF_MAX_WIDTH;
+ if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH;
+ p->mode.spec.nWrap = w;
+ chng = 1;
+ }else if( optionMatch(z,"ww") ){
+ p->mode.spec.bWordWrap = QRF_Yes;
+ chng = 1;
+ }else if( optionMatch(z,"wordwrap") ){
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ p->mode.spec.bWordWrap = (u8)booleanValue(azArg[++i]) ? QRF_Yes : QRF_No;
+ chng = 1;
+ }else if( optionMatch(z,"v") || optionMatch(z,"verbose") ){
+ bAll = 1;
+ }else if( z[0]=='-' ){
+ dotCmdError(p, i, "bad option", "Use \".help .mode\" for more info");
+ return 1;
+ }else{
+ dotCmdError(p, i, iMode>0?"bad argument":"unknown mode",
+ "Use \".help .mode\" for more info");
+ return 1;
+ }
+ }
+ if( !chng || bAll ){
+ const ModeInfo *pI = aModeInfo + p->mode.eMode;
+ sqlite3_str *pDesc = sqlite3_str_new(p->db);
+ char *zDesc;
+ const char *zSetting;
+
+ if( p->nPopMode ) sqlite3_str_appendall(pDesc, "--once ");
+ sqlite3_str_appendall(pDesc,pI->zName);
+ if( bAll || (p->mode.spec.nAlign && pI->eCx==2) ){
+ int ii;
+ sqlite3_str_appendall(pDesc, " --align \"");
+ for(ii=0; ii<p->mode.spec.nAlign; ii++){
+ unsigned char a = p->mode.spec.aAlign[ii];
+ sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]);
+ }
+ sqlite3_str_append(pDesc, "\"", 1);
+ }
+ if( bAll
+ || (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0)
+ ){
+ sqlite3_str_appendf(pDesc," --border %s",
+ p->mode.spec.bBorder==QRF_No ? "off" : "on");
+ }
+ if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){
+ const char *azBQuote[] =
+ { "auto", "text", "sql", "hex", "tcl", "json", "size" };
+ /* 0 1 2 3 4 5 6
+ ** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */
+ u8 e = p->mode.spec.eBlob;
+ sqlite3_str_appendf(pDesc, " --blob-quote %s", azBQuote[e]);
+ }
+ zSetting = aModeStr[pI->eCSep];
+ if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zColumnSep)!=0) ){
+ sqlite3_str_appendf(pDesc, " --colsep ");
+ append_c_string(pDesc, p->mode.spec.zColumnSep);
+ }
+ if( bAll || p->mode.spec.eEsc!=QRF_Auto ){
+ sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]);
+ }
+ if( bAll
+ || (p->mode.spec.nLineLimit>0 && pI->eCx>0)
+ || p->mode.spec.nCharLimit>0
+ || (p->mode.spec.nTitleLimit>0 && pI->eCx>0)
+ ){
+ if( p->mode.spec.nLineLimit==0
+ && p->mode.spec.nCharLimit==0
+ && p->mode.spec.nTitleLimit==0
+ ){
+ sqlite3_str_appendf(pDesc, " --limits off");
+ }else if( p->mode.spec.nLineLimit==DFLT_LINE_LIMIT
+ && p->mode.spec.nCharLimit==DFLT_CHAR_LIMIT
+ && p->mode.spec.nTitleLimit==DFLT_TITLE_LIMIT
+ ){
+ sqlite3_str_appendf(pDesc, " --limits on");
+ }else{
+ sqlite3_str_appendf(pDesc, " --limits %d,%d,%d",
+ p->mode.spec.nLineLimit, p->mode.spec.nCharLimit,
+ p->mode.spec.nTitleLimit);
+ }
+ }
+ if( bAll
+ || (p->mode.spec.nMultiInsert && p->mode.spec.eStyle==QRF_STYLE_Insert)
+ ){
+ sqlite3_str_appendf(pDesc, " --multiinsert %u",
+ p->mode.spec.nMultiInsert);
+ }
+ zSetting = aModeStr[pI->eNull];
+ if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zNull)!=0) ){
+ sqlite3_str_appendf(pDesc, " --null ");
+ append_c_string(pDesc, p->mode.spec.zNull);
+ }
+ if( bAll
+ || (pI->eText!=p->mode.spec.eText && (pI->eText>1 || p->mode.spec.eText>1))
+ ){
+ sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]);
+ }
+ zSetting = aModeStr[pI->eRSep];
+ if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zRowSep)!=0) ){
+ sqlite3_str_appendf(pDesc, " --rowsep ");
+ append_c_string(pDesc, p->mode.spec.zRowSep);
+ }
+ if( bAll
+ || (pI->eCx && (p->mode.spec.nScreenWidth>0 || p->mode.bAutoScreenWidth))
+ ){
+ if( p->mode.bAutoScreenWidth ){
+ sqlite3_str_appendall(pDesc, " --sw auto");
+ }else{
+ sqlite3_str_appendf(pDesc," --sw %d",
+ p->mode.spec.nScreenWidth);
+ }
+ }
+ if( bAll || p->mode.eMode==MODE_Insert ){
+ sqlite3_str_appendf(pDesc," --tablename ");
+ append_c_string(pDesc, p->mode.spec.zTableName);
+ }
+ if( bAll || p->mode.spec.bTextJsonb ){
+ sqlite3_str_appendf(pDesc," --textjsonb %s",
+ p->mode.spec.bTextJsonb==QRF_Yes ? "on" : "off");
+ }
+ k = modeTitleDsply(p, bAll);
+ if( k==1 ){
+ sqlite3_str_appendall(pDesc, " --titles off");
+ }else if( k==2 ){
+ sqlite3_str_appendall(pDesc, " --titles on");
+ }else if( k==3 ){
+ static const char *azTitle[] =
+ { "plain", "sql", "csv", "html", "tcl", "json"};
+ sqlite3_str_appendf(pDesc, " --titles %s",
+ azTitle[p->mode.spec.eTitle-1]);
+ }
+ if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){
+ int ii;
+ const char *zSep = " --widths ";
+ for(ii=0; ii<p->mode.spec.nWidth; ii++){
+ sqlite3_str_appendf(pDesc, "%s%d", zSep, (int)p->mode.spec.aWidth[ii]);
+ zSep = ",";
+ }
+ }else if( bAll ){
+ sqlite3_str_appendall(pDesc, " --widths \"\"");
+ }
+ if( bAll || (pI->eCx>0 && p->mode.spec.bWordWrap) ){
+ if( bAll ){
+ sqlite3_str_appendf(pDesc, " --wordwrap %s",
+ p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off");
+ }
+ if( p->mode.spec.nWrap ){
+ sqlite3_str_appendf(pDesc, " --wrap %d", p->mode.spec.nWrap);
+ }
+ if( !bAll ) sqlite3_str_append(pDesc, " --ww", 5);
+ }
+ zDesc = sqlite3_str_finish(pDesc);
+ cli_printf(p->out, ".mode %s\n", zDesc);
+ fflush(p->out);
+ sqlite3_free(zDesc);
+ }
+ return 0;
+}
+
+/*
+** DOT-COMMAND: .output
+** USAGE: .output [OPTIONS] [FILE]
+**
+** Begin redirecting output to FILE. Or if FILE is omitted, revert
+** to sending output to the console. If FILE begins with "|" then
+** the remainder of file is taken as a pipe and output is directed
+** into that pipe. If FILE is "memory" then output is captured in an
+** internal memory buffer. If FILE is "off" then output is redirected
+** into /dev/null or the equivalent.
+**
+** Options:
+** --bom Prepend a byte-order mark to the output
+** -e Accumulate output in a temporary text file then
+** launch a text editor when the redirection ends.
+** --error-prefix X Use X as the left-margin prefix for error messages.
+** Set to an empty string to restore the default.
+** --keep Keep redirecting output to its current destination.
+** Use this option in combination with --show or
+** with --error-prefix when you do not want to stop
+** a current redirection.
+** --plain Use plain text rather than HTML tables with -w
+** --show Show output text captured by .testcase or by
+** redirecting to "memory".
+** -w Show the output in a web browser. Output is
+** written into a temporary HTML file until the
+** redirect ends, then the web browser is launched.
+** Query results are shown as HTML tables, unless
+** the --plain is used too.
+** -x Show the output in a spreadsheet. Output is
+** written to a temp file as CSV then the spreadsheet
+** is launched when
+**
+** DOT-COMMAND: .once
+** USAGE: .once [OPTIONS] FILE ...
+**
+** Write the output for the next line of SQL or the next dot-command into
+** FILE. If FILE begins with "|" then it is a program into which output
+** is written. The FILE argument should be omitted if one of the -e, -w,
+** or -x options is used.
+**
+** Options:
+** -e Capture output into a temporary file then bring up
+** a text editor on that temporary file.
+** --plain Use plain text rather than HTML tables with -w
+** -w Capture output into an HTML file then bring up that
+** file in a web browser
+** -x Show the output in a spreadsheet. Output is
+** written to a temp file as CSV then the spreadsheet
+** is launched when
+**
+** DOT-COMMAND: .excel
+** Shorthand for ".once -x"
+**
+** DOT-COMMAND: .www [--plain]
+** Shorthand for ".once -w" or ".once --plain -w"
+*/
+static int dotCmdOutput(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg; /* Text of the arguments */
+ char *zFile = 0; /* The FILE argument */
+ int i; /* Loop counter */
+ int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */
+ int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */
+ int bPlain = 0; /* --plain option */
+ int bKeep = 0; /* Keep redirecting */
+ static const char *zBomUtf8 = "\357\273\277";
+ const char *zBom = 0;
+ char c = azArg[0][0];
+ int n = strlen30(azArg[0]);
+
+ failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
+ if( c=='e' ){
+ eMode = 'x';
+ bOnce = 2;
+ }else if( c=='w' ){
+ eMode = 'w';
+ bOnce = 2;
+ }else if( n>=2 && cli_strncmp(azArg[0],"once",n)==0 ){
+ bOnce = 1;
+ }
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ if( z[0]=='-' ){
+ if( z[1]=='-' ) z++;
+ if( cli_strcmp(z,"-bom")==0 ){
+ zBom = zBomUtf8;
+ }else if( cli_strcmp(z,"-plain")==0 ){
+ bPlain = 1;
+ }else if( c=='o' && sqlite3_strglob("-[ewx]",z)==0 ){
+ if( bKeep || eMode ){
+ dotCmdError(p, i, "incompatible with prior options",0);
+ goto dotCmdOutput_error;
+ }
+ eMode = z[1];
+ }else if( cli_strcmp(z,"-show")==0 ){
+ if( cli_output_capture ){
+ sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture));
+ }
+ }else if( cli_strcmp(z,"-keep")==0 ){
+ bKeep = 1;
+ }else if( optionMatch(z,"error-prefix") ){
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ free(p->zErrPrefix);
+ i++;
+ p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]);
+ }else{
+ dotCmdError(p, i, "unknown option", 0);
+ sqlite3_free(zFile);
+ return 1;
+ }
+ }else if( zFile==0 && eMode==0 ){
+ if( bKeep ){
+ dotCmdError(p, i, "incompatible with prior options",0);
+ goto dotCmdOutput_error;
+ }
+ if( cli_strcmp(z, "memory")==0 && bOnce ){
+ dotCmdError(p, 0, "cannot redirect to \"memory\"", 0);
+ goto dotCmdOutput_error;
+ }
+ if( cli_strcmp(z, "off")==0 ){
+#ifdef _WIN32
+ zFile = sqlite3_mprintf("nul");
+#else
+ zFile = sqlite3_mprintf("/dev/null");
+#endif
+ }else{
+ zFile = sqlite3_mprintf("%s", z);
+ }
+ if( zFile && zFile[0]=='|' ){
+ while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
+ break;
+ }
+ }else{
+ dotCmdError(p, i, "surplus argument", 0);
+ sqlite3_free(zFile);
+ return 1;
+ }
+ }
+ if( zFile==0 && !bKeep ){
+ zFile = sqlite3_mprintf("stdout");
+ shell_check_oom(zFile);
+ }
+ if( bOnce ){
+ p->nPopOutput = 2;
+ }else{
+ p->nPopOutput = 0;
+ }
+ if( !bKeep ) output_reset(p);
+#ifndef SQLITE_NOHAVE_SYSTEM
+ if( eMode=='e' || eMode=='x' || eMode=='w' ){
+ p->doXdgOpen = 1;
+ modePush(p);
+ if( eMode=='x' ){
+ /* spreadsheet mode. Output as CSV. */
+ newTempFile(p, "csv");
+ p->mode.mFlags &= ~MFLG_ECHO;
+ p->mode.eMode = MODE_Csv;
+ modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma);
+ modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf);
+#ifdef _WIN32
+ zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does
+ ** not work without it. */
+#endif
+ }else if( eMode=='w' ){
+ /* web-browser mode. */
+ newTempFile(p, "html");
+ if( !bPlain ) p->mode.eMode = MODE_Www;
+ }else{
+ /* text editor mode */
+ newTempFile(p, "txt");
+ }
+ sqlite3_free(zFile);
+ zFile = sqlite3_mprintf("%s", p->zTempFile);
+ }
+#endif /* SQLITE_NOHAVE_SYSTEM */
+ if( !bKeep ) shell_check_oom(zFile);
+ if( bKeep ){
+ /* no-op */
+ }else if( cli_strcmp(zFile,"memory")==0 ){
+ if( cli_output_capture ){
+ sqlite3_str_free(cli_output_capture);
+ }
+ cli_output_capture = sqlite3_str_new(0);
+ }else if( zFile[0]=='|' ){
+#ifdef SQLITE_OMIT_POPEN
+ eputz("Error: pipes are not supported in this OS\n");
+ output_redir(p, stdout);
+ goto dotCmdOutput_error;
+#else
+ FILE *pfPipe = sqlite3_popen(zFile + 1, "w");
+ if( pfPipe==0 ){
+ assert( stderr!=NULL );
+ cli_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
+ goto dotCmdOutput_error;
+ }else{
+ output_redir(p, pfPipe);
+ if( zBom ) cli_puts(zBom, pfPipe);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
+ }
+#endif
+ }else{
+ FILE *pfFile = output_file_open(p, zFile);
+ if( pfFile==0 ){
+ if( cli_strcmp(zFile,"off")!=0 ){
+ assert( stderr!=NULL );
+ cli_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
+ }
+ goto dotCmdOutput_error;
+ } else {
+ output_redir(p, pfFile);
+ if( zBom ) cli_puts(zBom, pfFile);
+ if( bPlain && eMode=='w' ){
+ cli_puts(
+ "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n",
+ pfFile
+ );
+ }
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
+ }
+ }
+ sqlite3_free(zFile);
+ return 0;
+
+dotCmdOutput_error:
+ sqlite3_free(zFile);
+ return 1;
+}
+
+/*
+** DOT-COMMAND: .check
+** USAGE: .check [OPTIONS] PATTERN
+**
+** Verify results of commands since the most recent .testcase command.
+** Restore output to the console, unless --keep is used.
+**
+** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from
+** subsequent lines of text up to the first line that begins with ENDMARK.
+** All pattern lines and the ENDMARK are discarded.
+**
+** Options:
+** --exact Do an exact comparison including leading and
+** trailing whitespace.
+** --glob Treat PATTERN as a GLOB
+** --keep Do not reset the testcase. More .check commands
+** will follow.
+** --notglob Output should not match PATTERN
+** --show Write testcase output to the screen, for debugging.
+*/
+static int dotCmdCheck(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg; /* Text of the arguments */
+ int i; /* Loop counter */
+ int k; /* Result of pickStr() */
+ char *zTest; /* Textcase result */
+ int bKeep = 0; /* --keep option */
+ char *zCheck = 0; /* PATTERN argument */
+ char *zPattern = 0; /* Actual test pattern */
+ int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --exact */
+ int isOk; /* True if results are OK */
+ sqlite3_int64 iStart = p->lineno; /* Line number of .check statement */
+
+ if( p->zTestcase[0]==0 ){
+ dotCmdError(p, 0, "no .testcase is active", 0);
+ return 1;
+ }
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;
+ if( cli_strcmp(z,"-keep")==0 ){
+ bKeep = 1;
+ }else if( cli_strcmp(z,"-show")==0 ){
+ if( cli_output_capture ){
+ sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture));
+ }
+ bKeep = 1;
+ }else if( z[0]=='-'
+ && (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0
+ ){
+ if( eCheck && eCheck!=k+1 ){
+ dotCmdError(p, i, "incompatible with prior options",0);
+ return 1;
+ }
+ eCheck = k+1;
+ }else if( zCheck ){
+ dotCmdError(p, i, "unknown option", 0);
+ return 1;
+ }else{
+ zCheck = azArg[i];
+ }
+ }
+ if( zCheck==0 ){
+ dotCmdError(p, 0, "no PATTERN specified", 0);
+ return 1;
+ }
+ if( cli_output_capture && sqlite3_str_length(cli_output_capture) ){
+ zTest = sqlite3_str_value(cli_output_capture);
+ shell_check_oom(zTest);
+ }else{
+ zTest = "";
+ }
+ p->nTestRun++;
+ if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){
+ int nCheck = strlen30(zCheck);
+ sqlite3_str *pPattern = sqlite3_str_new(p->db);
+ char zLine[2000];
+ while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){
+ if( strchr(zLine,'\n') ) p->lineno++;
+ if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break;
+ sqlite3_str_appendall(pPattern, zLine);
+ }
+ zPattern = sqlite3_str_finish(pPattern);
+ if( zPattern==0 ){
+ zPattern = sqlite3_mprintf("");
+ }
+ }else{
+ zPattern = zCheck;
+ }
+ shell_check_oom(zPattern);
+ switch( eCheck ){
+ case 1: {
+ char *zGlob = sqlite3_mprintf("*%s*", zPattern);
+ isOk = testcase_glob(zGlob, zTest)!=0;
+ sqlite3_free(zGlob);
+ break;
+ }
+ case 2: {
+ char *zGlob = sqlite3_mprintf("*%s*", zPattern);
+ isOk = testcase_glob(zGlob, zTest)==0;
+ sqlite3_free(zGlob);
+ break;
+ }
+ case 3: {
+ isOk = cli_strcmp(zTest,zPattern)==0;
+ break;
+ }
+ default: {
+ /* Skip leading and trailing \n and \r on both pattern and test output */
+ const char *z1 = zPattern;
+ const char *z2 = zTest;
+ size_t n1, n2;
+ while( z1[0]=='\n' || z1[0]=='\r' ) z1++;
+ n1 = strlen(z1);
+ while( n1>0 && (z1[n1-1]=='\n' || z1[n1-1]=='\r') ) n1--;
+ while( z2[0]=='\n' || z2[0]=='\r' ) z2++;
+ n2 = strlen(z2);
+ while( n2>0 && (z2[n2-1]=='\n' || z2[n2-1]=='\r') ) n2--;
+ isOk = n1==n2 && memcmp(z1,z2,n1)==0;
+ break;
+ }
+ }
+ if( !isOk ){
+ sqlite3_fprintf(stderr,
+ "%s:%lld: .check failed for testcase %s\n",
+ p->zInFile, iStart, p->zTestcase);
+ p->nTestErr++;
+ sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern);
+ sqlite3_fprintf(stderr, "Got: [%s]\n", zTest);
+ }
+ if( zPattern!=zCheck ){
+ sqlite3_free(zPattern);
+ }
+ if( !bKeep ){
+ output_reset(p);
+ p->zTestcase[0] = 0;
+ }
+ return 0;
+}
+
+/*
+** DOT-COMMAND: .testcase
+** USAGE: .testcase [OPTIONS] NAME
+**
+** Start a new test case identified by NAME. All output
+** through the next ".check" command is captured for comparison. See the
+** ".check" commandn for additional informatioon.
+**
+** Options:
+** --error-prefix TEXT Change error message prefix text to TEXT
+*/
+static int dotCmdTestcase(ShellState *p){
+ int nArg = p->dot.nArg; /* Number of arguments */
+ char **azArg = p->dot.azArg; /* Text of the arguments */
+ int i; /* Loop counter */
+ const char *zName = 0; /* Testcase name */
+
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;
+ if( optionMatch(z,"error-prefix") ){
+ if( i+1>=nArg ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ free(p->zErrPrefix);
+ i++;
+ p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]);
+ }else if( zName ){
+ dotCmdError(p, i, "unknown option", 0);
+ return 1;
+ }else{
+ zName = azArg[i];
+ }
+ }
+ output_reset(p);
+ if( cli_output_capture ){
+ sqlite3_str_free(cli_output_capture);
+ }
+ cli_output_capture = sqlite3_str_new(0);
+ if( zName ){
+ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName);
+ }else{
+ sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld",
+ p->zInFile, p->lineno);
+ }
+ return 0;
+}
+
+/*
+** Enlarge the space allocated in p->dot so that it can hold more
+** than nArg parsed command-line arguments.
+*/
+static void parseDotRealloc(ShellState *p, int nArg){
+ p->dot.nAlloc = nArg+22;
+ p->dot.azArg = realloc(p->dot.azArg,p->dot.nAlloc*sizeof(char*));
+ shell_check_oom(p->dot.azArg);
+ p->dot.aiOfst = realloc(p->dot.aiOfst,p->dot.nAlloc*sizeof(int));
+ shell_check_oom(p->dot.aiOfst);
+ p->dot.abQuot = realloc(p->dot.abQuot,p->dot.nAlloc);
+ shell_check_oom(p->dot.abQuot);
+}
+
+
+/*
+** Parse input line zLine up into individual arguments. Retain the
+** parse in the p->dot substructure.
+*/
+static void parseDotCmdArgs(const char *zLine, ShellState *p){
+ char *z;
+ int h = 1;
+ int nArg = 0;
+ size_t szLine;
+
+ p->dot.zOrig = zLine;
+ free(p->dot.zCopy);
+ z = p->dot.zCopy = strdup(zLine);
+ shell_check_oom(z);
+ szLine = strlen(z);
+ while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--;
+ if( szLine>0 && z[szLine-1]==';' ){
+ szLine--;
+ while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--;
+ }
+ z[szLine] = 0;
+ parseDotRealloc(p, 2);
+ while( z[h] ){
+ while( IsSpace(z[h]) ){ h++; }
+ if( z[h]==0 ) break;
+ if( nArg+2>p->dot.nAlloc ){
+ parseDotRealloc(p, nArg);
+ }
+ if( z[h]=='\'' || z[h]=='"' ){
+ int delim = z[h++];
+ p->dot.abQuot[nArg] = 1;
+ p->dot.azArg[nArg] = &z[h];
+ p->dot.aiOfst[nArg] = h;
+ while( z[h] && z[h]!=delim ){
+ if( z[h]=='\\' && delim=='"' && z[h+1]!=0 ) h++;
+ h++;
+ }
+ if( z[h]==delim ){
+ z[h++] = 0;
+ }
+ if( delim=='"' ) resolve_backslashes(p->dot.azArg[nArg]);
+ }else{
+ p->dot.abQuot[nArg] = 0;
+ p->dot.azArg[nArg] = &z[h];
+ p->dot.aiOfst[nArg] = h;
+ while( z[h] && !IsSpace(z[h]) ){ h++; }
+ if( z[h] ) z[h++] = 0;
+ }
+ nArg++;
+ }
+ p->dot.nArg = nArg;
+ p->dot.azArg[nArg] = 0;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
-static int do_meta_command(char *zLine, ShellState *p){
- int h = 1;
- int nArg = 0;
+static int do_meta_command(const char *zLine, ShellState *p){
+ int nArg;
int n, c;
int rc = 0;
- char *azArg[52];
+ char **azArg;
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( p->expert.pExpert ){
expertFinish(p, 1, 0);
}
#endif
- /* Parse the input line into tokens.
+ /* Parse the input line into tokens stored in p->dot.
*/
- while( zLine[h] && nArg<ArraySize(azArg)-1 ){
- while( IsSpace(zLine[h]) ){ h++; }
- if( zLine[h]==0 ) break;
- if( zLine[h]=='\'' || zLine[h]=='"' ){
- int delim = zLine[h++];
- azArg[nArg++] = &zLine[h];
- while( zLine[h] && zLine[h]!=delim ){
- if( zLine[h]=='\\' && delim=='"' && zLine[h+1]!=0 ) h++;
- h++;
- }
- if( zLine[h]==delim ){
- zLine[h++] = 0;
- }
- if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
- }else{
- azArg[nArg++] = &zLine[h];
- while( zLine[h] && !IsSpace(zLine[h]) ){ h++; }
- if( zLine[h] ) zLine[h++] = 0;
- }
- }
- azArg[nArg] = 0;
+ parseDotCmdArgs(zLine, p);
+ nArg = p->dot.nArg;
+ azArg = p->dot.azArg;
/* Process the input line.
*/
@@ -28848,7 +32491,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_OMIT_AUTHORIZATION
if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){
if( nArg!=2 ){
- sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n");
+ cli_printf(stderr, "Usage: .auth ON|OFF\n");
rc = 1;
goto meta_command_exit;
}
@@ -28895,7 +32538,7 @@ static int do_meta_command(char *zLine, ShellState *p){
bAsync = 1;
}else
{
- sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]);
+ dotCmdError(p, j, "unknown option", "should be -append or -async");
return 1;
}
}else if( zDestFile==0 ){
@@ -28904,19 +32547,19 @@ static int do_meta_command(char *zLine, ShellState *p){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
- sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
+ cli_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
return 1;
}
}
if( zDestFile==0 ){
- sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n");
+ cli_printf(stderr, "missing FILENAME argument on .backup\n");
return 1;
}
if( zDb==0 ) zDb = "main";
rc = sqlite3_open_v2(zDestFile, &pDest,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", zDestFile);
close_db(pDest);
return 1;
}
@@ -28977,7 +32620,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = chdir(azArg[1]);
#endif
if( rc ){
- sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]);
rc = 1;
}
}else{
@@ -28996,31 +32639,13 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
-#ifndef SQLITE_SHELL_FIDDLE
/* Cancel output redirection, if it is currently set (by .testcase)
** Then read the content of the testcase-out.txt file and compare against
** azArg[1]. If there are differences, report an error and exit.
*/
if( c=='c' && n>=3 && cli_strncmp(azArg[0], "check", n)==0 ){
- char *zRes = 0;
- output_reset(p);
- if( nArg!=2 ){
- eputz("Usage: .check GLOB-PATTERN\n");
- rc = 2;
- }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
- rc = 2;
- }else if( testcase_glob(azArg[1],zRes)==0 ){
- sqlite3_fprintf(stderr,
- "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
- p->zTestcase, azArg[1], zRes);
- rc = 1;
- }else{
- sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase);
- p->nCheck++;
- }
- sqlite3_free(zRes);
+ rc = dotCmdCheck(p);
}else
-#endif /* !defined(SQLITE_SHELL_FIDDLE) */
#ifndef SQLITE_SHELL_FIDDLE
if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){
@@ -29048,9 +32673,9 @@ static int do_meta_command(char *zLine, ShellState *p){
zFile = "(temporary-file)";
}
if( p->pAuxDb == &p->aAuxDb[i] ){
- sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile);
+ cli_printf(stdout, "ACTIVE %d: %s\n", i, zFile);
}else if( p->aAuxDb[i].db!=0 ){
- sqlite3_fprintf(stdout, " %d: %s\n", i, zFile);
+ cli_printf(stdout, " %d: %s\n", i, zFile);
}
}
}else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
@@ -29086,12 +32711,17 @@ static int do_meta_command(char *zLine, ShellState *p){
){
if( nArg==2 ){
#ifdef _WIN32
- p->crlfMode = booleanValue(azArg[1]);
+ if( booleanValue(azArg[1]) ){
+ p->mode.mFlags |= MFLG_CRLF;
+ }else{
+ p->mode.mFlags &= ~MFLG_CRLF;
+ }
#else
- p->crlfMode = 0;
+ p->mode.mFlags &= ~MFLG_CRLF;
#endif
}
- sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF");
+ cli_printf(stderr, "crlf is %s\n",
+ (p->mode.mFlags & MFLG_CRLF)!=0 ? "ON" : "OFF");
}else
if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){
@@ -29109,7 +32739,7 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
if( zSchema==0 || zFile==0 ) continue;
- azName = sqlite3_realloc(azName, (nName+1)*2*sizeof(char*));
+ azName = sqlite3_realloc64(azName, (nName+1)*2*sizeof(char*));
shell_check_oom(azName);
azName[nName*2] = strdup(zSchema);
azName[nName*2+1] = strdup(zFile);
@@ -29121,7 +32751,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int eTxn = sqlite3_txn_state(p->db, azName[i*2]);
int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]);
const char *z = azName[i*2+1];
- sqlite3_fprintf(p->out, "%s: %s %s%s\n",
+ cli_printf(p->out, "%s: %s %s%s\n",
azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w",
eTxn==SQLITE_TXN_NONE ? "" :
eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn");
@@ -29147,6 +32777,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER },
{ "enable_view", SQLITE_DBCONFIG_ENABLE_VIEW },
{ "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
+ { "fp_digits", SQLITE_DBCONFIG_FP_DIGITS },
{ "legacy_alter_table", SQLITE_DBCONFIG_LEGACY_ALTER_TABLE },
{ "legacy_file_format", SQLITE_DBCONFIG_LEGACY_FILE_FORMAT },
{ "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
@@ -29163,16 +32794,24 @@ static int do_meta_command(char *zLine, ShellState *p){
for(ii=0; ii<ArraySize(aDbConfig); ii++){
if( nArg>1 && cli_strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
if( nArg>=3 ){
- sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
+ if( aDbConfig[ii].op==SQLITE_DBCONFIG_FP_DIGITS ){
+ sqlite3_db_config(p->db, aDbConfig[ii].op, atoi(azArg[2]), 0);
+ }else{
+ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
+ }
}
sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
- sqlite3_fprintf(p->out, "%19s %s\n",
- aDbConfig[ii].zName, v ? "on" : "off");
+ if( aDbConfig[ii].op==SQLITE_DBCONFIG_FP_DIGITS ){
+ cli_printf(p->out, "%19s %d\n", aDbConfig[ii].zName, v);
+ }else{
+ cli_printf(p->out, "%19s %s\n",
+ aDbConfig[ii].zName, v ? "on" : "off");
+ }
if( nArg>1 ) break;
}
if( nArg>1 && ii==ArraySize(aDbConfig) ){
- sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]);
- eputz("Enter \".dbconfig\" with no arguments for a list\n");
+ dotCmdError(p, 1, "unknown dbconfig",
+ "Enter \".dbconfig\" with no arguments for a list");
}
}else
@@ -29191,19 +32830,19 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zLike = 0;
char *zSql;
int i;
- int savedShowHeader = p->showHeader;
int savedShellFlags = p->shellFlgs;
+ Mode saved_mode;
ShellClearFlag(p,
- SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo
- |SHFLG_DumpDataOnly|SHFLG_DumpNoSys);
+ SHFLG_PreserveRowid|SHFLG_DumpDataOnly|SHFLG_DumpNoSys);
for(i=1; i<nArg; i++){
if( azArg[i][0]=='-' ){
const char *z = azArg[i]+1;
if( z[0]=='-' ) z++;
if( cli_strcmp(z,"preserve-rowids")==0 ){
#ifdef SQLITE_OMIT_VIRTUALTABLE
- eputz("The --preserve-rowids option is not compatible"
- " with SQLITE_OMIT_VIRTUALTABLE\n");
+ dotCmdError(p, i, "unable",
+ "The --preserve-rowids option is not compatible"
+ " with SQLITE_OMIT_VIRTUALTABLE");
rc = 1;
sqlite3_free(zLike);
goto meta_command_exit;
@@ -29212,7 +32851,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#endif
}else
if( cli_strcmp(z,"newlines")==0 ){
- ShellSetFlag(p, SHFLG_Newlines);
+ /*ShellSetFlag(p, SHFLG_Newlines);*/
}else
if( cli_strcmp(z,"data-only")==0 ){
ShellSetFlag(p, SHFLG_DumpDataOnly);
@@ -29221,8 +32860,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ShellSetFlag(p, SHFLG_DumpNoSys);
}else
{
- sqlite3_fprintf(stderr,
- "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
+ dotCmdError(p, i, "unknown option", 0);
rc = 1;
sqlite3_free(zLike);
goto meta_command_exit;
@@ -29252,16 +32890,17 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
+ modeDup(&saved_mode, &p->mode);
outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
- sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out);
- sqlite3_fputs("BEGIN TRANSACTION;\n", p->out);
+ cli_puts("PRAGMA foreign_keys=OFF;\n", p->out);
+ cli_puts("BEGIN TRANSACTION;\n", p->out);
}
p->writableSchema = 0;
- p->showHeader = 0;
+ p->mode.spec.bTitles = QRF_No;
/* Set writable_schema=ON since doing so forces SQLite to initialize
** as much of the schema as it can even if the sqlite_schema table is
** corrupt. */
@@ -29290,21 +32929,27 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_free(zLike);
if( p->writableSchema ){
- sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out);
+ cli_puts("PRAGMA writable_schema=OFF;\n", p->out);
p->writableSchema = 0;
}
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
- sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out);
+ cli_puts(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out);
}
- p->showHeader = savedShowHeader;
p->shellFlgs = savedShellFlags;
+ modeFree(&p->mode);
+ p->mode = saved_mode;
+ rc = p->nErr>0;
}else
if( c=='e' && cli_strncmp(azArg[0], "echo", n)==0 ){
if( nArg==2 ){
- setOrClearFlag(p, SHFLG_Echo, azArg[1]);
+ if( booleanValue(azArg[1]) ){
+ p->mode.mFlags |= MFLG_ECHO;
+ }else{
+ p->mode.mFlags &= ~MFLG_ECHO;
+ }
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
@@ -29312,33 +32957,30 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
+ open_db(p, 0);
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
- p->autoEQPtest = 0;
- if( p->autoEQPtrace ){
+ if( p->mode.autoEQPtrace ){
if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
- p->autoEQPtrace = 0;
+ p->mode.autoEQPtrace = 0;
}
if( cli_strcmp(azArg[1],"full")==0 ){
- p->autoEQP = AUTOEQP_full;
+ p->mode.autoEQP = AUTOEQP_full;
}else if( cli_strcmp(azArg[1],"trigger")==0 ){
- p->autoEQP = AUTOEQP_trigger;
+ p->mode.autoEQP = AUTOEQP_trigger;
#ifdef SQLITE_DEBUG
- }else if( cli_strcmp(azArg[1],"test")==0 ){
- p->autoEQP = AUTOEQP_on;
- p->autoEQPtest = 1;
}else if( cli_strcmp(azArg[1],"trace")==0 ){
- p->autoEQP = AUTOEQP_full;
- p->autoEQPtrace = 1;
+ p->mode.autoEQP = AUTOEQP_full;
+ p->mode.autoEQPtrace = 1;
open_db(p, 0);
sqlite3_exec(p->db, "SELECT name FROM sqlite_schema LIMIT 1", 0, 0, 0);
sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0);
#endif
}else{
- p->autoEQP = (u8)booleanValue(azArg[1]);
+ p->mode.autoEQP = (u8)booleanValue(azArg[1]);
}
}else{
eputz("Usage: .eqp off|on|trace|trigger|full\n");
@@ -29348,7 +32990,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
if( c=='e' && cli_strncmp(azArg[0], "exit", n)==0 ){
- if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
+ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) cli_exit(rc);
rc = 2;
}else
#endif
@@ -29356,31 +32998,19 @@ static int do_meta_command(char *zLine, ShellState *p){
/* The ".explain" command is automatic now. It is largely pointless. It
** retained purely for backwards compatibility */
if( c=='e' && cli_strncmp(azArg[0], "explain", n)==0 ){
- int val = 1;
if( nArg>=2 ){
if( cli_strcmp(azArg[1],"auto")==0 ){
- val = 99;
+ p->mode.autoExplain = 1;
}else{
- val = booleanValue(azArg[1]);
+ p->mode.autoExplain = booleanValue(azArg[1]);
}
}
- if( val==1 && p->mode!=MODE_Explain ){
- p->normalMode = p->mode;
- p->mode = MODE_Explain;
- p->autoExplain = 0;
- }else if( val==0 ){
- if( p->mode==MODE_Explain ) p->mode = p->normalMode;
- p->autoExplain = 0;
- }else if( val==99 ){
- if( p->mode==MODE_Explain ) p->mode = p->normalMode;
- p->autoExplain = 1;
- }
}else
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Cannot run experimental commands such as \"%s\" in safe mode\n",
azArg[0]);
rc = 1;
@@ -29438,9 +33068,9 @@ static int do_meta_command(char *zLine, ShellState *p){
/* --help lists all file-controls */
if( cli_strcmp(zCmd,"help")==0 ){
- sqlite3_fputs("Available file-controls:\n", p->out);
+ cli_puts("Available file-controls:\n", p->out);
for(i=0; i<ArraySize(aCtrl); i++){
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
@@ -29456,7 +33086,7 @@ static int do_meta_command(char *zLine, ShellState *p){
filectrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
- sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n"
+ cli_printf(stderr,"Error: ambiguous file-control: \"%s\"\n"
"Use \".filectrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
@@ -29464,7 +33094,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( filectrl<0 ){
- sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n"
+ cli_printf(stderr,"Error: unknown file-control: %s\n"
"Use \".filectrl --help\" for help\n", zCmd);
}else{
switch(filectrl){
@@ -29508,7 +33138,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg!=2 ) break;
sqlite3_file_control(p->db, zSchema, filectrl, &z);
if( z ){
- sqlite3_fprintf(p->out, "%s\n", z);
+ cli_printf(p->out, "%s\n", z);
sqlite3_free(z);
}
isOk = 2;
@@ -29522,31 +33152,30 @@ static int do_meta_command(char *zLine, ShellState *p){
}
x = -1;
sqlite3_file_control(p->db, zSchema, filectrl, &x);
- sqlite3_fprintf(p->out, "%d\n", x);
+ cli_printf(p->out, "%d\n", x);
isOk = 2;
break;
}
}
}
if( isOk==0 && iCtrl>=0 ){
- sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n",
+ cli_printf(p->out, "Usage: .filectrl %s %s\n",
zCmd, aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
char zBuf[100];
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes);
- sqlite3_fprintf(p->out, "%s\n", zBuf);
+ cli_printf(p->out, "%s\n", zBuf);
}
}else
if( c=='f' && cli_strncmp(azArg[0], "fullschema", n)==0 ){
ShellState data;
int doStats = 0;
- memcpy(&data, p, sizeof(data));
- data.showHeader = 0;
- data.cMode = data.mode = MODE_Semi;
+ int hasStat[5];
+ int flgs = 0;
+ char *zSql;
if( nArg==2 && optionMatch(azArg[1], "indent") ){
- data.cMode = data.mode = MODE_Pretty;
nArg = 1;
}
if( nArg!=1 ){
@@ -29555,43 +33184,61 @@ static int do_meta_command(char *zLine, ShellState *p){
goto meta_command_exit;
}
open_db(p, 0);
- rc = sqlite3_exec(p->db,
- "SELECT sql FROM"
+ zSql = sqlite3_mprintf(
+ "SELECT shell_format_schema(sql,%d) FROM"
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_schema UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) "
- "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' "
- "ORDER BY x",
- callback, &data, 0
- );
+ "WHERE type!='meta' AND sql NOTNULL"
+ " AND name NOT LIKE 'sqlite__%%' ESCAPE '_' "
+ "ORDER BY x", flgs);
+ memcpy(&data, p, sizeof(data));
+ data.mode.spec.bTitles = QRF_No;
+ data.mode.eMode = MODE_List;
+ data.mode.spec.eText = QRF_TEXT_Plain;
+ data.mode.spec.nCharLimit = 0;
+ data.mode.spec.zRowSep = "\n";
+ rc = shell_exec(&data,zSql,0);
+ sqlite3_free(zSql);
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt;
+ memset(hasStat, 0, sizeof(hasStat));
rc = sqlite3_prepare_v2(p->db,
- "SELECT rowid FROM sqlite_schema"
+ "SELECT substr(name,12,1) FROM sqlite_schema"
" WHERE name GLOB 'sqlite_stat[134]'",
-1, &pStmt, 0);
if( rc==SQLITE_OK ){
- doStats = sqlite3_step(pStmt)==SQLITE_ROW;
- sqlite3_finalize(pStmt);
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int k = sqlite3_column_int(pStmt,0);
+ assert( k==1 || k==3 || k==4 );
+ hasStat[k] = 1;
+ doStats = 1;
+ }
}
+ sqlite3_finalize(pStmt);
}
if( doStats==0 ){
- sqlite3_fputs("/* No STAT tables available */\n", p->out);
+ cli_puts("/* No STAT tables available */\n", p->out);
}else{
- sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
- data.cMode = data.mode = MODE_Insert;
- data.zDestTable = "sqlite_stat1";
- shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
- data.zDestTable = "sqlite_stat4";
- shell_exec(&data, "SELECT * FROM sqlite_stat4", 0);
- sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
+ cli_puts("ANALYZE sqlite_schema;\n", p->out);
+ data.mode.eMode = MODE_Insert;
+ if( hasStat[1] ){
+ data.mode.spec.zTableName = "sqlite_stat1";
+ shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
+ }
+ if( hasStat[4] ){
+ data.mode.spec.zTableName = "sqlite_stat4";
+ shell_exec(&data, "SELECT * FROM sqlite_stat4", 0);
+ }
+ cli_puts("ANALYZE sqlite_schema;\n", p->out);
}
}else
if( c=='h' && cli_strncmp(azArg[0], "headers", n)==0 ){
if( nArg==2 ){
- p->showHeader = booleanValue(azArg[1]);
- p->shellFlgs |= SHFLG_HeaderSet;
+ p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No;
+ p->mode.mFlags |= MFLG_HDR;
+ p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr;
}else{
eputz("Usage: .headers on|off\n");
rc = 1;
@@ -29602,7 +33249,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg>=2 ){
n = showHelp(p->out, azArg[1]);
if( n==0 ){
- sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]);
+ cli_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
}
}else{
showHelp(p->out, 0);
@@ -29611,326 +33258,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
if( c=='i' && cli_strncmp(azArg[0], "import", n)==0 ){
- char *zTable = 0; /* Insert data into this table */
- char *zSchema = 0; /* Schema of zTable */
- char *zFile = 0; /* Name of file to extra content from */
- sqlite3_stmt *pStmt = NULL; /* A statement */
- int nCol; /* Number of columns in the table */
- i64 nByte; /* Number of bytes in an SQL string */
- int i, j; /* Loop counters */
- int needCommit; /* True to COMMIT or ROLLBACK at end */
- int nSep; /* Number of bytes in p->colSeparator[] */
- char *zSql = 0; /* An SQL statement */
- ImportCtx sCtx; /* Reader context */
- char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */
- int eVerbose = 0; /* Larger for more console output */
- int nSkip = 0; /* Initial lines to skip */
- int useOutputMode = 1; /* Use output mode to determine separators */
- char *zCreate = 0; /* CREATE TABLE statement text */
-
- failIfSafeMode(p, "cannot run .import in safe mode");
- memset(&sCtx, 0, sizeof(sCtx));
- if( p->mode==MODE_Ascii ){
- xRead = ascii_read_one_field;
- }else{
- xRead = csv_read_one_field;
- }
- rc = 1;
- for(i=1; i<nArg; i++){
- char *z = azArg[i];
- if( z[0]=='-' && z[1]=='-' ) z++;
- if( z[0]!='-' ){
- if( zFile==0 ){
- zFile = z;
- }else if( zTable==0 ){
- zTable = z;
- }else{
- sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z);
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- }else if( cli_strcmp(z,"-v")==0 ){
- eVerbose++;
- }else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){
- zSchema = azArg[++i];
- }else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){
- nSkip = integerValue(azArg[++i]);
- }else if( cli_strcmp(z,"-ascii")==0 ){
- sCtx.cColSep = SEP_Unit[0];
- sCtx.cRowSep = SEP_Record[0];
- xRead = ascii_read_one_field;
- useOutputMode = 0;
- }else if( cli_strcmp(z,"-csv")==0 ){
- sCtx.cColSep = ',';
- sCtx.cRowSep = '\n';
- xRead = csv_read_one_field;
- useOutputMode = 0;
- }else{
- sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z);
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- }
- if( zTable==0 ){
- sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n",
- zFile==0 ? "FILE" : "TABLE");
- showHelp(p->out, "import");
- goto meta_command_exit;
- }
- seenInterrupt = 0;
- open_db(p, 0);
- if( useOutputMode ){
- /* If neither the --csv or --ascii options are specified, then set
- ** the column and row separator characters from the output mode. */
- nSep = strlen30(p->colSeparator);
- if( nSep==0 ){
- eputz("Error: non-null column separator required for import\n");
- goto meta_command_exit;
- }
- if( nSep>1 ){
- eputz("Error: multi-character column separators not allowed"
- " for import\n");
- goto meta_command_exit;
- }
- nSep = strlen30(p->rowSeparator);
- if( nSep==0 ){
- eputz("Error: non-null row separator required for import\n");
- goto meta_command_exit;
- }
- if( nSep==2 && p->mode==MODE_Csv
- && cli_strcmp(p->rowSeparator,SEP_CrLf)==0
- ){
- /* When importing CSV (only), if the row separator is set to the
- ** default output row separator, change it to the default input
- ** row separator. This avoids having to maintain different input
- ** and output row separators. */
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- nSep = strlen30(p->rowSeparator);
- }
- if( nSep>1 ){
- eputz("Error: multi-character row separators not allowed"
- " for import\n");
- goto meta_command_exit;
- }
- sCtx.cColSep = (u8)p->colSeparator[0];
- sCtx.cRowSep = (u8)p->rowSeparator[0];
- }
- sCtx.zFile = zFile;
- sCtx.nLine = 1;
- if( sCtx.zFile[0]=='|' ){
-#ifdef SQLITE_OMIT_POPEN
- eputz("Error: pipes are not supported in this OS\n");
- goto meta_command_exit;
-#else
- sCtx.in = sqlite3_popen(sCtx.zFile+1, "r");
- sCtx.zFile = "<pipe>";
- sCtx.xCloser = pclose;
-#endif
- }else{
- sCtx.in = sqlite3_fopen(sCtx.zFile, "rb");
- sCtx.xCloser = fclose;
- }
- if( sCtx.in==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile);
- goto meta_command_exit;
- }
- if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
- char zSep[2];
- zSep[1] = 0;
- zSep[0] = sCtx.cColSep;
- sqlite3_fputs("Column separator ", p->out);
- output_c_string(p->out, zSep);
- sqlite3_fputs(", row separator ", p->out);
- zSep[0] = sCtx.cRowSep;
- output_c_string(p->out, zSep);
- sqlite3_fputs("\n", p->out);
- }
- sCtx.z = sqlite3_malloc64(120);
- if( sCtx.z==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- /* Below, resources must be freed before exit. */
- while( (nSkip--)>0 ){
- while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
- }
- import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
- if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
- && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
- " WHERE name=%Q AND type='view'",
- zSchema ? zSchema : "main", zTable)
- ){
- /* Table does not exist. Create it. */
- sqlite3 *dbCols = 0;
- char *zRenames = 0;
- char *zColDefs;
- zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
- zSchema ? zSchema : "main", zTable);
- while( xRead(&sCtx) ){
- zAutoColumn(sCtx.z, &dbCols, 0);
- if( sCtx.cTerm!=sCtx.cColSep ) break;
- }
- zColDefs = zAutoColumn(0, &dbCols, &zRenames);
- if( zRenames!=0 ){
- sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
- "Columns renamed during .import %s due to duplicates:\n"
- "%s\n", sCtx.zFile, zRenames);
- sqlite3_free(zRenames);
- }
- assert(dbCols==0);
- if( zColDefs==0 ){
- sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile);
- import_cleanup(&sCtx);
- rc = 1;
- sqlite3_free(zCreate);
- goto meta_command_exit;
- }
- zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
- if( zCreate==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- if( eVerbose>=1 ){
- sqlite3_fprintf(p->out, "%s\n", zCreate);
- }
- rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
- if( rc ){
- sqlite3_fprintf(stderr,
- "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
- }
- sqlite3_free(zCreate);
- zCreate = 0;
- if( rc ){
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- }
- zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);",
- zTable, zSchema);
- if( zSql==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- zSql = 0;
- if( rc ){
- if (pStmt) sqlite3_finalize(pStmt);
- shellDatabaseError(p->db);
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- if( sqlite3_step(pStmt)==SQLITE_ROW ){
- nCol = sqlite3_column_int(pStmt, 0);
- }else{
- nCol = 0;
- }
- sqlite3_finalize(pStmt);
- pStmt = 0;
- if( nCol==0 ) return 0; /* no columns, no error */
-
- nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
- + (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
- + strlen(zTable)*2 + 2 /* Quoted table name */
- + nCol*2; /* Space for ",?" for each column */
- zSql = sqlite3_malloc64( nByte );
- if( zSql==0 ){
- import_cleanup(&sCtx);
- shell_out_of_memory();
- }
- if( zSchema ){
- sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
- zSchema, zTable);
- }else{
- sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
- }
- j = strlen30(zSql);
- for(i=1; i<nCol; i++){
- zSql[j++] = ',';
- zSql[j++] = '?';
- }
- zSql[j++] = ')';
- zSql[j] = 0;
- assert( j<nByte );
- if( eVerbose>=2 ){
- sqlite3_fprintf(p->out, "Insert using: %s\n", zSql);
- }
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
- sqlite3_free(zSql);
- zSql = 0;
- if( rc ){
- shellDatabaseError(p->db);
- if (pStmt) sqlite3_finalize(pStmt);
- import_cleanup(&sCtx);
- rc = 1;
- goto meta_command_exit;
- }
- needCommit = sqlite3_get_autocommit(p->db);
- if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
- do{
- int startLine = sCtx.nLine;
- for(i=0; i<nCol; i++){
- char *z = xRead(&sCtx);
- /*
- ** Did we reach end-of-file before finding any columns?
- ** If so, stop instead of NULL filling the remaining columns.
- */
- if( z==0 && i==0 ) break;
- /*
- ** Did we reach end-of-file OR end-of-line before finding any
- ** columns in ASCII mode? If so, stop instead of NULL filling
- ** the remaining columns.
- */
- if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
- /*
- ** For CSV mode, per RFC 4180, accept EOF in lieu of final
- ** record terminator but only for last field of multi-field row.
- ** (If there are too few fields, it's not valid CSV anyway.)
- */
- if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
- z = "";
- }
- sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
- if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
- sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d"
- " - filling the rest with NULL\n",
- sCtx.zFile, startLine, nCol, i+1);
- i += 2;
- while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
- }
- }
- if( sCtx.cTerm==sCtx.cColSep ){
- do{
- xRead(&sCtx);
- i++;
- }while( sCtx.cTerm==sCtx.cColSep );
- sqlite3_fprintf(stderr,
- "%s:%d: expected %d columns but found %d - extras ignored\n",
- sCtx.zFile, startLine, nCol, i);
- }
- if( i>=nCol ){
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
- if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n",
- sCtx.zFile, startLine, sqlite3_errmsg(p->db));
- sCtx.nErr++;
- }else{
- sCtx.nRow++;
- }
- }
- }while( sCtx.cTerm!=EOF );
-
- import_cleanup(&sCtx);
- sqlite3_finalize(pStmt);
- if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
- if( eVerbose>0 ){
- sqlite3_fprintf(p->out,
- "Added %d rows with %d errors using %d lines of input\n",
- sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
- }
+ rc = dotCmdImport(p);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
@@ -29943,12 +33271,6 @@ static int do_meta_command(char *zLine, ShellState *p){
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
- if( !ShellHasFlag(p,SHFLG_TestingMode) ){
- sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n",
- "imposter");
- rc = 1;
- goto meta_command_exit;
- }
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
eputz("Usage: .imposter INDEX IMPOSTER\n"
" .imposter off\n");
@@ -29969,10 +33291,10 @@ static int do_meta_command(char *zLine, ShellState *p){
}
zSql = sqlite3_mprintf(
"SELECT rootpage, 0 FROM sqlite_schema"
- " WHERE name='%q' AND type='index'"
+ " WHERE type='index' AND lower(name)=lower('%q')"
"UNION ALL "
"SELECT rootpage, 1 FROM sqlite_schema"
- " WHERE name='%q' AND type='table'"
+ " WHERE type='table' AND lower(name)=lower('%q')"
" AND sql LIKE '%%without%%rowid%%'",
azArg[1], azArg[1]
);
@@ -30010,7 +33332,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_finalize(pStmt);
if( i==0 || tnum==0 ){
- sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"no such index: \"%s\"\n", azArg[1]);
rc = 1;
sqlite3_free(zCollist);
goto meta_command_exit;
@@ -30020,27 +33342,110 @@ static int do_meta_command(char *zLine, ShellState *p){
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
azArg[2], zCollist, lenPK, zCollist);
sqlite3_free(zCollist);
- rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 2, tnum);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
}else{
- sqlite3_fprintf(stdout, "%s;\n", zSql);
- sqlite3_fprintf(stdout,
- "WARNING: writing to an imposter table will corrupt"
- " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index");
+ cli_printf(stdout, "%s;\n", zSql);
}
}else{
- sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
+ cli_printf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
+ if( c=='i' && (cli_strncmp(azArg[0], "indices", n)==0
+ || cli_strncmp(azArg[0], "indexes", n)==0)
+ ){
+ sqlite3_str *pSql;
+ int i;
+ int allFlag = 0;
+ int sysFlag = 0;
+ int exprFlag = 0;
+ int debugFlag = 0; /* Undocument --debug flag */
+ const char *zPattern = 0;
+ const char *zSep = "WHERE";
+
+ for(i=1; i<nArg; i++){
+ if( azArg[i][0]=='-' ){
+ const char *z = azArg[i]+1;
+ if( z[0]=='-' ) z++;
+ if( cli_strcmp(z,"all")==0 || cli_strcmp(z,"a")==0 ){
+ allFlag = 1;
+ }else
+ if( cli_strcmp(z,"sys")==0 ){
+ sysFlag = 1;
+ allFlag = 0;
+ }else
+ if( cli_strcmp(z,"expr")==0 ){
+ exprFlag = 1;
+ allFlag = 0;
+ }else
+ if( cli_strcmp(z,"debug")==0 ){
+ debugFlag = 1;
+ }else
+ {
+ dotCmdError(p, i, "unknown option", 0);
+ rc = 1;
+ goto meta_command_exit;
+ }
+ }else if( zPattern==0 ){
+ zPattern = azArg[i];
+ }else{
+ dotCmdError(p, i, "unknown argument", 0);
+ rc = 1;
+ goto meta_command_exit;
+ }
+ }
+
+ open_db(p, 0);
+ pSql = sqlite3_str_new(p->db);
+ sqlite3_str_appendf(pSql,
+ "SELECT if(t.schema='main',i.name,t.schema||'.'||i.name)\n"
+ "FROM pragma_table_list t, pragma_index_list(t.name,t.schema) i\n"
+ );
+ if( exprFlag ){
+ allFlag = 0;
+ sqlite3_str_appendf(pSql,
+ "%s (EXISTS(SELECT 1 FROM pragma_index_xinfo(i.name) WHERE cid=-2)\n"
+ " OR\n"
+ " EXISTS(SELECT cid FROM pragma_table_xinfo(t.name) WHERE hidden=2"
+ " INTERSECT "
+ " SELECT cid FROM pragma_index_info(i.name)))\n", zSep);
+ zSep = "AND";
+ }
+ if( sysFlag ){
+ sqlite3_str_appendf(pSql,
+ "%s i.name LIKE 'sqlite__autoindex__%%' ESCAPE '_'\n", zSep);
+ zSep = "AND";
+ }else if( !allFlag ){
+ sqlite3_str_appendf(pSql,
+ "%s i.name NOT LIKE 'sqlite__%%' ESCAPE '_'\n", zSep);
+ zSep = "AND";
+ }
+ if( zPattern ){
+ sqlite3_str_appendf(pSql, "%s i.name LIKE '%%%q%%'\n", zSep, zPattern);
+ }
+ sqlite3_str_appendf(pSql, "ORDER BY 1");
+
+ /* Run the SQL statement in "split" mode. */
+ if( debugFlag ){
+ cli_printf(stdout,"%s;\n", sqlite3_str_value(pSql));
+ }else{
+ modePush(p);
+ modeChange(p, MODE_Split);
+ shell_exec(p, sqlite3_str_value(pSql), 0);
+ modePop(p);
+ }
+ sqlite3_str_free(pSql);
+ }else
+
if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){
i64 iArg = 0;
if( nArg==2 ){
@@ -30048,7 +33453,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( iArg==0 ) iArg = -1;
}
if( (nArg!=1 && nArg!=2) || iArg<0 ){
- sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n");
+ cli_printf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n");
rc = 1;
goto meta_command_exit;
}
@@ -30069,7 +33474,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
iotrace = sqlite3_fopen(azArg[1], "w");
if( iotrace==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
sqlite3IoTrace = 0;
rc = 1;
}else{
@@ -30088,6 +33493,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ "sql_length", SQLITE_LIMIT_SQL_LENGTH },
{ "column", SQLITE_LIMIT_COLUMN },
{ "expr_depth", SQLITE_LIMIT_EXPR_DEPTH },
+ { "parser_depth", SQLITE_LIMIT_PARSER_DEPTH },
{ "compound_select", SQLITE_LIMIT_COMPOUND_SELECT },
{ "vdbe_op", SQLITE_LIMIT_VDBE_OP },
{ "function_arg", SQLITE_LIMIT_FUNCTION_ARG },
@@ -30101,7 +33507,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
if( nArg==1 ){
for(i=0; i<ArraySize(aLimit); i++){
- sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName,
+ cli_printf(stdout, "%20s %d\n", aLimit[i].zLimitName,
sqlite3_limit(p->db, aLimit[i].limitCode, -1));
}
}else if( nArg>3 ){
@@ -30116,14 +33522,14 @@ static int do_meta_command(char *zLine, ShellState *p){
if( iLimit<0 ){
iLimit = i;
}else{
- sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]);
rc = 1;
goto meta_command_exit;
}
}
}
if( iLimit<0 ){
- sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n"
+ cli_printf(stderr,"unknown limit: \"%s\"\n"
"enter \".limits\" with no arguments for a list.\n",
azArg[1]);
rc = 1;
@@ -30132,9 +33538,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==3 ){
sqlite3_limit(p->db, aLimit[iLimit].limitCode,
(int)integerValue(azArg[2]));
+ }else{
+ cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName,
+ sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1));
}
- sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName,
- sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1));
}
}else
@@ -30182,174 +33589,12 @@ static int do_meta_command(char *zLine, ShellState *p){
}
output_file_close(p->pLog);
if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout";
- p->pLog = output_file_open(zFile);
+ p->pLog = output_file_open(p, zFile);
}
}else
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
- const char *zMode = 0;
- const char *zTabname = 0;
- int i, n2;
- int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */
- ColModeOpts cmOpts = ColModeOpts_default;
- for(i=1; i<nArg; i++){
- const char *z = azArg[i];
- if( optionMatch(z,"wrap") && i+1<nArg ){
- cmOpts.iWrap = integerValue(azArg[++i]);
- chng |= 1;
- }else if( optionMatch(z,"ww") ){
- cmOpts.bWordWrap = 1;
- chng |= 1;
- }else if( optionMatch(z,"wordwrap") && i+1<nArg ){
- cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
- chng |= 1;
- }else if( optionMatch(z,"quote") ){
- cmOpts.bQuote = 1;
- chng |= 1;
- }else if( optionMatch(z,"noquote") ){
- cmOpts.bQuote = 0;
- chng |= 1;
- }else if( optionMatch(z,"escape") && i+1<nArg ){
- /* See similar code at tag-20250224-1 */
- const char *zEsc = azArg[++i];
- int k;
- for(k=0; k<ArraySize(shell_EscModeNames); k++){
- if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
- p->eEscMode = k;
- chng |= 2;
- break;
- }
- }
- if( k>=ArraySize(shell_EscModeNames) ){
- sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
- " - choices:", zEsc);
- for(k=0; k<ArraySize(shell_EscModeNames); k++){
- sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
- }
- sqlite3_fprintf(stderr, "\n");
- rc = 1;
- goto meta_command_exit;
- }
- }else if( zMode==0 ){
- zMode = z;
- /* Apply defaults for qbox pseudo-mode. If that
- * overwrites already-set values, user was informed of this.
- */
- chng |= 1;
- if( cli_strcmp(z, "qbox")==0 ){
- ColModeOpts cmo = ColModeOpts_default_qbox;
- zMode = "box";
- cmOpts = cmo;
- }
- }else if( zTabname==0 ){
- zTabname = z;
- }else if( z[0]=='-' ){
- sqlite3_fprintf(stderr,"unknown option: %s\n", z);
- eputz("options:\n"
- " --escape MODE\n"
- " --noquote\n"
- " --quote\n"
- " --wordwrap on/off\n"
- " --wrap N\n"
- " --ww\n");
- rc = 1;
- goto meta_command_exit;
- }else{
- sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
- rc = 1;
- goto meta_command_exit;
- }
- }
- if( !chng ){
- if( p->mode==MODE_Column
- || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
- ){
- sqlite3_fprintf(p->out,
- "current output mode: %s --wrap %d --wordwrap %s "
- "--%squote --escape %s\n",
- modeDescr[p->mode], p->cmOpts.iWrap,
- p->cmOpts.bWordWrap ? "on" : "off",
- p->cmOpts.bQuote ? "" : "no",
- shell_EscModeNames[p->eEscMode]
- );
- }else{
- sqlite3_fprintf(p->out,
- "current output mode: %s --escape %s\n",
- modeDescr[p->mode],
- shell_EscModeNames[p->eEscMode]
- );
- }
- }
- if( zMode==0 ){
- zMode = modeDescr[p->mode];
- if( (chng&1)==0 ) cmOpts = p->cmOpts;
- }
- n2 = strlen30(zMode);
- if( cli_strncmp(zMode,"lines",n2)==0 ){
- p->mode = MODE_Line;
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( cli_strncmp(zMode,"columns",n2)==0 ){
- p->mode = MODE_Column;
- if( (p->shellFlgs & SHFLG_HeaderSet)==0 ){
- p->showHeader = 1;
- }
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- p->cmOpts = cmOpts;
- }else if( cli_strncmp(zMode,"list",n2)==0 ){
- p->mode = MODE_List;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( cli_strncmp(zMode,"html",n2)==0 ){
- p->mode = MODE_Html;
- }else if( cli_strncmp(zMode,"tcl",n2)==0 ){
- p->mode = MODE_Tcl;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( cli_strncmp(zMode,"csv",n2)==0 ){
- p->mode = MODE_Csv;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
- }else if( cli_strncmp(zMode,"tabs",n2)==0 ){
- p->mode = MODE_List;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
- }else if( cli_strncmp(zMode,"insert",n2)==0 ){
- p->mode = MODE_Insert;
- set_table_name(p, zTabname ? zTabname : "table");
- if( p->eEscMode==SHELL_ESC_OFF ){
- ShellSetFlag(p, SHFLG_Newlines);
- }else{
- ShellClearFlag(p, SHFLG_Newlines);
- }
- }else if( cli_strncmp(zMode,"quote",n2)==0 ){
- p->mode = MODE_Quote;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
- }else if( cli_strncmp(zMode,"ascii",n2)==0 ){
- p->mode = MODE_Ascii;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
- }else if( cli_strncmp(zMode,"markdown",n2)==0 ){
- p->mode = MODE_Markdown;
- p->cmOpts = cmOpts;
- }else if( cli_strncmp(zMode,"table",n2)==0 ){
- p->mode = MODE_Table;
- p->cmOpts = cmOpts;
- }else if( cli_strncmp(zMode,"box",n2)==0 ){
- p->mode = MODE_Box;
- p->cmOpts = cmOpts;
- }else if( cli_strncmp(zMode,"count",n2)==0 ){
- p->mode = MODE_Count;
- }else if( cli_strncmp(zMode,"off",n2)==0 ){
- p->mode = MODE_Off;
- }else if( cli_strncmp(zMode,"json",n2)==0 ){
- p->mode = MODE_Json;
- }else{
- eputz("Error: mode should be one of: "
- "ascii box column csv html insert json line list markdown "
- "qbox quote table tabs tcl\n");
- rc = 1;
- }
- p->cMode = p->mode;
+ rc = dotCmdMode(p);
}else
#ifndef SQLITE_SHELL_FIDDLE
@@ -30358,9 +33603,9 @@ static int do_meta_command(char *zLine, ShellState *p){
eputz("Usage: .nonce NONCE\n");
rc = 1;
}else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){
- sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n",
+ cli_printf(stderr,"line %lld: incorrect nonce: \"%s\"\n",
p->lineno, azArg[1]);
- exit(1);
+ cli_exit(1);
}else{
p->bSafeMode = 0;
return 0; /* Return immediately to bypass the safe mode reset
@@ -30371,8 +33616,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='n' && cli_strncmp(azArg[0], "nullvalue", n)==0 ){
if( nArg==2 ){
- sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
- "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
+ modeSetStr(&p->mode.spec.zNull, azArg[1]);
}else{
eputz("Usage: .nullvalue STRING\n");
rc = 1;
@@ -30385,6 +33629,9 @@ static int do_meta_command(char *zLine, ShellState *p){
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
int openMode = SHELL_OPEN_UNSPEC;
+ int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+
+ if( p->bSafeMode ) openFlags = SQLITE_OPEN_READONLY;
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
@@ -30393,31 +33640,38 @@ static int do_meta_command(char *zLine, ShellState *p){
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
- }else if( optionMatch(z, "zip") ){
+ }else if( optionMatch(z, "zip") && !p->bSafeMode ){
openMode = SHELL_OPEN_ZIPFILE;
#endif
- }else if( optionMatch(z, "append") ){
+ }else if( optionMatch(z, "append") && !p->bSafeMode ){
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
- openMode = SHELL_OPEN_READONLY;
+ openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
+ openFlags |= SQLITE_OPEN_READONLY;
+ }else if( optionMatch(z, "exclusive") ){
+ openFlags |= SQLITE_OPEN_EXCLUSIVE;
+ }else if( optionMatch(z, "ifexists") ){
+ openFlags &= ~(SQLITE_OPEN_CREATE);
}else if( optionMatch(z, "nofollow") ){
- p->openFlags |= SQLITE_OPEN_NOFOLLOW;
+ openFlags |= SQLITE_OPEN_NOFOLLOW;
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
openMode = SHELL_OPEN_HEXDB;
+ }else if( optionMatch(z, "normal") ){
+ openMode = SHELL_OPEN_NORMAL;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_OMIT_DESERIALIZE */
}else
#endif /* !SQLITE_SHELL_FIDDLE */
if( z[0]=='-' ){
- sqlite3_fprintf(stderr,"unknown option: %s\n", z);
+ cli_printf(stderr,"unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
}else if( zFN ){
- sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
+ cli_printf(stderr,"extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}else{
@@ -30433,12 +33687,21 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(p->pAuxDb->zFreeOnClose);
p->pAuxDb->zFreeOnClose = 0;
p->openMode = openMode;
- p->openFlags = 0;
+ p->openFlags = openFlags;
p->szMax = 0;
/* If a filename is specified, try to open it first */
if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
- if( newFlag && zFN && !p->bSafeMode ) shellDeleteFile(zFN);
+ if( newFlag && zFN && !p->bSafeMode ){
+ if( cli_strncmp(zFN,"file:",5)==0 ){
+ char *zDel = shellFilenameFromUri(zFN);
+ shell_check_oom(zDel);
+ shellDeleteFile(zDel);
+ sqlite3_free(zDel);
+ }else{
+ shellDeleteFile(zFN);
+ }
+ }
#ifndef SQLITE_SHELL_FIDDLE
if( p->bSafeMode
&& p->openMode!=SHELL_OPEN_HEXDB
@@ -30459,7 +33722,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename);
+ cli_printf(stderr,"Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
p->pAuxDb->zFreeOnClose = zNewFilename;
@@ -30479,144 +33742,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|| (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0)
|| (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0)
){
- char *zFile = 0;
- int i;
- int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */
- int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */
- int bPlain = 0; /* --plain option */
- static const char *zBomUtf8 = "\357\273\277";
- const char *zBom = 0;
-
- failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
- if( c=='e' ){
- eMode = 'x';
- bOnce = 2;
- }else if( c=='w' ){
- eMode = 'w';
- bOnce = 2;
- }else if( cli_strncmp(azArg[0],"once",n)==0 ){
- bOnce = 1;
- }
- for(i=1; i<nArg; i++){
- char *z = azArg[i];
- if( z[0]=='-' ){
- if( z[1]=='-' ) z++;
- if( cli_strcmp(z,"-bom")==0 ){
- zBom = zBomUtf8;
- }else if( cli_strcmp(z,"-plain")==0 ){
- bPlain = 1;
- }else if( c=='o' && cli_strcmp(z,"-x")==0 ){
- eMode = 'x'; /* spreadsheet */
- }else if( c=='o' && cli_strcmp(z,"-e")==0 ){
- eMode = 'e'; /* text editor */
- }else if( c=='o' && cli_strcmp(z,"-w")==0 ){
- eMode = 'w'; /* Web browser */
- }else{
- sqlite3_fprintf(p->out,
- "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]);
- showHelp(p->out, azArg[0]);
- rc = 1;
- goto meta_command_exit;
- }
- }else if( zFile==0 && eMode==0 ){
- if( cli_strcmp(z, "off")==0 ){
-#ifdef _WIN32
- zFile = sqlite3_mprintf("nul");
-#else
- zFile = sqlite3_mprintf("/dev/null");
-#endif
- }else{
- zFile = sqlite3_mprintf("%s", z);
- }
- if( zFile && zFile[0]=='|' ){
- while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]);
- break;
- }
- }else{
- sqlite3_fprintf(p->out,
- "ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]);
- showHelp(p->out, azArg[0]);
- rc = 1;
- sqlite3_free(zFile);
- goto meta_command_exit;
- }
- }
- if( zFile==0 ){
- zFile = sqlite3_mprintf("stdout");
- }
- shell_check_oom(zFile);
- if( bOnce ){
- p->outCount = 2;
- }else{
- p->outCount = 0;
- }
- output_reset(p);
-#ifndef SQLITE_NOHAVE_SYSTEM
- if( eMode=='e' || eMode=='x' || eMode=='w' ){
- p->doXdgOpen = 1;
- outputModePush(p);
- if( eMode=='x' ){
- /* spreadsheet mode. Output as CSV. */
- newTempFile(p, "csv");
- ShellClearFlag(p, SHFLG_Echo);
- p->mode = MODE_Csv;
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
-#ifdef _WIN32
- zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does
- ** not work without it. */
-#endif
- }else if( eMode=='w' ){
- /* web-browser mode. */
- newTempFile(p, "html");
- if( !bPlain ) p->mode = MODE_Www;
- }else{
- /* text editor mode */
- newTempFile(p, "txt");
- }
- sqlite3_free(zFile);
- zFile = sqlite3_mprintf("%s", p->zTempFile);
- }
-#endif /* SQLITE_NOHAVE_SYSTEM */
- shell_check_oom(zFile);
- if( zFile[0]=='|' ){
-#ifdef SQLITE_OMIT_POPEN
- eputz("Error: pipes are not supported in this OS\n");
- rc = 1;
- output_redir(p, stdout);
-#else
- FILE *pfPipe = sqlite3_popen(zFile + 1, "w");
- if( pfPipe==0 ){
- assert( stderr!=NULL );
- sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
- rc = 1;
- }else{
- output_redir(p, pfPipe);
- if( zBom ) sqlite3_fputs(zBom, pfPipe);
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
- }
-#endif
- }else{
- FILE *pfFile = output_file_open(zFile);
- if( pfFile==0 ){
- if( cli_strcmp(zFile,"off")!=0 ){
- assert( stderr!=NULL );
- sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
- }
- rc = 1;
- } else {
- output_redir(p, pfFile);
- if( zBom ) sqlite3_fputs(zBom, pfFile);
- if( bPlain && eMode=='w' ){
- sqlite3_fputs(
- "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n",
- pfFile
- );
- }
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
- }
- }
- sqlite3_free(zFile);
+ rc = dotCmdOutput(p);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
@@ -30653,7 +33779,7 @@ static int do_meta_command(char *zLine, ShellState *p){
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
@@ -30699,7 +33825,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
- sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
+ cli_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
@@ -30729,10 +33855,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
- if( i>1 ) sqlite3_fputs(" ", p->out);
- sqlite3_fputs(azArg[i], p->out);
+ if( i>1 ) cli_puts(" ", p->out);
+ cli_puts(azArg[i], p->out);
}
- sqlite3_fputs("\n", p->out);
+ cli_puts("\n", p->out);
}else
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
@@ -30759,6 +33885,19 @@ static int do_meta_command(char *zLine, ShellState *p){
p->flgProgress |= SHELL_PROGRESS_ONCE;
continue;
}
+ if( cli_strcmp(z,"timeout")==0 ){
+ if( i==nArg-1 ){
+ dotCmdError(p, i, "missing argument", 0);
+ return 1;
+ }
+ i++;
+ p->tmProgress = atof(azArg[i]);
+ if( p->tmProgress>0.0 ){
+ p->flgProgress = SHELL_PROGRESS_QUIET|SHELL_PROGRESS_TMOUT;
+ if( nn==0 ) nn = 100;
+ }
+ continue;
+ }
if( cli_strcmp(z,"limit")==0 ){
if( i+1>=nArg ){
eputz("Error: missing argument on --limit\n");
@@ -30769,7 +33908,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
continue;
}
- sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]);
+ cli_printf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]);
rc = 1;
goto meta_command_exit;
}else{
@@ -30799,7 +33938,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
if( c=='r' && n>=3 && cli_strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
- int savedLineno = p->lineno;
+ i64 savedLineno = p->lineno;
failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
eputz("Usage: .read FILE\n");
@@ -30813,18 +33952,20 @@ static int do_meta_command(char *zLine, ShellState *p){
#else
p->in = sqlite3_popen(azArg[1]+1, "r");
if( p->in==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
- rc = process_input(p);
+ rc = process_input(p, "<pipe>");
pclose(p->in);
}
#endif
}else if( (p->in = openChrSource(azArg[1]))==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
- rc = process_input(p);
+ char *zFilename = strdup(azArg[1]);
+ rc = process_input(p, zFilename);
+ free(zFilename);
fclose(p->in);
}
p->in = inSaved;
@@ -30854,7 +33995,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", zSrcFile);
close_db(pSrc);
return 1;
}
@@ -30892,21 +34033,21 @@ static int do_meta_command(char *zLine, ShellState *p){
){
if( nArg==2 ){
if( cli_strcmp(azArg[1], "vm")==0 ){
- p->scanstatsOn = 3;
+ p->mode.scanstatsOn = 3;
}else
if( cli_strcmp(azArg[1], "est")==0 ){
- p->scanstatsOn = 2;
+ p->mode.scanstatsOn = 2;
}else{
- p->scanstatsOn = (u8)booleanValue(azArg[1]);
+ p->mode.scanstatsOn = (u8)booleanValue(azArg[1]);
}
open_db(p, 0);
sqlite3_db_config(
- p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
+ p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0
);
#if !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
eputz("Warning: .scanstats not available in this build.\n");
#elif !defined(SQLITE_ENABLE_BYTECODE_VTAB)
- if( p->scanstatsOn==3 ){
+ if( p->mode.scanstatsOn==3 ){
eputz("Warning: \".scanstats vm\" not available in this build.\n");
}
#endif
@@ -30917,7 +34058,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
if( c=='s' && cli_strncmp(azArg[0], "schema", n)==0 ){
- ShellText sSelect;
ShellState data;
char *zErrMsg = 0;
const char *zDiv = "(";
@@ -30925,22 +34065,27 @@ static int do_meta_command(char *zLine, ShellState *p){
int iSchema = 0;
int bDebug = 0;
int bNoSystemTabs = 0;
+ int bIndent = 0;
int ii;
-
+ sqlite3_str *pSql;
+ sqlite3_stmt *pStmt = 0;
+
open_db(p, 0);
memcpy(&data, p, sizeof(data));
- data.showHeader = 0;
- data.cMode = data.mode = MODE_Semi;
- initText(&sSelect);
+ data.mode.spec.bTitles = QRF_No;
+ data.mode.eMode = MODE_List;
+ data.mode.spec.eText = QRF_TEXT_Plain;
+ data.mode.spec.nCharLimit = 0;
+ data.mode.spec.zRowSep = "\n";
for(ii=1; ii<nArg; ii++){
if( optionMatch(azArg[ii],"indent") ){
- data.cMode = data.mode = MODE_Pretty;
+ bIndent = 1;
}else if( optionMatch(azArg[ii],"debug") ){
bDebug = 1;
}else if( optionMatch(azArg[ii],"nosys") ){
bNoSystemTabs = 1;
}else if( azArg[ii][0]=='-' ){
- sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]);
+ cli_printf(stderr,"Unknown option: \"%s\"\n", azArg[ii]);
rc = 1;
goto meta_command_exit;
}else if( zName==0 ){
@@ -30957,96 +34102,85 @@ static int do_meta_command(char *zLine, ShellState *p){
|| sqlite3_strlike(zName,"sqlite_temp_master", '\\')==0
|| sqlite3_strlike(zName,"sqlite_temp_schema", '\\')==0;
if( isSchema ){
- char *new_argv[2], *new_colv[2];
- new_argv[0] = sqlite3_mprintf(
- "CREATE TABLE %s (\n"
+ cli_printf(p->out,
+ "CREATE TABLE %ssqlite_schema (\n"
" type text,\n"
" name text,\n"
" tbl_name text,\n"
" rootpage integer,\n"
" sql text\n"
- ")", zName);
- shell_check_oom(new_argv[0]);
- new_argv[1] = 0;
- new_colv[0] = "sql";
- new_colv[1] = 0;
- callback(&data, 1, new_argv, new_colv);
- sqlite3_free(new_argv[0]);
+ ");\n",
+ sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : ""
+ );
}
}
- if( zDiv ){
- sqlite3_stmt *pStmt = 0;
- rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list",
- -1, &pStmt, 0);
- if( rc ){
- shellDatabaseError(p->db);
- sqlite3_finalize(pStmt);
- rc = 1;
- goto meta_command_exit;
- }
- appendText(&sSelect, "SELECT sql FROM", 0);
- iSchema = 0;
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
- const char *zDb = (const char*)sqlite3_column_text(pStmt, 0);
- char zScNum[30];
- sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema);
- appendText(&sSelect, zDiv, 0);
- zDiv = " UNION ALL ";
- appendText(&sSelect, "SELECT shell_add_schema(sql,", 0);
- if( sqlite3_stricmp(zDb, "main")!=0 ){
- appendText(&sSelect, zDb, '\'');
- }else{
- appendText(&sSelect, "NULL", 0);
- }
- appendText(&sSelect, ",name) AS sql, type, tbl_name, name, rowid,", 0);
- appendText(&sSelect, zScNum, 0);
- appendText(&sSelect, " AS snum, ", 0);
- appendText(&sSelect, zDb, '\'');
- appendText(&sSelect, " AS sname FROM ", 0);
- appendText(&sSelect, zDb, quoteChar(zDb));
- appendText(&sSelect, ".sqlite_schema", 0);
- }
+ rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
+ if( rc ){
+ shellDatabaseError(p->db);
sqlite3_finalize(pStmt);
-#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS
- if( zName ){
- appendText(&sSelect,
- " UNION ALL SELECT shell_module_schema(name),"
- " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list",
- 0);
+
+ rc = 1;
+ goto meta_command_exit;
+ }
+ pSql = sqlite3_str_new(p->db);
+ sqlite3_str_appendf(pSql, "SELECT sql FROM", 0);
+ iSchema = 0;
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ const char *zDb = (const char*)sqlite3_column_text(pStmt, 1);
+ char zScNum[30];
+ sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema);
+ sqlite3_str_appendall(pSql, zDiv);
+ zDiv = " UNION ALL ";
+ if( sqlite3_stricmp(zDb, "main")==0 ){
+ sqlite3_str_appendf(pSql,
+ "SELECT shell_format_schema(shell_add_schema(sql,NULL,name),%d)",
+ bIndent);
+ }else{
+ sqlite3_str_appendf(pSql,
+ "SELECT shell_format_schema(shell_add_schema(sql,%Q,name),%d)",
+ zDb, bIndent);
}
+ sqlite3_str_appendf(pSql,
+ " AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname",
+ ++iSchema, zDb);
+ sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb);
+ }
+ sqlite3_finalize(pStmt);
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) \
+ && !defined(SQLITE_OMIT_VIRTUALTABLE)
+ if( zName ){
+ sqlite3_str_appendall(pSql,
+ " UNION ALL SELECT shell_module_schema(name),"
+ " 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list");
+ }
#endif
- appendText(&sSelect, ") WHERE ", 0);
- if( zName ){
- char *zQarg = sqlite3_mprintf("%Q", zName);
- int bGlob;
- shell_check_oom(zQarg);
- bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
- strchr(zName, '[') != 0;
- if( strchr(zName, '.') ){
- appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0);
- }else{
- appendText(&sSelect, "lower(tbl_name)", 0);
- }
- appendText(&sSelect, bGlob ? " GLOB " : " LIKE ", 0);
- appendText(&sSelect, zQarg, 0);
- if( !bGlob ){
- appendText(&sSelect, " ESCAPE '\\' ", 0);
- }
- appendText(&sSelect, " AND ", 0);
- sqlite3_free(zQarg);
- }
- if( bNoSystemTabs ){
- appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0);
+ sqlite3_str_appendf(pSql, ") WHERE ", 0);
+ if( zName ){
+ int bGlob;
+ bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 ||
+ strchr(zName, '[') != 0;
+ if( strchr(zName, '.') ){
+ sqlite3_str_appendall(pSql, "lower(format('%%s.%%s',sname,tbl_name))");
+ }else{
+ sqlite3_str_appendall(pSql, "lower(tbl_name)");
}
- appendText(&sSelect, "sql IS NOT NULL"
- " ORDER BY snum, rowid", 0);
- if( bDebug ){
- sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z);
+ if( bGlob ){
+ sqlite3_str_appendf(pSql, " GLOB %Q AND ", zName);
}else{
- rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
+ sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName);
}
- freeText(&sSelect);
}
+ if( bNoSystemTabs ){
+ sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ");
+ }
+ sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid");
+ if( bDebug ){
+ cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql));
+ }else{
+ rc = shell_exec(&data, sqlite3_str_value(pSql), &zErrMsg);
+ }
+ sqlite3_str_free(pSql);
+
if( zErrMsg ){
shellEmitError(zErrMsg);
sqlite3_free(zErrMsg);
@@ -31102,7 +34236,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"ERROR: sqlite3session_attach() returns %d\n",rc);
rc = 0;
}
@@ -31122,7 +34256,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( pSession->p==0 ) goto session_not_open;
out = sqlite3_fopen(azCmd[1], "wb");
if( out==0 ){
- sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n",
+ cli_printf(stderr,"ERROR: cannot open \"%s\" for writing\n",
azCmd[1]);
}else{
int szChng;
@@ -31133,12 +34267,12 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3session_patchset(pSession->p, &szChng, &pChng);
}
if( rc ){
- sqlite3_fprintf(stdout, "Error: error code %d\n", rc);
+ cli_printf(stdout, "Error: error code %d\n", rc);
rc = 0;
}
if( pChng
&& fwrite(pChng, szChng, 1, out)!=1 ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"ERROR: Failed to write entire %d-byte output\n", szChng);
}
sqlite3_free(pChng);
@@ -31166,7 +34300,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"session %s enable flag = %d\n", pSession->zName, ii);
}
}else
@@ -31175,7 +34309,8 @@ static int do_meta_command(char *zLine, ShellState *p){
** Set a list of GLOB patterns of table names to be excluded.
*/
if( cli_strcmp(azCmd[0], "filter")==0 ){
- int ii, nByte;
+ int ii;
+ i64 nByte;
if( nCmd<2 ) goto session_syntax_error;
if( pAuxDb->nSession ){
for(ii=0; ii<pSession->nFilter; ii++){
@@ -31183,7 +34318,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_free(pSession->azFilter);
nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
- pSession->azFilter = sqlite3_malloc( nByte );
+ pSession->azFilter = sqlite3_malloc64( nByte );
shell_check_oom( pSession->azFilter );
for(ii=1; ii<nCmd; ii++){
char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
@@ -31202,7 +34337,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"session %s indirect flag = %d\n", pSession->zName, ii);
}
}else
@@ -31215,7 +34350,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=1 ) goto session_syntax_error;
if( pAuxDb->nSession ){
ii = sqlite3session_isempty(pSession->p);
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"session %s isempty flag = %d\n", pSession->zName, ii);
}
}else
@@ -31225,7 +34360,7 @@ static int do_meta_command(char *zLine, ShellState *p){
*/
if( cli_strcmp(azCmd[0],"list")==0 ){
for(i=0; i<pAuxDb->nSession; i++){
- sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
+ cli_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
}
}else
@@ -31240,19 +34375,19 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<pAuxDb->nSession; i++){
if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
- sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName);
+ cli_printf(stderr,"Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
goto meta_command_exit;
}
pSession = &pAuxDb->aSession[pAuxDb->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
- sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc);
+ cli_printf(stderr,"Cannot open session: error code=%d\n", rc);
rc = 0;
goto meta_command_exit;
}
@@ -31276,7 +34411,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int i, v;
for(i=1; i<nArg; i++){
v = booleanValue(azArg[i]);
- sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
+ cli_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
}
}
if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){
@@ -31285,7 +34420,7 @@ static int do_meta_command(char *zLine, ShellState *p){
char zBuf[200];
v = integerValue(azArg[i]);
sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
- sqlite3_fputs(zBuf, p->out);
+ cli_puts(zBuf, p->out);
}
}
}else
@@ -31312,9 +34447,9 @@ static int do_meta_command(char *zLine, ShellState *p){
bVerbose++;
}else
{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
- sqlite3_fputs("Should be one of: --init -v\n", stderr);
+ cli_puts("Should be one of: --init -v\n", stderr);
rc = 1;
goto meta_command_exit;
}
@@ -31359,34 +34494,34 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zAns==0 ) continue;
k = 0;
if( bVerbose>0 ){
- sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql);
+ cli_printf(stdout, "%d: %s %s\n", tno, zOp, zSql);
}
if( cli_strcmp(zOp,"memo")==0 ){
- sqlite3_fprintf(p->out, "%s\n", zSql);
+ cli_printf(p->out, "%s\n", zSql);
}else
if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
str.n = 0;
- str.z[0] = 0;
+ str.zTxt[0] = 0;
rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
nTest++;
if( bVerbose ){
- sqlite3_fprintf(p->out, "Result: %s\n", str.z);
+ cli_printf(p->out, "Result: %s\n", str.zTxt);
}
if( rc || zErrMsg ){
nErr++;
rc = 1;
- sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
+ cli_printf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
sqlite3_free(zErrMsg);
- }else if( cli_strcmp(zAns,str.z)!=0 ){
+ }else if( cli_strcmp(zAns,str.zTxt)!=0 ){
nErr++;
rc = 1;
- sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
- sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z);
+ cli_printf(p->out, "%d: Expected: [%s]\n", tno, zAns);
+ cli_printf(p->out, "%d: Got: [%s]\n", tno, str.zTxt);
}
}
else{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
rc = 1;
break;
@@ -31395,7 +34530,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
} /* End loop over k */
freeText(&str);
- sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest);
+ cli_printf(p->out, "%d errors out of %d tests\n", nErr, nTest);
}else
if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){
@@ -31404,12 +34539,10 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
}
if( nArg>=2 ){
- sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator,
- "%.*s", (int)ArraySize(p->colSeparator)-1, azArg[1]);
+ modeSetStr(&p->mode.spec.zColumnSep, azArg[1]);
}
if( nArg>=3 ){
- sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator,
- "%.*s", (int)ArraySize(p->rowSeparator)-1, azArg[2]);
+ modeSetStr(&p->mode.spec.zRowSep,azArg[2]);
}
}else
@@ -31443,7 +34576,7 @@ static int do_meta_command(char *zLine, ShellState *p){
bDebug = 1;
}else
{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
showHelp(p->out, azArg[0]);
rc = 1;
@@ -31467,7 +34600,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
zSql = "SELECT lower(name) as tname FROM sqlite_schema"
" WHERE type='table' AND coalesce(rootpage,0)>1"
- " AND name NOT LIKE 'sqlite_%'"
+ " AND name NOT LIKE 'sqlite__%' ESCAPE '_'"
" ORDER BY 1 collate nocase";
}
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
@@ -31498,7 +34631,7 @@ static int do_meta_command(char *zLine, ShellState *p){
appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
}
appendText(&sSql, zSep, 0);
- appendText(&sSql, sQuery.z, '\'');
+ appendText(&sSql, sQuery.zTxt, '\'');
sQuery.n = 0;
appendText(&sSql, ",", 0);
appendText(&sSql, zTab, '\'');
@@ -31510,19 +34643,19 @@ static int do_meta_command(char *zLine, ShellState *p){
"%s))"
" SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label"
" FROM [sha3sum$query]",
- sSql.z, iSize);
+ sSql.zTxt, iSize);
}else{
zSql = sqlite3_mprintf(
"%s))"
" SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash"
" FROM [sha3sum$query]",
- sSql.z, iSize);
+ sSql.zTxt, iSize);
}
shell_check_oom(zSql);
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
- sqlite3_fprintf(p->out, "%s\n", zSql);
+ cli_printf(p->out, "%s\n", zSql);
}else{
shell_exec(p, zSql, 0);
}
@@ -31532,7 +34665,7 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zRevText = /* Query for reversible to-blob-to-text check */
"SELECT lower(name) as tname FROM sqlite_schema\n"
"WHERE type='table' AND coalesce(rootpage,0)>1\n"
- "AND name NOT LIKE 'sqlite_%%'%s\n"
+ "AND name NOT LIKE 'sqlite__%%' ESCAPE '_'%s\n"
"ORDER BY 1 collate nocase";
zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
zRevText = sqlite3_mprintf(
@@ -31552,7 +34685,7 @@ static int do_meta_command(char *zLine, ShellState *p){
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
- if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText);
+ if( bDebug ) cli_printf(p->out, "%s\n", zRevText);
lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
if( lrc!=SQLITE_OK ){
/* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the
@@ -31565,7 +34698,7 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
sqlite3_stmt *pCheckStmt;
lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
- if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery);
+ if( bDebug ) cli_printf(p->out, "%s\n", zGenQuery);
if( lrc!=SQLITE_OK ){
rc = 1;
}else{
@@ -31573,7 +34706,7 @@ static int do_meta_command(char *zLine, ShellState *p){
double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
if( countIrreversible>0 ){
int sz = (int)(countIrreversible + 0.5);
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Digest includes %d invalidly encoded text field%s.\n",
sz, (sz>1)? "s": "");
}
@@ -31612,7 +34745,7 @@ static int do_meta_command(char *zLine, ShellState *p){
x = zCmd!=0 ? system(zCmd) : 1;
/*consoleRenewSetup();*/
sqlite3_free(zCmd);
- if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x);
+ if( x ) cli_printf(stderr,"System command returns %d\n", x);
}else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */
@@ -31625,48 +34758,51 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = 1;
goto meta_command_exit;
}
- sqlite3_fprintf(p->out, "%12.12s: %s\n","echo",
- azBool[ShellHasFlag(p, SHFLG_Echo)]);
- sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]);
- sqlite3_fprintf(p->out, "%12.12s: %s\n","explain",
- p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
- sqlite3_fprintf(p->out, "%12.12s: %s\n","headers",
- azBool[p->showHeader!=0]);
- if( p->mode==MODE_Column
- || (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
+ cli_printf(p->out, "%12.12s: %s\n","echo",
+ azBool[(p->mode.mFlags & MFLG_ECHO)!=0]);
+ cli_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->mode.autoEQP&3]);
+ cli_printf(p->out, "%12.12s: %s\n","explain",
+ p->mode.autoExplain ? "auto" : "off");
+ cli_printf(p->out, "%12.12s: %s\n","headers",
+ azBool[p->mode.spec.bTitles==QRF_Yes]);
+ if( p->mode.spec.eStyle==QRF_STYLE_Column
+ || p->mode.spec.eStyle==QRF_STYLE_Box
+ || p->mode.spec.eStyle==QRF_STYLE_Table
+ || p->mode.spec.eStyle==QRF_STYLE_Markdown
){
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
- modeDescr[p->mode], p->cmOpts.iWrap,
- p->cmOpts.bWordWrap ? "on" : "off",
- p->cmOpts.bQuote ? "" : "no");
+ aModeInfo[p->mode.eMode].zName, p->mode.spec.nWrap,
+ p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off",
+ p->mode.spec.eText==QRF_TEXT_Sql ? "" : "no");
}else{
- sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
+ cli_printf(p->out, "%12.12s: %s\n","mode",
+ aModeInfo[p->mode.eMode].zName);
}
- sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue");
- output_c_string(p->out, p->nullValue);
- sqlite3_fputs("\n", p->out);
- sqlite3_fprintf(p->out, "%12.12s: %s\n","output",
+ cli_printf(p->out, "%12.12s: ", "nullvalue");
+ output_c_string(p->out, p->mode.spec.zNull);
+ cli_puts("\n", p->out);
+ cli_printf(p->out, "%12.12s: %s\n","output",
strlen30(p->outfile) ? p->outfile : "stdout");
- sqlite3_fprintf(p->out, "%12.12s: ", "colseparator");
- output_c_string(p->out, p->colSeparator);
- sqlite3_fputs("\n", p->out);
- sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator");
- output_c_string(p->out, p->rowSeparator);
- sqlite3_fputs("\n", p->out);
+ cli_printf(p->out, "%12.12s: ", "colseparator");
+ output_c_string(p->out, p->mode.spec.zColumnSep);
+ cli_puts("\n", p->out);
+ cli_printf(p->out, "%12.12s: ", "rowseparator");
+ output_c_string(p->out, p->mode.spec.zRowSep);
+ cli_puts("\n", p->out);
switch( p->statsOn ){
case 0: zOut = "off"; break;
default: zOut = "on"; break;
case 2: zOut = "stmt"; break;
case 3: zOut = "vmstep"; break;
}
- sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut);
- sqlite3_fprintf(p->out, "%12.12s: ", "width");
- for (i=0;i<p->nWidth;i++) {
- sqlite3_fprintf(p->out, "%d ", p->colWidth[i]);
+ cli_printf(p->out, "%12.12s: %s\n","stats", zOut);
+ cli_printf(p->out, "%12.12s: ", "width");
+ for(i=0; i<p->mode.spec.nWidth; i++){
+ cli_printf(p->out, "%d ", (int)p->mode.spec.aWidth[i]);
}
- sqlite3_fputs("\n", p->out);
- sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename",
+ cli_puts("\n", p->out);
+ cli_printf(p->out, "%12.12s: %s\n", "filename",
p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
}else
@@ -31687,16 +34823,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
- if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0)
- || (c=='i' && (cli_strncmp(azArg[0], "indices", n)==0
- || cli_strncmp(azArg[0], "indexes", n)==0) )
- ){
+ if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0) ){
sqlite3_stmt *pStmt;
- char **azResult;
- int nRow, nAlloc;
- int ii;
- ShellText s;
- initText(&s);
+ sqlite3_str *pSql;
+ const char *zPattern = nArg>1 ? azArg[1] : 0;
+
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ){
@@ -31704,112 +34835,46 @@ static int do_meta_command(char *zLine, ShellState *p){
return shellDatabaseError(p->db);
}
- if( nArg>2 && c=='i' ){
- /* It is an historical accident that the .indexes command shows an error
- ** when called with the wrong number of arguments whereas the .tables
- ** command does not. */
- eputz("Usage: .indexes ?LIKE-PATTERN?\n");
- rc = 1;
- sqlite3_finalize(pStmt);
- goto meta_command_exit;
- }
- for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
+ pSql = sqlite3_str_new(p->db);
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 ) continue;
- if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0);
+ if( sqlite3_str_length(pSql) ){
+ sqlite3_str_appendall(pSql, " UNION ALL ");
+ }
if( sqlite3_stricmp(zDbName, "main")==0 ){
- appendText(&s, "SELECT name FROM ", 0);
+ sqlite3_str_appendall(pSql, "SELECT name FROM ");
}else{
- appendText(&s, "SELECT ", 0);
- appendText(&s, zDbName, '\'');
- appendText(&s, "||'.'||name FROM ", 0);
+ sqlite3_str_appendf(pSql, "SELECT %Q||'.'||name FROM ", zDbName);
}
- appendText(&s, zDbName, '"');
- appendText(&s, ".sqlite_schema ", 0);
- if( c=='t' ){
- appendText(&s," WHERE type IN ('table','view')"
- " AND name NOT LIKE 'sqlite_%'"
- " AND name LIKE ?1", 0);
- }else{
- appendText(&s," WHERE type='index'"
- " AND tbl_name LIKE ?1", 0);
+ sqlite3_str_appendf(pSql, "\"%w\".sqlite_schema", zDbName);
+ sqlite3_str_appendf(pSql,
+ " WHERE type IN ('table','view')"
+ " AND name NOT LIKE 'sqlite__%%' ESCAPE '_'"
+ );
+ if( zPattern ){
+ sqlite3_str_appendf(pSql," AND name LIKE %Q", zPattern);
}
}
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
- appendText(&s, " ORDER BY 1", 0);
- rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0);
- }
- freeText(&s);
- if( rc ) return shellDatabaseError(p->db);
-
- /* Run the SQL statement prepared by the above block. Store the results
- ** as an array of nul-terminated strings in azResult[]. */
- nRow = nAlloc = 0;
- azResult = 0;
- if( nArg>1 ){
- sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
- }else{
- sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
- }
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
- if( nRow>=nAlloc ){
- char **azNew;
- int n2 = nAlloc*2 + 10;
- azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
- shell_check_oom(azNew);
- nAlloc = n2;
- azResult = azNew;
- }
- azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
- shell_check_oom(azResult[nRow]);
- nRow++;
- }
- if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
- rc = shellDatabaseError(p->db);
- }
-
- /* Pretty-print the contents of array azResult[] to the output */
- if( rc==0 && nRow>0 ){
- int len, maxlen = 0;
- int i, j;
- int nPrintCol, nPrintRow;
- for(i=0; i<nRow; i++){
- len = strlen30(azResult[i]);
- if( len>maxlen ) maxlen = len;
- }
- nPrintCol = 80/(maxlen+2);
- if( nPrintCol<1 ) nPrintCol = 1;
- nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
- for(i=0; i<nPrintRow; i++){
- for(j=i; j<nRow; j+=nPrintRow){
- char *zSp = j<nPrintRow ? "" : " ";
- sqlite3_fprintf(p->out,
- "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:"");
- }
- sqlite3_fputs("\n", p->out);
- }
+ sqlite3_str_appendall(pSql, " ORDER BY 1");
}
- for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
- sqlite3_free(azResult);
+ /* Run the SQL statement in "split" mode. */
+ modePush(p);
+ modeChange(p, MODE_Split);
+ shell_exec(p, sqlite3_str_value(pSql), 0);
+ sqlite3_str_free(pSql);
+ modePop(p);
+ if( rc ) return shellDatabaseError(p->db);
}else
-#ifndef SQLITE_SHELL_FIDDLE
- /* Begin redirecting output to the file "testcase-out.txt" */
+ /* Set the p->zTestcase name and begin redirecting output into
+ ** the cli_output_capture sqlite3_str */
if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){
- output_reset(p);
- p->out = output_file_open("testcase-out.txt");
- if( p->out==0 ){
- eputz("Error: cannot open 'testcase-out.txt'\n");
- }
- if( nArg>=2 ){
- sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]);
- }else{
- sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "?");
- }
+ rc = dotCmdTestcase(p);
}else
-#endif /* !defined(SQLITE_SHELL_FIDDLE) */
#ifndef SQLITE_UNTESTABLE
if( c=='t' && n>=8 && cli_strncmp(azArg[0], "testctrl", n)==0 ){
@@ -31822,7 +34887,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" },
{"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" },
/*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/
- /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
+ {"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "SIZE INT-ARRAY"},
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." },
@@ -31862,10 +34927,10 @@ static int do_meta_command(char *zLine, ShellState *p){
/* --help lists all test-controls */
if( cli_strcmp(zCmd,"help")==0 ){
- sqlite3_fputs("Available test-controls:\n", p->out);
+ cli_puts("Available test-controls:\n", p->out);
for(i=0; i<ArraySize(aCtrl); i++){
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
- sqlite3_fprintf(p->out, " .testctrl %s %s\n",
+ cli_printf(p->out, " .testctrl %s %s\n",
aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
@@ -31882,7 +34947,7 @@ static int do_meta_command(char *zLine, ShellState *p){
testctrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
- sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n"
+ cli_printf(stderr,"Error: ambiguous test-control: \"%s\"\n"
"Use \".testctrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
@@ -31890,7 +34955,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( testctrl<0 ){
- sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n"
+ cli_printf(stderr,"Error: unknown test-control: %s\n"
"Use \".testctrl --help\" for help\n", zCmd);
}else{
switch(testctrl){
@@ -31921,7 +34986,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ 0x00000080, 1, "Transitive" },
{ 0x00000100, 1, "OmitNoopJoin" },
{ 0x00000200, 1, "CountOfView" },
- { 0x00000400, 1, "CurosrHints" },
+ { 0x00000400, 1, "CursorHints" },
{ 0x00000800, 1, "Stat4" },
{ 0x00001000, 1, "PushDown" },
{ 0x00002000, 1, "SimplifyJoin" },
@@ -31941,6 +35006,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0x20000000, 1, "StarQuery" },
+ { 0x40000000, 1, "ExistsToJoin" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
@@ -31969,13 +35035,13 @@ static int do_meta_command(char *zLine, ShellState *p){
if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break;
}
if( jj>=ArraySize(aLabel) ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error: no such optimization: \"%s\"\n", zLabel);
- sqlite3_fputs("Should be one of:", stderr);
+ cli_puts("Should be one of:", stderr);
for(jj=0; jj<ArraySize(aLabel); jj++){
- sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel);
+ cli_printf(stderr," %s", aLabel[jj].zLabel);
}
- sqlite3_fputs("\n", stderr);
+ cli_puts("\n", stderr);
rc = 1;
goto meta_command_exit;
}
@@ -31993,23 +35059,23 @@ static int do_meta_command(char *zLine, ShellState *p){
if( m & newOpt ) nOff++;
}
if( nOff<12 ){
- sqlite3_fputs("+All", p->out);
+ cli_puts("+All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)!=0 ){
- sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel);
+ cli_printf(p->out, " -%s", aLabel[ii].zLabel);
}
}
}else{
- sqlite3_fputs("-All", p->out);
+ cli_puts("-All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)==0 ){
- sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel);
+ cli_printf(p->out, " +%s", aLabel[ii].zLabel);
}
}
}
- sqlite3_fputs("\n", p->out);
+ cli_puts("\n", p->out);
rc2 = isOk = 3;
break;
}
@@ -32049,7 +35115,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3 *db;
if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){
sqlite3_randomness(sizeof(ii),&ii);
- sqlite3_fprintf(stdout, "-- random seed: %d\n", ii);
+ cli_printf(stdout, "-- random seed: %d\n", ii);
}
if( nArg==3 ){
db = 0;
@@ -32102,7 +35168,7 @@ static int do_meta_command(char *zLine, ShellState *p){
case SQLITE_TESTCTRL_SEEK_COUNT: {
u64 x = 0;
rc2 = sqlite3_test_control(testctrl, p->db, &x);
- sqlite3_fprintf(p->out, "%llu\n", x);
+ cli_printf(p->out, "%llu\n", x);
isOk = 3;
break;
}
@@ -32133,11 +35199,11 @@ static int do_meta_command(char *zLine, ShellState *p){
int val = 0;
rc2 = sqlite3_test_control(testctrl, -id, &val);
if( rc2!=SQLITE_OK ) break;
- if( id>1 ) sqlite3_fputs(" ", p->out);
- sqlite3_fprintf(p->out, "%d: %d", id, val);
+ if( id>1 ) cli_puts(" ", p->out);
+ cli_printf(p->out, "%d: %d", id, val);
id++;
}
- if( id>1 ) sqlite3_fputs("\n", p->out);
+ if( id>1 ) cli_puts("\n", p->out);
isOk = 3;
}
break;
@@ -32160,6 +35226,49 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_test_control(testctrl, &rc2);
break;
+ case SQLITE_TESTCTRL_BITVEC_TEST: {
+ /* Examples:
+ ** .testctrl bitvec_test 100 6,1 -- Show BITVEC constants
+ ** .testctrl bitvec_test 1000 1,12,7,3 -- Simple test
+ ** ---- --------
+ ** size of Bitvec -----^ ^--- aOp array. 0 added at end.
+ **
+ ** See comments on sqlite3BitvecBuiltinTest() for more information
+ ** about the aOp[] array.
+ */
+ int iSize;
+ const char *zTestArg;
+ int nOp;
+ int ii, jj, x;
+ int *aOp;
+ if( nArg!=4 ){
+ cli_printf(stderr,
+ "ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n"
+ );
+ rc = 1;
+ goto meta_command_exit;
+ }
+ isOk = 3;
+ iSize = (int)integerValue(azArg[2]);
+ zTestArg = azArg[3];
+ nOp = (int)strlen(zTestArg)+1;
+ aOp = malloc( sizeof(int)*(nOp+1) );
+ shell_check_oom(aOp);
+ memset(aOp, 0, sizeof(int)*(nOp+1) );
+ for(ii = jj = x = 0; zTestArg[ii]!=0; ii++){
+ if( IsDigit(zTestArg[ii]) ){
+ x = x*10 + zTestArg[ii] - '0';
+ }else{
+ aOp[jj++] = x;
+ x = 0;
+ }
+ }
+ aOp[jj] = x;
+ x = sqlite3_test_control(testctrl, iSize, aOp);
+ cli_printf(p->out, "result: %d\n", x);
+ free(aOp);
+ break;
+ }
case SQLITE_TESTCTRL_FAULT_INSTALL: {
int kk;
int bShowHelp = nArg<=2;
@@ -32179,21 +35288,21 @@ static int do_meta_command(char *zLine, ShellState *p){
faultsim_state.nHit = 0;
sqlite3_test_control(testctrl, faultsim_callback);
}else if( cli_strcmp(z,"status")==0 ){
- sqlite3_fprintf(p->out, "faultsim.iId: %d\n",
+ cli_printf(p->out, "faultsim.iId: %d\n",
faultsim_state.iId);
- sqlite3_fprintf(p->out, "faultsim.iErr: %d\n",
+ cli_printf(p->out, "faultsim.iErr: %d\n",
faultsim_state.iErr);
- sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n",
+ cli_printf(p->out, "faultsim.iCnt: %d\n",
faultsim_state.iCnt);
- sqlite3_fprintf(p->out, "faultsim.nHit: %d\n",
+ cli_printf(p->out, "faultsim.nHit: %d\n",
faultsim_state.nHit);
- sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n",
+ cli_printf(p->out, "faultsim.iInterval: %d\n",
faultsim_state.iInterval);
- sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n",
+ cli_printf(p->out, "faultsim.eVerbose: %d\n",
faultsim_state.eVerbose);
- sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n",
+ cli_printf(p->out, "faultsim.nRepeat: %d\n",
faultsim_state.nRepeat);
- sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n",
+ cli_printf(p->out, "faultsim.nSkip: %d\n",
faultsim_state.nSkip);
}else if( cli_strcmp(z,"-v")==0 ){
if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++;
@@ -32212,7 +35321,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){
bShowHelp = 1;
}else{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Unrecognized fault_install argument: \"%s\"\n",
azArg[kk]);
rc = 1;
@@ -32221,7 +35330,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( bShowHelp ){
- sqlite3_fputs(
+ cli_puts(
"Usage: .testctrl fault_install ARGS\n"
"Possible arguments:\n"
" off Disable faultsim\n"
@@ -32243,13 +35352,13 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}
if( isOk==0 && iCtrl>=0 ){
- sqlite3_fprintf(p->out,
+ cli_printf(p->out,
"Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
- sqlite3_fprintf(p->out, "%d\n", rc2);
+ cli_printf(p->out, "%d\n", rc2);
}else if( isOk==2 ){
- sqlite3_fprintf(p->out, "0x%08x\n", rc2);
+ cli_printf(p->out, "0x%08x\n", rc2);
}
}else
#endif /* !defined(SQLITE_UNTESTABLE) */
@@ -32261,13 +35370,17 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='t' && n>=5 && cli_strncmp(azArg[0], "timer", n)==0 ){
if( nArg==2 ){
- enableTimer = booleanValue(azArg[1]);
- if( enableTimer && !HAS_TIMER ){
+ if( cli_strcmp(azArg[1],"once")==0 ){
+ p->enableTimer = 1;
+ }else{
+ p->enableTimer = 2*booleanValue(azArg[1]);
+ }
+ if( p->enableTimer && !HAS_TIMER ){
eputz("Error: timer not available on this system.\n");
- enableTimer = 0;
+ p->enableTimer = 0;
}
}else{
- eputz("Usage: .timer on|off\n");
+ eputz("Usage: .timer on|off|once\n");
rc = 1;
}
}else
@@ -32304,13 +35417,13 @@ static int do_meta_command(char *zLine, ShellState *p){
mType |= SQLITE_TRACE_CLOSE;
}
else {
- sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z);
+ cli_printf(stderr,"Unknown option \"%s\" on \".trace\"\n", z);
rc = 1;
goto meta_command_exit;
}
}else{
output_file_close(p->traceOut);
- p->traceOut = output_file_open(z);
+ p->traceOut = output_file_open(p, z);
}
}
if( p->traceOut==0 ){
@@ -32349,21 +35462,21 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){
char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit";
- sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
+ cli_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
#if SQLITE_HAVE_ZLIB
- sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion());
+ cli_printf(p->out, "zlib version %s\n", zlibVersion());
#endif
#define CTIMEOPT_VAL_(opt) #opt
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
#if defined(__clang__) && defined(__clang_major__)
- sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "."
+ cli_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "."
CTIMEOPT_VAL(__clang_minor__) "."
CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz);
#elif defined(_MSC_VER)
- sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz);
+ cli_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz);
#elif defined(__GNUC__) && defined(__VERSION__)
- sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz);
+ cli_printf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz);
#endif
}else
@@ -32373,10 +35486,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs);
if( pVfs ){
- sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName);
- sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
- sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
- sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
+ cli_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName);
+ cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
+ cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
+ cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
}
}
}else
@@ -32388,13 +35501,13 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent);
}
for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){
- sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName,
+ cli_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName,
pVfs==pCurrent ? " <--- CURRENT" : "");
- sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
- sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
- sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
+ cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
+ cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
+ cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
if( pVfs->pNext ){
- sqlite3_fputs("-----------------------------------\n", p->out);
+ cli_puts("-----------------------------------\n", p->out);
}
}
}else
@@ -32405,7 +35518,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
if( zVfsName ){
- sqlite3_fprintf(p->out, "%s\n", zVfsName);
+ cli_printf(p->out, "%s\n", zVfsName);
sqlite3_free(zVfsName);
}
}
@@ -32418,28 +35531,31 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='w' && cli_strncmp(azArg[0], "width", n)==0 ){
int j;
- assert( nArg<=ArraySize(azArg) );
- p->nWidth = nArg-1;
- p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
- if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
- if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
+ p->mode.spec.nWidth = nArg-1;
+ p->mode.spec.aWidth = realloc(p->mode.spec.aWidth,
+ (p->mode.spec.nWidth+1)*sizeof(short int));
+ shell_check_oom(p->mode.spec.aWidth);
for(j=1; j<nArg; j++){
- p->colWidth[j-1] = (int)integerValue(azArg[j]);
+ i64 w = integerValue(azArg[j]);
+ if( w < -QRF_MAX_WIDTH ) w = -QRF_MAX_WIDTH;
+ if( w > QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH;
+ p->mode.spec.aWidth[j-1] = (short int)w;
}
}else
{
- sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
+ cli_printf(stderr,"Error: unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
}
meta_command_exit:
- if( p->outCount ){
- p->outCount--;
- if( p->outCount==0 ) output_reset(p);
+ if( p->nPopOutput ){
+ p->nPopOutput--;
+ if( p->nPopOutput==0 ) output_reset(p);
}
p->bSafeMode = p->bSafeModePersist;
+ p->dot.nArg = 0;
return rc;
}
@@ -32669,18 +35785,25 @@ static int doAutoDetectRestore(ShellState *p, const char *zSql){
/*
** Run a single line of SQL. Return the number of errors.
*/
-static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
+static int runOneSqlLine(
+ ShellState *p, /* Execution context */
+ char *zSql, /* SQL to be run */
+ const char *zFilename, /* Source file of the sql */
+ int startline /* linenumber */
+){
int rc;
char *zErrMsg = 0;
open_db(p, 0);
if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
- BEGIN_TIMER;
+ BEGIN_TIMER(p);
rc = shell_exec(p, zSql, &zErrMsg);
- END_TIMER(p->out);
+ END_TIMER(p);
if( rc || zErrMsg ){
- char zPrefix[100];
+ char zPrefix[2048
+ /* must be relatively large:
+ ** https://sqlite.org/forum/forumpost/205f73db1b2806f5 */];
const char *zErrorTail;
const char *zErrorType;
if( zErrMsg==0 ){
@@ -32696,13 +35819,21 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
zErrorType = "Error";
zErrorTail = zErrMsg;
}
- if( in!=0 || !stdin_is_interactive ){
- sqlite3_snprintf(sizeof(zPrefix), zPrefix,
- "%s near line %d:", zErrorType, startline);
+ if( zFilename || !stdin_is_interactive ){
+ if( cli_strcmp(zFilename,"cmdline")==0 ){
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "%s in %r command line argument:", zErrorType, startline);
+ }else if( cli_strcmp(zFilename,"<stdin>")==0 ){
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "%s near line %d:", zErrorType, startline);
+ }else{
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix,
+ "%s near line %d of %s:", zErrorType, startline, zFilename);
+ }
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
- sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail);
+ cli_printf(stderr,"%s %s\n", zPrefix, zErrorTail);
sqlite3_free(zErrMsg);
zErrMsg = 0;
return 1;
@@ -32711,7 +35842,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
- sqlite3_fprintf(p->out, "%s\n", zLineBuf);
+ cli_printf(p->out, "%s\n", zLineBuf);
}
if( doAutoDetectRestore(p, zSql) ) return 1;
@@ -32719,8 +35850,8 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
}
static void echo_group_input(ShellState *p, const char *zDo){
- if( ShellHasFlag(p, SHFLG_Echo) ){
- sqlite3_fprintf(p->out, "%s\n", zDo);
+ if( p->mode.mFlags & MFLG_ECHO ){
+ cli_printf(p->out, "%s\n", zDo);
fflush(p->out);
}
}
@@ -32731,12 +35862,13 @@ static void echo_group_input(ShellState *p, const char *zDo){
** impl because we need the global shellState and cannot access it from that
** function without moving lots of code around (creating a larger/messier diff).
*/
-static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
+static char *one_input_line(ShellState *p, char *zPrior, int isContinuation){
/* Parse the next line from shellState.wasm.zInput. */
const char *zBegin = shellState.wasm.zPos;
const char *z = zBegin;
char *zLine = 0;
i64 nZ = 0;
+ FILE *in = p->in;
UNUSED_PARAMETER(in);
UNUSED_PARAMETER(isContinuation);
@@ -32767,7 +35899,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
**
** Return the number of errors.
*/
-static int process_input(ShellState *p){
+static int process_input(ShellState *p, const char *zSrc){
char *zLine = 0; /* A single input line */
char *zSql = 0; /* Accumulated SQL text */
i64 nLine; /* Length of current line */
@@ -32777,22 +35909,27 @@ static int process_input(ShellState *p){
int errCnt = 0; /* Number of errors seen */
i64 startline = 0; /* Line number for start of current input */
QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */
+ const char *saved_zInFile; /* Prior value of p->zInFile */
+ i64 saved_lineno; /* Prior value of p->lineno */
if( p->inputNesting==MAX_INPUT_NESTING ){
/* This will be more informative in a later version. */
- sqlite3_fprintf(stderr,"Input nesting limit (%d) reached at line %d."
- " Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
+ cli_printf(stderr,"%s: Input nesting limit (%d) reached at line %lld."
+ " Check recursion.\n", zSrc, MAX_INPUT_NESTING, p->lineno);
return 1;
}
++p->inputNesting;
+ saved_zInFile = p->zInFile;
+ p->zInFile = zSrc;
+ saved_lineno = p->lineno;
p->lineno = 0;
CONTINUE_PROMPT_RESET;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
- zLine = one_input_line(p->in, zLine, nSql>0);
+ zLine = one_input_line(p, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
- if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out);
+ if( p->in==0 && stdin_is_interactive ) cli_puts("\n", p->out);
break;
}
if( seenInterrupt ){
@@ -32846,17 +35983,29 @@ static int process_input(ShellState *p){
memcpy(zSql+nSql, zLine, nLine+1);
nSql += nLine;
}
- if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
+ if( nSql>0x7fff0000 ){
+ char zSize[100];
+ sqlite3_snprintf(sizeof(zSize),zSize,"%,lld",nSql);
+ cli_printf(stderr, "%s:%lld: Input SQL is too big: %s bytes\n",
+ zSrc, startline, zSize);
+ nSql = 0;
+ errCnt++;
+ break;
+ }else if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){
echo_group_input(p, zSql);
- errCnt += runOneSqlLine(p, zSql, p->in, startline);
+ errCnt += runOneSqlLine(p, zSql, p->zInFile, startline);
CONTINUE_PROMPT_RESET;
nSql = 0;
- if( p->outCount ){
+ if( p->nPopOutput ){
output_reset(p);
- p->outCount = 0;
+ p->nPopOutput = 0;
}else{
clearTempFile(p);
}
+ if( p->nPopMode ){
+ modePop(p);
+ p->nPopMode = 0;
+ }
p->bSafeMode = p->bSafeModePersist;
qss = QSS_Start;
}else if( nSql && QSS_PLAINWHITE(qss) ){
@@ -32868,12 +36017,14 @@ static int process_input(ShellState *p){
if( nSql ){
/* This may be incomplete. Let the SQL parser deal with that. */
echo_group_input(p, zSql);
- errCnt += runOneSqlLine(p, zSql, p->in, startline);
+ errCnt += runOneSqlLine(p, zSql, p->zInFile, startline);
CONTINUE_PROMPT_RESET;
}
free(zSql);
free(zLine);
--p->inputNesting;
+ p->zInFile = saved_zInFile;
+ p->lineno = saved_lineno;
return errCnt>0;
}
@@ -32947,59 +36098,79 @@ static char *find_home_dir(int clearFlag){
}
/*
-** On non-Windows platforms, look for $XDG_CONFIG_HOME.
-** If ${XDG_CONFIG_HOME}/sqlite3/sqliterc is found, return
-** the path to it. If there is no $(XDG_CONFIG_HOME) then
-** look for $(HOME)/.config/sqlite3/sqliterc and if found
-** return that. If none of these are found, return 0.
+** On non-Windows platforms, look for:
+**
+** - ${zEnvVar}/${zBaseName}
+** - ${HOME}/${zSubdir}/${zBaseName}
+**
+** $zEnvVar is intended to be the name of an XDG_... environment
+** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
+** NULL or getenv(zEnvVar) is NULL then fall back to the second
+** option. If the selected option is not found in the filesystem,
+** return 0.
+**
+** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
+** becomes the fallback.
**
-** The string returned is obtained from sqlite3_malloc() and
-** should be freed by the caller.
+** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
+** will conventionally be ".config" or ".local/state", which, not
+** coincidentally, is the typical subdir of the corresponding XDG_...
+** var with the XDG var's $HOME prefix.
+**
+** The returned string is obtained from sqlite3_malloc() and should be
+** sqlite3_free()'d by the caller.
*/
-static char *find_xdg_config(void){
+static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
+ const char *zBaseName){
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|| defined(__RTP__) || defined(_WRS_KERNEL)
return 0;
#else
- char *zConfig = 0;
- const char *zXdgHome;
+ char *zConfigFile = 0;
+ const char *zXdgDir;
- zXdgHome = getenv("XDG_CONFIG_HOME");
- if( zXdgHome==0 ){
- const char *zHome = getenv("HOME");
- if( zHome==0 ) return 0;
- zConfig = sqlite3_mprintf("%s/.config/sqlite3/sqliterc", zHome);
+ zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
+ if( zXdgDir ){
+ zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
}else{
- zConfig = sqlite3_mprintf("%s/sqlite3/sqliterc", zXdgHome);
+ const char * zHome = find_home_dir(0);
+ if( zHome==0 ) return 0;
+ zConfigFile = (zSubdir && *zSubdir)
+ ? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
+ : sqlite3_mprintf("%s/%s", zHome, zBaseName);
}
- shell_check_oom(zConfig);
- if( access(zConfig,0)!=0 ){
- sqlite3_free(zConfig);
- zConfig = 0;
+ shell_check_oom(zConfigFile);
+ if( access(zConfigFile,0)!=0 ){
+ sqlite3_free(zConfigFile);
+ zConfigFile = 0;
}
- return zConfig;
+ return zConfigFile;
#endif
}
/*
-** Read input from the file given by sqliterc_override. Or if that
-** parameter is NULL, take input from the first of find_xdg_config()
-** or ~/.sqliterc which is found.
+** Read input from the file sqliterc_override. If that parameter is
+** NULL, take it from find_xdg_file(), if found, or fall back to
+** ~/.sqliterc.
**
-** Returns the number of errors.
+** Failure to read the config is only considered a failure if
+** sqliterc_override is not NULL, in which case this function may emit
+** a warning or, if ::bail_on_error is true, fail fatally if the file
+** named by sqliterc_override is not found.
*/
static void process_sqliterc(
ShellState *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
- const char *sqliterc = sqliterc_override;
- char *zBuf = 0;
+ char *sqliterc = (char*)sqliterc_override;
FILE *inSaved = p->in;
- int savedLineno = p->lineno;
+ i64 savedLineno = p->lineno;
if( sqliterc == NULL ){
- sqliterc = zBuf = find_xdg_config();
+ sqliterc = find_xdg_file("XDG_CONFIG_HOME",
+ ".config",
+ "sqlite3/sqliterc");
}
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
@@ -33008,24 +36179,25 @@ static void process_sqliterc(
" cannot read ~/.sqliterc\n");
return;
}
- zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
- shell_check_oom(zBuf);
- sqliterc = zBuf;
+ sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
+ shell_check_oom(sqliterc);
}
- p->in = sqlite3_fopen(sqliterc,"rb");
+ p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
if( p->in ){
if( stdin_is_interactive ){
- sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
+ cli_printf(stderr,"-- Loading resources from %s\n", sqliterc);
}
- if( process_input(p) && bail_on_error ) exit(1);
+ if( process_input(p, sqliterc) && bail_on_error ) cli_exit(1);
fclose(p->in);
}else if( sqliterc_override!=0 ){
- sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
- if( bail_on_error ) exit(1);
+ cli_printf(stderr,"cannot open: \"%s\"\n", sqliterc);
+ if( bail_on_error ) cli_exit(1);
}
p->in = inSaved;
p->lineno = savedLineno;
- sqlite3_free(zBuf);
+ if( sqliterc != sqliterc_override ){
+ sqlite3_free(sqliterc);
+ }
}
/*
@@ -33041,8 +36213,8 @@ static const char zOptions[] =
" -bail stop after hitting an error\n"
" -batch force batch I/O\n"
" -box set output mode to 'box'\n"
- " -column set output mode to 'column'\n"
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
+ " -column set output mode to 'column'\n"
" -csv set output mode to 'csv'\n"
#if !defined(SQLITE_OMIT_DESERIALIZE)
" -deserialize open the database using sqlite3_deserialize()\n"
@@ -33056,6 +36228,7 @@ static const char zOptions[] =
#endif
" -help show this message\n"
" -html set output mode to HTML\n"
+ " -ifexists only open if database already exists\n"
" -interactive force interactive I/O\n"
" -json set output mode to 'json'\n"
" -line set output mode to 'line'\n"
@@ -33072,6 +36245,7 @@ static const char zOptions[] =
#endif
" -newline SEP set output row separator. Default: '\\n'\n"
" -nofollow refuse to open symbolic links to database files\n"
+ " -noinit Do not read the ~/.sqliterc file at startup\n"
" -nonce STRING set the safe-mode escape nonce\n"
" -no-rowid-in-view Disable rowid-in-view using sqlite3_config()\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
@@ -33080,6 +36254,7 @@ static const char zOptions[] =
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
" -safe enable safe-mode\n"
+ " -screenwidth N use N as the default screenwidth \n"
" -separator SEP set output column separator. Default: '|'\n"
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
" -sorterref SIZE sorter references threshold size\n"
@@ -33096,11 +36271,11 @@ static const char zOptions[] =
#endif
;
static void usage(int showDetail){
- sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n"
+ cli_printf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n"
"FILENAME is the name of an SQLite database. A new database is created\n"
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
- sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
+ cli_printf(stderr,"OPTIONS include:\n%s", zOptions);
}else{
eputz("Use the -help option for additional information\n");
}
@@ -33121,19 +36296,11 @@ static void verify_uninitialized(void){
/*
** Initialize the state information in data
*/
-static void main_init(ShellState *data) {
- memset(data, 0, sizeof(*data));
- data->normalMode = data->cMode = data->mode = MODE_List;
- data->autoExplain = 1;
-#ifdef _WIN32
- data->crlfMode = 1;
-#endif
- data->pAuxDb = &data->aAuxDb[0];
- memcpy(data->colSeparator,SEP_Column, 2);
- memcpy(data->rowSeparator,SEP_Row, 2);
- data->showHeader = 0;
- data->shellFlgs = SHFLG_Lookaside;
- sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
+static void main_init(ShellState *p) {
+ memset(p, 0, sizeof(*p));
+ p->pAuxDb = &p->aAuxDb[0];
+ p->shellFlgs = SHFLG_Lookaside;
+ sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p);
#if !defined(SQLITE_SHELL_FIDDLE)
verify_uninitialized();
#endif
@@ -33148,22 +36315,18 @@ static void main_init(ShellState *data) {
*/
#if defined(_WIN32) || defined(WIN32)
static void printBold(const char *zText){
-#if !SQLITE_OS_WINRT
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
SetConsoleTextAttribute(out,
FOREGROUND_RED|FOREGROUND_INTENSITY
);
-#endif
sputz(stdout, zText);
-#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
-#endif
}
#else
static void printBold(const char *zText){
- sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText);
+ cli_printf(stdout, "\033[1m%s\033[0m", zText);
}
#endif
@@ -33173,9 +36336,9 @@ static void printBold(const char *zText){
*/
static char *cmdline_option_value(int argc, char **argv, int i){
if( i==argc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"%s: Error: missing argument to %s\n", argv[0], argv[argc-1]);
- exit(1);
+ cli_exit(1);
}
return argv[i];
}
@@ -33188,34 +36351,85 @@ static void sayAbnormalExit(void){
*/
static int vfstraceOut(const char *z, void *pArg){
ShellState *p = (ShellState*)pArg;
- sqlite3_fputs(z, p->out);
+ cli_puts(z, p->out);
fflush(p->out);
return 1;
}
-#ifndef SQLITE_SHELL_IS_UTF8
-# if (defined(_WIN32) || defined(WIN32)) \
- && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
-# define SQLITE_SHELL_IS_UTF8 (0)
-# else
-# define SQLITE_SHELL_IS_UTF8 (1)
-# endif
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_SHELL_FIDDLE)
+/* Ensure that sqlite3_reset_auto_extension() clears auto-extension
+** memory. https://sqlite.org/forum/forumpost/310cb231b07c80d1.
+** Testing this: if we (inadvertently) remove the
+** sqlite3_reset_auto_extension() call from main(), most shell tests
+** will fail because of a leak message in their output. */
+static int auto_ext_leak_tester(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const struct sqlite3_api_routines *pThunk
+){
+ (void)db; (void)pzErrMsg; (void)pThunk;
+ return SQLITE_OK;
+}
#endif
+/* Alternative name to the entry point for Fiddle */
#ifdef SQLITE_SHELL_FIDDLE
# define main fiddle_main
#endif
-#if SQLITE_SHELL_IS_UTF8
-int SQLITE_CDECL main(int argc, char **argv){
-#else
+/* Use the wmain() entry point on Windows. Translate arguments to
+** UTF8, then invoke the traditional main() entry point which is
+** renamed using a #define to utf8_main() .
+*/
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(main)
+# define main utf8_main /* Rename entry point to utf_main() */
+int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
- char **argv;
-#endif
+ int rc, i;
+ char **argv = malloc( sizeof(char*) * (argc+1) );
+ char **orig = argv;
+ if( argv==0 ){
+ fprintf(stderr, "malloc failed\n");
+ exit(1);
+ }
+ for(i=0; i<argc; i++){
+ int nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0);
+ if( nByte==0 ){
+ argv[i] = 0;
+ }else{
+ argv[i] = malloc( nByte );
+ if( argv[i]==0 ){
+ fprintf(stderr, "malloc failed\n");
+ exit(1);
+ }
+ nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i],nByte,0,0);
+ if( nByte==0 ){
+ free(argv[i]);
+ argv[i] = 0;
+ }
+ }
+ }
+ argv[argc] = 0;
+ rc = utf8_main(argc, argv);
+ for(i=0; i<argc; i++) free(orig[i]);
+ free(argv);
+ return rc;
+}
+#endif /* WIN32 */
+
+/*
+** This is the main entry point for the process. Everything starts here.
+**
+** The "main" identifier may have been #defined to something else:
+**
+** utf8_main On Windows
+** fiddle_main In Fiddle
+** sqlite3_shell Other projects that use shell.c as a subroutine
+*/
+int SQLITE_CDECL main(int argc, char **argv){
#ifdef SQLITE_DEBUG
sqlite3_int64 mem_main_enter = 0;
#endif
- char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
# define data shellState
#else
@@ -33226,15 +36440,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
int rc = 0;
int warnInmemoryDb = 0;
int readStdin = 1;
+ int noInit = 0; /* Do not read ~/.sqliterc if true */
int nCmd = 0;
int nOptsEnd = argc;
int bEnableVfstrace = 0;
char **azCmd = 0;
+ int *aiCmd = 0;
const char *zVfs = 0; /* Value of -vfs command-line option */
-#if !SQLITE_SHELL_IS_UTF8
- char **argvToFree = 0;
- int argcToFree = 0;
-#endif
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
#ifdef SQLITE_SHELL_FIDDLE
@@ -33253,7 +36465,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( getenv("SQLITE_DEBUG_BREAK") ){
if( isatty(0) && isatty(2) ){
char zLine[100];
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"attach debugger to process %d and press ENTER to continue...",
GETPID());
if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0
@@ -33263,11 +36475,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
}else{
#if defined(_WIN32) || defined(WIN32)
-#if SQLITE_OS_WINRT
- __debugbreak();
-#else
DebugBreak();
-#endif
#elif defined(SIGTRAP)
raise(SIGTRAP);
#endif
@@ -33285,7 +36493,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
@@ -33293,32 +36501,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
main_init(&data);
- /* On Windows, we must translate command-line arguments into UTF-8.
- ** The SQLite memory allocator subsystem has to be enabled in order to
- ** do this. But we want to run an sqlite3_shutdown() afterwards so that
- ** subsequent sqlite3_config() calls will work. So copy all results into
- ** memory that does not come from the SQLite memory allocator.
- */
-#if !SQLITE_SHELL_IS_UTF8
- sqlite3_initialize();
- argvToFree = malloc(sizeof(argv[0])*argc*2);
- shell_check_oom(argvToFree);
- argcToFree = argc;
- argv = argvToFree + argc;
- for(i=0; i<argc; i++){
- char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
- i64 n;
- shell_check_oom(z);
- n = strlen(z);
- argv[i] = malloc( n+1 );
- shell_check_oom(argv[i]);
- memcpy(argv[i], z, n+1);
- argvToFree[i] = argv[i];
- sqlite3_free(z);
- }
- sqlite3_shutdown();
-#endif
-
assert( argc>=1 && argv && argv[0] );
Argv0 = argv[0];
@@ -33334,7 +36516,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
#endif
- /* Do an initial pass through the command-line argument to locate
+ /* Do an initial pass through the command-line arguments to locate
** the name of the database file, the name of the initialization file,
** the size of the alternative malloc heap, options affecting commands
** or SQL run from the command line, and the first command to execute.
@@ -33346,16 +36528,20 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char *z;
z = argv[i];
if( z[0]!='-' || i>nOptsEnd ){
- if( data.aAuxDb->zDbFilename==0 ){
+ if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){
data.aAuxDb->zDbFilename = z;
}else{
/* Excess arguments are interpreted as SQL (or dot-commands) and
** mean that nothing is read from stdin */
readStdin = 0;
+ stdin_is_interactive = 0;
nCmd++;
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
shell_check_oom(azCmd);
+ aiCmd = realloc(aiCmd, sizeof(aiCmd[0])*nCmd);
+ shell_check_oom(azCmd);
azCmd[nCmd-1] = z;
+ aiCmd[nCmd-1] = i;
}
continue;
}
@@ -33378,6 +36564,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
+ stdout_is_console = 0;
+ modeChange(&data, MODE_BATCH);
+ }else if( cli_strcmp(z,"-screenwidth")==0 ){
+ int n = atoi(cmdline_option_value(argc, argv, ++i));
+ if( n<2 ){
+ sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n");
+ exit(1);
+ }
+ stdout_tty_width = n;
}else if( cli_strcmp(z,"-utf8")==0 ){
}else if( cli_strcmp(z,"-no-utf8")==0 ){
}else if( cli_strcmp(z,"-no-rowid-in-view")==0 ){
@@ -33400,12 +36595,20 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
- if( sz>70000 ) sz = 70000;
+ if( sz>65536 ) sz = 65536;
if( sz<0 ) sz = 0;
n = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
n = 0xffffffffffffLL/sz;
}
+ if( sz>0 && (sz & (sz-1))==0 ){
+ /* If SIZE is a power of two, round it up by the PCACHE_HDRSZ */
+ int szHdr = 0;
+ sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &szHdr);
+ sz += szHdr;
+ cli_printf(stdout, "Page cache size increased to %d to accommodate"
+ " the %d-byte headers\n", (int)sz, szHdr);
+ }
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
@@ -33418,7 +36621,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( n<0 ) n = 0;
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
- if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
+ if( (i64)sz*(i64)n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
int n;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
@@ -33460,9 +36663,17 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
- data.openMode = SHELL_OPEN_READONLY;
+ data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
+ data.openFlags |= SQLITE_OPEN_READONLY;
}else if( cli_strcmp(z,"-nofollow")==0 ){
- data.openFlags = SQLITE_OPEN_NOFOLLOW;
+ data.openFlags |= SQLITE_OPEN_NOFOLLOW;
+ }else if( cli_strcmp(z,"-noinit")==0 ){
+ noInit = 1;
+ }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
+ data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
+ }else if( cli_strcmp(z,"-ifexists")==0 ){
+ data.openFlags &= ~(SQLITE_OPEN_CREATE);
+ if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A",2)==0 ){
/* All remaining command-line arguments are passed to the ".archive"
@@ -33485,6 +36696,16 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
/* skip over the argument */
i++;
+ }else if( cli_strcmp(z,"-test-argv")==0 ){
+ /* Undocumented test option. Print the values in argv[] and exit.
+ ** Use this to verify that any translation of the argv[], for example
+ ** on Windows that receives wargv[] from the OS and must convert
+ ** to UTF8 prior to calling this routine. */
+ int kk;
+ for(kk=0; kk<argc; kk++){
+ sqlite3_fprintf(stdout,"argv[%d] = \"%s\"\n", kk, argv[kk]);
+ }
+ return 0;
}
}
#ifndef SQLITE_SHELL_FIDDLE
@@ -33511,8 +36732,27 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
- }else{
- sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs);
+ }
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ else if( access(zVfs,0)==0 ){
+ /* If the VFS name is not the name of an existing VFS, but it is
+ ** the name of a file, then try to load that file as an extension.
+ ** Presumably the extension implements the desired VFS. */
+ sqlite3 *db = 0;
+ char *zErr = 0;
+ sqlite3_open(":memory:", &db);
+ sqlite3_enable_load_extension(db, 1);
+ rc = sqlite3_load_extension(db, zVfs, 0, &zErr);
+ sqlite3_close(db);
+ if( (rc&0xff)!=SQLITE_OK ){
+ cli_printf(stderr, "could not load extension VFS \"%s\": %s\n",
+ zVfs, zErr);
+ exit(1);
+ }
+ }
+#endif
+ else{
+ cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs);
exit(1);
}
}
@@ -33522,9 +36762,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
data.pAuxDb->zDbFilename = ":memory:";
warnInmemoryDb = argc==1;
#else
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"%s: Error: no database filename specified\n", Argv0);
- return 1;
+ rc = 1;
+ goto shell_main_exit;
#endif
}
data.out = stdout;
@@ -33533,7 +36774,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_appendvfs_init(0,0,0);
+#ifdef SQLITE_DEBUG
+ sqlite3_auto_extension( (void (*)(void))auto_ext_leak_tester );
+#endif
#endif
+ modeDefault(&data);
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
@@ -33548,9 +36793,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
** is given on the command line, look for a file named ~/.sqliterc and
** try to process it.
*/
- process_sqliterc(&data,zInitFile);
+ if( !noInit ) process_sqliterc(&data,zInitFile);
- /* Make a second pass through the command-line argument and set
+ /* Make a second pass through the command-line arguments and set
** options. This second pass is delayed until after the initialization
** file is processed so that the command-line arguments will override
** settings in the initialization file.
@@ -33562,45 +36807,44 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( cli_strcmp(z,"-init")==0 ){
i++;
}else if( cli_strcmp(z,"-html")==0 ){
- data.mode = MODE_Html;
+ modeChange(&data, MODE_Html);
}else if( cli_strcmp(z,"-list")==0 ){
- data.mode = MODE_List;
+ modeChange(&data, MODE_List);
}else if( cli_strcmp(z,"-quote")==0 ){
- data.mode = MODE_Quote;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator, SEP_Comma);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator, SEP_Row);
+ modeChange(&data, MODE_Quote);
}else if( cli_strcmp(z,"-line")==0 ){
- data.mode = MODE_Line;
+ modeChange(&data, MODE_Line);
}else if( cli_strcmp(z,"-column")==0 ){
- data.mode = MODE_Column;
+ modeChange(&data, MODE_Column);
}else if( cli_strcmp(z,"-json")==0 ){
- data.mode = MODE_Json;
+ modeChange(&data, MODE_Json);
}else if( cli_strcmp(z,"-markdown")==0 ){
- data.mode = MODE_Markdown;
+ modeChange(&data, MODE_Markdown);
}else if( cli_strcmp(z,"-table")==0 ){
- data.mode = MODE_Table;
+ modeChange(&data, MODE_Table);
+ }else if( cli_strcmp(z,"-psql")==0 ){
+ modeChange(&data, MODE_Psql);
}else if( cli_strcmp(z,"-box")==0 ){
- data.mode = MODE_Box;
+ modeChange(&data, MODE_Box);
}else if( cli_strcmp(z,"-csv")==0 ){
- data.mode = MODE_Csv;
- memcpy(data.colSeparator,",",2);
+ modeChange(&data, MODE_Csv);
}else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){
/* See similar code at tag-20250224-1 */
const char *zEsc = argv[++i];
int k;
- for(k=0; k<ArraySize(shell_EscModeNames); k++){
- if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){
- data.eEscMode = k;
+ for(k=0; k<ArraySize(qrfEscNames); k++){
+ if( sqlite3_stricmp(zEsc,qrfEscNames[k])==0 ){
+ data.mode.spec.eEsc = k;
break;
}
}
- if( k>=ArraySize(shell_EscModeNames) ){
- sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\""
+ if( k>=ArraySize(qrfEscNames) ){
+ cli_printf(stderr, "unknown control character escape mode \"%s\""
" - choices:", zEsc);
- for(k=0; k<ArraySize(shell_EscModeNames); k++){
- sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]);
+ for(k=0; k<ArraySize(qrfEscNames); k++){
+ cli_printf(stderr, " %s", qrfEscNames[k]);
}
- sqlite3_fprintf(stderr, "\n");
+ cli_printf(stderr, "\n");
exit(1);
}
#ifdef SQLITE_HAVE_ZLIB
@@ -33616,42 +36860,44 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
- data.openMode = SHELL_OPEN_READONLY;
+ data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
+ data.openFlags |= SQLITE_OPEN_READONLY;
}else if( cli_strcmp(z,"-nofollow")==0 ){
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
+ }else if( cli_strcmp(z,"-noinit")==0 ){
+ /* No-op */
+ }else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
+ data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
+ }else if( cli_strcmp(z,"-ifexists")==0 ){
+ data.openFlags &= ~(SQLITE_OPEN_CREATE);
+ if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
}else if( cli_strcmp(z,"-ascii")==0 ){
- data.mode = MODE_Ascii;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
+ modeChange(&data, MODE_Ascii);
}else if( cli_strcmp(z,"-tabs")==0 ){
- data.mode = MODE_List;
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Tab);
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Row);
+ modeChange(&data, MODE_Tabs);
}else if( cli_strcmp(z,"-separator")==0 ){
- sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
- "%s",cmdline_option_value(argc,argv,++i));
+ modeSetStr(&data.mode.spec.zColumnSep,
+ cmdline_option_value(argc,argv,++i));
}else if( cli_strcmp(z,"-newline")==0 ){
- sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
- "%s",cmdline_option_value(argc,argv,++i));
+ modeSetStr(&data.mode.spec.zRowSep,
+ cmdline_option_value(argc,argv,++i));
}else if( cli_strcmp(z,"-nullvalue")==0 ){
- sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
- "%s",cmdline_option_value(argc,argv,++i));
+ modeSetStr(&data.mode.spec.zNull,
+ cmdline_option_value(argc,argv,++i));
}else if( cli_strcmp(z,"-header")==0 ){
- data.showHeader = 1;
- ShellSetFlag(&data, SHFLG_HeaderSet);
+ data.mode.spec.bTitles = QRF_Yes;
}else if( cli_strcmp(z,"-noheader")==0 ){
- data.showHeader = 0;
- ShellSetFlag(&data, SHFLG_HeaderSet);
+ data.mode.spec.bTitles = QRF_No;
}else if( cli_strcmp(z,"-echo")==0 ){
- ShellSetFlag(&data, SHFLG_Echo);
+ data.mode.mFlags |= MFLG_ECHO;
}else if( cli_strcmp(z,"-eqp")==0 ){
- data.autoEQP = AUTOEQP_on;
+ data.mode.autoEQP = AUTOEQP_on;
}else if( cli_strcmp(z,"-eqpfull")==0 ){
- data.autoEQP = AUTOEQP_full;
+ data.mode.autoEQP = AUTOEQP_full;
}else if( cli_strcmp(z,"-stats")==0 ){
data.statsOn = 1;
}else if( cli_strcmp(z,"-scanstats")==0 ){
- data.scanstatsOn = 1;
+ data.mode.scanstatsOn = 1;
}else if( cli_strcmp(z,"-backslash")==0 ){
/* Undocumented command-line option: -backslash
** Causes C-style backslash escapes to be evaluated in SQL statements
@@ -33662,9 +36908,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
- sqlite3_fprintf(stdout, "%s %s (%d-bit)\n",
+ cli_printf(stdout, "%s %s (%d-bit)\n",
sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
- return 0;
+ rc = 0;
+ goto shell_main_exit;
}else if( cli_strcmp(z,"-interactive")==0 ){
/* Need to check for interactive override here to so that it can
** affect console setup (for Windows only) and testing thereof.
@@ -33672,6 +36919,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
/* already handled */
+ }else if( cli_strcmp(z,"-screenwidth")==0 ){
+ i++;
}else if( cli_strcmp(z,"-utf8")==0 ){
/* already handled */
}else if( cli_strcmp(z,"-no-utf8")==0 ){
@@ -33717,24 +36966,21 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
z = cmdline_option_value(argc,argv,++i);
if( z[0]=='.' ){
rc = do_meta_command(z, &data);
- if( rc && bail_on_error ) return rc==2 ? 0 : rc;
- }else{
- open_db(&data, 0);
- rc = shell_exec(&data, z, &zErrMsg);
- if( zErrMsg!=0 ){
- shellEmitError(zErrMsg);
- if( bail_on_error ) return rc!=0 ? rc : 1;
- }else if( rc!=0 ){
- sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z);
- if( bail_on_error ) return rc;
+ if( rc && (bail_on_error || rc==2) ){
+ if( rc==2 ) rc = 0;
+ goto shell_main_exit;
}
+ }else{
+ rc = runOneSqlLine(&data, z, "cmdline", i);
+ if( bail_on_error ) goto shell_main_exit;
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A", 2)==0 ){
if( nCmd>0 ){
- sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands"
+ cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands"
" with \"%s\"\n", z);
- return 1;
+ rc = 1;
+ goto shell_main_exit;
}
open_db(&data, OPEN_DB_ZIPFILE);
if( z[2] ){
@@ -33744,6 +36990,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
+ stdin_is_interactive = 0;
break;
#endif
}else if( cli_strcmp(z,"-safe")==0 ){
@@ -33751,11 +36998,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
/* Acted upon in first pass. */
}else{
- sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
+ cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
eputz("Use -help for a list of options.\n");
- return 1;
+ rc = 1;
+ goto shell_main_exit;
}
- data.cMode = data.mode;
}
if( !readStdin ){
@@ -33765,26 +37012,46 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
*/
for(i=0; i<nCmd; i++){
echo_group_input(&data, azCmd[i]);
- if( azCmd[i][0]=='.' ){
+ if( isScriptFile(azCmd[i],0) ){
+ FILE *inSaved = data.in;
+ i64 savedLineno = data.lineno;
+ int res = 1;
+ if( (data.in = openChrSource(azCmd[i]))!=0 ){
+ res = process_input(&data, azCmd[i]);
+ fclose(data.in);
+ }
+ data.in = inSaved;
+ data.lineno = savedLineno;
+ if( res ) i = nCmd;
+ }else if( azCmd[i][0]=='.' ){
+ char *zErrCtx = malloc( 64 );
+ shell_check_oom(zErrCtx);
+ sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]);
+ data.zInFile = "<cmdline>";
+ data.zErrPrefix = zErrCtx;
rc = do_meta_command(azCmd[i], &data);
+ free(data.zErrPrefix);
+ data.zErrPrefix = 0;
if( rc ){
if( rc==2 ) rc = 0;
goto shell_main_exit;
}
}else{
- open_db(&data, 0);
- rc = shell_exec(&data, azCmd[i], &zErrMsg);
- if( zErrMsg || rc ){
- if( zErrMsg!=0 ){
- shellEmitError(zErrMsg);
- }else{
- sqlite3_fprintf(stderr,
- "Error: unable to process SQL: %s\n", azCmd[i]);
- }
- sqlite3_free(zErrMsg);
- if( rc==0 ) rc = 1;
+ rc = runOneSqlLine(&data, azCmd[i], "cmdline", aiCmd[i]);
+ if( data.nPopMode ){
+ modePop(&data);
+ data.nPopMode = 0;
+ }
+ if( rc ){
goto shell_main_exit;
}
+
+ }
+ if( data.nPopOutput && azCmd[i][0]!='.' ){
+ output_reset(&data);
+ data.nPopOutput = 0;
+ }else{
+ clearTempFile(&data);
}
}
}else{
@@ -33793,8 +37060,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
- int nHistory;
- sqlite3_fprintf(stdout,
+ cli_printf(stdout,
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid());
@@ -33806,11 +37072,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
- zHistory = strdup(zHistory);
- }else if( (zHome = find_home_dir(0))!=0 ){
- nHistory = strlen30(zHome) + 20;
- if( (zHistory = malloc(nHistory))!=0 ){
- sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+ zHistory = sqlite3_mprintf("%s", zHistory);
+ shell_check_oom(zHistory);
+ }else{
+ zHistory = find_xdg_file("XDG_STATE_HOME",
+ ".local/state",
+ "sqlite_history");
+ if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
+ zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
+ shell_check_oom(zHistory);
}
}
if( zHistory ){ shell_read_history(zHistory); }
@@ -33822,27 +37092,28 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
data.in = 0;
- rc = process_input(&data);
+ rc = process_input(&data, "<stdin>");
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
- free(zHistory);
+ sqlite3_free(zHistory);
}
}else{
data.in = stdin;
- rc = process_input(&data);
+ rc = process_input(&data, "<stdin>");
}
}
#ifndef SQLITE_SHELL_FIDDLE
/* In WASM mode we have to leave the db state in place so that
** client code can "push" SQL into it after this call returns. */
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( data.expert.pExpert ){
expertFinish(&data, 1, 0);
}
#endif
shell_main_exit:
free(azCmd);
+ free(aiCmd);
set_table_name(&data, 0);
if( data.db ){
session_close_all(&data, -1);
@@ -33859,21 +37130,38 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
output_reset(&data);
data.doXdgOpen = 0;
clearTempFile(&data);
-#if !SQLITE_SHELL_IS_UTF8
- for(i=0; i<argcToFree; i++) free(argvToFree[i]);
- free(argvToFree);
-#endif
- free(data.colWidth);
+ modeFree(&data.mode);
+ if( data.nSavedModes ){
+ int ii;
+ for(ii=0; ii<data.nSavedModes; ii++){
+ modeFree(&data.aSavedModes[ii].mode);
+ free(data.aSavedModes[ii].zTag);
+ }
+ free(data.aSavedModes);
+ }
+ free(data.zErrPrefix);
free(data.zNonce);
+ free(data.dot.zCopy);
+ free(data.dot.azArg);
+ free(data.dot.aiOfst);
+ free(data.dot.abQuot);
+ if( data.nTestRun ){
+ sqlite3_fprintf(stdout, "%d test%s run with %d error%s\n",
+ data.nTestRun, data.nTestRun==1 ? "" : "s",
+ data.nTestErr, data.nTestErr==1 ? "" : "s");
+ fflush(stdout);
+ rc = data.nTestErr>0;
+ }
/* Clear the global data structure so that valgrind will detect memory
** leaks */
memset(&data, 0, sizeof(data));
if( bEnableVfstrace ){
vfstrace_unregister("trace");
}
+ sqlite3_reset_auto_extension();
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
- sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n",
+ cli_printf(stderr,"Memory leaked: %u bytes\n",
(unsigned int)(sqlite3_memory_used()-mem_main_enter));
}
#endif
@@ -33913,7 +37201,7 @@ sqlite3_vfs * fiddle_db_vfs(const char *zDbName){
/* Only for emcc experimentation purposes. */
sqlite3 * fiddle_db_arg(sqlite3 *arg){
- sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg);
+ cli_printf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg);
return arg;
}
@@ -33950,7 +37238,7 @@ void fiddle_reset_db(void){
** Resolve problem reported in
** https://sqlite.org/forum/forumpost/0b41a25d65
*/
- sqlite3_fputs("Rolling back in-progress transaction.\n", stdout);
+ cli_puts("Rolling back in-progress transaction.\n", stdout);
sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0);
}
rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
@@ -34012,8 +37300,9 @@ void fiddle_exec(const char * zSql){
if('.'==*zSql) puts(zSql);
shellState.wasm.zInput = zSql;
shellState.wasm.zPos = zSql;
- process_input(&shellState);
+ process_input(&shellState, "<stdin>");
shellState.wasm.zInput = shellState.wasm.zPos = 0;
}
}
#endif /* SQLITE_SHELL_FIDDLE */
+/************************* End src/shell.c.in ******************/