|
4f76459…
|
drh
|
1 |
/* |
|
4f76459…
|
drh
|
2 |
** This is the amalgamated source code to the "sqlite3" or "sqlite3.exe" |
|
4f76459…
|
drh
|
3 |
** command-line shell (CLI) for SQLite. This file is automatically |
|
4f76459…
|
drh
|
4 |
** generated by the tool/mkshellc.tcl script from the following sources: |
|
4f76459…
|
drh
|
5 |
** |
|
4f76459…
|
drh
|
6 |
** ext/expert/sqlite3expert.c |
|
4f76459…
|
drh
|
7 |
** ext/expert/sqlite3expert.h |
|
4f76459…
|
drh
|
8 |
** ext/intck/sqlite3intck.c |
|
4f76459…
|
drh
|
9 |
** ext/intck/sqlite3intck.h |
|
4f76459…
|
drh
|
10 |
** ext/misc/appendvfs.c |
|
4f76459…
|
drh
|
11 |
** ext/misc/base64.c |
|
4f76459…
|
drh
|
12 |
** ext/misc/base85.c |
|
4f76459…
|
drh
|
13 |
** ext/misc/completion.c |
|
4f76459…
|
drh
|
14 |
** ext/misc/decimal.c |
|
4f76459…
|
drh
|
15 |
** ext/misc/fileio.c |
|
4f76459…
|
drh
|
16 |
** ext/misc/ieee754.c |
|
4f76459…
|
drh
|
17 |
** ext/misc/memtrace.c |
|
4f76459…
|
drh
|
18 |
** ext/misc/pcachetrace.c |
|
4f76459…
|
drh
|
19 |
** ext/misc/regexp.c |
|
4f76459…
|
drh
|
20 |
** ext/misc/series.c |
|
4f76459…
|
drh
|
21 |
** ext/misc/sha1.c |
|
4f76459…
|
drh
|
22 |
** ext/misc/shathree.c |
|
4f76459…
|
drh
|
23 |
** ext/misc/sqlar.c |
|
4f76459…
|
drh
|
24 |
** ext/misc/sqlite3_stdio.c |
|
4f76459…
|
drh
|
25 |
** ext/misc/sqlite3_stdio.h |
|
4f76459…
|
drh
|
26 |
** ext/misc/stmtrand.c |
|
4f76459…
|
drh
|
27 |
** ext/misc/uint.c |
|
4f76459…
|
drh
|
28 |
** ext/misc/vfstrace.c |
|
4f76459…
|
drh
|
29 |
** ext/misc/windirent.h |
|
4f76459…
|
drh
|
30 |
** ext/misc/zipfile.c |
|
4f76459…
|
drh
|
31 |
** ext/qrf/qrf.c |
|
4f76459…
|
drh
|
32 |
** ext/qrf/qrf.h |
|
4f76459…
|
drh
|
33 |
** ext/recover/dbdata.c |
|
4f76459…
|
drh
|
34 |
** ext/recover/sqlite3recover.c |
|
4f76459…
|
drh
|
35 |
** ext/recover/sqlite3recover.h |
|
4f76459…
|
drh
|
36 |
** src/shell.c.in |
|
4f76459…
|
drh
|
37 |
** edit the src/shell.c.in file and/or some of the other files that are |
|
b9ecacf…
|
drh
|
38 |
** listed above, then rerun the command "make shell.c". |
|
4f76459…
|
drh
|
39 |
/************************* Begin src/shell.c.in ******************/ |
|
b9ecacf…
|
drh
|
40 |
** This file contains code to implement the "sqlite3" command line |
|
b9ecacf…
|
drh
|
41 |
|
|
b9ecacf…
|
drh
|
42 |
/* |
|
a10f931…
|
drh
|
43 |
** Limit input nesting via .read or any other input redirect. |
|
a10f931…
|
drh
|
44 |
** It's not too expensive, so a generous allowance can be made. |
|
a10f931…
|
drh
|
45 |
*/ |
|
a10f931…
|
drh
|
46 |
#define MAX_INPUT_NESTING 25 |
|
a10f931…
|
drh
|
47 |
|
|
a10f931…
|
drh
|
48 |
/* |
|
a10f931…
|
drh
|
49 |
** Used to prevent warnings about unused parameters |
|
a10f931…
|
drh
|
50 |
*/ |
|
a10f931…
|
drh
|
51 |
#define UNUSED_PARAMETER(x) (void)(x) |
|
a10f931…
|
drh
|
52 |
|
|
a10f931…
|
drh
|
53 |
/* |
|
a10f931…
|
drh
|
54 |
** Number of elements in an array |
|
a10f931…
|
drh
|
55 |
*/ |
|
a10f931…
|
drh
|
56 |
#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
|
a10f931…
|
drh
|
57 |
|
|
a10f931…
|
drh
|
58 |
/* |
|
2b2530d…
|
drh
|
59 |
#if defined(SQLITE_SHELL_FIDDLE) |
|
2b2530d…
|
drh
|
60 |
# undef SQLITE_OMIT_LOAD_EXTENSION |
|
2b2530d…
|
drh
|
61 |
# define SQLITE_OMIT_LOAD_EXTENSION 1 |
|
2b2530d…
|
drh
|
62 |
#endif |
|
cb89386…
|
drh
|
63 |
#include <stdint.h> |
|
4f76459…
|
drh
|
64 |
# include <sys/ioctl.h> |
|
4f76459…
|
drh
|
65 |
/************************* Begin ext/misc/sqlite3_stdio.h ******************/ |
|
4f76459…
|
drh
|
66 |
#include <stdarg.h> |
|
4f76459…
|
drh
|
67 |
int sqlite3_vfprintf(FILE *stream, const char *format, va_list); |
|
4f76459…
|
drh
|
68 |
#define sqlite3_vfprintf vfprintf |
|
4f76459…
|
drh
|
69 |
/************************* End ext/misc/sqlite3_stdio.h ********************/ |
|
4f76459…
|
drh
|
70 |
/************************* Begin ext/misc/sqlite3_stdio.c ******************/ |
|
0201a1e…
|
drh
|
71 |
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) ); |
|
0201a1e…
|
drh
|
72 |
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) ); |
|
0201a1e…
|
drh
|
73 |
b1 = sqlite3_malloc64( (sz1+1)*sizeof(b1[0]) ); |
|
0201a1e…
|
drh
|
74 |
b2 = sqlite3_malloc64( (sz2+1)*sizeof(b1[0]) ); |
|
0201a1e…
|
drh
|
75 |
wchar_t *b1 = sqlite3_malloc64( sz*sizeof(wchar_t) ); |
|
0201a1e…
|
drh
|
76 |
wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(wchar_t) ); |
|
4f76459…
|
drh
|
77 |
** Work-alikes for fprintf() and vfprintf() from the standard C library. |
|
4f76459…
|
drh
|
78 |
int sqlite3_vfprintf(FILE *out, const char *zFormat, va_list ap){ |
|
4f76459…
|
drh
|
79 |
int rc; |
|
4f76459…
|
drh
|
80 |
if( UseWtextForOutput(out) ){ |
|
4f76459…
|
drh
|
81 |
/* When writing to the command-prompt in Windows, it is necessary |
|
4f76459…
|
drh
|
82 |
** to use _O_WTEXT input mode and write UTF-16 characters. |
|
4f76459…
|
drh
|
83 |
*/ |
|
4f76459…
|
drh
|
84 |
char *z; |
|
4f76459…
|
drh
|
85 |
z = sqlite3_vmprintf(zFormat, ap); |
|
4f76459…
|
drh
|
86 |
sqlite3_fputs(z, out); |
|
4f76459…
|
drh
|
87 |
rc = (int)strlen(z); |
|
4f76459…
|
drh
|
88 |
sqlite3_free(z); |
|
4f76459…
|
drh
|
89 |
}else{ |
|
4f76459…
|
drh
|
90 |
/* Writing to a file or other destination, just write bytes without |
|
4f76459…
|
drh
|
91 |
** any translation. */ |
|
4f76459…
|
drh
|
92 |
rc = vfprintf(out, zFormat, ap); |
|
4f76459…
|
drh
|
93 |
} |
|
4f76459…
|
drh
|
94 |
return rc; |
|
4f76459…
|
drh
|
95 |
} |
|
4f76459…
|
drh
|
96 |
/************************* End ext/misc/sqlite3_stdio.c ********************/ |
|
4f76459…
|
drh
|
97 |
/************************* Begin ext/qrf/qrf.h ******************/ |
|
4f76459…
|
drh
|
98 |
/* |
|
4f76459…
|
drh
|
99 |
** 2025-10-20 |
|
4f76459…
|
drh
|
100 |
** |
|
4f76459…
|
drh
|
101 |
** The author disclaims copyright to this source code. In place of |
|
4f76459…
|
drh
|
102 |
** a legal notice, here is a blessing: |
|
4f76459…
|
drh
|
103 |
** |
|
4f76459…
|
drh
|
104 |
** May you do good and not evil. |
|
4f76459…
|
drh
|
105 |
** May you find forgiveness for yourself and forgive others. |
|
4f76459…
|
drh
|
106 |
** May you share freely, never taking more than you give. |
|
4f76459…
|
drh
|
107 |
** |
|
4f76459…
|
drh
|
108 |
************************************************************************* |
|
4f76459…
|
drh
|
109 |
** Header file for the Result-Format or "resfmt" utility library for SQLite. |
|
0201a1e…
|
drh
|
110 |
** See the README.md documentation for additional information. |
|
4f76459…
|
drh
|
111 |
*/ |
|
4f76459…
|
drh
|
112 |
#ifndef SQLITE_QRF_H |
|
4f76459…
|
drh
|
113 |
#define SQLITE_QRF_H |
|
9aee493…
|
drh
|
114 |
#ifdef __cplusplus |
|
9aee493…
|
drh
|
115 |
extern "C" { |
|
9aee493…
|
drh
|
116 |
#endif |
|
4f76459…
|
drh
|
117 |
#include <stdlib.h> |
|
4f76459…
|
drh
|
118 |
/* #include "sqlite3.h" */ |
|
4f76459…
|
drh
|
119 |
|
|
4f76459…
|
drh
|
120 |
/* |
|
4f76459…
|
drh
|
121 |
** Specification used by clients to define the output format they want |
|
4f76459…
|
drh
|
122 |
*/ |
|
4f76459…
|
drh
|
123 |
typedef struct sqlite3_qrf_spec sqlite3_qrf_spec; |
|
4f76459…
|
drh
|
124 |
struct sqlite3_qrf_spec { |
|
4f76459…
|
drh
|
125 |
unsigned char iVersion; /* Version number of this structure */ |
|
4f76459…
|
drh
|
126 |
unsigned char eStyle; /* Formatting style. "box", "csv", etc... */ |
|
4f76459…
|
drh
|
127 |
unsigned char eEsc; /* How to escape control characters in text */ |
|
4f76459…
|
drh
|
128 |
unsigned char eText; /* Quoting style for text */ |
|
4f76459…
|
drh
|
129 |
unsigned char eTitle; /* Quating style for the text of column names */ |
|
4f76459…
|
drh
|
130 |
unsigned char eBlob; /* Quoting style for BLOBs */ |
|
4f76459…
|
drh
|
131 |
unsigned char bTitles; /* True to show column names */ |
|
4f76459…
|
drh
|
132 |
unsigned char bWordWrap; /* Try to wrap on word boundaries */ |
|
4f76459…
|
drh
|
133 |
unsigned char bTextJsonb; /* Render JSONB blobs as JSON text */ |
|
4f76459…
|
drh
|
134 |
unsigned char eDfltAlign; /* Default alignment, no covered by aAlignment */ |
|
4f76459…
|
drh
|
135 |
unsigned char eTitleAlign; /* Alignment for column headers */ |
|
9aee493…
|
drh
|
136 |
unsigned char bSplitColumn; /* Wrap single-column output into many columns */ |
|
f4b3b59…
|
drh
|
137 |
unsigned char bBorder; /* Show outer border in Box and Table styles */ |
|
4f76459…
|
drh
|
138 |
short int nWrap; /* Wrap columns wider than this */ |
|
4f76459…
|
drh
|
139 |
short int nScreenWidth; /* Maximum overall table width */ |
|
4f76459…
|
drh
|
140 |
short int nLineLimit; /* Maximum number of lines for any row */ |
|
ae7e3f0…
|
drh
|
141 |
short int nTitleLimit; /* Maximum number of characters in a title */ |
|
cb89386…
|
drh
|
142 |
unsigned int nMultiInsert; /* Add rows to one INSERT until size exceeds */ |
|
4f76459…
|
drh
|
143 |
int nCharLimit; /* Maximum number of characters in a cell */ |
|
4f76459…
|
drh
|
144 |
int nWidth; /* Number of entries in aWidth[] */ |
|
4f76459…
|
drh
|
145 |
int nAlign; /* Number of entries in aAlignment[] */ |
|
4f76459…
|
drh
|
146 |
short int *aWidth; /* Column widths */ |
|
4f76459…
|
drh
|
147 |
unsigned char *aAlign; /* Column alignments */ |
|
4f76459…
|
drh
|
148 |
char *zColumnSep; /* Alternative column separator */ |
|
4f76459…
|
drh
|
149 |
char *zRowSep; /* Alternative row separator */ |
|
4f76459…
|
drh
|
150 |
char *zTableName; /* Output table name */ |
|
4f76459…
|
drh
|
151 |
char *zNull; /* Rendering of NULL */ |
|
4f76459…
|
drh
|
152 |
char *(*xRender)(void*,sqlite3_value*); /* Render a value */ |
|
4f76459…
|
drh
|
153 |
int (*xWrite)(void*,const char*,sqlite3_int64); /* Write output */ |
|
4f76459…
|
drh
|
154 |
void *pRenderArg; /* First argument to the xRender callback */ |
|
4f76459…
|
drh
|
155 |
void *pWriteArg; /* First argument to the xWrite callback */ |
|
4f76459…
|
drh
|
156 |
char **pzOutput; /* Storage location for output string */ |
|
4f76459…
|
drh
|
157 |
/* Additional fields may be added in the future */ |
|
4f76459…
|
drh
|
158 |
}; |
|
4f76459…
|
drh
|
159 |
|
|
4f76459…
|
drh
|
160 |
/* |
|
4f76459…
|
drh
|
161 |
** Interfaces |
|
4f76459…
|
drh
|
162 |
*/ |
|
4f76459…
|
drh
|
163 |
int sqlite3_format_query_result( |
|
4f76459…
|
drh
|
164 |
sqlite3_stmt *pStmt, /* SQL statement to run */ |
|
4f76459…
|
drh
|
165 |
const sqlite3_qrf_spec *pSpec, /* Result format specification */ |
|
4f76459…
|
drh
|
166 |
char **pzErr /* OUT: Write error message here */ |
|
4f76459…
|
drh
|
167 |
); |
|
4f76459…
|
drh
|
168 |
|
|
4f76459…
|
drh
|
169 |
/* |
|
4f76459…
|
drh
|
170 |
** Range of values for sqlite3_qrf_spec.aWidth[] entries and for |
|
4f76459…
|
drh
|
171 |
** sqlite3_qrf_spec.mxColWidth and .nScreenWidth |
|
4f76459…
|
drh
|
172 |
*/ |
|
4f76459…
|
drh
|
173 |
#define QRF_MAX_WIDTH 10000 |
|
4f76459…
|
drh
|
174 |
#define QRF_MIN_WIDTH 0 |
|
4f76459…
|
drh
|
175 |
|
|
4f76459…
|
drh
|
176 |
/* |
|
4f76459…
|
drh
|
177 |
** Output styles: |
|
4f76459…
|
drh
|
178 |
*/ |
|
4f76459…
|
drh
|
179 |
#define QRF_STYLE_Auto 0 /* Choose a style automatically */ |
|
4f76459…
|
drh
|
180 |
#define QRF_STYLE_Box 1 /* Unicode box-drawing characters */ |
|
4f76459…
|
drh
|
181 |
#define QRF_STYLE_Column 2 /* One record per line in neat columns */ |
|
4f76459…
|
drh
|
182 |
#define QRF_STYLE_Count 3 /* Output only a count of the rows of output */ |
|
4f76459…
|
drh
|
183 |
#define QRF_STYLE_Csv 4 /* Comma-separated-value */ |
|
4f76459…
|
drh
|
184 |
#define QRF_STYLE_Eqp 5 /* Format EXPLAIN QUERY PLAN output */ |
|
4f76459…
|
drh
|
185 |
#define QRF_STYLE_Explain 6 /* EXPLAIN output */ |
|
4f76459…
|
drh
|
186 |
#define QRF_STYLE_Html 7 /* Generate an XHTML table */ |
|
4f76459…
|
drh
|
187 |
#define QRF_STYLE_Insert 8 /* Generate SQL "insert" statements */ |
|
4f76459…
|
drh
|
188 |
#define QRF_STYLE_Json 9 /* Output is a list of JSON objects */ |
|
4f76459…
|
drh
|
189 |
#define QRF_STYLE_JObject 10 /* Independent JSON objects for each row */ |
|
4f76459…
|
drh
|
190 |
#define QRF_STYLE_Line 11 /* One column per line. */ |
|
4f76459…
|
drh
|
191 |
#define QRF_STYLE_List 12 /* One record per line with a separator */ |
|
4f76459…
|
drh
|
192 |
#define QRF_STYLE_Markdown 13 /* Markdown formatting */ |
|
4f76459…
|
drh
|
193 |
#define QRF_STYLE_Off 14 /* No query output shown */ |
|
4f76459…
|
drh
|
194 |
#define QRF_STYLE_Quote 15 /* SQL-quoted, comma-separated */ |
|
4f76459…
|
drh
|
195 |
#define QRF_STYLE_Stats 16 /* EQP-like output but with performance stats */ |
|
4f76459…
|
drh
|
196 |
#define QRF_STYLE_StatsEst 17 /* EQP-like output with planner estimates */ |
|
4f76459…
|
drh
|
197 |
#define QRF_STYLE_StatsVm 18 /* EXPLAIN-like output with performance stats */ |
|
4f76459…
|
drh
|
198 |
#define QRF_STYLE_Table 19 /* MySQL-style table formatting */ |
|
4f76459…
|
drh
|
199 |
|
|
4f76459…
|
drh
|
200 |
/* |
|
4f76459…
|
drh
|
201 |
** Quoting styles for text. |
|
4f76459…
|
drh
|
202 |
** Allowed values for sqlite3_qrf_spec.eText |
|
4f76459…
|
drh
|
203 |
*/ |
|
4f76459…
|
drh
|
204 |
#define QRF_TEXT_Auto 0 /* Choose text encoding automatically */ |
|
4f76459…
|
drh
|
205 |
#define QRF_TEXT_Plain 1 /* Literal text */ |
|
4f76459…
|
drh
|
206 |
#define QRF_TEXT_Sql 2 /* Quote as an SQL literal */ |
|
4f76459…
|
drh
|
207 |
#define QRF_TEXT_Csv 3 /* CSV-style quoting */ |
|
4f76459…
|
drh
|
208 |
#define QRF_TEXT_Html 4 /* HTML-style quoting */ |
|
4f76459…
|
drh
|
209 |
#define QRF_TEXT_Tcl 5 /* C/Tcl quoting */ |
|
4f76459…
|
drh
|
210 |
#define QRF_TEXT_Json 6 /* JSON quoting */ |
|
709b566…
|
drh
|
211 |
#define QRF_TEXT_Relaxed 7 /* Relaxed SQL quoting */ |
|
4f76459…
|
drh
|
212 |
|
|
4f76459…
|
drh
|
213 |
/* |
|
4f76459…
|
drh
|
214 |
** Quoting styles for BLOBs |
|
4f76459…
|
drh
|
215 |
** Allowed values for sqlite3_qrf_spec.eBlob |
|
4f76459…
|
drh
|
216 |
*/ |
|
4f76459…
|
drh
|
217 |
#define QRF_BLOB_Auto 0 /* Determine BLOB quoting using eText */ |
|
4f76459…
|
drh
|
218 |
#define QRF_BLOB_Text 1 /* Display content exactly as it is */ |
|
4f76459…
|
drh
|
219 |
#define QRF_BLOB_Sql 2 /* Quote as an SQL literal */ |
|
4f76459…
|
drh
|
220 |
#define QRF_BLOB_Hex 3 /* Hexadecimal representation */ |
|
4f76459…
|
drh
|
221 |
#define QRF_BLOB_Tcl 4 /* "\000" notation */ |
|
4f76459…
|
drh
|
222 |
#define QRF_BLOB_Json 5 /* A JSON string */ |
|
45de97f…
|
drh
|
223 |
#define QRF_BLOB_Size 6 /* Display the blob size only */ |
|
4f76459…
|
drh
|
224 |
|
|
4f76459…
|
drh
|
225 |
/* |
|
4f76459…
|
drh
|
226 |
** Control-character escape modes. |
|
4f76459…
|
drh
|
227 |
** Allowed values for sqlite3_qrf_spec.eEsc |
|
4f76459…
|
drh
|
228 |
*/ |
|
4f76459…
|
drh
|
229 |
#define QRF_ESC_Auto 0 /* Choose the ctrl-char escape automatically */ |
|
4f76459…
|
drh
|
230 |
#define QRF_ESC_Off 1 /* Do not escape control characters */ |
|
4f76459…
|
drh
|
231 |
#define QRF_ESC_Ascii 2 /* Unix-style escapes. Ex: U+0007 shows ^G */ |
|
4f76459…
|
drh
|
232 |
#define QRF_ESC_Symbol 3 /* Unicode escapes. Ex: U+0007 shows U+2407 */ |
|
4f76459…
|
drh
|
233 |
|
|
4f76459…
|
drh
|
234 |
/* |
|
4f76459…
|
drh
|
235 |
** Allowed values for "boolean" fields, such as "bColumnNames", "bWordWrap", |
|
4f76459…
|
drh
|
236 |
** and "bTextJsonb". There is an extra "auto" variants so these are actually |
|
4f76459…
|
drh
|
237 |
** tri-state settings, not booleans. |
|
4f76459…
|
drh
|
238 |
*/ |
|
4f76459…
|
drh
|
239 |
#define QRF_SW_Auto 0 /* Let QRF choose the best value */ |
|
4f76459…
|
drh
|
240 |
#define QRF_SW_Off 1 /* This setting is forced off */ |
|
4f76459…
|
drh
|
241 |
#define QRF_SW_On 2 /* This setting is forced on */ |
|
4f76459…
|
drh
|
242 |
#define QRF_Auto 0 /* Alternate spelling for QRF_*_Auto */ |
|
4f76459…
|
drh
|
243 |
#define QRF_No 1 /* Alternate spelling for QRF_SW_Off */ |
|
4f76459…
|
drh
|
244 |
#define QRF_Yes 2 /* Alternate spelling for QRF_SW_On */ |
|
4f76459…
|
drh
|
245 |
|
|
4f76459…
|
drh
|
246 |
/* |
|
4f76459…
|
drh
|
247 |
** Possible alignment values alignment settings |
|
4f76459…
|
drh
|
248 |
** |
|
4f76459…
|
drh
|
249 |
** Horizontal Vertial |
|
4f76459…
|
drh
|
250 |
** ---------- -------- */ |
|
4f76459…
|
drh
|
251 |
#define QRF_ALIGN_Auto 0 /* auto auto */ |
|
4f76459…
|
drh
|
252 |
#define QRF_ALIGN_Left 1 /* left auto */ |
|
4f76459…
|
drh
|
253 |
#define QRF_ALIGN_Center 2 /* center auto */ |
|
4f76459…
|
drh
|
254 |
#define QRF_ALIGN_Right 3 /* right auto */ |
|
4f76459…
|
drh
|
255 |
#define QRF_ALIGN_Top 4 /* auto top */ |
|
4f76459…
|
drh
|
256 |
#define QRF_ALIGN_NW 5 /* left top */ |
|
4f76459…
|
drh
|
257 |
#define QRF_ALIGN_N 6 /* center top */ |
|
4f76459…
|
drh
|
258 |
#define QRF_ALIGN_NE 7 /* right top */ |
|
4f76459…
|
drh
|
259 |
#define QRF_ALIGN_Middle 8 /* auto middle */ |
|
4f76459…
|
drh
|
260 |
#define QRF_ALIGN_W 9 /* left middle */ |
|
4f76459…
|
drh
|
261 |
#define QRF_ALIGN_C 10 /* center middle */ |
|
4f76459…
|
drh
|
262 |
#define QRF_ALIGN_E 11 /* right middle */ |
|
4f76459…
|
drh
|
263 |
#define QRF_ALIGN_Bottom 12 /* auto bottom */ |
|
4f76459…
|
drh
|
264 |
#define QRF_ALIGN_SW 13 /* left bottom */ |
|
4f76459…
|
drh
|
265 |
#define QRF_ALIGN_S 14 /* center bottom */ |
|
4f76459…
|
drh
|
266 |
#define QRF_ALIGN_SE 15 /* right bottom */ |
|
4f76459…
|
drh
|
267 |
#define QRF_ALIGN_HMASK 3 /* Horizontal alignment mask */ |
|
4f76459…
|
drh
|
268 |
#define QRF_ALIGN_VMASK 12 /* Vertical alignment mask */ |
|
4f76459…
|
drh
|
269 |
|
|
4f76459…
|
drh
|
270 |
/* |
|
4f76459…
|
drh
|
271 |
** Auxiliary routines contined within this module that might be useful |
|
4f76459…
|
drh
|
272 |
** in other contexts, and which are therefore exported. |
|
4f76459…
|
drh
|
273 |
*/ |
|
4f76459…
|
drh
|
274 |
/* |
|
4f76459…
|
drh
|
275 |
** Return an estimate of the width, in columns, for the single Unicode |
|
4f76459…
|
drh
|
276 |
** character c. For normal characters, the answer is always 1. But the |
|
4f76459…
|
drh
|
277 |
** estimate might be 0 or 2 for zero-width and double-width characters. |
|
4f76459…
|
drh
|
278 |
** |
|
d326547…
|
drh
|
279 |
** Different devices display unicode using different widths. So |
|
4f76459…
|
drh
|
280 |
** it is impossible to know that true display width with 100% accuracy. |
|
4f76459…
|
drh
|
281 |
** Inaccuracies in the width estimates might cause columns to be misaligned. |
|
4f76459…
|
drh
|
282 |
** Unfortunately, there is nothing we can do about that. |
|
4f76459…
|
drh
|
283 |
*/ |
|
4f76459…
|
drh
|
284 |
int sqlite3_qrf_wcwidth(int c); |
|
d326547…
|
drh
|
285 |
|
|
d326547…
|
drh
|
286 |
/* |
|
d326547…
|
drh
|
287 |
** Return an estimate of the number of display columns used by the |
|
d326547…
|
drh
|
288 |
** string in the argument. The width of individual characters is |
|
d326547…
|
drh
|
289 |
** determined as for sqlite3_qrf_wcwidth(). VT100 escape code sequences |
|
d326547…
|
drh
|
290 |
** are assigned a width of zero. |
|
d326547…
|
drh
|
291 |
*/ |
|
d326547…
|
drh
|
292 |
size_t sqlite3_qrf_wcswidth(const char*); |
|
4f76459…
|
drh
|
293 |
|
|
4f76459…
|
drh
|
294 |
|
|
9aee493…
|
drh
|
295 |
#ifdef __cplusplus |
|
9aee493…
|
drh
|
296 |
} |
|
9aee493…
|
drh
|
297 |
#endif |
|
4f76459…
|
drh
|
298 |
#endif /* !defined(SQLITE_QRF_H) */ |
|
4f76459…
|
drh
|
299 |
|
|
4f76459…
|
drh
|
300 |
/************************* End ext/qrf/qrf.h ********************/ |
|
4f76459…
|
drh
|
301 |
/************************* Begin ext/qrf/qrf.c ******************/ |
|
4f76459…
|
drh
|
302 |
/* |
|
4f76459…
|
drh
|
303 |
** 2025-10-20 |
|
4f76459…
|
drh
|
304 |
** |
|
4f76459…
|
drh
|
305 |
** The author disclaims copyright to this source code. In place of |
|
4f76459…
|
drh
|
306 |
** a legal notice, here is a blessing: |
|
4f76459…
|
drh
|
307 |
** |
|
4f76459…
|
drh
|
308 |
** May you do good and not evil. |
|
4f76459…
|
drh
|
309 |
** May you find forgiveness for yourself and forgive others. |
|
4f76459…
|
drh
|
310 |
** May you share freely, never taking more than you give. |
|
4f76459…
|
drh
|
311 |
** |
|
4f76459…
|
drh
|
312 |
************************************************************************* |
|
4f76459…
|
drh
|
313 |
** Implementation of the Result-Format or "qrf" utility library for SQLite. |
|
4f76459…
|
drh
|
314 |
** See the qrf.md documentation for additional information. |
|
4f76459…
|
drh
|
315 |
*/ |
|
4f76459…
|
drh
|
316 |
#ifndef SQLITE_QRF_H |
|
4f76459…
|
drh
|
317 |
#include "qrf.h" |
|
4f76459…
|
drh
|
318 |
#endif |
|
4f76459…
|
drh
|
319 |
#include <string.h> |
|
4f76459…
|
drh
|
320 |
#include <assert.h> |
|
7b0960d…
|
drh
|
321 |
#include <stdint.h> |
|
4f76459…
|
drh
|
322 |
|
|
4f76459…
|
drh
|
323 |
/* typedef sqlite3_int64 i64; */ |
|
4f76459…
|
drh
|
324 |
|
|
4f76459…
|
drh
|
325 |
/* A single line in the EQP output */ |
|
4f76459…
|
drh
|
326 |
typedef struct qrfEQPGraphRow qrfEQPGraphRow; |
|
4f76459…
|
drh
|
327 |
struct qrfEQPGraphRow { |
|
4f76459…
|
drh
|
328 |
int iEqpId; /* ID for this row */ |
|
4f76459…
|
drh
|
329 |
int iParentId; /* ID of the parent row */ |
|
4f76459…
|
drh
|
330 |
qrfEQPGraphRow *pNext; /* Next row in sequence */ |
|
4f76459…
|
drh
|
331 |
char zText[1]; /* Text to display for this row */ |
|
4f76459…
|
drh
|
332 |
}; |
|
4f76459…
|
drh
|
333 |
|
|
4f76459…
|
drh
|
334 |
/* All EQP output is collected into an instance of the following */ |
|
4f76459…
|
drh
|
335 |
typedef struct qrfEQPGraph qrfEQPGraph; |
|
4f76459…
|
drh
|
336 |
struct qrfEQPGraph { |
|
4f76459…
|
drh
|
337 |
qrfEQPGraphRow *pRow; /* Linked list of all rows of the EQP output */ |
|
4f76459…
|
drh
|
338 |
qrfEQPGraphRow *pLast; /* Last element of the pRow list */ |
|
7b0960d…
|
drh
|
339 |
int nWidth; /* Width of the graph */ |
|
7b0960d…
|
drh
|
340 |
char zPrefix[400]; /* Graph prefix */ |
|
4f76459…
|
drh
|
341 |
}; |
|
4f76459…
|
drh
|
342 |
|
|
4f76459…
|
drh
|
343 |
/* |
|
4f76459…
|
drh
|
344 |
** Private state information. Subject to change from one release to the |
|
4f76459…
|
drh
|
345 |
** next. |
|
4f76459…
|
drh
|
346 |
*/ |
|
4f76459…
|
drh
|
347 |
typedef struct Qrf Qrf; |
|
4f76459…
|
drh
|
348 |
struct Qrf { |
|
4f76459…
|
drh
|
349 |
sqlite3_stmt *pStmt; /* The statement whose output is to be rendered */ |
|
4f76459…
|
drh
|
350 |
sqlite3 *db; /* The corresponding database connection */ |
|
4f76459…
|
drh
|
351 |
sqlite3_stmt *pJTrans; /* JSONB to JSON translator statement */ |
|
4f76459…
|
drh
|
352 |
char **pzErr; /* Write error message here, if not NULL */ |
|
4f76459…
|
drh
|
353 |
sqlite3_str *pOut; /* Accumulated output */ |
|
4f76459…
|
drh
|
354 |
int iErr; /* Error code */ |
|
4f76459…
|
drh
|
355 |
int nCol; /* Number of output columns */ |
|
4f76459…
|
drh
|
356 |
int expMode; /* Original sqlite3_stmt_isexplain() plus 1 */ |
|
4f76459…
|
drh
|
357 |
int mxWidth; /* Screen width */ |
|
4f76459…
|
drh
|
358 |
int mxHeight; /* nLineLimit */ |
|
4f76459…
|
drh
|
359 |
union { |
|
4f76459…
|
drh
|
360 |
struct { /* Content for QRF_STYLE_Line */ |
|
4f76459…
|
drh
|
361 |
int mxColWth; /* Maximum display width of any column */ |
|
ae7e3f0…
|
drh
|
362 |
char **azCol; /* Names of output columns (MODE_Line) */ |
|
4f76459…
|
drh
|
363 |
} sLine; |
|
4f76459…
|
drh
|
364 |
qrfEQPGraph *pGraph; /* EQP graph (Eqp, Stats, and StatsEst) */ |
|
4f76459…
|
drh
|
365 |
struct { /* Content for QRF_STYLE_Explain */ |
|
4f76459…
|
drh
|
366 |
int nIndent; /* Slots allocated for aiIndent */ |
|
4f76459…
|
drh
|
367 |
int iIndent; /* Current slot */ |
|
4f76459…
|
drh
|
368 |
int *aiIndent; /* Indentation for each opcode */ |
|
4f76459…
|
drh
|
369 |
} sExpln; |
|
17f9878…
|
drh
|
370 |
unsigned int nIns; /* Bytes used for current INSERT stmt */ |
|
4f76459…
|
drh
|
371 |
} u; |
|
4f76459…
|
drh
|
372 |
sqlite3_int64 nRow; /* Number of rows handled so far */ |
|
4f76459…
|
drh
|
373 |
int *actualWidth; /* Actual width of each column */ |
|
4f76459…
|
drh
|
374 |
sqlite3_qrf_spec spec; /* Copy of the original spec */ |
|
4f76459…
|
drh
|
375 |
}; |
|
4f76459…
|
drh
|
376 |
|
|
4f76459…
|
drh
|
377 |
/* |
|
4f76459…
|
drh
|
378 |
** Data for substitute ctype.h functions. Used for x-platform |
|
4f76459…
|
drh
|
379 |
** consistency and so that '_' is counted as an alphabetic |
|
4f76459…
|
drh
|
380 |
** character. |
|
4f76459…
|
drh
|
381 |
** |
|
4f76459…
|
drh
|
382 |
** 0x01 - space |
|
4f76459…
|
drh
|
383 |
** 0x02 - digit |
|
4f76459…
|
drh
|
384 |
** 0x04 - alphabetic, including '_' |
|
4f76459…
|
drh
|
385 |
*/ |
|
4f76459…
|
drh
|
386 |
static const char qrfCType[] = { |
|
4f76459…
|
drh
|
387 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, |
|
4f76459…
|
drh
|
388 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
389 |
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
390 |
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
391 |
0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
|
4f76459…
|
drh
|
392 |
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 4, |
|
4f76459…
|
drh
|
393 |
0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, |
|
4f76459…
|
drh
|
394 |
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
395 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
396 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
397 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
398 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
399 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
400 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
401 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
402 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
|
4f76459…
|
drh
|
403 |
}; |
|
4f76459…
|
drh
|
404 |
#define qrfSpace(x) ((qrfCType[(unsigned char)x]&1)!=0) |
|
4f76459…
|
drh
|
405 |
#define qrfDigit(x) ((qrfCType[(unsigned char)x]&2)!=0) |
|
4f76459…
|
drh
|
406 |
#define qrfAlpha(x) ((qrfCType[(unsigned char)x]&4)!=0) |
|
4f76459…
|
drh
|
407 |
#define qrfAlnum(x) ((qrfCType[(unsigned char)x]&6)!=0) |
|
4f76459…
|
drh
|
408 |
|
|
709b566…
|
drh
|
409 |
#ifndef deliberate_fall_through |
|
709b566…
|
drh
|
410 |
/* Quiet some compilers about some of our intentional code. */ |
|
709b566…
|
drh
|
411 |
# if defined(GCC_VERSION) && GCC_VERSION>=7000000 |
|
709b566…
|
drh
|
412 |
# define deliberate_fall_through __attribute__((fallthrough)); |
|
709b566…
|
drh
|
413 |
# else |
|
709b566…
|
drh
|
414 |
# define deliberate_fall_through |
|
709b566…
|
drh
|
415 |
# endif |
|
709b566…
|
drh
|
416 |
#endif |
|
709b566…
|
drh
|
417 |
|
|
4f76459…
|
drh
|
418 |
/* |
|
4f76459…
|
drh
|
419 |
** Set an error code and error message. |
|
4f76459…
|
drh
|
420 |
*/ |
|
4f76459…
|
drh
|
421 |
static void qrfError( |
|
4f76459…
|
drh
|
422 |
Qrf *p, /* Query result state */ |
|
4f76459…
|
drh
|
423 |
int iCode, /* Error code */ |
|
4f76459…
|
drh
|
424 |
const char *zFormat, /* Message format (or NULL) */ |
|
4f76459…
|
drh
|
425 |
... |
|
4f76459…
|
drh
|
426 |
){ |
|
4f76459…
|
drh
|
427 |
p->iErr = iCode; |
|
4f76459…
|
drh
|
428 |
if( p->pzErr!=0 ){ |
|
4f76459…
|
drh
|
429 |
sqlite3_free(*p->pzErr); |
|
4f76459…
|
drh
|
430 |
*p->pzErr = 0; |
|
4f76459…
|
drh
|
431 |
if( zFormat ){ |
|
4f76459…
|
drh
|
432 |
va_list ap; |
|
4f76459…
|
drh
|
433 |
va_start(ap, zFormat); |
|
4f76459…
|
drh
|
434 |
*p->pzErr = sqlite3_vmprintf(zFormat, ap); |
|
4f76459…
|
drh
|
435 |
va_end(ap); |
|
4f76459…
|
drh
|
436 |
} |
|
4f76459…
|
drh
|
437 |
} |
|
4f76459…
|
drh
|
438 |
} |
|
4f76459…
|
drh
|
439 |
|
|
4f76459…
|
drh
|
440 |
/* |
|
4f76459…
|
drh
|
441 |
** Out-of-memory error. |
|
4f76459…
|
drh
|
442 |
*/ |
|
4f76459…
|
drh
|
443 |
static void qrfOom(Qrf *p){ |
|
4f76459…
|
drh
|
444 |
qrfError(p, SQLITE_NOMEM, "out of memory"); |
|
4f76459…
|
drh
|
445 |
} |
|
4f76459…
|
drh
|
446 |
|
|
2b2530d…
|
drh
|
447 |
/* |
|
2b2530d…
|
drh
|
448 |
** Transfer any error in pStr over into p. |
|
2b2530d…
|
drh
|
449 |
*/ |
|
2b2530d…
|
drh
|
450 |
static void qrfStrErr(Qrf *p, sqlite3_str *pStr){ |
|
2b2530d…
|
drh
|
451 |
int rc = pStr ? sqlite3_str_errcode(pStr) : 0; |
|
2b2530d…
|
drh
|
452 |
if( rc ){ |
|
2b2530d…
|
drh
|
453 |
qrfError(p, rc, sqlite3_errstr(rc)); |
|
2b2530d…
|
drh
|
454 |
} |
|
2b2530d…
|
drh
|
455 |
} |
|
4f76459…
|
drh
|
456 |
|
|
4f76459…
|
drh
|
457 |
|
|
4f76459…
|
drh
|
458 |
/* |
|
4f76459…
|
drh
|
459 |
** Add a new entry to the EXPLAIN QUERY PLAN data |
|
4f76459…
|
drh
|
460 |
*/ |
|
4f76459…
|
drh
|
461 |
static void qrfEqpAppend(Qrf *p, int iEqpId, int p2, const char *zText){ |
|
4f76459…
|
drh
|
462 |
qrfEQPGraphRow *pNew; |
|
4f76459…
|
drh
|
463 |
sqlite3_int64 nText; |
|
4f76459…
|
drh
|
464 |
if( zText==0 ) return; |
|
4f76459…
|
drh
|
465 |
if( p->u.pGraph==0 ){ |
|
4f76459…
|
drh
|
466 |
p->u.pGraph = sqlite3_malloc64( sizeof(qrfEQPGraph) ); |
|
4f76459…
|
drh
|
467 |
if( p->u.pGraph==0 ){ |
|
4f76459…
|
drh
|
468 |
qrfOom(p); |
|
4f76459…
|
drh
|
469 |
return; |
|
4f76459…
|
drh
|
470 |
} |
|
4f76459…
|
drh
|
471 |
memset(p->u.pGraph, 0, sizeof(qrfEQPGraph) ); |
|
4f76459…
|
drh
|
472 |
} |
|
4f76459…
|
drh
|
473 |
nText = strlen(zText); |
|
4f76459…
|
drh
|
474 |
pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); |
|
4f76459…
|
drh
|
475 |
if( pNew==0 ){ |
|
4f76459…
|
drh
|
476 |
qrfOom(p); |
|
4f76459…
|
drh
|
477 |
return; |
|
4f76459…
|
drh
|
478 |
} |
|
4f76459…
|
drh
|
479 |
pNew->iEqpId = iEqpId; |
|
4f76459…
|
drh
|
480 |
pNew->iParentId = p2; |
|
4f76459…
|
drh
|
481 |
memcpy(pNew->zText, zText, nText+1); |
|
4f76459…
|
drh
|
482 |
pNew->pNext = 0; |
|
4f76459…
|
drh
|
483 |
if( p->u.pGraph->pLast ){ |
|
4f76459…
|
drh
|
484 |
p->u.pGraph->pLast->pNext = pNew; |
|
4f76459…
|
drh
|
485 |
}else{ |
|
4f76459…
|
drh
|
486 |
p->u.pGraph->pRow = pNew; |
|
4f76459…
|
drh
|
487 |
} |
|
4f76459…
|
drh
|
488 |
p->u.pGraph->pLast = pNew; |
|
4f76459…
|
drh
|
489 |
} |
|
4f76459…
|
drh
|
490 |
|
|
4f76459…
|
drh
|
491 |
/* |
|
4f76459…
|
drh
|
492 |
** Free and reset the EXPLAIN QUERY PLAN data that has been collected |
|
4f76459…
|
drh
|
493 |
** in p->u.pGraph. |
|
4f76459…
|
drh
|
494 |
*/ |
|
4f76459…
|
drh
|
495 |
static void qrfEqpReset(Qrf *p){ |
|
4f76459…
|
drh
|
496 |
qrfEQPGraphRow *pRow, *pNext; |
|
4f76459…
|
drh
|
497 |
if( p->u.pGraph ){ |
|
4f76459…
|
drh
|
498 |
for(pRow = p->u.pGraph->pRow; pRow; pRow = pNext){ |
|
4f76459…
|
drh
|
499 |
pNext = pRow->pNext; |
|
4f76459…
|
drh
|
500 |
sqlite3_free(pRow); |
|
4f76459…
|
drh
|
501 |
} |
|
4f76459…
|
drh
|
502 |
sqlite3_free(p->u.pGraph); |
|
4f76459…
|
drh
|
503 |
p->u.pGraph = 0; |
|
4f76459…
|
drh
|
504 |
} |
|
4f76459…
|
drh
|
505 |
} |
|
4f76459…
|
drh
|
506 |
|
|
4f76459…
|
drh
|
507 |
/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after |
|
4f76459…
|
drh
|
508 |
** pOld, or return the first such line if pOld is NULL |
|
4f76459…
|
drh
|
509 |
*/ |
|
4f76459…
|
drh
|
510 |
static qrfEQPGraphRow *qrfEqpNextRow(Qrf *p, int iEqpId, qrfEQPGraphRow *pOld){ |
|
4f76459…
|
drh
|
511 |
qrfEQPGraphRow *pRow = pOld ? pOld->pNext : p->u.pGraph->pRow; |
|
4f76459…
|
drh
|
512 |
while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext; |
|
4f76459…
|
drh
|
513 |
return pRow; |
|
4f76459…
|
drh
|
514 |
} |
|
4f76459…
|
drh
|
515 |
|
|
4f76459…
|
drh
|
516 |
/* Render a single level of the graph that has iEqpId as its parent. Called |
|
4f76459…
|
drh
|
517 |
** recursively to render sublevels. |
|
4f76459…
|
drh
|
518 |
*/ |
|
4f76459…
|
drh
|
519 |
static void qrfEqpRenderLevel(Qrf *p, int iEqpId){ |
|
4f76459…
|
drh
|
520 |
qrfEQPGraphRow *pRow, *pNext; |
|
4f76459…
|
drh
|
521 |
i64 n = strlen(p->u.pGraph->zPrefix); |
|
4f76459…
|
drh
|
522 |
for(pRow = qrfEqpNextRow(p, iEqpId, 0); pRow; pRow = pNext){ |
|
4f76459…
|
drh
|
523 |
pNext = qrfEqpNextRow(p, iEqpId, pRow); |
|
4f76459…
|
drh
|
524 |
z = pRow->zText; |
|
4f76459…
|
drh
|
525 |
sqlite3_str_appendf(p->pOut, "%s%s%s\n", p->u.pGraph->zPrefix, |
|
4f76459…
|
drh
|
526 |
pNext ? "|--" : "`--", z); |
|
4f76459…
|
drh
|
527 |
if( n<(i64)sizeof(p->u.pGraph->zPrefix)-7 ){ |
|
4f76459…
|
drh
|
528 |
memcpy(&p->u.pGraph->zPrefix[n], pNext ? "| " : " ", 4); |
|
4f76459…
|
drh
|
529 |
qrfEqpRenderLevel(p, pRow->iEqpId); |
|
4f76459…
|
drh
|
530 |
p->u.pGraph->zPrefix[n] = 0; |
|
4f76459…
|
drh
|
531 |
} |
|
4f76459…
|
drh
|
532 |
} |
|
4f76459…
|
drh
|
533 |
} |
|
4f76459…
|
drh
|
534 |
|
|
4f76459…
|
drh
|
535 |
/* |
|
7b0960d…
|
drh
|
536 |
** Render the 64-bit value N in a more human-readable format into |
|
7b0960d…
|
drh
|
537 |
** pOut. |
|
7b0960d…
|
drh
|
538 |
** |
|
7b0960d…
|
drh
|
539 |
** + Only show the first three significant digits. |
|
7b0960d…
|
drh
|
540 |
** + Append suffixes K, M, G, T, P, and E for 1e3, 1e6, ... 1e18 |
|
7b0960d…
|
drh
|
541 |
*/ |
|
7b0960d…
|
drh
|
542 |
static void qrfApproxInt64(sqlite3_str *pOut, i64 N){ |
|
7b0960d…
|
drh
|
543 |
static const char aSuffix[] = { 'K', 'M', 'G', 'T', 'P', 'E' }; |
|
7b0960d…
|
drh
|
544 |
int i; |
|
7b0960d…
|
drh
|
545 |
if( N<0 ){ |
|
7b0960d…
|
drh
|
546 |
N = N==INT64_MIN ? INT64_MAX : -N; |
|
7b0960d…
|
drh
|
547 |
sqlite3_str_append(pOut, "-", 1); |
|
7b0960d…
|
drh
|
548 |
} |
|
7b0960d…
|
drh
|
549 |
if( N<10000 ){ |
|
7b0960d…
|
drh
|
550 |
sqlite3_str_appendf(pOut, "%4lld ", N); |
|
7b0960d…
|
drh
|
551 |
return; |
|
7b0960d…
|
drh
|
552 |
} |
|
7b0960d…
|
drh
|
553 |
for(i=1; i<=18; i++){ |
|
7b0960d…
|
drh
|
554 |
N = (N+5)/10; |
|
7b0960d…
|
drh
|
555 |
if( N<10000 ){ |
|
7b0960d…
|
drh
|
556 |
int n = (int)N; |
|
7b0960d…
|
drh
|
557 |
switch( i%3 ){ |
|
7b0960d…
|
drh
|
558 |
case 0: |
|
7b0960d…
|
drh
|
559 |
sqlite3_str_appendf(pOut, "%d.%02d", n/1000, (n%1000)/10); |
|
7b0960d…
|
drh
|
560 |
break; |
|
7b0960d…
|
drh
|
561 |
case 1: |
|
7b0960d…
|
drh
|
562 |
sqlite3_str_appendf(pOut, "%2d.%d", n/100, (n%100)/10); |
|
7b0960d…
|
drh
|
563 |
break; |
|
7b0960d…
|
drh
|
564 |
case 2: |
|
7b0960d…
|
drh
|
565 |
sqlite3_str_appendf(pOut, "%4d", n/10); |
|
7b0960d…
|
drh
|
566 |
break; |
|
7b0960d…
|
drh
|
567 |
} |
|
7b0960d…
|
drh
|
568 |
sqlite3_str_append(pOut, &aSuffix[i/3], 1); |
|
7b0960d…
|
drh
|
569 |
break; |
|
7b0960d…
|
drh
|
570 |
} |
|
7b0960d…
|
drh
|
571 |
} |
|
7b0960d…
|
drh
|
572 |
} |
|
7b0960d…
|
drh
|
573 |
|
|
7b0960d…
|
drh
|
574 |
/* |
|
4f76459…
|
drh
|
575 |
** Display and reset the EXPLAIN QUERY PLAN data |
|
4f76459…
|
drh
|
576 |
*/ |
|
4f76459…
|
drh
|
577 |
static void qrfEqpRender(Qrf *p, i64 nCycle){ |
|
4f76459…
|
drh
|
578 |
qrfEQPGraphRow *pRow; |
|
4f76459…
|
drh
|
579 |
if( p->u.pGraph!=0 && (pRow = p->u.pGraph->pRow)!=0 ){ |
|
4f76459…
|
drh
|
580 |
if( pRow->zText[0]=='-' ){ |
|
4f76459…
|
drh
|
581 |
if( pRow->pNext==0 ){ |
|
4f76459…
|
drh
|
582 |
qrfEqpReset(p); |
|
4f76459…
|
drh
|
583 |
return; |
|
4f76459…
|
drh
|
584 |
} |
|
4f76459…
|
drh
|
585 |
sqlite3_str_appendf(p->pOut, "%s\n", pRow->zText+3); |
|
4f76459…
|
drh
|
586 |
p->u.pGraph->pRow = pRow->pNext; |
|
4f76459…
|
drh
|
587 |
sqlite3_free(pRow); |
|
4f76459…
|
drh
|
588 |
}else if( nCycle>0 ){ |
|
7b0960d…
|
drh
|
589 |
int nSp = p->u.pGraph->nWidth - 2; |
|
7b0960d…
|
drh
|
590 |
if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
|
7b0960d…
|
drh
|
591 |
sqlite3_str_appendchar(p->pOut, nSp, ' '); |
|
7b0960d…
|
drh
|
592 |
sqlite3_str_appendall(p->pOut, |
|
7b0960d…
|
drh
|
593 |
"Cycles Loops (est) Rows (est)\n"); |
|
7b0960d…
|
drh
|
594 |
sqlite3_str_appendchar(p->pOut, nSp, ' '); |
|
7b0960d…
|
drh
|
595 |
sqlite3_str_appendall(p->pOut, |
|
7b0960d…
|
drh
|
596 |
"---------- ------------ ------------\n"); |
|
7b0960d…
|
drh
|
597 |
}else{ |
|
7b0960d…
|
drh
|
598 |
sqlite3_str_appendchar(p->pOut, nSp, ' '); |
|
7b0960d…
|
drh
|
599 |
sqlite3_str_appendall(p->pOut, |
|
7b0960d…
|
drh
|
600 |
"Cycles Loops Rows \n"); |
|
7b0960d…
|
drh
|
601 |
sqlite3_str_appendchar(p->pOut, nSp, ' '); |
|
7b0960d…
|
drh
|
602 |
sqlite3_str_appendall(p->pOut, |
|
7b0960d…
|
drh
|
603 |
"---------- ----- -----\n"); |
|
7b0960d…
|
drh
|
604 |
} |
|
7b0960d…
|
drh
|
605 |
sqlite3_str_appendall(p->pOut, "QUERY PLAN"); |
|
7b0960d…
|
drh
|
606 |
sqlite3_str_appendchar(p->pOut, nSp - 10, ' '); |
|
7b0960d…
|
drh
|
607 |
qrfApproxInt64(p->pOut, nCycle); |
|
7b0960d…
|
drh
|
608 |
sqlite3_str_appendall(p->pOut, " 100%\n"); |
|
4f76459…
|
drh
|
609 |
}else{ |
|
4f76459…
|
drh
|
610 |
sqlite3_str_appendall(p->pOut, "QUERY PLAN\n"); |
|
4f76459…
|
drh
|
611 |
} |
|
4f76459…
|
drh
|
612 |
p->u.pGraph->zPrefix[0] = 0; |
|
4f76459…
|
drh
|
613 |
qrfEqpRenderLevel(p, 0); |
|
4f76459…
|
drh
|
614 |
qrfEqpReset(p); |
|
4f76459…
|
drh
|
615 |
} |
|
4f76459…
|
drh
|
616 |
} |
|
4f76459…
|
drh
|
617 |
|
|
4f76459…
|
drh
|
618 |
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
|
4f76459…
|
drh
|
619 |
/* |
|
4f76459…
|
drh
|
620 |
** Helper function for qrfExpStats(). |
|
4f76459…
|
drh
|
621 |
** |
|
4f76459…
|
drh
|
622 |
*/ |
|
4f76459…
|
drh
|
623 |
static int qrfStatsHeight(sqlite3_stmt *p, int iEntry){ |
|
4f76459…
|
drh
|
624 |
int iPid = 0; |
|
4f76459…
|
drh
|
625 |
int ret = 1; |
|
4f76459…
|
drh
|
626 |
sqlite3_stmt_scanstatus_v2(p, iEntry, |
|
4f76459…
|
drh
|
627 |
SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
|
4f76459…
|
drh
|
628 |
); |
|
4f76459…
|
drh
|
629 |
while( iPid!=0 ){ |
|
4f76459…
|
drh
|
630 |
int ii; |
|
4f76459…
|
drh
|
631 |
for(ii=0; 1; ii++){ |
|
4f76459…
|
drh
|
632 |
int iId; |
|
4f76459…
|
drh
|
633 |
int res; |
|
4f76459…
|
drh
|
634 |
res = sqlite3_stmt_scanstatus_v2(p, ii, |
|
4f76459…
|
drh
|
635 |
SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId |
|
4f76459…
|
drh
|
636 |
); |
|
4f76459…
|
drh
|
637 |
if( res ) break; |
|
4f76459…
|
drh
|
638 |
if( iId==iPid ){ |
|
4f76459…
|
drh
|
639 |
sqlite3_stmt_scanstatus_v2(p, ii, |
|
4f76459…
|
drh
|
640 |
SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid |
|
4f76459…
|
drh
|
641 |
); |
|
4f76459…
|
drh
|
642 |
} |
|
4f76459…
|
drh
|
643 |
} |
|
4f76459…
|
drh
|
644 |
ret++; |
|
4f76459…
|
drh
|
645 |
} |
|
4f76459…
|
drh
|
646 |
return ret; |
|
4f76459…
|
drh
|
647 |
} |
|
4f76459…
|
drh
|
648 |
#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ |
|
4f76459…
|
drh
|
649 |
|
|
4f76459…
|
drh
|
650 |
|
|
4f76459…
|
drh
|
651 |
/* |
|
4f76459…
|
drh
|
652 |
** Generate ".scanstatus est" style of EQP output. |
|
4f76459…
|
drh
|
653 |
*/ |
|
4f76459…
|
drh
|
654 |
static void qrfEqpStats(Qrf *p){ |
|
4f76459…
|
drh
|
655 |
#ifndef SQLITE_ENABLE_STMT_SCANSTATUS |
|
4f76459…
|
drh
|
656 |
qrfError(p, SQLITE_ERROR, "not available in this build"); |
|
4f76459…
|
drh
|
657 |
#else |
|
4f76459…
|
drh
|
658 |
static const int f = SQLITE_SCANSTAT_COMPLEX; |
|
4f76459…
|
drh
|
659 |
sqlite3_stmt *pS = p->pStmt; |
|
4f76459…
|
drh
|
660 |
int i = 0; |
|
4f76459…
|
drh
|
661 |
i64 nTotal = 0; |
|
4f76459…
|
drh
|
662 |
int nWidth = 0; |
|
7b0960d…
|
drh
|
663 |
int prevPid = -1; /* Previous iPid */ |
|
7b0960d…
|
drh
|
664 |
double rEstCum = 1.0; /* Cumulative row estimate */ |
|
4f76459…
|
drh
|
665 |
sqlite3_str *pLine = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
666 |
sqlite3_str *pStats = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
667 |
qrfEqpReset(p); |
|
4f76459…
|
drh
|
668 |
|
|
4f76459…
|
drh
|
669 |
for(i=0; 1; i++){ |
|
4f76459…
|
drh
|
670 |
const char *z = 0; |
|
4f76459…
|
drh
|
671 |
int n = 0; |
|
4f76459…
|
drh
|
672 |
if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){ |
|
4f76459…
|
drh
|
673 |
break; |
|
4f76459…
|
drh
|
674 |
} |
|
4f76459…
|
drh
|
675 |
n = (int)strlen(z) + qrfStatsHeight(pS,i)*3; |
|
4f76459…
|
drh
|
676 |
if( n>nWidth ) nWidth = n; |
|
4f76459…
|
drh
|
677 |
} |
|
7b0960d…
|
drh
|
678 |
nWidth += 2; |
|
4f76459…
|
drh
|
679 |
|
|
4f76459…
|
drh
|
680 |
sqlite3_stmt_scanstatus_v2(pS,-1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal); |
|
4f76459…
|
drh
|
681 |
for(i=0; 1; i++){ |
|
4f76459…
|
drh
|
682 |
i64 nLoop = 0; |
|
4f76459…
|
drh
|
683 |
i64 nRow = 0; |
|
4f76459…
|
drh
|
684 |
i64 nCycle = 0; |
|
4f76459…
|
drh
|
685 |
int iId = 0; |
|
4f76459…
|
drh
|
686 |
int iPid = 0; |
|
4f76459…
|
drh
|
687 |
const char *zo = 0; |
|
4f76459…
|
drh
|
688 |
const char *zName = 0; |
|
4f76459…
|
drh
|
689 |
double rEst = 0.0; |
|
4f76459…
|
drh
|
690 |
|
|
4f76459…
|
drh
|
691 |
if( sqlite3_stmt_scanstatus_v2(pS,i,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){ |
|
4f76459…
|
drh
|
692 |
break; |
|
4f76459…
|
drh
|
693 |
} |
|
7b0960d…
|
drh
|
694 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid); |
|
7b0960d…
|
drh
|
695 |
if( iPid!=prevPid ){ |
|
7b0960d…
|
drh
|
696 |
prevPid = iPid; |
|
7b0960d…
|
drh
|
697 |
rEstCum = 1.0; |
|
7b0960d…
|
drh
|
698 |
} |
|
4f76459…
|
drh
|
699 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_EST,f,(void*)&rEst); |
|
7b0960d…
|
drh
|
700 |
rEstCum *= rEst; |
|
4f76459…
|
drh
|
701 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop); |
|
4f76459…
|
drh
|
702 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow); |
|
4f76459…
|
drh
|
703 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle); |
|
4f76459…
|
drh
|
704 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId); |
|
4f76459…
|
drh
|
705 |
sqlite3_stmt_scanstatus_v2(pS,i, SQLITE_SCANSTAT_NAME,f,(void*)&zName); |
|
4f76459…
|
drh
|
706 |
|
|
4f76459…
|
drh
|
707 |
if( nCycle>=0 || nLoop>=0 || nRow>=0 ){ |
|
7b0960d…
|
drh
|
708 |
int nSp = 0; |
|
4f76459…
|
drh
|
709 |
sqlite3_str_reset(pStats); |
|
4f76459…
|
drh
|
710 |
if( nCycle>=0 && nTotal>0 ){ |
|
7b0960d…
|
drh
|
711 |
qrfApproxInt64(pStats, nCycle); |
|
7b0960d…
|
drh
|
712 |
sqlite3_str_appendf(pStats, " %3d%%", |
|
7b0960d…
|
drh
|
713 |
((nCycle*100)+nTotal/2) / nTotal |
|
4f76459…
|
drh
|
714 |
); |
|
7b0960d…
|
drh
|
715 |
nSp = 2; |
|
4f76459…
|
drh
|
716 |
} |
|
4f76459…
|
drh
|
717 |
if( nLoop>=0 ){ |
|
7b0960d…
|
drh
|
718 |
if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); |
|
7b0960d…
|
drh
|
719 |
qrfApproxInt64(pStats, nLoop); |
|
7b0960d…
|
drh
|
720 |
nSp = 2; |
|
7b0960d…
|
drh
|
721 |
if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
|
7b0960d…
|
drh
|
722 |
sqlite3_str_appendf(pStats, " "); |
|
7b0960d…
|
drh
|
723 |
qrfApproxInt64(pStats, (i64)(rEstCum/rEst)); |
|
7b0960d…
|
drh
|
724 |
} |
|
4f76459…
|
drh
|
725 |
} |
|
4f76459…
|
drh
|
726 |
if( nRow>=0 ){ |
|
7b0960d…
|
drh
|
727 |
if( nSp ) sqlite3_str_appendchar(pStats, nSp, ' '); |
|
7b0960d…
|
drh
|
728 |
qrfApproxInt64(pStats, nRow); |
|
7b0960d…
|
drh
|
729 |
nSp = 2; |
|
7b0960d…
|
drh
|
730 |
if( p->spec.eStyle==QRF_STYLE_StatsEst ){ |
|
7b0960d…
|
drh
|
731 |
sqlite3_str_appendf(pStats, " "); |
|
7b0960d…
|
drh
|
732 |
qrfApproxInt64(pStats, (i64)rEstCum); |
|
7b0960d…
|
drh
|
733 |
} |
|
7b0960d…
|
drh
|
734 |
} |
|
4f76459…
|
drh
|
735 |
sqlite3_str_appendf(pLine, |
|
7b0960d…
|
drh
|
736 |
"% *s %s", -1*(nWidth-qrfStatsHeight(pS,i)*3), zo, |
|
4f76459…
|
drh
|
737 |
sqlite3_str_value(pStats) |
|
4f76459…
|
drh
|
738 |
); |
|
4f76459…
|
drh
|
739 |
sqlite3_str_reset(pStats); |
|
4f76459…
|
drh
|
740 |
qrfEqpAppend(p, iId, iPid, sqlite3_str_value(pLine)); |
|
4f76459…
|
drh
|
741 |
sqlite3_str_reset(pLine); |
|
4f76459…
|
drh
|
742 |
}else{ |
|
4f76459…
|
drh
|
743 |
qrfEqpAppend(p, iId, iPid, zo); |
|
4f76459…
|
drh
|
744 |
} |
|
4f76459…
|
drh
|
745 |
} |
|
7b0960d…
|
drh
|
746 |
if( p->u.pGraph ) p->u.pGraph->nWidth = nWidth; |
|
2b2530d…
|
drh
|
747 |
qrfStrErr(p, pLine); |
|
4f76459…
|
drh
|
748 |
sqlite3_free(sqlite3_str_finish(pLine)); |
|
2b2530d…
|
drh
|
749 |
qrfStrErr(p, pStats); |
|
4f76459…
|
drh
|
750 |
sqlite3_free(sqlite3_str_finish(pStats)); |
|
4f76459…
|
drh
|
751 |
#endif |
|
4f76459…
|
drh
|
752 |
} |
|
4f76459…
|
drh
|
753 |
|
|
4f76459…
|
drh
|
754 |
|
|
4f76459…
|
drh
|
755 |
/* |
|
4f76459…
|
drh
|
756 |
** Reset the prepared statement. |
|
4f76459…
|
drh
|
757 |
*/ |
|
4f76459…
|
drh
|
758 |
static void qrfResetStmt(Qrf *p){ |
|
4f76459…
|
drh
|
759 |
int rc = sqlite3_reset(p->pStmt); |
|
4f76459…
|
drh
|
760 |
if( rc!=SQLITE_OK && p->iErr==SQLITE_OK ){ |
|
4f76459…
|
drh
|
761 |
qrfError(p, rc, "%s", sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
762 |
} |
|
4f76459…
|
drh
|
763 |
} |
|
4f76459…
|
drh
|
764 |
|
|
4f76459…
|
drh
|
765 |
/* |
|
4f76459…
|
drh
|
766 |
** If xWrite is defined, send all content of pOut to xWrite and |
|
4f76459…
|
drh
|
767 |
** reset pOut. |
|
4f76459…
|
drh
|
768 |
*/ |
|
4f76459…
|
drh
|
769 |
static void qrfWrite(Qrf *p){ |
|
4f76459…
|
drh
|
770 |
int n; |
|
4f76459…
|
drh
|
771 |
if( p->spec.xWrite && (n = sqlite3_str_length(p->pOut))>0 ){ |
|
4f76459…
|
drh
|
772 |
int rc = p->spec.xWrite(p->spec.pWriteArg, |
|
4f76459…
|
drh
|
773 |
sqlite3_str_value(p->pOut), |
|
4f76459…
|
drh
|
774 |
(sqlite3_int64)n); |
|
4f76459…
|
drh
|
775 |
sqlite3_str_reset(p->pOut); |
|
4f76459…
|
drh
|
776 |
if( rc ){ |
|
4f76459…
|
drh
|
777 |
qrfError(p, rc, "Failed to write %d bytes of output", n); |
|
4f76459…
|
drh
|
778 |
} |
|
4f76459…
|
drh
|
779 |
} |
|
4f76459…
|
drh
|
780 |
} |
|
4f76459…
|
drh
|
781 |
} aQrfUWidth[] = { |
|
d326547…
|
drh
|
782 |
{0, 0x01036}, {1, 0x0103b}, {0, 0x01058}, |
|
4f76459…
|
drh
|
783 |
int sqlite3_qrf_wcwidth(int c){ |
|
d326547…
|
drh
|
784 |
if( c<0x300 ) return 1; |
|
4f76459…
|
drh
|
785 |
iLast = sizeof(aQrfUWidth)/sizeof(aQrfUWidth[0]) - 1; |
|
4f76459…
|
drh
|
786 |
int cMid = aQrfUWidth[iMid].iFirst; |
|
4f76459…
|
drh
|
787 |
return aQrfUWidth[iMid].w; |
|
4f76459…
|
drh
|
788 |
if( aQrfUWidth[iLast].iFirst > c ) return aQrfUWidth[iFirst].w; |
|
4f76459…
|
drh
|
789 |
return aQrfUWidth[iLast].w; |
|
4f76459…
|
drh
|
790 |
** begins at z[0]. Return the length. Write the Unicode value into *pU. |
|
4f76459…
|
drh
|
791 |
** This routine only works for *multi-byte* UTF-8 characters. It does |
|
4f76459…
|
drh
|
792 |
** not attempt to detect illegal characters. |
|
4f76459…
|
drh
|
793 |
int sqlite3_qrf_decode_utf8(const unsigned char *z, int *pU){ |
|
4f76459…
|
drh
|
794 |
static int qrfIsVt100(const unsigned char *z){ |
|
4f76459…
|
drh
|
795 |
** Return the length of a string in display characters. |
|
d326547…
|
drh
|
796 |
** |
|
d326547…
|
drh
|
797 |
** Most characters of the input string count as 1, including |
|
d326547…
|
drh
|
798 |
** multi-byte UTF8 characters. However, zero-width unicode |
|
d326547…
|
drh
|
799 |
** characters and VT100 escape sequences count as zero, and |
|
d326547…
|
drh
|
800 |
** double-width characters count as two. |
|
d326547…
|
drh
|
801 |
** |
|
d326547…
|
drh
|
802 |
** The definition of "zero-width" and "double-width" characters |
|
d326547…
|
drh
|
803 |
** is not precise. It depends on the output device, to some extent, |
|
d326547…
|
drh
|
804 |
** and it varies according to the Unicode version. This routine |
|
d326547…
|
drh
|
805 |
** makes the best guess that it can. |
|
4f76459…
|
drh
|
806 |
*/ |
|
d326547…
|
drh
|
807 |
size_t sqlite3_qrf_wcswidth(const char *zIn){ |
|
4f76459…
|
drh
|
808 |
const unsigned char *z = (const unsigned char*)zIn; |
|
d326547…
|
drh
|
809 |
size_t n = 0; |
|
4f76459…
|
drh
|
810 |
while( *z ){ |
|
4f76459…
|
drh
|
811 |
if( z[0]<' ' ){ |
|
4f76459…
|
drh
|
812 |
int k; |
|
4f76459…
|
drh
|
813 |
if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
|
4f76459…
|
drh
|
814 |
z += k; |
|
4f76459…
|
drh
|
815 |
}else{ |
|
4f76459…
|
drh
|
816 |
z++; |
|
4f76459…
|
drh
|
817 |
} |
|
4f76459…
|
drh
|
818 |
}else if( (0x80&z[0])==0 ){ |
|
4f76459…
|
drh
|
819 |
n++; |
|
4f76459…
|
drh
|
820 |
z++; |
|
4f76459…
|
drh
|
821 |
}else{ |
|
4f76459…
|
drh
|
822 |
int u = 0; |
|
4f76459…
|
drh
|
823 |
int len = sqlite3_qrf_decode_utf8(z, &u); |
|
4f76459…
|
drh
|
824 |
z += len; |
|
4f76459…
|
drh
|
825 |
n += sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
826 |
} |
|
4f76459…
|
drh
|
827 |
} |
|
4f76459…
|
drh
|
828 |
return n; |
|
4f76459…
|
drh
|
829 |
} |
|
4f76459…
|
drh
|
830 |
|
|
4f76459…
|
drh
|
831 |
/* |
|
4f76459…
|
drh
|
832 |
** Return the display width of the longest line of text |
|
4f76459…
|
drh
|
833 |
** in the (possibly) multi-line input string zIn[0..nByte]. |
|
4f76459…
|
drh
|
834 |
** zIn[] is not necessarily zero-terminated. Take |
|
4f76459…
|
drh
|
835 |
** into account tab characters, zero- and double-width |
|
4f76459…
|
drh
|
836 |
** characters, CR and NL, and VT100 escape codes. |
|
4f76459…
|
drh
|
837 |
** |
|
4f76459…
|
drh
|
838 |
** Write the number of newlines into *pnNL. So, *pnNL will |
|
4f76459…
|
drh
|
839 |
** return 0 if everything fits on one line, or positive it |
|
4f76459…
|
drh
|
840 |
** it will need to be split. |
|
4f76459…
|
drh
|
841 |
*/ |
|
4f76459…
|
drh
|
842 |
static int qrfDisplayWidth(const char *zIn, sqlite3_int64 nByte, int *pnNL){ |
|
d326547…
|
drh
|
843 |
const unsigned char *z; |
|
d326547…
|
drh
|
844 |
const unsigned char *zEnd; |
|
4f76459…
|
drh
|
845 |
int mx = 0; |
|
4f76459…
|
drh
|
846 |
int n = 0; |
|
4f76459…
|
drh
|
847 |
int nNL = 0; |
|
d326547…
|
drh
|
848 |
if( zIn==0 ) zIn = ""; |
|
d326547…
|
drh
|
849 |
z = (const unsigned char*)zIn; |
|
d326547…
|
drh
|
850 |
zEnd = &z[nByte]; |
|
4f76459…
|
drh
|
851 |
while( z<zEnd ){ |
|
4f76459…
|
drh
|
852 |
if( z[0]<' ' ){ |
|
4f76459…
|
drh
|
853 |
int k; |
|
4f76459…
|
drh
|
854 |
if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
|
4f76459…
|
drh
|
855 |
z += k; |
|
4f76459…
|
drh
|
856 |
}else{ |
|
4f76459…
|
drh
|
857 |
if( z[0]=='\t' ){ |
|
4f76459…
|
drh
|
858 |
n = (n+8)&~7; |
|
4f76459…
|
drh
|
859 |
}else if( z[0]=='\n' || z[0]=='\r' ){ |
|
4f76459…
|
drh
|
860 |
nNL++; |
|
4f76459…
|
drh
|
861 |
if( n>mx ) mx = n; |
|
4f76459…
|
drh
|
862 |
n = 0; |
|
4f76459…
|
drh
|
863 |
} |
|
4f76459…
|
drh
|
864 |
z++; |
|
4f76459…
|
drh
|
865 |
} |
|
4f76459…
|
drh
|
866 |
}else if( (0x80&z[0])==0 ){ |
|
4f76459…
|
drh
|
867 |
n++; |
|
4f76459…
|
drh
|
868 |
z++; |
|
4f76459…
|
drh
|
869 |
}else{ |
|
4f76459…
|
drh
|
870 |
int u = 0; |
|
4f76459…
|
drh
|
871 |
int len = sqlite3_qrf_decode_utf8(z, &u); |
|
4f76459…
|
drh
|
872 |
z += len; |
|
4f76459…
|
drh
|
873 |
n += sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
874 |
} |
|
4f76459…
|
drh
|
875 |
} |
|
4f76459…
|
drh
|
876 |
if( mx>n ) n = mx; |
|
4f76459…
|
drh
|
877 |
if( pnNL ) *pnNL = nNL; |
|
4f76459…
|
drh
|
878 |
return n; |
|
4f76459…
|
drh
|
879 |
} |
|
4f76459…
|
drh
|
880 |
|
|
4f76459…
|
drh
|
881 |
/* |
|
4f76459…
|
drh
|
882 |
** Escape the input string if it is needed and in accordance with |
|
4f76459…
|
drh
|
883 |
** eEsc, which is either QRF_ESC_Ascii or QRF_ESC_Symbol. |
|
4f76459…
|
drh
|
884 |
** |
|
4f76459…
|
drh
|
885 |
** Escaping is needed if the string contains any control characters |
|
4f76459…
|
drh
|
886 |
** other than \t, \n, and \r\n |
|
4f76459…
|
drh
|
887 |
** |
|
4f76459…
|
drh
|
888 |
** If no escaping is needed (the common case) then set *ppOut to NULL |
|
4f76459…
|
drh
|
889 |
** and return 0. If escaping is needed, write the escaped string into |
|
4f76459…
|
drh
|
890 |
** memory obtained from sqlite3_malloc64() and make *ppOut point to that |
|
4f76459…
|
drh
|
891 |
** memory and return 0. If an error occurs, return non-zero. |
|
4f76459…
|
drh
|
892 |
** |
|
4f76459…
|
drh
|
893 |
** The caller is responsible for freeing *ppFree if it is non-NULL in order |
|
4f76459…
|
drh
|
894 |
** to reclaim memory. |
|
4f76459…
|
drh
|
895 |
*/ |
|
4f76459…
|
drh
|
896 |
static void qrfEscape( |
|
4f76459…
|
drh
|
897 |
int eEsc, /* QRF_ESC_Ascii or QRF_ESC_Symbol */ |
|
4f76459…
|
drh
|
898 |
sqlite3_str *pStr, /* String to be escaped */ |
|
4f76459…
|
drh
|
899 |
int iStart /* Begin escapding on this byte of pStr */ |
|
4f76459…
|
drh
|
900 |
){ |
|
4f76459…
|
drh
|
901 |
sqlite3_int64 i, j; /* Loop counters */ |
|
4f76459…
|
drh
|
902 |
sqlite3_int64 sz; /* Size of the string prior to escaping */ |
|
4f76459…
|
drh
|
903 |
sqlite3_int64 nCtrl = 0;/* Number of control characters to escape */ |
|
4f76459…
|
drh
|
904 |
unsigned char *zIn; /* Text to be escaped */ |
|
4f76459…
|
drh
|
905 |
unsigned char c; /* A single character of the text */ |
|
4f76459…
|
drh
|
906 |
unsigned char *zOut; /* Where to write the results */ |
|
4f76459…
|
drh
|
907 |
|
|
4f76459…
|
drh
|
908 |
/* Find the text to be escaped */ |
|
4f76459…
|
drh
|
909 |
zIn = (unsigned char*)sqlite3_str_value(pStr); |
|
4f76459…
|
drh
|
910 |
if( zIn==0 ) return; |
|
4f76459…
|
drh
|
911 |
zIn += iStart; |
|
4f76459…
|
drh
|
912 |
|
|
4f76459…
|
drh
|
913 |
/* Count the control characters */ |
|
4f76459…
|
drh
|
914 |
for(i=0; (c = zIn[i])!=0; i++){ |
|
4f76459…
|
drh
|
915 |
if( c<=0x1f |
|
4f76459…
|
drh
|
916 |
&& c!='\t' |
|
4f76459…
|
drh
|
917 |
&& c!='\n' |
|
4f76459…
|
drh
|
918 |
&& (c!='\r' || zIn[i+1]!='\n') |
|
4f76459…
|
drh
|
919 |
){ |
|
4f76459…
|
drh
|
920 |
nCtrl++; |
|
4f76459…
|
drh
|
921 |
} |
|
4f76459…
|
drh
|
922 |
} |
|
4f76459…
|
drh
|
923 |
if( nCtrl==0 ) return; /* Early out if no control characters */ |
|
4f76459…
|
drh
|
924 |
|
|
4f76459…
|
drh
|
925 |
/* Make space to hold the escapes. Copy the original text to the end |
|
4f76459…
|
drh
|
926 |
** of the available space. */ |
|
4f76459…
|
drh
|
927 |
sz = sqlite3_str_length(pStr) - iStart; |
|
4f76459…
|
drh
|
928 |
if( eEsc==QRF_ESC_Symbol ) nCtrl *= 2; |
|
4f76459…
|
drh
|
929 |
sqlite3_str_appendchar(pStr, nCtrl, ' '); |
|
4f76459…
|
drh
|
930 |
zOut = (unsigned char*)sqlite3_str_value(pStr); |
|
4f76459…
|
drh
|
931 |
if( zOut==0 ) return; |
|
4f76459…
|
drh
|
932 |
zOut += iStart; |
|
4f76459…
|
drh
|
933 |
zIn = zOut + nCtrl; |
|
4f76459…
|
drh
|
934 |
memmove(zIn,zOut,sz); |
|
4f76459…
|
drh
|
935 |
|
|
4f76459…
|
drh
|
936 |
/* Convert the control characters */ |
|
4f76459…
|
drh
|
937 |
for(i=j=0; (c = zIn[i])!=0; i++){ |
|
4f76459…
|
drh
|
938 |
if( c>0x1f |
|
4f76459…
|
drh
|
939 |
|| c=='\t' |
|
4f76459…
|
drh
|
940 |
|| c=='\n' |
|
4f76459…
|
drh
|
941 |
|| (c=='\r' && zIn[i+1]=='\n') |
|
4f76459…
|
drh
|
942 |
){ |
|
4f76459…
|
drh
|
943 |
continue; |
|
4f76459…
|
drh
|
944 |
} |
|
4f76459…
|
drh
|
945 |
if( i>0 ){ |
|
4f76459…
|
drh
|
946 |
memmove(&zOut[j], zIn, i); |
|
4f76459…
|
drh
|
947 |
j += i; |
|
4f76459…
|
drh
|
948 |
} |
|
4f76459…
|
drh
|
949 |
zIn += i+1; |
|
4f76459…
|
drh
|
950 |
i = -1; |
|
4f76459…
|
drh
|
951 |
if( eEsc==QRF_ESC_Symbol ){ |
|
4f76459…
|
drh
|
952 |
zOut[j++] = 0xe2; |
|
4f76459…
|
drh
|
953 |
zOut[j++] = 0x90; |
|
4f76459…
|
drh
|
954 |
zOut[j++] = 0x80+c; |
|
4f76459…
|
drh
|
955 |
}else{ |
|
4f76459…
|
drh
|
956 |
zOut[j++] = '^'; |
|
4f76459…
|
drh
|
957 |
zOut[j++] = 0x40+c; |
|
4f76459…
|
drh
|
958 |
} |
|
4f76459…
|
drh
|
959 |
} |
|
4f76459…
|
drh
|
960 |
} |
|
4f76459…
|
drh
|
961 |
|
|
4f76459…
|
drh
|
962 |
/* |
|
709b566…
|
drh
|
963 |
** Determine if the string z[] can be shown as plain text. Return true |
|
709b566…
|
drh
|
964 |
** if z[] is unambiguously text. Return false if z[] needs to be |
|
709b566…
|
drh
|
965 |
** quoted. |
|
709b566…
|
drh
|
966 |
** |
|
709b566…
|
drh
|
967 |
** All of the following must be true in order for z[] to be relaxable: |
|
709b566…
|
drh
|
968 |
** |
|
709b566…
|
drh
|
969 |
** (1) z[] does not begin or end with ' or whitespace |
|
709b566…
|
drh
|
970 |
** (2) z[] is not the same as the NULL rendering |
|
709b566…
|
drh
|
971 |
** (3) z[] does not looks like a numeric literal |
|
709b566…
|
drh
|
972 |
*/ |
|
709b566…
|
drh
|
973 |
static int qrfRelaxable(Qrf *p, const char *z){ |
|
709b566…
|
drh
|
974 |
size_t i, n; |
|
709b566…
|
drh
|
975 |
if( z[0]=='\'' || qrfSpace(z[0]) ) return 0; |
|
5f65ed5…
|
drh
|
976 |
if( z[0]==0 ){ |
|
5f65ed5…
|
drh
|
977 |
return (p->spec.zNull!=0 && p->spec.zNull[0]!=0); |
|
5f65ed5…
|
drh
|
978 |
} |
|
709b566…
|
drh
|
979 |
n = strlen(z); |
|
5f65ed5…
|
drh
|
980 |
if( n==0 || z[n-1]=='\'' || qrfSpace(z[n-1]) ) return 0; |
|
709b566…
|
drh
|
981 |
if( p->spec.zNull && strcmp(p->spec.zNull,z)==0 ) return 0; |
|
709b566…
|
drh
|
982 |
i = (z[0]=='-' || z[0]=='+'); |
|
709b566…
|
drh
|
983 |
if( strcmp(z+i,"Inf")==0 ) return 0; |
|
709b566…
|
drh
|
984 |
if( !qrfDigit(z[i]) ) return 1; |
|
709b566…
|
drh
|
985 |
i++; |
|
709b566…
|
drh
|
986 |
while( qrfDigit(z[i]) ){ i++; } |
|
709b566…
|
drh
|
987 |
if( z[i]==0 ) return 0; |
|
709b566…
|
drh
|
988 |
if( z[i]=='.' ){ |
|
709b566…
|
drh
|
989 |
i++; |
|
709b566…
|
drh
|
990 |
while( qrfDigit(z[i]) ){ i++; } |
|
709b566…
|
drh
|
991 |
if( z[i]==0 ) return 0; |
|
709b566…
|
drh
|
992 |
} |
|
709b566…
|
drh
|
993 |
if( z[i]=='e' || z[i]=='E' ){ |
|
709b566…
|
drh
|
994 |
i++; |
|
709b566…
|
drh
|
995 |
if( z[i]=='+' || z[i]=='-' ){ i++; } |
|
709b566…
|
drh
|
996 |
if( !qrfDigit(z[i]) ) return 1; |
|
709b566…
|
drh
|
997 |
i++; |
|
709b566…
|
drh
|
998 |
while( qrfDigit(z[i]) ){ i++; } |
|
709b566…
|
drh
|
999 |
} |
|
709b566…
|
drh
|
1000 |
return z[i]!=0; |
|
709b566…
|
drh
|
1001 |
} |
|
709b566…
|
drh
|
1002 |
|
|
709b566…
|
drh
|
1003 |
/* |
|
4f76459…
|
drh
|
1004 |
** If a field contains any character identified by a 1 in the following |
|
4f76459…
|
drh
|
1005 |
** array, then the string must be quoted for CSV. |
|
4f76459…
|
drh
|
1006 |
*/ |
|
4f76459…
|
drh
|
1007 |
static const char qrfCsvQuote[] = { |
|
4f76459…
|
drh
|
1008 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1009 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1010 |
1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
1011 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
1012 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
1013 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
1014 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
|
4f76459…
|
drh
|
1015 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
|
4f76459…
|
drh
|
1016 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1017 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1018 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1019 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1020 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1021 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1022 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1023 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
|
4f76459…
|
drh
|
1024 |
}; |
|
4f76459…
|
drh
|
1025 |
|
|
4f76459…
|
drh
|
1026 |
/* |
|
4f76459…
|
drh
|
1027 |
** Encode text appropriately and append it to pOut. |
|
4f76459…
|
drh
|
1028 |
*/ |
|
4f76459…
|
drh
|
1029 |
static void qrfEncodeText(Qrf *p, sqlite3_str *pOut, const char *zTxt){ |
|
4f76459…
|
drh
|
1030 |
int iStart = sqlite3_str_length(pOut); |
|
4f76459…
|
drh
|
1031 |
switch( p->spec.eText ){ |
|
709b566…
|
drh
|
1032 |
case QRF_TEXT_Relaxed: |
|
709b566…
|
drh
|
1033 |
if( qrfRelaxable(p, zTxt) ){ |
|
709b566…
|
drh
|
1034 |
sqlite3_str_appendall(pOut, zTxt); |
|
709b566…
|
drh
|
1035 |
break; |
|
709b566…
|
drh
|
1036 |
} |
|
709b566…
|
drh
|
1037 |
deliberate_fall_through; /* FALLTHRU */ |
|
4f76459…
|
drh
|
1038 |
case QRF_TEXT_Sql: { |
|
4f76459…
|
drh
|
1039 |
if( p->spec.eEsc==QRF_ESC_Off ){ |
|
4f76459…
|
drh
|
1040 |
sqlite3_str_appendf(pOut, "%Q", zTxt); |
|
4f76459…
|
drh
|
1041 |
}else{ |
|
4f76459…
|
drh
|
1042 |
sqlite3_str_appendf(pOut, "%#Q", zTxt); |
|
4f76459…
|
drh
|
1043 |
} |
|
4f76459…
|
drh
|
1044 |
break; |
|
4f76459…
|
drh
|
1045 |
} |
|
4f76459…
|
drh
|
1046 |
case QRF_TEXT_Csv: { |
|
4f76459…
|
drh
|
1047 |
unsigned int i; |
|
4f76459…
|
drh
|
1048 |
for(i=0; zTxt[i]; i++){ |
|
4f76459…
|
drh
|
1049 |
if( qrfCsvQuote[((const unsigned char*)zTxt)[i]] ){ |
|
4f76459…
|
drh
|
1050 |
i = 0; |
|
4f76459…
|
drh
|
1051 |
break; |
|
4f76459…
|
drh
|
1052 |
} |
|
4f76459…
|
drh
|
1053 |
} |
|
4f76459…
|
drh
|
1054 |
if( i==0 || strstr(zTxt, p->spec.zColumnSep)!=0 ){ |
|
4f76459…
|
drh
|
1055 |
sqlite3_str_appendf(pOut, "\"%w\"", zTxt); |
|
4f76459…
|
drh
|
1056 |
}else{ |
|
4f76459…
|
drh
|
1057 |
sqlite3_str_appendall(pOut, zTxt); |
|
4f76459…
|
drh
|
1058 |
} |
|
4f76459…
|
drh
|
1059 |
break; |
|
4f76459…
|
drh
|
1060 |
} |
|
4f76459…
|
drh
|
1061 |
case QRF_TEXT_Html: { |
|
4f76459…
|
drh
|
1062 |
const unsigned char *z = (const unsigned char*)zTxt; |
|
4f76459…
|
drh
|
1063 |
while( *z ){ |
|
4f76459…
|
drh
|
1064 |
unsigned int i = 0; |
|
4f76459…
|
drh
|
1065 |
unsigned char c; |
|
4f76459…
|
drh
|
1066 |
while( (c=z[i])>'>' |
|
4f76459…
|
drh
|
1067 |
|| (c && c!='<' && c!='>' && c!='&' && c!='\"' && c!='\'') |
|
4f76459…
|
drh
|
1068 |
){ |
|
4f76459…
|
drh
|
1069 |
i++; |
|
4f76459…
|
drh
|
1070 |
} |
|
4f76459…
|
drh
|
1071 |
if( i>0 ){ |
|
4f76459…
|
drh
|
1072 |
sqlite3_str_append(pOut, (const char*)z, i); |
|
4f76459…
|
drh
|
1073 |
} |
|
4f76459…
|
drh
|
1074 |
switch( z[i] ){ |
|
4f76459…
|
drh
|
1075 |
case '>': sqlite3_str_append(pOut, "<", 4); break; |
|
4f76459…
|
drh
|
1076 |
case '&': sqlite3_str_append(pOut, "&", 5); break; |
|
4f76459…
|
drh
|
1077 |
case '<': sqlite3_str_append(pOut, "<", 4); break; |
|
4f76459…
|
drh
|
1078 |
case '"': sqlite3_str_append(pOut, """, 6); break; |
|
4f76459…
|
drh
|
1079 |
case '\'': sqlite3_str_append(pOut, "'", 5); break; |
|
4f76459…
|
drh
|
1080 |
default: i--; |
|
4f76459…
|
drh
|
1081 |
} |
|
4f76459…
|
drh
|
1082 |
z += i + 1; |
|
4f76459…
|
drh
|
1083 |
} |
|
4f76459…
|
drh
|
1084 |
break; |
|
4f76459…
|
drh
|
1085 |
} |
|
4f76459…
|
drh
|
1086 |
case QRF_TEXT_Tcl: |
|
4f76459…
|
drh
|
1087 |
case QRF_TEXT_Json: { |
|
4f76459…
|
drh
|
1088 |
const unsigned char *z = (const unsigned char*)zTxt; |
|
4f76459…
|
drh
|
1089 |
sqlite3_str_append(pOut, "\"", 1); |
|
4f76459…
|
drh
|
1090 |
while( *z ){ |
|
4f76459…
|
drh
|
1091 |
unsigned int i; |
|
4f76459…
|
drh
|
1092 |
for(i=0; z[i]>=0x20 && z[i]!='\\' && z[i]!='"'; i++){} |
|
4f76459…
|
drh
|
1093 |
if( i>0 ){ |
|
4f76459…
|
drh
|
1094 |
sqlite3_str_append(pOut, (const char*)z, i); |
|
4f76459…
|
drh
|
1095 |
} |
|
4f76459…
|
drh
|
1096 |
if( z[i]==0 ) break; |
|
4f76459…
|
drh
|
1097 |
switch( z[i] ){ |
|
4f76459…
|
drh
|
1098 |
case '"': sqlite3_str_append(pOut, "\\\"", 2); break; |
|
4f76459…
|
drh
|
1099 |
case '\\': sqlite3_str_append(pOut, "\\\\", 2); break; |
|
4f76459…
|
drh
|
1100 |
case '\b': sqlite3_str_append(pOut, "\\b", 2); break; |
|
4f76459…
|
drh
|
1101 |
case '\f': sqlite3_str_append(pOut, "\\f", 2); break; |
|
4f76459…
|
drh
|
1102 |
case '\n': sqlite3_str_append(pOut, "\\n", 2); break; |
|
4f76459…
|
drh
|
1103 |
case '\r': sqlite3_str_append(pOut, "\\r", 2); break; |
|
4f76459…
|
drh
|
1104 |
case '\t': sqlite3_str_append(pOut, "\\t", 2); break; |
|
4f76459…
|
drh
|
1105 |
default: { |
|
4f76459…
|
drh
|
1106 |
if( p->spec.eText==QRF_TEXT_Json ){ |
|
4f76459…
|
drh
|
1107 |
sqlite3_str_appendf(pOut, "\\u%04x", z[i]); |
|
4f76459…
|
drh
|
1108 |
}else{ |
|
4f76459…
|
drh
|
1109 |
sqlite3_str_appendf(pOut, "\\%03o", z[i]); |
|
4f76459…
|
drh
|
1110 |
} |
|
4f76459…
|
drh
|
1111 |
break; |
|
4f76459…
|
drh
|
1112 |
} |
|
4f76459…
|
drh
|
1113 |
} |
|
4f76459…
|
drh
|
1114 |
z += i + 1; |
|
4f76459…
|
drh
|
1115 |
} |
|
4f76459…
|
drh
|
1116 |
sqlite3_str_append(pOut, "\"", 1); |
|
4f76459…
|
drh
|
1117 |
break; |
|
4f76459…
|
drh
|
1118 |
} |
|
4f76459…
|
drh
|
1119 |
default: { |
|
4f76459…
|
drh
|
1120 |
sqlite3_str_appendall(pOut, zTxt); |
|
4f76459…
|
drh
|
1121 |
break; |
|
4f76459…
|
drh
|
1122 |
} |
|
4f76459…
|
drh
|
1123 |
} |
|
4f76459…
|
drh
|
1124 |
if( p->spec.eEsc!=QRF_ESC_Off ){ |
|
4f76459…
|
drh
|
1125 |
qrfEscape(p->spec.eEsc, pOut, iStart); |
|
4f76459…
|
drh
|
1126 |
} |
|
4f76459…
|
drh
|
1127 |
} |
|
4f76459…
|
drh
|
1128 |
|
|
4f76459…
|
drh
|
1129 |
/* |
|
4f76459…
|
drh
|
1130 |
** Do a quick sanity check to see aBlob[0..nBlob-1] is valid JSONB |
|
4f76459…
|
drh
|
1131 |
** return true if it is and false if it is not. |
|
4f76459…
|
drh
|
1132 |
** |
|
4f76459…
|
drh
|
1133 |
** False positives are possible, but not false negatives. |
|
4f76459…
|
drh
|
1134 |
*/ |
|
4f76459…
|
drh
|
1135 |
static int qrfJsonbQuickCheck(unsigned char *aBlob, int nBlob){ |
|
4f76459…
|
drh
|
1136 |
unsigned char x; /* Payload size half-byte */ |
|
4f76459…
|
drh
|
1137 |
int i; /* Loop counter */ |
|
4f76459…
|
drh
|
1138 |
int n; /* Bytes in the payload size integer */ |
|
4f76459…
|
drh
|
1139 |
sqlite3_uint64 sz; /* value of the payload size integer */ |
|
4f76459…
|
drh
|
1140 |
|
|
4f76459…
|
drh
|
1141 |
if( nBlob==0 ) return 0; |
|
4f76459…
|
drh
|
1142 |
x = aBlob[0]>>4; |
|
4f76459…
|
drh
|
1143 |
if( x<=11 ) return nBlob==(1+x); |
|
4f76459…
|
drh
|
1144 |
n = x<14 ? x-11 : 4*(x-13); |
|
4f76459…
|
drh
|
1145 |
if( nBlob<1+n ) return 0; |
|
4f76459…
|
drh
|
1146 |
sz = aBlob[1]; |
|
4f76459…
|
drh
|
1147 |
for(i=1; i<n; i++) sz = (sz<<8) + aBlob[i+1]; |
|
4f76459…
|
drh
|
1148 |
return sz+n+1==(sqlite3_uint64)nBlob; |
|
4f76459…
|
drh
|
1149 |
} |
|
4f76459…
|
drh
|
1150 |
|
|
4f76459…
|
drh
|
1151 |
/* |
|
4f76459…
|
drh
|
1152 |
** The current iCol-th column of p->pStmt is known to be a BLOB. Check |
|
4f76459…
|
drh
|
1153 |
** to see if that BLOB is really a JSONB blob. If it is, then translate |
|
4f76459…
|
drh
|
1154 |
** it into a text JSON representation and return a pointer to that text JSON. |
|
4f76459…
|
drh
|
1155 |
** If the BLOB is not JSONB, then return a NULL pointer. |
|
4f76459…
|
drh
|
1156 |
** |
|
4f76459…
|
drh
|
1157 |
** The memory used to hold the JSON text is managed internally by the |
|
4f76459…
|
drh
|
1158 |
** "p" object and is overwritten and/or deallocated upon the next call |
|
4f76459…
|
drh
|
1159 |
** to this routine (with the same p argument) or when the p object is |
|
4f76459…
|
drh
|
1160 |
** finailized. |
|
4f76459…
|
drh
|
1161 |
*/ |
|
4f76459…
|
drh
|
1162 |
static const char *qrfJsonbToJson(Qrf *p, int iCol){ |
|
4f76459…
|
drh
|
1163 |
int nByte; |
|
4f76459…
|
drh
|
1164 |
const void *pBlob; |
|
4f76459…
|
drh
|
1165 |
int rc; |
|
4f76459…
|
drh
|
1166 |
nByte = sqlite3_column_bytes(p->pStmt, iCol); |
|
4f76459…
|
drh
|
1167 |
pBlob = sqlite3_column_blob(p->pStmt, iCol); |
|
4f76459…
|
drh
|
1168 |
if( qrfJsonbQuickCheck((unsigned char*)pBlob, nByte)==0 ){ |
|
4f76459…
|
drh
|
1169 |
return 0; |
|
4f76459…
|
drh
|
1170 |
} |
|
4f76459…
|
drh
|
1171 |
if( p->pJTrans==0 ){ |
|
4f76459…
|
drh
|
1172 |
sqlite3 *db; |
|
4f76459…
|
drh
|
1173 |
rc = sqlite3_open(":memory:",&db); |
|
4f76459…
|
drh
|
1174 |
if( rc ){ |
|
4f76459…
|
drh
|
1175 |
sqlite3_close(db); |
|
4f76459…
|
drh
|
1176 |
return 0; |
|
4f76459…
|
drh
|
1177 |
} |
|
4f76459…
|
drh
|
1178 |
rc = sqlite3_prepare_v2(db, "SELECT json(?1)", -1, &p->pJTrans, 0); |
|
4f76459…
|
drh
|
1179 |
if( rc ){ |
|
4f76459…
|
drh
|
1180 |
sqlite3_finalize(p->pJTrans); |
|
4f76459…
|
drh
|
1181 |
p->pJTrans = 0; |
|
4f76459…
|
drh
|
1182 |
sqlite3_close(db); |
|
4f76459…
|
drh
|
1183 |
return 0; |
|
4f76459…
|
drh
|
1184 |
} |
|
4f76459…
|
drh
|
1185 |
}else{ |
|
4f76459…
|
drh
|
1186 |
sqlite3_reset(p->pJTrans); |
|
4f76459…
|
drh
|
1187 |
} |
|
4f76459…
|
drh
|
1188 |
sqlite3_bind_blob(p->pJTrans, 1, (void*)pBlob, nByte, SQLITE_STATIC); |
|
4f76459…
|
drh
|
1189 |
rc = sqlite3_step(p->pJTrans); |
|
4f76459…
|
drh
|
1190 |
if( rc==SQLITE_ROW ){ |
|
4f76459…
|
drh
|
1191 |
return (const char*)sqlite3_column_text(p->pJTrans, 0); |
|
4f76459…
|
drh
|
1192 |
}else{ |
|
4f76459…
|
drh
|
1193 |
return 0; |
|
4f76459…
|
drh
|
1194 |
} |
|
4f76459…
|
drh
|
1195 |
} |
|
ae7e3f0…
|
drh
|
1196 |
|
|
ae7e3f0…
|
drh
|
1197 |
/* |
|
ae7e3f0…
|
drh
|
1198 |
** Adjust the input string zIn[] such that it is no more than N display |
|
ae7e3f0…
|
drh
|
1199 |
** characters wide. If it is wider than that, then truncate and add |
|
ae7e3f0…
|
drh
|
1200 |
** ellipsis. Or if zIn[] contains a \r or \n, truncate at that point, |
|
ae7e3f0…
|
drh
|
1201 |
** adding ellipsis. Embedded tabs in zIn[] are converted into ordinary |
|
ae7e3f0…
|
drh
|
1202 |
** spaces. |
|
ae7e3f0…
|
drh
|
1203 |
** |
|
ae7e3f0…
|
drh
|
1204 |
** Return this display width of the modified title string. |
|
ae7e3f0…
|
drh
|
1205 |
*/ |
|
ae7e3f0…
|
drh
|
1206 |
static int qrfTitleLimit(char *zIn, int N){ |
|
ae7e3f0…
|
drh
|
1207 |
unsigned char *z = (unsigned char*)zIn; |
|
ae7e3f0…
|
drh
|
1208 |
int n = 0; |
|
ae7e3f0…
|
drh
|
1209 |
unsigned char *zEllipsis = 0; |
|
ae7e3f0…
|
drh
|
1210 |
while( 1 /*exit-by-break*/ ){ |
|
ae7e3f0…
|
drh
|
1211 |
if( z[0]<' ' ){ |
|
ae7e3f0…
|
drh
|
1212 |
int k; |
|
ae7e3f0…
|
drh
|
1213 |
if( z[0]==0 ){ |
|
ae7e3f0…
|
drh
|
1214 |
zEllipsis = 0; |
|
ae7e3f0…
|
drh
|
1215 |
break; |
|
ae7e3f0…
|
drh
|
1216 |
}else if( z[0]=='\033' && (k = qrfIsVt100(z))>0 ){ |
|
ae7e3f0…
|
drh
|
1217 |
z += k; |
|
ae7e3f0…
|
drh
|
1218 |
}else if( z[0]=='\t' ){ |
|
ae7e3f0…
|
drh
|
1219 |
z[0] = ' '; |
|
ae7e3f0…
|
drh
|
1220 |
}else if( z[0]=='\n' || z[0]=='\r' ){ |
|
ae7e3f0…
|
drh
|
1221 |
z[0] = ' '; |
|
ae7e3f0…
|
drh
|
1222 |
}else{ |
|
ae7e3f0…
|
drh
|
1223 |
z++; |
|
ae7e3f0…
|
drh
|
1224 |
} |
|
ae7e3f0…
|
drh
|
1225 |
}else if( (0x80&z[0])==0 ){ |
|
ae7e3f0…
|
drh
|
1226 |
if( n>=(N-3) && zEllipsis==0 ) zEllipsis = z; |
|
ae7e3f0…
|
drh
|
1227 |
if( n==N ){ z[0] = 0; break; } |
|
ae7e3f0…
|
drh
|
1228 |
n++; |
|
ae7e3f0…
|
drh
|
1229 |
z++; |
|
ae7e3f0…
|
drh
|
1230 |
}else{ |
|
ae7e3f0…
|
drh
|
1231 |
int u = 0; |
|
ae7e3f0…
|
drh
|
1232 |
int len = sqlite3_qrf_decode_utf8(z, &u); |
|
ae7e3f0…
|
drh
|
1233 |
if( n+len>(N-3) && zEllipsis==0 ) zEllipsis = z; |
|
ae7e3f0…
|
drh
|
1234 |
if( n+len>N ){ z[0] = 0; break; } |
|
ae7e3f0…
|
drh
|
1235 |
z += len; |
|
ae7e3f0…
|
drh
|
1236 |
n += sqlite3_qrf_wcwidth(u); |
|
ae7e3f0…
|
drh
|
1237 |
} |
|
ae7e3f0…
|
drh
|
1238 |
} |
|
ae7e3f0…
|
drh
|
1239 |
if( zEllipsis && N>=3 ) memcpy(zEllipsis,"...",4); |
|
ae7e3f0…
|
drh
|
1240 |
return n; |
|
ae7e3f0…
|
drh
|
1241 |
} |
|
ae7e3f0…
|
drh
|
1242 |
|
|
45de97f…
|
drh
|
1243 |
|
|
45de97f…
|
drh
|
1244 |
/* |
|
4f76459…
|
drh
|
1245 |
** Render value pVal into pOut |
|
4f76459…
|
drh
|
1246 |
*/ |
|
4f76459…
|
drh
|
1247 |
static void qrfRenderValue(Qrf *p, sqlite3_str *pOut, int iCol){ |
|
4f76459…
|
drh
|
1248 |
#if SQLITE_VERSION_NUMBER>=3052000 |
|
4f76459…
|
drh
|
1249 |
int iStartLen = sqlite3_str_length(pOut); |
|
4f76459…
|
drh
|
1250 |
#endif |
|
4f76459…
|
drh
|
1251 |
if( p->spec.xRender ){ |
|
4f76459…
|
drh
|
1252 |
sqlite3_value *pVal; |
|
4f76459…
|
drh
|
1253 |
char *z; |
|
4f76459…
|
drh
|
1254 |
pVal = sqlite3_value_dup(sqlite3_column_value(p->pStmt,iCol)); |
|
4f76459…
|
drh
|
1255 |
z = p->spec.xRender(p->spec.pRenderArg, pVal); |
|
4f76459…
|
drh
|
1256 |
sqlite3_value_free(pVal); |
|
4f76459…
|
drh
|
1257 |
if( z ){ |
|
4f76459…
|
drh
|
1258 |
sqlite3_str_appendall(pOut, z); |
|
4f76459…
|
drh
|
1259 |
sqlite3_free(z); |
|
4f76459…
|
drh
|
1260 |
return; |
|
4f76459…
|
drh
|
1261 |
} |
|
4f76459…
|
drh
|
1262 |
} |
|
4f76459…
|
drh
|
1263 |
switch( sqlite3_column_type(p->pStmt,iCol) ){ |
|
4f76459…
|
drh
|
1264 |
case SQLITE_INTEGER: { |
|
4f76459…
|
drh
|
1265 |
sqlite3_str_appendf(pOut, "%lld", sqlite3_column_int64(p->pStmt,iCol)); |
|
4f76459…
|
drh
|
1266 |
break; |
|
4f76459…
|
drh
|
1267 |
} |
|
4f76459…
|
drh
|
1268 |
case SQLITE_FLOAT: { |
|
4f76459…
|
drh
|
1269 |
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1270 |
sqlite3_str_appendall(pOut, zTxt); |
|
4f76459…
|
drh
|
1271 |
break; |
|
4f76459…
|
drh
|
1272 |
} |
|
4f76459…
|
drh
|
1273 |
case SQLITE_BLOB: { |
|
4f76459…
|
drh
|
1274 |
if( p->spec.bTextJsonb==QRF_Yes ){ |
|
4f76459…
|
drh
|
1275 |
const char *zJson = qrfJsonbToJson(p, iCol); |
|
4f76459…
|
drh
|
1276 |
if( zJson ){ |
|
4f76459…
|
drh
|
1277 |
if( p->spec.eText==QRF_TEXT_Sql ){ |
|
4f76459…
|
drh
|
1278 |
sqlite3_str_append(pOut,"jsonb(",6); |
|
4f76459…
|
drh
|
1279 |
qrfEncodeText(p, pOut, zJson); |
|
4f76459…
|
drh
|
1280 |
sqlite3_str_append(pOut,")",1); |
|
4f76459…
|
drh
|
1281 |
}else{ |
|
4f76459…
|
drh
|
1282 |
qrfEncodeText(p, pOut, zJson); |
|
4f76459…
|
drh
|
1283 |
} |
|
4f76459…
|
drh
|
1284 |
break; |
|
4f76459…
|
drh
|
1285 |
} |
|
4f76459…
|
drh
|
1286 |
} |
|
4f76459…
|
drh
|
1287 |
switch( p->spec.eBlob ){ |
|
4f76459…
|
drh
|
1288 |
case QRF_BLOB_Hex: |
|
4f76459…
|
drh
|
1289 |
case QRF_BLOB_Sql: { |
|
4f76459…
|
drh
|
1290 |
int iStart; |
|
4f76459…
|
drh
|
1291 |
int nBlob = sqlite3_column_bytes(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1292 |
int i, j; |
|
4f76459…
|
drh
|
1293 |
char *zVal; |
|
4f76459…
|
drh
|
1294 |
const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1295 |
if( p->spec.eBlob==QRF_BLOB_Sql ){ |
|
4f76459…
|
drh
|
1296 |
sqlite3_str_append(pOut, "x'", 2); |
|
4f76459…
|
drh
|
1297 |
} |
|
4f76459…
|
drh
|
1298 |
iStart = sqlite3_str_length(pOut); |
|
4f76459…
|
drh
|
1299 |
sqlite3_str_appendchar(pOut, nBlob, ' '); |
|
4f76459…
|
drh
|
1300 |
sqlite3_str_appendchar(pOut, nBlob, ' '); |
|
4f76459…
|
drh
|
1301 |
if( p->spec.eBlob==QRF_BLOB_Sql ){ |
|
4f76459…
|
drh
|
1302 |
sqlite3_str_appendchar(pOut, 1, '\''); |
|
4f76459…
|
drh
|
1303 |
} |
|
4f76459…
|
drh
|
1304 |
if( sqlite3_str_errcode(pOut) ) return; |
|
4f76459…
|
drh
|
1305 |
zVal = sqlite3_str_value(pOut); |
|
4f76459…
|
drh
|
1306 |
for(i=0, j=iStart; i<nBlob; i++, j+=2){ |
|
4f76459…
|
drh
|
1307 |
unsigned char c = a[i]; |
|
4f76459…
|
drh
|
1308 |
zVal[j] = "0123456789abcdef"[(c>>4)&0xf]; |
|
4f76459…
|
drh
|
1309 |
zVal[j+1] = "0123456789abcdef"[(c)&0xf]; |
|
4f76459…
|
drh
|
1310 |
} |
|
4f76459…
|
drh
|
1311 |
break; |
|
4f76459…
|
drh
|
1312 |
} |
|
4f76459…
|
drh
|
1313 |
case QRF_BLOB_Tcl: |
|
4f76459…
|
drh
|
1314 |
case QRF_BLOB_Json: { |
|
4f76459…
|
drh
|
1315 |
int iStart; |
|
4f76459…
|
drh
|
1316 |
int nBlob = sqlite3_column_bytes(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1317 |
int i, j; |
|
4f76459…
|
drh
|
1318 |
char *zVal; |
|
4f76459…
|
drh
|
1319 |
const unsigned char *a = sqlite3_column_blob(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1320 |
int szC = p->spec.eBlob==QRF_BLOB_Json ? 6 : 4; |
|
4f76459…
|
drh
|
1321 |
sqlite3_str_append(pOut, "\"", 1); |
|
4f76459…
|
drh
|
1322 |
iStart = sqlite3_str_length(pOut); |
|
4f76459…
|
drh
|
1323 |
for(i=szC; i>0; i--){ |
|
4f76459…
|
drh
|
1324 |
sqlite3_str_appendchar(pOut, nBlob, ' '); |
|
4f76459…
|
drh
|
1325 |
} |
|
4f76459…
|
drh
|
1326 |
sqlite3_str_appendchar(pOut, 1, '"'); |
|
4f76459…
|
drh
|
1327 |
if( sqlite3_str_errcode(pOut) ) return; |
|
4f76459…
|
drh
|
1328 |
zVal = sqlite3_str_value(pOut); |
|
4f76459…
|
drh
|
1329 |
for(i=0, j=iStart; i<nBlob; i++, j+=szC){ |
|
4f76459…
|
drh
|
1330 |
unsigned char c = a[i]; |
|
4f76459…
|
drh
|
1331 |
zVal[j] = '\\'; |
|
4f76459…
|
drh
|
1332 |
if( szC==4 ){ |
|
4f76459…
|
drh
|
1333 |
zVal[j+1] = '0' + ((c>>6)&3); |
|
4f76459…
|
drh
|
1334 |
zVal[j+2] = '0' + ((c>>3)&7); |
|
4f76459…
|
drh
|
1335 |
zVal[j+3] = '0' + (c&7); |
|
4f76459…
|
drh
|
1336 |
}else{ |
|
4f76459…
|
drh
|
1337 |
zVal[j+1] = 'u'; |
|
4f76459…
|
drh
|
1338 |
zVal[j+2] = '0'; |
|
4f76459…
|
drh
|
1339 |
zVal[j+3] = '0'; |
|
4f76459…
|
drh
|
1340 |
zVal[j+4] = "0123456789abcdef"[(c>>4)&0xf]; |
|
4f76459…
|
drh
|
1341 |
zVal[j+5] = "0123456789abcdef"[(c)&0xf]; |
|
4f76459…
|
drh
|
1342 |
} |
|
4f76459…
|
drh
|
1343 |
} |
|
4f76459…
|
drh
|
1344 |
break; |
|
4f76459…
|
drh
|
1345 |
} |
|
45de97f…
|
drh
|
1346 |
case QRF_BLOB_Size: { |
|
45de97f…
|
drh
|
1347 |
int nBlob = sqlite3_column_bytes(p->pStmt,iCol); |
|
45de97f…
|
drh
|
1348 |
sqlite3_str_appendf(pOut, "(%d-byte blob)", nBlob); |
|
45de97f…
|
drh
|
1349 |
break; |
|
45de97f…
|
drh
|
1350 |
} |
|
4f76459…
|
drh
|
1351 |
default: { |
|
4f76459…
|
drh
|
1352 |
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1353 |
qrfEncodeText(p, pOut, zTxt); |
|
4f76459…
|
drh
|
1354 |
} |
|
4f76459…
|
drh
|
1355 |
} |
|
4f76459…
|
drh
|
1356 |
break; |
|
4f76459…
|
drh
|
1357 |
} |
|
4f76459…
|
drh
|
1358 |
case SQLITE_NULL: { |
|
45de97f…
|
drh
|
1359 |
sqlite3_str_appendall(pOut, p->spec.zNull); |
|
4f76459…
|
drh
|
1360 |
break; |
|
4f76459…
|
drh
|
1361 |
} |
|
4f76459…
|
drh
|
1362 |
case SQLITE_TEXT: { |
|
4f76459…
|
drh
|
1363 |
const char *zTxt = (const char*)sqlite3_column_text(p->pStmt,iCol); |
|
4f76459…
|
drh
|
1364 |
qrfEncodeText(p, pOut, zTxt); |
|
4f76459…
|
drh
|
1365 |
break; |
|
4f76459…
|
drh
|
1366 |
} |
|
4f76459…
|
drh
|
1367 |
} |
|
4f76459…
|
drh
|
1368 |
#if SQLITE_VERSION_NUMBER>=3052000 |
|
4f76459…
|
drh
|
1369 |
if( p->spec.nCharLimit>0 |
|
4f76459…
|
drh
|
1370 |
&& (sqlite3_str_length(pOut) - iStartLen) > p->spec.nCharLimit |
|
4f76459…
|
drh
|
1371 |
){ |
|
4f76459…
|
drh
|
1372 |
const unsigned char *z; |
|
4f76459…
|
drh
|
1373 |
int ii = 0, w = 0, limit = p->spec.nCharLimit; |
|
4f76459…
|
drh
|
1374 |
z = (const unsigned char*)sqlite3_str_value(pOut) + iStartLen; |
|
4f76459…
|
drh
|
1375 |
if( limit<4 ) limit = 4; |
|
4f76459…
|
drh
|
1376 |
while( 1 ){ |
|
4f76459…
|
drh
|
1377 |
if( z[ii]<' ' ){ |
|
4f76459…
|
drh
|
1378 |
int k; |
|
4f76459…
|
drh
|
1379 |
if( z[ii]=='\033' && (k = qrfIsVt100(z+ii))>0 ){ |
|
4f76459…
|
drh
|
1380 |
ii += k; |
|
4f76459…
|
drh
|
1381 |
}else if( z[ii]==0 ){ |
|
4f76459…
|
drh
|
1382 |
break; |
|
4f76459…
|
drh
|
1383 |
}else{ |
|
4f76459…
|
drh
|
1384 |
ii++; |
|
4f76459…
|
drh
|
1385 |
} |
|
4f76459…
|
drh
|
1386 |
}else if( (0x80&z[ii])==0 ){ |
|
4f76459…
|
drh
|
1387 |
w++; |
|
4f76459…
|
drh
|
1388 |
if( w>limit ) break; |
|
4f76459…
|
drh
|
1389 |
ii++; |
|
4f76459…
|
drh
|
1390 |
}else{ |
|
4f76459…
|
drh
|
1391 |
int u = 0; |
|
4f76459…
|
drh
|
1392 |
int len = sqlite3_qrf_decode_utf8(&z[ii], &u); |
|
4f76459…
|
drh
|
1393 |
w += sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
1394 |
if( w>limit ) break; |
|
4f76459…
|
drh
|
1395 |
ii += len; |
|
4f76459…
|
drh
|
1396 |
} |
|
4f76459…
|
drh
|
1397 |
} |
|
4f76459…
|
drh
|
1398 |
if( w>limit ){ |
|
4f76459…
|
drh
|
1399 |
sqlite3_str_truncate(pOut, iStartLen+ii); |
|
4f76459…
|
drh
|
1400 |
sqlite3_str_append(pOut, "...", 3); |
|
4f76459…
|
drh
|
1401 |
} |
|
4f76459…
|
drh
|
1402 |
} |
|
9aee493…
|
drh
|
1403 |
#endif |
|
9aee493…
|
drh
|
1404 |
} |
|
9aee493…
|
drh
|
1405 |
|
|
9aee493…
|
drh
|
1406 |
/* Trim spaces of the end if pOut |
|
9aee493…
|
drh
|
1407 |
*/ |
|
9aee493…
|
drh
|
1408 |
static void qrfRTrim(sqlite3_str *pOut){ |
|
9aee493…
|
drh
|
1409 |
#if SQLITE_VERSION_NUMBER>=3052000 |
|
9aee493…
|
drh
|
1410 |
int nByte = sqlite3_str_length(pOut); |
|
9aee493…
|
drh
|
1411 |
const char *zOut = sqlite3_str_value(pOut); |
|
9aee493…
|
drh
|
1412 |
while( nByte>0 && zOut[nByte-1]==' ' ){ nByte--; } |
|
9aee493…
|
drh
|
1413 |
sqlite3_str_truncate(pOut, nByte); |
|
4f76459…
|
drh
|
1414 |
#endif |
|
4f76459…
|
drh
|
1415 |
} |
|
4f76459…
|
drh
|
1416 |
|
|
4f76459…
|
drh
|
1417 |
/* |
|
4f76459…
|
drh
|
1418 |
** Store string zUtf to pOut as w characters. If w is negative, |
|
4f76459…
|
drh
|
1419 |
** then right-justify the text. W is the width in display characters, not |
|
4f76459…
|
drh
|
1420 |
** in bytes. Double-width unicode characters count as two characters. |
|
4f76459…
|
drh
|
1421 |
** VT100 escape sequences count as zero. And so forth. |
|
4f76459…
|
drh
|
1422 |
*/ |
|
4f76459…
|
drh
|
1423 |
static void qrfWidthPrint(Qrf *p, sqlite3_str *pOut, int w, const char *zUtf){ |
|
4f76459…
|
drh
|
1424 |
(void)p; |
|
4f76459…
|
drh
|
1425 |
if( a==0 ) a = (const unsigned char*)""; |
|
4f76459…
|
drh
|
1426 |
int len = sqlite3_qrf_decode_utf8(a+i, &u); |
|
4f76459…
|
drh
|
1427 |
int x = sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
1428 |
}else if( c==0x1b && (k = qrfIsVt100(&a[i]))>0 ){ |
|
4f76459…
|
drh
|
1429 |
sqlite3_str_append(pOut, zUtf, i); |
|
4f76459…
|
drh
|
1430 |
if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); |
|
4f76459…
|
drh
|
1431 |
sqlite3_str_append(pOut, zUtf, i); |
|
4f76459…
|
drh
|
1432 |
}else{ |
|
4f76459…
|
drh
|
1433 |
sqlite3_str_append(pOut, zUtf, i); |
|
4f76459…
|
drh
|
1434 |
if( aw>n ) sqlite3_str_appendchar(pOut, aw-n, ' '); |
|
4f76459…
|
drh
|
1435 |
} |
|
4f76459…
|
drh
|
1436 |
} |
|
4f76459…
|
drh
|
1437 |
|
|
4f76459…
|
drh
|
1438 |
/* |
|
4f76459…
|
drh
|
1439 |
** (*pz)[] is a line of text that is to be displayed the box or table or |
|
4f76459…
|
drh
|
1440 |
** similar tabular formats. z[] contain newlines or might be too wide |
|
4f76459…
|
drh
|
1441 |
** to fit in the columns so will need to be split into multiple line. |
|
4f76459…
|
drh
|
1442 |
** |
|
4f76459…
|
drh
|
1443 |
** This routine determines: |
|
4f76459…
|
drh
|
1444 |
** |
|
4f76459…
|
drh
|
1445 |
** * How many bytes of z[] should be shown on the current line. |
|
4f76459…
|
drh
|
1446 |
** * How many character positions those bytes will cover. |
|
4f76459…
|
drh
|
1447 |
** * The byte offset to the start of the next line. |
|
4f76459…
|
drh
|
1448 |
*/ |
|
4f76459…
|
drh
|
1449 |
static void qrfWrapLine( |
|
4f76459…
|
drh
|
1450 |
const char *zIn, /* Input text to be displayed */ |
|
4f76459…
|
drh
|
1451 |
int w, /* Column width in characters (not bytes) */ |
|
4f76459…
|
drh
|
1452 |
int bWrap, /* True if we should do word-wrapping */ |
|
4f76459…
|
drh
|
1453 |
int *pnThis, /* OUT: How many bytes of z[] for the current line */ |
|
4f76459…
|
drh
|
1454 |
int *pnWide, /* OUT: How wide is the text of this line */ |
|
4f76459…
|
drh
|
1455 |
int *piNext /* OUT: Offset into z[] to start of the next line */ |
|
4f76459…
|
drh
|
1456 |
){ |
|
4f76459…
|
drh
|
1457 |
int i; /* Input bytes consumed */ |
|
4f76459…
|
drh
|
1458 |
int k; /* Bytes in a VT100 code */ |
|
4f76459…
|
drh
|
1459 |
int n; /* Output column number */ |
|
4f76459…
|
drh
|
1460 |
const unsigned char *z = (const unsigned char*)zIn; |
|
4f76459…
|
drh
|
1461 |
unsigned char c = 0; |
|
4f76459…
|
drh
|
1462 |
|
|
6ae9c9a…
|
drh
|
1463 |
if( z[0]==0 ){ |
|
4f76459…
|
drh
|
1464 |
*pnThis = 0; |
|
4f76459…
|
drh
|
1465 |
*pnWide = 0; |
|
4f76459…
|
drh
|
1466 |
*piNext = 0; |
|
4f76459…
|
drh
|
1467 |
return; |
|
4f76459…
|
drh
|
1468 |
} |
|
4f76459…
|
drh
|
1469 |
n = 0; |
|
6ae9c9a…
|
drh
|
1470 |
for(i=0; n<=w; i++){ |
|
6ae9c9a…
|
drh
|
1471 |
c = z[i]; |
|
4f76459…
|
drh
|
1472 |
if( c>=0xc0 ){ |
|
4f76459…
|
drh
|
1473 |
int u; |
|
4f76459…
|
drh
|
1474 |
int len = sqlite3_qrf_decode_utf8(&z[i], &u); |
|
4f76459…
|
drh
|
1475 |
int wcw = sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
1476 |
if( wcw+n>w ) break; |
|
4f76459…
|
drh
|
1477 |
i += len-1; |
|
4f76459…
|
drh
|
1478 |
n += wcw; |
|
4f76459…
|
drh
|
1479 |
continue; |
|
4f76459…
|
drh
|
1480 |
} |
|
4f76459…
|
drh
|
1481 |
if( c>=' ' ){ |
|
6ae9c9a…
|
drh
|
1482 |
if( n==w ) break; |
|
4f76459…
|
drh
|
1483 |
n++; |
|
4f76459…
|
drh
|
1484 |
continue; |
|
4f76459…
|
drh
|
1485 |
} |
|
4f76459…
|
drh
|
1486 |
if( c==0 || c=='\n' ) break; |
|
6ae9c9a…
|
drh
|
1487 |
if( c=='\r' && z[i+1]=='\n' ){ c = z[++i]; break; } |
|
4f76459…
|
drh
|
1488 |
if( c=='\t' ){ |
|
4f76459…
|
drh
|
1489 |
int wcw = 8 - (n&7); |
|
4f76459…
|
drh
|
1490 |
if( n+wcw>w ) break; |
|
4f76459…
|
drh
|
1491 |
n += wcw; |
|
4f76459…
|
drh
|
1492 |
continue; |
|
4f76459…
|
drh
|
1493 |
} |
|
4f76459…
|
drh
|
1494 |
if( c==0x1b && (k = qrfIsVt100(&z[i]))>0 ){ |
|
4f76459…
|
drh
|
1495 |
i += k-1; |
|
6ae9c9a…
|
drh
|
1496 |
}else if( n==w ){ |
|
6ae9c9a…
|
drh
|
1497 |
break; |
|
4f76459…
|
drh
|
1498 |
}else{ |
|
4f76459…
|
drh
|
1499 |
} |
|
4f76459…
|
drh
|
1500 |
} |
|
4f76459…
|
drh
|
1501 |
if( c==0 ){ |
|
4f76459…
|
drh
|
1502 |
*pnThis = i; |
|
4f76459…
|
drh
|
1503 |
*pnWide = n; |
|
4f76459…
|
drh
|
1504 |
*piNext = i; |
|
4f76459…
|
drh
|
1505 |
return; |
|
4f76459…
|
drh
|
1506 |
} |
|
4f76459…
|
drh
|
1507 |
if( c=='\n' ){ |
|
4f76459…
|
drh
|
1508 |
*pnThis = i; |
|
4f76459…
|
drh
|
1509 |
*pnWide = n; |
|
4f76459…
|
drh
|
1510 |
*piNext = i+1; |
|
4f76459…
|
drh
|
1511 |
return; |
|
4f76459…
|
drh
|
1512 |
} |
|
4f76459…
|
drh
|
1513 |
|
|
4f76459…
|
drh
|
1514 |
/* If we get this far, that means the current line will end at some |
|
4f76459…
|
drh
|
1515 |
** point that is neither a "\n" or a 0x00. Figure out where that |
|
4f76459…
|
drh
|
1516 |
** split should occur |
|
4f76459…
|
drh
|
1517 |
*/ |
|
4f76459…
|
drh
|
1518 |
if( bWrap && z[i]!=0 && !qrfSpace(z[i]) && qrfAlnum(c)==qrfAlnum(z[i]) ){ |
|
4f76459…
|
drh
|
1519 |
/* Perhaps try to back up to a better place to break the line */ |
|
4f76459…
|
drh
|
1520 |
for(k=i-1; k>=i/2; k--){ |
|
4f76459…
|
drh
|
1521 |
if( qrfSpace(z[k]) ) break; |
|
4f76459…
|
drh
|
1522 |
} |
|
4f76459…
|
drh
|
1523 |
if( k<i/2 ){ |
|
4f76459…
|
drh
|
1524 |
for(k=i; k>=i/2; k--){ |
|
4f76459…
|
drh
|
1525 |
if( qrfAlnum(z[k-1])!=qrfAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; |
|
4f76459…
|
drh
|
1526 |
} |
|
4f76459…
|
drh
|
1527 |
} |
|
4f76459…
|
drh
|
1528 |
if( k>=i/2 ){ |
|
4f76459…
|
drh
|
1529 |
i = k; |
|
4f76459…
|
drh
|
1530 |
n = qrfDisplayWidth((const char*)z, k, 0); |
|
4f76459…
|
drh
|
1531 |
} |
|
4f76459…
|
drh
|
1532 |
} |
|
4f76459…
|
drh
|
1533 |
*pnThis = i; |
|
4f76459…
|
drh
|
1534 |
*pnWide = n; |
|
4f76459…
|
drh
|
1535 |
while( zIn[i]==' ' || zIn[i]=='\t' || zIn[i]=='\r' ){ i++; } |
|
4f76459…
|
drh
|
1536 |
*piNext = i; |
|
4f76459…
|
drh
|
1537 |
} |
|
4f76459…
|
drh
|
1538 |
|
|
4f76459…
|
drh
|
1539 |
/* |
|
4f76459…
|
drh
|
1540 |
** Append nVal bytes of text from zVal onto the end of pOut. |
|
4f76459…
|
drh
|
1541 |
** Convert tab characters in zVal to the appropriate number of |
|
4f76459…
|
drh
|
1542 |
** spaces. |
|
4f76459…
|
drh
|
1543 |
*/ |
|
4f76459…
|
drh
|
1544 |
static void qrfAppendWithTabs( |
|
4f76459…
|
drh
|
1545 |
sqlite3_str *pOut, /* Append text here */ |
|
4f76459…
|
drh
|
1546 |
const char *zVal, /* Text to append */ |
|
4f76459…
|
drh
|
1547 |
int nVal /* Use only the first nVal bytes of zVal[] */ |
|
4f76459…
|
drh
|
1548 |
){ |
|
4f76459…
|
drh
|
1549 |
int i = 0; |
|
4f76459…
|
drh
|
1550 |
unsigned int col = 0; |
|
4f76459…
|
drh
|
1551 |
unsigned char *z = (unsigned char *)zVal; |
|
4f76459…
|
drh
|
1552 |
while( i<nVal ){ |
|
4f76459…
|
drh
|
1553 |
unsigned char c = z[i]; |
|
4f76459…
|
drh
|
1554 |
if( c<' ' ){ |
|
4f76459…
|
drh
|
1555 |
int k; |
|
4f76459…
|
drh
|
1556 |
sqlite3_str_append(pOut, (const char*)z, i); |
|
4f76459…
|
drh
|
1557 |
nVal -= i; |
|
4f76459…
|
drh
|
1558 |
z += i; |
|
4f76459…
|
drh
|
1559 |
i = 0; |
|
4f76459…
|
drh
|
1560 |
if( c=='\033' && (k = qrfIsVt100(z))>0 ){ |
|
4f76459…
|
drh
|
1561 |
sqlite3_str_append(pOut, (const char*)z, k); |
|
4f76459…
|
drh
|
1562 |
z += k; |
|
4f76459…
|
drh
|
1563 |
nVal -= k; |
|
4f76459…
|
drh
|
1564 |
}else if( c=='\t' ){ |
|
4f76459…
|
drh
|
1565 |
k = 8 - (col&7); |
|
4f76459…
|
drh
|
1566 |
sqlite3_str_appendchar(pOut, k, ' '); |
|
4f76459…
|
drh
|
1567 |
col += k; |
|
4f76459…
|
drh
|
1568 |
z++; |
|
4f76459…
|
drh
|
1569 |
nVal--; |
|
4f76459…
|
drh
|
1570 |
}else if( c=='\r' && nVal==1 ){ |
|
4f76459…
|
drh
|
1571 |
z++; |
|
4f76459…
|
drh
|
1572 |
nVal--; |
|
4f76459…
|
drh
|
1573 |
}else{ |
|
4f76459…
|
drh
|
1574 |
char zCtrlPik[4]; |
|
4f76459…
|
drh
|
1575 |
col++; |
|
4f76459…
|
drh
|
1576 |
zCtrlPik[0] = 0xe2; |
|
4f76459…
|
drh
|
1577 |
zCtrlPik[1] = 0x90; |
|
4f76459…
|
drh
|
1578 |
zCtrlPik[2] = 0x80+c; |
|
4f76459…
|
drh
|
1579 |
sqlite3_str_append(pOut, zCtrlPik, 3); |
|
4f76459…
|
drh
|
1580 |
z++; |
|
4f76459…
|
drh
|
1581 |
nVal--; |
|
4f76459…
|
drh
|
1582 |
} |
|
4f76459…
|
drh
|
1583 |
}else if( (0x80&c)==0 ){ |
|
4f76459…
|
drh
|
1584 |
i++; |
|
4f76459…
|
drh
|
1585 |
col++; |
|
4f76459…
|
drh
|
1586 |
int len = sqlite3_qrf_decode_utf8(&z[i], &u); |
|
4f76459…
|
drh
|
1587 |
i += len; |
|
4f76459…
|
drh
|
1588 |
col += sqlite3_qrf_wcwidth(u); |
|
4f76459…
|
drh
|
1589 |
} |
|
4f76459…
|
drh
|
1590 |
} |
|
4f76459…
|
drh
|
1591 |
sqlite3_str_append(pOut, (const char*)z, i); |
|
4f76459…
|
drh
|
1592 |
} |
|
4f76459…
|
drh
|
1593 |
|
|
4f76459…
|
drh
|
1594 |
/* |
|
4f76459…
|
drh
|
1595 |
** GCC does not define the offsetof() macro so we'll have to do it |
|
4f76459…
|
drh
|
1596 |
** ourselves. |
|
4f76459…
|
drh
|
1597 |
*/ |
|
4f76459…
|
drh
|
1598 |
#ifndef offsetof |
|
4f76459…
|
drh
|
1599 |
# define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) |
|
4f76459…
|
drh
|
1600 |
#endif |
|
4f76459…
|
drh
|
1601 |
|
|
4f76459…
|
drh
|
1602 |
/* |
|
4f76459…
|
drh
|
1603 |
** Data for columnar layout, collected into a single object so |
|
4f76459…
|
drh
|
1604 |
** that it can be more easily passed into subroutines. |
|
4f76459…
|
drh
|
1605 |
*/ |
|
4f76459…
|
drh
|
1606 |
typedef struct qrfColData qrfColData; |
|
4f76459…
|
drh
|
1607 |
struct qrfColData { |
|
4f76459…
|
drh
|
1608 |
Qrf *p; /* The QRF instance */ |
|
4f76459…
|
drh
|
1609 |
int nCol; /* Number of columns in the table */ |
|
4f76459…
|
drh
|
1610 |
unsigned char bMultiRow; /* One or more cells will span multiple lines */ |
|
4f76459…
|
drh
|
1611 |
unsigned char nMargin; /* Width of column margins */ |
|
4f76459…
|
drh
|
1612 |
sqlite3_int64 nRow; /* Number of rows */ |
|
4f76459…
|
drh
|
1613 |
sqlite3_int64 nAlloc; /* Number of cells allocated */ |
|
4f76459…
|
drh
|
1614 |
sqlite3_int64 n; /* Number of cells. nCol*nRow */ |
|
4f76459…
|
drh
|
1615 |
char **az; /* Content of all cells */ |
|
4f76459…
|
drh
|
1616 |
int *aiWth; /* Width of each cell */ |
|
f4b3b59…
|
drh
|
1617 |
unsigned char *abNum; /* True for each numeric cell */ |
|
4f76459…
|
drh
|
1618 |
struct qrfPerCol { /* Per-column data */ |
|
4f76459…
|
drh
|
1619 |
char *z; /* Cache of text for current row */ |
|
4f76459…
|
drh
|
1620 |
int w; /* Computed width of this column */ |
|
4f76459…
|
drh
|
1621 |
int mxW; /* Maximum natural (unwrapped) width */ |
|
4f76459…
|
drh
|
1622 |
unsigned char e; /* Alignment */ |
|
4f76459…
|
drh
|
1623 |
unsigned char fx; /* Width is fixed */ |
|
f4b3b59…
|
drh
|
1624 |
unsigned char bNum; /* True if is numeric */ |
|
4f76459…
|
drh
|
1625 |
} *a; /* One per column */ |
|
4f76459…
|
drh
|
1626 |
}; |
|
f4b3b59…
|
drh
|
1627 |
|
|
f4b3b59…
|
drh
|
1628 |
/* |
|
f4b3b59…
|
drh
|
1629 |
** Output horizontally justified text into pOut. The text is the |
|
f4b3b59…
|
drh
|
1630 |
** first nVal bytes of zVal. Include nWS bytes of whitespace, either |
|
f4b3b59…
|
drh
|
1631 |
** split between both sides, or on the left, or on the right, depending |
|
f4b3b59…
|
drh
|
1632 |
** on eAlign. |
|
f4b3b59…
|
drh
|
1633 |
*/ |
|
f4b3b59…
|
drh
|
1634 |
static void qrfPrintAligned( |
|
f4b3b59…
|
drh
|
1635 |
sqlite3_str *pOut, /* Append text here */ |
|
f4b3b59…
|
drh
|
1636 |
struct qrfPerCol *pCol, /* Information about the text to print */ |
|
f4b3b59…
|
drh
|
1637 |
int nVal, /* Use only the first nVal bytes of zVal[] */ |
|
f4b3b59…
|
drh
|
1638 |
int nWS /* Whitespace for horizonal alignment */ |
|
f4b3b59…
|
drh
|
1639 |
){ |
|
f4b3b59…
|
drh
|
1640 |
unsigned char eAlign = pCol->e & QRF_ALIGN_HMASK; |
|
f4b3b59…
|
drh
|
1641 |
if( eAlign==QRF_Auto && pCol->bNum ) eAlign = QRF_ALIGN_Right; |
|
f4b3b59…
|
drh
|
1642 |
if( eAlign==QRF_ALIGN_Center ){ |
|
f4b3b59…
|
drh
|
1643 |
/* Center the text */ |
|
f4b3b59…
|
drh
|
1644 |
sqlite3_str_appendchar(pOut, nWS/2, ' '); |
|
f4b3b59…
|
drh
|
1645 |
qrfAppendWithTabs(pOut, pCol->z, nVal); |
|
f4b3b59…
|
drh
|
1646 |
sqlite3_str_appendchar(pOut, nWS - nWS/2, ' '); |
|
f4b3b59…
|
drh
|
1647 |
}else if( eAlign==QRF_ALIGN_Right ){ |
|
f4b3b59…
|
drh
|
1648 |
/* Right justify the text */ |
|
f4b3b59…
|
drh
|
1649 |
sqlite3_str_appendchar(pOut, nWS, ' '); |
|
f4b3b59…
|
drh
|
1650 |
qrfAppendWithTabs(pOut, pCol->z, nVal); |
|
f4b3b59…
|
drh
|
1651 |
}else{ |
|
f4b3b59…
|
drh
|
1652 |
/* Left justify the text */ |
|
f4b3b59…
|
drh
|
1653 |
qrfAppendWithTabs(pOut, pCol->z, nVal); |
|
f4b3b59…
|
drh
|
1654 |
sqlite3_str_appendchar(pOut, nWS, ' '); |
|
f4b3b59…
|
drh
|
1655 |
} |
|
f4b3b59…
|
drh
|
1656 |
} |
|
4f76459…
|
drh
|
1657 |
|
|
4f76459…
|
drh
|
1658 |
/* |
|
4f76459…
|
drh
|
1659 |
** Free all the memory allocates in the qrfColData object |
|
4f76459…
|
drh
|
1660 |
*/ |
|
4f76459…
|
drh
|
1661 |
static void qrfColDataFree(qrfColData *p){ |
|
4f76459…
|
drh
|
1662 |
sqlite3_int64 i; |
|
4f76459…
|
drh
|
1663 |
for(i=0; i<p->n; i++) sqlite3_free(p->az[i]); |
|
4f76459…
|
drh
|
1664 |
sqlite3_free(p->az); |
|
4f76459…
|
drh
|
1665 |
sqlite3_free(p->aiWth); |
|
f4b3b59…
|
drh
|
1666 |
sqlite3_free(p->abNum); |
|
4f76459…
|
drh
|
1667 |
sqlite3_free(p->a); |
|
4f76459…
|
drh
|
1668 |
|
|
4f76459…
|
drh
|
1669 |
/* |
|
4f76459…
|
drh
|
1670 |
** Allocate space for more cells in the qrfColData object. |
|
4f76459…
|
drh
|
1671 |
** Return non-zero if a memory allocation fails. |
|
4f76459…
|
drh
|
1672 |
*/ |
|
4f76459…
|
drh
|
1673 |
static int qrfColDataEnlarge(qrfColData *p){ |
|
4f76459…
|
drh
|
1674 |
char **azData; |
|
4f76459…
|
drh
|
1675 |
int *aiWth; |
|
f4b3b59…
|
drh
|
1676 |
unsigned char *abNum; |
|
4f76459…
|
drh
|
1677 |
p->nAlloc = 2*p->nAlloc + 10*p->nCol; |
|
4f76459…
|
drh
|
1678 |
azData = sqlite3_realloc64(p->az, p->nAlloc*sizeof(char*)); |
|
4f76459…
|
drh
|
1679 |
if( azData==0 ){ |
|
4f76459…
|
drh
|
1680 |
qrfOom(p->p); |
|
4f76459…
|
drh
|
1681 |
qrfColDataFree(p); |
|
4f76459…
|
drh
|
1682 |
return 1; |
|
4f76459…
|
drh
|
1683 |
} |
|
4f76459…
|
drh
|
1684 |
p->az = azData; |
|
4f76459…
|
drh
|
1685 |
aiWth = sqlite3_realloc64(p->aiWth, p->nAlloc*sizeof(int)); |
|
4f76459…
|
drh
|
1686 |
if( aiWth==0 ){ |
|
4f76459…
|
drh
|
1687 |
qrfOom(p->p); |
|
4f76459…
|
drh
|
1688 |
qrfColDataFree(p); |
|
4f76459…
|
drh
|
1689 |
return 1; |
|
4f76459…
|
drh
|
1690 |
} |
|
4f76459…
|
drh
|
1691 |
p->aiWth = aiWth; |
|
f4b3b59…
|
drh
|
1692 |
abNum = sqlite3_realloc64(p->abNum, p->nAlloc); |
|
f4b3b59…
|
drh
|
1693 |
if( abNum==0 ){ |
|
f4b3b59…
|
drh
|
1694 |
qrfOom(p->p); |
|
f4b3b59…
|
drh
|
1695 |
qrfColDataFree(p); |
|
f4b3b59…
|
drh
|
1696 |
return 1; |
|
f4b3b59…
|
drh
|
1697 |
} |
|
f4b3b59…
|
drh
|
1698 |
p->abNum = abNum; |
|
4f76459…
|
drh
|
1699 |
return 0; |
|
4f76459…
|
drh
|
1700 |
} |
|
4f76459…
|
drh
|
1701 |
|
|
4f76459…
|
drh
|
1702 |
/* |
|
4f76459…
|
drh
|
1703 |
** Print a markdown or table-style row separator using ascii-art |
|
4f76459…
|
drh
|
1704 |
*/ |
|
4f76459…
|
drh
|
1705 |
static void qrfRowSeparator(sqlite3_str *pOut, qrfColData *p, char cSep){ |
|
4f76459…
|
drh
|
1706 |
int i; |
|
4f76459…
|
drh
|
1707 |
if( p->nCol>0 ){ |
|
f4b3b59…
|
drh
|
1708 |
int useBorder = p->p->spec.bBorder!=QRF_No; |
|
f4b3b59…
|
drh
|
1709 |
if( useBorder ){ |
|
f4b3b59…
|
drh
|
1710 |
sqlite3_str_append(pOut, &cSep, 1); |
|
f4b3b59…
|
drh
|
1711 |
} |
|
4f76459…
|
drh
|
1712 |
sqlite3_str_appendchar(pOut, p->a[0].w+p->nMargin, '-'); |
|
4f76459…
|
drh
|
1713 |
for(i=1; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
1714 |
sqlite3_str_append(pOut, &cSep, 1); |
|
4f76459…
|
drh
|
1715 |
sqlite3_str_appendchar(pOut, p->a[i].w+p->nMargin, '-'); |
|
4f76459…
|
drh
|
1716 |
} |
|
f4b3b59…
|
drh
|
1717 |
if( useBorder ){ |
|
f4b3b59…
|
drh
|
1718 |
sqlite3_str_append(pOut, &cSep, 1); |
|
f4b3b59…
|
drh
|
1719 |
} |
|
4f76459…
|
drh
|
1720 |
} |
|
4f76459…
|
drh
|
1721 |
sqlite3_str_append(pOut, "\n", 1); |
|
4f76459…
|
drh
|
1722 |
} |
|
4f76459…
|
drh
|
1723 |
|
|
4f76459…
|
drh
|
1724 |
/* |
|
4f76459…
|
drh
|
1725 |
** UTF8 box-drawing characters. Imagine box lines like this: |
|
4f76459…
|
drh
|
1726 |
** |
|
4f76459…
|
drh
|
1727 |
** 1 |
|
4f76459…
|
drh
|
1728 |
** | |
|
4f76459…
|
drh
|
1729 |
** 4 --+-- 2 |
|
4f76459…
|
drh
|
1730 |
** | |
|
4f76459…
|
drh
|
1731 |
** 3 |
|
4f76459…
|
drh
|
1732 |
** |
|
4f76459…
|
drh
|
1733 |
** Each box characters has between 2 and 4 of the lines leading from |
|
4f76459…
|
drh
|
1734 |
** the center. The characters are here identified by the numbers of |
|
4f76459…
|
drh
|
1735 |
** their corresponding lines. |
|
4f76459…
|
drh
|
1736 |
*/ |
|
4f76459…
|
drh
|
1737 |
#define BOX_24 "\342\224\200" /* U+2500 --- */ |
|
4f76459…
|
drh
|
1738 |
#define BOX_13 "\342\224\202" /* U+2502 | */ |
|
4f76459…
|
drh
|
1739 |
#define BOX_23 "\342\224\214" /* U+250c ,- */ |
|
4f76459…
|
drh
|
1740 |
#define BOX_34 "\342\224\220" /* U+2510 -, */ |
|
4f76459…
|
drh
|
1741 |
#define BOX_12 "\342\224\224" /* U+2514 '- */ |
|
4f76459…
|
drh
|
1742 |
#define BOX_14 "\342\224\230" /* U+2518 -' */ |
|
4f76459…
|
drh
|
1743 |
#define BOX_123 "\342\224\234" /* U+251c |- */ |
|
4f76459…
|
drh
|
1744 |
#define BOX_134 "\342\224\244" /* U+2524 -| */ |
|
4f76459…
|
drh
|
1745 |
#define BOX_234 "\342\224\254" /* U+252c -,- */ |
|
4f76459…
|
drh
|
1746 |
#define BOX_124 "\342\224\264" /* U+2534 -'- */ |
|
4f76459…
|
drh
|
1747 |
#define BOX_1234 "\342\224\274" /* U+253c -|- */ |
|
4f76459…
|
drh
|
1748 |
|
|
709b566…
|
drh
|
1749 |
/* Rounded corners: */ |
|
709b566…
|
drh
|
1750 |
#define BOX_R12 "\342\225\260" /* U+2570 '- */ |
|
709b566…
|
drh
|
1751 |
#define BOX_R23 "\342\225\255" /* U+256d ,- */ |
|
709b566…
|
drh
|
1752 |
#define BOX_R34 "\342\225\256" /* U+256e -, */ |
|
709b566…
|
drh
|
1753 |
#define BOX_R14 "\342\225\257" /* U+256f -' */ |
|
709b566…
|
drh
|
1754 |
|
|
709b566…
|
drh
|
1755 |
/* Doubled horizontal lines: */ |
|
709b566…
|
drh
|
1756 |
#define DBL_24 "\342\225\220" /* U+2550 === */ |
|
709b566…
|
drh
|
1757 |
#define DBL_123 "\342\225\236" /* U+255e |= */ |
|
709b566…
|
drh
|
1758 |
#define DBL_134 "\342\225\241" /* U+2561 =| */ |
|
709b566…
|
drh
|
1759 |
#define DBL_1234 "\342\225\252" /* U+256a =|= */ |
|
709b566…
|
drh
|
1760 |
|
|
4f76459…
|
drh
|
1761 |
/* Draw horizontal line N characters long using unicode box |
|
4f76459…
|
drh
|
1762 |
** characters |
|
4f76459…
|
drh
|
1763 |
*/ |
|
709b566…
|
drh
|
1764 |
static void qrfBoxLine(sqlite3_str *pOut, int N, int bDbl){ |
|
709b566…
|
drh
|
1765 |
const char *azDash[2] = { |
|
709b566…
|
drh
|
1766 |
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24, |
|
709b566…
|
drh
|
1767 |
DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 DBL_24 |
|
709b566…
|
drh
|
1768 |
};/* 0 1 2 3 4 5 6 7 8 9 */ |
|
709b566…
|
drh
|
1769 |
const int nDash = 30; |
|
4f76459…
|
drh
|
1770 |
N *= 3; |
|
4f76459…
|
drh
|
1771 |
while( N>nDash ){ |
|
709b566…
|
drh
|
1772 |
sqlite3_str_append(pOut, azDash[bDbl], nDash); |
|
4f76459…
|
drh
|
1773 |
N -= nDash; |
|
4f76459…
|
drh
|
1774 |
} |
|
709b566…
|
drh
|
1775 |
sqlite3_str_append(pOut, azDash[bDbl], N); |
|
4f76459…
|
drh
|
1776 |
} |
|
4f76459…
|
drh
|
1777 |
|
|
4f76459…
|
drh
|
1778 |
/* |
|
4f76459…
|
drh
|
1779 |
** Draw a horizontal separator for a QRF_STYLE_Box table. |
|
4f76459…
|
drh
|
1780 |
*/ |
|
4f76459…
|
drh
|
1781 |
static void qrfBoxSeparator( |
|
4f76459…
|
drh
|
1782 |
sqlite3_str *pOut, |
|
4f76459…
|
drh
|
1783 |
qrfColData *p, |
|
4f76459…
|
drh
|
1784 |
const char *zSep1, |
|
4f76459…
|
drh
|
1785 |
const char *zSep2, |
|
709b566…
|
drh
|
1786 |
const char *zSep3, |
|
709b566…
|
drh
|
1787 |
int bDbl |
|
4f76459…
|
drh
|
1788 |
){ |
|
4f76459…
|
drh
|
1789 |
int i; |
|
4f76459…
|
drh
|
1790 |
if( p->nCol>0 ){ |
|
f4b3b59…
|
drh
|
1791 |
int useBorder = p->p->spec.bBorder!=QRF_No; |
|
f4b3b59…
|
drh
|
1792 |
if( useBorder ){ |
|
f4b3b59…
|
drh
|
1793 |
sqlite3_str_appendall(pOut, zSep1); |
|
f4b3b59…
|
drh
|
1794 |
} |
|
709b566…
|
drh
|
1795 |
qrfBoxLine(pOut, p->a[0].w+p->nMargin, bDbl); |
|
4f76459…
|
drh
|
1796 |
for(i=1; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
1797 |
sqlite3_str_appendall(pOut, zSep2); |
|
709b566…
|
drh
|
1798 |
qrfBoxLine(pOut, p->a[i].w+p->nMargin, bDbl); |
|
4f76459…
|
drh
|
1799 |
} |
|
f4b3b59…
|
drh
|
1800 |
if( useBorder ){ |
|
f4b3b59…
|
drh
|
1801 |
sqlite3_str_appendall(pOut, zSep3); |
|
f4b3b59…
|
drh
|
1802 |
} |
|
4f76459…
|
drh
|
1803 |
} |
|
4f76459…
|
drh
|
1804 |
sqlite3_str_append(pOut, "\n", 1); |
|
4f76459…
|
drh
|
1805 |
} |
|
4f76459…
|
drh
|
1806 |
|
|
4f76459…
|
drh
|
1807 |
/* |
|
4f76459…
|
drh
|
1808 |
** Load into pData the default alignment for the body of a table. |
|
4f76459…
|
drh
|
1809 |
*/ |
|
4f76459…
|
drh
|
1810 |
static void qrfLoadAlignment(qrfColData *pData, Qrf *p){ |
|
4f76459…
|
drh
|
1811 |
sqlite3_int64 i; |
|
4f76459…
|
drh
|
1812 |
for(i=0; i<pData->nCol; i++){ |
|
4f76459…
|
drh
|
1813 |
pData->a[i].e = p->spec.eDfltAlign; |
|
4f76459…
|
drh
|
1814 |
if( i<p->spec.nAlign ){ |
|
4f76459…
|
drh
|
1815 |
unsigned char ax = p->spec.aAlign[i]; |
|
4f76459…
|
drh
|
1816 |
if( (ax & QRF_ALIGN_HMASK)!=0 ){ |
|
4f76459…
|
drh
|
1817 |
pData->a[i].e = (ax & QRF_ALIGN_HMASK) | |
|
4f76459…
|
drh
|
1818 |
(pData->a[i].e & QRF_ALIGN_VMASK); |
|
4f76459…
|
drh
|
1819 |
} |
|
4f76459…
|
drh
|
1820 |
}else if( i<p->spec.nWidth ){ |
|
4f76459…
|
drh
|
1821 |
if( p->spec.aWidth[i]<0 ){ |
|
4f76459…
|
drh
|
1822 |
pData->a[i].e = QRF_ALIGN_Right | |
|
4f76459…
|
drh
|
1823 |
(pData->a[i].e & QRF_ALIGN_VMASK); |
|
4f76459…
|
drh
|
1824 |
} |
|
4f76459…
|
drh
|
1825 |
} |
|
4f76459…
|
drh
|
1826 |
} |
|
4f76459…
|
drh
|
1827 |
} |
|
4f76459…
|
drh
|
1828 |
|
|
4f76459…
|
drh
|
1829 |
/* |
|
9aee493…
|
drh
|
1830 |
** If the single column in pData->a[] with pData->n entries can be |
|
9aee493…
|
drh
|
1831 |
** laid out as nCol columns with a 2-space gap between each such |
|
9aee493…
|
drh
|
1832 |
** that all columns fit within nSW, then return a pointer to an array |
|
9aee493…
|
drh
|
1833 |
** of integers which is the width of each column from left to right. |
|
9aee493…
|
drh
|
1834 |
** |
|
9aee493…
|
drh
|
1835 |
** If the layout is not possible, return a NULL pointer. |
|
9aee493…
|
drh
|
1836 |
** |
|
9aee493…
|
drh
|
1837 |
** Space to hold the returned array is from sqlite_malloc64(). |
|
9aee493…
|
drh
|
1838 |
*/ |
|
9aee493…
|
drh
|
1839 |
static int *qrfValidLayout( |
|
9aee493…
|
drh
|
1840 |
qrfColData *pData, /* Collected query results */ |
|
9aee493…
|
drh
|
1841 |
Qrf *p, /* On which to report an OOM */ |
|
9aee493…
|
drh
|
1842 |
int nCol, /* Attempt this many columns */ |
|
9aee493…
|
drh
|
1843 |
int nSW /* Screen width */ |
|
9aee493…
|
drh
|
1844 |
){ |
|
9aee493…
|
drh
|
1845 |
int i; /* Loop counter */ |
|
9aee493…
|
drh
|
1846 |
int nr; /* Number of rows */ |
|
9aee493…
|
drh
|
1847 |
int w = 0; /* Width of the current column */ |
|
9aee493…
|
drh
|
1848 |
int t; /* Total width of all columns */ |
|
9aee493…
|
drh
|
1849 |
int *aw; /* Array of individual column widths */ |
|
9aee493…
|
drh
|
1850 |
|
|
9aee493…
|
drh
|
1851 |
aw = sqlite3_malloc64( sizeof(int)*nCol ); |
|
9aee493…
|
drh
|
1852 |
if( aw==0 ){ |
|
9aee493…
|
drh
|
1853 |
qrfOom(p); |
|
9aee493…
|
drh
|
1854 |
return 0; |
|
9aee493…
|
drh
|
1855 |
} |
|
9aee493…
|
drh
|
1856 |
nr = (pData->n + nCol - 1)/nCol; |
|
9aee493…
|
drh
|
1857 |
for(i=0; i<pData->n; i++){ |
|
9aee493…
|
drh
|
1858 |
if( (i%nr)==0 ){ |
|
9aee493…
|
drh
|
1859 |
if( i>0 ) aw[i/nr-1] = w; |
|
9aee493…
|
drh
|
1860 |
w = pData->aiWth[i]; |
|
9aee493…
|
drh
|
1861 |
}else if( pData->aiWth[i]>w ){ |
|
9aee493…
|
drh
|
1862 |
w = pData->aiWth[i]; |
|
9aee493…
|
drh
|
1863 |
} |
|
9aee493…
|
drh
|
1864 |
} |
|
9aee493…
|
drh
|
1865 |
aw[nCol-1] = w; |
|
9aee493…
|
drh
|
1866 |
for(t=i=0; i<nCol; i++) t += aw[i]; |
|
9aee493…
|
drh
|
1867 |
t += 2*(nCol-1); |
|
9aee493…
|
drh
|
1868 |
if( t>nSW ){ |
|
9aee493…
|
drh
|
1869 |
sqlite3_free(aw); |
|
9aee493…
|
drh
|
1870 |
return 0; |
|
9aee493…
|
drh
|
1871 |
} |
|
9aee493…
|
drh
|
1872 |
return aw; |
|
9aee493…
|
drh
|
1873 |
} |
|
9aee493…
|
drh
|
1874 |
|
|
9aee493…
|
drh
|
1875 |
/* |
|
9aee493…
|
drh
|
1876 |
** The output is single-column and the bSplitColumn flag is set. |
|
9aee493…
|
drh
|
1877 |
** Check to see if the single-column output can be split into multiple |
|
9aee493…
|
drh
|
1878 |
** columns that appear side-by-side. Adjust pData appropriately. |
|
9aee493…
|
drh
|
1879 |
*/ |
|
9aee493…
|
drh
|
1880 |
static void qrfSplitColumn(qrfColData *pData, Qrf *p){ |
|
9aee493…
|
drh
|
1881 |
int nCol = 1; |
|
9aee493…
|
drh
|
1882 |
int *aw = 0; |
|
9aee493…
|
drh
|
1883 |
char **az = 0; |
|
9aee493…
|
drh
|
1884 |
int *aiWth = 0; |
|
f4b3b59…
|
drh
|
1885 |
unsigned char *abNum = 0; |
|
9aee493…
|
drh
|
1886 |
int nColNext = 2; |
|
9aee493…
|
drh
|
1887 |
int w; |
|
9aee493…
|
drh
|
1888 |
struct qrfPerCol *a = 0; |
|
9aee493…
|
drh
|
1889 |
sqlite3_int64 nRow = 1; |
|
9aee493…
|
drh
|
1890 |
sqlite3_int64 i; |
|
9aee493…
|
drh
|
1891 |
while( 1/*exit-by-break*/ ){ |
|
9aee493…
|
drh
|
1892 |
int *awNew = qrfValidLayout(pData, p, nColNext, p->spec.nScreenWidth); |
|
9aee493…
|
drh
|
1893 |
if( awNew==0 ) break; |
|
9aee493…
|
drh
|
1894 |
sqlite3_free(aw); |
|
9aee493…
|
drh
|
1895 |
aw = awNew; |
|
9aee493…
|
drh
|
1896 |
nCol = nColNext; |
|
9aee493…
|
drh
|
1897 |
nRow = (pData->n + nCol - 1)/nCol; |
|
9aee493…
|
drh
|
1898 |
if( nRow==1 ) break; |
|
9aee493…
|
drh
|
1899 |
nColNext++; |
|
9aee493…
|
drh
|
1900 |
while( (pData->n + nColNext - 1)/nColNext == nRow ) nColNext++; |
|
9aee493…
|
drh
|
1901 |
} |
|
9aee493…
|
drh
|
1902 |
if( nCol==1 ){ |
|
9aee493…
|
drh
|
1903 |
sqlite3_free(aw); |
|
9aee493…
|
drh
|
1904 |
return; /* Cannot do better than 1 column */ |
|
9aee493…
|
drh
|
1905 |
} |
|
9aee493…
|
drh
|
1906 |
az = sqlite3_malloc64( nRow*nCol*sizeof(char*) ); |
|
9aee493…
|
drh
|
1907 |
if( az==0 ){ |
|
9aee493…
|
drh
|
1908 |
qrfOom(p); |
|
9aee493…
|
drh
|
1909 |
return; |
|
9aee493…
|
drh
|
1910 |
} |
|
9aee493…
|
drh
|
1911 |
aiWth = sqlite3_malloc64( nRow*nCol*sizeof(int) ); |
|
9aee493…
|
drh
|
1912 |
if( aiWth==0 ){ |
|
9aee493…
|
drh
|
1913 |
sqlite3_free(az); |
|
9aee493…
|
drh
|
1914 |
qrfOom(p); |
|
9aee493…
|
drh
|
1915 |
return; |
|
9aee493…
|
drh
|
1916 |
} |
|
9aee493…
|
drh
|
1917 |
a = sqlite3_malloc64( nCol*sizeof(struct qrfPerCol) ); |
|
9aee493…
|
drh
|
1918 |
if( a==0 ){ |
|
9aee493…
|
drh
|
1919 |
sqlite3_free(az); |
|
9aee493…
|
drh
|
1920 |
sqlite3_free(aiWth); |
|
9aee493…
|
drh
|
1921 |
qrfOom(p); |
|
9aee493…
|
drh
|
1922 |
return; |
|
9aee493…
|
drh
|
1923 |
} |
|
f4b3b59…
|
drh
|
1924 |
abNum = sqlite3_malloc64( nRow*nCol ); |
|
f4b3b59…
|
drh
|
1925 |
if( abNum==0 ){ |
|
f4b3b59…
|
drh
|
1926 |
sqlite3_free(az); |
|
f4b3b59…
|
drh
|
1927 |
sqlite3_free(aiWth); |
|
f4b3b59…
|
drh
|
1928 |
sqlite3_free(a); |
|
f4b3b59…
|
drh
|
1929 |
qrfOom(p); |
|
f4b3b59…
|
drh
|
1930 |
return; |
|
f4b3b59…
|
drh
|
1931 |
} |
|
9aee493…
|
drh
|
1932 |
for(i=0; i<pData->n; i++){ |
|
9aee493…
|
drh
|
1933 |
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow); |
|
9aee493…
|
drh
|
1934 |
az[j] = pData->az[i]; |
|
f4b3b59…
|
drh
|
1935 |
abNum[j]= pData->abNum[i]; |
|
9aee493…
|
drh
|
1936 |
pData->az[i] = 0; |
|
9aee493…
|
drh
|
1937 |
aiWth[j] = pData->aiWth[i]; |
|
9aee493…
|
drh
|
1938 |
} |
|
9aee493…
|
drh
|
1939 |
while( i<nRow*nCol ){ |
|
9aee493…
|
drh
|
1940 |
sqlite3_int64 j = (i%nRow)*nCol + (i/nRow); |
|
9aee493…
|
drh
|
1941 |
az[j] = sqlite3_mprintf(""); |
|
9aee493…
|
drh
|
1942 |
if( az[j]==0 ) qrfOom(p); |
|
9aee493…
|
drh
|
1943 |
aiWth[j] = 0; |
|
f4b3b59…
|
drh
|
1944 |
abNum[j] = 0; |
|
9aee493…
|
drh
|
1945 |
i++; |
|
9aee493…
|
drh
|
1946 |
} |
|
9aee493…
|
drh
|
1947 |
for(i=0; i<nCol; i++){ |
|
9aee493…
|
drh
|
1948 |
a[i].fx = a[i].mxW = a[i].w = aw[i]; |
|
9aee493…
|
drh
|
1949 |
a[i].e = pData->a[0].e; |
|
9aee493…
|
drh
|
1950 |
} |
|
9aee493…
|
drh
|
1951 |
sqlite3_free(pData->az); |
|
9aee493…
|
drh
|
1952 |
sqlite3_free(pData->aiWth); |
|
9aee493…
|
drh
|
1953 |
sqlite3_free(pData->a); |
|
f4b3b59…
|
drh
|
1954 |
sqlite3_free(pData->abNum); |
|
9aee493…
|
drh
|
1955 |
sqlite3_free(aw); |
|
9aee493…
|
drh
|
1956 |
pData->az = az; |
|
9aee493…
|
drh
|
1957 |
pData->aiWth = aiWth; |
|
9aee493…
|
drh
|
1958 |
pData->a = a; |
|
f4b3b59…
|
drh
|
1959 |
pData->abNum = abNum; |
|
9aee493…
|
drh
|
1960 |
pData->nCol = nCol; |
|
9aee493…
|
drh
|
1961 |
pData->n = pData->nAlloc = nRow*nCol; |
|
9aee493…
|
drh
|
1962 |
for(i=w=0; i<nCol; i++) w += a[i].w; |
|
9aee493…
|
drh
|
1963 |
pData->nMargin = (p->spec.nScreenWidth - w)/(nCol - 1); |
|
9aee493…
|
drh
|
1964 |
if( pData->nMargin>5 ) pData->nMargin = 5; |
|
9aee493…
|
drh
|
1965 |
} |
|
9aee493…
|
drh
|
1966 |
|
|
9aee493…
|
drh
|
1967 |
/* |
|
4f76459…
|
drh
|
1968 |
** Adjust the layout for the screen width restriction |
|
4f76459…
|
drh
|
1969 |
*/ |
|
4f76459…
|
drh
|
1970 |
static void qrfRestrictScreenWidth(qrfColData *pData, Qrf *p){ |
|
4f76459…
|
drh
|
1971 |
int sepW; /* Width of all box separators and margins */ |
|
4f76459…
|
drh
|
1972 |
int sumW; /* Total width of data area over all columns */ |
|
4f76459…
|
drh
|
1973 |
int targetW; /* Desired total data area */ |
|
4f76459…
|
drh
|
1974 |
int i; /* Loop counters */ |
|
4f76459…
|
drh
|
1975 |
int nCol; /* Number of columns */ |
|
4f76459…
|
drh
|
1976 |
|
|
4f76459…
|
drh
|
1977 |
pData->nMargin = 2; /* Default to normal margins */ |
|
4f76459…
|
drh
|
1978 |
if( p->spec.nScreenWidth==0 ) return; |
|
4f76459…
|
drh
|
1979 |
if( p->spec.eStyle==QRF_STYLE_Column ){ |
|
4f76459…
|
drh
|
1980 |
sepW = pData->nCol*2 - 2; |
|
4f76459…
|
drh
|
1981 |
}else{ |
|
4f76459…
|
drh
|
1982 |
sepW = pData->nCol*3 + 1; |
|
f4b3b59…
|
drh
|
1983 |
if( p->spec.bBorder==QRF_No ) sepW -= 2; |
|
4f76459…
|
drh
|
1984 |
} |
|
4f76459…
|
drh
|
1985 |
nCol = pData->nCol; |
|
4f76459…
|
drh
|
1986 |
for(i=sumW=0; i<nCol; i++) sumW += pData->a[i].w; |
|
4f76459…
|
drh
|
1987 |
if( p->spec.nScreenWidth >= sumW+sepW ) return; |
|
4f76459…
|
drh
|
1988 |
|
|
4f76459…
|
drh
|
1989 |
/* First thing to do is reduce the separation between columns */ |
|
4f76459…
|
drh
|
1990 |
pData->nMargin = 0; |
|
4f76459…
|
drh
|
1991 |
if( p->spec.eStyle==QRF_STYLE_Column ){ |
|
4f76459…
|
drh
|
1992 |
sepW = pData->nCol - 1; |
|
4f76459…
|
drh
|
1993 |
}else{ |
|
4f76459…
|
drh
|
1994 |
sepW = pData->nCol + 1; |
|
f4b3b59…
|
drh
|
1995 |
if( p->spec.bBorder==QRF_No ) sepW -= 2; |
|
4f76459…
|
drh
|
1996 |
} |
|
4f76459…
|
drh
|
1997 |
targetW = p->spec.nScreenWidth - sepW; |
|
4f76459…
|
drh
|
1998 |
|
|
4f76459…
|
drh
|
1999 |
#define MIN_SQUOZE 8 |
|
4f76459…
|
drh
|
2000 |
#define MIN_EX_SQUOZE 16 |
|
4f76459…
|
drh
|
2001 |
/* Reduce the width of the widest eligible column. A column is |
|
4f76459…
|
drh
|
2002 |
** eligible for narrowing if: |
|
4f76459…
|
drh
|
2003 |
** |
|
4f76459…
|
drh
|
2004 |
** * It is not a fixed-width column (a[0].fx is false) |
|
4f76459…
|
drh
|
2005 |
** * The current width is more than MIN_SQUOZE |
|
4f76459…
|
drh
|
2006 |
** * Either: |
|
4f76459…
|
drh
|
2007 |
** + The current width is more then MIN_EX_SQUOZE, or |
|
4f76459…
|
drh
|
2008 |
** + The current width is more than half the max width (a[].mxW) |
|
4f76459…
|
drh
|
2009 |
** |
|
4f76459…
|
drh
|
2010 |
** Keep making reductions until either no more reductions are |
|
4f76459…
|
drh
|
2011 |
** possible or until the size target is reached. |
|
4f76459…
|
drh
|
2012 |
*/ |
|
4f76459…
|
drh
|
2013 |
while( sumW > targetW ){ |
|
4f76459…
|
drh
|
2014 |
int gain, w; |
|
4f76459…
|
drh
|
2015 |
int ix = -1; |
|
4f76459…
|
drh
|
2016 |
int mx = 0; |
|
4f76459…
|
drh
|
2017 |
for(i=0; i<nCol; i++){ |
|
4f76459…
|
drh
|
2018 |
if( pData->a[i].fx==0 |
|
4f76459…
|
drh
|
2019 |
&& (w = pData->a[i].w)>mx |
|
4f76459…
|
drh
|
2020 |
&& w>MIN_SQUOZE |
|
4f76459…
|
drh
|
2021 |
&& (w>MIN_EX_SQUOZE || w*2>pData->a[i].mxW) |
|
4f76459…
|
drh
|
2022 |
){ |
|
4f76459…
|
drh
|
2023 |
ix = i; |
|
4f76459…
|
drh
|
2024 |
mx = w; |
|
4f76459…
|
drh
|
2025 |
} |
|
4f76459…
|
drh
|
2026 |
} |
|
4f76459…
|
drh
|
2027 |
if( ix<0 ) break; |
|
4f76459…
|
drh
|
2028 |
if( mx>=MIN_SQUOZE*2 ){ |
|
4f76459…
|
drh
|
2029 |
gain = mx/2; |
|
4f76459…
|
drh
|
2030 |
}else{ |
|
4f76459…
|
drh
|
2031 |
gain = mx - MIN_SQUOZE; |
|
4f76459…
|
drh
|
2032 |
} |
|
4f76459…
|
drh
|
2033 |
if( sumW - gain < targetW ){ |
|
4f76459…
|
drh
|
2034 |
gain = sumW - targetW; |
|
4f76459…
|
drh
|
2035 |
} |
|
4f76459…
|
drh
|
2036 |
sumW -= gain; |
|
4f76459…
|
drh
|
2037 |
pData->a[ix].w -= gain; |
|
4f76459…
|
drh
|
2038 |
pData->bMultiRow = 1; |
|
4f76459…
|
drh
|
2039 |
} |
|
4f76459…
|
drh
|
2040 |
} |
|
4f76459…
|
drh
|
2041 |
|
|
4f76459…
|
drh
|
2042 |
/* |
|
4f76459…
|
drh
|
2043 |
** Columnar modes require that the entire query be evaluated first, with |
|
4f76459…
|
drh
|
2044 |
** results written into memory, so that we can compute appropriate column |
|
4f76459…
|
drh
|
2045 |
** widths. |
|
4f76459…
|
drh
|
2046 |
*/ |
|
4f76459…
|
drh
|
2047 |
static void qrfColumnar(Qrf *p){ |
|
4f76459…
|
drh
|
2048 |
sqlite3_int64 i, j; /* Loop counters */ |
|
4f76459…
|
drh
|
2049 |
const char *colSep = 0; /* Column separator text */ |
|
4f76459…
|
drh
|
2050 |
const char *rowSep = 0; /* Row terminator text */ |
|
4f76459…
|
drh
|
2051 |
const char *rowStart = 0; /* Row start text */ |
|
4f76459…
|
drh
|
2052 |
int szColSep, szRowSep, szRowStart; /* Size in bytes of previous 3 */ |
|
4f76459…
|
drh
|
2053 |
int rc; /* Result code */ |
|
4f76459…
|
drh
|
2054 |
int nColumn = p->nCol; /* Number of columns */ |
|
4f76459…
|
drh
|
2055 |
int bWW; /* True to do word-wrap */ |
|
4f76459…
|
drh
|
2056 |
sqlite3_str *pStr; /* Temporary rendering */ |
|
4f76459…
|
drh
|
2057 |
qrfColData data; /* Columnar layout data */ |
|
9aee493…
|
drh
|
2058 |
int bRTrim; /* Trim trailing space */ |
|
4f76459…
|
drh
|
2059 |
|
|
4f76459…
|
drh
|
2060 |
rc = sqlite3_step(p->pStmt); |
|
4f76459…
|
drh
|
2061 |
if( rc!=SQLITE_ROW || nColumn==0 ){ |
|
4f76459…
|
drh
|
2062 |
return; /* No output */ |
|
4f76459…
|
drh
|
2063 |
} |
|
4f76459…
|
drh
|
2064 |
|
|
4f76459…
|
drh
|
2065 |
/* Initialize the data container */ |
|
4f76459…
|
drh
|
2066 |
memset(&data, 0, sizeof(data)); |
|
4f76459…
|
drh
|
2067 |
data.nCol = p->nCol; |
|
f4b3b59…
|
drh
|
2068 |
data.p = p; |
|
4f76459…
|
drh
|
2069 |
data.a = sqlite3_malloc64( nColumn*sizeof(struct qrfPerCol) ); |
|
4f76459…
|
drh
|
2070 |
if( data.a==0 ){ |
|
4f76459…
|
drh
|
2071 |
qrfOom(p); |
|
4f76459…
|
drh
|
2072 |
return; |
|
4f76459…
|
drh
|
2073 |
} |
|
4f76459…
|
drh
|
2074 |
memset(data.a, 0, nColumn*sizeof(struct qrfPerCol) ); |
|
4f76459…
|
drh
|
2075 |
if( qrfColDataEnlarge(&data) ) return; |
|
4f76459…
|
drh
|
2076 |
assert( data.az!=0 ); |
|
4f76459…
|
drh
|
2077 |
|
|
4f76459…
|
drh
|
2078 |
/* Load the column header names and all cell content into data */ |
|
4f76459…
|
drh
|
2079 |
if( p->spec.bTitles==QRF_Yes ){ |
|
4f76459…
|
drh
|
2080 |
unsigned char saved_eText = p->spec.eText; |
|
4f76459…
|
drh
|
2081 |
p->spec.eText = p->spec.eTitle; |
|
f4b3b59…
|
drh
|
2082 |
memset(data.abNum, 0, nColumn); |
|
4f76459…
|
drh
|
2083 |
for(i=0; i<nColumn; i++){ |
|
4f76459…
|
drh
|
2084 |
const char *z = (const char*)sqlite3_column_name(p->pStmt,i); |
|
4f76459…
|
drh
|
2085 |
int nNL = 0; |
|
4f76459…
|
drh
|
2086 |
int n, w; |
|
4f76459…
|
drh
|
2087 |
pStr = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
2088 |
qrfEncodeText(p, pStr, z ? z : ""); |
|
4f76459…
|
drh
|
2089 |
n = sqlite3_str_length(pStr); |
|
2b2530d…
|
drh
|
2090 |
qrfStrErr(p, pStr); |
|
4f76459…
|
drh
|
2091 |
z = data.az[data.n] = sqlite3_str_finish(pStr); |
|
ae7e3f0…
|
drh
|
2092 |
if( p->spec.nTitleLimit ){ |
|
ae7e3f0…
|
drh
|
2093 |
nNL = 0; |
|
ae7e3f0…
|
drh
|
2094 |
data.aiWth[data.n] = w = qrfTitleLimit(data.az[data.n], |
|
ae7e3f0…
|
drh
|
2095 |
p->spec.nTitleLimit ); |
|
ae7e3f0…
|
drh
|
2096 |
}else{ |
|
ae7e3f0…
|
drh
|
2097 |
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
|
ae7e3f0…
|
drh
|
2098 |
} |
|
4f76459…
|
drh
|
2099 |
data.n++; |
|
4f76459…
|
drh
|
2100 |
if( w>data.a[i].mxW ) data.a[i].mxW = w; |
|
4f76459…
|
drh
|
2101 |
if( nNL ) data.bMultiRow = 1; |
|
4f76459…
|
drh
|
2102 |
} |
|
4f76459…
|
drh
|
2103 |
p->spec.eText = saved_eText; |
|
4f76459…
|
drh
|
2104 |
p->nRow++; |
|
4f76459…
|
drh
|
2105 |
} |
|
4f76459…
|
drh
|
2106 |
do{ |
|
4f76459…
|
drh
|
2107 |
if( data.n+nColumn > data.nAlloc ){ |
|
4f76459…
|
drh
|
2108 |
if( qrfColDataEnlarge(&data) ) return; |
|
4f76459…
|
drh
|
2109 |
} |
|
4f76459…
|
drh
|
2110 |
for(i=0; i<nColumn; i++){ |
|
4f76459…
|
drh
|
2111 |
char *z; |
|
4f76459…
|
drh
|
2112 |
int nNL = 0; |
|
4f76459…
|
drh
|
2113 |
int n, w; |
|
f4b3b59…
|
drh
|
2114 |
int eType = sqlite3_column_type(p->pStmt,i); |
|
4f76459…
|
drh
|
2115 |
pStr = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
2116 |
qrfRenderValue(p, pStr, i); |
|
4f76459…
|
drh
|
2117 |
n = sqlite3_str_length(pStr); |
|
2b2530d…
|
drh
|
2118 |
qrfStrErr(p, pStr); |
|
4f76459…
|
drh
|
2119 |
z = data.az[data.n] = sqlite3_str_finish(pStr); |
|
f4b3b59…
|
drh
|
2120 |
data.abNum[data.n] = eType==SQLITE_INTEGER || eType==SQLITE_FLOAT; |
|
4f76459…
|
drh
|
2121 |
data.aiWth[data.n] = w = qrfDisplayWidth(z, n, &nNL); |
|
4f76459…
|
drh
|
2122 |
data.n++; |
|
4f76459…
|
drh
|
2123 |
if( w>data.a[i].mxW ) data.a[i].mxW = w; |
|
4f76459…
|
drh
|
2124 |
if( nNL ) data.bMultiRow = 1; |
|
4f76459…
|
drh
|
2125 |
} |
|
4f76459…
|
drh
|
2126 |
p->nRow++; |
|
4f76459…
|
drh
|
2127 |
}while( sqlite3_step(p->pStmt)==SQLITE_ROW && p->iErr==SQLITE_OK ); |
|
4f76459…
|
drh
|
2128 |
if( p->iErr ){ |
|
4f76459…
|
drh
|
2129 |
qrfColDataFree(&data); |
|
4f76459…
|
drh
|
2130 |
return; |
|
4f76459…
|
drh
|
2131 |
} |
|
4f76459…
|
drh
|
2132 |
|
|
4f76459…
|
drh
|
2133 |
/* Compute the width and alignment of every column */ |
|
4f76459…
|
drh
|
2134 |
if( p->spec.bTitles==QRF_No ){ |
|
4f76459…
|
drh
|
2135 |
qrfLoadAlignment(&data, p); |
|
4f76459…
|
drh
|
2136 |
}else{ |
|
4f76459…
|
drh
|
2137 |
unsigned char e; |
|
4f76459…
|
drh
|
2138 |
if( p->spec.eTitleAlign==QRF_Auto ){ |
|
4f76459…
|
drh
|
2139 |
e = QRF_ALIGN_Center; |
|
4f76459…
|
drh
|
2140 |
}else{ |
|
4f76459…
|
drh
|
2141 |
e = p->spec.eTitleAlign; |
|
4f76459…
|
drh
|
2142 |
} |
|
4f76459…
|
drh
|
2143 |
for(i=0; i<nColumn; i++) data.a[i].e = e; |
|
4f76459…
|
drh
|
2144 |
} |
|
4f76459…
|
drh
|
2145 |
|
|
4f76459…
|
drh
|
2146 |
for(i=0; i<nColumn; i++){ |
|
4f76459…
|
drh
|
2147 |
int w = 0; |
|
4f76459…
|
drh
|
2148 |
if( i<p->spec.nWidth ){ |
|
4f76459…
|
drh
|
2149 |
w = p->spec.aWidth[i]; |
|
4f76459…
|
drh
|
2150 |
if( w==(-32768) ){ |
|
4f76459…
|
drh
|
2151 |
w = 0; |
|
4f76459…
|
drh
|
2152 |
if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){ |
|
4f76459…
|
drh
|
2153 |
data.a[i].e |= QRF_ALIGN_Right; |
|
4f76459…
|
drh
|
2154 |
} |
|
4f76459…
|
drh
|
2155 |
}else if( w<0 ){ |
|
4f76459…
|
drh
|
2156 |
w = -w; |
|
4f76459…
|
drh
|
2157 |
if( p->spec.nAlign>i && (p->spec.aAlign[i] & QRF_ALIGN_HMASK)==0 ){ |
|
4f76459…
|
drh
|
2158 |
data.a[i].e |= QRF_ALIGN_Right; |
|
4f76459…
|
drh
|
2159 |
} |
|
4f76459…
|
drh
|
2160 |
} |
|
4f76459…
|
drh
|
2161 |
if( w ) data.a[i].fx = 1; |
|
4f76459…
|
drh
|
2162 |
} |
|
4f76459…
|
drh
|
2163 |
if( w==0 ){ |
|
4f76459…
|
drh
|
2164 |
w = data.a[i].mxW; |
|
4f76459…
|
drh
|
2165 |
if( p->spec.nWrap>0 && w>p->spec.nWrap ){ |
|
4f76459…
|
drh
|
2166 |
w = p->spec.nWrap; |
|
4f76459…
|
drh
|
2167 |
data.bMultiRow = 1; |
|
4f76459…
|
drh
|
2168 |
} |
|
4f76459…
|
drh
|
2169 |
}else if( (data.bMultiRow==0 || w==1) && data.a[i].mxW>w ){ |
|
4f76459…
|
drh
|
2170 |
data.bMultiRow = 1; |
|
4f76459…
|
drh
|
2171 |
if( w==1 ){ |
|
4f76459…
|
drh
|
2172 |
/* If aiWth[j] is 2 or more, then there might be a double-wide |
|
4f76459…
|
drh
|
2173 |
** character somewhere. So make the column width at least 2. */ |
|
4f76459…
|
drh
|
2174 |
w = 2; |
|
4f76459…
|
drh
|
2175 |
} |
|
4f76459…
|
drh
|
2176 |
} |
|
4f76459…
|
drh
|
2177 |
data.a[i].w = w; |
|
4f76459…
|
drh
|
2178 |
} |
|
4f76459…
|
drh
|
2179 |
|
|
9aee493…
|
drh
|
2180 |
if( nColumn==1 |
|
9aee493…
|
drh
|
2181 |
&& data.n>1 |
|
9aee493…
|
drh
|
2182 |
&& p->spec.bSplitColumn==QRF_Yes |
|
9aee493…
|
drh
|
2183 |
&& p->spec.eStyle==QRF_STYLE_Column |
|
9aee493…
|
drh
|
2184 |
&& p->spec.bTitles==QRF_No |
|
9aee493…
|
drh
|
2185 |
&& p->spec.nScreenWidth>data.a[0].w+3 |
|
9aee493…
|
drh
|
2186 |
){ |
|
9aee493…
|
drh
|
2187 |
/* Attempt to convert single-column tables into multi-column by |
|
9aee493…
|
drh
|
2188 |
** verticle wrapping, if the screen is wide enough and if the |
|
9aee493…
|
drh
|
2189 |
** bSplitColumn flag is set. */ |
|
9aee493…
|
drh
|
2190 |
qrfSplitColumn(&data, p); |
|
9aee493…
|
drh
|
2191 |
nColumn = data.nCol; |
|
9aee493…
|
drh
|
2192 |
}else{ |
|
9aee493…
|
drh
|
2193 |
/* Adjust the column widths due to screen width restrictions */ |
|
9aee493…
|
drh
|
2194 |
qrfRestrictScreenWidth(&data, p); |
|
9aee493…
|
drh
|
2195 |
} |
|
4f76459…
|
drh
|
2196 |
|
|
4f76459…
|
drh
|
2197 |
/* Draw the line across the top of the table. Also initialize |
|
4f76459…
|
drh
|
2198 |
** the row boundary and column separator texts. */ |
|
4f76459…
|
drh
|
2199 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2200 |
case QRF_STYLE_Box: |
|
4f76459…
|
drh
|
2201 |
if( data.nMargin ){ |
|
4f76459…
|
drh
|
2202 |
rowStart = BOX_13 " "; |
|
4f76459…
|
drh
|
2203 |
colSep = " " BOX_13 " "; |
|
4f76459…
|
drh
|
2204 |
rowSep = " " BOX_13 "\n"; |
|
4f76459…
|
drh
|
2205 |
}else{ |
|
4f76459…
|
drh
|
2206 |
rowStart = BOX_13; |
|
4f76459…
|
drh
|
2207 |
colSep = BOX_13; |
|
4f76459…
|
drh
|
2208 |
rowSep = BOX_13 "\n"; |
|
4f76459…
|
drh
|
2209 |
} |
|
f4b3b59…
|
drh
|
2210 |
if( p->spec.bBorder==QRF_No){ |
|
f4b3b59…
|
drh
|
2211 |
rowStart += 3; |
|
f4b3b59…
|
drh
|
2212 |
rowSep = "\n"; |
|
f4b3b59…
|
drh
|
2213 |
}else{ |
|
709b566…
|
drh
|
2214 |
qrfBoxSeparator(p->pOut, &data, BOX_R23, BOX_234, BOX_R34, 0); |
|
f4b3b59…
|
drh
|
2215 |
} |
|
4f76459…
|
drh
|
2216 |
break; |
|
4f76459…
|
drh
|
2217 |
case QRF_STYLE_Table: |
|
4f76459…
|
drh
|
2218 |
if( data.nMargin ){ |
|
4f76459…
|
drh
|
2219 |
rowStart = "| "; |
|
4f76459…
|
drh
|
2220 |
colSep = " | "; |
|
4f76459…
|
drh
|
2221 |
rowSep = " |\n"; |
|
4f76459…
|
drh
|
2222 |
}else{ |
|
4f76459…
|
drh
|
2223 |
rowStart = "|"; |
|
4f76459…
|
drh
|
2224 |
colSep = "|"; |
|
4f76459…
|
drh
|
2225 |
rowSep = "|\n"; |
|
4f76459…
|
drh
|
2226 |
} |
|
f4b3b59…
|
drh
|
2227 |
if( p->spec.bBorder==QRF_No ){ |
|
f4b3b59…
|
drh
|
2228 |
rowStart += 1; |
|
f4b3b59…
|
drh
|
2229 |
rowSep = "\n"; |
|
f4b3b59…
|
drh
|
2230 |
}else{ |
|
f4b3b59…
|
drh
|
2231 |
qrfRowSeparator(p->pOut, &data, '+'); |
|
f4b3b59…
|
drh
|
2232 |
} |
|
4f76459…
|
drh
|
2233 |
break; |
|
45de97f…
|
drh
|
2234 |
case QRF_STYLE_Column: { |
|
45de97f…
|
drh
|
2235 |
static const char zSpace[] = " "; |
|
4f76459…
|
drh
|
2236 |
rowStart = ""; |
|
9aee493…
|
drh
|
2237 |
if( data.nMargin<2 ){ |
|
9aee493…
|
drh
|
2238 |
colSep = " "; |
|
9aee493…
|
drh
|
2239 |
}else if( data.nMargin<=5 ){ |
|
45de97f…
|
drh
|
2240 |
colSep = &zSpace[5-data.nMargin]; |
|
9aee493…
|
drh
|
2241 |
}else{ |
|
45de97f…
|
drh
|
2242 |
colSep = zSpace; |
|
9aee493…
|
drh
|
2243 |
} |
|
4f76459…
|
drh
|
2244 |
rowSep = "\n"; |
|
4f76459…
|
drh
|
2245 |
break; |
|
45de97f…
|
drh
|
2246 |
} |
|
4f76459…
|
drh
|
2247 |
default: /*case QRF_STYLE_Markdown:*/ |
|
4f76459…
|
drh
|
2248 |
if( data.nMargin ){ |
|
4f76459…
|
drh
|
2249 |
rowStart = "| "; |
|
4f76459…
|
drh
|
2250 |
colSep = " | "; |
|
4f76459…
|
drh
|
2251 |
rowSep = " |\n"; |
|
4f76459…
|
drh
|
2252 |
}else{ |
|
4f76459…
|
drh
|
2253 |
rowStart = "|"; |
|
4f76459…
|
drh
|
2254 |
colSep = "|"; |
|
4f76459…
|
drh
|
2255 |
rowSep = "|\n"; |
|
4f76459…
|
drh
|
2256 |
} |
|
4f76459…
|
drh
|
2257 |
break; |
|
4f76459…
|
drh
|
2258 |
} |
|
4f76459…
|
drh
|
2259 |
szRowStart = (int)strlen(rowStart); |
|
4f76459…
|
drh
|
2260 |
szRowSep = (int)strlen(rowSep); |
|
4f76459…
|
drh
|
2261 |
szColSep = (int)strlen(colSep); |
|
4f76459…
|
drh
|
2262 |
|
|
4f76459…
|
drh
|
2263 |
bWW = (p->spec.bWordWrap==QRF_Yes && data.bMultiRow); |
|
f4b3b59…
|
drh
|
2264 |
if( p->spec.eStyle==QRF_STYLE_Column |
|
f4b3b59…
|
drh
|
2265 |
|| (p->spec.bBorder==QRF_No |
|
f4b3b59…
|
drh
|
2266 |
&& (p->spec.eStyle==QRF_STYLE_Box || p->spec.eStyle==QRF_STYLE_Table) |
|
f4b3b59…
|
drh
|
2267 |
) |
|
f4b3b59…
|
drh
|
2268 |
){ |
|
f4b3b59…
|
drh
|
2269 |
bRTrim = 1; |
|
f4b3b59…
|
drh
|
2270 |
}else{ |
|
f4b3b59…
|
drh
|
2271 |
bRTrim = 0; |
|
f4b3b59…
|
drh
|
2272 |
} |
|
2b2530d…
|
drh
|
2273 |
for(i=0; i<data.n && sqlite3_str_errcode(p->pOut)==SQLITE_OK; i+=nColumn){ |
|
4f76459…
|
drh
|
2274 |
int bMore; |
|
4f76459…
|
drh
|
2275 |
int nRow = 0; |
|
4f76459…
|
drh
|
2276 |
|
|
4f76459…
|
drh
|
2277 |
/* Draw a single row of the table. This might be the title line |
|
4f76459…
|
drh
|
2278 |
** (if there is a title line) or a row in the body of the table. |
|
4f76459…
|
drh
|
2279 |
** The column number will be j. The row number is i/nColumn. |
|
4f76459…
|
drh
|
2280 |
*/ |
|
f4b3b59…
|
drh
|
2281 |
for(j=0; j<nColumn; j++){ |
|
f4b3b59…
|
drh
|
2282 |
data.a[j].z = data.az[i+j]; |
|
d326547…
|
drh
|
2283 |
if( data.a[j].z==0 ) data.a[j].z = ""; |
|
f4b3b59…
|
drh
|
2284 |
data.a[j].bNum = data.abNum[i+j]; |
|
f4b3b59…
|
drh
|
2285 |
} |
|
4f76459…
|
drh
|
2286 |
do{ |
|
4f76459…
|
drh
|
2287 |
sqlite3_str_append(p->pOut, rowStart, szRowStart); |
|
4f76459…
|
drh
|
2288 |
bMore = 0; |
|
4f76459…
|
drh
|
2289 |
for(j=0; j<nColumn; j++){ |
|
4f76459…
|
drh
|
2290 |
int nThis = 0; |
|
4f76459…
|
drh
|
2291 |
int nWide = 0; |
|
4f76459…
|
drh
|
2292 |
int iNext = 0; |
|
4f76459…
|
drh
|
2293 |
int nWS; |
|
4f76459…
|
drh
|
2294 |
qrfWrapLine(data.a[j].z, data.a[j].w, bWW, &nThis, &nWide, &iNext); |
|
4f76459…
|
drh
|
2295 |
nWS = data.a[j].w - nWide; |
|
f4b3b59…
|
drh
|
2296 |
qrfPrintAligned(p->pOut, &data.a[j], nThis, nWS); |
|
4f76459…
|
drh
|
2297 |
data.a[j].z += iNext; |
|
9aee493…
|
drh
|
2298 |
if( data.a[j].z[0]!=0 ){ |
|
9aee493…
|
drh
|
2299 |
bMore = 1; |
|
9aee493…
|
drh
|
2300 |
} |
|
4f76459…
|
drh
|
2301 |
if( j<nColumn-1 ){ |
|
4f76459…
|
drh
|
2302 |
sqlite3_str_append(p->pOut, colSep, szColSep); |
|
4f76459…
|
drh
|
2303 |
}else{ |
|
9aee493…
|
drh
|
2304 |
if( bRTrim ) qrfRTrim(p->pOut); |
|
4f76459…
|
drh
|
2305 |
sqlite3_str_append(p->pOut, rowSep, szRowSep); |
|
4f76459…
|
drh
|
2306 |
} |
|
4f76459…
|
drh
|
2307 |
} |
|
4f76459…
|
drh
|
2308 |
}while( bMore && ++nRow < p->mxHeight ); |
|
4f76459…
|
drh
|
2309 |
if( bMore ){ |
|
4f76459…
|
drh
|
2310 |
/* This row was terminated by nLineLimit. Show ellipsis. */ |
|
4f76459…
|
drh
|
2311 |
sqlite3_str_append(p->pOut, rowStart, szRowStart); |
|
4f76459…
|
drh
|
2312 |
for(j=0; j<nColumn; j++){ |
|
4f76459…
|
drh
|
2313 |
if( data.a[j].z[0]==0 ){ |
|
4f76459…
|
drh
|
2314 |
sqlite3_str_appendchar(p->pOut, data.a[j].w, ' '); |
|
4f76459…
|
drh
|
2315 |
}else{ |
|
4f76459…
|
drh
|
2316 |
int nE = 3; |
|
4f76459…
|
drh
|
2317 |
if( nE>data.a[j].w ) nE = data.a[j].w; |
|
f4b3b59…
|
drh
|
2318 |
data.a[j].z = "..."; |
|
f4b3b59…
|
drh
|
2319 |
qrfPrintAligned(p->pOut, &data.a[j], nE, data.a[j].w-nE); |
|
4f76459…
|
drh
|
2320 |
} |
|
4f76459…
|
drh
|
2321 |
if( j<nColumn-1 ){ |
|
4f76459…
|
drh
|
2322 |
sqlite3_str_append(p->pOut, colSep, szColSep); |
|
4f76459…
|
drh
|
2323 |
}else{ |
|
9aee493…
|
drh
|
2324 |
if( bRTrim ) qrfRTrim(p->pOut); |
|
4f76459…
|
drh
|
2325 |
sqlite3_str_append(p->pOut, rowSep, szRowSep); |
|
4f76459…
|
drh
|
2326 |
} |
|
4f76459…
|
drh
|
2327 |
} |
|
4f76459…
|
drh
|
2328 |
} |
|
4f76459…
|
drh
|
2329 |
|
|
4f76459…
|
drh
|
2330 |
/* Draw either (1) the separator between the title line and the body |
|
4f76459…
|
drh
|
2331 |
** of the table, or (2) separators between individual rows of the table |
|
4f76459…
|
drh
|
2332 |
** body. isTitleDataSeparator will be true if we are doing (1). |
|
4f76459…
|
drh
|
2333 |
*/ |
|
4f76459…
|
drh
|
2334 |
if( (i==0 || data.bMultiRow) && i+nColumn<data.n ){ |
|
4f76459…
|
drh
|
2335 |
int isTitleDataSeparator = (i==0 && p->spec.bTitles==QRF_Yes); |
|
4f76459…
|
drh
|
2336 |
if( isTitleDataSeparator ){ |
|
4f76459…
|
drh
|
2337 |
qrfLoadAlignment(&data, p); |
|
4f76459…
|
drh
|
2338 |
} |
|
4f76459…
|
drh
|
2339 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2340 |
case QRF_STYLE_Table: { |
|
4f76459…
|
drh
|
2341 |
if( isTitleDataSeparator || data.bMultiRow ){ |
|
4f76459…
|
drh
|
2342 |
qrfRowSeparator(p->pOut, &data, '+'); |
|
4f76459…
|
drh
|
2343 |
} |
|
4f76459…
|
drh
|
2344 |
break; |
|
4f76459…
|
drh
|
2345 |
} |
|
4f76459…
|
drh
|
2346 |
case QRF_STYLE_Box: { |
|
709b566…
|
drh
|
2347 |
if( isTitleDataSeparator ){ |
|
709b566…
|
drh
|
2348 |
qrfBoxSeparator(p->pOut, &data, DBL_123, DBL_1234, DBL_134, 1); |
|
709b566…
|
drh
|
2349 |
}else if( data.bMultiRow ){ |
|
709b566…
|
drh
|
2350 |
qrfBoxSeparator(p->pOut, &data, BOX_123, BOX_1234, BOX_134, 0); |
|
4f76459…
|
drh
|
2351 |
} |
|
4f76459…
|
drh
|
2352 |
break; |
|
4f76459…
|
drh
|
2353 |
} |
|
4f76459…
|
drh
|
2354 |
case QRF_STYLE_Markdown: { |
|
4f76459…
|
drh
|
2355 |
if( isTitleDataSeparator ){ |
|
4f76459…
|
drh
|
2356 |
qrfRowSeparator(p->pOut, &data, '|'); |
|
4f76459…
|
drh
|
2357 |
} |
|
4f76459…
|
drh
|
2358 |
break; |
|
4f76459…
|
drh
|
2359 |
} |
|
4f76459…
|
drh
|
2360 |
case QRF_STYLE_Column: { |
|
4f76459…
|
drh
|
2361 |
if( isTitleDataSeparator ){ |
|
4f76459…
|
drh
|
2362 |
for(j=0; j<nColumn; j++){ |
|
4f76459…
|
drh
|
2363 |
sqlite3_str_appendchar(p->pOut, data.a[j].w, '-'); |
|
4f76459…
|
drh
|
2364 |
if( j<nColumn-1 ){ |
|
4f76459…
|
drh
|
2365 |
sqlite3_str_append(p->pOut, colSep, szColSep); |
|
4f76459…
|
drh
|
2366 |
}else{ |
|
9aee493…
|
drh
|
2367 |
qrfRTrim(p->pOut); |
|
4f76459…
|
drh
|
2368 |
sqlite3_str_append(p->pOut, rowSep, szRowSep); |
|
4f76459…
|
drh
|
2369 |
} |
|
4f76459…
|
drh
|
2370 |
} |
|
4f76459…
|
drh
|
2371 |
}else if( data.bMultiRow ){ |
|
9aee493…
|
drh
|
2372 |
qrfRTrim(p->pOut); |
|
4f76459…
|
drh
|
2373 |
sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2374 |
} |
|
4f76459…
|
drh
|
2375 |
break; |
|
4f76459…
|
drh
|
2376 |
} |
|
4f76459…
|
drh
|
2377 |
} |
|
4f76459…
|
drh
|
2378 |
} |
|
4f76459…
|
drh
|
2379 |
} |
|
4f76459…
|
drh
|
2380 |
|
|
4f76459…
|
drh
|
2381 |
/* Draw the line across the bottom of the table */ |
|
f4b3b59…
|
drh
|
2382 |
if( p->spec.bBorder!=QRF_No ){ |
|
f4b3b59…
|
drh
|
2383 |
switch( p->spec.eStyle ){ |
|
f4b3b59…
|
drh
|
2384 |
case QRF_STYLE_Box: |
|
709b566…
|
drh
|
2385 |
qrfBoxSeparator(p->pOut, &data, BOX_R12, BOX_124, BOX_R14, 0); |
|
f4b3b59…
|
drh
|
2386 |
break; |
|
f4b3b59…
|
drh
|
2387 |
case QRF_STYLE_Table: |
|
f4b3b59…
|
drh
|
2388 |
qrfRowSeparator(p->pOut, &data, '+'); |
|
f4b3b59…
|
drh
|
2389 |
break; |
|
f4b3b59…
|
drh
|
2390 |
} |
|
4f76459…
|
drh
|
2391 |
} |
|
4f76459…
|
drh
|
2392 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2393 |
|
|
4f76459…
|
drh
|
2394 |
qrfColDataFree(&data); |
|
4f76459…
|
drh
|
2395 |
return; |
|
4f76459…
|
drh
|
2396 |
} |
|
4f76459…
|
drh
|
2397 |
|
|
4f76459…
|
drh
|
2398 |
/* |
|
4f76459…
|
drh
|
2399 |
** Parameter azArray points to a zero-terminated array of strings. zStr |
|
4f76459…
|
drh
|
2400 |
** points to a single nul-terminated string. Return non-zero if zStr |
|
4f76459…
|
drh
|
2401 |
** is equal, according to strcmp(), to any of the strings in the array. |
|
4f76459…
|
drh
|
2402 |
** Otherwise, return zero. |
|
4f76459…
|
drh
|
2403 |
*/ |
|
4f76459…
|
drh
|
2404 |
static int qrfStringInArray(const char *zStr, const char **azArray){ |
|
4f76459…
|
drh
|
2405 |
int i; |
|
4f76459…
|
drh
|
2406 |
if( zStr==0 ) return 0; |
|
4f76459…
|
drh
|
2407 |
for(i=0; azArray[i]; i++){ |
|
4f76459…
|
drh
|
2408 |
if( 0==strcmp(zStr, azArray[i]) ) return 1; |
|
4f76459…
|
drh
|
2409 |
} |
|
4f76459…
|
drh
|
2410 |
return 0; |
|
4f76459…
|
drh
|
2411 |
} |
|
4f76459…
|
drh
|
2412 |
|
|
4f76459…
|
drh
|
2413 |
/* |
|
4f76459…
|
drh
|
2414 |
** Print out an EXPLAIN with indentation. This is a two-pass algorithm. |
|
4f76459…
|
drh
|
2415 |
** |
|
4f76459…
|
drh
|
2416 |
** On the first pass, we compute aiIndent[iOp] which is the amount of |
|
4f76459…
|
drh
|
2417 |
** indentation to apply to the iOp-th opcode. The output actually occurs |
|
4f76459…
|
drh
|
2418 |
** on the second pass. |
|
4f76459…
|
drh
|
2419 |
** |
|
4f76459…
|
drh
|
2420 |
** The indenting rules are: |
|
4f76459…
|
drh
|
2421 |
** |
|
4f76459…
|
drh
|
2422 |
** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent |
|
4f76459…
|
drh
|
2423 |
** all opcodes that occur between the p2 jump destination and the opcode |
|
4f76459…
|
drh
|
2424 |
** itself by 2 spaces. |
|
4f76459…
|
drh
|
2425 |
** |
|
4f76459…
|
drh
|
2426 |
** * Do the previous for "Return" instructions for when P2 is positive. |
|
4f76459…
|
drh
|
2427 |
** See tag-20220407a in wherecode.c and vdbe.c. |
|
4f76459…
|
drh
|
2428 |
** |
|
4f76459…
|
drh
|
2429 |
** * For each "Goto", if the jump destination is earlier in the program |
|
4f76459…
|
drh
|
2430 |
** and ends on one of: |
|
4f76459…
|
drh
|
2431 |
** Yield SeekGt SeekLt RowSetRead Rewind |
|
4f76459…
|
drh
|
2432 |
** or if the P1 parameter is one instead of zero, |
|
4f76459…
|
drh
|
2433 |
** then indent all opcodes between the earlier instruction |
|
4f76459…
|
drh
|
2434 |
** and "Goto" by 2 spaces. |
|
4f76459…
|
drh
|
2435 |
*/ |
|
4f76459…
|
drh
|
2436 |
static void qrfExplain(Qrf *p){ |
|
4f76459…
|
drh
|
2437 |
int *abYield = 0; /* abYield[iOp] is rue if opcode iOp is an OP_Yield */ |
|
4f76459…
|
drh
|
2438 |
int *aiIndent = 0; /* Indent the iOp-th opcode by aiIndent[iOp] */ |
|
4f76459…
|
drh
|
2439 |
i64 nAlloc = 0; /* Allocated size of aiIndent[], abYield */ |
|
4f76459…
|
drh
|
2440 |
int nIndent = 0; /* Number of entries in aiIndent[] */ |
|
4f76459…
|
drh
|
2441 |
int iOp; /* Opcode number */ |
|
4f76459…
|
drh
|
2442 |
int i; /* Column loop counter */ |
|
4f76459…
|
drh
|
2443 |
|
|
4f76459…
|
drh
|
2444 |
const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext", |
|
4f76459…
|
drh
|
2445 |
"Return", 0 }; |
|
4f76459…
|
drh
|
2446 |
const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", |
|
4f76459…
|
drh
|
2447 |
"Rewind", 0 }; |
|
4f76459…
|
drh
|
2448 |
const char *azGoto[] = { "Goto", 0 }; |
|
4f76459…
|
drh
|
2449 |
|
|
4f76459…
|
drh
|
2450 |
/* The caller guarantees that the leftmost 4 columns of the statement |
|
4f76459…
|
drh
|
2451 |
** passed to this function are equivalent to the leftmost 4 columns |
|
4f76459…
|
drh
|
2452 |
** of EXPLAIN statement output. In practice the statement may be |
|
4f76459…
|
drh
|
2453 |
** an EXPLAIN, or it may be a query on the bytecode() virtual table. */ |
|
4f76459…
|
drh
|
2454 |
assert( sqlite3_column_count(p->pStmt)>=4 ); |
|
4f76459…
|
drh
|
2455 |
assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 0), "addr" ) ); |
|
4f76459…
|
drh
|
2456 |
assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 1), "opcode" ) ); |
|
4f76459…
|
drh
|
2457 |
assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 2), "p1" ) ); |
|
4f76459…
|
drh
|
2458 |
assert( 0==sqlite3_stricmp( sqlite3_column_name(p->pStmt, 3), "p2" ) ); |
|
4f76459…
|
drh
|
2459 |
|
|
2b2530d…
|
drh
|
2460 |
for(iOp=0; SQLITE_ROW==sqlite3_step(p->pStmt) && !p->iErr; iOp++){ |
|
4f76459…
|
drh
|
2461 |
int iAddr = sqlite3_column_int(p->pStmt, 0); |
|
4f76459…
|
drh
|
2462 |
const char *zOp = (const char*)sqlite3_column_text(p->pStmt, 1); |
|
4f76459…
|
drh
|
2463 |
int p1 = sqlite3_column_int(p->pStmt, 2); |
|
4f76459…
|
drh
|
2464 |
int p2 = sqlite3_column_int(p->pStmt, 3); |
|
4f76459…
|
drh
|
2465 |
|
|
4f76459…
|
drh
|
2466 |
/* Assuming that p2 is an instruction address, set variable p2op to the |
|
4f76459…
|
drh
|
2467 |
** index of that instruction in the aiIndent[] array. p2 and p2op may be |
|
4f76459…
|
drh
|
2468 |
** different if the current instruction is part of a sub-program generated |
|
4f76459…
|
drh
|
2469 |
** by an SQL trigger or foreign key. */ |
|
4f76459…
|
drh
|
2470 |
int p2op = (p2 + (iOp-iAddr)); |
|
4f76459…
|
drh
|
2471 |
|
|
4f76459…
|
drh
|
2472 |
/* Grow the aiIndent array as required */ |
|
4f76459…
|
drh
|
2473 |
if( iOp>=nAlloc ){ |
|
4f76459…
|
drh
|
2474 |
nAlloc += 100; |
|
4f76459…
|
drh
|
2475 |
aiIndent = (int*)sqlite3_realloc64(aiIndent, nAlloc*sizeof(int)); |
|
4f76459…
|
drh
|
2476 |
abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int)); |
|
4f76459…
|
drh
|
2477 |
if( aiIndent==0 || abYield==0 ){ |
|
4f76459…
|
drh
|
2478 |
qrfOom(p); |
|
4f76459…
|
drh
|
2479 |
sqlite3_free(aiIndent); |
|
4f76459…
|
drh
|
2480 |
sqlite3_free(abYield); |
|
4f76459…
|
drh
|
2481 |
return; |
|
4f76459…
|
drh
|
2482 |
} |
|
4f76459…
|
drh
|
2483 |
} |
|
4f76459…
|
drh
|
2484 |
|
|
4f76459…
|
drh
|
2485 |
abYield[iOp] = qrfStringInArray(zOp, azYield); |
|
4f76459…
|
drh
|
2486 |
aiIndent[iOp] = 0; |
|
4f76459…
|
drh
|
2487 |
nIndent = iOp+1; |
|
4f76459…
|
drh
|
2488 |
if( qrfStringInArray(zOp, azNext) && p2op>0 ){ |
|
4f76459…
|
drh
|
2489 |
for(i=p2op; i<iOp; i++) aiIndent[i] += 2; |
|
4f76459…
|
drh
|
2490 |
} |
|
4f76459…
|
drh
|
2491 |
if( qrfStringInArray(zOp, azGoto) && p2op<iOp && (abYield[p2op] || p1) ){ |
|
4f76459…
|
drh
|
2492 |
for(i=p2op; i<iOp; i++) aiIndent[i] += 2; |
|
4f76459…
|
drh
|
2493 |
} |
|
4f76459…
|
drh
|
2494 |
} |
|
4f76459…
|
drh
|
2495 |
sqlite3_free(abYield); |
|
4f76459…
|
drh
|
2496 |
|
|
4f76459…
|
drh
|
2497 |
/* Second pass. Actually generate output */ |
|
4f76459…
|
drh
|
2498 |
sqlite3_reset(p->pStmt); |
|
4f76459…
|
drh
|
2499 |
if( p->iErr==SQLITE_OK ){ |
|
4f76459…
|
drh
|
2500 |
static const int aExplainWidth[] = {4, 13, 4, 4, 4, 13, 2, 13}; |
|
4f76459…
|
drh
|
2501 |
static const int aExplainMap[] = {0, 1, 2, 3, 4, 5, 6, 7 }; |
|
4f76459…
|
drh
|
2502 |
static const int aScanExpWidth[] = {4,15, 6, 13, 4, 4, 4, 13, 2, 13}; |
|
4f76459…
|
drh
|
2503 |
static const int aScanExpMap[] = {0, 9, 8, 1, 2, 3, 4, 5, 6, 7 }; |
|
4f76459…
|
drh
|
2504 |
const int *aWidth = aExplainWidth; |
|
4f76459…
|
drh
|
2505 |
const int *aMap = aExplainMap; |
|
4f76459…
|
drh
|
2506 |
int nWidth = sizeof(aExplainWidth)/sizeof(int); |
|
4f76459…
|
drh
|
2507 |
int iIndent = 1; |
|
4f76459…
|
drh
|
2508 |
int nArg = p->nCol; |
|
4f76459…
|
drh
|
2509 |
if( p->spec.eStyle==QRF_STYLE_StatsVm ){ |
|
4f76459…
|
drh
|
2510 |
aWidth = aScanExpWidth; |
|
4f76459…
|
drh
|
2511 |
aMap = aScanExpMap; |
|
4f76459…
|
drh
|
2512 |
nWidth = sizeof(aScanExpWidth)/sizeof(int); |
|
4f76459…
|
drh
|
2513 |
iIndent = 3; |
|
4f76459…
|
drh
|
2514 |
} |
|
4f76459…
|
drh
|
2515 |
if( nArg>nWidth ) nArg = nWidth; |
|
4f76459…
|
drh
|
2516 |
|
|
2b2530d…
|
drh
|
2517 |
for(iOp=0; sqlite3_step(p->pStmt)==SQLITE_ROW && !p->iErr; iOp++){ |
|
4f76459…
|
drh
|
2518 |
/* If this is the first row seen, print out the headers */ |
|
4f76459…
|
drh
|
2519 |
if( iOp==0 ){ |
|
4f76459…
|
drh
|
2520 |
for(i=0; i<nArg; i++){ |
|
4f76459…
|
drh
|
2521 |
const char *zCol = sqlite3_column_name(p->pStmt, aMap[i]); |
|
4f76459…
|
drh
|
2522 |
qrfWidthPrint(p,p->pOut, aWidth[i], zCol); |
|
4f76459…
|
drh
|
2523 |
if( i==nArg-1 ){ |
|
4f76459…
|
drh
|
2524 |
sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2525 |
}else{ |
|
4f76459…
|
drh
|
2526 |
sqlite3_str_append(p->pOut, " ", 2); |
|
4f76459…
|
drh
|
2527 |
} |
|
4f76459…
|
drh
|
2528 |
} |
|
4f76459…
|
drh
|
2529 |
for(i=0; i<nArg; i++){ |
|
4f76459…
|
drh
|
2530 |
sqlite3_str_appendf(p->pOut, "%.*c", aWidth[i], '-'); |
|
4f76459…
|
drh
|
2531 |
if( i==nArg-1 ){ |
|
4f76459…
|
drh
|
2532 |
sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2533 |
}else{ |
|
4f76459…
|
drh
|
2534 |
sqlite3_str_append(p->pOut, " ", 2); |
|
4f76459…
|
drh
|
2535 |
} |
|
4f76459…
|
drh
|
2536 |
} |
|
4f76459…
|
drh
|
2537 |
} |
|
4f76459…
|
drh
|
2538 |
|
|
4f76459…
|
drh
|
2539 |
for(i=0; i<nArg; i++){ |
|
4f76459…
|
drh
|
2540 |
const char *zSep = " "; |
|
4f76459…
|
drh
|
2541 |
int w = aWidth[i]; |
|
4f76459…
|
drh
|
2542 |
const char *zVal = (const char*)sqlite3_column_text(p->pStmt, aMap[i]); |
|
4f76459…
|
drh
|
2543 |
int len; |
|
4f76459…
|
drh
|
2544 |
if( i==nArg-1 ) w = 0; |
|
4f76459…
|
drh
|
2545 |
if( zVal==0 ) zVal = ""; |
|
d326547…
|
drh
|
2546 |
len = (int)sqlite3_qrf_wcswidth(zVal); |
|
4f76459…
|
drh
|
2547 |
if( len>w ){ |
|
4f76459…
|
drh
|
2548 |
w = len; |
|
4f76459…
|
drh
|
2549 |
zSep = " "; |
|
4f76459…
|
drh
|
2550 |
} |
|
4f76459…
|
drh
|
2551 |
if( i==iIndent && aiIndent && iOp<nIndent ){ |
|
4f76459…
|
drh
|
2552 |
sqlite3_str_appendchar(p->pOut, aiIndent[iOp], ' '); |
|
4f76459…
|
drh
|
2553 |
} |
|
4f76459…
|
drh
|
2554 |
qrfWidthPrint(p, p->pOut, w, zVal); |
|
4f76459…
|
drh
|
2555 |
if( i==nArg-1 ){ |
|
4f76459…
|
drh
|
2556 |
sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2557 |
}else{ |
|
4f76459…
|
drh
|
2558 |
sqlite3_str_appendall(p->pOut, zSep); |
|
4f76459…
|
drh
|
2559 |
} |
|
4f76459…
|
drh
|
2560 |
} |
|
4f76459…
|
drh
|
2561 |
p->nRow++; |
|
4f76459…
|
drh
|
2562 |
} |
|
4f76459…
|
drh
|
2563 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2564 |
} |
|
4f76459…
|
drh
|
2565 |
sqlite3_free(aiIndent); |
|
4f76459…
|
drh
|
2566 |
} |
|
4f76459…
|
drh
|
2567 |
|
|
4f76459…
|
drh
|
2568 |
/* |
|
4f76459…
|
drh
|
2569 |
** Do a "scanstatus vm" style EXPLAIN listing on p->pStmt. |
|
4f76459…
|
drh
|
2570 |
** |
|
4f76459…
|
drh
|
2571 |
** p->pStmt is probably not an EXPLAIN query. Instead, construct a |
|
4f76459…
|
drh
|
2572 |
** new query that is a bytecode() rendering of p->pStmt with extra |
|
4f76459…
|
drh
|
2573 |
** columns for the "scanstatus vm" outputs, and run the results of |
|
4f76459…
|
drh
|
2574 |
** that new query through the normal EXPLAIN formatting. |
|
4f76459…
|
drh
|
2575 |
*/ |
|
4f76459…
|
drh
|
2576 |
static void qrfScanStatusVm(Qrf *p){ |
|
4f76459…
|
drh
|
2577 |
sqlite3_stmt *pOrigStmt = p->pStmt; |
|
4f76459…
|
drh
|
2578 |
sqlite3_stmt *pExplain; |
|
4f76459…
|
drh
|
2579 |
int rc; |
|
4f76459…
|
drh
|
2580 |
static const char *zSql = |
|
4f76459…
|
drh
|
2581 |
" SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec," |
|
4f76459…
|
drh
|
2582 |
" format('% 6s (%.2f%%)'," |
|
4f76459…
|
drh
|
2583 |
" CASE WHEN ncycle<100_000 THEN ncycle || ' '" |
|
4f76459…
|
drh
|
2584 |
" WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'" |
|
4f76459…
|
drh
|
2585 |
" WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'" |
|
4f76459…
|
drh
|
2586 |
" ELSE (ncycle/1000_000_000) || 'G' END," |
|
4f76459…
|
drh
|
2587 |
" ncycle*100.0/(sum(ncycle) OVER ())" |
|
4f76459…
|
drh
|
2588 |
" ) AS cycles" |
|
4f76459…
|
drh
|
2589 |
" FROM bytecode(?1)"; |
|
4f76459…
|
drh
|
2590 |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pExplain, 0); |
|
4f76459…
|
drh
|
2591 |
if( rc ){ |
|
4f76459…
|
drh
|
2592 |
qrfError(p, rc, "%s", sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
2593 |
sqlite3_finalize(pExplain); |
|
4f76459…
|
drh
|
2594 |
return; |
|
4f76459…
|
drh
|
2595 |
} |
|
4f76459…
|
drh
|
2596 |
sqlite3_bind_pointer(pExplain, 1, pOrigStmt, "stmt-pointer", 0); |
|
4f76459…
|
drh
|
2597 |
p->pStmt = pExplain; |
|
4f76459…
|
drh
|
2598 |
p->nCol = 10; |
|
4f76459…
|
drh
|
2599 |
qrfExplain(p); |
|
4f76459…
|
drh
|
2600 |
sqlite3_finalize(pExplain); |
|
4f76459…
|
drh
|
2601 |
p->pStmt = pOrigStmt; |
|
4f76459…
|
drh
|
2602 |
** Return 1 if quoting is required. Return 0 if no quoting is required. |
|
4f76459…
|
drh
|
2603 |
|
|
4f76459…
|
drh
|
2604 |
static int qrf_need_quote(const char *zName){ |
|
4f76459…
|
drh
|
2605 |
int i; |
|
4f76459…
|
drh
|
2606 |
const unsigned char *z = (const unsigned char*)zName; |
|
4f76459…
|
drh
|
2607 |
if( z==0 ) return 1; |
|
4f76459…
|
drh
|
2608 |
if( !qrfAlpha(z[0]) ) return 1; |
|
4f76459…
|
drh
|
2609 |
for(i=0; z[i]; i++){ |
|
4f76459…
|
drh
|
2610 |
if( !qrfAlnum(z[i]) ) return 1; |
|
4f76459…
|
drh
|
2611 |
} |
|
4f76459…
|
drh
|
2612 |
return sqlite3_keyword_check(zName, i)!=0; |
|
4f76459…
|
drh
|
2613 |
} |
|
4f76459…
|
drh
|
2614 |
|
|
4f76459…
|
drh
|
2615 |
/* |
|
4f76459…
|
drh
|
2616 |
** Helper function for QRF_STYLE_Json and QRF_STYLE_JObject. |
|
4f76459…
|
drh
|
2617 |
** The initial "{" for a JSON object that will contain row content |
|
4f76459…
|
drh
|
2618 |
** has been output. Now output all the content. |
|
4f76459…
|
drh
|
2619 |
*/ |
|
4f76459…
|
drh
|
2620 |
static void qrfOneJsonRow(Qrf *p){ |
|
4f76459…
|
drh
|
2621 |
int i, nItem; |
|
4f76459…
|
drh
|
2622 |
for(nItem=i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2623 |
const char *zCName; |
|
4f76459…
|
drh
|
2624 |
zCName = sqlite3_column_name(p->pStmt, i); |
|
4f76459…
|
drh
|
2625 |
if( nItem>0 ) sqlite3_str_append(p->pOut, ",", 1); |
|
4f76459…
|
drh
|
2626 |
nItem++; |
|
4f76459…
|
drh
|
2627 |
qrfEncodeText(p, p->pOut, zCName); |
|
4f76459…
|
drh
|
2628 |
sqlite3_str_append(p->pOut, ":", 1); |
|
4f76459…
|
drh
|
2629 |
qrfRenderValue(p, p->pOut, i); |
|
4f76459…
|
drh
|
2630 |
} |
|
4f76459…
|
drh
|
2631 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2632 |
} |
|
4f76459…
|
drh
|
2633 |
|
|
4f76459…
|
drh
|
2634 |
/* |
|
4f76459…
|
drh
|
2635 |
** Render a single row of output for non-columnar styles - any |
|
4f76459…
|
drh
|
2636 |
** style that lets us render row by row as the content is received |
|
4f76459…
|
drh
|
2637 |
** from the query. |
|
4f76459…
|
drh
|
2638 |
*/ |
|
4f76459…
|
drh
|
2639 |
static void qrfOneSimpleRow(Qrf *p){ |
|
4f76459…
|
drh
|
2640 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2641 |
case QRF_STYLE_Off: |
|
4f76459…
|
drh
|
2642 |
case QRF_STYLE_Count: { |
|
4f76459…
|
drh
|
2643 |
/* No-op */ |
|
4f76459…
|
drh
|
2644 |
break; |
|
4f76459…
|
drh
|
2645 |
} |
|
4f76459…
|
drh
|
2646 |
case QRF_STYLE_Json: { |
|
4f76459…
|
drh
|
2647 |
if( p->nRow==0 ){ |
|
4f76459…
|
drh
|
2648 |
sqlite3_str_append(p->pOut, "[{", 2); |
|
4f76459…
|
drh
|
2649 |
}else{ |
|
4f76459…
|
drh
|
2650 |
sqlite3_str_append(p->pOut, "},\n{", 4); |
|
4f76459…
|
drh
|
2651 |
} |
|
4f76459…
|
drh
|
2652 |
qrfOneJsonRow(p); |
|
4f76459…
|
drh
|
2653 |
break; |
|
4f76459…
|
drh
|
2654 |
} |
|
4f76459…
|
drh
|
2655 |
case QRF_STYLE_JObject: { |
|
4f76459…
|
drh
|
2656 |
if( p->nRow==0 ){ |
|
4f76459…
|
drh
|
2657 |
sqlite3_str_append(p->pOut, "{", 1); |
|
4f76459…
|
drh
|
2658 |
}else{ |
|
4f76459…
|
drh
|
2659 |
sqlite3_str_append(p->pOut, "}\n{", 3); |
|
4f76459…
|
drh
|
2660 |
} |
|
4f76459…
|
drh
|
2661 |
qrfOneJsonRow(p); |
|
4f76459…
|
drh
|
2662 |
break; |
|
4f76459…
|
drh
|
2663 |
} |
|
4f76459…
|
drh
|
2664 |
case QRF_STYLE_Html: { |
|
4f76459…
|
drh
|
2665 |
if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
|
4f76459…
|
drh
|
2666 |
sqlite3_str_append(p->pOut, "<TR>", 4); |
|
4f76459…
|
drh
|
2667 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2668 |
const char *zCName = sqlite3_column_name(p->pStmt, i); |
|
4f76459…
|
drh
|
2669 |
sqlite3_str_append(p->pOut, "\n<TH>", 5); |
|
4f76459…
|
drh
|
2670 |
qrfEncodeText(p, p->pOut, zCName); |
|
4f76459…
|
drh
|
2671 |
} |
|
4f76459…
|
drh
|
2672 |
sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
|
4f76459…
|
drh
|
2673 |
} |
|
4f76459…
|
drh
|
2674 |
sqlite3_str_append(p->pOut, "<TR>", 4); |
|
4f76459…
|
drh
|
2675 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2676 |
sqlite3_str_append(p->pOut, "\n<TD>", 5); |
|
4f76459…
|
drh
|
2677 |
qrfRenderValue(p, p->pOut, i); |
|
4f76459…
|
drh
|
2678 |
} |
|
4f76459…
|
drh
|
2679 |
sqlite3_str_append(p->pOut, "\n</TR>\n", 7); |
|
4f76459…
|
drh
|
2680 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2681 |
break; |
|
4f76459…
|
drh
|
2682 |
} |
|
4f76459…
|
drh
|
2683 |
case QRF_STYLE_Insert: { |
|
cb89386…
|
drh
|
2684 |
unsigned int mxIns = p->spec.nMultiInsert; |
|
17f9878…
|
drh
|
2685 |
int szStart = sqlite3_str_length(p->pOut); |
|
17f9878…
|
drh
|
2686 |
if( p->u.nIns==0 || p->u.nIns>=mxIns ){ |
|
17f9878…
|
drh
|
2687 |
if( p->u.nIns ){ |
|
17f9878…
|
drh
|
2688 |
sqlite3_str_append(p->pOut, ";\n", 2); |
|
17f9878…
|
drh
|
2689 |
p->u.nIns = 0; |
|
17f9878…
|
drh
|
2690 |
} |
|
17f9878…
|
drh
|
2691 |
if( qrf_need_quote(p->spec.zTableName) ){ |
|
17f9878…
|
drh
|
2692 |
sqlite3_str_appendf(p->pOut,"INSERT INTO \"%w\"",p->spec.zTableName); |
|
17f9878…
|
drh
|
2693 |
}else{ |
|
17f9878…
|
drh
|
2694 |
sqlite3_str_appendf(p->pOut,"INSERT INTO %s",p->spec.zTableName); |
|
17f9878…
|
drh
|
2695 |
} |
|
17f9878…
|
drh
|
2696 |
if( p->spec.bTitles==QRF_Yes ){ |
|
17f9878…
|
drh
|
2697 |
for(i=0; i<p->nCol; i++){ |
|
17f9878…
|
drh
|
2698 |
const char *zCName = sqlite3_column_name(p->pStmt, i); |
|
17f9878…
|
drh
|
2699 |
if( qrf_need_quote(zCName) ){ |
|
17f9878…
|
drh
|
2700 |
sqlite3_str_appendf(p->pOut, "%c\"%w\"", |
|
17f9878…
|
drh
|
2701 |
i==0 ? '(' : ',', zCName); |
|
17f9878…
|
drh
|
2702 |
}else{ |
|
17f9878…
|
drh
|
2703 |
sqlite3_str_appendf(p->pOut, "%c%s", |
|
17f9878…
|
drh
|
2704 |
i==0 ? '(' : ',', zCName); |
|
17f9878…
|
drh
|
2705 |
} |
|
17f9878…
|
drh
|
2706 |
} |
|
17f9878…
|
drh
|
2707 |
sqlite3_str_append(p->pOut, ")", 1); |
|
17f9878…
|
drh
|
2708 |
} |
|
17f9878…
|
drh
|
2709 |
sqlite3_str_append(p->pOut," VALUES(", 8); |
|
17f9878…
|
drh
|
2710 |
}else{ |
|
17f9878…
|
drh
|
2711 |
sqlite3_str_append(p->pOut,",\n (", 5); |
|
17f9878…
|
drh
|
2712 |
} |
|
4f76459…
|
drh
|
2713 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2714 |
if( i>0 ) sqlite3_str_append(p->pOut, ",", 1); |
|
4f76459…
|
drh
|
2715 |
qrfRenderValue(p, p->pOut, i); |
|
4f76459…
|
drh
|
2716 |
} |
|
17f9878…
|
drh
|
2717 |
p->u.nIns += sqlite3_str_length(p->pOut) + 2 - szStart; |
|
17f9878…
|
drh
|
2718 |
if( p->u.nIns>=mxIns ){ |
|
17f9878…
|
drh
|
2719 |
sqlite3_str_append(p->pOut, ");\n", 3); |
|
17f9878…
|
drh
|
2720 |
p->u.nIns = 0; |
|
17f9878…
|
drh
|
2721 |
}else{ |
|
17f9878…
|
drh
|
2722 |
sqlite3_str_append(p->pOut, ")", 1); |
|
17f9878…
|
drh
|
2723 |
} |
|
4f76459…
|
drh
|
2724 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2725 |
break; |
|
4f76459…
|
drh
|
2726 |
} |
|
4f76459…
|
drh
|
2727 |
case QRF_STYLE_Line: { |
|
4f76459…
|
drh
|
2728 |
sqlite3_str *pVal; |
|
4f76459…
|
drh
|
2729 |
int mxW; |
|
4f76459…
|
drh
|
2730 |
int bWW; |
|
ae7e3f0…
|
drh
|
2731 |
int nSep; |
|
4f76459…
|
drh
|
2732 |
if( p->u.sLine.azCol==0 ){ |
|
4f76459…
|
drh
|
2733 |
p->u.sLine.azCol = sqlite3_malloc64( p->nCol*sizeof(char*) ); |
|
4f76459…
|
drh
|
2734 |
if( p->u.sLine.azCol==0 ){ |
|
4f76459…
|
drh
|
2735 |
qrfOom(p); |
|
4f76459…
|
drh
|
2736 |
break; |
|
4f76459…
|
drh
|
2737 |
} |
|
4f76459…
|
drh
|
2738 |
p->u.sLine.mxColWth = 0; |
|
4f76459…
|
drh
|
2739 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2740 |
int sz; |
|
ae7e3f0…
|
drh
|
2741 |
const char *zCName = sqlite3_column_name(p->pStmt, i); |
|
ae7e3f0…
|
drh
|
2742 |
if( zCName==0 ) zCName = "unknown"; |
|
ae7e3f0…
|
drh
|
2743 |
p->u.sLine.azCol[i] = sqlite3_mprintf("%s", zCName); |
|
ae7e3f0…
|
drh
|
2744 |
if( p->spec.nTitleLimit>0 ){ |
|
ae7e3f0…
|
drh
|
2745 |
(void)qrfTitleLimit(p->u.sLine.azCol[i], p->spec.nTitleLimit); |
|
ae7e3f0…
|
drh
|
2746 |
} |
|
d326547…
|
drh
|
2747 |
sz = (int)sqlite3_qrf_wcswidth(p->u.sLine.azCol[i]); |
|
4f76459…
|
drh
|
2748 |
if( sz > p->u.sLine.mxColWth ) p->u.sLine.mxColWth = sz; |
|
4f76459…
|
drh
|
2749 |
} |
|
4f76459…
|
drh
|
2750 |
} |
|
4f76459…
|
drh
|
2751 |
if( p->nRow ) sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2752 |
pVal = sqlite3_str_new(p->db); |
|
ae7e3f0…
|
drh
|
2753 |
nSep = (int)strlen(p->spec.zColumnSep); |
|
ae7e3f0…
|
drh
|
2754 |
mxW = p->mxWidth - (nSep + p->u.sLine.mxColWth); |
|
4f76459…
|
drh
|
2755 |
bWW = p->spec.bWordWrap==QRF_Yes; |
|
4f76459…
|
drh
|
2756 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2757 |
const char *zVal; |
|
4f76459…
|
drh
|
2758 |
int cnt = 0; |
|
4f76459…
|
drh
|
2759 |
qrfWidthPrint(p, p->pOut, -p->u.sLine.mxColWth, p->u.sLine.azCol[i]); |
|
ae7e3f0…
|
drh
|
2760 |
sqlite3_str_append(p->pOut, p->spec.zColumnSep, nSep); |
|
4f76459…
|
drh
|
2761 |
qrfRenderValue(p, pVal, i); |
|
4f76459…
|
drh
|
2762 |
zVal = sqlite3_str_value(pVal); |
|
4f76459…
|
drh
|
2763 |
if( zVal==0 ) zVal = ""; |
|
4f76459…
|
drh
|
2764 |
do{ |
|
4f76459…
|
drh
|
2765 |
int nThis, nWide, iNext; |
|
4f76459…
|
drh
|
2766 |
qrfWrapLine(zVal, mxW, bWW, &nThis, &nWide, &iNext); |
|
17f9878…
|
drh
|
2767 |
if( cnt ){ |
|
17f9878…
|
drh
|
2768 |
sqlite3_str_appendchar(p->pOut,p->u.sLine.mxColWth+nSep,' '); |
|
17f9878…
|
drh
|
2769 |
} |
|
4f76459…
|
drh
|
2770 |
cnt++; |
|
4f76459…
|
drh
|
2771 |
if( cnt>p->mxHeight ){ |
|
4f76459…
|
drh
|
2772 |
zVal = "..."; |
|
4f76459…
|
drh
|
2773 |
nThis = iNext = 3; |
|
4f76459…
|
drh
|
2774 |
} |
|
4f76459…
|
drh
|
2775 |
sqlite3_str_append(p->pOut, zVal, nThis); |
|
4f76459…
|
drh
|
2776 |
sqlite3_str_append(p->pOut, "\n", 1); |
|
4f76459…
|
drh
|
2777 |
zVal += iNext; |
|
4f76459…
|
drh
|
2778 |
}while( zVal[0] ); |
|
4f76459…
|
drh
|
2779 |
sqlite3_str_reset(pVal); |
|
4f76459…
|
drh
|
2780 |
} |
|
2b2530d…
|
drh
|
2781 |
qrfStrErr(p, pVal); |
|
4f76459…
|
drh
|
2782 |
sqlite3_free(sqlite3_str_finish(pVal)); |
|
4f76459…
|
drh
|
2783 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2784 |
break; |
|
4f76459…
|
drh
|
2785 |
} |
|
4f76459…
|
drh
|
2786 |
case QRF_STYLE_Eqp: { |
|
4f76459…
|
drh
|
2787 |
const char *zEqpLine = (const char*)sqlite3_column_text(p->pStmt,3); |
|
4f76459…
|
drh
|
2788 |
int iEqpId = sqlite3_column_int(p->pStmt, 0); |
|
4f76459…
|
drh
|
2789 |
int iParentId = sqlite3_column_int(p->pStmt, 1); |
|
4f76459…
|
drh
|
2790 |
if( zEqpLine==0 ) zEqpLine = ""; |
|
4f76459…
|
drh
|
2791 |
if( zEqpLine[0]=='-' ) qrfEqpRender(p, 0); |
|
4f76459…
|
drh
|
2792 |
qrfEqpAppend(p, iEqpId, iParentId, zEqpLine); |
|
4f76459…
|
drh
|
2793 |
break; |
|
4f76459…
|
drh
|
2794 |
} |
|
4f76459…
|
drh
|
2795 |
default: { /* QRF_STYLE_List */ |
|
4f76459…
|
drh
|
2796 |
if( p->nRow==0 && p->spec.bTitles==QRF_Yes ){ |
|
4f76459…
|
drh
|
2797 |
int saved_eText = p->spec.eText; |
|
4f76459…
|
drh
|
2798 |
p->spec.eText = p->spec.eTitle; |
|
4f76459…
|
drh
|
2799 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2800 |
const char *zCName = sqlite3_column_name(p->pStmt, i); |
|
4f76459…
|
drh
|
2801 |
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
|
4f76459…
|
drh
|
2802 |
qrfEncodeText(p, p->pOut, zCName); |
|
4f76459…
|
drh
|
2803 |
} |
|
4f76459…
|
drh
|
2804 |
sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
|
4f76459…
|
drh
|
2805 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2806 |
p->spec.eText = saved_eText; |
|
4f76459…
|
drh
|
2807 |
} |
|
4f76459…
|
drh
|
2808 |
for(i=0; i<p->nCol; i++){ |
|
4f76459…
|
drh
|
2809 |
if( i>0 ) sqlite3_str_appendall(p->pOut, p->spec.zColumnSep); |
|
4f76459…
|
drh
|
2810 |
qrfRenderValue(p, p->pOut, i); |
|
4f76459…
|
drh
|
2811 |
} |
|
4f76459…
|
drh
|
2812 |
sqlite3_str_appendall(p->pOut, p->spec.zRowSep); |
|
4f76459…
|
drh
|
2813 |
qrfWrite(p); |
|
4f76459…
|
drh
|
2814 |
break; |
|
4f76459…
|
drh
|
2815 |
} |
|
4f76459…
|
drh
|
2816 |
} |
|
4f76459…
|
drh
|
2817 |
p->nRow++; |
|
4f76459…
|
drh
|
2818 |
} |
|
4f76459…
|
drh
|
2819 |
|
|
4f76459…
|
drh
|
2820 |
/* |
|
4f76459…
|
drh
|
2821 |
** Initialize the internal Qrf object. |
|
4f76459…
|
drh
|
2822 |
*/ |
|
4f76459…
|
drh
|
2823 |
static void qrfInitialize( |
|
4f76459…
|
drh
|
2824 |
Qrf *p, /* State object to be initialized */ |
|
4f76459…
|
drh
|
2825 |
sqlite3_stmt *pStmt, /* Query whose output to be formatted */ |
|
4f76459…
|
drh
|
2826 |
const sqlite3_qrf_spec *pSpec, /* Format specification */ |
|
4f76459…
|
drh
|
2827 |
char **pzErr /* Write errors here */ |
|
4f76459…
|
drh
|
2828 |
){ |
|
4f76459…
|
drh
|
2829 |
size_t sz; /* Size of pSpec[], based on pSpec->iVersion */ |
|
4f76459…
|
drh
|
2830 |
memset(p, 0, sizeof(*p)); |
|
4f76459…
|
drh
|
2831 |
p->pzErr = pzErr; |
|
cb89386…
|
drh
|
2832 |
if( pSpec->iVersion>1 ){ |
|
4f76459…
|
drh
|
2833 |
qrfError(p, SQLITE_ERROR, |
|
4f76459…
|
drh
|
2834 |
"unusable sqlite3_qrf_spec.iVersion (%d)", |
|
4f76459…
|
drh
|
2835 |
pSpec->iVersion); |
|
4f76459…
|
drh
|
2836 |
return; |
|
4f76459…
|
drh
|
2837 |
} |
|
4f76459…
|
drh
|
2838 |
p->pStmt = pStmt; |
|
4f76459…
|
drh
|
2839 |
p->db = sqlite3_db_handle(pStmt); |
|
4f76459…
|
drh
|
2840 |
p->pOut = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
2841 |
if( p->pOut==0 ){ |
|
4f76459…
|
drh
|
2842 |
qrfOom(p); |
|
4f76459…
|
drh
|
2843 |
return; |
|
4f76459…
|
drh
|
2844 |
} |
|
2b2530d…
|
drh
|
2845 |
p->iErr = SQLITE_OK; |
|
4f76459…
|
drh
|
2846 |
p->nCol = sqlite3_column_count(p->pStmt); |
|
4f76459…
|
drh
|
2847 |
p->nRow = 0; |
|
4f76459…
|
drh
|
2848 |
sz = sizeof(sqlite3_qrf_spec); |
|
4f76459…
|
drh
|
2849 |
memcpy(&p->spec, pSpec, sz); |
|
4f76459…
|
drh
|
2850 |
if( p->spec.zNull==0 ) p->spec.zNull = ""; |
|
4f76459…
|
drh
|
2851 |
p->mxWidth = p->spec.nScreenWidth; |
|
4f76459…
|
drh
|
2852 |
if( p->mxWidth<=0 ) p->mxWidth = QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
2853 |
p->mxHeight = p->spec.nLineLimit; |
|
4f76459…
|
drh
|
2854 |
if( p->mxHeight<=0 ) p->mxHeight = 2147483647; |
|
45de97f…
|
drh
|
2855 |
if( p->spec.eStyle>QRF_STYLE_Table ) p->spec.eStyle = QRF_Auto; |
|
45de97f…
|
drh
|
2856 |
if( p->spec.eEsc>QRF_ESC_Symbol ) p->spec.eEsc = QRF_Auto; |
|
709b566…
|
drh
|
2857 |
if( p->spec.eText>QRF_TEXT_Relaxed ) p->spec.eText = QRF_Auto; |
|
709b566…
|
drh
|
2858 |
if( p->spec.eTitle>QRF_TEXT_Relaxed ) p->spec.eTitle = QRF_Auto; |
|
45de97f…
|
drh
|
2859 |
if( p->spec.eBlob>QRF_BLOB_Size ) p->spec.eBlob = QRF_Auto; |
|
4f76459…
|
drh
|
2860 |
qrf_reinit: |
|
4f76459…
|
drh
|
2861 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2862 |
case QRF_Auto: { |
|
4f76459…
|
drh
|
2863 |
switch( sqlite3_stmt_isexplain(pStmt) ){ |
|
4f76459…
|
drh
|
2864 |
case 0: p->spec.eStyle = QRF_STYLE_Box; break; |
|
4f76459…
|
drh
|
2865 |
case 1: p->spec.eStyle = QRF_STYLE_Explain; break; |
|
4f76459…
|
drh
|
2866 |
default: p->spec.eStyle = QRF_STYLE_Eqp; break; |
|
4f76459…
|
drh
|
2867 |
} |
|
4f76459…
|
drh
|
2868 |
goto qrf_reinit; |
|
4f76459…
|
drh
|
2869 |
} |
|
4f76459…
|
drh
|
2870 |
case QRF_STYLE_List: { |
|
4f76459…
|
drh
|
2871 |
if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = "|"; |
|
4f76459…
|
drh
|
2872 |
if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; |
|
4f76459…
|
drh
|
2873 |
break; |
|
4f76459…
|
drh
|
2874 |
} |
|
4f76459…
|
drh
|
2875 |
case QRF_STYLE_JObject: |
|
4f76459…
|
drh
|
2876 |
case QRF_STYLE_Json: { |
|
4f76459…
|
drh
|
2877 |
p->spec.eText = QRF_TEXT_Json; |
|
4f76459…
|
drh
|
2878 |
p->spec.zNull = "null"; |
|
4f76459…
|
drh
|
2879 |
break; |
|
4f76459…
|
drh
|
2880 |
} |
|
4f76459…
|
drh
|
2881 |
case QRF_STYLE_Html: { |
|
4f76459…
|
drh
|
2882 |
p->spec.eText = QRF_TEXT_Html; |
|
4f76459…
|
drh
|
2883 |
p->spec.zNull = "null"; |
|
4f76459…
|
drh
|
2884 |
break; |
|
4f76459…
|
drh
|
2885 |
} |
|
4f76459…
|
drh
|
2886 |
case QRF_STYLE_Insert: { |
|
4f76459…
|
drh
|
2887 |
p->spec.eText = QRF_TEXT_Sql; |
|
4f76459…
|
drh
|
2888 |
p->spec.zNull = "NULL"; |
|
4f76459…
|
drh
|
2889 |
if( p->spec.zTableName==0 || p->spec.zTableName[0]==0 ){ |
|
4f76459…
|
drh
|
2890 |
p->spec.zTableName = "tab"; |
|
ae7e3f0…
|
drh
|
2891 |
} |
|
17f9878…
|
drh
|
2892 |
p->u.nIns = 0; |
|
ae7e3f0…
|
drh
|
2893 |
break; |
|
ae7e3f0…
|
drh
|
2894 |
} |
|
ae7e3f0…
|
drh
|
2895 |
case QRF_STYLE_Line: { |
|
ae7e3f0…
|
drh
|
2896 |
if( p->spec.zColumnSep==0 ){ |
|
ae7e3f0…
|
drh
|
2897 |
p->spec.zColumnSep = ": "; |
|
4f76459…
|
drh
|
2898 |
} |
|
4f76459…
|
drh
|
2899 |
break; |
|
4f76459…
|
drh
|
2900 |
} |
|
4f76459…
|
drh
|
2901 |
case QRF_STYLE_Csv: { |
|
4f76459…
|
drh
|
2902 |
p->spec.eStyle = QRF_STYLE_List; |
|
4f76459…
|
drh
|
2903 |
p->spec.eText = QRF_TEXT_Csv; |
|
4f76459…
|
drh
|
2904 |
p->spec.zColumnSep = ","; |
|
4f76459…
|
drh
|
2905 |
p->spec.zRowSep = "\r\n"; |
|
4f76459…
|
drh
|
2906 |
p->spec.zNull = ""; |
|
4f76459…
|
drh
|
2907 |
break; |
|
4f76459…
|
drh
|
2908 |
} |
|
4f76459…
|
drh
|
2909 |
case QRF_STYLE_Quote: { |
|
4f76459…
|
drh
|
2910 |
p->spec.eText = QRF_TEXT_Sql; |
|
4f76459…
|
drh
|
2911 |
p->spec.zNull = "NULL"; |
|
4f76459…
|
drh
|
2912 |
p->spec.zColumnSep = ","; |
|
4f76459…
|
drh
|
2913 |
p->spec.zRowSep = "\n"; |
|
4f76459…
|
drh
|
2914 |
break; |
|
4f76459…
|
drh
|
2915 |
} |
|
4f76459…
|
drh
|
2916 |
case QRF_STYLE_Eqp: { |
|
4f76459…
|
drh
|
2917 |
int expMode = sqlite3_stmt_isexplain(p->pStmt); |
|
4f76459…
|
drh
|
2918 |
if( expMode!=2 ){ |
|
4f76459…
|
drh
|
2919 |
sqlite3_stmt_explain(p->pStmt, 2); |
|
4f76459…
|
drh
|
2920 |
p->expMode = expMode+1; |
|
4f76459…
|
drh
|
2921 |
} |
|
4f76459…
|
drh
|
2922 |
break; |
|
4f76459…
|
drh
|
2923 |
} |
|
4f76459…
|
drh
|
2924 |
case QRF_STYLE_Explain: { |
|
4f76459…
|
drh
|
2925 |
int expMode = sqlite3_stmt_isexplain(p->pStmt); |
|
4f76459…
|
drh
|
2926 |
if( expMode!=1 ){ |
|
4f76459…
|
drh
|
2927 |
sqlite3_stmt_explain(p->pStmt, 1); |
|
4f76459…
|
drh
|
2928 |
p->expMode = expMode+1; |
|
4f76459…
|
drh
|
2929 |
} |
|
4f76459…
|
drh
|
2930 |
break; |
|
4f76459…
|
drh
|
2931 |
} |
|
4f76459…
|
drh
|
2932 |
} |
|
4f76459…
|
drh
|
2933 |
if( p->spec.eEsc==QRF_Auto ){ |
|
4f76459…
|
drh
|
2934 |
p->spec.eEsc = QRF_ESC_Ascii; |
|
4f76459…
|
drh
|
2935 |
} |
|
4f76459…
|
drh
|
2936 |
if( p->spec.eText==QRF_Auto ){ |
|
4f76459…
|
drh
|
2937 |
p->spec.eText = QRF_TEXT_Plain; |
|
4f76459…
|
drh
|
2938 |
} |
|
4f76459…
|
drh
|
2939 |
if( p->spec.eTitle==QRF_Auto ){ |
|
4f76459…
|
drh
|
2940 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2941 |
case QRF_STYLE_Box: |
|
4f76459…
|
drh
|
2942 |
case QRF_STYLE_Column: |
|
4f76459…
|
drh
|
2943 |
case QRF_STYLE_Table: |
|
4f76459…
|
drh
|
2944 |
p->spec.eTitle = QRF_TEXT_Plain; |
|
4f76459…
|
drh
|
2945 |
break; |
|
4f76459…
|
drh
|
2946 |
default: |
|
4f76459…
|
drh
|
2947 |
p->spec.eTitle = p->spec.eText; |
|
4f76459…
|
drh
|
2948 |
break; |
|
4f76459…
|
drh
|
2949 |
} |
|
4f76459…
|
drh
|
2950 |
} |
|
4f76459…
|
drh
|
2951 |
if( p->spec.eBlob==QRF_Auto ){ |
|
4f76459…
|
drh
|
2952 |
switch( p->spec.eText ){ |
|
4f76459…
|
drh
|
2953 |
case QRF_TEXT_Sql: p->spec.eBlob = QRF_BLOB_Sql; break; |
|
4f76459…
|
drh
|
2954 |
case QRF_TEXT_Csv: p->spec.eBlob = QRF_BLOB_Tcl; break; |
|
4f76459…
|
drh
|
2955 |
case QRF_TEXT_Tcl: p->spec.eBlob = QRF_BLOB_Tcl; break; |
|
4f76459…
|
drh
|
2956 |
case QRF_TEXT_Json: p->spec.eBlob = QRF_BLOB_Json; break; |
|
4f76459…
|
drh
|
2957 |
default: p->spec.eBlob = QRF_BLOB_Text; break; |
|
4f76459…
|
drh
|
2958 |
} |
|
4f76459…
|
drh
|
2959 |
} |
|
4f76459…
|
drh
|
2960 |
if( p->spec.bTitles==QRF_Auto ){ |
|
4f76459…
|
drh
|
2961 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2962 |
case QRF_STYLE_Box: |
|
4f76459…
|
drh
|
2963 |
case QRF_STYLE_Csv: |
|
4f76459…
|
drh
|
2964 |
case QRF_STYLE_Column: |
|
4f76459…
|
drh
|
2965 |
case QRF_STYLE_Table: |
|
4f76459…
|
drh
|
2966 |
case QRF_STYLE_Markdown: |
|
4f76459…
|
drh
|
2967 |
p->spec.bTitles = QRF_Yes; |
|
4f76459…
|
drh
|
2968 |
break; |
|
4f76459…
|
drh
|
2969 |
default: |
|
4f76459…
|
drh
|
2970 |
p->spec.bTitles = QRF_No; |
|
4f76459…
|
drh
|
2971 |
break; |
|
4f76459…
|
drh
|
2972 |
} |
|
4f76459…
|
drh
|
2973 |
} |
|
4f76459…
|
drh
|
2974 |
if( p->spec.bWordWrap==QRF_Auto ){ |
|
4f76459…
|
drh
|
2975 |
p->spec.bWordWrap = QRF_Yes; |
|
4f76459…
|
drh
|
2976 |
} |
|
4f76459…
|
drh
|
2977 |
if( p->spec.bTextJsonb==QRF_Auto ){ |
|
4f76459…
|
drh
|
2978 |
p->spec.bTextJsonb = QRF_No; |
|
4f76459…
|
drh
|
2979 |
} |
|
4f76459…
|
drh
|
2980 |
if( p->spec.zColumnSep==0 ) p->spec.zColumnSep = ","; |
|
4f76459…
|
drh
|
2981 |
if( p->spec.zRowSep==0 ) p->spec.zRowSep = "\n"; |
|
4f76459…
|
drh
|
2982 |
} |
|
4f76459…
|
drh
|
2983 |
|
|
4f76459…
|
drh
|
2984 |
/* |
|
4f76459…
|
drh
|
2985 |
** Finish rendering the results |
|
4f76459…
|
drh
|
2986 |
*/ |
|
4f76459…
|
drh
|
2987 |
static void qrfFinalize(Qrf *p){ |
|
4f76459…
|
drh
|
2988 |
switch( p->spec.eStyle ){ |
|
4f76459…
|
drh
|
2989 |
case QRF_STYLE_Count: { |
|
4f76459…
|
drh
|
2990 |
sqlite3_str_appendf(p->pOut, "%lld\n", p->nRow); |
|
4f76459…
|
drh
|
2991 |
break; |
|
4f76459…
|
drh
|
2992 |
} |
|
4f76459…
|
drh
|
2993 |
case QRF_STYLE_Json: { |
|
f07aa62…
|
drh
|
2994 |
if( p->nRow>0 ){ |
|
f07aa62…
|
drh
|
2995 |
sqlite3_str_append(p->pOut, "}]\n", 3); |
|
f07aa62…
|
drh
|
2996 |
} |
|
4f76459…
|
drh
|
2997 |
break; |
|
4f76459…
|
drh
|
2998 |
} |
|
4f76459…
|
drh
|
2999 |
case QRF_STYLE_JObject: { |
|
f07aa62…
|
drh
|
3000 |
if( p->nRow>0 ){ |
|
f07aa62…
|
drh
|
3001 |
sqlite3_str_append(p->pOut, "}\n", 2); |
|
17f9878…
|
drh
|
3002 |
} |
|
17f9878…
|
drh
|
3003 |
break; |
|
17f9878…
|
drh
|
3004 |
} |
|
17f9878…
|
drh
|
3005 |
case QRF_STYLE_Insert: { |
|
17f9878…
|
drh
|
3006 |
if( p->u.nIns ){ |
|
17f9878…
|
drh
|
3007 |
sqlite3_str_append(p->pOut, ";\n", 2); |
|
f07aa62…
|
drh
|
3008 |
} |
|
4f76459…
|
drh
|
3009 |
break; |
|
4f76459…
|
drh
|
3010 |
} |
|
4f76459…
|
drh
|
3011 |
case QRF_STYLE_Line: { |
|
ae7e3f0…
|
drh
|
3012 |
if( p->u.sLine.azCol ){ |
|
ae7e3f0…
|
drh
|
3013 |
int i; |
|
ae7e3f0…
|
drh
|
3014 |
for(i=0; i<p->nCol; i++) sqlite3_free(p->u.sLine.azCol[i]); |
|
ae7e3f0…
|
drh
|
3015 |
sqlite3_free(p->u.sLine.azCol); |
|
ae7e3f0…
|
drh
|
3016 |
} |
|
4f76459…
|
drh
|
3017 |
break; |
|
4f76459…
|
drh
|
3018 |
} |
|
4f76459…
|
drh
|
3019 |
case QRF_STYLE_Stats: |
|
7b0960d…
|
drh
|
3020 |
case QRF_STYLE_StatsEst: { |
|
7b0960d…
|
drh
|
3021 |
i64 nCycle = 0; |
|
7b0960d…
|
drh
|
3022 |
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS |
|
7b0960d…
|
drh
|
3023 |
sqlite3_stmt_scanstatus_v2(p->pStmt, -1, SQLITE_SCANSTAT_NCYCLE, |
|
7b0960d…
|
drh
|
3024 |
SQLITE_SCANSTAT_COMPLEX, (void*)&nCycle); |
|
7b0960d…
|
drh
|
3025 |
#endif |
|
7b0960d…
|
drh
|
3026 |
qrfEqpRender(p, nCycle); |
|
7b0960d…
|
drh
|
3027 |
break; |
|
7b0960d…
|
drh
|
3028 |
} |
|
4f76459…
|
drh
|
3029 |
case QRF_STYLE_Eqp: { |
|
4f76459…
|
drh
|
3030 |
qrfEqpRender(p, 0); |
|
4f76459…
|
drh
|
3031 |
break; |
|
4f76459…
|
drh
|
3032 |
} |
|
4f76459…
|
drh
|
3033 |
} |
|
17f9878…
|
drh
|
3034 |
qrfWrite(p); |
|
2b2530d…
|
drh
|
3035 |
qrfStrErr(p, p->pOut); |
|
4f76459…
|
drh
|
3036 |
if( p->spec.pzOutput ){ |
|
4f76459…
|
drh
|
3037 |
if( p->spec.pzOutput[0] ){ |
|
4f76459…
|
drh
|
3038 |
sqlite3_int64 n, sz; |
|
4f76459…
|
drh
|
3039 |
char *zCombined; |
|
4f76459…
|
drh
|
3040 |
sz = strlen(p->spec.pzOutput[0]); |
|
4f76459…
|
drh
|
3041 |
n = sqlite3_str_length(p->pOut); |
|
ba8756a…
|
drh
|
3042 |
zCombined = sqlite3_realloc64(p->spec.pzOutput[0], sz+n+1); |
|
4f76459…
|
drh
|
3043 |
if( zCombined==0 ){ |
|
4f76459…
|
drh
|
3044 |
sqlite3_free(p->spec.pzOutput[0]); |
|
4f76459…
|
drh
|
3045 |
p->spec.pzOutput[0] = 0; |
|
4f76459…
|
drh
|
3046 |
qrfOom(p); |
|
4f76459…
|
drh
|
3047 |
}else{ |
|
4f76459…
|
drh
|
3048 |
p->spec.pzOutput[0] = zCombined; |
|
4f76459…
|
drh
|
3049 |
memcpy(zCombined+sz, sqlite3_str_value(p->pOut), n+1); |
|
4f76459…
|
drh
|
3050 |
} |
|
4f76459…
|
drh
|
3051 |
sqlite3_free(sqlite3_str_finish(p->pOut)); |
|
4f76459…
|
drh
|
3052 |
}else{ |
|
4f76459…
|
drh
|
3053 |
p->spec.pzOutput[0] = sqlite3_str_finish(p->pOut); |
|
4f76459…
|
drh
|
3054 |
} |
|
4f76459…
|
drh
|
3055 |
}else if( p->pOut ){ |
|
4f76459…
|
drh
|
3056 |
sqlite3_free(sqlite3_str_finish(p->pOut)); |
|
4f76459…
|
drh
|
3057 |
} |
|
4f76459…
|
drh
|
3058 |
if( p->expMode>0 ){ |
|
4f76459…
|
drh
|
3059 |
sqlite3_stmt_explain(p->pStmt, p->expMode-1); |
|
4f76459…
|
drh
|
3060 |
} |
|
4f76459…
|
drh
|
3061 |
if( p->actualWidth ){ |
|
4f76459…
|
drh
|
3062 |
sqlite3_free(p->actualWidth); |
|
4f76459…
|
drh
|
3063 |
} |
|
4f76459…
|
drh
|
3064 |
if( p->pJTrans ){ |
|
4f76459…
|
drh
|
3065 |
sqlite3 *db = sqlite3_db_handle(p->pJTrans); |
|
4f76459…
|
drh
|
3066 |
sqlite3_finalize(p->pJTrans); |
|
4f76459…
|
drh
|
3067 |
sqlite3_close(db); |
|
4f76459…
|
drh
|
3068 |
} |
|
4f76459…
|
drh
|
3069 |
} |
|
4f76459…
|
drh
|
3070 |
|
|
4f76459…
|
drh
|
3071 |
/* |
|
4f76459…
|
drh
|
3072 |
** Run the prepared statement pStmt and format the results according |
|
4f76459…
|
drh
|
3073 |
** to the specification provided in pSpec. Return an error code. |
|
4f76459…
|
drh
|
3074 |
** If pzErr is not NULL and if an error occurs, write an error message |
|
4f76459…
|
drh
|
3075 |
** into *pzErr. |
|
4f76459…
|
drh
|
3076 |
*/ |
|
4f76459…
|
drh
|
3077 |
int sqlite3_format_query_result( |
|
4f76459…
|
drh
|
3078 |
sqlite3_stmt *pStmt, /* Statement to evaluate */ |
|
4f76459…
|
drh
|
3079 |
const sqlite3_qrf_spec *pSpec, /* Format specification */ |
|
4f76459…
|
drh
|
3080 |
char **pzErr /* Write error message here */ |
|
4f76459…
|
drh
|
3081 |
){ |
|
4f76459…
|
drh
|
3082 |
Qrf qrf; /* The new Qrf being created */ |
|
4f76459…
|
drh
|
3083 |
|
|
4f76459…
|
drh
|
3084 |
if( pStmt==0 ) return SQLITE_OK; /* No-op */ |
|
4f76459…
|
drh
|
3085 |
if( pSpec==0 ) return SQLITE_MISUSE; |
|
4f76459…
|
drh
|
3086 |
qrfInitialize(&qrf, pStmt, pSpec, pzErr); |
|
4f76459…
|
drh
|
3087 |
switch( qrf.spec.eStyle ){ |
|
4f76459…
|
drh
|
3088 |
case QRF_STYLE_Box: |
|
4f76459…
|
drh
|
3089 |
case QRF_STYLE_Column: |
|
4f76459…
|
drh
|
3090 |
case QRF_STYLE_Markdown: |
|
4f76459…
|
drh
|
3091 |
case QRF_STYLE_Table: { |
|
4f76459…
|
drh
|
3092 |
/* Columnar modes require that the entire query be evaluated and the |
|
4f76459…
|
drh
|
3093 |
** results stored in memory, so that we can compute column widths */ |
|
4f76459…
|
drh
|
3094 |
qrfColumnar(&qrf); |
|
4f76459…
|
drh
|
3095 |
break; |
|
4f76459…
|
drh
|
3096 |
} |
|
4f76459…
|
drh
|
3097 |
case QRF_STYLE_Explain: { |
|
4f76459…
|
drh
|
3098 |
qrfExplain(&qrf); |
|
4f76459…
|
drh
|
3099 |
break; |
|
4f76459…
|
drh
|
3100 |
} |
|
4f76459…
|
drh
|
3101 |
case QRF_STYLE_StatsVm: { |
|
4f76459…
|
drh
|
3102 |
qrfScanStatusVm(&qrf); |
|
4f76459…
|
drh
|
3103 |
break; |
|
4f76459…
|
drh
|
3104 |
} |
|
4f76459…
|
drh
|
3105 |
case QRF_STYLE_Stats: |
|
4f76459…
|
drh
|
3106 |
case QRF_STYLE_StatsEst: { |
|
4f76459…
|
drh
|
3107 |
qrfEqpStats(&qrf); |
|
4f76459…
|
drh
|
3108 |
break; |
|
4f76459…
|
drh
|
3109 |
} |
|
4f76459…
|
drh
|
3110 |
default: { |
|
4f76459…
|
drh
|
3111 |
/* Non-columnar modes where the output can occur after each row |
|
4f76459…
|
drh
|
3112 |
** of result is received */ |
|
4f76459…
|
drh
|
3113 |
while( qrf.iErr==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
4f76459…
|
drh
|
3114 |
qrfOneSimpleRow(&qrf); |
|
4f76459…
|
drh
|
3115 |
} |
|
4f76459…
|
drh
|
3116 |
break; |
|
4f76459…
|
drh
|
3117 |
} |
|
4f76459…
|
drh
|
3118 |
} |
|
4f76459…
|
drh
|
3119 |
qrfResetStmt(&qrf); |
|
4f76459…
|
drh
|
3120 |
qrfFinalize(&qrf); |
|
4f76459…
|
drh
|
3121 |
return qrf.iErr; |
|
4f76459…
|
drh
|
3122 |
} |
|
4f76459…
|
drh
|
3123 |
|
|
4f76459…
|
drh
|
3124 |
/************************* End ext/qrf/qrf.c ********************/ |
|
4f76459…
|
drh
|
3125 |
|
|
4f76459…
|
drh
|
3126 |
/* Use console I/O package as a direct INCLUDE. */ |
|
4f76459…
|
drh
|
3127 |
#define SQLITE_INTERNAL_LINKAGE static |
|
4f76459…
|
drh
|
3128 |
|
|
4f76459…
|
drh
|
3129 |
#ifdef SQLITE_SHELL_FIDDLE |
|
4f76459…
|
drh
|
3130 |
/* Deselect most features from the console I/O package for Fiddle. */ |
|
4f76459…
|
drh
|
3131 |
# define SQLITE_CIO_NO_REDIRECT |
|
4f76459…
|
drh
|
3132 |
# define SQLITE_CIO_NO_CLASSIFY |
|
4f76459…
|
drh
|
3133 |
# define SQLITE_CIO_NO_TRANSLATE |
|
4f76459…
|
drh
|
3134 |
# define SQLITE_CIO_NO_SETMODE |
|
4f76459…
|
drh
|
3135 |
# define SQLITE_CIO_NO_FLUSH |
|
4f76459…
|
drh
|
3136 |
#endif |
|
4f76459…
|
drh
|
3137 |
/************************* Begin ext/misc/windirent.h ******************/ |
|
4f76459…
|
drh
|
3138 |
/************************* End ext/misc/windirent.h ********************/ |
|
4f76459…
|
drh
|
3139 |
/************************* Begin ext/misc/memtrace.c ******************/ |
|
4f76459…
|
drh
|
3140 |
/************************* End ext/misc/memtrace.c ********************/ |
|
4f76459…
|
drh
|
3141 |
/************************* Begin ext/misc/pcachetrace.c ******************/ |
|
4f76459…
|
drh
|
3142 |
/************************* End ext/misc/pcachetrace.c ********************/ |
|
4f76459…
|
drh
|
3143 |
/************************* Begin ext/misc/shathree.c ******************/ |
|
4f76459…
|
drh
|
3144 |
/************************* End ext/misc/shathree.c ********************/ |
|
4f76459…
|
drh
|
3145 |
/************************* Begin ext/misc/sha1.c ******************/ |
|
f4b3b59…
|
drh
|
3146 |
** Two SQL functions: sha1(X) and sha1b(X). |
|
f4b3b59…
|
drh
|
3147 |
** sha1(X) returns a lower-case hexadecimal rendering of the SHA1 hash |
|
f4b3b59…
|
drh
|
3148 |
** of the argument X. If X is a BLOB, it is hashed as is. For all other |
|
f4b3b59…
|
drh
|
3149 |
** is hashed without the trailing 0x00 terminator. The hash of a NULL |
|
f4b3b59…
|
drh
|
3150 |
** |
|
f4b3b59…
|
drh
|
3151 |
** sha1b(X) is the same except that it returns a 20-byte BLOB containing |
|
f4b3b59…
|
drh
|
3152 |
** the binary hash instead of a hexadecimal string. |
|
17f9878…
|
drh
|
3153 |
const unsigned char *pData; |
|
17f9878…
|
drh
|
3154 |
pData = (const unsigned char*)sqlite3_value_blob(argv[0]); |
|
17f9878…
|
drh
|
3155 |
pData = (const unsigned char*)sqlite3_value_text(argv[0]); |
|
17f9878…
|
drh
|
3156 |
if( pData==0 ) return; |
|
17f9878…
|
drh
|
3157 |
hash_step(&cx, pData, nByte); |
|
f4b3b59…
|
drh
|
3158 |
/* sha1b() - binary result */ |
|
f4b3b59…
|
drh
|
3159 |
/* sha1() - hexadecimal text result */ |
|
f4b3b59…
|
drh
|
3160 |
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT); |
|
17f9878…
|
drh
|
3161 |
if( z==0 ) z = ""; |
|
4f76459…
|
drh
|
3162 |
/************************* End ext/misc/sha1.c ********************/ |
|
4f76459…
|
drh
|
3163 |
/************************* Begin ext/misc/uint.c ******************/ |
|
4f76459…
|
drh
|
3164 |
/************************* End ext/misc/uint.c ********************/ |
|
4f76459…
|
drh
|
3165 |
/************************* Begin ext/misc/decimal.c ******************/ |
|
4f76459…
|
drh
|
3166 |
#endif |
|
4f76459…
|
drh
|
3167 |
|
|
17f9878…
|
drh
|
3168 |
#ifndef SQLITE_DECIMAL_MAX_DIGIT |
|
17f9878…
|
drh
|
3169 |
# define SQLITE_DECIMAL_MAX_DIGIT 10000000 |
|
17f9878…
|
drh
|
3170 |
#endif |
|
17f9878…
|
drh
|
3171 |
|
|
17f9878…
|
drh
|
3172 |
if( zIn==0 ) goto new_from_text_failed; |
|
0201a1e…
|
drh
|
3173 |
p = sqlite3_malloc64( sizeof(*p) ); |
|
17f9878…
|
drh
|
3174 |
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit |
|
3c639f7…
|
drh
|
3175 |
+ (sqlite3_int64)iExp + 1 ); |
|
17f9878…
|
drh
|
3176 |
if( a==0 ) goto new_from_text_failed; |
|
17f9878…
|
drh
|
3177 |
p->a = a; |
|
17f9878…
|
drh
|
3178 |
signed char *a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit |
|
3c639f7…
|
drh
|
3179 |
+ (sqlite3_int64)iExp + 1 ); |
|
17f9878…
|
drh
|
3180 |
if( a==0 ) goto new_from_text_failed; |
|
17f9878…
|
drh
|
3181 |
p->a = a; |
|
17f9878…
|
drh
|
3182 |
if( p->nDigit>SQLITE_DECIMAL_MAX_DIGIT ) goto new_from_text_failed; |
|
3c639f7…
|
drh
|
3183 |
z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 ); |
|
b8ab8b3…
|
drh
|
3184 |
** Round a decimal value to N significant digits. N must be positive. |
|
b8ab8b3…
|
drh
|
3185 |
*/ |
|
b8ab8b3…
|
drh
|
3186 |
static void decimal_round(Decimal *p, int N){ |
|
b8ab8b3…
|
drh
|
3187 |
int i; |
|
b8ab8b3…
|
drh
|
3188 |
int nZero; |
|
b8ab8b3…
|
drh
|
3189 |
if( N<1 ) return; |
|
17f9878…
|
drh
|
3190 |
if( p==0 ) return; |
|
17f9878…
|
drh
|
3191 |
if( p->nDigit<=N ) return; |
|
b8ab8b3…
|
drh
|
3192 |
for(nZero=0; nZero<p->nDigit && p->a[nZero]==0; nZero++){} |
|
b8ab8b3…
|
drh
|
3193 |
N += nZero; |
|
b8ab8b3…
|
drh
|
3194 |
if( p->nDigit<=N ) return; |
|
b8ab8b3…
|
drh
|
3195 |
if( p->a[N]>4 ){ |
|
b8ab8b3…
|
drh
|
3196 |
p->a[N-1]++; |
|
b8ab8b3…
|
drh
|
3197 |
for(i=N-1; i>0 && p->a[i]>9; i--){ |
|
b8ab8b3…
|
drh
|
3198 |
p->a[i] = 0; |
|
b8ab8b3…
|
drh
|
3199 |
p->a[i-1]++; |
|
b8ab8b3…
|
drh
|
3200 |
} |
|
b8ab8b3…
|
drh
|
3201 |
if( p->a[0]>9 ){ |
|
b8ab8b3…
|
drh
|
3202 |
p->a[0] = 1; |
|
b8ab8b3…
|
drh
|
3203 |
p->nFrac--; |
|
b8ab8b3…
|
drh
|
3204 |
} |
|
b8ab8b3…
|
drh
|
3205 |
} |
|
b8ab8b3…
|
drh
|
3206 |
memset(&p->a[N], 0, p->nDigit - N); |
|
b8ab8b3…
|
drh
|
3207 |
} |
|
b8ab8b3…
|
drh
|
3208 |
|
|
b8ab8b3…
|
drh
|
3209 |
/* |
|
b8ab8b3…
|
drh
|
3210 |
static void decimal_result_sci(sqlite3_context *pCtx, Decimal *p, int N){ |
|
b8ab8b3…
|
drh
|
3211 |
if( N<1 ) N = 0; |
|
b8ab8b3…
|
drh
|
3212 |
for(nDigit=p->nDigit; nDigit>N && p->a[nDigit-1]==0; nDigit--){} |
|
3c639f7…
|
drh
|
3213 |
z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 ); |
|
17f9878…
|
drh
|
3214 |
signed char *a; |
|
17f9878…
|
drh
|
3215 |
if( nDigit+1>SQLITE_DECIMAL_MAX_DIGIT ){ p->oom = 1; return; } |
|
17f9878…
|
drh
|
3216 |
a = sqlite3_realloc64(p->a, nDigit+1); |
|
17f9878…
|
drh
|
3217 |
if( a==0 ){ |
|
17f9878…
|
drh
|
3218 |
p->a = a; |
|
17f9878…
|
drh
|
3219 |
sqlite3_int64 sumDigit; |
|
17f9878…
|
drh
|
3220 |
sumDigit = pA->nDigit; |
|
17f9878…
|
drh
|
3221 |
sumDigit += pB->nDigit; |
|
17f9878…
|
drh
|
3222 |
sumDigit += 2; |
|
17f9878…
|
drh
|
3223 |
if( sumDigit>SQLITE_DECIMAL_MAX_DIGIT ){ pA->oom = 1; return; } |
|
17f9878…
|
drh
|
3224 |
acc = sqlite3_malloc64( sumDigit ); |
|
b8ab8b3…
|
drh
|
3225 |
int N; |
|
b8ab8b3…
|
drh
|
3226 |
if( argc==2 ){ |
|
b8ab8b3…
|
drh
|
3227 |
N = sqlite3_value_int(argv[1]); |
|
b8ab8b3…
|
drh
|
3228 |
if( N>0 ) decimal_round(p, N); |
|
b8ab8b3…
|
drh
|
3229 |
}else{ |
|
b8ab8b3…
|
drh
|
3230 |
N = 0; |
|
b8ab8b3…
|
drh
|
3231 |
} |
|
b8ab8b3…
|
drh
|
3232 |
decimal_result_sci(context, p, N); |
|
0201a1e…
|
drh
|
3233 |
p->a = sqlite3_malloc64(2); |
|
b8ab8b3…
|
drh
|
3234 |
decimal_result_sci(context, pA, 0); |
|
b8ab8b3…
|
drh
|
3235 |
{ "decimal", 2, 0, decimalFunc }, |
|
b8ab8b3…
|
drh
|
3236 |
{ "decimal_exp", 2, 1, decimalFunc }, |
|
4f76459…
|
drh
|
3237 |
/************************* End ext/misc/decimal.c ********************/ |
|
4f76459…
|
drh
|
3238 |
/************************* Begin ext/misc/base64.c ******************/ |
|
70539ee…
|
drh
|
3239 |
sqlite3_int64 nb; |
|
70539ee…
|
drh
|
3240 |
sqlite3_int64 nv = sqlite3_value_bytes(av[0]); |
|
0201a1e…
|
drh
|
3241 |
cBuf = sqlite3_malloc64(nc); |
|
0201a1e…
|
drh
|
3242 |
bBuf = sqlite3_malloc64(nb); |
|
17f9878…
|
drh
|
3243 |
int sqlite3_base64_init |
|
4f76459…
|
drh
|
3244 |
/************************* End ext/misc/base64.c ********************/ |
|
4f76459…
|
drh
|
3245 |
/************************* Begin ext/misc/base85.c ******************/ |
|
17f9878…
|
drh
|
3246 |
#ifndef OMIT_BASE85_CHECKER |
|
17f9878…
|
drh
|
3247 |
#endif |
|
70539ee…
|
drh
|
3248 |
sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0]); |
|
0201a1e…
|
drh
|
3249 |
cBuf = sqlite3_malloc64(nc); |
|
0201a1e…
|
drh
|
3250 |
bBuf = sqlite3_malloc64(nb); |
|
17f9878…
|
drh
|
3251 |
int sqlite3_base85_init |
|
17f9878…
|
drh
|
3252 |
#ifndef OMIT_BASE85_CHECKER |
|
17f9878…
|
drh
|
3253 |
#endif |
|
17f9878…
|
drh
|
3254 |
#ifndef OMIT_BASE85_CHECKER |
|
17f9878…
|
drh
|
3255 |
#endif |
|
4f76459…
|
drh
|
3256 |
/************************* End ext/misc/base85.c ********************/ |
|
4f76459…
|
drh
|
3257 |
/************************* Begin ext/misc/ieee754.c ******************/ |
|
b9ecacf…
|
drh
|
3258 |
if( m<(-9223372036854775807LL) ) return; |
|
b8ab8b3…
|
drh
|
3259 |
** Functions to convert between 64-bit integers and floats. |
|
b8ab8b3…
|
drh
|
3260 |
** |
|
b8ab8b3…
|
drh
|
3261 |
** The bit patterns are copied. The numeric values are different. |
|
b8ab8b3…
|
drh
|
3262 |
*/ |
|
b8ab8b3…
|
drh
|
3263 |
static void ieee754func_from_int( |
|
b8ab8b3…
|
drh
|
3264 |
sqlite3_context *context, |
|
b8ab8b3…
|
drh
|
3265 |
int argc, |
|
b8ab8b3…
|
drh
|
3266 |
sqlite3_value **argv |
|
b8ab8b3…
|
drh
|
3267 |
){ |
|
b8ab8b3…
|
drh
|
3268 |
UNUSED_PARAMETER(argc); |
|
b8ab8b3…
|
drh
|
3269 |
if( sqlite3_value_type(argv[0])==SQLITE_INTEGER ){ |
|
b8ab8b3…
|
drh
|
3270 |
double r; |
|
b8ab8b3…
|
drh
|
3271 |
sqlite3_int64 v = sqlite3_value_int64(argv[0]); |
|
b8ab8b3…
|
drh
|
3272 |
memcpy(&r, &v, sizeof(r)); |
|
b8ab8b3…
|
drh
|
3273 |
sqlite3_result_double(context, r); |
|
b8ab8b3…
|
drh
|
3274 |
} |
|
b8ab8b3…
|
drh
|
3275 |
} |
|
b8ab8b3…
|
drh
|
3276 |
static void ieee754func_to_int( |
|
b8ab8b3…
|
drh
|
3277 |
sqlite3_context *context, |
|
b8ab8b3…
|
drh
|
3278 |
int argc, |
|
b8ab8b3…
|
drh
|
3279 |
sqlite3_value **argv |
|
b8ab8b3…
|
drh
|
3280 |
){ |
|
b8ab8b3…
|
drh
|
3281 |
UNUSED_PARAMETER(argc); |
|
b8ab8b3…
|
drh
|
3282 |
if( sqlite3_value_type(argv[0])==SQLITE_FLOAT ){ |
|
b8ab8b3…
|
drh
|
3283 |
double r = sqlite3_value_double(argv[0]); |
|
b8ab8b3…
|
drh
|
3284 |
sqlite3_uint64 v; |
|
b8ab8b3…
|
drh
|
3285 |
memcpy(&v, &r, sizeof(v)); |
|
b8ab8b3…
|
drh
|
3286 |
sqlite3_result_int64(context, v); |
|
b8ab8b3…
|
drh
|
3287 |
} |
|
b8ab8b3…
|
drh
|
3288 |
} |
|
b8ab8b3…
|
drh
|
3289 |
|
|
b8ab8b3…
|
drh
|
3290 |
/* |
|
b8ab8b3…
|
drh
|
3291 |
{ "ieee754_to_int", 1, 0, ieee754func_to_int }, |
|
b8ab8b3…
|
drh
|
3292 |
{ "ieee754_from_int", 1, 0, ieee754func_from_int }, |
|
4f76459…
|
drh
|
3293 |
/************************* End ext/misc/ieee754.c ********************/ |
|
4f76459…
|
drh
|
3294 |
/************************* Begin ext/misc/series.c ******************/ |
|
0201a1e…
|
drh
|
3295 |
pNew = *ppVtab = sqlite3_malloc64( sizeof(*pNew) ); |
|
0201a1e…
|
drh
|
3296 |
pCur = sqlite3_malloc64( sizeof(*pCur) ); |
|
3c639f7…
|
drh
|
3297 |
|
|
3c639f7…
|
drh
|
3298 |
#if defined(SQLITE_ENABLE_MATH_FUNCTIONS) || defined(_WIN32) |
|
3c639f7…
|
drh
|
3299 |
/* |
|
3c639f7…
|
drh
|
3300 |
** Case 1 (the most common case): |
|
3c639f7…
|
drh
|
3301 |
** The standard math library is available so use ceil() and floor() from there. |
|
3c639f7…
|
drh
|
3302 |
*/ |
|
3c639f7…
|
drh
|
3303 |
static double seriesCeil(double r){ return ceil(r); } |
|
3c639f7…
|
drh
|
3304 |
static double seriesFloor(double r){ return floor(r); } |
|
3c639f7…
|
drh
|
3305 |
#elif defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC) |
|
3c639f7…
|
drh
|
3306 |
/* |
|
3c639f7…
|
drh
|
3307 |
** Case 2 (2nd most common): Use GCC/Clang builtins |
|
3c639f7…
|
drh
|
3308 |
*/ |
|
3c639f7…
|
drh
|
3309 |
static double seriesCeil(double r){ return __builtin_ceil(r); } |
|
3c639f7…
|
drh
|
3310 |
static double seriesFloor(double r){ return __builtin_floor(r); } |
|
3c639f7…
|
drh
|
3311 |
#else |
|
3c639f7…
|
drh
|
3312 |
/* |
|
3c639f7…
|
drh
|
3313 |
** Case 3 (rarely happens): Use home-grown ceil() and floor() routines. |
|
3c639f7…
|
drh
|
3314 |
*/ |
|
3c639f7…
|
drh
|
3315 |
static double seriesCeil(double r){ |
|
3c639f7…
|
drh
|
3316 |
sqlite3_int64 x; |
|
3c639f7…
|
drh
|
3317 |
if( r!=r ) return r; |
|
3c639f7…
|
drh
|
3318 |
if( r<=(-4503599627370496.0) ) return r; |
|
3c639f7…
|
drh
|
3319 |
if( r>=(+4503599627370496.0) ) return r; |
|
3c639f7…
|
drh
|
3320 |
x = (sqlite3_int64)r; |
|
3c639f7…
|
drh
|
3321 |
if( r==(double)x ) return r; |
|
3c639f7…
|
drh
|
3322 |
if( r>(double)x ) x++; |
|
3c639f7…
|
drh
|
3323 |
return (double)x; |
|
3c639f7…
|
drh
|
3324 |
} |
|
3c639f7…
|
drh
|
3325 |
static double seriesFloor(double r){ |
|
3c639f7…
|
drh
|
3326 |
sqlite3_int64 x; |
|
3c639f7…
|
drh
|
3327 |
if( r!=r ) return r; |
|
3c639f7…
|
drh
|
3328 |
if( r<=(-4503599627370496.0) ) return r; |
|
3c639f7…
|
drh
|
3329 |
if( r>=(+4503599627370496.0) ) return r; |
|
3c639f7…
|
drh
|
3330 |
x = (sqlite3_int64)r; |
|
3c639f7…
|
drh
|
3331 |
if( r==(double)x ) return r; |
|
3c639f7…
|
drh
|
3332 |
if( r<(double)x ) x--; |
|
3c639f7…
|
drh
|
3333 |
return (double)x; |
|
3c639f7…
|
drh
|
3334 |
} |
|
3c639f7…
|
drh
|
3335 |
#endif |
|
3c639f7…
|
drh
|
3336 |
if( r==seriesCeil(r) |
|
3c639f7…
|
drh
|
3337 |
}else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){ |
|
3c639f7…
|
drh
|
3338 |
iMin = (sqlite3_int64)seriesCeil(r+1.0); |
|
3c639f7…
|
drh
|
3339 |
iMin = (sqlite3_int64)seriesCeil(r); |
|
3c639f7…
|
drh
|
3340 |
}else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){ |
|
3c639f7…
|
drh
|
3341 |
iMax = (sqlite3_int64)seriesFloor(r); |
|
3c639f7…
|
drh
|
3342 |
if( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){ |
|
4f76459…
|
drh
|
3343 |
/************************* End ext/misc/series.c ********************/ |
|
4f76459…
|
drh
|
3344 |
/************************* Begin ext/misc/regexp.c ******************/ |
|
2b2530d…
|
drh
|
3345 |
** \c Character c where c is one of \{}()[]|*+?-. |
|
2b2530d…
|
drh
|
3346 |
static const char zEsc[] = "afnrtv\\()*.+?[$^{|}]-"; |
|
92871e0…
|
drh
|
3347 |
** Version of re_free() that accepts a pointer of type (void*). Required |
|
92871e0…
|
drh
|
3348 |
** to satisfy sanitizers when the re_free() function is called via a |
|
92871e0…
|
drh
|
3349 |
** function pointer. |
|
92871e0…
|
drh
|
3350 |
*/ |
|
92871e0…
|
drh
|
3351 |
static void re_free_voidptr(void *p){ |
|
92871e0…
|
drh
|
3352 |
re_free((ReCompiled*)p); |
|
92871e0…
|
drh
|
3353 |
} |
|
92871e0…
|
drh
|
3354 |
|
|
92871e0…
|
drh
|
3355 |
/* |
|
0201a1e…
|
drh
|
3356 |
pRe = sqlite3_malloc64( sizeof(*pRe) ); |
|
5f65ed5…
|
drh
|
3357 |
** The value of LIMIT_MAX_PATTERN_LENGTH. |
|
5f65ed5…
|
drh
|
3358 |
return sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1); |
|
5f65ed5…
|
drh
|
3359 |
} |
|
5f65ed5…
|
drh
|
3360 |
|
|
5f65ed5…
|
drh
|
3361 |
/* |
|
5f65ed5…
|
drh
|
3362 |
** Maximum NFA size given a maximum pattern length. |
|
5f65ed5…
|
drh
|
3363 |
*/ |
|
5f65ed5…
|
drh
|
3364 |
static int re_maxnfa(int mxlen){ |
|
5f65ed5…
|
drh
|
3365 |
return 75+mxlen/2; |
|
5f65ed5…
|
drh
|
3366 |
int mxLen = re_maxlen(context); |
|
5f65ed5…
|
drh
|
3367 |
int nPattern; |
|
5f65ed5…
|
drh
|
3368 |
nPattern = sqlite3_value_bytes(argv[0]); |
|
5f65ed5…
|
drh
|
3369 |
if( nPattern>mxLen ){ |
|
5f65ed5…
|
drh
|
3370 |
zErr = "REGEXP pattern too big"; |
|
5f65ed5…
|
drh
|
3371 |
}else{ |
|
5f65ed5…
|
drh
|
3372 |
zErr = re_compile(&pRe, zPattern, re_maxnfa(mxLen), |
|
5f65ed5…
|
drh
|
3373 |
sqlite3_user_data(context)!=0); |
|
5f65ed5…
|
drh
|
3374 |
} |
|
92871e0…
|
drh
|
3375 |
sqlite3_set_auxdata(context, 0, pRe, re_free_voidptr); |
|
17f9878…
|
drh
|
3376 |
(void)argc; |
|
5f65ed5…
|
drh
|
3377 |
zErr = re_compile(&pRe, zPattern, re_maxnfa(re_maxlen(context)), |
|
4f76459…
|
drh
|
3378 |
/************************* End ext/misc/regexp.c ********************/ |
|
4f76459…
|
drh
|
3379 |
/************************* Begin ext/misc/fileio.c ******************/ |
|
b8ab8b3…
|
drh
|
3380 |
# include <limits.h> |
|
b8ab8b3…
|
drh
|
3381 |
# include <stdlib.h> |
|
b8ab8b3…
|
drh
|
3382 |
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char*); |
|
b8ab8b3…
|
drh
|
3383 |
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); |
|
b8ab8b3…
|
drh
|
3384 |
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath); |
|
b8ab8b3…
|
drh
|
3385 |
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath); |
|
b8ab8b3…
|
drh
|
3386 |
#endif /* _WIN32 */ |
|
b8ab8b3…
|
drh
|
3387 |
wchar_t *b1 = sqlite3_win32_utf8_to_unicode(zPath); |
|
b8ab8b3…
|
drh
|
3388 |
if( rc==0 ){ |
|
b8ab8b3…
|
drh
|
3389 |
HANDLE hFindFile; |
|
b8ab8b3…
|
drh
|
3390 |
WIN32_FIND_DATAW fd; |
|
b8ab8b3…
|
drh
|
3391 |
memset(&fd, 0, sizeof(WIN32_FIND_DATAW)); |
|
b8ab8b3…
|
drh
|
3392 |
hFindFile = FindFirstFileW(b1, &fd); |
|
b8ab8b3…
|
drh
|
3393 |
if( hFindFile!=NULL ){ |
|
b8ab8b3…
|
drh
|
3394 |
pStatBuf->st_ctime = (time_t)fileTimeToUnixTime(&fd.ftCreationTime); |
|
b8ab8b3…
|
drh
|
3395 |
pStatBuf->st_atime = (time_t)fileTimeToUnixTime(&fd.ftLastAccessTime); |
|
b8ab8b3…
|
drh
|
3396 |
pStatBuf->st_mtime = (time_t)fileTimeToUnixTime(&fd.ftLastWriteTime); |
|
b8ab8b3…
|
drh
|
3397 |
FindClose(hFindFile); |
|
b8ab8b3…
|
drh
|
3398 |
} |
|
b8ab8b3…
|
drh
|
3399 |
} |
|
709b566…
|
drh
|
3400 |
sqlite3_free(b1); |
|
0201a1e…
|
drh
|
3401 |
pNew = (fsdir_tab*)sqlite3_malloc64( sizeof(*pNew) ); |
|
0201a1e…
|
drh
|
3402 |
pCur = sqlite3_malloc64( sizeof(*pCur) ); |
|
92871e0…
|
drh
|
3403 |
fsdirSetErrmsg(pCur, "cannot read directory: %s", pLvl->zDir); |
|
b8ab8b3…
|
drh
|
3404 |
/* |
|
b8ab8b3…
|
drh
|
3405 |
** This version of realpath() works on any system. The string |
|
b8ab8b3…
|
drh
|
3406 |
** returned is held in memory allocated using sqlite3_malloc64(). |
|
b8ab8b3…
|
drh
|
3407 |
** The caller is responsible for calling sqlite3_free(). |
|
b8ab8b3…
|
drh
|
3408 |
*/ |
|
b8ab8b3…
|
drh
|
3409 |
static char *portable_realpath(const char *zPath){ |
|
b8ab8b3…
|
drh
|
3410 |
#if !defined(_WIN32) /* BEGIN unix */ |
|
b8ab8b3…
|
drh
|
3411 |
|
|
b8ab8b3…
|
drh
|
3412 |
char *zOut = 0; /* Result */ |
|
b8ab8b3…
|
drh
|
3413 |
char *z; /* Temporary buffer */ |
|
b8ab8b3…
|
drh
|
3414 |
#if defined(PATH_MAX) |
|
b8ab8b3…
|
drh
|
3415 |
char zBuf[PATH_MAX+1]; /* Space for the temporary buffer */ |
|
b8ab8b3…
|
drh
|
3416 |
#endif |
|
b8ab8b3…
|
drh
|
3417 |
|
|
b8ab8b3…
|
drh
|
3418 |
if( zPath==0 ) return 0; |
|
b8ab8b3…
|
drh
|
3419 |
#if defined(PATH_MAX) |
|
b8ab8b3…
|
drh
|
3420 |
z = realpath(zPath, zBuf); |
|
b8ab8b3…
|
drh
|
3421 |
if( z ){ |
|
b8ab8b3…
|
drh
|
3422 |
zOut = sqlite3_mprintf("%s", zBuf); |
|
b8ab8b3…
|
drh
|
3423 |
} |
|
b8ab8b3…
|
drh
|
3424 |
#endif /* defined(PATH_MAX) */ |
|
b8ab8b3…
|
drh
|
3425 |
if( zOut==0 ){ |
|
b8ab8b3…
|
drh
|
3426 |
/* Try POSIX.1-2008 malloc behavior */ |
|
b8ab8b3…
|
drh
|
3427 |
z = realpath(zPath, NULL); |
|
b8ab8b3…
|
drh
|
3428 |
if( z ){ |
|
b8ab8b3…
|
drh
|
3429 |
zOut = sqlite3_mprintf("%s", z); |
|
b8ab8b3…
|
drh
|
3430 |
free(z); |
|
b8ab8b3…
|
drh
|
3431 |
} |
|
b8ab8b3…
|
drh
|
3432 |
} |
|
b8ab8b3…
|
drh
|
3433 |
return zOut; |
|
b8ab8b3…
|
drh
|
3434 |
|
|
b8ab8b3…
|
drh
|
3435 |
#else /* End UNIX, Begin WINDOWS */ |
|
b8ab8b3…
|
drh
|
3436 |
|
|
b8ab8b3…
|
drh
|
3437 |
wchar_t *zPath16; /* UTF16 translation of zPath */ |
|
b8ab8b3…
|
drh
|
3438 |
char *zOut = 0; /* Result */ |
|
b8ab8b3…
|
drh
|
3439 |
wchar_t *z = 0; /* Temporary buffer */ |
|
b8ab8b3…
|
drh
|
3440 |
|
|
b8ab8b3…
|
drh
|
3441 |
if( zPath==0 ) return 0; |
|
b8ab8b3…
|
drh
|
3442 |
|
|
b8ab8b3…
|
drh
|
3443 |
zPath16 = sqlite3_win32_utf8_to_unicode(zPath); |
|
b8ab8b3…
|
drh
|
3444 |
if( zPath16==0 ) return 0; |
|
b8ab8b3…
|
drh
|
3445 |
z = _wfullpath(NULL, zPath16, 0); |
|
b8ab8b3…
|
drh
|
3446 |
sqlite3_free(zPath16); |
|
b8ab8b3…
|
drh
|
3447 |
if( z ){ |
|
b8ab8b3…
|
drh
|
3448 |
zOut = sqlite3_win32_unicode_to_utf8(z); |
|
b8ab8b3…
|
drh
|
3449 |
free(z); |
|
b8ab8b3…
|
drh
|
3450 |
} |
|
b8ab8b3…
|
drh
|
3451 |
return zOut; |
|
b8ab8b3…
|
drh
|
3452 |
|
|
b8ab8b3…
|
drh
|
3453 |
#endif /* End WINDOWS, Begin common code */ |
|
b8ab8b3…
|
drh
|
3454 |
} |
|
b8ab8b3…
|
drh
|
3455 |
|
|
b8ab8b3…
|
drh
|
3456 |
/* |
|
b8ab8b3…
|
drh
|
3457 |
** SQL function: realpath(X) |
|
b8ab8b3…
|
drh
|
3458 |
** |
|
b8ab8b3…
|
drh
|
3459 |
** Try to convert file or pathname X into its real, absolute pathname. |
|
b8ab8b3…
|
drh
|
3460 |
** Return NULL if unable. |
|
b8ab8b3…
|
drh
|
3461 |
** |
|
b8ab8b3…
|
drh
|
3462 |
** The file or directory X is not required to exist. The answer is formed |
|
b8ab8b3…
|
drh
|
3463 |
** by calling system realpath() on the prefix of X that does exist and |
|
b8ab8b3…
|
drh
|
3464 |
** appending the tail of X that does not (yet) exist. |
|
b8ab8b3…
|
drh
|
3465 |
*/ |
|
b8ab8b3…
|
drh
|
3466 |
static void realpathFunc( |
|
b8ab8b3…
|
drh
|
3467 |
sqlite3_context *context, |
|
b8ab8b3…
|
drh
|
3468 |
int argc, |
|
b8ab8b3…
|
drh
|
3469 |
sqlite3_value **argv |
|
b8ab8b3…
|
drh
|
3470 |
){ |
|
b8ab8b3…
|
drh
|
3471 |
const char *zPath; /* Original input path */ |
|
b8ab8b3…
|
drh
|
3472 |
char *zCopy; /* An editable copy of zPath */ |
|
b8ab8b3…
|
drh
|
3473 |
char *zOut; /* The result */ |
|
b8ab8b3…
|
drh
|
3474 |
char cSep = 0; /* Separator turned into \000 */ |
|
b8ab8b3…
|
drh
|
3475 |
size_t len; /* Prefix length before cSep */ |
|
b8ab8b3…
|
drh
|
3476 |
#ifdef _WIN32 |
|
b8ab8b3…
|
drh
|
3477 |
const int isWin = 1; |
|
b8ab8b3…
|
drh
|
3478 |
#else |
|
b8ab8b3…
|
drh
|
3479 |
const int isWin = 0; |
|
b8ab8b3…
|
drh
|
3480 |
#endif |
|
b8ab8b3…
|
drh
|
3481 |
|
|
b8ab8b3…
|
drh
|
3482 |
(void)argc; |
|
b8ab8b3…
|
drh
|
3483 |
zPath = (const char*)sqlite3_value_text(argv[0]); |
|
b8ab8b3…
|
drh
|
3484 |
if( zPath==0 ) return; |
|
b8ab8b3…
|
drh
|
3485 |
if( zPath[0]==0 ) zPath = "."; |
|
b8ab8b3…
|
drh
|
3486 |
zCopy = sqlite3_mprintf("%s",zPath); |
|
b8ab8b3…
|
drh
|
3487 |
len = strlen(zCopy); |
|
b8ab8b3…
|
drh
|
3488 |
while( len>1 && (zCopy[len-1]=='/' || (isWin && zCopy[len-1]=='\\')) ){ |
|
b8ab8b3…
|
drh
|
3489 |
len--; |
|
b8ab8b3…
|
drh
|
3490 |
} |
|
b8ab8b3…
|
drh
|
3491 |
zCopy[len] = 0; |
|
b8ab8b3…
|
drh
|
3492 |
while( 1 /*exit-by-break*/ ){ |
|
b8ab8b3…
|
drh
|
3493 |
zOut = portable_realpath(zCopy); |
|
b8ab8b3…
|
drh
|
3494 |
zCopy[len] = cSep; |
|
b8ab8b3…
|
drh
|
3495 |
if( zOut ){ |
|
b8ab8b3…
|
drh
|
3496 |
if( cSep ){ |
|
b8ab8b3…
|
drh
|
3497 |
zOut = sqlite3_mprintf("%z%s",zOut,&zCopy[len]); |
|
b8ab8b3…
|
drh
|
3498 |
} |
|
b8ab8b3…
|
drh
|
3499 |
break; |
|
b8ab8b3…
|
drh
|
3500 |
}else{ |
|
b8ab8b3…
|
drh
|
3501 |
size_t i = len-1; |
|
b8ab8b3…
|
drh
|
3502 |
while( i>0 ){ |
|
b8ab8b3…
|
drh
|
3503 |
if( zCopy[i]=='/' || (isWin && zCopy[i]=='\\') ) break; |
|
b8ab8b3…
|
drh
|
3504 |
i--; |
|
b8ab8b3…
|
drh
|
3505 |
} |
|
b8ab8b3…
|
drh
|
3506 |
if( i<=0 ){ |
|
b8ab8b3…
|
drh
|
3507 |
if( zCopy[0]=='/' ){ |
|
b8ab8b3…
|
drh
|
3508 |
zOut = zCopy; |
|
b8ab8b3…
|
drh
|
3509 |
zCopy = 0; |
|
b8ab8b3…
|
drh
|
3510 |
}else if( (zOut = portable_realpath("."))!=0 ){ |
|
b8ab8b3…
|
drh
|
3511 |
zOut = sqlite3_mprintf("%z/%s", zOut, zCopy); |
|
b8ab8b3…
|
drh
|
3512 |
} |
|
b8ab8b3…
|
drh
|
3513 |
break; |
|
b8ab8b3…
|
drh
|
3514 |
} |
|
b8ab8b3…
|
drh
|
3515 |
cSep = zCopy[i]; |
|
b8ab8b3…
|
drh
|
3516 |
zCopy[i] = 0; |
|
b8ab8b3…
|
drh
|
3517 |
len = i; |
|
b8ab8b3…
|
drh
|
3518 |
} |
|
b8ab8b3…
|
drh
|
3519 |
} |
|
b8ab8b3…
|
drh
|
3520 |
sqlite3_free(zCopy); |
|
b8ab8b3…
|
drh
|
3521 |
if( zOut ){ |
|
b8ab8b3…
|
drh
|
3522 |
/* Simplify any "/./" or "/../" that might have snuck into the |
|
b8ab8b3…
|
drh
|
3523 |
** pathname due to appending of zCopy. We only have to consider |
|
b8ab8b3…
|
drh
|
3524 |
** unix "/" separators, because the _wfilepath() system call on |
|
b8ab8b3…
|
drh
|
3525 |
** Windows will have already done this simplification for us. */ |
|
b8ab8b3…
|
drh
|
3526 |
size_t i, j, n; |
|
b8ab8b3…
|
drh
|
3527 |
n = strlen(zOut); |
|
b8ab8b3…
|
drh
|
3528 |
for(i=j=0; i<n; i++){ |
|
b8ab8b3…
|
drh
|
3529 |
if( zOut[i]=='/' ){ |
|
b8ab8b3…
|
drh
|
3530 |
if( zOut[i+1]=='/' ) continue; |
|
b8ab8b3…
|
drh
|
3531 |
if( zOut[i+1]=='.' && i+2<n && zOut[i+2]=='/' ){ |
|
b8ab8b3…
|
drh
|
3532 |
i += 1; |
|
b8ab8b3…
|
drh
|
3533 |
continue; |
|
b8ab8b3…
|
drh
|
3534 |
} |
|
b8ab8b3…
|
drh
|
3535 |
if( zOut[i+1]=='.' && i+3<n && zOut[i+2]=='.' && zOut[i+3]=='/' ){ |
|
b8ab8b3…
|
drh
|
3536 |
while( j>0 && zOut[j-1]!='/' ){ j--; } |
|
b8ab8b3…
|
drh
|
3537 |
if( j>0 ){ j--; } |
|
b8ab8b3…
|
drh
|
3538 |
i += 2; |
|
b8ab8b3…
|
drh
|
3539 |
continue; |
|
b8ab8b3…
|
drh
|
3540 |
} |
|
b8ab8b3…
|
drh
|
3541 |
} |
|
b8ab8b3…
|
drh
|
3542 |
zOut[j++] = zOut[i]; |
|
b8ab8b3…
|
drh
|
3543 |
} |
|
b8ab8b3…
|
drh
|
3544 |
zOut[j] = 0; |
|
b8ab8b3…
|
drh
|
3545 |
|
|
b8ab8b3…
|
drh
|
3546 |
/* Return the result */ |
|
b8ab8b3…
|
drh
|
3547 |
sqlite3_result_text(context, zOut, -1, sqlite3_free); |
|
b8ab8b3…
|
drh
|
3548 |
} |
|
b8ab8b3…
|
drh
|
3549 |
} |
|
b8ab8b3…
|
drh
|
3550 |
|
|
b8ab8b3…
|
drh
|
3551 |
|
|
b8ab8b3…
|
drh
|
3552 |
} |
|
b8ab8b3…
|
drh
|
3553 |
if( rc==SQLITE_OK ){ |
|
b8ab8b3…
|
drh
|
3554 |
rc = sqlite3_create_function(db, "realpath", 1, |
|
b8ab8b3…
|
drh
|
3555 |
SQLITE_UTF8, 0, |
|
b8ab8b3…
|
drh
|
3556 |
realpathFunc, 0, 0); |
|
4f76459…
|
drh
|
3557 |
} |
|
4f76459…
|
drh
|
3558 |
/************************* End ext/misc/fileio.c ********************/ |
|
4f76459…
|
drh
|
3559 |
/************************* Begin ext/misc/completion.c ******************/ |
|
0201a1e…
|
drh
|
3560 |
pNew = sqlite3_malloc64( sizeof(*pNew) ); |
|
0201a1e…
|
drh
|
3561 |
pCur = sqlite3_malloc64( sizeof(*pCur) ); |
|
17f9878…
|
drh
|
3562 |
int rc; |
|
17f9878…
|
drh
|
3563 |
sqlite3_str* pStr = sqlite3_str_new(pCur->db); |
|
17f9878…
|
drh
|
3564 |
sqlite3_str_appendf(pStr, |
|
17f9878…
|
drh
|
3565 |
"%s" |
|
17f9878…
|
drh
|
3566 |
zSep, zDb |
|
17f9878…
|
drh
|
3567 |
rc = sqlite3_finalize(pS2); |
|
17f9878…
|
drh
|
3568 |
zSql = sqlite3_str_finish(pStr); |
|
17f9878…
|
drh
|
3569 |
if( zSql==0 ) return SQLITE_NOMEM; |
|
17f9878…
|
drh
|
3570 |
if( rc==SQLITE_OK ){ |
|
17f9878…
|
drh
|
3571 |
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); |
|
17f9878…
|
drh
|
3572 |
} |
|
17f9878…
|
drh
|
3573 |
if( rc ) return rc; |
|
17f9878…
|
drh
|
3574 |
sqlite3_str *pStr = sqlite3_str_new(pCur->db); |
|
17f9878…
|
drh
|
3575 |
sqlite3_str_appendf(pStr, |
|
17f9878…
|
drh
|
3576 |
"%s" |
|
17f9878…
|
drh
|
3577 |
zSep, zDb, zDb |
|
17f9878…
|
drh
|
3578 |
rc = sqlite3_finalize(pS2); |
|
17f9878…
|
drh
|
3579 |
zSql = sqlite3_str_finish(pStr); |
|
17f9878…
|
drh
|
3580 |
if( zSql==0 ) return SQLITE_NOMEM; |
|
17f9878…
|
drh
|
3581 |
if( rc==SQLITE_OK ){ |
|
17f9878…
|
drh
|
3582 |
sqlite3_prepare_v2(pCur->db, zSql, -1, &pCur->pStmt, 0); |
|
17f9878…
|
drh
|
3583 |
} |
|
17f9878…
|
drh
|
3584 |
if( rc ) return rc; |
|
17f9878…
|
drh
|
3585 |
rc = sqlite3_finalize(pCur->pStmt); |
|
17f9878…
|
drh
|
3586 |
if( rc ) return rc; |
|
4f76459…
|
drh
|
3587 |
/************************* End ext/misc/completion.c ********************/ |
|
4f76459…
|
drh
|
3588 |
/************************* Begin ext/misc/appendvfs.c ******************/ |
|
4f76459…
|
drh
|
3589 |
/************************* End ext/misc/appendvfs.c ********************/ |
|
4f76459…
|
drh
|
3590 |
/************************* Begin ext/misc/zipfile.c ******************/ |
|
13c221a…
|
drh
|
3591 |
pNew = (ZipfileTab*)sqlite3_malloc64((i64)nByte+nFile); |
|
0201a1e…
|
drh
|
3592 |
pCsr = sqlite3_malloc64(sizeof(*pCsr)); |
|
13c221a…
|
drh
|
3593 |
i64 nRead, /* Number of bytes to read */ |
|
13c221a…
|
drh
|
3594 |
n = fread(aRead, 1, (long)nRead, pFile); |
|
13c221a…
|
drh
|
3595 |
if( n!=(size_t)nRead ){ |
|
13c221a…
|
drh
|
3596 |
sqlite3_free(*pzErrmsg); |
|
13c221a…
|
drh
|
3597 |
zipfileTableErr(pTab,"error in fwrite()"); |
|
ba8756a…
|
drh
|
3598 |
/* Stop when there are less than 9 bytes left to scan in the buffer. This |
|
ba8756a…
|
drh
|
3599 |
** is because the timestamp field requires exactly 9 bytes - 4 bytes of |
|
ba8756a…
|
drh
|
3600 |
** header fields and 5 bytes of data. If there are less than 9 bytes |
|
ba8756a…
|
drh
|
3601 |
** remaining, either it is some other field or else the extra data |
|
ba8756a…
|
drh
|
3602 |
** is corrupt. Either way, do not process it. */ |
|
ba8756a…
|
drh
|
3603 |
while( p+(2*sizeof(u16) + 1 + sizeof(u32))<=pEnd ){ |
|
13c221a…
|
drh
|
3604 |
sqlite3_free(*pzErr); |
|
13c221a…
|
drh
|
3605 |
i64 nBlob, /* Size of aBlob[] in bytes */ |
|
13c221a…
|
drh
|
3606 |
zipfileTableErr(pTab, "failed to read CDS at offset %lld", iOff); |
|
12e4a0f…
|
drh
|
3607 |
if( (iOff + ZIPFILE_CDS_FIXED_SZ + nFile + nExtra)>nBlob ){ |
|
13c221a…
|
drh
|
3608 |
if( ((i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){ |
|
d326547…
|
drh
|
3609 |
memset(&lfh, 0, sizeof(lfh)); |
|
13c221a…
|
drh
|
3610 |
pNew->iDataOff = (i64)pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ; |
|
13c221a…
|
drh
|
3611 |
zipfileTableErr(pTab, "failed to read LFH at offset %d", |
|
13c221a…
|
drh
|
3612 |
i64 iEof = (i64)pCsr->eocd.iOffset + (i64)pCsr->eocd.nSize; |
|
0201a1e…
|
drh
|
3613 |
u8 *aRes = sqlite3_malloc64(nOut); |
|
ae7e3f0…
|
drh
|
3614 |
sqlite3_result_blob(pCtx, aRes, (int)str.total_out, zipfileFree); |
|
13c221a…
|
drh
|
3615 |
i64 nBlob, /* Size of aBlob[] in bytes */ |
|
13c221a…
|
drh
|
3616 |
i64 nRead; /* Bytes to read from file */ |
|
13c221a…
|
drh
|
3617 |
i64 i; |
|
13c221a…
|
drh
|
3618 |
zipfileTableErr(pTab, "cannot find end of central directory record"); |
|
13c221a…
|
drh
|
3619 |
static int zipfileLoadDirectory(ZipfileTab *pTab, const u8 *aBlob, i64 nBlob){ |
|
13c221a…
|
drh
|
3620 |
i64 nBlob = sqlite3_value_bytes(argv[0]); |
|
0201a1e…
|
drh
|
3621 |
pNew = sqlite3_malloc64(sizeof(ZipfileEntry)); |
|
13c221a…
|
drh
|
3622 |
zipfileTableErr(pTab, "zipfile: missing filename"); |
|
13c221a…
|
drh
|
3623 |
zipfileTableErr(pTab, |
|
13c221a…
|
drh
|
3624 |
"zipfile: failed to open file %s for writing", pTab->zFile |
|
13c221a…
|
drh
|
3625 |
); |
|
13c221a…
|
drh
|
3626 |
static int zipfileBufferGrow(ZipfileBuffer *pBuf, i64 nByte){ |
|
13c221a…
|
drh
|
3627 |
i64 nByte; |
|
4f76459…
|
drh
|
3628 |
/************************* End ext/misc/zipfile.c ********************/ |
|
4f76459…
|
drh
|
3629 |
/************************* Begin ext/misc/sqlar.c ******************/ |
|
0201a1e…
|
drh
|
3630 |
pOut = (Bytef*)sqlite3_malloc64(nOut); |
|
17f9878…
|
drh
|
3631 |
sz = sqlite3_value_int64(argv[1]); |
|
0201a1e…
|
drh
|
3632 |
Bytef *pOut = sqlite3_malloc64(sz); |
|
4f76459…
|
drh
|
3633 |
/************************* End ext/misc/sqlar.c ********************/ |
|
4f76459…
|
drh
|
3634 |
/************************* Begin ext/expert/sqlite3expert.h ******************/ |
|
4f76459…
|
drh
|
3635 |
/************************* End ext/expert/sqlite3expert.h ********************/ |
|
4f76459…
|
drh
|
3636 |
/************************* Begin ext/expert/sqlite3expert.c ******************/ |
|
92871e0…
|
drh
|
3637 |
static void *idxMalloc(int *pRc, i64 nByte){ |
|
92871e0…
|
drh
|
3638 |
pRet = sqlite3_malloc64(nByte); |
|
92871e0…
|
drh
|
3639 |
pEntry = idxMalloc(pRc, sizeof(IdxHashEntry) + (i64)nKey+1 + (i64)nVal+1); |
|
92871e0…
|
drh
|
3640 |
i64 n = STRLEN(zIn); |
|
92871e0…
|
drh
|
3641 |
char *zRet = sqlite3_malloc64(n); |
|
92871e0…
|
drh
|
3642 |
i64 iOut = 0; |
|
92871e0…
|
drh
|
3643 |
i64 iIn = 0; |
|
92871e0…
|
drh
|
3644 |
i64 nByte; |
|
92871e0…
|
drh
|
3645 |
i64 nIn = zIn ? STRLEN(zIn) : 0; |
|
92871e0…
|
drh
|
3646 |
i64 nAppend = 0; |
|
92871e0…
|
drh
|
3647 |
zRet = (char*)sqlite3_malloc64(nIn + nAppend + 1); |
|
92871e0…
|
drh
|
3648 |
i64 nByte; /* Bytes of space allocated at z */ |
|
92871e0…
|
drh
|
3649 |
i64 n; /* Size of buffer z */ |
|
92871e0…
|
drh
|
3650 |
assert( pSlot->n <= 0x7fffffff ); |
|
92871e0…
|
drh
|
3651 |
sqlite3_result_blob(pCtx, pSlot->z, (int)pSlot->n, SQLITE_TRANSIENT); |
|
92871e0…
|
drh
|
3652 |
assert( pSlot->n <= 0x7fffffff ); |
|
92871e0…
|
drh
|
3653 |
sqlite3_result_text(pCtx, pSlot->z, (int)pSlot->n, SQLITE_TRANSIENT); |
|
92871e0…
|
drh
|
3654 |
i64 nByte = sqlite3_value_bytes(argv[1]); |
|
92871e0…
|
drh
|
3655 |
char *zNew = (char*)sqlite3_realloc64(pSlot->z, nByte*2); |
|
92871e0…
|
drh
|
3656 |
i64 *aStat = 0; |
|
92871e0…
|
drh
|
3657 |
aStat = (i64*)idxMalloc(&rc, sizeof(i64)*(nCol+1)); |
|
92871e0…
|
drh
|
3658 |
i64 s0 = aStat[0]; |
|
92871e0…
|
drh
|
3659 |
zStat = sqlite3_mprintf("%lld", s0); |
|
92871e0…
|
drh
|
3660 |
zStat = idxAppendText(&rc, zStat, " %lld", (s0+aStat[i]/2) / aStat[i]); |
|
92871e0…
|
drh
|
3661 |
i64 nByte = sizeof(struct IdxRemCtx) + (sizeof(struct IdxRemSlot) * nMax); |
|
92871e0…
|
drh
|
3662 |
pCtx->nSlot = (i64)nMax+1; |
|
92871e0…
|
drh
|
3663 |
i64 n = STRLEN(z); |
|
4f76459…
|
drh
|
3664 |
/************************* End ext/expert/sqlite3expert.c ********************/ |
|
4f76459…
|
drh
|
3665 |
/************************* Begin ext/intck/sqlite3intck.h ******************/ |
|
4f76459…
|
drh
|
3666 |
/************************* End ext/intck/sqlite3intck.h ********************/ |
|
4f76459…
|
drh
|
3667 |
/************************* Begin ext/intck/sqlite3intck.c ******************/ |
|
4f76459…
|
drh
|
3668 |
va_end(ap); |
|
ba8756a…
|
drh
|
3669 |
while( z[iRet] ){ |
|
4f76459…
|
drh
|
3670 |
/************************* End ext/intck/sqlite3intck.c ********************/ |
|
4f76459…
|
drh
|
3671 |
/************************* Begin ext/misc/stmtrand.c ******************/ |
|
0201a1e…
|
drh
|
3672 |
p = sqlite3_malloc64( sizeof(*p) ); |
|
4f76459…
|
drh
|
3673 |
/************************* End ext/misc/stmtrand.c ********************/ |
|
4f76459…
|
drh
|
3674 |
/************************* Begin ext/misc/vfstrace.c ******************/ |
|
0201a1e…
|
drh
|
3675 |
sqlite3_io_methods *pNew = sqlite3_malloc64( sizeof(*pNew) ); |
|
4f76459…
|
drh
|
3676 |
/************************* End ext/misc/vfstrace.c ********************/ |
|
4f76459…
|
drh
|
3677 |
/************************* Begin ext/recover/sqlite3recover.h ******************/ |
|
4f76459…
|
drh
|
3678 |
/************************* End ext/recover/sqlite3recover.h ********************/ |
|
4f76459…
|
drh
|
3679 |
/************************* Begin ext/recover/dbdata.c ******************/ |
|
4f76459…
|
drh
|
3680 |
/************************* End ext/recover/dbdata.c ********************/ |
|
4f76459…
|
drh
|
3681 |
/************************* Begin ext/recover/sqlite3recover.c ******************/ |
|
4f76459…
|
drh
|
3682 |
/************************* End ext/recover/sqlite3recover.c ********************/ |
|
4f76459…
|
drh
|
3683 |
/* All the parameters that determine how to render query results. |
|
4f76459…
|
drh
|
3684 |
*/ |
|
4f76459…
|
drh
|
3685 |
typedef struct Mode { |
|
4f76459…
|
drh
|
3686 |
u8 autoExplain; /* Automatically turn on .explain mode */ |
|
4f76459…
|
drh
|
3687 |
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to each SQL stmt */ |
|
4f76459…
|
drh
|
3688 |
u8 autoEQPtrace; /* autoEQP is in trace mode */ |
|
4f76459…
|
drh
|
3689 |
u8 scanstatsOn; /* True to display scan stats before each finalize */ |
|
4f76459…
|
drh
|
3690 |
u8 bAutoScreenWidth; /* Using the TTY to determine screen width */ |
|
12e4a0f…
|
drh
|
3691 |
u8 mFlags; /* MFLG_ECHO, MFLG_CRLF, etc. */ |
|
4f76459…
|
drh
|
3692 |
u8 eMode; /* One of the MODE_ values */ |
|
4f76459…
|
drh
|
3693 |
sqlite3_qrf_spec spec; /* Spec to be passed into QRF */ |
|
4f76459…
|
drh
|
3694 |
} Mode; |
|
4f76459…
|
drh
|
3695 |
|
|
4f76459…
|
drh
|
3696 |
/* Flags for Mode.mFlags */ |
|
4f76459…
|
drh
|
3697 |
#define MFLG_ECHO 0x01 /* Echo inputs to output */ |
|
4f76459…
|
drh
|
3698 |
#define MFLG_CRLF 0x02 /* Use CR/LF output line endings */ |
|
12e4a0f…
|
drh
|
3699 |
#define MFLG_HDR 0x04 /* .header used to change headers on/off */ |
|
4f76459…
|
drh
|
3700 |
|
|
4f76459…
|
drh
|
3701 |
u8 nPopOutput; /* Revert .output settings when reaching zero */ |
|
4f76459…
|
drh
|
3702 |
u8 nPopMode; /* Revert .mode settings when reaching zero */ |
|
7b0960d…
|
drh
|
3703 |
u8 enableTimer; /* Enable the timer. 2: permanently 1: only once */ |
|
7b0960d…
|
drh
|
3704 |
double prevTimer; /* Last reported timer value */ |
|
7b0960d…
|
drh
|
3705 |
double tmProgress; /* --timeout option for .progress */ |
|
92871e0…
|
drh
|
3706 |
i64 lineno; /* Line number of last line read from in */ |
|
4f76459…
|
drh
|
3707 |
const char *zInFile; /* Name of the input file */ |
|
4f76459…
|
drh
|
3708 |
unsigned nTestRun; /* Number of test cases run */ |
|
4f76459…
|
drh
|
3709 |
unsigned nTestErr; /* Number of test cases that failed */ |
|
4f76459…
|
drh
|
3710 |
char *zErrPrefix; /* Alternative error message prefix */ |
|
4f76459…
|
drh
|
3711 |
Mode mode; /* Current display mode */ |
|
4f76459…
|
drh
|
3712 |
Mode modePrior; /* Backup */ |
|
4f76459…
|
drh
|
3713 |
struct SavedMode { /* Ability to define custom mode configurations */ |
|
4f76459…
|
drh
|
3714 |
char *zTag; /* Name of this saved mode */ |
|
4f76459…
|
drh
|
3715 |
Mode mode; /* The saved mode */ |
|
4f76459…
|
drh
|
3716 |
} *aSavedModes; /* Array of saved .mode settings. system malloc() */ |
|
4f76459…
|
drh
|
3717 |
int nSavedModes; /* Number of saved .mode settings */ |
|
4f76459…
|
drh
|
3718 |
struct DotCmdLine { /* Info about arguments to a dot-command */ |
|
4f76459…
|
drh
|
3719 |
const char *zOrig; /* Original text of the dot-command */ |
|
4f76459…
|
drh
|
3720 |
char *zCopy; /* Copy of zOrig, from malloc() */ |
|
4f76459…
|
drh
|
3721 |
int nAlloc; /* Size of allocates for arrays below */ |
|
4f76459…
|
drh
|
3722 |
int nArg; /* Number of argument slots actually used */ |
|
4f76459…
|
drh
|
3723 |
char **azArg; /* Pointer to each argument, dequoted */ |
|
4f76459…
|
drh
|
3724 |
int *aiOfst; /* Offset into zOrig[] for start of each arg */ |
|
4f76459…
|
drh
|
3725 |
char *abQuot; /* True if the argment was originally quoted */ |
|
4f76459…
|
drh
|
3726 |
} dot; |
|
4f76459…
|
drh
|
3727 |
/* Allowed values for ShellState.mode.autoEQP |
|
7b0960d…
|
drh
|
3728 |
#define SHELL_PROGRESS_TMOUT 0x08 /* Stop after tmProgress seconds */ |
|
4f76459…
|
drh
|
3729 |
|
|
4f76459…
|
drh
|
3730 |
/* Names of values for Mode.spec.eEsc and Mode.spec.eText |
|
4f76459…
|
drh
|
3731 |
*/ |
|
4f76459…
|
drh
|
3732 |
static const char *qrfEscNames[] = { "auto", "off", "ascii", "symbol" }; |
|
4f76459…
|
drh
|
3733 |
static const char *qrfQuoteNames[] = |
|
709b566…
|
drh
|
3734 |
{ "off","off","sql","hex","csv","tcl","json","relaxed"}; |
|
4f76459…
|
drh
|
3735 |
#define SHFLG_NoErrLineno 0x00000010 /* Omit line numbers from error msgs */ |
|
4f76459…
|
drh
|
3736 |
** These are the allowed values for Mode.eMode. There is a lot of overlap |
|
4f76459…
|
drh
|
3737 |
** between these values and the Mode.spec.eStyle values, but they are not |
|
4f76459…
|
drh
|
3738 |
** one-to-one, and thus need to be tracked separately. |
|
4f76459…
|
drh
|
3739 |
#define MODE_Ascii 0 /* Use ASCII unit and record separators (0x1F/0x1E) */ |
|
4f76459…
|
drh
|
3740 |
#define MODE_Box 1 /* Unicode box-drawing characters */ |
|
4f76459…
|
drh
|
3741 |
#define MODE_C 2 /* Comma-separated list of C-strings */ |
|
4f76459…
|
drh
|
3742 |
#define MODE_Column 3 /* One record per line in neat columns */ |
|
4f76459…
|
drh
|
3743 |
#define MODE_Count 4 /* Output only a count of the rows of output */ |
|
4f76459…
|
drh
|
3744 |
#define MODE_Csv 5 /* Quote strings, numbers are plain */ |
|
4f76459…
|
drh
|
3745 |
#define MODE_Html 6 /* Generate an XHTML table */ |
|
4f76459…
|
drh
|
3746 |
#define MODE_Insert 7 /* Generate SQL "insert" statements */ |
|
4f76459…
|
drh
|
3747 |
#define MODE_JAtom 8 /* Comma-separated list of JSON atoms */ |
|
4f76459…
|
drh
|
3748 |
#define MODE_JObject 9 /* One JSON object per row */ |
|
4f76459…
|
drh
|
3749 |
#define MODE_Json 10 /* Output JSON */ |
|
4f76459…
|
drh
|
3750 |
#define MODE_Line 11 /* One column per line. Blank line between records */ |
|
4f76459…
|
drh
|
3751 |
#define MODE_List 12 /* One record per line with a separator */ |
|
4f76459…
|
drh
|
3752 |
#define MODE_Markdown 13 /* Markdown formatting */ |
|
4f76459…
|
drh
|
3753 |
#define MODE_Off 14 /* No query output shown */ |
|
f4b3b59…
|
drh
|
3754 |
#define MODE_Psql 15 /* Similar to psql */ |
|
f4b3b59…
|
drh
|
3755 |
#define MODE_QBox 16 /* BOX with SQL-quoted content */ |
|
f4b3b59…
|
drh
|
3756 |
#define MODE_Quote 17 /* Quote values as for SQL */ |
|
f4b3b59…
|
drh
|
3757 |
#define MODE_Split 18 /* Split-column mode */ |
|
f4b3b59…
|
drh
|
3758 |
#define MODE_Table 19 /* MySQL-style table formatting */ |
|
f4b3b59…
|
drh
|
3759 |
#define MODE_Tabs 20 /* Tab-separated values */ |
|
f4b3b59…
|
drh
|
3760 |
#define MODE_Tcl 21 /* Space-separated list of TCL strings */ |
|
f4b3b59…
|
drh
|
3761 |
#define MODE_Www 22 /* Full web-page output */ |
|
f4b3b59…
|
drh
|
3762 |
|
|
f4b3b59…
|
drh
|
3763 |
#define MODE_BUILTIN 22 /* Maximum built-in mode */ |
|
4f76459…
|
drh
|
3764 |
#define MODE_BATCH 50 /* Default mode for batch processing */ |
|
4f76459…
|
drh
|
3765 |
#define MODE_TTY 51 /* Default mode for interactive processing */ |
|
4f76459…
|
drh
|
3766 |
#define MODE_USER 75 /* First user-defined mode */ |
|
4f76459…
|
drh
|
3767 |
#define MODE_N_USER 25 /* Maximum number of user-defined modes */ |
|
4f76459…
|
drh
|
3768 |
|
|
4f76459…
|
drh
|
3769 |
/* |
|
4f76459…
|
drh
|
3770 |
** Information about built-in display modes |
|
4f76459…
|
drh
|
3771 |
*/ |
|
4f76459…
|
drh
|
3772 |
typedef struct ModeInfo ModeInfo; |
|
4f76459…
|
drh
|
3773 |
struct ModeInfo { |
|
4f76459…
|
drh
|
3774 |
char zName[9]; /* Symbolic name of the mode */ |
|
4f76459…
|
drh
|
3775 |
unsigned char eCSep; /* Column separator */ |
|
4f76459…
|
drh
|
3776 |
unsigned char eRSep; /* Row separator */ |
|
4f76459…
|
drh
|
3777 |
unsigned char eNull; /* Null representation */ |
|
4f76459…
|
drh
|
3778 |
unsigned char eText; /* Default text encoding */ |
|
4f76459…
|
drh
|
3779 |
unsigned char eHdr; /* Default header encoding. */ |
|
4f76459…
|
drh
|
3780 |
unsigned char eBlob; /* Default blob encoding. */ |
|
4f76459…
|
drh
|
3781 |
unsigned char bHdr; /* Show headers by default. 0: n/a, 1: no 2: yes */ |
|
4f76459…
|
drh
|
3782 |
unsigned char eStyle; /* Underlying QRF style */ |
|
4f76459…
|
drh
|
3783 |
unsigned char eCx; /* 0: other, 1: line, 2: columnar */ |
|
f4b3b59…
|
drh
|
3784 |
unsigned char mFlg; /* Flags. 1=border-off 2=split-column */ |
|
4f76459…
|
drh
|
3785 |
/* String constants used by built-in modes */ |
|
4f76459…
|
drh
|
3786 |
static const char *aModeStr[] = |
|
4f76459…
|
drh
|
3787 |
/* 0 1 2 3 4 5 6 7 8 */ |
|
4f76459…
|
drh
|
3788 |
{ 0, "\n", "|", " ", ",", "\r\n", "\036", "\037", "\t", |
|
ae7e3f0…
|
drh
|
3789 |
"", "NULL", "null", "\"\"", ": ", }; |
|
ae7e3f0…
|
drh
|
3790 |
/* 9 10 11 12 13 */ |
|
4f76459…
|
drh
|
3791 |
|
|
4f76459…
|
drh
|
3792 |
static const ModeInfo aModeInfo[] = { |
|
f4b3b59…
|
drh
|
3793 |
/* zName eCSep eRSep eNull eText eHdr eBlob bHdr eStyle eCx mFlg */ |
|
f4b3b59…
|
drh
|
3794 |
{ "ascii", 7, 6, 9, 1, 1, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3795 |
{ "box", 0, 0, 9, 1, 1, 0, 2, 1, 2, 0 }, |
|
f4b3b59…
|
drh
|
3796 |
{ "c", 4, 1, 10, 5, 5, 4, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3797 |
{ "column", 0, 0, 9, 1, 1, 0, 2, 2, 2, 0 }, |
|
f4b3b59…
|
drh
|
3798 |
{ "count", 0, 0, 0, 0, 0, 0, 0, 3, 0, 0 }, |
|
f4b3b59…
|
drh
|
3799 |
{ "csv", 4, 5, 9, 3, 3, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3800 |
{ "html", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 }, |
|
f4b3b59…
|
drh
|
3801 |
{ "insert", 0, 0, 10, 2, 2, 0, 1, 8, 0, 0 }, |
|
f4b3b59…
|
drh
|
3802 |
{ "jatom", 4, 1, 11, 6, 6, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3803 |
{ "jobject", 0, 1, 11, 6, 6, 0, 0, 10, 0, 0 }, |
|
f4b3b59…
|
drh
|
3804 |
{ "json", 0, 0, 11, 6, 6, 0, 0, 9, 0, 0 }, |
|
ae7e3f0…
|
drh
|
3805 |
{ "line", 13, 1, 9, 1, 1, 0, 0, 11, 1, 0 }, |
|
f4b3b59…
|
drh
|
3806 |
{ "list", 2, 1, 9, 1, 1, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3807 |
{ "markdown", 0, 0, 9, 1, 1, 0, 2, 13, 2, 0 }, |
|
f4b3b59…
|
drh
|
3808 |
{ "off", 0, 0, 0, 0, 0, 0, 0, 14, 0, 0 }, |
|
f4b3b59…
|
drh
|
3809 |
{ "psql", 0, 0, 9, 1, 1, 0, 2, 19, 2, 1 }, |
|
f4b3b59…
|
drh
|
3810 |
{ "qbox", 0, 0, 10, 2, 1, 0, 2, 1, 2, 0 }, |
|
f4b3b59…
|
drh
|
3811 |
{ "quote", 4, 1, 10, 2, 2, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3812 |
{ "split", 0, 0, 9, 1, 1, 0, 1, 2, 2, 2 }, |
|
f4b3b59…
|
drh
|
3813 |
{ "table", 0, 0, 9, 1, 1, 0, 2, 19, 2, 0 }, |
|
f4b3b59…
|
drh
|
3814 |
{ "tabs", 8, 1, 9, 3, 3, 0, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3815 |
{ "tcl", 3, 1, 12, 5, 5, 4, 1, 12, 0, 0 }, |
|
f4b3b59…
|
drh
|
3816 |
{ "www", 0, 0, 9, 4, 4, 0, 2, 7, 0, 0 } |
|
4f76459…
|
drh
|
3817 |
}; /* | / / | / / | | \ |
|
4f76459…
|
drh
|
3818 |
** | / / | / / | | \_ 2: columnar |
|
4f76459…
|
drh
|
3819 |
** Index into aModeStr[] | / / | | 1: line |
|
4f76459…
|
drh
|
3820 |
** | / / | | 0: other |
|
4f76459…
|
drh
|
3821 |
** | / / | \ |
|
4f76459…
|
drh
|
3822 |
** text encoding |/ | show | \ |
|
4f76459…
|
drh
|
3823 |
** v-------------------' | hdrs? | The QRF style |
|
4f76459…
|
drh
|
3824 |
** 0: n/a blob | v-----' |
|
4f76459…
|
drh
|
3825 |
** 1: plain v_---------' 0: n/a |
|
45de97f…
|
drh
|
3826 |
** 2: sql 0: auto 1: no |
|
4f76459…
|
drh
|
3827 |
** 3: csv 1: as-text 2: yes |
|
4f76459…
|
drh
|
3828 |
** 4: html 2: sql |
|
4f76459…
|
drh
|
3829 |
** 5: c 3: hex |
|
4f76459…
|
drh
|
3830 |
** 6: json 4: c |
|
4f76459…
|
drh
|
3831 |
** 5: json |
|
45de97f…
|
drh
|
3832 |
** 6: size |
|
4f76459…
|
drh
|
3833 |
******************************************************************/ |
|
ae7e3f0…
|
drh
|
3834 |
** Default values for the various QRF limits |
|
ae7e3f0…
|
drh
|
3835 |
*/ |
|
ae7e3f0…
|
drh
|
3836 |
#ifndef DFLT_CHAR_LIMIT |
|
ae7e3f0…
|
drh
|
3837 |
# define DFLT_CHAR_LIMIT 300 |
|
ae7e3f0…
|
drh
|
3838 |
#endif |
|
ae7e3f0…
|
drh
|
3839 |
#ifndef DFLT_LINE_LIMIT |
|
ae7e3f0…
|
drh
|
3840 |
# define DFLT_LINE_LIMIT 5 |
|
ae7e3f0…
|
drh
|
3841 |
#endif |
|
ae7e3f0…
|
drh
|
3842 |
#ifndef DFLT_TITLE_LIMIT |
|
ae7e3f0…
|
drh
|
3843 |
# define DFLT_TITLE_LIMIT 20 |
|
ae7e3f0…
|
drh
|
3844 |
#endif |
|
17f9878…
|
drh
|
3845 |
#ifndef DFLT_MULTI_INSERT |
|
17f9878…
|
drh
|
3846 |
# define DFLT_MULTI_INSERT 3000 |
|
17f9878…
|
drh
|
3847 |
#endif |
|
17f9878…
|
drh
|
3848 |
|
|
17f9878…
|
drh
|
3849 |
/* |
|
a10f931…
|
drh
|
3850 |
** If the following flag is set, then command execution stops |
|
a10f931…
|
drh
|
3851 |
** at an error if we are not interactive. |
|
a10f931…
|
drh
|
3852 |
*/ |
|
a10f931…
|
drh
|
3853 |
static int bail_on_error = 0; |
|
a10f931…
|
drh
|
3854 |
|
|
a10f931…
|
drh
|
3855 |
/* |
|
a10f931…
|
drh
|
3856 |
** Treat stdin as an interactive input if the following variable |
|
a10f931…
|
drh
|
3857 |
** is true. Otherwise, assume stdin is connected to a file or pipe. |
|
a10f931…
|
drh
|
3858 |
*/ |
|
a10f931…
|
drh
|
3859 |
static int stdin_is_interactive = 1; |
|
a10f931…
|
drh
|
3860 |
|
|
a10f931…
|
drh
|
3861 |
/* |
|
a10f931…
|
drh
|
3862 |
** Treat stdout like a TTY if true. |
|
a10f931…
|
drh
|
3863 |
*/ |
|
a10f931…
|
drh
|
3864 |
static int stdout_is_console = 1; |
|
a10f931…
|
drh
|
3865 |
|
|
a10f931…
|
drh
|
3866 |
/* |
|
a10f931…
|
drh
|
3867 |
** Use this value as the width of the output device. Or, figure it |
|
a10f931…
|
drh
|
3868 |
** out at runtime if the value is negative. Or use a default width |
|
a10f931…
|
drh
|
3869 |
** if this value is zero. |
|
a10f931…
|
drh
|
3870 |
*/ |
|
a10f931…
|
drh
|
3871 |
static int stdout_tty_width = -1; |
|
a10f931…
|
drh
|
3872 |
|
|
a10f931…
|
drh
|
3873 |
/* |
|
a10f931…
|
drh
|
3874 |
** The following is the open SQLite database. We make a pointer |
|
a10f931…
|
drh
|
3875 |
** to this database a static variable so that it can be accessed |
|
a10f931…
|
drh
|
3876 |
** by the SIGINT handler to interrupt database processing. |
|
a10f931…
|
drh
|
3877 |
*/ |
|
a10f931…
|
drh
|
3878 |
static sqlite3 *globalDb = 0; |
|
a10f931…
|
drh
|
3879 |
|
|
a10f931…
|
drh
|
3880 |
/* |
|
a10f931…
|
drh
|
3881 |
** True if an interrupt (Control-C) has been received. |
|
a10f931…
|
drh
|
3882 |
*/ |
|
a10f931…
|
drh
|
3883 |
static volatile int seenInterrupt = 0; |
|
a10f931…
|
drh
|
3884 |
|
|
a10f931…
|
drh
|
3885 |
/* |
|
a10f931…
|
drh
|
3886 |
** This is the name of our program. It is set in main(), used |
|
a10f931…
|
drh
|
3887 |
** in a number of other places, mostly for error messages. |
|
a10f931…
|
drh
|
3888 |
*/ |
|
a10f931…
|
drh
|
3889 |
static char *Argv0; |
|
a10f931…
|
drh
|
3890 |
|
|
a10f931…
|
drh
|
3891 |
/* |
|
a10f931…
|
drh
|
3892 |
** Prompt strings. Initialized in main. Settable with |
|
a10f931…
|
drh
|
3893 |
** .prompt main continue |
|
a10f931…
|
drh
|
3894 |
*/ |
|
a10f931…
|
drh
|
3895 |
#define PROMPT_LEN_MAX 128 |
|
a10f931…
|
drh
|
3896 |
/* First line prompt. default: "sqlite> " */ |
|
a10f931…
|
drh
|
3897 |
static char mainPrompt[PROMPT_LEN_MAX]; |
|
a10f931…
|
drh
|
3898 |
/* Continuation prompt. default: " ...> " */ |
|
a10f931…
|
drh
|
3899 |
static char continuePrompt[PROMPT_LEN_MAX]; |
|
a10f931…
|
drh
|
3900 |
|
|
a10f931…
|
drh
|
3901 |
/* |
|
a10f931…
|
drh
|
3902 |
** Write I/O traces to the following stream. |
|
a10f931…
|
drh
|
3903 |
*/ |
|
a10f931…
|
drh
|
3904 |
#ifdef SQLITE_ENABLE_IOTRACE |
|
a10f931…
|
drh
|
3905 |
static FILE *iotrace = 0; |
|
a10f931…
|
drh
|
3906 |
#endif |
|
a10f931…
|
drh
|
3907 |
|
|
a10f931…
|
drh
|
3908 |
/* |
|
a10f931…
|
drh
|
3909 |
** Output routines that are able to redirect to memory rather than |
|
a10f931…
|
drh
|
3910 |
** doing actually I/O. |
|
a10f931…
|
drh
|
3911 |
** Works like. |
|
a10f931…
|
drh
|
3912 |
** -------------- |
|
a10f931…
|
drh
|
3913 |
** cli_printf(FILE*, const char*, ...); fprintf() |
|
a10f931…
|
drh
|
3914 |
** cli_puts(const char*, FILE*); fputs() |
|
a10f931…
|
drh
|
3915 |
** cli_vprintf(FILE*, const char*, va_list); vfprintf() |
|
a10f931…
|
drh
|
3916 |
** |
|
a10f931…
|
drh
|
3917 |
** These are just thin wrappers with the following added semantics: |
|
a10f931…
|
drh
|
3918 |
** If the file-scope variable cli_output_capture is not NULL, and |
|
a10f931…
|
drh
|
3919 |
** if the FILE* argument is stdout or stderr, then rather than |
|
a10f931…
|
drh
|
3920 |
** writing to stdout/stdout, append the text to the cli_output_capture |
|
a10f931…
|
drh
|
3921 |
** variable. |
|
a10f931…
|
drh
|
3922 |
** |
|
a10f931…
|
drh
|
3923 |
** The cli_exit(int) routine works like exit() except that it |
|
a10f931…
|
drh
|
3924 |
** first dumps any capture output to stdout. |
|
a10f931…
|
drh
|
3925 |
*/ |
|
a10f931…
|
drh
|
3926 |
static sqlite3_str *cli_output_capture = 0; |
|
a10f931…
|
drh
|
3927 |
static int cli_printf(FILE *out, const char *zFormat, ...){ |
|
a10f931…
|
drh
|
3928 |
va_list ap; |
|
a10f931…
|
drh
|
3929 |
int rc; |
|
a10f931…
|
drh
|
3930 |
va_start(ap,zFormat); |
|
a10f931…
|
drh
|
3931 |
if( cli_output_capture && (out==stdout || out==stderr) ){ |
|
a10f931…
|
drh
|
3932 |
sqlite3_str_vappendf(cli_output_capture, zFormat, ap); |
|
a10f931…
|
drh
|
3933 |
rc = 1; |
|
a10f931…
|
drh
|
3934 |
}else{ |
|
a10f931…
|
drh
|
3935 |
rc = sqlite3_vfprintf(out, zFormat, ap); |
|
a10f931…
|
drh
|
3936 |
} |
|
a10f931…
|
drh
|
3937 |
va_end(ap); |
|
a10f931…
|
drh
|
3938 |
return rc; |
|
a10f931…
|
drh
|
3939 |
} |
|
a10f931…
|
drh
|
3940 |
static int cli_puts(const char *zText, FILE *out){ |
|
a10f931…
|
drh
|
3941 |
if( cli_output_capture && (out==stdout || out==stderr) ){ |
|
a10f931…
|
drh
|
3942 |
sqlite3_str_appendall(cli_output_capture, zText); |
|
a10f931…
|
drh
|
3943 |
return 1; |
|
a10f931…
|
drh
|
3944 |
} |
|
a10f931…
|
drh
|
3945 |
return sqlite3_fputs(zText, out); |
|
a10f931…
|
drh
|
3946 |
} |
|
a10f931…
|
drh
|
3947 |
#if 0 /* Not currently used - available if we need it later */ |
|
a10f931…
|
drh
|
3948 |
static int cli_vprintf(FILE *out, const char *zFormat, va_list ap){ |
|
a10f931…
|
drh
|
3949 |
if( cli_output_capture && (out==stdout || out==stderr) ){ |
|
a10f931…
|
drh
|
3950 |
sqlite3_str_vappendf(cli_output_capture, zFormat, ap); |
|
a10f931…
|
drh
|
3951 |
return 1; |
|
a10f931…
|
drh
|
3952 |
}else{ |
|
a10f931…
|
drh
|
3953 |
return sqlite3_vfprintf(out, zFormat, ap); |
|
a10f931…
|
drh
|
3954 |
} |
|
a10f931…
|
drh
|
3955 |
} |
|
a10f931…
|
drh
|
3956 |
#endif |
|
a10f931…
|
drh
|
3957 |
static void cli_exit(int rc){ |
|
a10f931…
|
drh
|
3958 |
if( cli_output_capture ){ |
|
a10f931…
|
drh
|
3959 |
char *z = sqlite3_str_finish(cli_output_capture); |
|
a10f931…
|
drh
|
3960 |
sqlite3_fputs(z, stdout); |
|
a10f931…
|
drh
|
3961 |
fflush(stdout); |
|
a10f931…
|
drh
|
3962 |
} |
|
a10f931…
|
drh
|
3963 |
exit(rc); |
|
a10f931…
|
drh
|
3964 |
} |
|
a10f931…
|
drh
|
3965 |
|
|
a10f931…
|
drh
|
3966 |
|
|
a10f931…
|
drh
|
3967 |
#define eputz(z) cli_puts(z,stderr) |
|
a10f931…
|
drh
|
3968 |
#define sputz(fp,z) cli_puts(z,fp) |
|
a10f931…
|
drh
|
3969 |
|
|
a10f931…
|
drh
|
3970 |
/* A version of strcmp() that works with NULL values */ |
|
a10f931…
|
drh
|
3971 |
static int cli_strcmp(const char *a, const char *b){ |
|
a10f931…
|
drh
|
3972 |
if( a==0 ) a = ""; |
|
a10f931…
|
drh
|
3973 |
if( b==0 ) b = ""; |
|
a10f931…
|
drh
|
3974 |
return strcmp(a,b); |
|
a10f931…
|
drh
|
3975 |
} |
|
a10f931…
|
drh
|
3976 |
static int cli_strncmp(const char *a, const char *b, size_t n){ |
|
a10f931…
|
drh
|
3977 |
if( a==0 ) a = ""; |
|
a10f931…
|
drh
|
3978 |
if( b==0 ) b = ""; |
|
a10f931…
|
drh
|
3979 |
return strncmp(a,b,n); |
|
a10f931…
|
drh
|
3980 |
} |
|
a10f931…
|
drh
|
3981 |
|
|
a10f931…
|
drh
|
3982 |
/* Return the current wall-clock time in microseconds since the |
|
a10f931…
|
drh
|
3983 |
** Unix epoch (1970-01-01T00:00:00Z) |
|
a10f931…
|
drh
|
3984 |
*/ |
|
a10f931…
|
drh
|
3985 |
static sqlite3_int64 timeOfDay(void){ |
|
a10f931…
|
drh
|
3986 |
#if defined(_WIN64) && _WIN32_WINNT >= _WIN32_WINNT_WIN8 |
|
a10f931…
|
drh
|
3987 |
sqlite3_uint64 t; |
|
a10f931…
|
drh
|
3988 |
FILETIME tm; |
|
a10f931…
|
drh
|
3989 |
GetSystemTimePreciseAsFileTime(&tm); |
|
a10f931…
|
drh
|
3990 |
t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime; |
|
a10f931…
|
drh
|
3991 |
t += 116444736000000000LL; |
|
a10f931…
|
drh
|
3992 |
t /= 10; |
|
a10f931…
|
drh
|
3993 |
return t; |
|
a10f931…
|
drh
|
3994 |
#elif defined(_WIN32) |
|
a10f931…
|
drh
|
3995 |
static sqlite3_vfs *clockVfs = 0; |
|
a10f931…
|
drh
|
3996 |
sqlite3_int64 t; |
|
a10f931…
|
drh
|
3997 |
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); |
|
a10f931…
|
drh
|
3998 |
if( clockVfs==0 ) return 0; /* Never actually happens */ |
|
a10f931…
|
drh
|
3999 |
if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ |
|
a10f931…
|
drh
|
4000 |
clockVfs->xCurrentTimeInt64(clockVfs, &t); |
|
a10f931…
|
drh
|
4001 |
}else{ |
|
a10f931…
|
drh
|
4002 |
double r; |
|
a10f931…
|
drh
|
4003 |
clockVfs->xCurrentTime(clockVfs, &r); |
|
a10f931…
|
drh
|
4004 |
t = (sqlite3_int64)(r*86400000.0); |
|
a10f931…
|
drh
|
4005 |
} |
|
a10f931…
|
drh
|
4006 |
return t*1000; |
|
a10f931…
|
drh
|
4007 |
#else |
|
a10f931…
|
drh
|
4008 |
struct timeval sNow; |
|
a10f931…
|
drh
|
4009 |
(void)gettimeofday(&sNow,0); |
|
a10f931…
|
drh
|
4010 |
return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec; |
|
a10f931…
|
drh
|
4011 |
#endif |
|
a10f931…
|
drh
|
4012 |
} |
|
a10f931…
|
drh
|
4013 |
|
|
a10f931…
|
drh
|
4014 |
|
|
a10f931…
|
drh
|
4015 |
|
|
a10f931…
|
drh
|
4016 |
/* This is variant of the standard-library strncpy() routine with the |
|
a10f931…
|
drh
|
4017 |
** one change that the destination string is always zero-terminated, even |
|
a10f931…
|
drh
|
4018 |
** if there is no zero-terminator in the first n-1 characters of the source |
|
a10f931…
|
drh
|
4019 |
** string. |
|
a10f931…
|
drh
|
4020 |
*/ |
|
a10f931…
|
drh
|
4021 |
static char *shell_strncpy(char *dest, const char *src, size_t n){ |
|
a10f931…
|
drh
|
4022 |
size_t i; |
|
a10f931…
|
drh
|
4023 |
for(i=0; i<n-1 && src[i]!=0; i++) dest[i] = src[i]; |
|
a10f931…
|
drh
|
4024 |
dest[i] = 0; |
|
a10f931…
|
drh
|
4025 |
return dest; |
|
a10f931…
|
drh
|
4026 |
} |
|
a10f931…
|
drh
|
4027 |
|
|
a10f931…
|
drh
|
4028 |
/* |
|
a10f931…
|
drh
|
4029 |
** strcpy() workalike to squelch an unwarranted link-time warning |
|
a10f931…
|
drh
|
4030 |
** from OpenBSD. |
|
a10f931…
|
drh
|
4031 |
*/ |
|
a10f931…
|
drh
|
4032 |
static void shell_strcpy(char *dest, const char *src){ |
|
a10f931…
|
drh
|
4033 |
while( (*(dest++) = *(src++))!=0 ){} |
|
a10f931…
|
drh
|
4034 |
} |
|
a10f931…
|
drh
|
4035 |
|
|
a10f931…
|
drh
|
4036 |
/* |
|
a10f931…
|
drh
|
4037 |
** Optionally disable dynamic continuation prompt. |
|
a10f931…
|
drh
|
4038 |
** Unless disabled, the continuation prompt shows open SQL lexemes if any, |
|
a10f931…
|
drh
|
4039 |
** or open parentheses level if non-zero, or continuation prompt as set. |
|
a10f931…
|
drh
|
4040 |
** This facility interacts with the scanner and process_input() where the |
|
a10f931…
|
drh
|
4041 |
** below 5 macros are used. |
|
a10f931…
|
drh
|
4042 |
*/ |
|
a10f931…
|
drh
|
4043 |
#ifdef SQLITE_OMIT_DYNAPROMPT |
|
a10f931…
|
drh
|
4044 |
# define CONTINUATION_PROMPT continuePrompt |
|
a10f931…
|
drh
|
4045 |
# define CONTINUE_PROMPT_RESET |
|
a10f931…
|
drh
|
4046 |
# define CONTINUE_PROMPT_AWAITS(p,s) |
|
a10f931…
|
drh
|
4047 |
# define CONTINUE_PROMPT_AWAITC(p,c) |
|
a10f931…
|
drh
|
4048 |
# define CONTINUE_PAREN_INCR(p,n) |
|
a10f931…
|
drh
|
4049 |
# define CONTINUE_PROMPT_PSTATE 0 |
|
a10f931…
|
drh
|
4050 |
typedef void *t_NoDynaPrompt; |
|
a10f931…
|
drh
|
4051 |
# define SCAN_TRACKER_REFTYPE t_NoDynaPrompt |
|
a10f931…
|
drh
|
4052 |
#else |
|
a10f931…
|
drh
|
4053 |
# define CONTINUATION_PROMPT dynamicContinuePrompt() |
|
a10f931…
|
drh
|
4054 |
# define CONTINUE_PROMPT_RESET \ |
|
a10f931…
|
drh
|
4055 |
do {setLexemeOpen(&dynPrompt,0,0); trackParenLevel(&dynPrompt,0);} while(0) |
|
a10f931…
|
drh
|
4056 |
# define CONTINUE_PROMPT_AWAITS(p,s) \ |
|
a10f931…
|
drh
|
4057 |
if(p && stdin_is_interactive) setLexemeOpen(p, s, 0) |
|
a10f931…
|
drh
|
4058 |
# define CONTINUE_PROMPT_AWAITC(p,c) \ |
|
a10f931…
|
drh
|
4059 |
if(p && stdin_is_interactive) setLexemeOpen(p, 0, c) |
|
a10f931…
|
drh
|
4060 |
# define CONTINUE_PAREN_INCR(p,n) \ |
|
a10f931…
|
drh
|
4061 |
if(p && stdin_is_interactive) (trackParenLevel(p,n)) |
|
a10f931…
|
drh
|
4062 |
# define CONTINUE_PROMPT_PSTATE (&dynPrompt) |
|
a10f931…
|
drh
|
4063 |
typedef struct DynaPrompt *t_DynaPromptRef; |
|
a10f931…
|
drh
|
4064 |
# define SCAN_TRACKER_REFTYPE t_DynaPromptRef |
|
a10f931…
|
drh
|
4065 |
|
|
a10f931…
|
drh
|
4066 |
static struct DynaPrompt { |
|
a10f931…
|
drh
|
4067 |
char dynamicPrompt[PROMPT_LEN_MAX]; |
|
a10f931…
|
drh
|
4068 |
char acAwait[2]; |
|
a10f931…
|
drh
|
4069 |
int inParenLevel; |
|
a10f931…
|
drh
|
4070 |
char *zScannerAwaits; |
|
a10f931…
|
drh
|
4071 |
} dynPrompt = { {0}, {0}, 0, 0 }; |
|
a10f931…
|
drh
|
4072 |
|
|
a10f931…
|
drh
|
4073 |
/* Record parenthesis nesting level change, or force level to 0. */ |
|
a10f931…
|
drh
|
4074 |
static void trackParenLevel(struct DynaPrompt *p, int ni){ |
|
a10f931…
|
drh
|
4075 |
p->inParenLevel += ni; |
|
a10f931…
|
drh
|
4076 |
if( ni==0 ) p->inParenLevel = 0; |
|
a10f931…
|
drh
|
4077 |
p->zScannerAwaits = 0; |
|
a10f931…
|
drh
|
4078 |
} |
|
a10f931…
|
drh
|
4079 |
|
|
a10f931…
|
drh
|
4080 |
/* Record that a lexeme is opened, or closed with args==0. */ |
|
a10f931…
|
drh
|
4081 |
static void setLexemeOpen(struct DynaPrompt *p, char *s, char c){ |
|
a10f931…
|
drh
|
4082 |
if( s!=0 || c==0 ){ |
|
a10f931…
|
drh
|
4083 |
p->zScannerAwaits = s; |
|
a10f931…
|
drh
|
4084 |
p->acAwait[0] = 0; |
|
a10f931…
|
drh
|
4085 |
}else{ |
|
a10f931…
|
drh
|
4086 |
p->acAwait[0] = c; |
|
a10f931…
|
drh
|
4087 |
p->zScannerAwaits = p->acAwait; |
|
a10f931…
|
drh
|
4088 |
} |
|
a10f931…
|
drh
|
4089 |
} |
|
a10f931…
|
drh
|
4090 |
|
|
a10f931…
|
drh
|
4091 |
/* Upon demand, derive the continuation prompt to display. */ |
|
a10f931…
|
drh
|
4092 |
static char *dynamicContinuePrompt(void){ |
|
a10f931…
|
drh
|
4093 |
if( continuePrompt[0]==0 |
|
a10f931…
|
drh
|
4094 |
|| (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){ |
|
a10f931…
|
drh
|
4095 |
return continuePrompt; |
|
a10f931…
|
drh
|
4096 |
}else{ |
|
a10f931…
|
drh
|
4097 |
if( dynPrompt.zScannerAwaits ){ |
|
a10f931…
|
drh
|
4098 |
size_t ncp = strlen(continuePrompt); |
|
a10f931…
|
drh
|
4099 |
size_t ndp = strlen(dynPrompt.zScannerAwaits); |
|
a10f931…
|
drh
|
4100 |
if( ndp > ncp-3 ) return continuePrompt; |
|
a10f931…
|
drh
|
4101 |
shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); |
|
a10f931…
|
drh
|
4102 |
while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' '; |
|
a10f931…
|
drh
|
4103 |
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, |
|
a10f931…
|
drh
|
4104 |
PROMPT_LEN_MAX-4); |
|
a10f931…
|
drh
|
4105 |
}else{ |
|
a10f931…
|
drh
|
4106 |
if( dynPrompt.inParenLevel>9 ){ |
|
a10f931…
|
drh
|
4107 |
shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4); |
|
a10f931…
|
drh
|
4108 |
}else if( dynPrompt.inParenLevel<0 ){ |
|
a10f931…
|
drh
|
4109 |
shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4); |
|
a10f931…
|
drh
|
4110 |
}else{ |
|
a10f931…
|
drh
|
4111 |
shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4); |
|
a10f931…
|
drh
|
4112 |
dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel); |
|
a10f931…
|
drh
|
4113 |
} |
|
a10f931…
|
drh
|
4114 |
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, |
|
a10f931…
|
drh
|
4115 |
PROMPT_LEN_MAX-4); |
|
a10f931…
|
drh
|
4116 |
} |
|
a10f931…
|
drh
|
4117 |
} |
|
a10f931…
|
drh
|
4118 |
return dynPrompt.dynamicPrompt; |
|
a10f931…
|
drh
|
4119 |
} |
|
a10f931…
|
drh
|
4120 |
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */ |
|
a10f931…
|
drh
|
4121 |
|
|
a10f931…
|
drh
|
4122 |
/* Indicate out-of-memory and exit. */ |
|
a10f931…
|
drh
|
4123 |
static void shell_out_of_memory(void){ |
|
a10f931…
|
drh
|
4124 |
eputz("Error: out of memory\n"); |
|
a10f931…
|
drh
|
4125 |
cli_exit(1); |
|
a10f931…
|
drh
|
4126 |
} |
|
a10f931…
|
drh
|
4127 |
|
|
a10f931…
|
drh
|
4128 |
/* Check a pointer to see if it is NULL. If it is NULL, exit with an |
|
a10f931…
|
drh
|
4129 |
** out-of-memory error. |
|
a10f931…
|
drh
|
4130 |
*/ |
|
a10f931…
|
drh
|
4131 |
static void shell_check_oom(const void *p){ |
|
a10f931…
|
drh
|
4132 |
if( p==0 ) shell_out_of_memory(); |
|
a10f931…
|
drh
|
4133 |
} |
|
a10f931…
|
drh
|
4134 |
|
|
a10f931…
|
drh
|
4135 |
/* |
|
a10f931…
|
drh
|
4136 |
** This routine works like printf in that its first argument is a |
|
a10f931…
|
drh
|
4137 |
** format string and subsequent arguments are values to be substituted |
|
a10f931…
|
drh
|
4138 |
** in place of % fields. The result of formatting this string |
|
a10f931…
|
drh
|
4139 |
** is written to iotrace. |
|
a10f931…
|
drh
|
4140 |
*/ |
|
a10f931…
|
drh
|
4141 |
#ifdef SQLITE_ENABLE_IOTRACE |
|
a10f931…
|
drh
|
4142 |
static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ |
|
a10f931…
|
drh
|
4143 |
va_list ap; |
|
a10f931…
|
drh
|
4144 |
char *z; |
|
a10f931…
|
drh
|
4145 |
if( iotrace==0 ) return; |
|
a10f931…
|
drh
|
4146 |
va_start(ap, zFormat); |
|
a10f931…
|
drh
|
4147 |
z = sqlite3_vmprintf(zFormat, ap); |
|
a10f931…
|
drh
|
4148 |
va_end(ap); |
|
a10f931…
|
drh
|
4149 |
cli_printf(iotrace, "%s", z); |
|
a10f931…
|
drh
|
4150 |
sqlite3_free(z); |
|
a10f931…
|
drh
|
4151 |
} |
|
a10f931…
|
drh
|
4152 |
#endif |
|
a10f931…
|
drh
|
4153 |
|
|
a10f931…
|
drh
|
4154 |
/* |
|
a10f931…
|
drh
|
4155 |
** Compute a string length that is limited to what can be stored in |
|
a10f931…
|
drh
|
4156 |
** lower 30 bits of a 32-bit signed integer. |
|
a10f931…
|
drh
|
4157 |
*/ |
|
a10f931…
|
drh
|
4158 |
static int strlen30(const char *z){ |
|
a10f931…
|
drh
|
4159 |
size_t n; |
|
a10f931…
|
drh
|
4160 |
if( z==0 ) return 0; |
|
a10f931…
|
drh
|
4161 |
n = strlen(z); |
|
a10f931…
|
drh
|
4162 |
return n>0x3fffffff ? 0x3fffffff : (int)n; |
|
a10f931…
|
drh
|
4163 |
} |
|
a10f931…
|
drh
|
4164 |
|
|
a10f931…
|
drh
|
4165 |
/* |
|
a10f931…
|
drh
|
4166 |
** Return open FILE * if zFile exists, can be opened for read |
|
a10f931…
|
drh
|
4167 |
** and is an ordinary file or a character stream source. |
|
a10f931…
|
drh
|
4168 |
** Otherwise return 0. |
|
a10f931…
|
drh
|
4169 |
*/ |
|
a10f931…
|
drh
|
4170 |
static FILE * openChrSource(const char *zFile){ |
|
a10f931…
|
drh
|
4171 |
#if defined(_WIN32) || defined(WIN32) |
|
a10f931…
|
drh
|
4172 |
struct __stat64 x = {0}; |
|
a10f931…
|
drh
|
4173 |
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0) |
|
a10f931…
|
drh
|
4174 |
/* On Windows, open first, then check the stream nature. This order |
|
a10f931…
|
drh
|
4175 |
** is necessary because _stat() and sibs, when checking a named pipe, |
|
a10f931…
|
drh
|
4176 |
** effectively break the pipe as its supplier sees it. */ |
|
a10f931…
|
drh
|
4177 |
FILE *rv = sqlite3_fopen(zFile, "rb"); |
|
a10f931…
|
drh
|
4178 |
if( rv==0 ) return 0; |
|
a10f931…
|
drh
|
4179 |
if( _fstat64(_fileno(rv), &x) != 0 |
|
a10f931…
|
drh
|
4180 |
|| !STAT_CHR_SRC(x.st_mode)){ |
|
a10f931…
|
drh
|
4181 |
fclose(rv); |
|
a10f931…
|
drh
|
4182 |
rv = 0; |
|
a10f931…
|
drh
|
4183 |
} |
|
a10f931…
|
drh
|
4184 |
return rv; |
|
a10f931…
|
drh
|
4185 |
#else |
|
a10f931…
|
drh
|
4186 |
struct stat x = {0}; |
|
a10f931…
|
drh
|
4187 |
int rc = stat(zFile, &x); |
|
a10f931…
|
drh
|
4188 |
# define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) |
|
a10f931…
|
drh
|
4189 |
if( rc!=0 ) return 0; |
|
a10f931…
|
drh
|
4190 |
if( STAT_CHR_SRC(x.st_mode) ){ |
|
a10f931…
|
drh
|
4191 |
return sqlite3_fopen(zFile, "rb"); |
|
a10f931…
|
drh
|
4192 |
}else{ |
|
a10f931…
|
drh
|
4193 |
return 0; |
|
a10f931…
|
drh
|
4194 |
} |
|
a10f931…
|
drh
|
4195 |
#endif |
|
a10f931…
|
drh
|
4196 |
#undef STAT_CHR_SRC |
|
a10f931…
|
drh
|
4197 |
} |
|
a10f931…
|
drh
|
4198 |
|
|
a10f931…
|
drh
|
4199 |
/* |
|
a10f931…
|
drh
|
4200 |
** This routine reads a line of text from FILE in, stores |
|
a10f931…
|
drh
|
4201 |
** the text in memory obtained from malloc() and returns a pointer |
|
a10f931…
|
drh
|
4202 |
** to the text. NULL is returned at end of file, or if malloc() |
|
a10f931…
|
drh
|
4203 |
** fails, or if the length of the line is longer than about a gigabyte. |
|
a10f931…
|
drh
|
4204 |
** |
|
a10f931…
|
drh
|
4205 |
** If zLine is not NULL then it is a malloced buffer returned from |
|
a10f931…
|
drh
|
4206 |
** a previous call to this routine that may be reused. |
|
a10f931…
|
drh
|
4207 |
*/ |
|
a10f931…
|
drh
|
4208 |
static char *local_getline(char *zLine, FILE *in){ |
|
a10f931…
|
drh
|
4209 |
int nLine = zLine==0 ? 0 : 100; |
|
a10f931…
|
drh
|
4210 |
int n = 0; |
|
a10f931…
|
drh
|
4211 |
|
|
a10f931…
|
drh
|
4212 |
while( 1 ){ |
|
a10f931…
|
drh
|
4213 |
if( n+100>nLine ){ |
|
a10f931…
|
drh
|
4214 |
if( nLine>=1073741773 ){ |
|
a10f931…
|
drh
|
4215 |
free(zLine); |
|
a10f931…
|
drh
|
4216 |
return 0; |
|
a10f931…
|
drh
|
4217 |
} |
|
a10f931…
|
drh
|
4218 |
nLine = nLine*2 + 100; |
|
a10f931…
|
drh
|
4219 |
zLine = realloc(zLine, nLine); |
|
a10f931…
|
drh
|
4220 |
shell_check_oom(zLine); |
|
a10f931…
|
drh
|
4221 |
} |
|
a10f931…
|
drh
|
4222 |
if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){ |
|
a10f931…
|
drh
|
4223 |
if( n==0 ){ |
|
a10f931…
|
drh
|
4224 |
free(zLine); |
|
a10f931…
|
drh
|
4225 |
return 0; |
|
a10f931…
|
drh
|
4226 |
} |
|
a10f931…
|
drh
|
4227 |
zLine[n] = 0; |
|
a10f931…
|
drh
|
4228 |
break; |
|
a10f931…
|
drh
|
4229 |
} |
|
a10f931…
|
drh
|
4230 |
while( zLine[n] ) n++; |
|
a10f931…
|
drh
|
4231 |
if( n>0 && zLine[n-1]=='\n' ){ |
|
a10f931…
|
drh
|
4232 |
n--; |
|
a10f931…
|
drh
|
4233 |
if( n>0 && zLine[n-1]=='\r' ) n--; |
|
a10f931…
|
drh
|
4234 |
zLine[n] = 0; |
|
a10f931…
|
drh
|
4235 |
break; |
|
a10f931…
|
drh
|
4236 |
} |
|
a10f931…
|
drh
|
4237 |
} |
|
a10f931…
|
drh
|
4238 |
return zLine; |
|
a10f931…
|
drh
|
4239 |
} |
|
a10f931…
|
drh
|
4240 |
|
|
a10f931…
|
drh
|
4241 |
/* |
|
a10f931…
|
drh
|
4242 |
** Retrieve a single line of input text. |
|
a10f931…
|
drh
|
4243 |
** |
|
a10f931…
|
drh
|
4244 |
** If in==0 then read from standard input and prompt before each line. |
|
a10f931…
|
drh
|
4245 |
** If isContinuation is true, then a continuation prompt is appropriate. |
|
a10f931…
|
drh
|
4246 |
** If isContinuation is zero, then the main prompt should be used. |
|
a10f931…
|
drh
|
4247 |
** |
|
a10f931…
|
drh
|
4248 |
** If zPrior is not NULL then it is a buffer from a prior call to this |
|
a10f931…
|
drh
|
4249 |
** routine that can be reused. |
|
a10f931…
|
drh
|
4250 |
** |
|
a10f931…
|
drh
|
4251 |
** The result is stored in space obtained from malloc() and must either |
|
a10f931…
|
drh
|
4252 |
** be freed by the caller or else passed back into this routine via the |
|
a10f931…
|
drh
|
4253 |
** zPrior argument for reuse. |
|
a10f931…
|
drh
|
4254 |
*/ |
|
a10f931…
|
drh
|
4255 |
#ifndef SQLITE_SHELL_FIDDLE |
|
a10f931…
|
drh
|
4256 |
static char *one_input_line(ShellState *p, char *zPrior, int isContinuation){ |
|
a10f931…
|
drh
|
4257 |
char *zPrompt; |
|
a10f931…
|
drh
|
4258 |
char *zResult; |
|
a10f931…
|
drh
|
4259 |
FILE *in = p->in; |
|
a10f931…
|
drh
|
4260 |
if( in!=0 ){ |
|
a10f931…
|
drh
|
4261 |
zResult = local_getline(zPrior, in); |
|
a10f931…
|
drh
|
4262 |
}else{ |
|
a10f931…
|
drh
|
4263 |
zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt; |
|
a10f931…
|
drh
|
4264 |
#if SHELL_USE_LOCAL_GETLINE |
|
a10f931…
|
drh
|
4265 |
sputz(stdout, zPrompt); |
|
a10f931…
|
drh
|
4266 |
fflush(stdout); |
|
a10f931…
|
drh
|
4267 |
do{ |
|
a10f931…
|
drh
|
4268 |
zResult = local_getline(zPrior, stdin); |
|
a10f931…
|
drh
|
4269 |
zPrior = 0; |
|
a10f931…
|
drh
|
4270 |
/* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ |
|
a10f931…
|
drh
|
4271 |
if( zResult==0 ) sqlite3_sleep(50); |
|
a10f931…
|
drh
|
4272 |
}while( zResult==0 && seenInterrupt>0 ); |
|
a10f931…
|
drh
|
4273 |
#else |
|
a10f931…
|
drh
|
4274 |
free(zPrior); |
|
a10f931…
|
drh
|
4275 |
zResult = shell_readline(zPrompt); |
|
a10f931…
|
drh
|
4276 |
while( zResult==0 ){ |
|
a10f931…
|
drh
|
4277 |
/* ^C trap creates a false EOF, so let "interrupt" thread catch up. */ |
|
a10f931…
|
drh
|
4278 |
sqlite3_sleep(50); |
|
a10f931…
|
drh
|
4279 |
if( seenInterrupt==0 ) break; |
|
a10f931…
|
drh
|
4280 |
zResult = shell_readline(""); |
|
a10f931…
|
drh
|
4281 |
} |
|
a10f931…
|
drh
|
4282 |
if( zResult && *zResult ) shell_add_history(zResult); |
|
a10f931…
|
drh
|
4283 |
#endif |
|
a10f931…
|
drh
|
4284 |
} |
|
a10f931…
|
drh
|
4285 |
return zResult; |
|
a10f931…
|
drh
|
4286 |
} |
|
a10f931…
|
drh
|
4287 |
#endif /* !SQLITE_SHELL_FIDDLE */ |
|
a10f931…
|
drh
|
4288 |
|
|
a10f931…
|
drh
|
4289 |
/* |
|
a10f931…
|
drh
|
4290 |
** Return the value of a hexadecimal digit. Return -1 if the input |
|
a10f931…
|
drh
|
4291 |
** is not a hex digit. |
|
a10f931…
|
drh
|
4292 |
*/ |
|
a10f931…
|
drh
|
4293 |
static int hexDigitValue(char c){ |
|
a10f931…
|
drh
|
4294 |
if( c>='0' && c<='9' ) return c - '0'; |
|
a10f931…
|
drh
|
4295 |
if( c>='a' && c<='f' ) return c - 'a' + 10; |
|
a10f931…
|
drh
|
4296 |
if( c>='A' && c<='F' ) return c - 'A' + 10; |
|
a10f931…
|
drh
|
4297 |
return -1; |
|
a10f931…
|
drh
|
4298 |
} |
|
a10f931…
|
drh
|
4299 |
|
|
a10f931…
|
drh
|
4300 |
/* |
|
a10f931…
|
drh
|
4301 |
** Interpret zArg as an integer value, possibly with suffixes. |
|
a10f931…
|
drh
|
4302 |
** |
|
a10f931…
|
drh
|
4303 |
** If the value specified by zArg is outside the range of values that |
|
a10f931…
|
drh
|
4304 |
** can be represented using a 64-bit twos-complement integer, then return |
|
a10f931…
|
drh
|
4305 |
** the nearest representable value. |
|
a10f931…
|
drh
|
4306 |
*/ |
|
a10f931…
|
drh
|
4307 |
static sqlite3_int64 integerValue(const char *zArg){ |
|
a10f931…
|
drh
|
4308 |
sqlite3_uint64 v = 0; |
|
a10f931…
|
drh
|
4309 |
static const struct { char *zSuffix; unsigned int iMult; } aMult[] = { |
|
a10f931…
|
drh
|
4310 |
{ "KiB", 1024 }, |
|
a10f931…
|
drh
|
4311 |
{ "MiB", 1024*1024 }, |
|
a10f931…
|
drh
|
4312 |
{ "GiB", 1024*1024*1024 }, |
|
a10f931…
|
drh
|
4313 |
{ "KB", 1000 }, |
|
a10f931…
|
drh
|
4314 |
{ "MB", 1000000 }, |
|
a10f931…
|
drh
|
4315 |
{ "GB", 1000000000 }, |
|
a10f931…
|
drh
|
4316 |
{ "K", 1000 }, |
|
a10f931…
|
drh
|
4317 |
{ "M", 1000000 }, |
|
a10f931…
|
drh
|
4318 |
{ "G", 1000000000 }, |
|
a10f931…
|
drh
|
4319 |
}; |
|
a10f931…
|
drh
|
4320 |
int i; |
|
a10f931…
|
drh
|
4321 |
int isNeg = 0; |
|
a10f931…
|
drh
|
4322 |
if( zArg[0]=='-' ){ |
|
a10f931…
|
drh
|
4323 |
isNeg = 1; |
|
a10f931…
|
drh
|
4324 |
zArg++; |
|
a10f931…
|
drh
|
4325 |
}else if( zArg[0]=='+' ){ |
|
a10f931…
|
drh
|
4326 |
zArg++; |
|
a10f931…
|
drh
|
4327 |
} |
|
a10f931…
|
drh
|
4328 |
if( zArg[0]=='0' && zArg[1]=='x' ){ |
|
a10f931…
|
drh
|
4329 |
int x; |
|
a10f931…
|
drh
|
4330 |
zArg += 2; |
|
a10f931…
|
drh
|
4331 |
while( (x = hexDigitValue(zArg[0]))>=0 ){ |
|
a10f931…
|
drh
|
4332 |
if( v > 0x0fffffffffffffffULL ) goto integer_overflow; |
|
a10f931…
|
drh
|
4333 |
v = (v<<4) + x; |
|
a10f931…
|
drh
|
4334 |
zArg++; |
|
a10f931…
|
drh
|
4335 |
} |
|
a10f931…
|
drh
|
4336 |
}else{ |
|
a10f931…
|
drh
|
4337 |
while( IsDigit(zArg[0]) ){ |
|
a10f931…
|
drh
|
4338 |
if( v>=922337203685477580LL ){ |
|
a10f931…
|
drh
|
4339 |
if( v>922337203685477580LL || zArg[0]>='8' ) goto integer_overflow; |
|
a10f931…
|
drh
|
4340 |
} |
|
a10f931…
|
drh
|
4341 |
v = v*10 + (zArg[0] - '0'); |
|
a10f931…
|
drh
|
4342 |
zArg++; |
|
a10f931…
|
drh
|
4343 |
} |
|
a10f931…
|
drh
|
4344 |
} |
|
a10f931…
|
drh
|
4345 |
for(i=0; i<ArraySize(aMult); i++){ |
|
a10f931…
|
drh
|
4346 |
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ |
|
a10f931…
|
drh
|
4347 |
if( 0x7fffffffffffffffULL/aMult[i].iMult < v ) goto integer_overflow; |
|
a10f931…
|
drh
|
4348 |
v *= aMult[i].iMult; |
|
a10f931…
|
drh
|
4349 |
break; |
|
a10f931…
|
drh
|
4350 |
} |
|
a10f931…
|
drh
|
4351 |
} |
|
a10f931…
|
drh
|
4352 |
if( isNeg && v>0x7fffffffffffffffULL ) goto integer_overflow; |
|
a10f931…
|
drh
|
4353 |
return isNeg? -(sqlite3_int64)v : (sqlite3_int64)v; |
|
a10f931…
|
drh
|
4354 |
integer_overflow: |
|
a10f931…
|
drh
|
4355 |
return isNeg ? (i64)0x8000000000000000LL : 0x7fffffffffffffffLL; |
|
a10f931…
|
drh
|
4356 |
} |
|
a10f931…
|
drh
|
4357 |
|
|
a10f931…
|
drh
|
4358 |
/* |
|
a10f931…
|
drh
|
4359 |
** A variable length string to which one can append text. |
|
a10f931…
|
drh
|
4360 |
*/ |
|
a10f931…
|
drh
|
4361 |
typedef struct ShellText ShellText; |
|
a10f931…
|
drh
|
4362 |
struct ShellText { |
|
a10f931…
|
drh
|
4363 |
char *zTxt; /* The text */ |
|
a10f931…
|
drh
|
4364 |
i64 n; /* Number of bytes of zTxt[] actually used */ |
|
a10f931…
|
drh
|
4365 |
i64 nAlloc; /* Number of bytes allocated for zTxt[] */ |
|
a10f931…
|
drh
|
4366 |
}; |
|
a10f931…
|
drh
|
4367 |
|
|
a10f931…
|
drh
|
4368 |
/* |
|
a10f931…
|
drh
|
4369 |
** Initialize and destroy a ShellText object |
|
a10f931…
|
drh
|
4370 |
*/ |
|
a10f931…
|
drh
|
4371 |
static void initText(ShellText *p){ |
|
a10f931…
|
drh
|
4372 |
memset(p, 0, sizeof(*p)); |
|
a10f931…
|
drh
|
4373 |
} |
|
a10f931…
|
drh
|
4374 |
static void freeText(ShellText *p){ |
|
a10f931…
|
drh
|
4375 |
sqlite3_free(p->zTxt); |
|
a10f931…
|
drh
|
4376 |
initText(p); |
|
a10f931…
|
drh
|
4377 |
} |
|
a10f931…
|
drh
|
4378 |
|
|
a10f931…
|
drh
|
4379 |
/* zIn is either a pointer to a NULL-terminated string in memory obtained |
|
a10f931…
|
drh
|
4380 |
** from malloc(), or a NULL pointer. The string pointed to by zAppend is |
|
a10f931…
|
drh
|
4381 |
** added to zIn, and the result returned in memory obtained from malloc(). |
|
a10f931…
|
drh
|
4382 |
** zIn, if it was not NULL, is freed. |
|
a10f931…
|
drh
|
4383 |
** |
|
a10f931…
|
drh
|
4384 |
** If the third argument, quote, is not '\0', then it is used as a |
|
a10f931…
|
drh
|
4385 |
** quote character for zAppend. |
|
a10f931…
|
drh
|
4386 |
*/ |
|
a10f931…
|
drh
|
4387 |
static void appendText(ShellText *p, const char *zAppend, char quote){ |
|
a10f931…
|
drh
|
4388 |
i64 len; |
|
a10f931…
|
drh
|
4389 |
i64 i; |
|
a10f931…
|
drh
|
4390 |
i64 nAppend = strlen30(zAppend); |
|
a10f931…
|
drh
|
4391 |
|
|
a10f931…
|
drh
|
4392 |
len = nAppend+p->n+1; |
|
a10f931…
|
drh
|
4393 |
if( quote ){ |
|
a10f931…
|
drh
|
4394 |
len += 2; |
|
a10f931…
|
drh
|
4395 |
for(i=0; i<nAppend; i++){ |
|
a10f931…
|
drh
|
4396 |
if( zAppend[i]==quote ) len++; |
|
a10f931…
|
drh
|
4397 |
} |
|
a10f931…
|
drh
|
4398 |
} |
|
a10f931…
|
drh
|
4399 |
|
|
a10f931…
|
drh
|
4400 |
if( p->zTxt==0 || p->n+len>=p->nAlloc ){ |
|
a10f931…
|
drh
|
4401 |
p->nAlloc = p->nAlloc*2 + len + 20; |
|
a10f931…
|
drh
|
4402 |
p->zTxt = sqlite3_realloc64(p->zTxt, p->nAlloc); |
|
a10f931…
|
drh
|
4403 |
shell_check_oom(p->zTxt); |
|
a10f931…
|
drh
|
4404 |
} |
|
a10f931…
|
drh
|
4405 |
|
|
a10f931…
|
drh
|
4406 |
if( quote ){ |
|
a10f931…
|
drh
|
4407 |
char *zCsr = p->zTxt+p->n; |
|
a10f931…
|
drh
|
4408 |
*zCsr++ = quote; |
|
a10f931…
|
drh
|
4409 |
for(i=0; i<nAppend; i++){ |
|
a10f931…
|
drh
|
4410 |
*zCsr++ = zAppend[i]; |
|
a10f931…
|
drh
|
4411 |
if( zAppend[i]==quote ) *zCsr++ = quote; |
|
a10f931…
|
drh
|
4412 |
} |
|
a10f931…
|
drh
|
4413 |
*zCsr++ = quote; |
|
a10f931…
|
drh
|
4414 |
p->n = (i64)(zCsr - p->zTxt); |
|
a10f931…
|
drh
|
4415 |
*zCsr = '\0'; |
|
a10f931…
|
drh
|
4416 |
}else{ |
|
a10f931…
|
drh
|
4417 |
memcpy(p->zTxt+p->n, zAppend, nAppend); |
|
a10f931…
|
drh
|
4418 |
p->n += nAppend; |
|
a10f931…
|
drh
|
4419 |
p->zTxt[p->n] = '\0'; |
|
a10f931…
|
drh
|
4420 |
} |
|
a10f931…
|
drh
|
4421 |
} |
|
a10f931…
|
drh
|
4422 |
|
|
a10f931…
|
drh
|
4423 |
/* |
|
a10f931…
|
drh
|
4424 |
** Attempt to determine if identifier zName needs to be quoted, either |
|
a10f931…
|
drh
|
4425 |
** because it contains non-alphanumeric characters, or because it is an |
|
a10f931…
|
drh
|
4426 |
** SQLite keyword. Be conservative in this estimate: When in doubt assume |
|
a10f931…
|
drh
|
4427 |
** that quoting is required. |
|
a10f931…
|
drh
|
4428 |
** |
|
a10f931…
|
drh
|
4429 |
** Return '"' if quoting is required. Return 0 if no quoting is required. |
|
a10f931…
|
drh
|
4430 |
*/ |
|
a10f931…
|
drh
|
4431 |
static char quoteChar(const char *zName){ |
|
a10f931…
|
drh
|
4432 |
int i; |
|
a10f931…
|
drh
|
4433 |
if( zName==0 ) return '"'; |
|
a10f931…
|
drh
|
4434 |
if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"'; |
|
a10f931…
|
drh
|
4435 |
for(i=0; zName[i]; i++){ |
|
a10f931…
|
drh
|
4436 |
if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"'; |
|
a10f931…
|
drh
|
4437 |
} |
|
a10f931…
|
drh
|
4438 |
return sqlite3_keyword_check(zName, i) ? '"' : 0; |
|
a10f931…
|
drh
|
4439 |
} |
|
a10f931…
|
drh
|
4440 |
|
|
a10f931…
|
drh
|
4441 |
/* |
|
a10f931…
|
drh
|
4442 |
** Construct a fake object name and column list to describe the structure |
|
a10f931…
|
drh
|
4443 |
** of the view, virtual table, or table valued function zSchema.zName. |
|
a10f931…
|
drh
|
4444 |
** |
|
a10f931…
|
drh
|
4445 |
** The returned string comes from sqlite3_mprintf() and should be freed |
|
a10f931…
|
drh
|
4446 |
** by the caller using sqlite3_free(). |
|
a10f931…
|
drh
|
4447 |
*/ |
|
a10f931…
|
drh
|
4448 |
static char *shellFakeSchema( |
|
a10f931…
|
drh
|
4449 |
sqlite3 *db, /* The database connection containing the vtab */ |
|
a10f931…
|
drh
|
4450 |
const char *zSchema, /* Schema of the database holding the vtab */ |
|
a10f931…
|
drh
|
4451 |
const char *zName /* The name of the virtual table */ |
|
a10f931…
|
drh
|
4452 |
){ |
|
a10f931…
|
drh
|
4453 |
sqlite3_stmt *pStmt = 0; |
|
a10f931…
|
drh
|
4454 |
char *zSql; |
|
a10f931…
|
drh
|
4455 |
ShellText s; |
|
a10f931…
|
drh
|
4456 |
char cQuote; |
|
a10f931…
|
drh
|
4457 |
char *zDiv = "("; |
|
a10f931…
|
drh
|
4458 |
int nRow = 0; |
|
a10f931…
|
drh
|
4459 |
|
|
a10f931…
|
drh
|
4460 |
zSql = sqlite3_mprintf("PRAGMA \"%w\".table_info=%Q;", |
|
a10f931…
|
drh
|
4461 |
zSchema ? zSchema : "main", zName); |
|
a10f931…
|
drh
|
4462 |
shell_check_oom(zSql); |
|
a10f931…
|
drh
|
4463 |
sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
|
a10f931…
|
drh
|
4464 |
sqlite3_free(zSql); |
|
a10f931…
|
drh
|
4465 |
initText(&s); |
|
a10f931…
|
drh
|
4466 |
if( zSchema ){ |
|
a10f931…
|
drh
|
4467 |
cQuote = quoteChar(zSchema); |
|
a10f931…
|
drh
|
4468 |
if( cQuote && sqlite3_stricmp(zSchema,"temp")==0 ) cQuote = 0; |
|
a10f931…
|
drh
|
4469 |
appendText(&s, zSchema, cQuote); |
|
a10f931…
|
drh
|
4470 |
appendText(&s, ".", 0); |
|
a10f931…
|
drh
|
4471 |
} |
|
a10f931…
|
drh
|
4472 |
cQuote = quoteChar(zName); |
|
a10f931…
|
drh
|
4473 |
appendText(&s, zName, cQuote); |
|
a10f931…
|
drh
|
4474 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
a10f931…
|
drh
|
4475 |
const char *zCol = (const char*)sqlite3_column_text(pStmt, 1); |
|
a10f931…
|
drh
|
4476 |
nRow++; |
|
a10f931…
|
drh
|
4477 |
appendText(&s, zDiv, 0); |
|
a10f931…
|
drh
|
4478 |
zDiv = ","; |
|
a10f931…
|
drh
|
4479 |
if( zCol==0 ) zCol = ""; |
|
a10f931…
|
drh
|
4480 |
cQuote = quoteChar(zCol); |
|
a10f931…
|
drh
|
4481 |
appendText(&s, zCol, cQuote); |
|
a10f931…
|
drh
|
4482 |
} |
|
a10f931…
|
drh
|
4483 |
appendText(&s, ")", 0); |
|
a10f931…
|
drh
|
4484 |
sqlite3_finalize(pStmt); |
|
a10f931…
|
drh
|
4485 |
if( nRow==0 ){ |
|
a10f931…
|
drh
|
4486 |
freeText(&s); |
|
a10f931…
|
drh
|
4487 |
s.zTxt = 0; |
|
a10f931…
|
drh
|
4488 |
} |
|
a10f931…
|
drh
|
4489 |
return s.zTxt; |
|
a10f931…
|
drh
|
4490 |
} |
|
a10f931…
|
drh
|
4491 |
|
|
a10f931…
|
drh
|
4492 |
/* |
|
a10f931…
|
drh
|
4493 |
** SQL function: strtod(X) |
|
a10f931…
|
drh
|
4494 |
** |
|
a10f931…
|
drh
|
4495 |
** Use the C-library strtod() function to convert string X into a double. |
|
a10f931…
|
drh
|
4496 |
** Used for comparing the accuracy of SQLite's internal text-to-float conversion |
|
a10f931…
|
drh
|
4497 |
** routines against the C-library. |
|
a10f931…
|
drh
|
4498 |
*/ |
|
a10f931…
|
drh
|
4499 |
static void shellStrtod( |
|
a10f931…
|
drh
|
4500 |
sqlite3_context *pCtx, |
|
a10f931…
|
drh
|
4501 |
int nVal, |
|
a10f931…
|
drh
|
4502 |
sqlite3_value **apVal |
|
a10f931…
|
drh
|
4503 |
){ |
|
a10f931…
|
drh
|
4504 |
char *z = (char*)sqlite3_value_text(apVal[0]); |
|
a10f931…
|
drh
|
4505 |
UNUSED_PARAMETER(nVal); |
|
a10f931…
|
drh
|
4506 |
if( z==0 ) return; |
|
a10f931…
|
drh
|
4507 |
sqlite3_result_double(pCtx, strtod(z,0)); |
|
a10f931…
|
drh
|
4508 |
} |
|
a10f931…
|
drh
|
4509 |
|
|
a10f931…
|
drh
|
4510 |
/* |
|
a10f931…
|
drh
|
4511 |
** SQL function: dtostr(X) |
|
a10f931…
|
drh
|
4512 |
** |
|
a10f931…
|
drh
|
4513 |
** Use the C-library printf() function to convert real value X into a string. |
|
a10f931…
|
drh
|
4514 |
** Used for comparing the accuracy of SQLite's internal float-to-text conversion |
|
a10f931…
|
drh
|
4515 |
** routines against the C-library. |
|
a10f931…
|
drh
|
4516 |
*/ |
|
a10f931…
|
drh
|
4517 |
static void shellDtostr( |
|
a10f931…
|
drh
|
4518 |
sqlite3_context *pCtx, |
|
a10f931…
|
drh
|
4519 |
int nVal, |
|
a10f931…
|
drh
|
4520 |
sqlite3_value **apVal |
|
a10f931…
|
drh
|
4521 |
){ |
|
a10f931…
|
drh
|
4522 |
double r = sqlite3_value_double(apVal[0]); |
|
a10f931…
|
drh
|
4523 |
int n = nVal>=2 ? sqlite3_value_int(apVal[1]) : 26; |
|
a10f931…
|
drh
|
4524 |
char z[400]; |
|
a10f931…
|
drh
|
4525 |
if( n<1 ) n = 1; |
|
a10f931…
|
drh
|
4526 |
if( n>350 ) n = 350; |
|
a10f931…
|
drh
|
4527 |
sprintf(z, "%#+.*e", n, r); |
|
a10f931…
|
drh
|
4528 |
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); |
|
a10f931…
|
drh
|
4529 |
} |
|
a10f931…
|
drh
|
4530 |
|
|
a10f931…
|
drh
|
4531 |
/* |
|
a10f931…
|
drh
|
4532 |
** SQL function: shell_add_schema(S,X) |
|
a10f931…
|
drh
|
4533 |
** |
|
a10f931…
|
drh
|
4534 |
** Add the schema name X to the CREATE statement in S and return the result. |
|
a10f931…
|
drh
|
4535 |
** Examples: |
|
a10f931…
|
drh
|
4536 |
** |
|
a10f931…
|
drh
|
4537 |
** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); |
|
a10f931…
|
drh
|
4538 |
** |
|
a10f931…
|
drh
|
4539 |
** Also works on |
|
a10f931…
|
drh
|
4540 |
** |
|
a10f931…
|
drh
|
4541 |
** CREATE INDEX |
|
a10f931…
|
drh
|
4542 |
** CREATE UNIQUE INDEX |
|
a10f931…
|
drh
|
4543 |
** CREATE VIEW |
|
a10f931…
|
drh
|
4544 |
** CREATE TRIGGER |
|
a10f931…
|
drh
|
4545 |
** CREATE VIRTUAL TABLE |
|
a10f931…
|
drh
|
4546 |
** |
|
a10f931…
|
drh
|
4547 |
** This UDF is used by the .schema command to insert the schema name of |
|
a10f931…
|
drh
|
4548 |
** attached databases into the middle of the sqlite_schema.sql field. |
|
a10f931…
|
drh
|
4549 |
*/ |
|
a10f931…
|
drh
|
4550 |
static void shellAddSchemaName( |
|
a10f931…
|
drh
|
4551 |
sqlite3_context *pCtx, |
|
a10f931…
|
drh
|
4552 |
int nVal, |
|
a10f931…
|
drh
|
4553 |
sqlite3_value **apVal |
|
a10f931…
|
drh
|
4554 |
){ |
|
a10f931…
|
drh
|
4555 |
static const char *aPrefix[] = { |
|
a10f931…
|
drh
|
4556 |
"TABLE", |
|
a10f931…
|
drh
|
4557 |
"INDEX", |
|
a10f931…
|
drh
|
4558 |
"UNIQUE INDEX", |
|
a10f931…
|
drh
|
4559 |
"VIEW", |
|
a10f931…
|
drh
|
4560 |
"TRIGGER", |
|
a10f931…
|
drh
|
4561 |
"VIRTUAL TABLE" |
|
a10f931…
|
drh
|
4562 |
}; |
|
a10f931…
|
drh
|
4563 |
int i = 0; |
|
a10f931…
|
drh
|
4564 |
const char *zIn = (const char*)sqlite3_value_text(apVal[0]); |
|
a10f931…
|
drh
|
4565 |
const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); |
|
a10f931…
|
drh
|
4566 |
const char *zName = (const char*)sqlite3_value_text(apVal[2]); |
|
a10f931…
|
drh
|
4567 |
sqlite3 *db = sqlite3_context_db_handle(pCtx); |
|
a10f931…
|
drh
|
4568 |
UNUSED_PARAMETER(nVal); |
|
a10f931…
|
drh
|
4569 |
if( zIn!=0 && cli_strncmp(zIn, "CREATE ", 7)==0 ){ |
|
a10f931…
|
drh
|
4570 |
for(i=0; i<ArraySize(aPrefix); i++){ |
|
a10f931…
|
drh
|
4571 |
int n = strlen30(aPrefix[i]); |
|
a10f931…
|
drh
|
4572 |
if( cli_strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){ |
|
a10f931…
|
drh
|
4573 |
char *z = 0; |
|
a10f931…
|
drh
|
4574 |
char *zFake = 0; |
|
a10f931…
|
drh
|
4575 |
if( zSchema ){ |
|
a10f931…
|
drh
|
4576 |
char cQuote = quoteChar(zSchema); |
|
a10f931…
|
drh
|
4577 |
if( cQuote && sqlite3_stricmp(zSchema,"temp")!=0 ){ |
|
a10f931…
|
drh
|
4578 |
z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8); |
|
a10f931…
|
drh
|
4579 |
}else{ |
|
a10f931…
|
drh
|
4580 |
z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8); |
|
a10f931…
|
drh
|
4581 |
} |
|
a10f931…
|
drh
|
4582 |
} |
|
a10f931…
|
drh
|
4583 |
if( zName |
|
a10f931…
|
drh
|
4584 |
&& aPrefix[i][0]=='V' |
|
a10f931…
|
drh
|
4585 |
&& (zFake = shellFakeSchema(db, zSchema, zName))!=0 |
|
a10f931…
|
drh
|
4586 |
){ |
|
a10f931…
|
drh
|
4587 |
if( z==0 ){ |
|
a10f931…
|
drh
|
4588 |
z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake); |
|
a10f931…
|
drh
|
4589 |
}else{ |
|
a10f931…
|
drh
|
4590 |
z = sqlite3_mprintf("%z\n/* %s */", z, zFake); |
|
a10f931…
|
drh
|
4591 |
} |
|
a10f931…
|
drh
|
4592 |
sqlite3_free(zFake); |
|
a10f931…
|
drh
|
4593 |
} |
|
a10f931…
|
drh
|
4594 |
if( z ){ |
|
a10f931…
|
drh
|
4595 |
sqlite3_result_text(pCtx, z, -1, sqlite3_free); |
|
a10f931…
|
drh
|
4596 |
return; |
|
a10f931…
|
drh
|
4597 |
} |
|
a10f931…
|
drh
|
4598 |
} |
|
a10f931…
|
drh
|
4599 |
} |
|
a10f931…
|
drh
|
4600 |
} |
|
a10f931…
|
drh
|
4601 |
sqlite3_result_value(pCtx, apVal[0]); |
|
a10f931…
|
drh
|
4602 |
} |
|
a10f931…
|
drh
|
4603 |
|
|
7b0960d…
|
drh
|
4604 |
|
|
7b0960d…
|
drh
|
4605 |
/************************* BEGIN PERFORMANCE TIMER *****************************/ |
|
7b0960d…
|
drh
|
4606 |
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) |
|
7b0960d…
|
drh
|
4607 |
#include <sys/time.h> |
|
7b0960d…
|
drh
|
4608 |
#include <sys/resource.h> |
|
7b0960d…
|
drh
|
4609 |
/* VxWorks does not support getrusage() as far as we can determine */ |
|
7b0960d…
|
drh
|
4610 |
#if defined(_WRS_KERNEL) || defined(__RTP__) |
|
7b0960d…
|
drh
|
4611 |
struct rusage { |
|
7b0960d…
|
drh
|
4612 |
struct timeval ru_utime; /* user CPU time used */ |
|
7b0960d…
|
drh
|
4613 |
struct timeval ru_stime; /* system CPU time used */ |
|
7b0960d…
|
drh
|
4614 |
}; |
|
7b0960d…
|
drh
|
4615 |
#define getrusage(A,B) memset(B,0,sizeof(*B)) |
|
7b0960d…
|
drh
|
4616 |
#endif |
|
7b0960d…
|
drh
|
4617 |
|
|
7b0960d…
|
drh
|
4618 |
/* Saved resource information for the beginning of an operation */ |
|
7b0960d…
|
drh
|
4619 |
static struct rusage sBegin; /* CPU time at start */ |
|
7b0960d…
|
drh
|
4620 |
static sqlite3_int64 iBegin; /* Wall-clock time at start */ |
|
7b0960d…
|
drh
|
4621 |
|
|
7b0960d…
|
drh
|
4622 |
/* |
|
7b0960d…
|
drh
|
4623 |
** Begin timing an operation |
|
7b0960d…
|
drh
|
4624 |
*/ |
|
7b0960d…
|
drh
|
4625 |
static void beginTimer(ShellState *p){ |
|
7b0960d…
|
drh
|
4626 |
if( p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 ){ |
|
7b0960d…
|
drh
|
4627 |
getrusage(RUSAGE_SELF, &sBegin); |
|
7b0960d…
|
drh
|
4628 |
iBegin = timeOfDay(); |
|
7b0960d…
|
drh
|
4629 |
} |
|
7b0960d…
|
drh
|
4630 |
} |
|
7b0960d…
|
drh
|
4631 |
|
|
7b0960d…
|
drh
|
4632 |
/* Return the difference of two time_structs in seconds */ |
|
7b0960d…
|
drh
|
4633 |
static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ |
|
7b0960d…
|
drh
|
4634 |
return (pEnd->tv_usec - pStart->tv_usec)*0.000001 + |
|
7b0960d…
|
drh
|
4635 |
(double)(pEnd->tv_sec - pStart->tv_sec); |
|
7b0960d…
|
drh
|
4636 |
} |
|
7b0960d…
|
drh
|
4637 |
|
|
6c6c1fe…
|
drh
|
4638 |
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
|
7b0960d…
|
drh
|
4639 |
/* Return the time since the start of the timer in |
|
7b0960d…
|
drh
|
4640 |
** seconds. */ |
|
7b0960d…
|
drh
|
4641 |
static double elapseTime(ShellState *NotUsed){ |
|
7b0960d…
|
drh
|
4642 |
(void)NotUsed; |
|
7b0960d…
|
drh
|
4643 |
if( iBegin==0 ) return 0.0; |
|
7b0960d…
|
drh
|
4644 |
return (timeOfDay() - iBegin)*0.000001; |
|
7b0960d…
|
drh
|
4645 |
} |
|
6c6c1fe…
|
drh
|
4646 |
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
|
7b0960d…
|
drh
|
4647 |
|
|
7b0960d…
|
drh
|
4648 |
/* |
|
7b0960d…
|
drh
|
4649 |
** Print the timing results. |
|
7b0960d…
|
drh
|
4650 |
*/ |
|
7b0960d…
|
drh
|
4651 |
static void endTimer(ShellState *p){ |
|
7b0960d…
|
drh
|
4652 |
if( p->enableTimer ){ |
|
7b0960d…
|
drh
|
4653 |
sqlite3_int64 iEnd = timeOfDay(); |
|
7b0960d…
|
drh
|
4654 |
struct rusage sEnd; |
|
7b0960d…
|
drh
|
4655 |
getrusage(RUSAGE_SELF, &sEnd); |
|
7b0960d…
|
drh
|
4656 |
p->prevTimer = (iEnd - iBegin)*0.000001; |
|
7b0960d…
|
drh
|
4657 |
cli_printf(p->out, "Run Time: real %.6f user %.6f sys %.6f\n", |
|
7b0960d…
|
drh
|
4658 |
p->prevTimer, |
|
7b0960d…
|
drh
|
4659 |
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), |
|
7b0960d…
|
drh
|
4660 |
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); |
|
7b0960d…
|
drh
|
4661 |
if( p->enableTimer==1 ) p->enableTimer = 0; |
|
7b0960d…
|
drh
|
4662 |
iBegin = 0; |
|
7b0960d…
|
drh
|
4663 |
} |
|
7b0960d…
|
drh
|
4664 |
} |
|
7b0960d…
|
drh
|
4665 |
|
|
7b0960d…
|
drh
|
4666 |
#define BEGIN_TIMER(X) beginTimer(X) |
|
7b0960d…
|
drh
|
4667 |
#define END_TIMER(X) endTimer(X) |
|
7b0960d…
|
drh
|
4668 |
#define ELAPSE_TIME(X) elapseTime(X) |
|
7b0960d…
|
drh
|
4669 |
#define HAS_TIMER 1 |
|
7b0960d…
|
drh
|
4670 |
|
|
7b0960d…
|
drh
|
4671 |
#elif (defined(_WIN32) || defined(WIN32)) |
|
7b0960d…
|
drh
|
4672 |
|
|
7b0960d…
|
drh
|
4673 |
/* Saved resource information for the beginning of an operation */ |
|
7b0960d…
|
drh
|
4674 |
static HANDLE hProcess; |
|
7b0960d…
|
drh
|
4675 |
static FILETIME ftKernelBegin; |
|
7b0960d…
|
drh
|
4676 |
static FILETIME ftUserBegin; |
|
7b0960d…
|
drh
|
4677 |
static sqlite3_int64 ftWallBegin; |
|
7b0960d…
|
drh
|
4678 |
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, |
|
7b0960d…
|
drh
|
4679 |
LPFILETIME, LPFILETIME); |
|
7b0960d…
|
drh
|
4680 |
static GETPROCTIMES getProcessTimesAddr = NULL; |
|
7b0960d…
|
drh
|
4681 |
|
|
7b0960d…
|
drh
|
4682 |
/* |
|
7b0960d…
|
drh
|
4683 |
** Check to see if we have timer support. Return 1 if necessary |
|
7b0960d…
|
drh
|
4684 |
** support found (or found previously). |
|
7b0960d…
|
drh
|
4685 |
*/ |
|
7b0960d…
|
drh
|
4686 |
static int hasTimer(void){ |
|
7b0960d…
|
drh
|
4687 |
if( getProcessTimesAddr ){ |
|
7b0960d…
|
drh
|
4688 |
return 1; |
|
7b0960d…
|
drh
|
4689 |
} else { |
|
7b0960d…
|
drh
|
4690 |
/* GetProcessTimes() isn't supported in WIN95 and some other Windows |
|
7b0960d…
|
drh
|
4691 |
** versions. See if the version we are running on has it, and if it |
|
7b0960d…
|
drh
|
4692 |
** does, save off a pointer to it and the current process handle. |
|
7b0960d…
|
drh
|
4693 |
*/ |
|
7b0960d…
|
drh
|
4694 |
hProcess = GetCurrentProcess(); |
|
7b0960d…
|
drh
|
4695 |
if( hProcess ){ |
|
7b0960d…
|
drh
|
4696 |
HINSTANCE hinstLib = LoadLibrary(TEXT("Kernel32.dll")); |
|
7b0960d…
|
drh
|
4697 |
if( NULL != hinstLib ){ |
|
7b0960d…
|
drh
|
4698 |
getProcessTimesAddr = |
|
7b0960d…
|
drh
|
4699 |
(GETPROCTIMES) GetProcAddress(hinstLib, "GetProcessTimes"); |
|
7b0960d…
|
drh
|
4700 |
if( NULL != getProcessTimesAddr ){ |
|
7b0960d…
|
drh
|
4701 |
return 1; |
|
7b0960d…
|
drh
|
4702 |
} |
|
7b0960d…
|
drh
|
4703 |
FreeLibrary(hinstLib); |
|
7b0960d…
|
drh
|
4704 |
} |
|
7b0960d…
|
drh
|
4705 |
} |
|
7b0960d…
|
drh
|
4706 |
} |
|
7b0960d…
|
drh
|
4707 |
return 0; |
|
7b0960d…
|
drh
|
4708 |
} |
|
7b0960d…
|
drh
|
4709 |
|
|
7b0960d…
|
drh
|
4710 |
/* |
|
7b0960d…
|
drh
|
4711 |
** Begin timing an operation |
|
7b0960d…
|
drh
|
4712 |
*/ |
|
7b0960d…
|
drh
|
4713 |
static void beginTimer(ShellState *p){ |
|
7b0960d…
|
drh
|
4714 |
if( (p->enableTimer || (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0) |
|
7b0960d…
|
drh
|
4715 |
&& getProcessTimesAddr |
|
7b0960d…
|
drh
|
4716 |
){ |
|
7b0960d…
|
drh
|
4717 |
FILETIME ftCreation, ftExit; |
|
7b0960d…
|
drh
|
4718 |
getProcessTimesAddr(hProcess,&ftCreation,&ftExit, |
|
7b0960d…
|
drh
|
4719 |
&ftKernelBegin,&ftUserBegin); |
|
7b0960d…
|
drh
|
4720 |
ftWallBegin = timeOfDay(); |
|
7b0960d…
|
drh
|
4721 |
} |
|
7b0960d…
|
drh
|
4722 |
} |
|
7b0960d…
|
drh
|
4723 |
|
|
7b0960d…
|
drh
|
4724 |
/* Return the difference of two FILETIME structs in seconds */ |
|
7b0960d…
|
drh
|
4725 |
static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ |
|
7b0960d…
|
drh
|
4726 |
sqlite_int64 i64Start = *((sqlite_int64 *) pStart); |
|
7b0960d…
|
drh
|
4727 |
sqlite_int64 i64End = *((sqlite_int64 *) pEnd); |
|
7b0960d…
|
drh
|
4728 |
return (double) ((i64End - i64Start) / 10000000.0); |
|
7b0960d…
|
drh
|
4729 |
} |
|
7b0960d…
|
drh
|
4730 |
|
|
6c6c1fe…
|
drh
|
4731 |
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
|
7b0960d…
|
drh
|
4732 |
/* Return the time since the start of the timer in |
|
7b0960d…
|
drh
|
4733 |
** seconds. */ |
|
7b0960d…
|
drh
|
4734 |
static double elapseTime(ShellState *NotUsed){ |
|
7b0960d…
|
drh
|
4735 |
(void)NotUsed; |
|
7b0960d…
|
drh
|
4736 |
if( ftWallBegin==0 ) return 0.0; |
|
7b0960d…
|
drh
|
4737 |
return (timeOfDay() - ftWallBegin)*0.000001; |
|
7b0960d…
|
drh
|
4738 |
} |
|
6c6c1fe…
|
drh
|
4739 |
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */ |
|
7b0960d…
|
drh
|
4740 |
|
|
7b0960d…
|
drh
|
4741 |
/* |
|
7b0960d…
|
drh
|
4742 |
** Print the timing results. |
|
7b0960d…
|
drh
|
4743 |
*/ |
|
7b0960d…
|
drh
|
4744 |
static void endTimer(ShellState *p){ |
|
7b0960d…
|
drh
|
4745 |
if( p->enableTimer && getProcessTimesAddr){ |
|
7b0960d…
|
drh
|
4746 |
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; |
|
7b0960d…
|
drh
|
4747 |
sqlite3_int64 ftWallEnd = timeOfDay(); |
|
7b0960d…
|
drh
|
4748 |
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); |
|
7b0960d…
|
drh
|
4749 |
p->prevTimer = (ftWallEnd - ftWallBegin)*0.000001; |
|
7b0960d…
|
drh
|
4750 |
#ifdef _WIN64 |
|
7b0960d…
|
drh
|
4751 |
/* microsecond precision on 64-bit windows */ |
|
7b0960d…
|
drh
|
4752 |
cli_printf(p->out, "Run Time: real %.6f user %f sys %f\n", |
|
7b0960d…
|
drh
|
4753 |
p->prevTimer, |
|
7b0960d…
|
drh
|
4754 |
timeDiff(&ftUserBegin, &ftUserEnd), |
|
7b0960d…
|
drh
|
4755 |
timeDiff(&ftKernelBegin, &ftKernelEnd)); |
|
7b0960d…
|
drh
|
4756 |
#else |
|
7b0960d…
|
drh
|
4757 |
/* millisecond precisino on 32-bit windows */ |
|
7b0960d…
|
drh
|
4758 |
cli_printf(p->out, "Run Time: real %.3f user %.3f sys %.3f\n", |
|
7b0960d…
|
drh
|
4759 |
p->prevTimer, |
|
7b0960d…
|
drh
|
4760 |
timeDiff(&ftUserBegin, &ftUserEnd), |
|
7b0960d…
|
drh
|
4761 |
timeDiff(&ftKernelBegin, &ftKernelEnd)); |
|
7b0960d…
|
drh
|
4762 |
#endif |
|
7b0960d…
|
drh
|
4763 |
if( p->enableTimer==1 ) p->enableTimer = 0; |
|
7b0960d…
|
drh
|
4764 |
ftWallBegin = 0; |
|
7b0960d…
|
drh
|
4765 |
} |
|
7b0960d…
|
drh
|
4766 |
} |
|
7b0960d…
|
drh
|
4767 |
|
|
7b0960d…
|
drh
|
4768 |
#define BEGIN_TIMER(X) beginTimer(X) |
|
7b0960d…
|
drh
|
4769 |
#define ELAPSE_TIME(X) elapseTime(X) |
|
7b0960d…
|
drh
|
4770 |
#define END_TIMER(X) endTimer(X) |
|
7b0960d…
|
drh
|
4771 |
#define HAS_TIMER hasTimer() |
|
7b0960d…
|
drh
|
4772 |
|
|
7b0960d…
|
drh
|
4773 |
#else |
|
7b0960d…
|
drh
|
4774 |
#define BEGIN_TIMER(X) /* no-op */ |
|
7b0960d…
|
drh
|
4775 |
#define ELAPSE_TIME(X) 0.0 |
|
7b0960d…
|
drh
|
4776 |
#define END_TIMER(X) /*no-op*/ |
|
7b0960d…
|
drh
|
4777 |
#define HAS_TIMER 0 |
|
7b0960d…
|
drh
|
4778 |
#endif |
|
7b0960d…
|
drh
|
4779 |
/************************* END PERFORMANCE TIMER ******************************/ |
|
4f76459…
|
drh
|
4780 |
|
|
4f76459…
|
drh
|
4781 |
/* |
|
4f76459…
|
drh
|
4782 |
** Clear a display mode, freeing any allocated memory that it |
|
4f76459…
|
drh
|
4783 |
** contains. |
|
4f76459…
|
drh
|
4784 |
*/ |
|
4f76459…
|
drh
|
4785 |
static void modeFree(Mode *p){ |
|
f07aa62…
|
drh
|
4786 |
u8 autoExplain = p->autoExplain; |
|
4f76459…
|
drh
|
4787 |
free(p->spec.aWidth); |
|
4f76459…
|
drh
|
4788 |
free(p->spec.aAlign); |
|
4f76459…
|
drh
|
4789 |
free(p->spec.zColumnSep); |
|
4f76459…
|
drh
|
4790 |
free(p->spec.zRowSep); |
|
4f76459…
|
drh
|
4791 |
free(p->spec.zTableName); |
|
4f76459…
|
drh
|
4792 |
free(p->spec.zNull); |
|
4f76459…
|
drh
|
4793 |
memset(p, 0, sizeof(*p)); |
|
cb89386…
|
drh
|
4794 |
p->spec.iVersion = 1; |
|
f07aa62…
|
drh
|
4795 |
p->autoExplain = autoExplain; |
|
4f76459…
|
drh
|
4796 |
} |
|
4f76459…
|
drh
|
4797 |
|
|
4f76459…
|
drh
|
4798 |
/* |
|
4f76459…
|
drh
|
4799 |
** Duplicate Mode pSrc into pDest. pDest is assumed to be |
|
4f76459…
|
drh
|
4800 |
** uninitialized prior to invoking this routine. |
|
4f76459…
|
drh
|
4801 |
*/ |
|
4f76459…
|
drh
|
4802 |
static void modeDup(Mode *pDest, Mode *pSrc){ |
|
4f76459…
|
drh
|
4803 |
memcpy(pDest, pSrc, sizeof(*pDest)); |
|
4f76459…
|
drh
|
4804 |
if( pDest->spec.aWidth ){ |
|
4f76459…
|
drh
|
4805 |
size_t sz = sizeof(pSrc->spec.aWidth[0]) * pSrc->spec.nWidth; |
|
4f76459…
|
drh
|
4806 |
pDest->spec.aWidth = malloc( sz ); |
|
4f76459…
|
drh
|
4807 |
if( pDest->spec.aWidth ){ |
|
4f76459…
|
drh
|
4808 |
memcpy(pDest->spec.aWidth, pSrc->spec.aWidth, sz); |
|
4f76459…
|
drh
|
4809 |
}else{ |
|
4f76459…
|
drh
|
4810 |
pDest->spec.nWidth = 0; |
|
4f76459…
|
drh
|
4811 |
} |
|
4f76459…
|
drh
|
4812 |
} |
|
4f76459…
|
drh
|
4813 |
if( pDest->spec.aAlign ){ |
|
4f76459…
|
drh
|
4814 |
size_t sz = sizeof(pSrc->spec.aAlign[0]) * pSrc->spec.nAlign; |
|
4f76459…
|
drh
|
4815 |
pDest->spec.aAlign = malloc( sz ); |
|
4f76459…
|
drh
|
4816 |
if( pDest->spec.aAlign ){ |
|
4f76459…
|
drh
|
4817 |
memcpy(pDest->spec.aAlign, pSrc->spec.aAlign, sz); |
|
4f76459…
|
drh
|
4818 |
}else{ |
|
4f76459…
|
drh
|
4819 |
pDest->spec.nAlign = 0; |
|
4f76459…
|
drh
|
4820 |
} |
|
4f76459…
|
drh
|
4821 |
} |
|
4f76459…
|
drh
|
4822 |
if( pDest->spec.zColumnSep ){ |
|
4f76459…
|
drh
|
4823 |
pDest->spec.zColumnSep = strdup(pSrc->spec.zColumnSep); |
|
4f76459…
|
drh
|
4824 |
} |
|
4f76459…
|
drh
|
4825 |
if( pDest->spec.zRowSep ){ |
|
4f76459…
|
drh
|
4826 |
pDest->spec.zRowSep = strdup(pSrc->spec.zRowSep); |
|
4f76459…
|
drh
|
4827 |
} |
|
4f76459…
|
drh
|
4828 |
if( pDest->spec.zTableName ){ |
|
4f76459…
|
drh
|
4829 |
pDest->spec.zTableName = strdup(pSrc->spec.zTableName); |
|
4f76459…
|
drh
|
4830 |
} |
|
4f76459…
|
drh
|
4831 |
if( pDest->spec.zNull ){ |
|
4f76459…
|
drh
|
4832 |
pDest->spec.zNull = strdup(pSrc->spec.zNull); |
|
4f76459…
|
drh
|
4833 |
} |
|
4f76459…
|
drh
|
4834 |
} |
|
4f76459…
|
drh
|
4835 |
|
|
4f76459…
|
drh
|
4836 |
/* |
|
4f76459…
|
drh
|
4837 |
** Set a string value to a copy of the zNew string in memory |
|
4f76459…
|
drh
|
4838 |
** obtained from system malloc(). |
|
4f76459…
|
drh
|
4839 |
*/ |
|
4f76459…
|
drh
|
4840 |
static void modeSetStr(char **az, const char *zNew){ |
|
4f76459…
|
drh
|
4841 |
free(*az); |
|
4f76459…
|
drh
|
4842 |
if( zNew==0 ){ |
|
4f76459…
|
drh
|
4843 |
*az = 0; |
|
4f76459…
|
drh
|
4844 |
}else{ |
|
4f76459…
|
drh
|
4845 |
size_t n = strlen(zNew); |
|
4f76459…
|
drh
|
4846 |
*az = malloc( n+1 ); |
|
4f76459…
|
drh
|
4847 |
if( *az ){ |
|
4f76459…
|
drh
|
4848 |
memcpy(*az, zNew, n+1 ); |
|
4f76459…
|
drh
|
4849 |
} |
|
4f76459…
|
drh
|
4850 |
} |
|
4f76459…
|
drh
|
4851 |
} |
|
4f76459…
|
drh
|
4852 |
|
|
4f76459…
|
drh
|
4853 |
/* |
|
4f76459…
|
drh
|
4854 |
** Change the mode to eMode |
|
4f76459…
|
drh
|
4855 |
*/ |
|
4f76459…
|
drh
|
4856 |
static void modeChange(ShellState *p, unsigned char eMode){ |
|
4f76459…
|
drh
|
4857 |
const ModeInfo *pI; |
|
4f76459…
|
drh
|
4858 |
if( eMode<ArraySize(aModeInfo) ){ |
|
4f76459…
|
drh
|
4859 |
Mode *pM = &p->mode; |
|
0276100…
|
drh
|
4860 |
pI = &aModeInfo[eMode]; |
|
4f76459…
|
drh
|
4861 |
pM->eMode = eMode; |
|
4f76459…
|
drh
|
4862 |
if( pI->eCSep ) modeSetStr(&pM->spec.zColumnSep, aModeStr[pI->eCSep]); |
|
4f76459…
|
drh
|
4863 |
if( pI->eRSep ) modeSetStr(&pM->spec.zRowSep, aModeStr[pI->eRSep]); |
|
4f76459…
|
drh
|
4864 |
if( pI->eNull ) modeSetStr(&pM->spec.zNull, aModeStr[pI->eNull]); |
|
4f76459…
|
drh
|
4865 |
pM->spec.eText = pI->eText; |
|
4f76459…
|
drh
|
4866 |
pM->spec.eBlob = pI->eBlob; |
|
12e4a0f…
|
drh
|
4867 |
if( (pM->mFlags & MFLG_HDR)==0 ){ |
|
12e4a0f…
|
drh
|
4868 |
pM->spec.bTitles = pI->bHdr; |
|
12e4a0f…
|
drh
|
4869 |
} |
|
4f76459…
|
drh
|
4870 |
pM->spec.eTitle = pI->eHdr; |
|
f4b3b59…
|
drh
|
4871 |
if( pI->mFlg & 0x01 ){ |
|
f4b3b59…
|
drh
|
4872 |
pM->spec.bBorder = QRF_No; |
|
f4b3b59…
|
drh
|
4873 |
}else{ |
|
f4b3b59…
|
drh
|
4874 |
pM->spec.bBorder = QRF_Auto; |
|
f4b3b59…
|
drh
|
4875 |
} |
|
f4b3b59…
|
drh
|
4876 |
if( pI->mFlg & 0x02 ){ |
|
9aee493…
|
drh
|
4877 |
pM->spec.bSplitColumn = QRF_Yes; |
|
9aee493…
|
drh
|
4878 |
pM->bAutoScreenWidth = 1; |
|
9aee493…
|
drh
|
4879 |
}else{ |
|
9aee493…
|
drh
|
4880 |
pM->spec.bSplitColumn = QRF_No; |
|
9aee493…
|
drh
|
4881 |
} |
|
4f76459…
|
drh
|
4882 |
}else if( eMode>=MODE_USER && eMode-MODE_USER<p->nSavedModes ){ |
|
4f76459…
|
drh
|
4883 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
4884 |
modeDup(&p->mode, &p->aSavedModes[eMode-MODE_USER].mode); |
|
4f76459…
|
drh
|
4885 |
}else if( eMode==MODE_BATCH ){ |
|
4f76459…
|
drh
|
4886 |
u8 mFlags = p->mode.mFlags; |
|
4f76459…
|
drh
|
4887 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
4888 |
modeChange(p, MODE_List); |
|
4f76459…
|
drh
|
4889 |
p->mode.mFlags = mFlags; |
|
4f76459…
|
drh
|
4890 |
}else if( eMode==MODE_TTY ){ |
|
4f76459…
|
drh
|
4891 |
u8 mFlags = p->mode.mFlags; |
|
4f76459…
|
drh
|
4892 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
4893 |
modeChange(p, MODE_QBox); |
|
4f76459…
|
drh
|
4894 |
p->mode.bAutoScreenWidth = 1; |
|
709b566…
|
drh
|
4895 |
p->mode.spec.eText = QRF_TEXT_Relaxed; |
|
ae7e3f0…
|
drh
|
4896 |
p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT; |
|
ae7e3f0…
|
drh
|
4897 |
p->mode.spec.nLineLimit = DFLT_LINE_LIMIT; |
|
4f76459…
|
drh
|
4898 |
p->mode.spec.bTextJsonb = QRF_Yes; |
|
ae7e3f0…
|
drh
|
4899 |
p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT; |
|
17f9878…
|
drh
|
4900 |
p->mode.spec.nMultiInsert = DFLT_MULTI_INSERT; |
|
4f76459…
|
drh
|
4901 |
p->mode.mFlags = mFlags; |
|
4f76459…
|
drh
|
4902 |
} |
|
4f76459…
|
drh
|
4903 |
} |
|
4f76459…
|
drh
|
4904 |
|
|
4f76459…
|
drh
|
4905 |
/* |
|
5f65ed5…
|
drh
|
4906 |
** Set the mode to the default. It assumed that the mode has |
|
5f65ed5…
|
drh
|
4907 |
** already been freed and zeroed prior to calling this routine. |
|
4f76459…
|
drh
|
4908 |
*/ |
|
4f76459…
|
drh
|
4909 |
static void modeDefault(ShellState *p){ |
|
cb89386…
|
drh
|
4910 |
p->mode.spec.iVersion = 1; |
|
4f76459…
|
drh
|
4911 |
p->mode.autoExplain = 1; |
|
5f65ed5…
|
drh
|
4912 |
if( stdin_is_interactive || stdout_is_console ){ |
|
4f76459…
|
drh
|
4913 |
modeChange(p, MODE_TTY); |
|
4f76459…
|
drh
|
4914 |
}else{ |
|
4f76459…
|
drh
|
4915 |
modeChange(p, MODE_BATCH); |
|
4f76459…
|
drh
|
4916 |
} |
|
4f76459…
|
drh
|
4917 |
} |
|
4f76459…
|
drh
|
4918 |
|
|
4f76459…
|
drh
|
4919 |
/* |
|
4f76459…
|
drh
|
4920 |
** Find the number of a display mode given its name. Return -1 if |
|
4f76459…
|
drh
|
4921 |
** the name does not match any mode. |
|
4f76459…
|
drh
|
4922 |
** |
|
4f76459…
|
drh
|
4923 |
** Saved modes are also searched if p!=NULL. The number returned |
|
4f76459…
|
drh
|
4924 |
** for a saved mode is the index into the p->aSavedModes[] array |
|
4f76459…
|
drh
|
4925 |
** plus MODE_USER. |
|
4f76459…
|
drh
|
4926 |
** |
|
4f76459…
|
drh
|
4927 |
** Two special mode names are also available: "batch" and "tty". |
|
4f76459…
|
drh
|
4928 |
** evaluate to the default mode for batch operation and interactive |
|
4f76459…
|
drh
|
4929 |
** operation on a TTY, respectively. |
|
4f76459…
|
drh
|
4930 |
*/ |
|
4f76459…
|
drh
|
4931 |
static int modeFind(ShellState *p, const char *zName){ |
|
4f76459…
|
drh
|
4932 |
int i; |
|
4f76459…
|
drh
|
4933 |
for(i=0; i<ArraySize(aModeInfo); i++){ |
|
4f76459…
|
drh
|
4934 |
if( cli_strcmp(aModeInfo[i].zName,zName)==0 ) return i; |
|
4f76459…
|
drh
|
4935 |
} |
|
4f76459…
|
drh
|
4936 |
for(i=0; i<p->nSavedModes; i++){ |
|
4f76459…
|
drh
|
4937 |
if( cli_strcmp(p->aSavedModes[i].zTag,zName)==0 ) return i+MODE_USER; |
|
4f76459…
|
drh
|
4938 |
} |
|
4f76459…
|
drh
|
4939 |
if( strcmp(zName,"batch")==0 ) return MODE_BATCH; |
|
4f76459…
|
drh
|
4940 |
if( strcmp(zName,"tty")==0 ) return MODE_TTY; |
|
4f76459…
|
drh
|
4941 |
return -1; |
|
4f76459…
|
drh
|
4942 |
} |
|
4f76459…
|
drh
|
4943 |
|
|
4f76459…
|
drh
|
4944 |
/* |
|
4f76459…
|
drh
|
4945 |
** Save or restore the current output mode |
|
4f76459…
|
drh
|
4946 |
*/ |
|
4f76459…
|
drh
|
4947 |
static void modePush(ShellState *p){ |
|
4f76459…
|
drh
|
4948 |
if( p->nPopMode==0 ){ |
|
4f76459…
|
drh
|
4949 |
modeFree(&p->modePrior); |
|
4f76459…
|
drh
|
4950 |
modeDup(&p->modePrior,&p->mode); |
|
4f76459…
|
drh
|
4951 |
} |
|
4f76459…
|
drh
|
4952 |
} |
|
4f76459…
|
drh
|
4953 |
static void modePop(ShellState *p){ |
|
4f76459…
|
drh
|
4954 |
if( p->modePrior.spec.iVersion>0 ){ |
|
4f76459…
|
drh
|
4955 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
4956 |
p->mode = p->modePrior; |
|
4f76459…
|
drh
|
4957 |
memset(&p->modePrior, 0, sizeof(p->modePrior)); |
|
4f76459…
|
drh
|
4958 |
} |
|
4f76459…
|
drh
|
4959 |
} |
|
4f76459…
|
drh
|
4960 |
|
|
4f76459…
|
drh
|
4961 |
cli_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
|
4f76459…
|
drh
|
4962 |
cli_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); |
|
4f76459…
|
drh
|
4963 |
} |
|
4f76459…
|
drh
|
4964 |
|
|
4f76459…
|
drh
|
4965 |
/* |
|
4f76459…
|
drh
|
4966 |
** Compute the name of the location of an input error in memory |
|
4f76459…
|
drh
|
4967 |
** obtained from sqlite3_malloc(). |
|
4f76459…
|
drh
|
4968 |
*/ |
|
4f76459…
|
drh
|
4969 |
static char *shellErrorLocation(ShellState *p){ |
|
4f76459…
|
drh
|
4970 |
char *zLoc; |
|
4f76459…
|
drh
|
4971 |
if( p->zErrPrefix ){ |
|
4f76459…
|
drh
|
4972 |
zLoc = sqlite3_mprintf("%s", p->zErrPrefix); |
|
4f76459…
|
drh
|
4973 |
}else if( p->zInFile==0 || strcmp(p->zInFile,"<stdin>")==0){ |
|
4f76459…
|
drh
|
4974 |
zLoc = sqlite3_mprintf("line %lld:", p->lineno); |
|
4f76459…
|
drh
|
4975 |
}else{ |
|
4f76459…
|
drh
|
4976 |
zLoc = sqlite3_mprintf("%s:%lld:", p->zInFile, p->lineno); |
|
4f76459…
|
drh
|
4977 |
} |
|
4f76459…
|
drh
|
4978 |
return zLoc; |
|
4f76459…
|
drh
|
4979 |
char *zLoc = shellErrorLocation(p); |
|
4f76459…
|
drh
|
4980 |
cli_printf(stderr, "%s %s\n", zLoc, zMsg); |
|
4f76459…
|
drh
|
4981 |
cli_exit(1); |
|
4f76459…
|
drh
|
4982 |
|
|
4f76459…
|
drh
|
4983 |
/* |
|
4f76459…
|
drh
|
4984 |
** Issue an error message from a dot-command. |
|
4f76459…
|
drh
|
4985 |
*/ |
|
4f76459…
|
drh
|
4986 |
static void dotCmdError( |
|
4f76459…
|
drh
|
4987 |
ShellState *p, /* Shell state */ |
|
4f76459…
|
drh
|
4988 |
int iArg, /* Index of argument on which error occurred */ |
|
4f76459…
|
drh
|
4989 |
const char *zBrief, /* Brief (<20 character) error description */ |
|
4f76459…
|
drh
|
4990 |
const char *zDetail, /* Error details */ |
|
4f76459…
|
drh
|
4991 |
... |
|
4f76459…
|
drh
|
4992 |
){ |
|
4f76459…
|
drh
|
4993 |
FILE *out = stderr; |
|
4f76459…
|
drh
|
4994 |
char *zLoc = shellErrorLocation(p); |
|
4f76459…
|
drh
|
4995 |
if( zBrief!=0 && iArg>=0 && iArg<p->dot.nArg ){ |
|
4f76459…
|
drh
|
4996 |
int i = p->dot.aiOfst[iArg]; |
|
4f76459…
|
drh
|
4997 |
int nPrompt = strlen30(zBrief) + 5; |
|
4f76459…
|
drh
|
4998 |
cli_printf(out, "%s %s\n", zLoc, p->dot.zOrig); |
|
4f76459…
|
drh
|
4999 |
if( i > nPrompt ){ |
|
4f76459…
|
drh
|
5000 |
cli_printf(out, "%s %*s%s ---^\n", zLoc, 1+i-nPrompt, "", zBrief); |
|
4f76459…
|
drh
|
5001 |
}else{ |
|
4f76459…
|
drh
|
5002 |
cli_printf(out, "%s %*s^--- %s\n", zLoc, i, "", zBrief); |
|
4f76459…
|
drh
|
5003 |
} |
|
4f76459…
|
drh
|
5004 |
} |
|
4f76459…
|
drh
|
5005 |
if( zDetail ){ |
|
4f76459…
|
drh
|
5006 |
char *zMsg; |
|
4f76459…
|
drh
|
5007 |
va_list ap; |
|
4f76459…
|
drh
|
5008 |
va_start(ap, zDetail); |
|
4f76459…
|
drh
|
5009 |
zMsg = sqlite3_vmprintf(zDetail,ap); |
|
4f76459…
|
drh
|
5010 |
va_end(ap); |
|
4f76459…
|
drh
|
5011 |
cli_printf(out,"%s %s\n", zLoc, zMsg); |
|
4f76459…
|
drh
|
5012 |
sqlite3_free(zMsg); |
|
4f76459…
|
drh
|
5013 |
} |
|
4f76459…
|
drh
|
5014 |
sqlite3_free(zLoc); |
|
4f76459…
|
drh
|
5015 |
} |
|
4f76459…
|
drh
|
5016 |
|
|
4f76459…
|
drh
|
5017 |
if( p->mode.mFlags & MFLG_CRLF ){ |
|
4f76459…
|
drh
|
5018 |
cli_puts(zq, out); |
|
4f76459…
|
drh
|
5019 |
if( z==0 ) z = ""; |
|
4f76459…
|
drh
|
5020 |
while( *z!=0 ){ |
|
4f76459…
|
drh
|
5021 |
const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); |
|
4f76459…
|
drh
|
5022 |
const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); |
|
4f76459…
|
drh
|
5023 |
const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; |
|
4f76459…
|
drh
|
5024 |
if( pcEnd > z ){ |
|
4f76459…
|
drh
|
5025 |
cli_printf(out, "%.*s", (int)(pcEnd-z), z); |
|
4f76459…
|
drh
|
5026 |
} |
|
4f76459…
|
drh
|
5027 |
if( (c = *pcEnd)==0 ) break; |
|
4f76459…
|
drh
|
5028 |
++pcEnd; |
|
4f76459…
|
drh
|
5029 |
switch( c ){ |
|
4f76459…
|
drh
|
5030 |
case '\\': case '"': |
|
4f76459…
|
drh
|
5031 |
cbsSay = (char)c; |
|
4f76459…
|
drh
|
5032 |
break; |
|
4f76459…
|
drh
|
5033 |
case '\t': cbsSay = 't'; break; |
|
4f76459…
|
drh
|
5034 |
case '\n': cbsSay = 'n'; break; |
|
4f76459…
|
drh
|
5035 |
case '\r': cbsSay = 'r'; break; |
|
4f76459…
|
drh
|
5036 |
case '\f': cbsSay = 'f'; break; |
|
4f76459…
|
drh
|
5037 |
default: cbsSay = 0; break; |
|
4f76459…
|
drh
|
5038 |
} |
|
4f76459…
|
drh
|
5039 |
if( cbsSay ){ |
|
4f76459…
|
drh
|
5040 |
ace[1] = cbsSay; |
|
4f76459…
|
drh
|
5041 |
cli_puts(ace, out); |
|
4f76459…
|
drh
|
5042 |
}else if( !isprint(c&0xff) ){ |
|
4f76459…
|
drh
|
5043 |
cli_printf(out, "\\%03o", c&0xff); |
|
4f76459…
|
drh
|
5044 |
}else{ |
|
4f76459…
|
drh
|
5045 |
ace[1] = (char)c; |
|
4f76459…
|
drh
|
5046 |
cli_puts(ace+1, out); |
|
4f76459…
|
drh
|
5047 |
} |
|
4f76459…
|
drh
|
5048 |
z = pcEnd; |
|
4f76459…
|
drh
|
5049 |
} |
|
4f76459…
|
drh
|
5050 |
cli_puts(zq, out); |
|
4f76459…
|
drh
|
5051 |
} |
|
4f76459…
|
drh
|
5052 |
|
|
4f76459…
|
drh
|
5053 |
/* Encode input string z[] as a C-language string literal and |
|
4f76459…
|
drh
|
5054 |
** append it to the sqlite3_str. If z is NULL render and empty string. |
|
4f76459…
|
drh
|
5055 |
*/ |
|
4f76459…
|
drh
|
5056 |
static void append_c_string(sqlite3_str *out, const char *z){ |
|
4f76459…
|
drh
|
5057 |
char c; |
|
4f76459…
|
drh
|
5058 |
static const char *zq = "\""; |
|
4f76459…
|
drh
|
5059 |
static long ctrlMask = ~0L; |
|
4f76459…
|
drh
|
5060 |
static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ |
|
4f76459…
|
drh
|
5061 |
char ace[3] = "\\?"; |
|
4f76459…
|
drh
|
5062 |
char cbsSay; |
|
4f76459…
|
drh
|
5063 |
if( z==0 ) z = ""; |
|
4f76459…
|
drh
|
5064 |
sqlite3_str_appendall(out,zq); |
|
4f76459…
|
drh
|
5065 |
sqlite3_str_appendf(out, "%.*s", (int)(pcEnd-z), z); |
|
4f76459…
|
drh
|
5066 |
sqlite3_str_appendall(out,ace); |
|
4f76459…
|
drh
|
5067 |
}else if( !isprint(c&0xff) ){ |
|
4f76459…
|
drh
|
5068 |
sqlite3_str_appendf(out, "\\%03o", c&0xff); |
|
4f76459…
|
drh
|
5069 |
}else{ |
|
4f76459…
|
drh
|
5070 |
ace[1] = (char)c; |
|
4f76459…
|
drh
|
5071 |
sqlite3_str_appendall(out, ace+1); |
|
4f76459…
|
drh
|
5072 |
} |
|
4f76459…
|
drh
|
5073 |
z = pcEnd; |
|
4f76459…
|
drh
|
5074 |
} |
|
4f76459…
|
drh
|
5075 |
sqlite3_str_appendall(out, zq); |
|
4f76459…
|
drh
|
5076 |
if( ++seenInterrupt>1 ) cli_exit(1); |
|
4f76459…
|
drh
|
5077 |
} |
|
4f76459…
|
drh
|
5078 |
|
|
4f76459…
|
drh
|
5079 |
/* Try to determine the screen width. Use the default if unable. |
|
4f76459…
|
drh
|
5080 |
*/ |
|
4f76459…
|
drh
|
5081 |
int shellScreenWidth(void){ |
|
0276100…
|
drh
|
5082 |
if( stdout_tty_width>0 ){ |
|
0276100…
|
drh
|
5083 |
return stdout_tty_width; |
|
0276100…
|
drh
|
5084 |
}else{ |
|
4f76459…
|
drh
|
5085 |
#if defined(TIOCGSIZE) |
|
0276100…
|
drh
|
5086 |
struct ttysize ts; |
|
0276100…
|
drh
|
5087 |
if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 |
|
0276100…
|
drh
|
5088 |
|| ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 |
|
0276100…
|
drh
|
5089 |
|| ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 |
|
0276100…
|
drh
|
5090 |
){ |
|
0276100…
|
drh
|
5091 |
return ts.ts_cols; |
|
0276100…
|
drh
|
5092 |
} |
|
4f76459…
|
drh
|
5093 |
#elif defined(TIOCGWINSZ) |
|
0276100…
|
drh
|
5094 |
struct winsize ws; |
|
0276100…
|
drh
|
5095 |
if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 |
|
0276100…
|
drh
|
5096 |
|| ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 |
|
0276100…
|
drh
|
5097 |
|| ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 |
|
0276100…
|
drh
|
5098 |
){ |
|
0276100…
|
drh
|
5099 |
return ws.ws_col; |
|
0276100…
|
drh
|
5100 |
} |
|
4f76459…
|
drh
|
5101 |
#elif defined(_WIN32) |
|
0276100…
|
drh
|
5102 |
CONSOLE_SCREEN_BUFFER_INFO csbi; |
|
0276100…
|
drh
|
5103 |
if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) |
|
0276100…
|
drh
|
5104 |
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) |
|
0276100…
|
drh
|
5105 |
|| GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) |
|
0276100…
|
drh
|
5106 |
){ |
|
0276100…
|
drh
|
5107 |
return csbi.srWindow.Right - csbi.srWindow.Left + 1; |
|
0276100…
|
drh
|
5108 |
} |
|
4f76459…
|
drh
|
5109 |
#endif |
|
4f76459…
|
drh
|
5110 |
#define DEFAULT_SCREEN_WIDTH 80 |
|
0276100…
|
drh
|
5111 |
return DEFAULT_SCREEN_WIDTH; |
|
0276100…
|
drh
|
5112 |
} |
|
81eeb1c…
|
drh
|
5113 |
"realpath", |
|
4f76459…
|
drh
|
5114 |
cli_printf(p->out, "authorizer: %s", azAction[op]); |
|
4f76459…
|
drh
|
5115 |
cli_puts(" ", p->out); |
|
4f76459…
|
drh
|
5116 |
cli_puts("NULL", p->out); |
|
4f76459…
|
drh
|
5117 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
5118 |
** Print a schema statement. This is helper routine to dump_callbac(). |
|
4f76459…
|
drh
|
5119 |
cli_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); |
|
4f76459…
|
drh
|
5120 |
cli_printf(out, "%s%s", z, zTail); |
|
4f76459…
|
drh
|
5121 |
** SQL Function: shell_format_schema(SQL,FLAGS) |
|
4f76459…
|
drh
|
5122 |
** |
|
4f76459…
|
drh
|
5123 |
** This function is internally by the CLI to assist with the |
|
4f76459…
|
drh
|
5124 |
** ".schema", ".fullschema", and ".dump" commands. The first |
|
4f76459…
|
drh
|
5125 |
** argument is the value from sqlite_schema.sql. The value returned |
|
4f76459…
|
drh
|
5126 |
** is a modification of the input that can actually be run as SQL |
|
4f76459…
|
drh
|
5127 |
** to recreate the schema object. |
|
4f76459…
|
drh
|
5128 |
** |
|
4f76459…
|
drh
|
5129 |
** When FLAGS is zero, the only changes is to append ";". If the |
|
4f76459…
|
drh
|
5130 |
** 0x01 bit of FLAGS is set, then transformations are made to implement |
|
4f76459…
|
drh
|
5131 |
** ".schema --indent". |
|
4f76459…
|
drh
|
5132 |
*/ |
|
4f76459…
|
drh
|
5133 |
static void shellFormatSchema( |
|
4f76459…
|
drh
|
5134 |
sqlite3_context *pCtx, |
|
4f76459…
|
drh
|
5135 |
int nVal, |
|
4f76459…
|
drh
|
5136 |
sqlite3_value **apVal |
|
4f76459…
|
drh
|
5137 |
){ |
|
4f76459…
|
drh
|
5138 |
int flags; /* Value of 2nd parameter */ |
|
4f76459…
|
drh
|
5139 |
const char *zSql; /* Value of 1st parameter */ |
|
4f76459…
|
drh
|
5140 |
int nSql; /* Bytes of text in zSql[] */ |
|
4f76459…
|
drh
|
5141 |
sqlite3_str *pOut; /* Output buffer */ |
|
4f76459…
|
drh
|
5142 |
char *z; /* Writable copy of zSql */ |
|
4f76459…
|
drh
|
5143 |
int i, j; /* Loop counters */ |
|
4f76459…
|
drh
|
5144 |
int nParen = 0; |
|
4f76459…
|
drh
|
5145 |
char cEnd = 0; |
|
4f76459…
|
drh
|
5146 |
char c; |
|
4f76459…
|
drh
|
5147 |
int nLine = 0; |
|
4f76459…
|
drh
|
5148 |
int isIndex; |
|
4f76459…
|
drh
|
5149 |
int isWhere = 0; |
|
4f76459…
|
drh
|
5150 |
|
|
4f76459…
|
drh
|
5151 |
assert( nVal==2 ); |
|
4f76459…
|
drh
|
5152 |
pOut = sqlite3_str_new(sqlite3_context_db_handle(pCtx)); |
|
4f76459…
|
drh
|
5153 |
nSql = sqlite3_value_bytes(apVal[0]); |
|
4f76459…
|
drh
|
5154 |
zSql = (const char*)sqlite3_value_text(apVal[0]); |
|
4f76459…
|
drh
|
5155 |
if( zSql==0 || zSql[0]==0 ) goto shellFormatSchema_finish; |
|
4f76459…
|
drh
|
5156 |
flags = sqlite3_value_int(apVal[1]); |
|
4f76459…
|
drh
|
5157 |
if( (flags & 0x01)==0 ){ |
|
4f76459…
|
drh
|
5158 |
sqlite3_str_append(pOut, zSql, nSql); |
|
4f76459…
|
drh
|
5159 |
sqlite3_str_append(pOut, ";", 1); |
|
4f76459…
|
drh
|
5160 |
goto shellFormatSchema_finish; |
|
4f76459…
|
drh
|
5161 |
} |
|
4f76459…
|
drh
|
5162 |
if( sqlite3_strlike("CREATE VIEW%", zSql, 0)==0 |
|
4f76459…
|
drh
|
5163 |
|| sqlite3_strlike("CREATE TRIG%", zSql, 0)==0 |
|
4f76459…
|
drh
|
5164 |
){ |
|
4f76459…
|
drh
|
5165 |
sqlite3_str_append(pOut, zSql, nSql); |
|
4f76459…
|
drh
|
5166 |
sqlite3_str_append(pOut, ";", 1); |
|
4f76459…
|
drh
|
5167 |
goto shellFormatSchema_finish; |
|
4f76459…
|
drh
|
5168 |
} |
|
4f76459…
|
drh
|
5169 |
isIndex = sqlite3_strlike("CREATE INDEX%", zSql, 0)==0 |
|
4f76459…
|
drh
|
5170 |
|| sqlite3_strlike("CREATE UNIQUE INDEX%", zSql, 0)==0; |
|
4f76459…
|
drh
|
5171 |
z = sqlite3_mprintf("%s", zSql); |
|
4f76459…
|
drh
|
5172 |
if( z==0 ){ |
|
4f76459…
|
drh
|
5173 |
sqlite3_str_free(pOut); |
|
4f76459…
|
drh
|
5174 |
sqlite3_result_error_nomem(pCtx); |
|
4f76459…
|
drh
|
5175 |
return; |
|
4f76459…
|
drh
|
5176 |
} |
|
4f76459…
|
drh
|
5177 |
j = 0; |
|
4f76459…
|
drh
|
5178 |
for(i=0; IsSpace(z[i]); i++){} |
|
4f76459…
|
drh
|
5179 |
for(; (c = z[i])!=0; i++){ |
|
4f76459…
|
drh
|
5180 |
if( IsSpace(c) ){ |
|
4f76459…
|
drh
|
5181 |
if( z[j-1]=='\r' ) z[j-1] = '\n'; |
|
4f76459…
|
drh
|
5182 |
if( IsSpace(z[j-1]) || z[j-1]=='(' ) continue; |
|
4f76459…
|
drh
|
5183 |
}else if( (c=='(' || c==')') && j>0 && IsSpace(z[j-1]) ){ |
|
4f76459…
|
drh
|
5184 |
j--; |
|
4f76459…
|
drh
|
5185 |
} |
|
4f76459…
|
drh
|
5186 |
z[j++] = c; |
|
4f76459…
|
drh
|
5187 |
} |
|
4f76459…
|
drh
|
5188 |
while( j>0 && IsSpace(z[j-1]) ){ j--; } |
|
4f76459…
|
drh
|
5189 |
z[j] = 0; |
|
4f76459…
|
drh
|
5190 |
if( strlen30(z)>=79 ){ |
|
4f76459…
|
drh
|
5191 |
for(i=j=0; (c = z[i])!=0; i++){ /* Copy from z[i] back to z[j] */ |
|
4f76459…
|
drh
|
5192 |
if( c==cEnd ){ |
|
4f76459…
|
drh
|
5193 |
cEnd = 0; |
|
d326547…
|
drh
|
5194 |
}else if( cEnd!=0){ |
|
d326547…
|
drh
|
5195 |
/* No-op */ |
|
4f76459…
|
drh
|
5196 |
}else if( c=='"' || c=='\'' || c=='`' ){ |
|
4f76459…
|
drh
|
5197 |
cEnd = c; |
|
4f76459…
|
drh
|
5198 |
}else if( c=='[' ){ |
|
4f76459…
|
drh
|
5199 |
cEnd = ']'; |
|
4f76459…
|
drh
|
5200 |
}else if( c=='-' && z[i+1]=='-' ){ |
|
4f76459…
|
drh
|
5201 |
cEnd = '\n'; |
|
4f76459…
|
drh
|
5202 |
}else if( c=='(' ){ |
|
4f76459…
|
drh
|
5203 |
nParen++; |
|
4f76459…
|
drh
|
5204 |
}else if( c==')' ){ |
|
4f76459…
|
drh
|
5205 |
nParen--; |
|
4f76459…
|
drh
|
5206 |
if( nLine>0 && nParen==0 && j>0 && !isWhere ){ |
|
4f76459…
|
drh
|
5207 |
sqlite3_str_append(pOut, z, j); |
|
4f76459…
|
drh
|
5208 |
sqlite3_str_append(pOut, "\n", 1); |
|
4f76459…
|
drh
|
5209 |
j = 0; |
|
4f76459…
|
drh
|
5210 |
} |
|
4f76459…
|
drh
|
5211 |
}else if( (c=='w' || c=='W') |
|
4f76459…
|
drh
|
5212 |
&& nParen==0 && isIndex |
|
4f76459…
|
drh
|
5213 |
&& sqlite3_strnicmp("WHERE",&z[i],5)==0 |
|
4f76459…
|
drh
|
5214 |
&& !IsAlnum(z[i+5]) && z[i+5]!='_' ){ |
|
4f76459…
|
drh
|
5215 |
isWhere = 1; |
|
4f76459…
|
drh
|
5216 |
}else if( isWhere && (c=='A' || c=='a') |
|
4f76459…
|
drh
|
5217 |
&& nParen==0 |
|
4f76459…
|
drh
|
5218 |
&& sqlite3_strnicmp("AND",&z[i],3)==0 |
|
4f76459…
|
drh
|
5219 |
&& !IsAlnum(z[i+3]) && z[i+3]!='_' ){ |
|
4f76459…
|
drh
|
5220 |
sqlite3_str_append(pOut, z, j); |
|
4f76459…
|
drh
|
5221 |
sqlite3_str_append(pOut, "\n ", 5); |
|
4f76459…
|
drh
|
5222 |
j = 0; |
|
4f76459…
|
drh
|
5223 |
} |
|
4f76459…
|
drh
|
5224 |
z[j++] = c; |
|
4f76459…
|
drh
|
5225 |
if( nParen==1 && cEnd==0 |
|
4f76459…
|
drh
|
5226 |
&& (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
|
4f76459…
|
drh
|
5227 |
&& !isWhere |
|
4f76459…
|
drh
|
5228 |
){ |
|
4f76459…
|
drh
|
5229 |
if( c=='\n' ) j--; |
|
4f76459…
|
drh
|
5230 |
sqlite3_str_append(pOut, z, j); |
|
4f76459…
|
drh
|
5231 |
sqlite3_str_append(pOut, "\n ", 3); |
|
4f76459…
|
drh
|
5232 |
j = 0; |
|
4f76459…
|
drh
|
5233 |
nLine++; |
|
4f76459…
|
drh
|
5234 |
while( IsSpace(z[i+1]) ){ i++; } |
|
4f76459…
|
drh
|
5235 |
} |
|
4f76459…
|
drh
|
5236 |
} |
|
4f76459…
|
drh
|
5237 |
z[j] = 0; |
|
4f76459…
|
drh
|
5238 |
} |
|
4f76459…
|
drh
|
5239 |
sqlite3_str_appendall(pOut, z); |
|
4f76459…
|
drh
|
5240 |
sqlite3_str_append(pOut, ";", 1); |
|
4f76459…
|
drh
|
5241 |
sqlite3_free(z); |
|
4f76459…
|
drh
|
5242 |
|
|
4f76459…
|
drh
|
5243 |
shellFormatSchema_finish: |
|
4f76459…
|
drh
|
5244 |
sqlite3_result_text(pCtx, sqlite3_str_finish(pOut), -1, sqlite3_free); |
|
7b0960d…
|
drh
|
5245 |
if( (p->flgProgress & SHELL_PROGRESS_TMOUT)!=0 |
|
7b0960d…
|
drh
|
5246 |
&& ELAPSE_TIME(p)>=p->tmProgress |
|
7b0960d…
|
drh
|
5247 |
){ |
|
7b0960d…
|
drh
|
5248 |
cli_printf(p->out, "Progress timeout after %.6f seconds\n", |
|
7b0960d…
|
drh
|
5249 |
ELAPSE_TIME(p)); |
|
7b0960d…
|
drh
|
5250 |
return 1; |
|
7b0960d…
|
drh
|
5251 |
} |
|
4f76459…
|
drh
|
5252 |
cli_printf(p->out, "Progress limit reached (%u)\n", p->nProgress); |
|
4f76459…
|
drh
|
5253 |
cli_printf(p->out, "Progress %u\n", p->nProgress); |
|
4f76459…
|
drh
|
5254 |
cli_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); |
|
4f76459…
|
drh
|
5255 |
p->zDestTable = sqlite3_mprintf("%s", zName); |
|
4f76459…
|
drh
|
5256 |
cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", |
|
4f76459…
|
drh
|
5257 |
cli_printf(p->out, "%s", z); |
|
4f76459…
|
drh
|
5258 |
cli_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); |
|
4f76459…
|
drh
|
5259 |
cli_puts("\n;\n", p->out); |
|
4f76459…
|
drh
|
5260 |
cli_puts(";\n", p->out); |
|
4f76459…
|
drh
|
5261 |
cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n", |
|
4f76459…
|
drh
|
5262 |
cli_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); |
|
4f76459…
|
drh
|
5263 |
cli_printf(out, "%-36s %s\n", zLabel, zLine); |
|
3c639f7…
|
drh
|
5264 |
int iCur, iHiwtr; |
|
3c639f7…
|
drh
|
5265 |
sqlite3_int64 iCur64, iHiwtr64; |
|
4f76459…
|
drh
|
5266 |
cli_printf(out, "%-36s %d\n", "Number of output columns:", nCol); |
|
4f76459…
|
drh
|
5267 |
cli_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); |
|
4f76459…
|
drh
|
5268 |
cli_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); |
|
4f76459…
|
drh
|
5269 |
cli_printf(out, "%-36s %s\n", z, |
|
4f76459…
|
drh
|
5270 |
cli_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); |
|
4f76459…
|
drh
|
5271 |
cli_printf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); |
|
4f76459…
|
drh
|
5272 |
cli_printf(out, "VM-steps: %d\n", iCur); |
|
4f76459…
|
drh
|
5273 |
cli_printf(out, |
|
4f76459…
|
drh
|
5274 |
cli_printf(out, |
|
4f76459…
|
drh
|
5275 |
cli_printf(out, |
|
4f76459…
|
drh
|
5276 |
cli_printf(out, |
|
4f76459…
|
drh
|
5277 |
cli_printf(out, |
|
4f76459…
|
drh
|
5278 |
cli_printf(out, |
|
4f76459…
|
drh
|
5279 |
cli_printf(out, |
|
3c639f7…
|
drh
|
5280 |
iHiwtr64 = iCur64 = -1; |
|
3c639f7…
|
drh
|
5281 |
sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64, |
|
3c639f7…
|
drh
|
5282 |
0); |
|
4f76459…
|
drh
|
5283 |
cli_printf(out, |
|
4f76459…
|
drh
|
5284 |
cli_printf(out, |
|
4f76459…
|
drh
|
5285 |
cli_printf(out, |
|
3c639f7…
|
drh
|
5286 |
"Temporary data spilled to disk: %lld\n", iCur64); |
|
3c639f7…
|
drh
|
5287 |
sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64, |
|
3c639f7…
|
drh
|
5288 |
1); |
|
4f76459…
|
drh
|
5289 |
cli_printf(out, |
|
4f76459…
|
drh
|
5290 |
cli_printf(out, |
|
4f76459…
|
drh
|
5291 |
cli_printf(out, |
|
4f76459…
|
drh
|
5292 |
cli_printf(out, |
|
4f76459…
|
drh
|
5293 |
cli_printf(out, |
|
4f76459…
|
drh
|
5294 |
cli_printf(out, |
|
4f76459…
|
drh
|
5295 |
cli_printf(out, |
|
4f76459…
|
drh
|
5296 |
cli_printf(out, |
|
4f76459…
|
drh
|
5297 |
cli_printf(out, |
|
4f76459…
|
drh
|
5298 |
cli_printf(out, |
|
7b0960d…
|
drh
|
5299 |
}else if( strcmp(zVar, "$TIMER")==0 ){ |
|
7b0960d…
|
drh
|
5300 |
sqlite3_bind_double(pStmt, i, pArg->prevTimer); |
|
70539ee…
|
drh
|
5301 |
#ifdef SQLITE_ENABLE_CARRAY |
|
70539ee…
|
drh
|
5302 |
}else if( strncmp(zVar, "$carray_", 8)==0 ){ |
|
70539ee…
|
drh
|
5303 |
static char *azColorNames[] = { |
|
70539ee…
|
drh
|
5304 |
"azure", "black", "blue", "brown", "cyan", "fuchsia", "gold", |
|
70539ee…
|
drh
|
5305 |
"gray", "green", "indigo", "khaki", "lime", "magenta", "maroon", |
|
70539ee…
|
drh
|
5306 |
"navy", "olive", "orange", "pink", "purple", "red", "silver", |
|
70539ee…
|
drh
|
5307 |
"tan", "teal", "violet", "white", "yellow" |
|
70539ee…
|
drh
|
5308 |
}; |
|
70539ee…
|
drh
|
5309 |
static int aPrimes[] = { |
|
70539ee…
|
drh
|
5310 |
1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, |
|
70539ee…
|
drh
|
5311 |
53, 59, 61, 67, 71, 73, 79, 83, 89, 97 |
|
70539ee…
|
drh
|
5312 |
}; |
|
70539ee…
|
drh
|
5313 |
/* Special bindings: carray($carray_clr), carray($carray_primes) |
|
70539ee…
|
drh
|
5314 |
** with --unsafe-testing: carray($carray_clr_p,26,'char*'), |
|
70539ee…
|
drh
|
5315 |
** carray($carray_primes_p,26,'int32') |
|
70539ee…
|
drh
|
5316 |
*/ |
|
70539ee…
|
drh
|
5317 |
if( strcmp(zVar+8,"clr")==0 ){ |
|
70539ee…
|
drh
|
5318 |
sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0); |
|
70539ee…
|
drh
|
5319 |
}else if( strcmp(zVar+8,"primes")==0 ){ |
|
70539ee…
|
drh
|
5320 |
sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0); |
|
70539ee…
|
drh
|
5321 |
}else if( strcmp(zVar+8,"clr_p")==0 |
|
70539ee…
|
drh
|
5322 |
&& ShellHasFlag(pArg,SHFLG_TestingMode) ){ |
|
70539ee…
|
drh
|
5323 |
sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0); |
|
70539ee…
|
drh
|
5324 |
}else if( strcmp(zVar+8,"primes_p")==0 |
|
70539ee…
|
drh
|
5325 |
&& ShellHasFlag(pArg,SHFLG_TestingMode) ){ |
|
70539ee…
|
drh
|
5326 |
sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0); |
|
70539ee…
|
drh
|
5327 |
}else{ |
|
70539ee…
|
drh
|
5328 |
sqlite3_bind_null(pStmt, i); |
|
70539ee…
|
drh
|
5329 |
} |
|
70539ee…
|
drh
|
5330 |
#endif |
|
4f76459…
|
drh
|
5331 |
cli_puts("-- Candidates -----------------------------\n", out); |
|
4f76459…
|
drh
|
5332 |
cli_printf(out, "%s\n", zCand); |
|
4f76459…
|
drh
|
5333 |
cli_printf(out, |
|
4f76459…
|
drh
|
5334 |
cli_printf(out, "%s\n%s\n", zIdx, zEQP); |
|
4f76459…
|
drh
|
5335 |
cli_printf(stderr, "option requires an argument: %s\n", z); |
|
4f76459…
|
drh
|
5336 |
cli_printf(stderr,"value out of range: %s\n", azArg[i]); |
|
4f76459…
|
drh
|
5337 |
cli_printf(stderr,"unknown option: %s\n", z); |
|
4f76459…
|
drh
|
5338 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5339 |
|
|
4f76459…
|
drh
|
5340 |
/* |
|
4f76459…
|
drh
|
5341 |
** QRF write callback |
|
4f76459…
|
drh
|
5342 |
*/ |
|
4f76459…
|
drh
|
5343 |
static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){ |
|
4f76459…
|
drh
|
5344 |
ShellState *pArg = (ShellState*)pX; |
|
4f76459…
|
drh
|
5345 |
cli_printf(pArg->out, "%.*s", (int)n, z); |
|
4f76459…
|
drh
|
5346 |
return SQLITE_OK; |
|
4f76459…
|
drh
|
5347 |
} |
|
4f76459…
|
drh
|
5348 |
unsigned char eStyle; |
|
4f76459…
|
drh
|
5349 |
sqlite3_qrf_spec spec; |
|
4f76459…
|
drh
|
5350 |
} |
|
4f76459…
|
drh
|
5351 |
memcpy(&spec, &pArg->mode.spec, sizeof(spec)); |
|
4f76459…
|
drh
|
5352 |
spec.xWrite = shellWriteQR; |
|
4f76459…
|
drh
|
5353 |
spec.pWriteArg = (void*)pArg; |
|
cb89386…
|
drh
|
5354 |
if( pArg->mode.eMode==MODE_Insert && ShellHasFlag(pArg,SHFLG_PreserveRowid) ){ |
|
4f76459…
|
drh
|
5355 |
spec.bTitles = QRF_SW_On; |
|
4f76459…
|
drh
|
5356 |
} |
|
cb89386…
|
drh
|
5357 |
/* ,- This is true, but it is omitted |
|
cb89386…
|
drh
|
5358 |
** vvvvvvvvvvvvvvvvvvv ----- to avoid compiler warnings. */ |
|
cb89386…
|
drh
|
5359 |
assert( /*pArg->mode.eMode>=0 &&*/ pArg->mode.eMode<ArraySize(aModeInfo) ); |
|
4f76459…
|
drh
|
5360 |
eStyle = aModeInfo[pArg->mode.eMode].eStyle; |
|
4f76459…
|
drh
|
5361 |
if( pArg->mode.bAutoScreenWidth ){ |
|
4f76459…
|
drh
|
5362 |
spec.nScreenWidth = shellScreenWidth(); |
|
45de97f…
|
drh
|
5363 |
} |
|
45de97f…
|
drh
|
5364 |
if( spec.eBlob==QRF_BLOB_Auto ){ |
|
45de97f…
|
drh
|
5365 |
switch( spec.eText ){ |
|
709b566…
|
drh
|
5366 |
case QRF_TEXT_Relaxed: /* fall through */ |
|
45de97f…
|
drh
|
5367 |
case QRF_TEXT_Sql: spec.eBlob = QRF_BLOB_Sql; break; |
|
45de97f…
|
drh
|
5368 |
case QRF_TEXT_Json: spec.eBlob = QRF_BLOB_Json; break; |
|
45de97f…
|
drh
|
5369 |
default: spec.eBlob = QRF_BLOB_Text; break; |
|
45de97f…
|
drh
|
5370 |
} |
|
d326547…
|
drh
|
5371 |
while( zSql && zSql[0] && (SQLITE_OK == rc) ){ |
|
4f76459…
|
drh
|
5372 |
int isExplain; |
|
4f76459…
|
drh
|
5373 |
/* save off the prepared statement handle */ |
|
4f76459…
|
drh
|
5374 |
|
|
4f76459…
|
drh
|
5375 |
isExplain = sqlite3_stmt_isexplain(pStmt); |
|
7b0960d…
|
drh
|
5376 |
if( pArg && pArg->mode.autoEQP && isExplain==0 && pArg->dot.nArg==0 ){ |
|
7b0960d…
|
drh
|
5377 |
u8 savedEnableTimer = pArg->enableTimer; |
|
7b0960d…
|
drh
|
5378 |
pArg->enableTimer = 0; |
|
4f76459…
|
drh
|
5379 |
if( pArg->mode.autoEQP>=AUTOEQP_trigger ){ |
|
4f76459…
|
drh
|
5380 |
sqlite3_reset(pStmt); |
|
4f76459…
|
drh
|
5381 |
spec.eStyle = QRF_STYLE_Auto; |
|
13c221a…
|
drh
|
5382 |
sqlite3_stmt_explain(pStmt, 2); |
|
4f76459…
|
drh
|
5383 |
sqlite3_format_query_result(pStmt, &spec, 0); |
|
13c221a…
|
drh
|
5384 |
if( pArg->mode.autoEQP>=AUTOEQP_full ){ |
|
13c221a…
|
drh
|
5385 |
sqlite3_reset(pStmt); |
|
13c221a…
|
drh
|
5386 |
sqlite3_stmt_explain(pStmt, 1); |
|
13c221a…
|
drh
|
5387 |
sqlite3_format_query_result(pStmt, &spec, 0); |
|
13c221a…
|
drh
|
5388 |
} |
|
13c221a…
|
drh
|
5389 |
|
|
4f76459…
|
drh
|
5390 |
if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){ |
|
7b0960d…
|
drh
|
5391 |
pArg->enableTimer = savedEnableTimer; |
|
4f76459…
|
drh
|
5392 |
if( isExplain && pArg->mode.autoExplain ){ |
|
4f76459…
|
drh
|
5393 |
spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp; |
|
4f76459…
|
drh
|
5394 |
sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
|
4f76459…
|
drh
|
5395 |
}else if( pArg->mode.eMode==MODE_Www ){ |
|
4f76459…
|
drh
|
5396 |
cli_printf(pArg->out, |
|
4f76459…
|
drh
|
5397 |
"</PRE>\n" |
|
4f76459…
|
drh
|
5398 |
"<TABLE border='1' cellspacing='0' cellpadding='2'>\n"); |
|
4f76459…
|
drh
|
5399 |
spec.eStyle = QRF_STYLE_Html; |
|
4f76459…
|
drh
|
5400 |
sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
|
4f76459…
|
drh
|
5401 |
cli_printf(pArg->out, |
|
4f76459…
|
drh
|
5402 |
"</TABLE>\n" |
|
4f76459…
|
drh
|
5403 |
"<PRE>"); |
|
4f76459…
|
drh
|
5404 |
}else{ |
|
4f76459…
|
drh
|
5405 |
spec.eStyle = eStyle; |
|
4f76459…
|
drh
|
5406 |
sqlite3_format_query_result(pStmt, &spec, pzErrMsg); |
|
4f76459…
|
drh
|
5407 |
} |
|
4f76459…
|
drh
|
5408 |
if( pArg && pArg->mode.scanstatsOn ){ |
|
4f76459…
|
drh
|
5409 |
char *zErr = 0; |
|
4f76459…
|
drh
|
5410 |
switch( pArg->mode.scanstatsOn ){ |
|
4f76459…
|
drh
|
5411 |
case 1: spec.eStyle = QRF_STYLE_Stats; break; |
|
4f76459…
|
drh
|
5412 |
case 2: spec.eStyle = QRF_STYLE_StatsEst; break; |
|
4f76459…
|
drh
|
5413 |
default: spec.eStyle = QRF_STYLE_StatsVm; break; |
|
4f76459…
|
drh
|
5414 |
} |
|
4f76459…
|
drh
|
5415 |
sqlite3_reset(pStmt); |
|
4f76459…
|
drh
|
5416 |
rc = sqlite3_format_query_result(pStmt, &spec, &zErr); |
|
4f76459…
|
drh
|
5417 |
if( rc ){ |
|
4f76459…
|
drh
|
5418 |
cli_printf(stderr, "Stats query failed: %s\n", zErr); |
|
4f76459…
|
drh
|
5419 |
sqlite3_free(zErr); |
|
4f76459…
|
drh
|
5420 |
} |
|
8690598…
|
drh
|
5421 |
/* Forward reference */ |
|
8690598…
|
drh
|
5422 |
static int db_int(sqlite3 *db, const char *zSql, ...); |
|
8690598…
|
drh
|
5423 |
|
|
8690598…
|
drh
|
5424 |
/* The sqlite_sequence table is repopulated last. Delete content |
|
8690598…
|
drh
|
5425 |
** in the sqlite_sequence table added by prior repopulations prior to |
|
8690598…
|
drh
|
5426 |
** repopulating sqlite_sequence itself. But only do this if the |
|
8690598…
|
drh
|
5427 |
** table is non-empty, because if it is empty the table might not |
|
8690598…
|
drh
|
5428 |
** have been recreated by prior repopulations. See forum posts: |
|
8690598…
|
drh
|
5429 |
** 2024-10-13T17:10:01z and 2025-10-29T19:38:43z |
|
8690598…
|
drh
|
5430 |
*/ |
|
8690598…
|
drh
|
5431 |
if( db_int(p->db, "SELECT count(*) FROM sqlite_sequence")>0 ){ |
|
8690598…
|
drh
|
5432 |
if( !p->writableSchema ){ |
|
4f76459…
|
drh
|
5433 |
cli_puts("PRAGMA writable_schema=ON;\n", p->out); |
|
8690598…
|
drh
|
5434 |
p->writableSchema = 1; |
|
8690598…
|
drh
|
5435 |
} |
|
4f76459…
|
drh
|
5436 |
cli_puts("CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq);\n" |
|
8690598…
|
drh
|
5437 |
"DELETE FROM sqlite_sequence;\n", p->out); |
|
8690598…
|
drh
|
5438 |
} |
|
4f76459…
|
drh
|
5439 |
if( !dataOnly ) cli_puts("ANALYZE sqlite_schema;\n", p->out); |
|
4f76459…
|
drh
|
5440 |
cli_puts("PRAGMA writable_schema=ON;\n", p->out); |
|
4f76459…
|
drh
|
5441 |
cli_printf(p->out, "%s\n", zIns); |
|
4f76459…
|
drh
|
5442 |
Mode savedMode; |
|
4f76459…
|
drh
|
5443 |
|
|
4f76459…
|
drh
|
5444 |
p->mode.spec.zTableName = (char*)zTable; |
|
4f76459…
|
drh
|
5445 |
p->mode.eMode = MODE_Insert; |
|
45de97f…
|
drh
|
5446 |
p->mode.spec.eText = QRF_TEXT_Sql; |
|
45de97f…
|
drh
|
5447 |
p->mode.spec.eBlob = QRF_BLOB_Sql; |
|
4f76459…
|
drh
|
5448 |
p->mode.spec.bTitles = QRF_No; |
|
17f9878…
|
drh
|
5449 |
p->mode.spec.nCharLimit = 0; |
|
4f76459…
|
drh
|
5450 |
cli_puts("/****** CORRUPTION ERROR *******/\n", p->out); |
|
4f76459…
|
drh
|
5451 |
cli_puts("/****** CORRUPTION ERROR *******/\n", p->out); |
|
4f76459…
|
drh
|
5452 |
cli_printf(p->out, "/****** %s ******/\n", zErr); |
|
4f76459…
|
drh
|
5453 |
cli_printf(p->out, "/****** ERROR: %s ******/\n", zErr); |
|
5f65ed5…
|
drh
|
5454 |
".check OPTIONS ... Verify the results of a .testcase", |
|
4f76459…
|
drh
|
5455 |
",headers on|off Turn display of headers on or off", |
|
3c639f7…
|
drh
|
5456 |
".imposter INDEX TABLE Create imposter table TABLE on index INDEX", |
|
17f9878…
|
drh
|
5457 |
".indexes ?PATTERN? Show names of indexes matching PATTERN", |
|
17f9878…
|
drh
|
5458 |
" -a|--all Also show system-generated indexes", |
|
17f9878…
|
drh
|
5459 |
" --expr Show only expression indexes", |
|
17f9878…
|
drh
|
5460 |
" --sys Show only system-generated indexes", |
|
92871e0…
|
drh
|
5461 |
" --normal FILE is an ordinary SQLite database", |
|
7b0960d…
|
drh
|
5462 |
" --timeout S Halt after running for S seconds", |
|
4f76459…
|
drh
|
5463 |
",separator COL ?ROW? Change the column and row separators", |
|
4f76459…
|
drh
|
5464 |
",show Show the current values for various settings", |
|
5f65ed5…
|
drh
|
5465 |
".testcase NAME Begin a test case.", |
|
7b0960d…
|
drh
|
5466 |
".timer on|off|once Turn SQL timer on or off.", |
|
4f76459…
|
drh
|
5467 |
",width NUM1 NUM2 ... Set minimum column widths for columnar output", |
|
4f76459…
|
drh
|
5468 |
|
|
4f76459…
|
drh
|
5469 |
/************************************************************** |
|
4f76459…
|
drh
|
5470 |
** "Usage" help text automatically generated from comments */ |
|
4f76459…
|
drh
|
5471 |
static const struct { |
|
4f76459…
|
drh
|
5472 |
const char *zCmd; /* Name of the dot-command */ |
|
4f76459…
|
drh
|
5473 |
const char *zUsage; /* Documentation */ |
|
4f76459…
|
drh
|
5474 |
} aUsage[] = { |
|
4f76459…
|
drh
|
5475 |
{ ".import", |
|
4f76459…
|
drh
|
5476 |
"USAGE: .import [OPTIONS] FILE TABLE\n" |
|
4f76459…
|
drh
|
5477 |
"\n" |
|
4f76459…
|
drh
|
5478 |
"Import CSV or similar text from FILE into TABLE. If TABLE does\n" |
|
4f76459…
|
drh
|
5479 |
"not exist, it is created using the first row of FILE as the column\n" |
|
4f76459…
|
drh
|
5480 |
"names. If FILE begins with \"|\" then it is a command that is run\n" |
|
b9ecacf…
|
drh
|
5481 |
"and the output from the command is used as the input data. If\n" |
|
b9ecacf…
|
drh
|
5482 |
"FILE begins with \"<<\" followed by a label, then content is read from\n" |
|
b9ecacf…
|
drh
|
5483 |
"the script until the first line that matches the label.\n" |
|
b9ecacf…
|
drh
|
5484 |
"\n" |
|
b9ecacf…
|
drh
|
5485 |
"The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n" |
|
b9ecacf…
|
drh
|
5486 |
"rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n" |
|
b9ecacf…
|
drh
|
5487 |
"the --ascii option is used.\n" |
|
4f76459…
|
drh
|
5488 |
"\n" |
|
b9ecacf…
|
drh
|
5489 |
"The column and row separators must be single ASCII characters. If\n" |
|
b9ecacf…
|
drh
|
5490 |
"multiple characters or a Unicode character are specified for the\n" |
|
b9ecacf…
|
drh
|
5491 |
"separators, then only the first byte of the separator is used. Except,\n" |
|
b9ecacf…
|
drh
|
5492 |
"if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n" |
|
b9ecacf…
|
drh
|
5493 |
"understood as a row separator too.\n" |
|
4f76459…
|
drh
|
5494 |
"\n" |
|
4f76459…
|
drh
|
5495 |
"Options:\n" |
|
b9ecacf…
|
drh
|
5496 |
" --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n" |
|
b9ecacf…
|
drh
|
5497 |
" as column and row separators on input, unless other\n" |
|
b9ecacf…
|
drh
|
5498 |
" delimiters are specified using --colsep and/or --rowsep\n" |
|
b9ecacf…
|
drh
|
5499 |
" --colsep CHAR Use CHAR as the column separator.\n" |
|
4f76459…
|
drh
|
5500 |
" --csv Input is standard RFC-4180 CSV.\n" |
|
7b0960d…
|
drh
|
5501 |
" --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.\n" |
|
7b0960d…
|
drh
|
5502 |
" --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.\n" |
|
b9ecacf…
|
drh
|
5503 |
" --rowsep CHAR Use CHAR as the row separator.\n" |
|
4f76459…
|
drh
|
5504 |
" --schema S When creating TABLE, put it in schema S\n" |
|
4f76459…
|
drh
|
5505 |
" --skip N Ignore the first N rows of input\n" |
|
4f76459…
|
drh
|
5506 |
" -v Verbose mode\n" |
|
4f76459…
|
drh
|
5507 |
}, |
|
4f76459…
|
drh
|
5508 |
{ ".mode", |
|
4f76459…
|
drh
|
5509 |
"USAGE: .mode [MODE] [OPTIONS]\n" |
|
4f76459…
|
drh
|
5510 |
"\n" |
|
45de97f…
|
drh
|
5511 |
"Change the output mode to MODE and/or apply OPTIONS to the output mode.\n" |
|
45de97f…
|
drh
|
5512 |
"Arguments are processed from left to right. If no arguments, show the\n" |
|
45de97f…
|
drh
|
5513 |
"current output mode and relevant options.\n" |
|
4f76459…
|
drh
|
5514 |
"\n" |
|
4f76459…
|
drh
|
5515 |
"Options:\n" |
|
4f76459…
|
drh
|
5516 |
" --align STRING Set the alignment of text in columnar modes\n" |
|
4f76459…
|
drh
|
5517 |
" String consists of characters 'L', 'C', 'R'\n" |
|
4f76459…
|
drh
|
5518 |
" meaning \"left\", \"centered\", and \"right\", with\n" |
|
4f76459…
|
drh
|
5519 |
" one letter per column starting from the left.\n" |
|
4f76459…
|
drh
|
5520 |
" Unspecified alignment defaults to 'L'.\n" |
|
45de97f…
|
drh
|
5521 |
" --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n" |
|
45de97f…
|
drh
|
5522 |
" \"json\", or \"size\". Default is \"auto\".\n" |
|
f4b3b59…
|
drh
|
5523 |
" --border on|off Show outer border on \"box\" and \"table\" modes.\n" |
|
4f76459…
|
drh
|
5524 |
" --charlimit N Set the maximum number of output characters to\n" |
|
4f76459…
|
drh
|
5525 |
" show for any single SQL value to N. Longer values\n" |
|
4f76459…
|
drh
|
5526 |
" truncated. Zero means \"no limit\".\n" |
|
4f76459…
|
drh
|
5527 |
" --colsep STRING Use STRING as the column separator\n" |
|
4f76459…
|
drh
|
5528 |
" --escape ESC Enable/disable escaping of control characters\n" |
|
45de97f…
|
drh
|
5529 |
" found in the output. ESC can be \"off\", \"ascii\",\n" |
|
45de97f…
|
drh
|
5530 |
" or \"symbol\".\n" |
|
4f76459…
|
drh
|
5531 |
" --linelimit N Set the maximum number of output lines to show for\n" |
|
4f76459…
|
drh
|
5532 |
" any single SQL value to N. Longer values are\n" |
|
4f76459…
|
drh
|
5533 |
" truncated. Zero means \"no limit\". Only works\n" |
|
4f76459…
|
drh
|
5534 |
" in \"line\" mode and in columnar modes.\n" |
|
ae7e3f0…
|
drh
|
5535 |
" --limits L,C,T Shorthand for \"--linelimit L --charlimit C\n" |
|
ae7e3f0…
|
drh
|
5536 |
" --titlelimit T\". The \",T\" can be omitted in which\n" |
|
ae7e3f0…
|
drh
|
5537 |
" case the --titlelimit is unchanged. The argument\n" |
|
ae7e3f0…
|
drh
|
5538 |
" can also be \"off\" to mean \"0,0,0\" or \"on\" to\n" |
|
ae7e3f0…
|
drh
|
5539 |
" mean \"5,300,20\".\n" |
|
4f76459…
|
drh
|
5540 |
" --list List available modes\n" |
|
17f9878…
|
drh
|
5541 |
" --multiinsert N In \"insert\" mode, put multiple rows on a single\n" |
|
17f9878…
|
drh
|
5542 |
" INSERT statement until the size exceeds N bytes.\n" |
|
4f76459…
|
drh
|
5543 |
" --null STRING Render SQL NULL values as the given string\n" |
|
4f76459…
|
drh
|
5544 |
" --once Setting changes to the right are reverted after\n" |
|
4f76459…
|
drh
|
5545 |
" the next SQL command.\n" |
|
4f76459…
|
drh
|
5546 |
" --quote ARG Enable/disable quoting of text. ARG can be\n" |
|
709b566…
|
drh
|
5547 |
" \"off\", \"on\", \"sql\", \"relaxed\", \"csv\", \"html\",\n" |
|
709b566…
|
drh
|
5548 |
" \"tcl\", or \"json\". \"off\" means show the text as-is.\n" |
|
45de97f…
|
drh
|
5549 |
" \"on\" is an alias for \"sql\".\n" |
|
4f76459…
|
drh
|
5550 |
" --reset Changes all mode settings back to their default.\n" |
|
4f76459…
|
drh
|
5551 |
" --rowsep STRING Use STRING as the row separator\n" |
|
45de97f…
|
drh
|
5552 |
" --sw|--screenwidth N Declare the screen width of the output device\n" |
|
4f76459…
|
drh
|
5553 |
" to be N characters. An attempt may be made to\n" |
|
4f76459…
|
drh
|
5554 |
" wrap output text to fit within this limit. Zero\n" |
|
4f76459…
|
drh
|
5555 |
" means \"no limit\". Or N can be \"auto\" to set the\n" |
|
4f76459…
|
drh
|
5556 |
" width automatically.\n" |
|
4f76459…
|
drh
|
5557 |
" --tablename NAME Set the name of the table for \"insert\" mode.\n" |
|
4f76459…
|
drh
|
5558 |
" --tag NAME Save mode to the left as NAME.\n" |
|
4f76459…
|
drh
|
5559 |
" --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n" |
|
45de97f…
|
drh
|
5560 |
" --title ARG Whether or not to show column headers, and if so\n" |
|
4f76459…
|
drh
|
5561 |
" how to encode them. ARG can be \"off\", \"on\",\n" |
|
4f76459…
|
drh
|
5562 |
" \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n" |
|
ae7e3f0…
|
drh
|
5563 |
" --titlelimit N Limit the length of column titles to N characters.\n" |
|
4f76459…
|
drh
|
5564 |
" -v|--verbose Verbose output\n" |
|
4f76459…
|
drh
|
5565 |
" --widths LIST Set the columns widths for columnar modes. The\n" |
|
4f76459…
|
drh
|
5566 |
" argument is a list of integers, one for each\n" |
|
4f76459…
|
drh
|
5567 |
" column. A \"0\" width means use a dynamic width\n" |
|
4f76459…
|
drh
|
5568 |
" based on the actual width of data. If there are\n" |
|
4f76459…
|
drh
|
5569 |
" fewer entries in LIST than columns, \"0\" is used\n" |
|
4f76459…
|
drh
|
5570 |
" for the unspecified widths.\n" |
|
4f76459…
|
drh
|
5571 |
" --wordwrap BOOLEAN Enable/disable word wrapping\n" |
|
4f76459…
|
drh
|
5572 |
" --wrap N Wrap columns wider than N characters\n" |
|
4f76459…
|
drh
|
5573 |
" --ww Shorthand for \"--wordwrap on\"\n" |
|
4f76459…
|
drh
|
5574 |
}, |
|
4f76459…
|
drh
|
5575 |
{ ".output", |
|
4f76459…
|
drh
|
5576 |
"USAGE: .output [OPTIONS] [FILE]\n" |
|
4f76459…
|
drh
|
5577 |
"\n" |
|
4f76459…
|
drh
|
5578 |
"Begin redirecting output to FILE. Or if FILE is omitted, revert\n" |
|
4f76459…
|
drh
|
5579 |
"to sending output to the console. If FILE begins with \"|\" then\n" |
|
4f76459…
|
drh
|
5580 |
"the remainder of file is taken as a pipe and output is directed\n" |
|
4f76459…
|
drh
|
5581 |
"into that pipe. If FILE is \"memory\" then output is captured in an\n" |
|
4f76459…
|
drh
|
5582 |
"internal memory buffer. If FILE is \"off\" then output is redirected\n" |
|
4f76459…
|
drh
|
5583 |
"into /dev/null or the equivalent.\n" |
|
4f76459…
|
drh
|
5584 |
"\n" |
|
4f76459…
|
drh
|
5585 |
"Options:\n" |
|
4f76459…
|
drh
|
5586 |
" --bom Prepend a byte-order mark to the output\n" |
|
4f76459…
|
drh
|
5587 |
" -e Accumulate output in a temporary text file then\n" |
|
4f76459…
|
drh
|
5588 |
" launch a text editor when the redirection ends.\n" |
|
4f76459…
|
drh
|
5589 |
" --error-prefix X Use X as the left-margin prefix for error messages.\n" |
|
4f76459…
|
drh
|
5590 |
" Set to an empty string to restore the default.\n" |
|
5f65ed5…
|
drh
|
5591 |
" --keep Keep redirecting output to its current destination.\n" |
|
5f65ed5…
|
drh
|
5592 |
" Use this option in combination with --show or\n" |
|
5f65ed5…
|
drh
|
5593 |
" with --error-prefix when you do not want to stop\n" |
|
5f65ed5…
|
drh
|
5594 |
" a current redirection.\n" |
|
4f76459…
|
drh
|
5595 |
" --plain Use plain text rather than HTML tables with -w\n" |
|
5f65ed5…
|
drh
|
5596 |
" --show Show output text captured by .testcase or by\n" |
|
5f65ed5…
|
drh
|
5597 |
" redirecting to \"memory\".\n" |
|
4f76459…
|
drh
|
5598 |
" -w Show the output in a web browser. Output is\n" |
|
4f76459…
|
drh
|
5599 |
" written into a temporary HTML file until the\n" |
|
4f76459…
|
drh
|
5600 |
" redirect ends, then the web browser is launched.\n" |
|
4f76459…
|
drh
|
5601 |
" Query results are shown as HTML tables, unless\n" |
|
4f76459…
|
drh
|
5602 |
" the --plain is used too.\n" |
|
4f76459…
|
drh
|
5603 |
" -x Show the output in a spreadsheet. Output is\n" |
|
4f76459…
|
drh
|
5604 |
" written to a temp file as CSV then the spreadsheet\n" |
|
4f76459…
|
drh
|
5605 |
" is launched when\n" |
|
4f76459…
|
drh
|
5606 |
}, |
|
4f76459…
|
drh
|
5607 |
{ ".once", |
|
4f76459…
|
drh
|
5608 |
"USAGE: .once [OPTIONS] FILE ...\n" |
|
4f76459…
|
drh
|
5609 |
"\n" |
|
4f76459…
|
drh
|
5610 |
"Write the output for the next line of SQL or the next dot-command into\n" |
|
4f76459…
|
drh
|
5611 |
"FILE. If FILE begins with \"|\" then it is a program into which output\n" |
|
4f76459…
|
drh
|
5612 |
"is written. The FILE argument should be omitted if one of the -e, -w,\n" |
|
4f76459…
|
drh
|
5613 |
"or -x options is used.\n" |
|
4f76459…
|
drh
|
5614 |
"\n" |
|
4f76459…
|
drh
|
5615 |
"Options:\n" |
|
4f76459…
|
drh
|
5616 |
" -e Capture output into a temporary file then bring up\n" |
|
4f76459…
|
drh
|
5617 |
" a text editor on that temporary file.\n" |
|
4f76459…
|
drh
|
5618 |
" --plain Use plain text rather than HTML tables with -w\n" |
|
4f76459…
|
drh
|
5619 |
" -w Capture output into an HTML file then bring up that\n" |
|
4f76459…
|
drh
|
5620 |
" file in a web browser\n" |
|
4f76459…
|
drh
|
5621 |
" -x Show the output in a spreadsheet. Output is\n" |
|
4f76459…
|
drh
|
5622 |
" written to a temp file as CSV then the spreadsheet\n" |
|
4f76459…
|
drh
|
5623 |
" is launched when\n" |
|
5f65ed5…
|
drh
|
5624 |
}, |
|
5f65ed5…
|
drh
|
5625 |
{ ".check", |
|
5f65ed5…
|
drh
|
5626 |
"USAGE: .check [OPTIONS] PATTERN\n" |
|
5f65ed5…
|
drh
|
5627 |
"\n" |
|
5f65ed5…
|
drh
|
5628 |
"Verify results of commands since the most recent .testcase command.\n" |
|
5f65ed5…
|
drh
|
5629 |
"Restore output to the console, unless --keep is used.\n" |
|
5f65ed5…
|
drh
|
5630 |
"\n" |
|
5f65ed5…
|
drh
|
5631 |
"If PATTERN starts with \"<<ENDMARK\" then the actual pattern is taken from\n" |
|
5f65ed5…
|
drh
|
5632 |
"subsequent lines of text up to the first line that begins with ENDMARK.\n" |
|
5f65ed5…
|
drh
|
5633 |
"All pattern lines and the ENDMARK are discarded.\n" |
|
5f65ed5…
|
drh
|
5634 |
"\n" |
|
5f65ed5…
|
drh
|
5635 |
"Options:\n" |
|
5f65ed5…
|
drh
|
5636 |
" --exact Do an exact comparison including leading and\n" |
|
5f65ed5…
|
drh
|
5637 |
" trailing whitespace.\n" |
|
5f65ed5…
|
drh
|
5638 |
" --glob Treat PATTERN as a GLOB\n" |
|
5f65ed5…
|
drh
|
5639 |
" --keep Do not reset the testcase. More .check commands\n" |
|
5f65ed5…
|
drh
|
5640 |
" will follow.\n" |
|
5f65ed5…
|
drh
|
5641 |
" --notglob Output should not match PATTERN\n" |
|
5f65ed5…
|
drh
|
5642 |
" --show Write testcase output to the screen, for debugging.\n" |
|
5f65ed5…
|
drh
|
5643 |
}, |
|
5f65ed5…
|
drh
|
5644 |
{ ".testcase", |
|
5f65ed5…
|
drh
|
5645 |
"USAGE: .testcase [OPTIONS] NAME\n" |
|
5f65ed5…
|
drh
|
5646 |
"\n" |
|
5f65ed5…
|
drh
|
5647 |
"Start a new test case identified by NAME. All output\n" |
|
5f65ed5…
|
drh
|
5648 |
"through the next \".check\" command is captured for comparison. See the\n" |
|
5f65ed5…
|
drh
|
5649 |
"\".check\" commandn for additional informatioon.\n" |
|
5f65ed5…
|
drh
|
5650 |
"\n" |
|
5f65ed5…
|
drh
|
5651 |
"Options:\n" |
|
5f65ed5…
|
drh
|
5652 |
" --error-prefix TEXT Change error message prefix text to TEXT\n" |
|
4f76459…
|
drh
|
5653 |
}, |
|
4f76459…
|
drh
|
5654 |
}; |
|
4f76459…
|
drh
|
5655 |
|
|
4f76459…
|
drh
|
5656 |
/* |
|
4f76459…
|
drh
|
5657 |
** Return a pointer to usage text for zCmd, or NULL if none exists. |
|
4f76459…
|
drh
|
5658 |
*/ |
|
4f76459…
|
drh
|
5659 |
static const char *findUsage(const char *zCmd){ |
|
4f76459…
|
drh
|
5660 |
int i; |
|
4f76459…
|
drh
|
5661 |
for(i=0; i<ArraySize(aUsage); i++){ |
|
4f76459…
|
drh
|
5662 |
if( sqlite3_strglob(zCmd, aUsage[i].zCmd)==0 ) return aUsage[i].zUsage; |
|
4f76459…
|
drh
|
5663 |
} |
|
4f76459…
|
drh
|
5664 |
return 0; |
|
4f76459…
|
drh
|
5665 |
} |
|
4f76459…
|
drh
|
5666 |
const char *zHit = 0; |
|
4f76459…
|
drh
|
5667 |
cli_printf(out, ".%s\n", &azHelp[i][1]); |
|
4f76459…
|
drh
|
5668 |
cli_printf(out, "%s\n", azHelp[i]); |
|
4f76459…
|
drh
|
5669 |
zPat = sqlite3_mprintf(".%s*", zPattern[0]=='.' ? &zPattern[1] : zPattern); |
|
4f76459…
|
drh
|
5670 |
if( zHit ) cli_printf(out, "%s\n", zHit); |
|
4f76459…
|
drh
|
5671 |
zHit = azHelp[i]; |
|
4f76459…
|
drh
|
5672 |
const char *zUsage = findUsage(zPat); |
|
4f76459…
|
drh
|
5673 |
if( zUsage ){ |
|
4f76459…
|
drh
|
5674 |
cli_puts(zUsage, out); |
|
4f76459…
|
drh
|
5675 |
}else{ |
|
4f76459…
|
drh
|
5676 |
/* when zPattern is a prefix of exactly one command, then include |
|
4f76459…
|
drh
|
5677 |
** the details of that command, which should begin at offset j */ |
|
4f76459…
|
drh
|
5678 |
cli_printf(out, "%s\n", zHit); |
|
4f76459…
|
drh
|
5679 |
while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ |
|
4f76459…
|
drh
|
5680 |
cli_printf(out, "%s\n", azHelp[j]); |
|
4f76459…
|
drh
|
5681 |
j++; |
|
4f76459…
|
drh
|
5682 |
} |
|
4f76459…
|
drh
|
5683 |
} |
|
4f76459…
|
drh
|
5684 |
}else{ |
|
4f76459…
|
drh
|
5685 |
cli_printf(out, "%s\n", zHit); |
|
4f76459…
|
drh
|
5686 |
} |
|
4f76459…
|
drh
|
5687 |
} |
|
4f76459…
|
drh
|
5688 |
sqlite3_free(zPat); |
|
4f76459…
|
drh
|
5689 |
if( n ) return n; |
|
4f76459…
|
drh
|
5690 |
cli_printf(out, "%s\n", azHelp[j]); |
|
4f76459…
|
drh
|
5691 |
cli_printf(out, "%s\n", azHelp[j]); |
|
92871e0…
|
drh
|
5692 |
static int process_input(ShellState *p, const char*); |
|
4f76459…
|
drh
|
5693 |
cli_printf(stderr,"Error: '%s' not seekable\n", zName); |
|
4f76459…
|
drh
|
5694 |
cli_puts("Error: out of memory\n", stderr); |
|
4f76459…
|
drh
|
5695 |
cli_printf(stderr,"Error: cannot read '%s'\n", zName); |
|
5f65ed5…
|
drh
|
5696 |
** Return the size of the named file in bytes. Or return a negative |
|
5f65ed5…
|
drh
|
5697 |
** number if the file does not exist. |
|
5f65ed5…
|
drh
|
5698 |
*/ |
|
5f65ed5…
|
drh
|
5699 |
static sqlite3_int64 fileSize(const char *zFile){ |
|
5f65ed5…
|
drh
|
5700 |
#if defined(_WIN32) || defined(WIN32) |
|
5f65ed5…
|
drh
|
5701 |
struct _stat64 x; |
|
5f65ed5…
|
drh
|
5702 |
if( _stat64(zFile, &x)!=0 ) return -1; |
|
5f65ed5…
|
drh
|
5703 |
return (sqlite3_int64)x.st_size; |
|
5f65ed5…
|
drh
|
5704 |
#else |
|
5f65ed5…
|
drh
|
5705 |
struct stat x; |
|
5f65ed5…
|
drh
|
5706 |
if( stat(zFile, &x)!=0 ) return -1; |
|
5f65ed5…
|
drh
|
5707 |
return (sqlite3_int64)x.st_size; |
|
5f65ed5…
|
drh
|
5708 |
#endif |
|
5f65ed5…
|
drh
|
5709 |
} |
|
5f65ed5…
|
drh
|
5710 |
|
|
5f65ed5…
|
drh
|
5711 |
/* |
|
5f65ed5…
|
drh
|
5712 |
** Return true if zFile is an SQLite database. |
|
5f65ed5…
|
drh
|
5713 |
** |
|
5f65ed5…
|
drh
|
5714 |
** Algorithm: |
|
5f65ed5…
|
drh
|
5715 |
** * If the file does not exist -> return false |
|
5f65ed5…
|
drh
|
5716 |
** * If the size of the file is not a multiple of 512 -> return false |
|
5f65ed5…
|
drh
|
5717 |
** * If sqlite3_open() fails -> return false |
|
5f65ed5…
|
drh
|
5718 |
** * if sqlite3_prepare() or sqlite3_step() fails -> return false |
|
5f65ed5…
|
drh
|
5719 |
** * Otherwise -> return true |
|
5f65ed5…
|
drh
|
5720 |
*/ |
|
5f65ed5…
|
drh
|
5721 |
static int isDatabaseFile(const char *zFile, int openFlags){ |
|
5f65ed5…
|
drh
|
5722 |
sqlite3 *db = 0; |
|
5f65ed5…
|
drh
|
5723 |
sqlite3_stmt *pStmt = 0; |
|
5f65ed5…
|
drh
|
5724 |
int rc; |
|
5f65ed5…
|
drh
|
5725 |
sqlite3_int64 sz = fileSize(zFile); |
|
5f65ed5…
|
drh
|
5726 |
if( sz<512 || (sz%512)!=0 ) return 0; |
|
5f65ed5…
|
drh
|
5727 |
if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK |
|
5f65ed5…
|
drh
|
5728 |
&& sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) |
|
5f65ed5…
|
drh
|
5729 |
==SQLITE_OK |
|
5f65ed5…
|
drh
|
5730 |
&& sqlite3_step(pStmt)==SQLITE_ROW |
|
5f65ed5…
|
drh
|
5731 |
){ |
|
5f65ed5…
|
drh
|
5732 |
rc = 1; |
|
5f65ed5…
|
drh
|
5733 |
}else{ |
|
5f65ed5…
|
drh
|
5734 |
rc = 0; |
|
5f65ed5…
|
drh
|
5735 |
} |
|
5f65ed5…
|
drh
|
5736 |
sqlite3_finalize(pStmt); |
|
5f65ed5…
|
drh
|
5737 |
sqlite3_close(db); |
|
5f65ed5…
|
drh
|
5738 |
return rc; |
|
5f65ed5…
|
drh
|
5739 |
} |
|
5f65ed5…
|
drh
|
5740 |
|
|
5f65ed5…
|
drh
|
5741 |
/* |
|
92871e0…
|
drh
|
5742 |
int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ |
|
92871e0…
|
drh
|
5743 |
FILE *f; |
|
92871e0…
|
drh
|
5744 |
if( access(zName,0)!=0 ) goto database_type_by_name; |
|
5f65ed5…
|
drh
|
5745 |
if( isDatabaseFile(zName, openFlags) ){ |
|
92871e0…
|
drh
|
5746 |
rc = SHELL_OPEN_NORMAL; |
|
92871e0…
|
drh
|
5747 |
if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; |
|
92871e0…
|
drh
|
5748 |
f = sqlite3_fopen(zName, "rb"); |
|
92871e0…
|
drh
|
5749 |
if( f==0 ) goto database_type_by_name; |
|
92871e0…
|
drh
|
5750 |
|
|
92871e0…
|
drh
|
5751 |
database_type_by_name: |
|
92871e0…
|
drh
|
5752 |
if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){ |
|
92871e0…
|
drh
|
5753 |
rc = SHELL_OPEN_ZIPFILE; |
|
92871e0…
|
drh
|
5754 |
}else{ |
|
92871e0…
|
drh
|
5755 |
rc = SHELL_OPEN_NORMAL; |
|
92871e0…
|
drh
|
5756 |
} |
|
92871e0…
|
drh
|
5757 |
return rc; |
|
5f65ed5…
|
drh
|
5758 |
} |
|
5f65ed5…
|
drh
|
5759 |
|
|
5f65ed5…
|
drh
|
5760 |
/* |
|
5f65ed5…
|
drh
|
5761 |
** If the text in z[] is the name of a readable file and that file appears |
|
5f65ed5…
|
drh
|
5762 |
** to contain SQL text and/or dot-commands, then return true. If z[] is |
|
5f65ed5…
|
drh
|
5763 |
** not a file, or if the file is unreadable, or if the file is a database |
|
5f65ed5…
|
drh
|
5764 |
** or anything else that is not SQL text and dot-commands, then return false. |
|
5f65ed5…
|
drh
|
5765 |
** |
|
5f65ed5…
|
drh
|
5766 |
** If the bLeaveUninit flag is set, then be sure to leave SQLite in an |
|
5f65ed5…
|
drh
|
5767 |
** uninitialized state. This means invoking sqlite3_shutdown() after any |
|
5f65ed5…
|
drh
|
5768 |
** SQLite API is used. |
|
5f65ed5…
|
drh
|
5769 |
** |
|
5f65ed5…
|
drh
|
5770 |
** Some amount of guesswork is involved in this decision. |
|
5f65ed5…
|
drh
|
5771 |
*/ |
|
5f65ed5…
|
drh
|
5772 |
static int isScriptFile(const char *z, int bLeaveUninit){ |
|
5f65ed5…
|
drh
|
5773 |
sqlite3_int64 sz = fileSize(z); |
|
5f65ed5…
|
drh
|
5774 |
if( sz<=0 ) return 0; |
|
5f65ed5…
|
drh
|
5775 |
if( (sz%512)==0 ){ |
|
13c221a…
|
drh
|
5776 |
int rc; |
|
13c221a…
|
drh
|
5777 |
sqlite3_initialize(); |
|
13c221a…
|
drh
|
5778 |
rc = isDatabaseFile(z, SQLITE_OPEN_READONLY); |
|
5f65ed5…
|
drh
|
5779 |
if( bLeaveUninit ){ |
|
5f65ed5…
|
drh
|
5780 |
sqlite3_shutdown(); |
|
5f65ed5…
|
drh
|
5781 |
} |
|
5f65ed5…
|
drh
|
5782 |
if( rc ) return 0; /* Is a database */ |
|
5f65ed5…
|
drh
|
5783 |
} |
|
5f65ed5…
|
drh
|
5784 |
if( sqlite3_strlike("%.sql",z,0)==0 ) return 1; |
|
5f65ed5…
|
drh
|
5785 |
if( sqlite3_strlike("%.txt",z,0)==0 ) return 1; |
|
5f65ed5…
|
drh
|
5786 |
return 0; |
|
4f76459…
|
drh
|
5787 |
} |
|
4f76459…
|
drh
|
5788 |
|
|
92871e0…
|
drh
|
5789 |
i64 nLine; |
|
4f76459…
|
drh
|
5790 |
cli_printf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); |
|
4f76459…
|
drh
|
5791 |
cli_puts("invalid pagesize\n", stderr); |
|
92871e0…
|
drh
|
5792 |
if( nLine>=2000000000 ){ |
|
4f76459…
|
drh
|
5793 |
cli_printf(stderr, "input too big\n"); |
|
92871e0…
|
drh
|
5794 |
goto readHexDb_error; |
|
92871e0…
|
drh
|
5795 |
} |
|
4f76459…
|
drh
|
5796 |
cli_printf(stderr,"Error on line %lld of --hexdb input\n", nLine); |
|
92871e0…
|
drh
|
5797 |
(openFlags & OPEN_DB_ZIPFILE)!=0, p->openFlags); |
|
4f76459…
|
drh
|
5798 |
cli_printf(stderr,"Error: unable to open database \"%s\": %s\n", |
|
4f76459…
|
drh
|
5799 |
cli_exit(1); |
|
4f76459…
|
drh
|
5800 |
cli_puts("Also: unable to open substitute in-memory database.\n", |
|
4f76459…
|
drh
|
5801 |
cli_exit(1); |
|
4f76459…
|
drh
|
5802 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5803 |
sqlite3_create_function(p->db, "shell_format_schema", 2, SQLITE_UTF8, p, |
|
4f76459…
|
drh
|
5804 |
shellFormatSchema, 0, 0); |
|
4f76459…
|
drh
|
5805 |
cli_printf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); |
|
4f76459…
|
drh
|
5806 |
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0 |
|
4f76459…
|
drh
|
5807 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5808 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5809 |
static FILE *output_file_open(ShellState *p, const char *zFile){ |
|
4f76459…
|
drh
|
5810 |
}else if( cli_strcmp(zFile, "off")==0 || p->bSafeMode ){ |
|
4f76459…
|
drh
|
5811 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile); |
|
4f76459…
|
drh
|
5812 |
cli_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql); |
|
4f76459…
|
drh
|
5813 |
cli_printf(p->traceOut, |
|
b9ecacf…
|
drh
|
5814 |
char *zIn; /* Input text */ |
|
b9ecacf…
|
drh
|
5815 |
i64 nUsed; /* Bytes of zIn[] used so far */ |
|
7b0960d…
|
drh
|
5816 |
int cQEscape; /* Escape character with "...". 0 for none */ |
|
7b0960d…
|
drh
|
5817 |
int cUQEscape; /* Escape character not with "...". 0 for none */ |
|
b9ecacf…
|
drh
|
5818 |
if( p->zIn ){ |
|
b9ecacf…
|
drh
|
5819 |
sqlite3_free(p->zIn); |
|
b9ecacf…
|
drh
|
5820 |
p->zIn = 0; |
|
b9ecacf…
|
drh
|
5821 |
} |
|
b9ecacf…
|
drh
|
5822 |
/* Read a single character of the .import input text. Return EOF |
|
b9ecacf…
|
drh
|
5823 |
** at end-of-file. |
|
b9ecacf…
|
drh
|
5824 |
*/ |
|
b9ecacf…
|
drh
|
5825 |
static int import_getc(ImportCtx *p){ |
|
b9ecacf…
|
drh
|
5826 |
if( p->in ){ |
|
b9ecacf…
|
drh
|
5827 |
return fgetc(p->in); |
|
b9ecacf…
|
drh
|
5828 |
}else if( p->zIn && p->zIn[p->nUsed]!=0 ){ |
|
b9ecacf…
|
drh
|
5829 |
return p->zIn[p->nUsed++]; |
|
b9ecacf…
|
drh
|
5830 |
}else{ |
|
b9ecacf…
|
drh
|
5831 |
return EOF; |
|
b9ecacf…
|
drh
|
5832 |
} |
|
b9ecacf…
|
drh
|
5833 |
} |
|
b9ecacf…
|
drh
|
5834 |
|
|
b9ecacf…
|
drh
|
5835 |
/* Append a single byte to the field value begin constructed |
|
b9ecacf…
|
drh
|
5836 |
** in the p->z[] buffer |
|
b9ecacf…
|
drh
|
5837 |
*/ |
|
b9ecacf…
|
drh
|
5838 |
** + Use p->cColSep as the column separator. The default is ",". |
|
b9ecacf…
|
drh
|
5839 |
** + Use p->cRowSep as the row separator. The default is "\n". |
|
b9ecacf…
|
drh
|
5840 |
c = import_getc(p); |
|
7b0960d…
|
drh
|
5841 |
int cEsc = (u8)p->cQEscape; |
|
b9ecacf…
|
drh
|
5842 |
c = import_getc(p); |
|
7b0960d…
|
drh
|
5843 |
if( c==cEsc && cEsc!=0 ){ |
|
7b0960d…
|
drh
|
5844 |
c = import_getc(p); |
|
7b0960d…
|
drh
|
5845 |
import_append_char(p, c); |
|
7b0960d…
|
drh
|
5846 |
ppc = pc = 0; |
|
7b0960d…
|
drh
|
5847 |
continue; |
|
7b0960d…
|
drh
|
5848 |
} |
|
4f76459…
|
drh
|
5849 |
cli_printf(stderr,"%s:%d: unescaped %c character\n", |
|
7b0960d…
|
drh
|
5850 |
p->zFile, p->nLine, cQuote); |
|
4f76459…
|
drh
|
5851 |
cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n", |
|
7b0960d…
|
drh
|
5852 |
int cEsc = p->cUQEscape; |
|
b9ecacf…
|
drh
|
5853 |
c = import_getc(p); |
|
b9ecacf…
|
drh
|
5854 |
c = import_getc(p); |
|
7b0960d…
|
drh
|
5855 |
if( c==cEsc && cEsc!=0 ) c = import_getc(p); |
|
b9ecacf…
|
drh
|
5856 |
c = import_getc(p); |
|
b9ecacf…
|
drh
|
5857 |
** + Use p->cColSep as the column separator. The default is "\x1F". |
|
b9ecacf…
|
drh
|
5858 |
** + Use p->cRowSep as the row separator. The default is "\x1E". |
|
b9ecacf…
|
drh
|
5859 |
c = import_getc(p); |
|
b9ecacf…
|
drh
|
5860 |
c = import_getc(p); |
|
4f76459…
|
drh
|
5861 |
cli_printf(stderr,"Error %d: %s on [%s]\n", |
|
4f76459…
|
drh
|
5862 |
cli_printf(stderr,"Error %d: %s on [%s]\n", |
|
4f76459…
|
drh
|
5863 |
cli_printf(stderr,"Error %d: %s\n", |
|
4f76459…
|
drh
|
5864 |
cli_printf(stderr,"Warning: cannot step \"%s\" backwards", zTable); |
|
4f76459…
|
drh
|
5865 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5866 |
cli_printf(stdout, "%s... ", zName); fflush(stdout); |
|
4f76459…
|
drh
|
5867 |
cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
|
4f76459…
|
drh
|
5868 |
cli_printf(stderr,"Error: (%d) %s on [%s]\n", |
|
4f76459…
|
drh
|
5869 |
cli_printf(stdout, "%s... ", zName); fflush(stdout); |
|
4f76459…
|
drh
|
5870 |
cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); |
|
4f76459…
|
drh
|
5871 |
cli_printf(stderr,"File \"%s\" already exists.\n", zNewDb); |
|
4f76459…
|
drh
|
5872 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5873 |
cli_puts("Output already redirected.\n", stderr); |
|
4f76459…
|
drh
|
5874 |
if( p->mode.eMode==MODE_Www ){ |
|
4f76459…
|
drh
|
5875 |
cli_puts( |
|
4f76459…
|
drh
|
5876 |
if( p->mode.eMode==MODE_Www ){ |
|
4f76459…
|
drh
|
5877 |
cli_puts("</PRE></BODY></HTML>\n", p->out); |
|
4f76459…
|
drh
|
5878 |
cli_printf(stderr,"Failed: [%s]\n", zCmd); |
|
4f76459…
|
drh
|
5879 |
modePop(p); |
|
4f76459…
|
drh
|
5880 |
if( cli_output_capture ){ |
|
4f76459…
|
drh
|
5881 |
sqlite3_str_free(cli_output_capture); |
|
4f76459…
|
drh
|
5882 |
cli_output_capture = 0; |
|
4f76459…
|
drh
|
5883 |
} |
|
4f76459…
|
drh
|
5884 |
cli_printf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
5885 |
cli_puts("unable to read database header\n", stderr); |
|
4f76459…
|
drh
|
5886 |
cli_printf(p->out, "%-20s %d\n", "database page size:", i); |
|
4f76459…
|
drh
|
5887 |
cli_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]); |
|
4f76459…
|
drh
|
5888 |
cli_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]); |
|
4f76459…
|
drh
|
5889 |
cli_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); |
|
4f76459…
|
drh
|
5890 |
cli_printf(p->out, "%-20s %u", aField[i].zName, val); |
|
4f76459…
|
drh
|
5891 |
if( val==1 ) cli_puts(" (utf8)", p->out); |
|
4f76459…
|
drh
|
5892 |
if( val==2 ) cli_puts(" (utf16le)", p->out); |
|
4f76459…
|
drh
|
5893 |
if( val==3 ) cli_puts(" (utf16be)", p->out); |
|
4f76459…
|
drh
|
5894 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
5895 |
cli_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); |
|
4f76459…
|
drh
|
5896 |
cli_printf(p->out, "%-20s %u\n", "data version", iDataVersion); |
|
4f76459…
|
drh
|
5897 |
cli_printf(p->out, "| size %lld pagesize %d filename %s\n", |
|
4f76459…
|
drh
|
5898 |
cli_printf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz); |
|
4f76459…
|
drh
|
5899 |
cli_printf(p->out, "| %5d:", i); |
|
4f76459…
|
drh
|
5900 |
for(j=0; j<16; j++) cli_printf(p->out, " %02x", aLine[j]); |
|
4f76459…
|
drh
|
5901 |
cli_printf(p->out, " "); |
|
4f76459…
|
drh
|
5902 |
cli_printf(p->out, "%c", bShow[c]); |
|
4f76459…
|
drh
|
5903 |
cli_printf(p->out, "\n"); |
|
4f76459…
|
drh
|
5904 |
cli_printf(p->out, "| end %s\n", zName); |
|
4f76459…
|
drh
|
5905 |
cli_printf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
5906 |
cli_printf(stderr,"Error: %s\n", zErr); |
|
70539ee…
|
drh
|
5907 |
** The input zFN is guaranteed to start with "file:" and is thus a URI |
|
70539ee…
|
drh
|
5908 |
** filename. Extract the actual filename and return a pointer to that |
|
70539ee…
|
drh
|
5909 |
** filename in spaced obtained from sqlite3_malloc(). |
|
70539ee…
|
drh
|
5910 |
** |
|
70539ee…
|
drh
|
5911 |
** The caller is responsible for freeing space using sqlite3_free() when |
|
70539ee…
|
drh
|
5912 |
** it has finished with the filename. |
|
70539ee…
|
drh
|
5913 |
*/ |
|
70539ee…
|
drh
|
5914 |
static char *shellFilenameFromUri(const char *zFN){ |
|
70539ee…
|
drh
|
5915 |
char *zOut; |
|
70539ee…
|
drh
|
5916 |
int i, j, d1, d2; |
|
70539ee…
|
drh
|
5917 |
|
|
70539ee…
|
drh
|
5918 |
assert( cli_strncmp(zFN,"file:",5)==0 ); |
|
70539ee…
|
drh
|
5919 |
zOut = sqlite3_mprintf("%s", zFN+5); |
|
70539ee…
|
drh
|
5920 |
shell_check_oom(zOut); |
|
70539ee…
|
drh
|
5921 |
for(i=j=0; zOut[i]!=0 && zOut[i]!='?'; i++){ |
|
70539ee…
|
drh
|
5922 |
if( zOut[i]!='%' ){ |
|
70539ee…
|
drh
|
5923 |
zOut[j++] = zOut[i]; |
|
70539ee…
|
drh
|
5924 |
continue; |
|
70539ee…
|
drh
|
5925 |
} |
|
70539ee…
|
drh
|
5926 |
d1 = hexDigitValue(zOut[i+1]); |
|
70539ee…
|
drh
|
5927 |
if( d1<0 ){ |
|
70539ee…
|
drh
|
5928 |
zOut[j] = 0; |
|
70539ee…
|
drh
|
5929 |
break; |
|
70539ee…
|
drh
|
5930 |
} |
|
70539ee…
|
drh
|
5931 |
d2 = hexDigitValue(zOut[i+2]); |
|
70539ee…
|
drh
|
5932 |
if( d2<0 ){ |
|
70539ee…
|
drh
|
5933 |
zOut[j] = 0; |
|
70539ee…
|
drh
|
5934 |
break; |
|
70539ee…
|
drh
|
5935 |
} |
|
70539ee…
|
drh
|
5936 |
zOut[j++] = d1*16 + d2; |
|
70539ee…
|
drh
|
5937 |
i += 2; |
|
70539ee…
|
drh
|
5938 |
} |
|
70539ee…
|
drh
|
5939 |
zOut[j] = 0; |
|
70539ee…
|
drh
|
5940 |
return zOut; |
|
70539ee…
|
drh
|
5941 |
} |
|
70539ee…
|
drh
|
5942 |
|
|
70539ee…
|
drh
|
5943 |
/* |
|
4f76459…
|
drh
|
5944 |
/* Forward reference */ |
|
4f76459…
|
drh
|
5945 |
static char *find_home_dir(int clearFlag); |
|
4f76459…
|
drh
|
5946 |
|
|
4f76459…
|
drh
|
5947 |
** |
|
4f76459…
|
drh
|
5948 |
** Because the classic temp folders like /tmp are no longer |
|
4f76459…
|
drh
|
5949 |
** accessible to web browsers, for security reasons, create the |
|
4f76459…
|
drh
|
5950 |
** temp file in the user's home directory. |
|
4f76459…
|
drh
|
5951 |
char *zHome; /* Home directory */ |
|
4f76459…
|
drh
|
5952 |
int i; /* Loop counter */ |
|
4f76459…
|
drh
|
5953 |
sqlite3_uint64 r = 0; /* Integer with 64 bits of randomness */ |
|
4f76459…
|
drh
|
5954 |
char zRand[32]; /* Text string with 160 bits of randomness */ |
|
4f76459…
|
drh
|
5955 |
#ifdef _WIN32 |
|
4f76459…
|
drh
|
5956 |
const char cDirSep = '\\'; |
|
4f76459…
|
drh
|
5957 |
#else |
|
4f76459…
|
drh
|
5958 |
const char cDirSep = '/'; |
|
4f76459…
|
drh
|
5959 |
#endif |
|
4f76459…
|
drh
|
5960 |
|
|
4f76459…
|
drh
|
5961 |
for(i=0; i<31; i++){ |
|
4f76459…
|
drh
|
5962 |
if( (i%12)==0 ) sqlite3_randomness(sizeof(r),&r); |
|
4f76459…
|
drh
|
5963 |
zRand[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[r%36]; |
|
4f76459…
|
drh
|
5964 |
r /= 36; |
|
4f76459…
|
drh
|
5965 |
} |
|
4f76459…
|
drh
|
5966 |
zRand[i] = 0; |
|
4f76459…
|
drh
|
5967 |
zHome = find_home_dir(0); |
|
4f76459…
|
drh
|
5968 |
p->zTempFile = sqlite3_mprintf("%s%ctemp-%s.%s", |
|
4f76459…
|
drh
|
5969 |
zHome,cDirSep,zRand,zSuffix); |
|
4f76459…
|
drh
|
5970 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5971 |
cli_puts("Error: internal error", stderr); |
|
4f76459…
|
drh
|
5972 |
cli_printf(out, "-- Parent table %s\n", zParent); |
|
4f76459…
|
drh
|
5973 |
cli_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); |
|
4f76459…
|
drh
|
5974 |
cli_printf(out, |
|
4f76459…
|
drh
|
5975 |
cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
|
4f76459…
|
drh
|
5976 |
cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
|
4f76459…
|
drh
|
5977 |
cli_printf(stderr,"%s\n", sqlite3_errmsg(db)); |
|
4f76459…
|
drh
|
5978 |
cli_printf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); |
|
4f76459…
|
drh
|
5979 |
cli_printf(stderr, "Where sub-commands are:\n"); |
|
4f76459…
|
drh
|
5980 |
cli_printf(stderr, " fkey-indexes\n"); |
|
4f76459…
|
drh
|
5981 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
5982 |
cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
|
4f76459…
|
drh
|
5983 |
cli_printf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); |
|
4f76459…
|
drh
|
5984 |
cli_puts("Use \"-A\" for more help\n", stderr); |
|
4f76459…
|
drh
|
5985 |
cli_puts("Use \".archive --help\" for more help\n", stderr); |
|
4f76459…
|
drh
|
5986 |
cli_printf(stderr, "Wrong number of arguments. Usage:\n"); |
|
4f76459…
|
drh
|
5987 |
cli_printf(stderr, "Required argument missing. Usage:\n"); |
|
4f76459…
|
drh
|
5988 |
cli_printf(stderr,"not found in archive: %s\n", z); |
|
92871e0…
|
drh
|
5989 |
char *z1 = sqlite3_mprintf(pAr->bGlob ? "" : "name IN("); |
|
92871e0…
|
drh
|
5990 |
char *z2 = sqlite3_mprintf(""); |
|
92871e0…
|
drh
|
5991 |
const char *zSep1 = ""; |
|
92871e0…
|
drh
|
5992 |
const char *zSep2 = ""; |
|
92871e0…
|
drh
|
5993 |
|
|
92871e0…
|
drh
|
5994 |
for(i=0; i<pAr->nArg && z1 && z2; i++){ |
|
92871e0…
|
drh
|
5995 |
int n = strlen30(z); |
|
92871e0…
|
drh
|
5996 |
|
|
92871e0…
|
drh
|
5997 |
if( pAr->bGlob ){ |
|
92871e0…
|
drh
|
5998 |
z1 = sqlite3_mprintf("%z%sname GLOB '%q'", z1, zSep2, z); |
|
92871e0…
|
drh
|
5999 |
z2 = sqlite3_mprintf( |
|
92871e0…
|
drh
|
6000 |
"%z%ssubstr(name,1,%d) GLOB '%q/'", z2, zSep2, n+1,z |
|
92871e0…
|
drh
|
6001 |
); |
|
92871e0…
|
drh
|
6002 |
}else{ |
|
92871e0…
|
drh
|
6003 |
z1 = sqlite3_mprintf("%z%s'%q'", z1, zSep1, z); |
|
92871e0…
|
drh
|
6004 |
z2 = sqlite3_mprintf("%z%ssubstr(name,1,%d) = '%q/'",z2,zSep2,n+1,z); |
|
92871e0…
|
drh
|
6005 |
} |
|
92871e0…
|
drh
|
6006 |
zSep1 = ", "; |
|
92871e0…
|
drh
|
6007 |
zSep2 = " OR "; |
|
92871e0…
|
drh
|
6008 |
} |
|
92871e0…
|
drh
|
6009 |
if( z1==0 || z2==0 ){ |
|
92871e0…
|
drh
|
6010 |
*pRc = SQLITE_NOMEM; |
|
92871e0…
|
drh
|
6011 |
}else{ |
|
92871e0…
|
drh
|
6012 |
zWhere = sqlite3_mprintf("(%s%s OR (name GLOB '*/*' AND (%s))) ", |
|
92871e0…
|
drh
|
6013 |
z1, pAr->bGlob==0 ? ")" : "", z2 |
|
92871e0…
|
drh
|
6014 |
sqlite3_free(z1); |
|
92871e0…
|
drh
|
6015 |
sqlite3_free(z2); |
|
4f76459…
|
drh
|
6016 |
cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
|
4f76459…
|
drh
|
6017 |
cli_printf(pAr->out, "%s % 10d %s %s\n", |
|
4f76459…
|
drh
|
6018 |
cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
|
4f76459…
|
drh
|
6019 |
cli_printf(pAr->out, "%s\n", zSql); |
|
4f76459…
|
drh
|
6020 |
cli_printf(stdout, "ERROR: %s\n", zErr); /* stdout? */ |
|
b8ab8b3…
|
drh
|
6021 |
"WITH dest(dpath,dlen) AS (SELECT realpath($dir),length(realpath($dir)))\n" |
|
b8ab8b3…
|
drh
|
6022 |
"SELECT ($dir || name),\n" |
|
b8ab8b3…
|
drh
|
6023 |
" CASE WHEN $dryrun THEN 0\n" |
|
b8ab8b3…
|
drh
|
6024 |
" ELSE writefile($dir||name, %s, mode, mtime) END\n" |
|
b8ab8b3…
|
drh
|
6025 |
" FROM dest CROSS JOIN %s\n" |
|
b8ab8b3…
|
drh
|
6026 |
" WHERE (%s)\n" |
|
b8ab8b3…
|
drh
|
6027 |
" AND (data IS NULL OR $pass==0)\n" /* Dirs both passes */ |
|
b8ab8b3…
|
drh
|
6028 |
" AND dpath=substr(realpath($dir||name),1,dlen)\n" /* No escapes */ |
|
b8ab8b3…
|
drh
|
6029 |
" AND name NOT GLOB '*..[/\\]*'\n"; /* No /../ in paths */ |
|
b8ab8b3…
|
drh
|
6030 |
j = sqlite3_bind_parameter_index(pSql, "$dryrun"); |
|
b8ab8b3…
|
drh
|
6031 |
sqlite3_bind_int(pSql, j, pAr->bDryRun); |
|
b8ab8b3…
|
drh
|
6032 |
/* Run the SELECT statement twice |
|
b8ab8b3…
|
drh
|
6033 |
** (0) writefile() all files and directories |
|
b8ab8b3…
|
drh
|
6034 |
** (1) writefile() for directory again |
|
b8ab8b3…
|
drh
|
6035 |
** The second pass is so that the timestamps for extracted directories |
|
b8ab8b3…
|
drh
|
6036 |
** will be reset to the value in the archive, since populating them |
|
b8ab8b3…
|
drh
|
6037 |
** in the first pass will have changed the timestamp. */ |
|
b8ab8b3…
|
drh
|
6038 |
j = sqlite3_bind_parameter_index(pSql, "$pass"); |
|
4f76459…
|
drh
|
6039 |
cli_printf(pAr->out, "%s\n", sqlite3_sql(pSql)); |
|
b8ab8b3…
|
drh
|
6040 |
if( pAr->bVerbose==0 ) break; |
|
b8ab8b3…
|
drh
|
6041 |
} |
|
b8ab8b3…
|
drh
|
6042 |
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
|
b8ab8b3…
|
drh
|
6043 |
if( i==0 && pAr->bVerbose ){ |
|
b8ab8b3…
|
drh
|
6044 |
cli_printf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); |
|
b8ab8b3…
|
drh
|
6045 |
if( pAr->bDryRun ) break; |
|
4f76459…
|
drh
|
6046 |
cli_printf(pAr->out, "%s\n", zSql); |
|
4f76459…
|
drh
|
6047 |
cli_printf(stdout, "ERROR: %s\n", zErr); |
|
92871e0…
|
drh
|
6048 |
eDbType = deduceDatabaseType(cmd.zFile, 1, 0); |
|
4f76459…
|
drh
|
6049 |
cli_printf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, |
|
4f76459…
|
drh
|
6050 |
cli_printf(stderr, "cannot open file: %s (%s)\n", |
|
4f76459…
|
drh
|
6051 |
cli_printf(stderr, "database does not contain an 'sqlar' table\n"); |
|
4f76459…
|
drh
|
6052 |
cli_printf(pState->out, "%s;\n", zSql); |
|
4f76459…
|
drh
|
6053 |
cli_printf(stderr,"unexpected option: %s\n", azArg[i]); |
|
d326547…
|
drh
|
6054 |
if( !pState->bSafeMode ){ |
|
d326547…
|
drh
|
6055 |
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
|
d326547…
|
drh
|
6056 |
} |
|
4f76459…
|
drh
|
6057 |
cli_printf(pState->out, ".dbconfig defensive off\n"); |
|
4f76459…
|
drh
|
6058 |
cli_printf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
|
4f76459…
|
drh
|
6059 |
cli_printf(pState->out, "%s\n", zMsg); |
|
4f76459…
|
drh
|
6060 |
cli_printf(stderr,"%s\n", zErr); |
|
4f76459…
|
drh
|
6061 |
cli_printf(pState->out, "%lld steps, %lld errors\n", nStep, nError); |
|
4f76459…
|
drh
|
6062 |
cli_printf(stderr,"E:%d\n",rc), assert(0) |
|
7b0960d…
|
drh
|
6063 |
cname||' ANY',\ |
|
17f9878…
|
drh
|
6064 |
"sql LIKE 'CREATE VIRTUAL TABLE%%' AND (%s)", zLike ? zLike : "true" |
|
4f76459…
|
drh
|
6065 |
cli_puts("/* WARNING: " |
|
4f76459…
|
drh
|
6066 |
cli_printf(stdout, |
|
4f76459…
|
drh
|
6067 |
cli_printf(stdout, |
|
4f76459…
|
drh
|
6068 |
** pickStr(zArg, &zErr, zS1, zS2, ..., ""); |
|
4f76459…
|
drh
|
6069 |
** |
|
4f76459…
|
drh
|
6070 |
** Try to match zArg against zS1, zS2, and so forth until the first |
|
4f76459…
|
drh
|
6071 |
** emptry string. Return the index of the match or -1 if none is found. |
|
4f76459…
|
drh
|
6072 |
** If no match is found, and &zErr is not NULL, then write into |
|
4f76459…
|
drh
|
6073 |
** zErr a message describing the valid choices. |
|
4f76459…
|
drh
|
6074 |
*/ |
|
4f76459…
|
drh
|
6075 |
static int pickStr(const char *zArg, char **pzErr, ...){ |
|
4f76459…
|
drh
|
6076 |
int i, n; |
|
4f76459…
|
drh
|
6077 |
const char *z; |
|
4f76459…
|
drh
|
6078 |
sqlite3_str *pMsg; |
|
4f76459…
|
drh
|
6079 |
va_list ap; |
|
4f76459…
|
drh
|
6080 |
va_start(ap, pzErr); |
|
4f76459…
|
drh
|
6081 |
i = 0; |
|
4f76459…
|
drh
|
6082 |
while( (z = va_arg(ap,const char*))!=0 && z[0]!=0 ){ |
|
4f76459…
|
drh
|
6083 |
if( cli_strcmp(zArg, z)==0 ) return i; |
|
4f76459…
|
drh
|
6084 |
i++; |
|
4f76459…
|
drh
|
6085 |
} |
|
4f76459…
|
drh
|
6086 |
va_end(ap); |
|
4f76459…
|
drh
|
6087 |
if( pzErr==0 ) return -1; |
|
4f76459…
|
drh
|
6088 |
n = i; |
|
4f76459…
|
drh
|
6089 |
pMsg = sqlite3_str_new(0); |
|
4f76459…
|
drh
|
6090 |
va_start(ap, pzErr); |
|
4f76459…
|
drh
|
6091 |
sqlite3_str_appendall(pMsg, "should be"); |
|
4f76459…
|
drh
|
6092 |
i = 0; |
|
4f76459…
|
drh
|
6093 |
while( (z = va_arg(ap, const char*))!=0 && z[0]!=0 ){ |
|
4f76459…
|
drh
|
6094 |
if( i==n-1 ){ |
|
4f76459…
|
drh
|
6095 |
sqlite3_str_append(pMsg,", or",4); |
|
4f76459…
|
drh
|
6096 |
}else if( i>0 ){ |
|
4f76459…
|
drh
|
6097 |
sqlite3_str_append(pMsg, ",", 1); |
|
4f76459…
|
drh
|
6098 |
} |
|
4f76459…
|
drh
|
6099 |
sqlite3_str_appendf(pMsg, " %s", z); |
|
4f76459…
|
drh
|
6100 |
i++; |
|
4f76459…
|
drh
|
6101 |
} |
|
4f76459…
|
drh
|
6102 |
va_end(ap); |
|
4f76459…
|
drh
|
6103 |
*pzErr = sqlite3_str_finish(pMsg); |
|
4f76459…
|
drh
|
6104 |
return -1; |
|
4f76459…
|
drh
|
6105 |
} |
|
4f76459…
|
drh
|
6106 |
|
|
4f76459…
|
drh
|
6107 |
/* |
|
4f76459…
|
drh
|
6108 |
** DOT-COMMAND: .import |
|
4f76459…
|
drh
|
6109 |
** |
|
4f76459…
|
drh
|
6110 |
** USAGE: .import [OPTIONS] FILE TABLE |
|
4f76459…
|
drh
|
6111 |
** |
|
4f76459…
|
drh
|
6112 |
** Import CSV or similar text from FILE into TABLE. If TABLE does |
|
4f76459…
|
drh
|
6113 |
** not exist, it is created using the first row of FILE as the column |
|
4f76459…
|
drh
|
6114 |
** names. If FILE begins with "|" then it is a command that is run |
|
b9ecacf…
|
drh
|
6115 |
** and the output from the command is used as the input data. If |
|
b9ecacf…
|
drh
|
6116 |
** FILE begins with "<<" followed by a label, then content is read from |
|
b9ecacf…
|
drh
|
6117 |
** the script until the first line that matches the label. |
|
b9ecacf…
|
drh
|
6118 |
** |
|
b9ecacf…
|
drh
|
6119 |
** The content of FILE is interpreted using RFC-4180 ("CSV") quoting |
|
b9ecacf…
|
drh
|
6120 |
** rules unless the current mode is "ascii" or "tabs" or unless one |
|
b9ecacf…
|
drh
|
6121 |
** the --ascii option is used. |
|
4f76459…
|
drh
|
6122 |
** |
|
b9ecacf…
|
drh
|
6123 |
** The column and row separators must be single ASCII characters. If |
|
b9ecacf…
|
drh
|
6124 |
** multiple characters or a Unicode character are specified for the |
|
b9ecacf…
|
drh
|
6125 |
** separators, then only the first byte of the separator is used. Except, |
|
b9ecacf…
|
drh
|
6126 |
** if the row separator is \n and the mode is not --ascii, then \r\n is |
|
b9ecacf…
|
drh
|
6127 |
** understood as a row separator too. |
|
4f76459…
|
drh
|
6128 |
** |
|
4f76459…
|
drh
|
6129 |
** Options: |
|
b9ecacf…
|
drh
|
6130 |
** --ascii Do not use RFC-4180 quoting. Use \037 and \036 |
|
b9ecacf…
|
drh
|
6131 |
** as column and row separators on input, unless other |
|
b9ecacf…
|
drh
|
6132 |
** delimiters are specified using --colsep and/or --rowsep |
|
b9ecacf…
|
drh
|
6133 |
** --colsep CHAR Use CHAR as the column separator. |
|
4f76459…
|
drh
|
6134 |
** --csv Input is standard RFC-4180 CSV. |
|
7b0960d…
|
drh
|
6135 |
** --esc CHAR Use CHAR as an escape character in unquoted CSV inputs. |
|
7b0960d…
|
drh
|
6136 |
** --qesc CHAR Use CHAR as an escape character in quoted CSV inputs. |
|
b9ecacf…
|
drh
|
6137 |
** --rowsep CHAR Use CHAR as the row separator. |
|
4f76459…
|
drh
|
6138 |
** --schema S When creating TABLE, put it in schema S |
|
4f76459…
|
drh
|
6139 |
** --skip N Ignore the first N rows of input |
|
4f76459…
|
drh
|
6140 |
** -v Verbose mode |
|
4f76459…
|
drh
|
6141 |
*/ |
|
4f76459…
|
drh
|
6142 |
static int dotCmdImport(ShellState *p){ |
|
4f76459…
|
drh
|
6143 |
int nArg = p->dot.nArg; /* Number of arguments */ |
|
4f76459…
|
drh
|
6144 |
char **azArg = p->dot.azArg;/* Argument list */ |
|
4f76459…
|
drh
|
6145 |
char *zTable = 0; /* Insert data into this table */ |
|
4f76459…
|
drh
|
6146 |
char *zSchema = 0; /* Schema of zTable */ |
|
4f76459…
|
drh
|
6147 |
char *zFile = 0; /* Name of file to extra content from */ |
|
4f76459…
|
drh
|
6148 |
sqlite3_stmt *pStmt = NULL; /* A statement */ |
|
4f76459…
|
drh
|
6149 |
int nCol; /* Number of columns in the table */ |
|
4f76459…
|
drh
|
6150 |
i64 nByte; /* Number of bytes in an SQL string */ |
|
4f76459…
|
drh
|
6151 |
int i, j; /* Loop counters */ |
|
4f76459…
|
drh
|
6152 |
int needCommit; /* True to COMMIT or ROLLBACK at end */ |
|
4f76459…
|
drh
|
6153 |
char *zSql = 0; /* An SQL statement */ |
|
4f76459…
|
drh
|
6154 |
ImportCtx sCtx; /* Reader context */ |
|
4f76459…
|
drh
|
6155 |
char *(SQLITE_CDECL *xRead)(ImportCtx*); /* Func to read one value */ |
|
4f76459…
|
drh
|
6156 |
int eVerbose = 0; /* Larger for more console output */ |
|
4f76459…
|
drh
|
6157 |
i64 nSkip = 0; /* Initial lines to skip */ |
|
b9ecacf…
|
drh
|
6158 |
i64 iLineOffset = 0; /* Offset to the first line of input */ |
|
4f76459…
|
drh
|
6159 |
char *zCreate = 0; /* CREATE TABLE statement text */ |
|
4f76459…
|
drh
|
6160 |
int rc; /* Result code */ |
|
4f76459…
|
drh
|
6161 |
|
|
4f76459…
|
drh
|
6162 |
failIfSafeMode(p, "cannot run .import in safe mode"); |
|
4f76459…
|
drh
|
6163 |
memset(&sCtx, 0, sizeof(sCtx)); |
|
4f76459…
|
drh
|
6164 |
if( p->mode.eMode==MODE_Ascii ){ |
|
4f76459…
|
drh
|
6165 |
xRead = ascii_read_one_field; |
|
4f76459…
|
drh
|
6166 |
}else{ |
|
4f76459…
|
drh
|
6167 |
xRead = csv_read_one_field; |
|
4f76459…
|
drh
|
6168 |
} |
|
4f76459…
|
drh
|
6169 |
for(i=1; i<nArg; i++){ |
|
4f76459…
|
drh
|
6170 |
char *z = azArg[i]; |
|
4f76459…
|
drh
|
6171 |
if( z[0]=='-' && z[1]=='-' ) z++; |
|
4f76459…
|
drh
|
6172 |
if( z[0]!='-' ){ |
|
4f76459…
|
drh
|
6173 |
if( zFile==0 ){ |
|
4f76459…
|
drh
|
6174 |
zFile = z; |
|
4f76459…
|
drh
|
6175 |
}else if( zTable==0 ){ |
|
4f76459…
|
drh
|
6176 |
zTable = z; |
|
4f76459…
|
drh
|
6177 |
}else{ |
|
4f76459…
|
drh
|
6178 |
dotCmdError(p, i, "unknown argument", 0); |
|
4f76459…
|
drh
|
6179 |
return 1; |
|
4f76459…
|
drh
|
6180 |
} |
|
4f76459…
|
drh
|
6181 |
}else if( cli_strcmp(z,"-v")==0 ){ |
|
4f76459…
|
drh
|
6182 |
eVerbose++; |
|
4f76459…
|
drh
|
6183 |
}else if( cli_strcmp(z,"-schema")==0 && i<nArg-1 ){ |
|
4f76459…
|
drh
|
6184 |
zSchema = azArg[++i]; |
|
4f76459…
|
drh
|
6185 |
}else if( cli_strcmp(z,"-skip")==0 && i<nArg-1 ){ |
|
4f76459…
|
drh
|
6186 |
nSkip = integerValue(azArg[++i]); |
|
4f76459…
|
drh
|
6187 |
}else if( cli_strcmp(z,"-ascii")==0 ){ |
|
b9ecacf…
|
drh
|
6188 |
if( sCtx.cColSep==0 ) sCtx.cColSep = SEP_Unit[0]; |
|
b9ecacf…
|
drh
|
6189 |
if( sCtx.cRowSep==0 ) sCtx.cRowSep = SEP_Record[0]; |
|
4f76459…
|
drh
|
6190 |
xRead = ascii_read_one_field; |
|
4f76459…
|
drh
|
6191 |
}else if( cli_strcmp(z,"-csv")==0 ){ |
|
b9ecacf…
|
drh
|
6192 |
if( sCtx.cColSep==0 ) sCtx.cColSep = ','; |
|
b9ecacf…
|
drh
|
6193 |
if( sCtx.cRowSep==0 ) sCtx.cRowSep = '\n'; |
|
4f76459…
|
drh
|
6194 |
xRead = csv_read_one_field; |
|
7b0960d…
|
drh
|
6195 |
}else if( cli_strcmp(z,"-esc")==0 ){ |
|
7b0960d…
|
drh
|
6196 |
sCtx.cUQEscape = azArg[++i][0]; |
|
7b0960d…
|
drh
|
6197 |
}else if( cli_strcmp(z,"-qesc")==0 ){ |
|
7b0960d…
|
drh
|
6198 |
sCtx.cQEscape = azArg[++i][0]; |
|
b9ecacf…
|
drh
|
6199 |
}else if( cli_strcmp(z,"-colsep")==0 ){ |
|
b9ecacf…
|
drh
|
6200 |
if( i==nArg-1 ){ |
|
b9ecacf…
|
drh
|
6201 |
dotCmdError(p, i, "missing argument", 0); |
|
b9ecacf…
|
drh
|
6202 |
return 1; |
|
b9ecacf…
|
drh
|
6203 |
} |
|
b9ecacf…
|
drh
|
6204 |
i++; |
|
b9ecacf…
|
drh
|
6205 |
sCtx.cColSep = azArg[i][0]; |
|
b9ecacf…
|
drh
|
6206 |
}else if( cli_strcmp(z,"-rowsep")==0 ){ |
|
b9ecacf…
|
drh
|
6207 |
if( i==nArg-1 ){ |
|
b9ecacf…
|
drh
|
6208 |
dotCmdError(p, i, "missing argument", 0); |
|
b9ecacf…
|
drh
|
6209 |
return 1; |
|
b9ecacf…
|
drh
|
6210 |
} |
|
b9ecacf…
|
drh
|
6211 |
i++; |
|
b9ecacf…
|
drh
|
6212 |
sCtx.cRowSep = azArg[i][0]; |
|
4f76459…
|
drh
|
6213 |
}else{ |
|
4f76459…
|
drh
|
6214 |
dotCmdError(p, i, "unknown option", 0); |
|
4f76459…
|
drh
|
6215 |
return 1; |
|
4f76459…
|
drh
|
6216 |
} |
|
4f76459…
|
drh
|
6217 |
} |
|
4f76459…
|
drh
|
6218 |
if( zTable==0 ){ |
|
b9ecacf…
|
drh
|
6219 |
dotCmdError(p, nArg, 0, "Missing %s argument\n", |
|
4f76459…
|
drh
|
6220 |
zFile==0 ? "FILE" : "TABLE"); |
|
4f76459…
|
drh
|
6221 |
return 1; |
|
4f76459…
|
drh
|
6222 |
} |
|
4f76459…
|
drh
|
6223 |
seenInterrupt = 0; |
|
4f76459…
|
drh
|
6224 |
open_db(p, 0); |
|
b9ecacf…
|
drh
|
6225 |
if( sCtx.cColSep==0 ){ |
|
b9ecacf…
|
drh
|
6226 |
if( p->mode.spec.zColumnSep && p->mode.spec.zColumnSep[0]!=0 ){ |
|
b9ecacf…
|
drh
|
6227 |
sCtx.cColSep = p->mode.spec.zColumnSep[0]; |
|
b9ecacf…
|
drh
|
6228 |
}else{ |
|
b9ecacf…
|
drh
|
6229 |
sCtx.cColSep = ','; |
|
b9ecacf…
|
drh
|
6230 |
} |
|
b9ecacf…
|
drh
|
6231 |
} |
|
b9ecacf…
|
drh
|
6232 |
if( (sCtx.cColSep & 0x80)!=0 ){ |
|
b9ecacf…
|
drh
|
6233 |
eputz("Error: .import column separator must be ASCII\n"); |
|
b9ecacf…
|
drh
|
6234 |
return 1; |
|
b9ecacf…
|
drh
|
6235 |
} |
|
b9ecacf…
|
drh
|
6236 |
if( sCtx.cRowSep==0 ){ |
|
b9ecacf…
|
drh
|
6237 |
if( p->mode.spec.zRowSep && p->mode.spec.zRowSep[0]!=0 ){ |
|
b9ecacf…
|
drh
|
6238 |
sCtx.cRowSep = p->mode.spec.zRowSep[0]; |
|
b9ecacf…
|
drh
|
6239 |
}else{ |
|
b9ecacf…
|
drh
|
6240 |
sCtx.cRowSep = '\n'; |
|
b9ecacf…
|
drh
|
6241 |
} |
|
b9ecacf…
|
drh
|
6242 |
} |
|
b9ecacf…
|
drh
|
6243 |
if( sCtx.cRowSep=='\r' && xRead!=ascii_read_one_field ){ |
|
b9ecacf…
|
drh
|
6244 |
sCtx.cRowSep = '\n'; |
|
b9ecacf…
|
drh
|
6245 |
} |
|
b9ecacf…
|
drh
|
6246 |
if( (sCtx.cRowSep & 0x80)!=0 ){ |
|
b9ecacf…
|
drh
|
6247 |
eputz("Error: .import row separator must be ASCII\n"); |
|
b9ecacf…
|
drh
|
6248 |
return 1; |
|
4f76459…
|
drh
|
6249 |
} |
|
4f76459…
|
drh
|
6250 |
sCtx.zFile = zFile; |
|
4f76459…
|
drh
|
6251 |
sCtx.nLine = 1; |
|
4f76459…
|
drh
|
6252 |
if( sCtx.zFile[0]=='|' ){ |
|
4f76459…
|
drh
|
6253 |
#ifdef SQLITE_OMIT_POPEN |
|
4f76459…
|
drh
|
6254 |
eputz("Error: pipes are not supported in this OS\n"); |
|
4f76459…
|
drh
|
6255 |
return 1; |
|
4f76459…
|
drh
|
6256 |
#else |
|
4f76459…
|
drh
|
6257 |
sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); |
|
4f76459…
|
drh
|
6258 |
sCtx.zFile = "<pipe>"; |
|
4f76459…
|
drh
|
6259 |
sCtx.xCloser = pclose; |
|
4f76459…
|
drh
|
6260 |
#endif |
|
b9ecacf…
|
drh
|
6261 |
}else if( sCtx.zFile[0]=='<' && sCtx.zFile[1]=='<' && sCtx.zFile[2]!=0 ){ |
|
b9ecacf…
|
drh
|
6262 |
/* Input text comes from subsequent lines of script until the zFile |
|
b9ecacf…
|
drh
|
6263 |
** delimiter */ |
|
b9ecacf…
|
drh
|
6264 |
int nEndMark = strlen30(zFile)-2; |
|
b9ecacf…
|
drh
|
6265 |
char *zEndMark = &zFile[2]; |
|
b9ecacf…
|
drh
|
6266 |
sqlite3_str *pContent = sqlite3_str_new(p->db); |
|
b9ecacf…
|
drh
|
6267 |
int ckEnd = 1; |
|
b9ecacf…
|
drh
|
6268 |
i64 iStart = p->lineno; |
|
b9ecacf…
|
drh
|
6269 |
char zLine[2000]; |
|
b9ecacf…
|
drh
|
6270 |
sCtx.zFile = p->zInFile; |
|
b9ecacf…
|
drh
|
6271 |
sCtx.nLine = p->lineno+1; |
|
b9ecacf…
|
drh
|
6272 |
iLineOffset = p->lineno; |
|
b9ecacf…
|
drh
|
6273 |
while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
|
b9ecacf…
|
drh
|
6274 |
if( ckEnd && cli_strncmp(zLine,zEndMark,nEndMark)==0 ){ |
|
b9ecacf…
|
drh
|
6275 |
ckEnd = 2; |
|
b9ecacf…
|
drh
|
6276 |
if( strchr(zLine,'\n') ) p->lineno++; |
|
b9ecacf…
|
drh
|
6277 |
break; |
|
b9ecacf…
|
drh
|
6278 |
} |
|
b9ecacf…
|
drh
|
6279 |
if( strchr(zLine,'\n') ){ |
|
b9ecacf…
|
drh
|
6280 |
p->lineno++; |
|
b9ecacf…
|
drh
|
6281 |
ckEnd = 1; |
|
b9ecacf…
|
drh
|
6282 |
}else{ |
|
b9ecacf…
|
drh
|
6283 |
ckEnd = 0; |
|
b9ecacf…
|
drh
|
6284 |
} |
|
b9ecacf…
|
drh
|
6285 |
sqlite3_str_appendall(pContent, zLine); |
|
b9ecacf…
|
drh
|
6286 |
} |
|
b9ecacf…
|
drh
|
6287 |
sCtx.zIn = sqlite3_str_finish(pContent); |
|
b9ecacf…
|
drh
|
6288 |
if( sCtx.zIn==0 ){ |
|
b9ecacf…
|
drh
|
6289 |
sCtx.zIn = sqlite3_mprintf(""); |
|
b9ecacf…
|
drh
|
6290 |
} |
|
b9ecacf…
|
drh
|
6291 |
if( ckEnd<2 ){ |
|
b9ecacf…
|
drh
|
6292 |
i64 savedLn = p->lineno; |
|
b9ecacf…
|
drh
|
6293 |
p->lineno = iStart; |
|
b9ecacf…
|
drh
|
6294 |
dotCmdError(p, 0, 0,"Content terminator \"%s\" not found.",zEndMark); |
|
b9ecacf…
|
drh
|
6295 |
p->lineno = savedLn; |
|
b9ecacf…
|
drh
|
6296 |
import_cleanup(&sCtx); |
|
b9ecacf…
|
drh
|
6297 |
return 1; |
|
b9ecacf…
|
drh
|
6298 |
} |
|
4f76459…
|
drh
|
6299 |
}else{ |
|
4f76459…
|
drh
|
6300 |
sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); |
|
4f76459…
|
drh
|
6301 |
sCtx.xCloser = fclose; |
|
4f76459…
|
drh
|
6302 |
} |
|
b9ecacf…
|
drh
|
6303 |
if( sCtx.in==0 && sCtx.zIn==0 ){ |
|
b9ecacf…
|
drh
|
6304 |
dotCmdError(p, 0, 0, "cannot open \"%s\"", zFile); |
|
b9ecacf…
|
drh
|
6305 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6306 |
return 1; |
|
4f76459…
|
drh
|
6307 |
} |
|
b9ecacf…
|
drh
|
6308 |
if( eVerbose>=1 ){ |
|
4f76459…
|
drh
|
6309 |
char zSep[2]; |
|
4f76459…
|
drh
|
6310 |
zSep[1] = 0; |
|
4f76459…
|
drh
|
6311 |
zSep[0] = sCtx.cColSep; |
|
4f76459…
|
drh
|
6312 |
cli_puts("Column separator ", p->out); |
|
4f76459…
|
drh
|
6313 |
output_c_string(p->out, zSep); |
|
4f76459…
|
drh
|
6314 |
cli_puts(", row separator ", p->out); |
|
4f76459…
|
drh
|
6315 |
zSep[0] = sCtx.cRowSep; |
|
4f76459…
|
drh
|
6316 |
output_c_string(p->out, zSep); |
|
4f76459…
|
drh
|
6317 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
6318 |
} |
|
4f76459…
|
drh
|
6319 |
sCtx.z = sqlite3_malloc64(120); |
|
4f76459…
|
drh
|
6320 |
if( sCtx.z==0 ){ |
|
4f76459…
|
drh
|
6321 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6322 |
shell_out_of_memory(); |
|
4f76459…
|
drh
|
6323 |
} |
|
4f76459…
|
drh
|
6324 |
/* Below, resources must be freed before exit. */ |
|
4f76459…
|
drh
|
6325 |
while( nSkip>0 ){ |
|
4f76459…
|
drh
|
6326 |
nSkip--; |
|
4f76459…
|
drh
|
6327 |
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} |
|
4f76459…
|
drh
|
6328 |
} |
|
4f76459…
|
drh
|
6329 |
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ |
|
4f76459…
|
drh
|
6330 |
if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) |
|
4f76459…
|
drh
|
6331 |
&& 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" |
|
4f76459…
|
drh
|
6332 |
" WHERE name=%Q AND type='view'", |
|
4f76459…
|
drh
|
6333 |
zSchema ? zSchema : "main", zTable) |
|
4f76459…
|
drh
|
6334 |
){ |
|
4f76459…
|
drh
|
6335 |
/* Table does not exist. Create it. */ |
|
4f76459…
|
drh
|
6336 |
sqlite3 *dbCols = 0; |
|
4f76459…
|
drh
|
6337 |
char *zRenames = 0; |
|
4f76459…
|
drh
|
6338 |
char *zColDefs; |
|
4f76459…
|
drh
|
6339 |
zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"", |
|
4f76459…
|
drh
|
6340 |
zSchema ? zSchema : "main", zTable); |
|
4f76459…
|
drh
|
6341 |
while( xRead(&sCtx) ){ |
|
4f76459…
|
drh
|
6342 |
zAutoColumn(sCtx.z, &dbCols, 0); |
|
4f76459…
|
drh
|
6343 |
if( sCtx.cTerm!=sCtx.cColSep ) break; |
|
4f76459…
|
drh
|
6344 |
} |
|
4f76459…
|
drh
|
6345 |
zColDefs = zAutoColumn(0, &dbCols, &zRenames); |
|
4f76459…
|
drh
|
6346 |
if( zRenames!=0 ){ |
|
4f76459…
|
drh
|
6347 |
cli_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr, |
|
4f76459…
|
drh
|
6348 |
"Columns renamed during .import %s due to duplicates:\n" |
|
4f76459…
|
drh
|
6349 |
"%s\n", sCtx.zFile, zRenames); |
|
4f76459…
|
drh
|
6350 |
sqlite3_free(zRenames); |
|
4f76459…
|
drh
|
6351 |
} |
|
4f76459…
|
drh
|
6352 |
assert(dbCols==0); |
|
4f76459…
|
drh
|
6353 |
if( zColDefs==0 ){ |
|
4f76459…
|
drh
|
6354 |
cli_printf(stderr,"%s: empty file\n", sCtx.zFile); |
|
4f76459…
|
drh
|
6355 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6356 |
sqlite3_free(zCreate); |
|
4f76459…
|
drh
|
6357 |
return 1; |
|
4f76459…
|
drh
|
6358 |
} |
|
4f76459…
|
drh
|
6359 |
zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs); |
|
4f76459…
|
drh
|
6360 |
if( zCreate==0 ){ |
|
4f76459…
|
drh
|
6361 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6362 |
shell_out_of_memory(); |
|
4f76459…
|
drh
|
6363 |
} |
|
4f76459…
|
drh
|
6364 |
if( eVerbose>=1 ){ |
|
4f76459…
|
drh
|
6365 |
cli_printf(p->out, "%s\n", zCreate); |
|
4f76459…
|
drh
|
6366 |
} |
|
4f76459…
|
drh
|
6367 |
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); |
|
4f76459…
|
drh
|
6368 |
if( rc ){ |
|
4f76459…
|
drh
|
6369 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
6370 |
"%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
6371 |
} |
|
4f76459…
|
drh
|
6372 |
sqlite3_free(zCreate); |
|
4f76459…
|
drh
|
6373 |
zCreate = 0; |
|
4f76459…
|
drh
|
6374 |
if( rc ){ |
|
4f76459…
|
drh
|
6375 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6376 |
return 1; |
|
4f76459…
|
drh
|
6377 |
} |
|
4f76459…
|
drh
|
6378 |
} |
|
4f76459…
|
drh
|
6379 |
zSql = sqlite3_mprintf("SELECT count(*) FROM pragma_table_info(%Q,%Q);", |
|
4f76459…
|
drh
|
6380 |
zTable, zSchema); |
|
4f76459…
|
drh
|
6381 |
if( zSql==0 ){ |
|
4f76459…
|
drh
|
6382 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6383 |
shell_out_of_memory(); |
|
4f76459…
|
drh
|
6384 |
} |
|
4f76459…
|
drh
|
6385 |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
|
4f76459…
|
drh
|
6386 |
sqlite3_free(zSql); |
|
4f76459…
|
drh
|
6387 |
zSql = 0; |
|
4f76459…
|
drh
|
6388 |
if( rc ){ |
|
4f76459…
|
drh
|
6389 |
if (pStmt) sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
6390 |
shellDatabaseError(p->db); |
|
4f76459…
|
drh
|
6391 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6392 |
return 1; |
|
4f76459…
|
drh
|
6393 |
} |
|
4f76459…
|
drh
|
6394 |
if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
4f76459…
|
drh
|
6395 |
nCol = sqlite3_column_int(pStmt, 0); |
|
4f76459…
|
drh
|
6396 |
}else{ |
|
4f76459…
|
drh
|
6397 |
nCol = 0; |
|
4f76459…
|
drh
|
6398 |
} |
|
4f76459…
|
drh
|
6399 |
sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
6400 |
pStmt = 0; |
|
4f76459…
|
drh
|
6401 |
if( nCol==0 ) return 0; /* no columns, no error */ |
|
4f76459…
|
drh
|
6402 |
|
|
4f76459…
|
drh
|
6403 |
nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */ |
|
4f76459…
|
drh
|
6404 |
+ (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */ |
|
4f76459…
|
drh
|
6405 |
+ strlen(zTable)*2 + 2 /* Quoted table name */ |
|
4f76459…
|
drh
|
6406 |
+ nCol*2; /* Space for ",?" for each column */ |
|
4f76459…
|
drh
|
6407 |
zSql = sqlite3_malloc64( nByte ); |
|
4f76459…
|
drh
|
6408 |
if( zSql==0 ){ |
|
4f76459…
|
drh
|
6409 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6410 |
shell_out_of_memory(); |
|
4f76459…
|
drh
|
6411 |
} |
|
4f76459…
|
drh
|
6412 |
if( zSchema ){ |
|
4f76459…
|
drh
|
6413 |
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?", |
|
4f76459…
|
drh
|
6414 |
zSchema, zTable); |
|
4f76459…
|
drh
|
6415 |
}else{ |
|
4f76459…
|
drh
|
6416 |
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable); |
|
4f76459…
|
drh
|
6417 |
} |
|
4f76459…
|
drh
|
6418 |
j = strlen30(zSql); |
|
4f76459…
|
drh
|
6419 |
for(i=1; i<nCol; i++){ |
|
4f76459…
|
drh
|
6420 |
zSql[j++] = ','; |
|
4f76459…
|
drh
|
6421 |
zSql[j++] = '?'; |
|
4f76459…
|
drh
|
6422 |
} |
|
4f76459…
|
drh
|
6423 |
zSql[j++] = ')'; |
|
4f76459…
|
drh
|
6424 |
zSql[j] = 0; |
|
4f76459…
|
drh
|
6425 |
assert( j<nByte ); |
|
4f76459…
|
drh
|
6426 |
if( eVerbose>=2 ){ |
|
4f76459…
|
drh
|
6427 |
cli_printf(p->out, "Insert using: %s\n", zSql); |
|
4f76459…
|
drh
|
6428 |
} |
|
4f76459…
|
drh
|
6429 |
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); |
|
4f76459…
|
drh
|
6430 |
sqlite3_free(zSql); |
|
4f76459…
|
drh
|
6431 |
zSql = 0; |
|
4f76459…
|
drh
|
6432 |
if( rc ){ |
|
4f76459…
|
drh
|
6433 |
shellDatabaseError(p->db); |
|
4f76459…
|
drh
|
6434 |
if (pStmt) sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
6435 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6436 |
return 1; |
|
4f76459…
|
drh
|
6437 |
} |
|
4f76459…
|
drh
|
6438 |
needCommit = sqlite3_get_autocommit(p->db); |
|
4f76459…
|
drh
|
6439 |
if( needCommit ) sqlite3_exec(p->db, "BEGIN", 0, 0, 0); |
|
4f76459…
|
drh
|
6440 |
do{ |
|
4f76459…
|
drh
|
6441 |
int startLine = sCtx.nLine; |
|
4f76459…
|
drh
|
6442 |
for(i=0; i<nCol; i++){ |
|
4f76459…
|
drh
|
6443 |
char *z = xRead(&sCtx); |
|
4f76459…
|
drh
|
6444 |
/* |
|
4f76459…
|
drh
|
6445 |
** Did we reach end-of-file before finding any columns? |
|
4f76459…
|
drh
|
6446 |
** If so, stop instead of NULL filling the remaining columns. |
|
4f76459…
|
drh
|
6447 |
*/ |
|
4f76459…
|
drh
|
6448 |
if( z==0 && i==0 ) break; |
|
4f76459…
|
drh
|
6449 |
/* |
|
4f76459…
|
drh
|
6450 |
** Did we reach end-of-file OR end-of-line before finding any |
|
4f76459…
|
drh
|
6451 |
** columns in ASCII mode? If so, stop instead of NULL filling |
|
4f76459…
|
drh
|
6452 |
** the remaining columns. |
|
4f76459…
|
drh
|
6453 |
*/ |
|
4f76459…
|
drh
|
6454 |
if( p->mode.eMode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break; |
|
4f76459…
|
drh
|
6455 |
/* |
|
4f76459…
|
drh
|
6456 |
** For CSV mode, per RFC 4180, accept EOF in lieu of final |
|
4f76459…
|
drh
|
6457 |
** record terminator but only for last field of multi-field row. |
|
4f76459…
|
drh
|
6458 |
** (If there are too few fields, it's not valid CSV anyway.) |
|
4f76459…
|
drh
|
6459 |
*/ |
|
4f76459…
|
drh
|
6460 |
if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){ |
|
4f76459…
|
drh
|
6461 |
z = ""; |
|
4f76459…
|
drh
|
6462 |
} |
|
4f76459…
|
drh
|
6463 |
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); |
|
4f76459…
|
drh
|
6464 |
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ |
|
b9ecacf…
|
drh
|
6465 |
if( i==0 && (strcmp(z,"\n")==0 || strcmp(z,"\r\n")==0) ){ |
|
b9ecacf…
|
drh
|
6466 |
/* Ignore trailing \n or \r\n when some other row separator */ |
|
b9ecacf…
|
drh
|
6467 |
break; |
|
b9ecacf…
|
drh
|
6468 |
} |
|
4f76459…
|
drh
|
6469 |
cli_printf(stderr,"%s:%d: expected %d columns but found %d" |
|
4f76459…
|
drh
|
6470 |
" - filling the rest with NULL\n", |
|
4f76459…
|
drh
|
6471 |
sCtx.zFile, startLine, nCol, i+1); |
|
4f76459…
|
drh
|
6472 |
i += 2; |
|
4f76459…
|
drh
|
6473 |
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; } |
|
4f76459…
|
drh
|
6474 |
} |
|
4f76459…
|
drh
|
6475 |
} |
|
4f76459…
|
drh
|
6476 |
if( sCtx.cTerm==sCtx.cColSep ){ |
|
4f76459…
|
drh
|
6477 |
do{ |
|
4f76459…
|
drh
|
6478 |
xRead(&sCtx); |
|
4f76459…
|
drh
|
6479 |
i++; |
|
4f76459…
|
drh
|
6480 |
}while( sCtx.cTerm==sCtx.cColSep ); |
|
4f76459…
|
drh
|
6481 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
6482 |
"%s:%d: expected %d columns but found %d - extras ignored\n", |
|
4f76459…
|
drh
|
6483 |
sCtx.zFile, startLine, nCol, i); |
|
4f76459…
|
drh
|
6484 |
} |
|
4f76459…
|
drh
|
6485 |
if( i>=nCol ){ |
|
4f76459…
|
drh
|
6486 |
sqlite3_step(pStmt); |
|
4f76459…
|
drh
|
6487 |
rc = sqlite3_reset(pStmt); |
|
4f76459…
|
drh
|
6488 |
if( rc!=SQLITE_OK ){ |
|
4f76459…
|
drh
|
6489 |
cli_printf(stderr,"%s:%d: INSERT failed: %s\n", |
|
4f76459…
|
drh
|
6490 |
sCtx.zFile, startLine, sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
6491 |
sCtx.nErr++; |
|
b9ecacf…
|
drh
|
6492 |
if( bail_on_error ) break; |
|
4f76459…
|
drh
|
6493 |
}else{ |
|
4f76459…
|
drh
|
6494 |
sCtx.nRow++; |
|
4f76459…
|
drh
|
6495 |
} |
|
4f76459…
|
drh
|
6496 |
} |
|
4f76459…
|
drh
|
6497 |
}while( sCtx.cTerm!=EOF ); |
|
4f76459…
|
drh
|
6498 |
|
|
4f76459…
|
drh
|
6499 |
import_cleanup(&sCtx); |
|
4f76459…
|
drh
|
6500 |
sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
6501 |
if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); |
|
4f76459…
|
drh
|
6502 |
if( eVerbose>0 ){ |
|
4f76459…
|
drh
|
6503 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
6504 |
"Added %d rows with %d errors using %d lines of input\n", |
|
b9ecacf…
|
drh
|
6505 |
sCtx.nRow, sCtx.nErr, sCtx.nLine-1-iLineOffset); |
|
4f76459…
|
drh
|
6506 |
} |
|
b9ecacf…
|
drh
|
6507 |
return sCtx.nErr ? 1 : 0; |
|
4f76459…
|
drh
|
6508 |
} |
|
4f76459…
|
drh
|
6509 |
|
|
4f76459…
|
drh
|
6510 |
|
|
4f76459…
|
drh
|
6511 |
/* |
|
4f76459…
|
drh
|
6512 |
** This function computes what to show the user about the configured |
|
4f76459…
|
drh
|
6513 |
** titles (or column-names). Output is an integer between 0 and 3: |
|
4f76459…
|
drh
|
6514 |
** |
|
4f76459…
|
drh
|
6515 |
** 0: The titles do not matter. Never show anything. |
|
4f76459…
|
drh
|
6516 |
** 1: Show "--titles off" |
|
4f76459…
|
drh
|
6517 |
** 2: Show "--titles on" |
|
4f76459…
|
drh
|
6518 |
** 3: Show "--title VALUE" where VALUE is an encoding method |
|
4f76459…
|
drh
|
6519 |
** to use, one of: plain sql csv html tcl json |
|
4f76459…
|
drh
|
6520 |
** |
|
4f76459…
|
drh
|
6521 |
** Inputs are: |
|
4f76459…
|
drh
|
6522 |
** |
|
4f76459…
|
drh
|
6523 |
** spec.bTitles (bT) Whether or not to show the titles |
|
4f76459…
|
drh
|
6524 |
** spec.eTitle (eT) The actual encoding to be used for titles |
|
4f76459…
|
drh
|
6525 |
** ModeInfo.bHdr (bH) Default value for spec.bTitles |
|
4f76459…
|
drh
|
6526 |
** ModeInfo.eHdr (eH) Default value for spec.eTitle |
|
4f76459…
|
drh
|
6527 |
** bAll Whether the -v option is used |
|
4f76459…
|
drh
|
6528 |
*/ |
|
4f76459…
|
drh
|
6529 |
static int modeTitleDsply(ShellState *p, int bAll){ |
|
4f76459…
|
drh
|
6530 |
int eMode = p->mode.eMode; |
|
4f76459…
|
drh
|
6531 |
const ModeInfo *pI = &aModeInfo[eMode]; |
|
4f76459…
|
drh
|
6532 |
int bT = p->mode.spec.bTitles; |
|
4f76459…
|
drh
|
6533 |
int eT = p->mode.spec.eTitle; |
|
4f76459…
|
drh
|
6534 |
int bH = pI->bHdr; |
|
4f76459…
|
drh
|
6535 |
int eH = pI->eHdr; |
|
4f76459…
|
drh
|
6536 |
|
|
4f76459…
|
drh
|
6537 |
/* Variable "v" is the truth table that will determine the answer |
|
4f76459…
|
drh
|
6538 |
** |
|
4f76459…
|
drh
|
6539 |
** Actual encoding is different from default |
|
cb89386…
|
drh
|
6540 |
** vvvvvvvv */ |
|
cb89386…
|
drh
|
6541 |
sqlite3_uint64 v = UINT64_C(0x0133013311220102); |
|
cb89386…
|
drh
|
6542 |
/* ^^^^ ^^^^ |
|
4f76459…
|
drh
|
6543 |
** Upper 2-byte groups for when ON/OFF disagrees with |
|
4f76459…
|
drh
|
6544 |
** the default. */ |
|
4f76459…
|
drh
|
6545 |
|
|
4f76459…
|
drh
|
6546 |
if( bH==0 ) return 0; /* Header not appliable. Ex: off, count */ |
|
4f76459…
|
drh
|
6547 |
|
|
4f76459…
|
drh
|
6548 |
if( eT==0 ) eT = eH; /* Fill in missing spec.eTitle */ |
|
4f76459…
|
drh
|
6549 |
if( bT==0 ) bT = bH; /* Fill in missing spec.bTitles */ |
|
4f76459…
|
drh
|
6550 |
|
|
4f76459…
|
drh
|
6551 |
if( eT!=eH ) v >>= 32; /* Encoding disagree in upper 4-bytes */ |
|
4f76459…
|
drh
|
6552 |
if( bT!=bH ) v >>= 16; /* ON/OFF disagree in upper 2-byte pairs */ |
|
4f76459…
|
drh
|
6553 |
if( bT<2 ) v >>= 8; /* ON in even bytes, OFF in odd bytes (1st byte 0) */ |
|
4f76459…
|
drh
|
6554 |
if( !bAll ) v >>= 4; /* bAll values are in the lower half-byte */ |
|
4f76459…
|
drh
|
6555 |
|
|
4f76459…
|
drh
|
6556 |
return v & 3; /* Return the selected truth-table entry */ |
|
4f76459…
|
drh
|
6557 |
} |
|
4f76459…
|
drh
|
6558 |
|
|
4f76459…
|
drh
|
6559 |
/* |
|
4f76459…
|
drh
|
6560 |
** DOT-COMMAND: .mode |
|
4f76459…
|
drh
|
6561 |
** |
|
4f76459…
|
drh
|
6562 |
** USAGE: .mode [MODE] [OPTIONS] |
|
4f76459…
|
drh
|
6563 |
** |
|
45de97f…
|
drh
|
6564 |
** Change the output mode to MODE and/or apply OPTIONS to the output mode. |
|
45de97f…
|
drh
|
6565 |
** Arguments are processed from left to right. If no arguments, show the |
|
45de97f…
|
drh
|
6566 |
** current output mode and relevant options. |
|
4f76459…
|
drh
|
6567 |
** |
|
4f76459…
|
drh
|
6568 |
** Options: |
|
4f76459…
|
drh
|
6569 |
** --align STRING Set the alignment of text in columnar modes |
|
4f76459…
|
drh
|
6570 |
** String consists of characters 'L', 'C', 'R' |
|
4f76459…
|
drh
|
6571 |
** meaning "left", "centered", and "right", with |
|
4f76459…
|
drh
|
6572 |
** one letter per column starting from the left. |
|
4f76459…
|
drh
|
6573 |
** Unspecified alignment defaults to 'L'. |
|
45de97f…
|
drh
|
6574 |
** --blob-quote ARG ARG can be "auto", "text", "sql", "hex", "tcl", |
|
45de97f…
|
drh
|
6575 |
** "json", or "size". Default is "auto". |
|
f4b3b59…
|
drh
|
6576 |
** --border on|off Show outer border on "box" and "table" modes. |
|
4f76459…
|
drh
|
6577 |
** --charlimit N Set the maximum number of output characters to |
|
4f76459…
|
drh
|
6578 |
** show for any single SQL value to N. Longer values |
|
4f76459…
|
drh
|
6579 |
** truncated. Zero means "no limit". |
|
4f76459…
|
drh
|
6580 |
** --colsep STRING Use STRING as the column separator |
|
4f76459…
|
drh
|
6581 |
** --escape ESC Enable/disable escaping of control characters |
|
45de97f…
|
drh
|
6582 |
** found in the output. ESC can be "off", "ascii", |
|
45de97f…
|
drh
|
6583 |
** or "symbol". |
|
4f76459…
|
drh
|
6584 |
** --linelimit N Set the maximum number of output lines to show for |
|
4f76459…
|
drh
|
6585 |
** any single SQL value to N. Longer values are |
|
4f76459…
|
drh
|
6586 |
** truncated. Zero means "no limit". Only works |
|
4f76459…
|
drh
|
6587 |
** in "line" mode and in columnar modes. |
|
ae7e3f0…
|
drh
|
6588 |
** --limits L,C,T Shorthand for "--linelimit L --charlimit C |
|
ae7e3f0…
|
drh
|
6589 |
** --titlelimit T". The ",T" can be omitted in which |
|
ae7e3f0…
|
drh
|
6590 |
** case the --titlelimit is unchanged. The argument |
|
ae7e3f0…
|
drh
|
6591 |
** can also be "off" to mean "0,0,0" or "on" to |
|
ae7e3f0…
|
drh
|
6592 |
** mean "5,300,20". |
|
4f76459…
|
drh
|
6593 |
** --list List available modes |
|
17f9878…
|
drh
|
6594 |
** --multiinsert N In "insert" mode, put multiple rows on a single |
|
17f9878…
|
drh
|
6595 |
** INSERT statement until the size exceeds N bytes. |
|
4f76459…
|
drh
|
6596 |
** --null STRING Render SQL NULL values as the given string |
|
4f76459…
|
drh
|
6597 |
** --once Setting changes to the right are reverted after |
|
4f76459…
|
drh
|
6598 |
** the next SQL command. |
|
4f76459…
|
drh
|
6599 |
** --quote ARG Enable/disable quoting of text. ARG can be |
|
709b566…
|
drh
|
6600 |
** "off", "on", "sql", "relaxed", "csv", "html", |
|
709b566…
|
drh
|
6601 |
** "tcl", or "json". "off" means show the text as-is. |
|
45de97f…
|
drh
|
6602 |
** "on" is an alias for "sql". |
|
4f76459…
|
drh
|
6603 |
** --reset Changes all mode settings back to their default. |
|
4f76459…
|
drh
|
6604 |
** --rowsep STRING Use STRING as the row separator |
|
45de97f…
|
drh
|
6605 |
** --sw|--screenwidth N Declare the screen width of the output device |
|
4f76459…
|
drh
|
6606 |
** to be N characters. An attempt may be made to |
|
4f76459…
|
drh
|
6607 |
** wrap output text to fit within this limit. Zero |
|
4f76459…
|
drh
|
6608 |
** means "no limit". Or N can be "auto" to set the |
|
4f76459…
|
drh
|
6609 |
** width automatically. |
|
4f76459…
|
drh
|
6610 |
** --tablename NAME Set the name of the table for "insert" mode. |
|
4f76459…
|
drh
|
6611 |
** --tag NAME Save mode to the left as NAME. |
|
4f76459…
|
drh
|
6612 |
** --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON. |
|
45de97f…
|
drh
|
6613 |
** --title ARG Whether or not to show column headers, and if so |
|
4f76459…
|
drh
|
6614 |
** how to encode them. ARG can be "off", "on", |
|
4f76459…
|
drh
|
6615 |
** "sql", "csv", "html", "tcl", or "json". |
|
ae7e3f0…
|
drh
|
6616 |
** --titlelimit N Limit the length of column titles to N characters. |
|
4f76459…
|
drh
|
6617 |
** -v|--verbose Verbose output |
|
4f76459…
|
drh
|
6618 |
** --widths LIST Set the columns widths for columnar modes. The |
|
4f76459…
|
drh
|
6619 |
** argument is a list of integers, one for each |
|
4f76459…
|
drh
|
6620 |
** column. A "0" width means use a dynamic width |
|
4f76459…
|
drh
|
6621 |
** based on the actual width of data. If there are |
|
4f76459…
|
drh
|
6622 |
** fewer entries in LIST than columns, "0" is used |
|
4f76459…
|
drh
|
6623 |
** for the unspecified widths. |
|
4f76459…
|
drh
|
6624 |
** --wordwrap BOOLEAN Enable/disable word wrapping |
|
4f76459…
|
drh
|
6625 |
** --wrap N Wrap columns wider than N characters |
|
4f76459…
|
drh
|
6626 |
** --ww Shorthand for "--wordwrap on" |
|
4f76459…
|
drh
|
6627 |
*/ |
|
4f76459…
|
drh
|
6628 |
static int dotCmdMode(ShellState *p){ |
|
4f76459…
|
drh
|
6629 |
int nArg = p->dot.nArg; /* Number of arguments */ |
|
4f76459…
|
drh
|
6630 |
char **azArg = p->dot.azArg;/* Argument list */ |
|
4f76459…
|
drh
|
6631 |
int eMode = -1; /* New mode value, or -1 for none */ |
|
4f76459…
|
drh
|
6632 |
int iMode = -1; /* Index of the argument that is the mode name */ |
|
4f76459…
|
drh
|
6633 |
int i; /* Loop counter */ |
|
4f76459…
|
drh
|
6634 |
int k; /* Misc index variable */ |
|
4f76459…
|
drh
|
6635 |
int chng = 0; /* True if anything has changed */ |
|
4f76459…
|
drh
|
6636 |
int bAll = 0; /* Show all details of the mode */ |
|
4f76459…
|
drh
|
6637 |
|
|
4f76459…
|
drh
|
6638 |
for(i=1; i<nArg; i++){ |
|
4f76459…
|
drh
|
6639 |
const char *z = azArg[i]; |
|
4f76459…
|
drh
|
6640 |
if( z[0]=='-' && z[1]=='-' ) z++; |
|
4f76459…
|
drh
|
6641 |
if( z[0]!='-' |
|
4f76459…
|
drh
|
6642 |
&& iMode<0 |
|
4f76459…
|
drh
|
6643 |
&& (eMode = modeFind(p, azArg[i]))>=0 |
|
4f76459…
|
drh
|
6644 |
&& eMode!=MODE_Www |
|
4f76459…
|
drh
|
6645 |
){ |
|
4f76459…
|
drh
|
6646 |
iMode = i; |
|
4f76459…
|
drh
|
6647 |
modeChange(p, eMode); |
|
4f76459…
|
drh
|
6648 |
/* (Legacy) If the mode is MODE_Insert and the next argument |
|
4f76459…
|
drh
|
6649 |
** is not an option, then the next argument must be the table |
|
4f76459…
|
drh
|
6650 |
** name. |
|
4f76459…
|
drh
|
6651 |
*/ |
|
4f76459…
|
drh
|
6652 |
if( i+1<nArg && azArg[i+1][0]!='-' ){ |
|
4f76459…
|
drh
|
6653 |
i++; |
|
4f76459…
|
drh
|
6654 |
modeSetStr(&p->mode.spec.zTableName, azArg[i]); |
|
4f76459…
|
drh
|
6655 |
} |
|
4f76459…
|
drh
|
6656 |
chng = 1; |
|
4f76459…
|
drh
|
6657 |
}else if( optionMatch(z,"align") ){ |
|
4f76459…
|
drh
|
6658 |
char *zAlign; |
|
4f76459…
|
drh
|
6659 |
int nAlign; |
|
4f76459…
|
drh
|
6660 |
int nErr = 0; |
|
4f76459…
|
drh
|
6661 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6662 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6663 |
return 1; |
|
4f76459…
|
drh
|
6664 |
} |
|
4f76459…
|
drh
|
6665 |
i++; |
|
4f76459…
|
drh
|
6666 |
zAlign = azArg[i]; |
|
4f76459…
|
drh
|
6667 |
nAlign = 0x3fff & strlen(zAlign); |
|
4f76459…
|
drh
|
6668 |
free(p->mode.spec.aAlign); |
|
4f76459…
|
drh
|
6669 |
p->mode.spec.aAlign = malloc(nAlign); |
|
4f76459…
|
drh
|
6670 |
shell_check_oom(p->mode.spec.aAlign); |
|
4f76459…
|
drh
|
6671 |
for(k=0; k<nAlign; k++){ |
|
4f76459…
|
drh
|
6672 |
unsigned char c = 0; |
|
4f76459…
|
drh
|
6673 |
switch( zAlign[k] ){ |
|
4f76459…
|
drh
|
6674 |
case 'l': case 'L': c = QRF_ALIGN_Left; break; |
|
4f76459…
|
drh
|
6675 |
case 'c': case 'C': c = QRF_ALIGN_Center; break; |
|
4f76459…
|
drh
|
6676 |
case 'r': case 'R': c = QRF_ALIGN_Right; break; |
|
4f76459…
|
drh
|
6677 |
default: nErr++; break; |
|
4f76459…
|
drh
|
6678 |
} |
|
4f76459…
|
drh
|
6679 |
p->mode.spec.aAlign[k] = c; |
|
4f76459…
|
drh
|
6680 |
} |
|
4f76459…
|
drh
|
6681 |
p->mode.spec.nAlign = nAlign; |
|
4f76459…
|
drh
|
6682 |
chng = 1; |
|
4f76459…
|
drh
|
6683 |
if( nErr ){ |
|
4f76459…
|
drh
|
6684 |
dotCmdError(p, i, "bad alignment string", |
|
4f76459…
|
drh
|
6685 |
"Should contain only characters L, C, and R."); |
|
4f76459…
|
drh
|
6686 |
return 1; |
|
4f76459…
|
drh
|
6687 |
} |
|
45de97f…
|
drh
|
6688 |
}else if( pickStr(z,0,"-blob","-blob-quote","")>=0 ){ |
|
45de97f…
|
drh
|
6689 |
if( (++i)>=nArg ){ |
|
45de97f…
|
drh
|
6690 |
dotCmdError(p, i-1, "missing argument", 0); |
|
45de97f…
|
drh
|
6691 |
return 1; |
|
45de97f…
|
drh
|
6692 |
} |
|
45de97f…
|
drh
|
6693 |
k = pickStr(azArg[i], 0, |
|
45de97f…
|
drh
|
6694 |
"auto", "text", "sql", "hex", "tcl", "json", "size", ""); |
|
45de97f…
|
drh
|
6695 |
/* 0 1 2 3 4 5 6 |
|
45de97f…
|
drh
|
6696 |
** Must match QRF_BLOB_xxxx values. See also tag-20251124a */ |
|
45de97f…
|
drh
|
6697 |
if( k>=0 ){ |
|
45de97f…
|
drh
|
6698 |
p->mode.spec.eBlob = k & 0xff; |
|
45de97f…
|
drh
|
6699 |
} |
|
45de97f…
|
drh
|
6700 |
chng = 1; |
|
f4b3b59…
|
drh
|
6701 |
}else if( optionMatch(z,"border") ){ |
|
f4b3b59…
|
drh
|
6702 |
if( (++i)>=nArg ){ |
|
f4b3b59…
|
drh
|
6703 |
dotCmdError(p, i-1, "missing argument", 0); |
|
f4b3b59…
|
drh
|
6704 |
return 1; |
|
f4b3b59…
|
drh
|
6705 |
} |
|
f4b3b59…
|
drh
|
6706 |
k = pickStr(azArg[i], 0, "auto", "off", "on", ""); |
|
f4b3b59…
|
drh
|
6707 |
if( k>=0 ){ |
|
f4b3b59…
|
drh
|
6708 |
p->mode.spec.bBorder = k & 0x3; |
|
f4b3b59…
|
drh
|
6709 |
} |
|
f4b3b59…
|
drh
|
6710 |
chng = 1; |
|
17f9878…
|
drh
|
6711 |
}else if( 0<=(k=pickStr(z,0, |
|
17f9878…
|
drh
|
6712 |
"-charlimit","-linelimit","-titlelimit","-multiinsert","")) ){ |
|
17f9878…
|
drh
|
6713 |
int w; /* 0 1 2 3 */ |
|
4f76459…
|
drh
|
6714 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6715 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6716 |
return 1; |
|
4f76459…
|
drh
|
6717 |
} |
|
4f76459…
|
drh
|
6718 |
w = integerValue(azArg[++i]); |
|
ae7e3f0…
|
drh
|
6719 |
switch( k ){ |
|
17f9878…
|
drh
|
6720 |
case 0: p->mode.spec.nCharLimit = w; break; |
|
17f9878…
|
drh
|
6721 |
case 1: p->mode.spec.nLineLimit = w; break; |
|
17f9878…
|
drh
|
6722 |
case 2: p->mode.spec.nTitleLimit = w; break; |
|
17f9878…
|
drh
|
6723 |
default: p->mode.spec.nMultiInsert = w; break; |
|
4f76459…
|
drh
|
6724 |
} |
|
4f76459…
|
drh
|
6725 |
chng = 1; |
|
4f76459…
|
drh
|
6726 |
}else if( 0<=(k=pickStr(z,0,"-tablename","-rowsep","-colsep","-null","")) ){ |
|
4f76459…
|
drh
|
6727 |
/* 0 1 2 3 */ |
|
4f76459…
|
drh
|
6728 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6729 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6730 |
return 1; |
|
4f76459…
|
drh
|
6731 |
} |
|
4f76459…
|
drh
|
6732 |
i++; |
|
4f76459…
|
drh
|
6733 |
switch( k ){ |
|
4f76459…
|
drh
|
6734 |
case 0: modeSetStr(&p->mode.spec.zTableName, azArg[i]); break; |
|
4f76459…
|
drh
|
6735 |
case 1: modeSetStr(&p->mode.spec.zRowSep, azArg[i]); break; |
|
4f76459…
|
drh
|
6736 |
case 2: modeSetStr(&p->mode.spec.zColumnSep, azArg[i]); break; |
|
4f76459…
|
drh
|
6737 |
case 3: modeSetStr(&p->mode.spec.zNull, azArg[i]); break; |
|
4f76459…
|
drh
|
6738 |
} |
|
4f76459…
|
drh
|
6739 |
chng = 1; |
|
4f76459…
|
drh
|
6740 |
}else if( optionMatch(z,"escape") ){ |
|
4f76459…
|
drh
|
6741 |
/* See similar code at tag-20250224-1 */ |
|
4f76459…
|
drh
|
6742 |
char *zErr = 0; |
|
45de97f…
|
drh
|
6743 |
if( (++i)>=nArg ){ |
|
45de97f…
|
drh
|
6744 |
dotCmdError(p, i-1, "missing argument", 0); |
|
4f76459…
|
drh
|
6745 |
return 1; |
|
45de97f…
|
drh
|
6746 |
} /* 0 1 2 <-- One less than QRF_ESC_ */ |
|
4f76459…
|
drh
|
6747 |
k = pickStr(azArg[i],&zErr,"off","ascii","symbol",""); |
|
4f76459…
|
drh
|
6748 |
if( k<0 ){ |
|
4f76459…
|
drh
|
6749 |
dotCmdError(p, i, "unknown escape type", "%s", zErr); |
|
4f76459…
|
drh
|
6750 |
sqlite3_free(zErr); |
|
4f76459…
|
drh
|
6751 |
return 1; |
|
4f76459…
|
drh
|
6752 |
} |
|
4f76459…
|
drh
|
6753 |
p->mode.spec.eEsc = k+1; |
|
45de97f…
|
drh
|
6754 |
chng = 1; |
|
45de97f…
|
drh
|
6755 |
}else if( optionMatch(z,"limits") ){ |
|
45de97f…
|
drh
|
6756 |
if( (++i)>=nArg ){ |
|
45de97f…
|
drh
|
6757 |
dotCmdError(p, i-1, "missing argument", 0); |
|
45de97f…
|
drh
|
6758 |
return 1; |
|
45de97f…
|
drh
|
6759 |
} |
|
45de97f…
|
drh
|
6760 |
k = pickStr(azArg[i],0,"on","off",""); |
|
45de97f…
|
drh
|
6761 |
if( k==0 ){ |
|
ae7e3f0…
|
drh
|
6762 |
p->mode.spec.nLineLimit = DFLT_LINE_LIMIT; |
|
ae7e3f0…
|
drh
|
6763 |
p->mode.spec.nCharLimit = DFLT_CHAR_LIMIT; |
|
ae7e3f0…
|
drh
|
6764 |
p->mode.spec.nTitleLimit = DFLT_TITLE_LIMIT; |
|
45de97f…
|
drh
|
6765 |
}else if( k==1 ){ |
|
45de97f…
|
drh
|
6766 |
p->mode.spec.nLineLimit = 0; |
|
45de97f…
|
drh
|
6767 |
p->mode.spec.nCharLimit = 0; |
|
ae7e3f0…
|
drh
|
6768 |
p->mode.spec.nTitleLimit = 0; |
|
45de97f…
|
drh
|
6769 |
}else{ |
|
ae7e3f0…
|
drh
|
6770 |
int L, C, T = 0; |
|
ae7e3f0…
|
drh
|
6771 |
int nNum = sscanf(azArg[i], "%d,%d,%d", &L, &C, &T); |
|
ae7e3f0…
|
drh
|
6772 |
if( nNum<2 || L<0 || C<0 || T<0){ |
|
ae7e3f0…
|
drh
|
6773 |
dotCmdError(p, i, "bad argument", "Should be \"L,C,T\" where L, C" |
|
ae7e3f0…
|
drh
|
6774 |
" and T are unsigned integers"); |
|
45de97f…
|
drh
|
6775 |
return 1; |
|
45de97f…
|
drh
|
6776 |
} |
|
45de97f…
|
drh
|
6777 |
p->mode.spec.nLineLimit = L; |
|
45de97f…
|
drh
|
6778 |
p->mode.spec.nCharLimit = C; |
|
ae7e3f0…
|
drh
|
6779 |
if( nNum==3 ) p->mode.spec.nTitleLimit = T; |
|
45de97f…
|
drh
|
6780 |
} |
|
4f76459…
|
drh
|
6781 |
chng = 1; |
|
4f76459…
|
drh
|
6782 |
}else if( optionMatch(z,"list") ){ |
|
4f76459…
|
drh
|
6783 |
int ii; |
|
4f76459…
|
drh
|
6784 |
cli_puts("available modes:", p->out); |
|
4f76459…
|
drh
|
6785 |
for(ii=0; ii<ArraySize(aModeInfo); ii++){ |
|
4f76459…
|
drh
|
6786 |
if( ii==MODE_Www ) continue; |
|
4f76459…
|
drh
|
6787 |
cli_printf(p->out, " %s", aModeInfo[ii].zName); |
|
4f76459…
|
drh
|
6788 |
} |
|
4f76459…
|
drh
|
6789 |
for(ii=0; ii<p->nSavedModes; ii++){ |
|
4f76459…
|
drh
|
6790 |
cli_printf(p->out, " %s", p->aSavedModes[ii].zTag); |
|
4f76459…
|
drh
|
6791 |
} |
|
4f76459…
|
drh
|
6792 |
cli_puts(" batch tty\n", p->out); |
|
45de97f…
|
drh
|
6793 |
chng = 1; /* Not really a change, but we still want to suppress the |
|
45de97f…
|
drh
|
6794 |
** "current mode" output */ |
|
9aee493…
|
drh
|
6795 |
}else if( optionMatch(z,"once") ){ |
|
9aee493…
|
drh
|
6796 |
p->nPopMode = 0; |
|
9aee493…
|
drh
|
6797 |
modePush(p); |
|
9aee493…
|
drh
|
6798 |
p->nPopMode = 1; |
|
9aee493…
|
drh
|
6799 |
}else if( optionMatch(z,"noquote") ){ |
|
9aee493…
|
drh
|
6800 |
/* (undocumented legacy) --noquote always turns quoting off */ |
|
9aee493…
|
drh
|
6801 |
p->mode.spec.eText = QRF_TEXT_Plain; |
|
45de97f…
|
drh
|
6802 |
p->mode.spec.eBlob = QRF_BLOB_Auto; |
|
9aee493…
|
drh
|
6803 |
chng = 1; |
|
4f76459…
|
drh
|
6804 |
}else if( optionMatch(z,"quote") ){ |
|
4f76459…
|
drh
|
6805 |
if( i+1<nArg |
|
4f76459…
|
drh
|
6806 |
&& azArg[i+1][0]!='-' |
|
4f76459…
|
drh
|
6807 |
&& (iMode>0 || strcmp(azArg[i+1],"off")==0 || modeFind(p, azArg[i+1])<0) |
|
4f76459…
|
drh
|
6808 |
){ |
|
4f76459…
|
drh
|
6809 |
/* --quote is followed by an argument other that is not an option |
|
4f76459…
|
drh
|
6810 |
** or a mode name. See it must be a boolean or a keyword to describe |
|
4f76459…
|
drh
|
6811 |
** how to set quoting. */ |
|
4f76459…
|
drh
|
6812 |
i++; |
|
4f76459…
|
drh
|
6813 |
if( (k = pickStr(azArg[i],0,"no","yes","0","1",""))>=0 ){ |
|
4f76459…
|
drh
|
6814 |
k &= 1; /* 0 for "off". 1 for "on". */ |
|
4f76459…
|
drh
|
6815 |
}else{ |
|
709b566…
|
drh
|
6816 |
char *zErr = 0; |
|
709b566…
|
drh
|
6817 |
k = pickStr(azArg[i],&zErr, |
|
709b566…
|
drh
|
6818 |
"off","on","sql","csv","html","tcl","json","relaxed",""); |
|
709b566…
|
drh
|
6819 |
/* 0 1 2 3 4 5 6 7 */ |
|
4f76459…
|
drh
|
6820 |
if( k<0 ){ |
|
4f76459…
|
drh
|
6821 |
dotCmdError(p, i, "unknown", "%z", zErr); |
|
4f76459…
|
drh
|
6822 |
return 1; |
|
4f76459…
|
drh
|
6823 |
} |
|
4f76459…
|
drh
|
6824 |
} |
|
4f76459…
|
drh
|
6825 |
}else{ |
|
4f76459…
|
drh
|
6826 |
/* (Legacy) no following boolean argument. Turn quoting on */ |
|
4f76459…
|
drh
|
6827 |
k = 1; |
|
4f76459…
|
drh
|
6828 |
} |
|
4f76459…
|
drh
|
6829 |
switch( k ){ |
|
4f76459…
|
drh
|
6830 |
case 1: /* on */ |
|
45de97f…
|
drh
|
6831 |
modeSetStr(&p->mode.spec.zNull, "NULL"); |
|
45de97f…
|
drh
|
6832 |
/* Fall through */ |
|
4f76459…
|
drh
|
6833 |
case 2: /* sql */ |
|
4f76459…
|
drh
|
6834 |
p->mode.spec.eText = QRF_TEXT_Sql; |
|
4f76459…
|
drh
|
6835 |
break; |
|
4f76459…
|
drh
|
6836 |
case 3: /* csv */ |
|
4f76459…
|
drh
|
6837 |
p->mode.spec.eText = QRF_TEXT_Csv; |
|
4f76459…
|
drh
|
6838 |
break; |
|
4f76459…
|
drh
|
6839 |
case 4: /* html */ |
|
4f76459…
|
drh
|
6840 |
p->mode.spec.eText = QRF_TEXT_Html; |
|
4f76459…
|
drh
|
6841 |
break; |
|
4f76459…
|
drh
|
6842 |
case 5: /* tcl */ |
|
4f76459…
|
drh
|
6843 |
p->mode.spec.eText = QRF_TEXT_Tcl; |
|
4f76459…
|
drh
|
6844 |
break; |
|
4f76459…
|
drh
|
6845 |
case 6: /* json */ |
|
4f76459…
|
drh
|
6846 |
p->mode.spec.eText = QRF_TEXT_Json; |
|
709b566…
|
drh
|
6847 |
break; |
|
709b566…
|
drh
|
6848 |
case 7: /* relaxed */ |
|
709b566…
|
drh
|
6849 |
p->mode.spec.eText = QRF_TEXT_Relaxed; |
|
9aee493…
|
drh
|
6850 |
break; |
|
45de97f…
|
drh
|
6851 |
default: /* off */ |
|
9aee493…
|
drh
|
6852 |
p->mode.spec.eText = QRF_TEXT_Plain; |
|
9aee493…
|
drh
|
6853 |
break; |
|
9aee493…
|
drh
|
6854 |
} |
|
4f76459…
|
drh
|
6855 |
chng = 1; |
|
4f76459…
|
drh
|
6856 |
}else if( optionMatch(z,"reset") ){ |
|
4f76459…
|
drh
|
6857 |
int saved_eMode = p->mode.eMode; |
|
4f76459…
|
drh
|
6858 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
6859 |
modeChange(p, saved_eMode); |
|
45de97f…
|
drh
|
6860 |
}else if( optionMatch(z,"screenwidth") || optionMatch(z,"sw") ){ |
|
45de97f…
|
drh
|
6861 |
if( (++i)>=nArg ){ |
|
45de97f…
|
drh
|
6862 |
dotCmdError(p, i-1, "missing argument", 0); |
|
4f76459…
|
drh
|
6863 |
return 1; |
|
4f76459…
|
drh
|
6864 |
} |
|
45de97f…
|
drh
|
6865 |
k = pickStr(azArg[i],0,"off","auto",""); |
|
4f76459…
|
drh
|
6866 |
if( k==0 ){ |
|
4f76459…
|
drh
|
6867 |
p->mode.bAutoScreenWidth = 0; |
|
4f76459…
|
drh
|
6868 |
p->mode.spec.nScreenWidth = 0; |
|
4f76459…
|
drh
|
6869 |
}else if( k==1 ){ |
|
4f76459…
|
drh
|
6870 |
p->mode.bAutoScreenWidth = 1; |
|
4f76459…
|
drh
|
6871 |
}else{ |
|
45de97f…
|
drh
|
6872 |
i64 w = integerValue(azArg[i]); |
|
4f76459…
|
drh
|
6873 |
p->mode.bAutoScreenWidth = 0; |
|
4f76459…
|
drh
|
6874 |
if( w<0 ) w = 0; |
|
4f76459…
|
drh
|
6875 |
if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
6876 |
p->mode.spec.nScreenWidth = w; |
|
4f76459…
|
drh
|
6877 |
} |
|
4f76459…
|
drh
|
6878 |
chng = 1; |
|
4f76459…
|
drh
|
6879 |
}else if( optionMatch(z,"tag") ){ |
|
4f76459…
|
drh
|
6880 |
size_t nByte; |
|
4f76459…
|
drh
|
6881 |
int n; |
|
4f76459…
|
drh
|
6882 |
const char *zTag; |
|
4f76459…
|
drh
|
6883 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6884 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6885 |
return 1; |
|
4f76459…
|
drh
|
6886 |
} |
|
4f76459…
|
drh
|
6887 |
zTag = azArg[++i]; |
|
4f76459…
|
drh
|
6888 |
if( modeFind(p, zTag)>=0 ){ |
|
4f76459…
|
drh
|
6889 |
dotCmdError(p, i, "mode already exists", 0); |
|
4f76459…
|
drh
|
6890 |
return 1; |
|
4f76459…
|
drh
|
6891 |
} |
|
4f76459…
|
drh
|
6892 |
if( p->nSavedModes > MODE_N_USER ){ |
|
4f76459…
|
drh
|
6893 |
dotCmdError(p, i-1, "cannot add more modes", 0); |
|
4f76459…
|
drh
|
6894 |
return 1; |
|
4f76459…
|
drh
|
6895 |
} |
|
4f76459…
|
drh
|
6896 |
n = p->nSavedModes++; |
|
4f76459…
|
drh
|
6897 |
nByte = sizeof(p->aSavedModes[0]); |
|
4f76459…
|
drh
|
6898 |
nByte *= n+1; |
|
4f76459…
|
drh
|
6899 |
p->aSavedModes = realloc( p->aSavedModes, nByte ); |
|
4f76459…
|
drh
|
6900 |
shell_check_oom(p->aSavedModes); |
|
4f76459…
|
drh
|
6901 |
p->aSavedModes[n].zTag = strdup(zTag); |
|
4f76459…
|
drh
|
6902 |
shell_check_oom(p->aSavedModes[n].zTag); |
|
4f76459…
|
drh
|
6903 |
modeDup(&p->aSavedModes[n].mode, &p->mode); |
|
4f76459…
|
drh
|
6904 |
chng = 1; |
|
4f76459…
|
drh
|
6905 |
}else if( optionMatch(z,"textjsonb") ){ |
|
4f76459…
|
drh
|
6906 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6907 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6908 |
return 1; |
|
4f76459…
|
drh
|
6909 |
} |
|
4f76459…
|
drh
|
6910 |
p->mode.spec.bTextJsonb = booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
|
4f76459…
|
drh
|
6911 |
chng = 1; |
|
4f76459…
|
drh
|
6912 |
}else if( optionMatch(z,"titles") || optionMatch(z,"title") ){ |
|
4f76459…
|
drh
|
6913 |
char *zErr = 0; |
|
4f76459…
|
drh
|
6914 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6915 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6916 |
return 1; |
|
4f76459…
|
drh
|
6917 |
} |
|
4f76459…
|
drh
|
6918 |
k = pickStr(azArg[++i],&zErr, |
|
4f76459…
|
drh
|
6919 |
"off","on","plain","sql","csv","html","tcl","json",""); |
|
4f76459…
|
drh
|
6920 |
/* 0 1 2 3 4 5 6 7 */ |
|
4f76459…
|
drh
|
6921 |
if( k<0 ){ |
|
4f76459…
|
drh
|
6922 |
dotCmdError(p, i, "bad --titles value","%z", zErr); |
|
4f76459…
|
drh
|
6923 |
return 1; |
|
4f76459…
|
drh
|
6924 |
} |
|
4f76459…
|
drh
|
6925 |
p->mode.spec.bTitles = k>=1 ? QRF_Yes : QRF_No; |
|
12e4a0f…
|
drh
|
6926 |
p->mode.mFlags &= ~MFLG_HDR; |
|
4f76459…
|
drh
|
6927 |
p->mode.spec.eTitle = k>1 ? k-1 : aModeInfo[p->mode.eMode].eHdr; |
|
4f76459…
|
drh
|
6928 |
chng = 1; |
|
4f76459…
|
drh
|
6929 |
}else if( optionMatch(z,"widths") || optionMatch(z,"width") ){ |
|
4f76459…
|
drh
|
6930 |
int nWidth = 0; |
|
4f76459…
|
drh
|
6931 |
short int *aWidth; |
|
4f76459…
|
drh
|
6932 |
const char *zW; |
|
4f76459…
|
drh
|
6933 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6934 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6935 |
return 1; |
|
4f76459…
|
drh
|
6936 |
} |
|
4f76459…
|
drh
|
6937 |
zW = azArg[++i]; |
|
4f76459…
|
drh
|
6938 |
/* Every width value takes at least 2 bytes in the input string to |
|
4f76459…
|
drh
|
6939 |
** specify, so strlen(zW) bytes should be plenty of space to hold the |
|
4f76459…
|
drh
|
6940 |
** result. */ |
|
4f76459…
|
drh
|
6941 |
aWidth = malloc( strlen(zW) ); |
|
f4b3b59…
|
drh
|
6942 |
while( IsSpace(zW[0]) ) zW++; |
|
4f76459…
|
drh
|
6943 |
while( zW[0] ){ |
|
4f76459…
|
drh
|
6944 |
int w = 0; |
|
4f76459…
|
drh
|
6945 |
int nDigit = 0; |
|
f4b3b59…
|
drh
|
6946 |
k = zW[0]=='-' && IsDigit(zW[1]); |
|
f4b3b59…
|
drh
|
6947 |
while( IsDigit(zW[k]) ){ |
|
4f76459…
|
drh
|
6948 |
w = w*10 + zW[k] - '0'; |
|
4f76459…
|
drh
|
6949 |
if( w>QRF_MAX_WIDTH ){ |
|
4f76459…
|
drh
|
6950 |
dotCmdError(p,i+1,"width too big", |
|
4f76459…
|
drh
|
6951 |
"Maximum column width is %d", QRF_MAX_WIDTH); |
|
4f76459…
|
drh
|
6952 |
free(aWidth); |
|
4f76459…
|
drh
|
6953 |
return 1; |
|
4f76459…
|
drh
|
6954 |
} |
|
4f76459…
|
drh
|
6955 |
nDigit++; |
|
4f76459…
|
drh
|
6956 |
k++; |
|
4f76459…
|
drh
|
6957 |
} |
|
4f76459…
|
drh
|
6958 |
if( nDigit==0 ){ |
|
4f76459…
|
drh
|
6959 |
dotCmdError(p,i+1,"syntax error", |
|
4f76459…
|
drh
|
6960 |
"should be a comma-separated list if integers"); |
|
4f76459…
|
drh
|
6961 |
free(aWidth); |
|
4f76459…
|
drh
|
6962 |
return 1; |
|
4f76459…
|
drh
|
6963 |
} |
|
4f76459…
|
drh
|
6964 |
if( zW[0]=='-' ) w = -w; |
|
4f76459…
|
drh
|
6965 |
aWidth[nWidth++] = w; |
|
4f76459…
|
drh
|
6966 |
zW += k; |
|
4f76459…
|
drh
|
6967 |
if( zW[0]==',' ) zW++; |
|
f4b3b59…
|
drh
|
6968 |
while( IsSpace(zW[0]) ) zW++; |
|
4f76459…
|
drh
|
6969 |
} |
|
4f76459…
|
drh
|
6970 |
free(p->mode.spec.aWidth); |
|
4f76459…
|
drh
|
6971 |
p->mode.spec.aWidth = aWidth; |
|
4f76459…
|
drh
|
6972 |
p->mode.spec.nWidth = nWidth; |
|
4f76459…
|
drh
|
6973 |
chng = 1; |
|
4f76459…
|
drh
|
6974 |
}else if( optionMatch(z,"wrap") ){ |
|
4f76459…
|
drh
|
6975 |
int w; |
|
4f76459…
|
drh
|
6976 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6977 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6978 |
return 1; |
|
4f76459…
|
drh
|
6979 |
} |
|
4f76459…
|
drh
|
6980 |
w = integerValue(azArg[++i]); |
|
4f76459…
|
drh
|
6981 |
if( w<(-QRF_MAX_WIDTH) ) w = -QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
6982 |
if( w>QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
6983 |
p->mode.spec.nWrap = w; |
|
4f76459…
|
drh
|
6984 |
chng = 1; |
|
4f76459…
|
drh
|
6985 |
}else if( optionMatch(z,"ww") ){ |
|
4f76459…
|
drh
|
6986 |
p->mode.spec.bWordWrap = QRF_Yes; |
|
4f76459…
|
drh
|
6987 |
chng = 1; |
|
4f76459…
|
drh
|
6988 |
}else if( optionMatch(z,"wordwrap") ){ |
|
4f76459…
|
drh
|
6989 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
6990 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
6991 |
return 1; |
|
4f76459…
|
drh
|
6992 |
} |
|
4f76459…
|
drh
|
6993 |
p->mode.spec.bWordWrap = (u8)booleanValue(azArg[++i]) ? QRF_Yes : QRF_No; |
|
4f76459…
|
drh
|
6994 |
chng = 1; |
|
4f76459…
|
drh
|
6995 |
}else if( optionMatch(z,"v") || optionMatch(z,"verbose") ){ |
|
4f76459…
|
drh
|
6996 |
bAll = 1; |
|
4f76459…
|
drh
|
6997 |
}else if( z[0]=='-' ){ |
|
4f76459…
|
drh
|
6998 |
dotCmdError(p, i, "bad option", "Use \".help .mode\" for more info"); |
|
4f76459…
|
drh
|
6999 |
return 1; |
|
4f76459…
|
drh
|
7000 |
}else{ |
|
4f76459…
|
drh
|
7001 |
dotCmdError(p, i, iMode>0?"bad argument":"unknown mode", |
|
4f76459…
|
drh
|
7002 |
"Use \".help .mode\" for more info"); |
|
4f76459…
|
drh
|
7003 |
return 1; |
|
4f76459…
|
drh
|
7004 |
} |
|
4f76459…
|
drh
|
7005 |
} |
|
4f76459…
|
drh
|
7006 |
if( !chng || bAll ){ |
|
4f76459…
|
drh
|
7007 |
const ModeInfo *pI = aModeInfo + p->mode.eMode; |
|
4f76459…
|
drh
|
7008 |
sqlite3_str *pDesc = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
7009 |
char *zDesc; |
|
4f76459…
|
drh
|
7010 |
const char *zSetting; |
|
4f76459…
|
drh
|
7011 |
|
|
4f76459…
|
drh
|
7012 |
if( p->nPopMode ) sqlite3_str_appendall(pDesc, "--once "); |
|
4f76459…
|
drh
|
7013 |
sqlite3_str_appendall(pDesc,pI->zName); |
|
4f76459…
|
drh
|
7014 |
if( bAll || (p->mode.spec.nAlign && pI->eCx==2) ){ |
|
4f76459…
|
drh
|
7015 |
int ii; |
|
4f76459…
|
drh
|
7016 |
sqlite3_str_appendall(pDesc, " --align \""); |
|
4f76459…
|
drh
|
7017 |
for(ii=0; ii<p->mode.spec.nAlign; ii++){ |
|
4f76459…
|
drh
|
7018 |
unsigned char a = p->mode.spec.aAlign[ii]; |
|
4f76459…
|
drh
|
7019 |
sqlite3_str_appendchar(pDesc, 1, "LLCR"[a&3]); |
|
4f76459…
|
drh
|
7020 |
} |
|
4f76459…
|
drh
|
7021 |
sqlite3_str_append(pDesc, "\"", 1); |
|
4f76459…
|
drh
|
7022 |
} |
|
f4b3b59…
|
drh
|
7023 |
if( bAll |
|
f4b3b59…
|
drh
|
7024 |
|| (p->mode.spec.bBorder==QRF_No) != ((pI->mFlg&1)!=0) |
|
f4b3b59…
|
drh
|
7025 |
){ |
|
f4b3b59…
|
drh
|
7026 |
sqlite3_str_appendf(pDesc," --border %s", |
|
f4b3b59…
|
drh
|
7027 |
p->mode.spec.bBorder==QRF_No ? "off" : "on"); |
|
f4b3b59…
|
drh
|
7028 |
} |
|
45de97f…
|
drh
|
7029 |
if( bAll || p->mode.spec.eBlob!=QRF_BLOB_Auto ){ |
|
45de97f…
|
drh
|
7030 |
const char *azBQuote[] = |
|
45de97f…
|
drh
|
7031 |
{ "auto", "text", "sql", "hex", "tcl", "json", "size" }; |
|
45de97f…
|
drh
|
7032 |
/* 0 1 2 3 4 5 6 |
|
45de97f…
|
drh
|
7033 |
** Must match QRF_BLOB_xxxx values. See all instances of tag-20251124a */ |
|
45de97f…
|
drh
|
7034 |
u8 e = p->mode.spec.eBlob; |
|
45de97f…
|
drh
|
7035 |
sqlite3_str_appendf(pDesc, " --blob-quote %s", azBQuote[e]); |
|
4f76459…
|
drh
|
7036 |
} |
|
4f76459…
|
drh
|
7037 |
zSetting = aModeStr[pI->eCSep]; |
|
4f76459…
|
drh
|
7038 |
if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zColumnSep)!=0) ){ |
|
4f76459…
|
drh
|
7039 |
sqlite3_str_appendf(pDesc, " --colsep "); |
|
4f76459…
|
drh
|
7040 |
append_c_string(pDesc, p->mode.spec.zColumnSep); |
|
4f76459…
|
drh
|
7041 |
} |
|
4f76459…
|
drh
|
7042 |
if( bAll || p->mode.spec.eEsc!=QRF_Auto ){ |
|
4f76459…
|
drh
|
7043 |
sqlite3_str_appendf(pDesc, " --escape %s",qrfEscNames[p->mode.spec.eEsc]); |
|
4f76459…
|
drh
|
7044 |
} |
|
ae7e3f0…
|
drh
|
7045 |
if( bAll |
|
ae7e3f0…
|
drh
|
7046 |
|| (p->mode.spec.nLineLimit>0 && pI->eCx>0) |
|
ae7e3f0…
|
drh
|
7047 |
|| p->mode.spec.nCharLimit>0 |
|
ae7e3f0…
|
drh
|
7048 |
|| (p->mode.spec.nTitleLimit>0 && pI->eCx>0) |
|
ae7e3f0…
|
drh
|
7049 |
){ |
|
ae7e3f0…
|
drh
|
7050 |
if( p->mode.spec.nLineLimit==0 |
|
ae7e3f0…
|
drh
|
7051 |
&& p->mode.spec.nCharLimit==0 |
|
ae7e3f0…
|
drh
|
7052 |
&& p->mode.spec.nTitleLimit==0 |
|
ae7e3f0…
|
drh
|
7053 |
){ |
|
45de97f…
|
drh
|
7054 |
sqlite3_str_appendf(pDesc, " --limits off"); |
|
ae7e3f0…
|
drh
|
7055 |
}else if( p->mode.spec.nLineLimit==DFLT_LINE_LIMIT |
|
ae7e3f0…
|
drh
|
7056 |
&& p->mode.spec.nCharLimit==DFLT_CHAR_LIMIT |
|
ae7e3f0…
|
drh
|
7057 |
&& p->mode.spec.nTitleLimit==DFLT_TITLE_LIMIT |
|
ae7e3f0…
|
drh
|
7058 |
){ |
|
ae7e3f0…
|
drh
|
7059 |
sqlite3_str_appendf(pDesc, " --limits on"); |
|
45de97f…
|
drh
|
7060 |
}else{ |
|
ae7e3f0…
|
drh
|
7061 |
sqlite3_str_appendf(pDesc, " --limits %d,%d,%d", |
|
ae7e3f0…
|
drh
|
7062 |
p->mode.spec.nLineLimit, p->mode.spec.nCharLimit, |
|
ae7e3f0…
|
drh
|
7063 |
p->mode.spec.nTitleLimit); |
|
45de97f…
|
drh
|
7064 |
} |
|
17f9878…
|
drh
|
7065 |
} |
|
17f9878…
|
drh
|
7066 |
if( bAll |
|
17f9878…
|
drh
|
7067 |
|| (p->mode.spec.nMultiInsert && p->mode.spec.eStyle==QRF_STYLE_Insert) |
|
17f9878…
|
drh
|
7068 |
){ |
|
17f9878…
|
drh
|
7069 |
sqlite3_str_appendf(pDesc, " --multiinsert %u", |
|
17f9878…
|
drh
|
7070 |
p->mode.spec.nMultiInsert); |
|
5f65ed5…
|
drh
|
7071 |
} |
|
4f76459…
|
drh
|
7072 |
zSetting = aModeStr[pI->eNull]; |
|
4f76459…
|
drh
|
7073 |
if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zNull)!=0) ){ |
|
4f76459…
|
drh
|
7074 |
sqlite3_str_appendf(pDesc, " --null "); |
|
4f76459…
|
drh
|
7075 |
append_c_string(pDesc, p->mode.spec.zNull); |
|
4f76459…
|
drh
|
7076 |
} |
|
4f76459…
|
drh
|
7077 |
if( bAll |
|
4f76459…
|
drh
|
7078 |
|| (pI->eText!=p->mode.spec.eText && (pI->eText>1 || p->mode.spec.eText>1)) |
|
4f76459…
|
drh
|
7079 |
){ |
|
4f76459…
|
drh
|
7080 |
sqlite3_str_appendf(pDesc," --quote %s",qrfQuoteNames[p->mode.spec.eText]); |
|
4f76459…
|
drh
|
7081 |
} |
|
4f76459…
|
drh
|
7082 |
zSetting = aModeStr[pI->eRSep]; |
|
4f76459…
|
drh
|
7083 |
if( bAll || (zSetting && cli_strcmp(zSetting,p->mode.spec.zRowSep)!=0) ){ |
|
4f76459…
|
drh
|
7084 |
sqlite3_str_appendf(pDesc, " --rowsep "); |
|
4f76459…
|
drh
|
7085 |
append_c_string(pDesc, p->mode.spec.zRowSep); |
|
4f76459…
|
drh
|
7086 |
} |
|
4f76459…
|
drh
|
7087 |
if( bAll |
|
4f76459…
|
drh
|
7088 |
|| (pI->eCx && (p->mode.spec.nScreenWidth>0 || p->mode.bAutoScreenWidth)) |
|
4f76459…
|
drh
|
7089 |
){ |
|
4f76459…
|
drh
|
7090 |
if( p->mode.bAutoScreenWidth ){ |
|
ae7e3f0…
|
drh
|
7091 |
sqlite3_str_appendall(pDesc, " --sw auto"); |
|
4f76459…
|
drh
|
7092 |
}else{ |
|
ae7e3f0…
|
drh
|
7093 |
sqlite3_str_appendf(pDesc," --sw %d", |
|
4f76459…
|
drh
|
7094 |
p->mode.spec.nScreenWidth); |
|
4f76459…
|
drh
|
7095 |
} |
|
4f76459…
|
drh
|
7096 |
} |
|
4f76459…
|
drh
|
7097 |
if( bAll || p->mode.eMode==MODE_Insert ){ |
|
4f76459…
|
drh
|
7098 |
sqlite3_str_appendf(pDesc," --tablename "); |
|
4f76459…
|
drh
|
7099 |
append_c_string(pDesc, p->mode.spec.zTableName); |
|
4f76459…
|
drh
|
7100 |
} |
|
4f76459…
|
drh
|
7101 |
if( bAll || p->mode.spec.bTextJsonb ){ |
|
4f76459…
|
drh
|
7102 |
sqlite3_str_appendf(pDesc," --textjsonb %s", |
|
4f76459…
|
drh
|
7103 |
p->mode.spec.bTextJsonb==QRF_Yes ? "on" : "off"); |
|
4f76459…
|
drh
|
7104 |
} |
|
4f76459…
|
drh
|
7105 |
k = modeTitleDsply(p, bAll); |
|
4f76459…
|
drh
|
7106 |
if( k==1 ){ |
|
4f76459…
|
drh
|
7107 |
sqlite3_str_appendall(pDesc, " --titles off"); |
|
4f76459…
|
drh
|
7108 |
}else if( k==2 ){ |
|
4f76459…
|
drh
|
7109 |
sqlite3_str_appendall(pDesc, " --titles on"); |
|
4f76459…
|
drh
|
7110 |
}else if( k==3 ){ |
|
4f76459…
|
drh
|
7111 |
static const char *azTitle[] = |
|
4f76459…
|
drh
|
7112 |
{ "plain", "sql", "csv", "html", "tcl", "json"}; |
|
4f76459…
|
drh
|
7113 |
sqlite3_str_appendf(pDesc, " --titles %s", |
|
4f76459…
|
drh
|
7114 |
azTitle[p->mode.spec.eTitle-1]); |
|
4f76459…
|
drh
|
7115 |
} |
|
4f76459…
|
drh
|
7116 |
if( p->mode.spec.nWidth>0 && (bAll || pI->eCx==2) ){ |
|
4f76459…
|
drh
|
7117 |
int ii; |
|
4f76459…
|
drh
|
7118 |
const char *zSep = " --widths "; |
|
4f76459…
|
drh
|
7119 |
for(ii=0; ii<p->mode.spec.nWidth; ii++){ |
|
4f76459…
|
drh
|
7120 |
sqlite3_str_appendf(pDesc, "%s%d", zSep, (int)p->mode.spec.aWidth[ii]); |
|
4f76459…
|
drh
|
7121 |
zSep = ","; |
|
4f76459…
|
drh
|
7122 |
} |
|
4f76459…
|
drh
|
7123 |
}else if( bAll ){ |
|
4f76459…
|
drh
|
7124 |
sqlite3_str_appendall(pDesc, " --widths \"\""); |
|
4f76459…
|
drh
|
7125 |
} |
|
4f76459…
|
drh
|
7126 |
if( bAll || (pI->eCx>0 && p->mode.spec.bWordWrap) ){ |
|
4f76459…
|
drh
|
7127 |
if( bAll ){ |
|
4f76459…
|
drh
|
7128 |
sqlite3_str_appendf(pDesc, " --wordwrap %s", |
|
4f76459…
|
drh
|
7129 |
p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off"); |
|
4f76459…
|
drh
|
7130 |
} |
|
4f76459…
|
drh
|
7131 |
if( p->mode.spec.nWrap ){ |
|
4f76459…
|
drh
|
7132 |
sqlite3_str_appendf(pDesc, " --wrap %d", p->mode.spec.nWrap); |
|
4f76459…
|
drh
|
7133 |
} |
|
4f76459…
|
drh
|
7134 |
if( !bAll ) sqlite3_str_append(pDesc, " --ww", 5); |
|
4f76459…
|
drh
|
7135 |
} |
|
4f76459…
|
drh
|
7136 |
zDesc = sqlite3_str_finish(pDesc); |
|
ae7e3f0…
|
drh
|
7137 |
cli_printf(p->out, ".mode %s\n", zDesc); |
|
4f76459…
|
drh
|
7138 |
fflush(p->out); |
|
4f76459…
|
drh
|
7139 |
sqlite3_free(zDesc); |
|
4f76459…
|
drh
|
7140 |
} |
|
4f76459…
|
drh
|
7141 |
return 0; |
|
4f76459…
|
drh
|
7142 |
} |
|
4f76459…
|
drh
|
7143 |
|
|
4f76459…
|
drh
|
7144 |
/* |
|
4f76459…
|
drh
|
7145 |
** DOT-COMMAND: .output |
|
4f76459…
|
drh
|
7146 |
** USAGE: .output [OPTIONS] [FILE] |
|
4f76459…
|
drh
|
7147 |
** |
|
4f76459…
|
drh
|
7148 |
** Begin redirecting output to FILE. Or if FILE is omitted, revert |
|
4f76459…
|
drh
|
7149 |
** to sending output to the console. If FILE begins with "|" then |
|
4f76459…
|
drh
|
7150 |
** the remainder of file is taken as a pipe and output is directed |
|
4f76459…
|
drh
|
7151 |
** into that pipe. If FILE is "memory" then output is captured in an |
|
4f76459…
|
drh
|
7152 |
** internal memory buffer. If FILE is "off" then output is redirected |
|
4f76459…
|
drh
|
7153 |
** into /dev/null or the equivalent. |
|
4f76459…
|
drh
|
7154 |
** |
|
4f76459…
|
drh
|
7155 |
** Options: |
|
4f76459…
|
drh
|
7156 |
** --bom Prepend a byte-order mark to the output |
|
4f76459…
|
drh
|
7157 |
** -e Accumulate output in a temporary text file then |
|
4f76459…
|
drh
|
7158 |
** launch a text editor when the redirection ends. |
|
4f76459…
|
drh
|
7159 |
** --error-prefix X Use X as the left-margin prefix for error messages. |
|
4f76459…
|
drh
|
7160 |
** Set to an empty string to restore the default. |
|
5f65ed5…
|
drh
|
7161 |
** --keep Keep redirecting output to its current destination. |
|
5f65ed5…
|
drh
|
7162 |
** Use this option in combination with --show or |
|
5f65ed5…
|
drh
|
7163 |
** with --error-prefix when you do not want to stop |
|
5f65ed5…
|
drh
|
7164 |
** a current redirection. |
|
4f76459…
|
drh
|
7165 |
** --plain Use plain text rather than HTML tables with -w |
|
5f65ed5…
|
drh
|
7166 |
** --show Show output text captured by .testcase or by |
|
5f65ed5…
|
drh
|
7167 |
** redirecting to "memory". |
|
4f76459…
|
drh
|
7168 |
** -w Show the output in a web browser. Output is |
|
4f76459…
|
drh
|
7169 |
** written into a temporary HTML file until the |
|
4f76459…
|
drh
|
7170 |
** redirect ends, then the web browser is launched. |
|
4f76459…
|
drh
|
7171 |
** Query results are shown as HTML tables, unless |
|
4f76459…
|
drh
|
7172 |
** the --plain is used too. |
|
4f76459…
|
drh
|
7173 |
** -x Show the output in a spreadsheet. Output is |
|
4f76459…
|
drh
|
7174 |
** written to a temp file as CSV then the spreadsheet |
|
4f76459…
|
drh
|
7175 |
** is launched when |
|
4f76459…
|
drh
|
7176 |
** |
|
4f76459…
|
drh
|
7177 |
** DOT-COMMAND: .once |
|
4f76459…
|
drh
|
7178 |
** USAGE: .once [OPTIONS] FILE ... |
|
4f76459…
|
drh
|
7179 |
** |
|
4f76459…
|
drh
|
7180 |
** Write the output for the next line of SQL or the next dot-command into |
|
4f76459…
|
drh
|
7181 |
** FILE. If FILE begins with "|" then it is a program into which output |
|
4f76459…
|
drh
|
7182 |
** is written. The FILE argument should be omitted if one of the -e, -w, |
|
4f76459…
|
drh
|
7183 |
** or -x options is used. |
|
4f76459…
|
drh
|
7184 |
** |
|
4f76459…
|
drh
|
7185 |
** Options: |
|
4f76459…
|
drh
|
7186 |
** -e Capture output into a temporary file then bring up |
|
4f76459…
|
drh
|
7187 |
** a text editor on that temporary file. |
|
4f76459…
|
drh
|
7188 |
** --plain Use plain text rather than HTML tables with -w |
|
4f76459…
|
drh
|
7189 |
** -w Capture output into an HTML file then bring up that |
|
4f76459…
|
drh
|
7190 |
** file in a web browser |
|
4f76459…
|
drh
|
7191 |
** -x Show the output in a spreadsheet. Output is |
|
4f76459…
|
drh
|
7192 |
** written to a temp file as CSV then the spreadsheet |
|
4f76459…
|
drh
|
7193 |
** is launched when |
|
4f76459…
|
drh
|
7194 |
** |
|
4f76459…
|
drh
|
7195 |
** DOT-COMMAND: .excel |
|
4f76459…
|
drh
|
7196 |
** Shorthand for ".once -x" |
|
4f76459…
|
drh
|
7197 |
** |
|
4f76459…
|
drh
|
7198 |
** DOT-COMMAND: .www [--plain] |
|
4f76459…
|
drh
|
7199 |
** Shorthand for ".once -w" or ".once --plain -w" |
|
4f76459…
|
drh
|
7200 |
*/ |
|
4f76459…
|
drh
|
7201 |
static int dotCmdOutput(ShellState *p){ |
|
4f76459…
|
drh
|
7202 |
int nArg = p->dot.nArg; /* Number of arguments */ |
|
4f76459…
|
drh
|
7203 |
char **azArg = p->dot.azArg; /* Text of the arguments */ |
|
4f76459…
|
drh
|
7204 |
char *zFile = 0; /* The FILE argument */ |
|
4f76459…
|
drh
|
7205 |
int i; /* Loop counter */ |
|
4f76459…
|
drh
|
7206 |
int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ |
|
4f76459…
|
drh
|
7207 |
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ |
|
4f76459…
|
drh
|
7208 |
int bPlain = 0; /* --plain option */ |
|
5f65ed5…
|
drh
|
7209 |
int bKeep = 0; /* Keep redirecting */ |
|
4f76459…
|
drh
|
7210 |
static const char *zBomUtf8 = "\357\273\277"; |
|
4f76459…
|
drh
|
7211 |
const char *zBom = 0; |
|
4f76459…
|
drh
|
7212 |
char c = azArg[0][0]; |
|
4f76459…
|
drh
|
7213 |
int n = strlen30(azArg[0]); |
|
4f76459…
|
drh
|
7214 |
|
|
4f76459…
|
drh
|
7215 |
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); |
|
4f76459…
|
drh
|
7216 |
if( c=='e' ){ |
|
4f76459…
|
drh
|
7217 |
eMode = 'x'; |
|
4f76459…
|
drh
|
7218 |
bOnce = 2; |
|
4f76459…
|
drh
|
7219 |
}else if( c=='w' ){ |
|
4f76459…
|
drh
|
7220 |
eMode = 'w'; |
|
4f76459…
|
drh
|
7221 |
bOnce = 2; |
|
4f76459…
|
drh
|
7222 |
}else if( n>=2 && cli_strncmp(azArg[0],"once",n)==0 ){ |
|
4f76459…
|
drh
|
7223 |
bOnce = 1; |
|
4f76459…
|
drh
|
7224 |
} |
|
4f76459…
|
drh
|
7225 |
for(i=1; i<nArg; i++){ |
|
4f76459…
|
drh
|
7226 |
char *z = azArg[i]; |
|
4f76459…
|
drh
|
7227 |
if( z[0]=='-' ){ |
|
4f76459…
|
drh
|
7228 |
if( z[1]=='-' ) z++; |
|
4f76459…
|
drh
|
7229 |
if( cli_strcmp(z,"-bom")==0 ){ |
|
4f76459…
|
drh
|
7230 |
zBom = zBomUtf8; |
|
4f76459…
|
drh
|
7231 |
}else if( cli_strcmp(z,"-plain")==0 ){ |
|
4f76459…
|
drh
|
7232 |
bPlain = 1; |
|
4f76459…
|
drh
|
7233 |
}else if( c=='o' && z[0]=='1' && z[1]!=0 && z[2]==0 |
|
4f76459…
|
drh
|
7234 |
&& (z[1]=='x' || z[1]=='e' || z[1]=='w') ){ |
|
5f65ed5…
|
drh
|
7235 |
if( bKeep || eMode ){ |
|
4f76459…
|
drh
|
7236 |
dotCmdError(p, i, "incompatible with prior options",0); |
|
4f76459…
|
drh
|
7237 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7238 |
} |
|
4f76459…
|
drh
|
7239 |
eMode = z[1]; |
|
4f76459…
|
drh
|
7240 |
}else if( cli_strcmp(z,"-show")==0 ){ |
|
4f76459…
|
drh
|
7241 |
if( cli_output_capture ){ |
|
4f76459…
|
drh
|
7242 |
sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
|
4f76459…
|
drh
|
7243 |
} |
|
5f65ed5…
|
drh
|
7244 |
}else if( cli_strcmp(z,"-keep")==0 ){ |
|
4f76459…
|
drh
|
7245 |
bKeep = 1; |
|
4f76459…
|
drh
|
7246 |
}else if( optionMatch(z,"error-prefix") ){ |
|
4f76459…
|
drh
|
7247 |
if( i+1>=nArg ){ |
|
4f76459…
|
drh
|
7248 |
dotCmdError(p, i, "missing argument", 0); |
|
4f76459…
|
drh
|
7249 |
return 1; |
|
4f76459…
|
drh
|
7250 |
} |
|
4f76459…
|
drh
|
7251 |
free(p->zErrPrefix); |
|
4f76459…
|
drh
|
7252 |
i++; |
|
4f76459…
|
drh
|
7253 |
p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); |
|
4f76459…
|
drh
|
7254 |
}else{ |
|
4f76459…
|
drh
|
7255 |
dotCmdError(p, i, "unknown option", 0); |
|
4f76459…
|
drh
|
7256 |
sqlite3_free(zFile); |
|
4f76459…
|
drh
|
7257 |
return 1; |
|
4f76459…
|
drh
|
7258 |
} |
|
4f76459…
|
drh
|
7259 |
}else if( zFile==0 && eMode==0 ){ |
|
5f65ed5…
|
drh
|
7260 |
if( bKeep ){ |
|
4f76459…
|
drh
|
7261 |
dotCmdError(p, i, "incompatible with prior options",0); |
|
4f76459…
|
drh
|
7262 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7263 |
} |
|
4f76459…
|
drh
|
7264 |
if( cli_strcmp(z, "memory")==0 && bOnce ){ |
|
4f76459…
|
drh
|
7265 |
dotCmdError(p, 0, "cannot redirect to \"memory\"", 0); |
|
4f76459…
|
drh
|
7266 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7267 |
} |
|
4f76459…
|
drh
|
7268 |
if( cli_strcmp(z, "off")==0 ){ |
|
4f76459…
|
drh
|
7269 |
#ifdef _WIN32 |
|
4f76459…
|
drh
|
7270 |
zFile = sqlite3_mprintf("nul"); |
|
4f76459…
|
drh
|
7271 |
#else |
|
4f76459…
|
drh
|
7272 |
zFile = sqlite3_mprintf("/dev/null"); |
|
4f76459…
|
drh
|
7273 |
#endif |
|
4f76459…
|
drh
|
7274 |
}else{ |
|
4f76459…
|
drh
|
7275 |
zFile = sqlite3_mprintf("%s", z); |
|
4f76459…
|
drh
|
7276 |
} |
|
4f76459…
|
drh
|
7277 |
if( zFile && zFile[0]=='|' ){ |
|
4f76459…
|
drh
|
7278 |
while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); |
|
4f76459…
|
drh
|
7279 |
break; |
|
4f76459…
|
drh
|
7280 |
} |
|
4f76459…
|
drh
|
7281 |
}else{ |
|
4f76459…
|
drh
|
7282 |
dotCmdError(p, i, "surplus argument", 0); |
|
4f76459…
|
drh
|
7283 |
sqlite3_free(zFile); |
|
4f76459…
|
drh
|
7284 |
return 1; |
|
4f76459…
|
drh
|
7285 |
} |
|
4f76459…
|
drh
|
7286 |
} |
|
4f76459…
|
drh
|
7287 |
if( zFile==0 && !bKeep ){ |
|
4f76459…
|
drh
|
7288 |
zFile = sqlite3_mprintf("stdout"); |
|
4f76459…
|
drh
|
7289 |
shell_check_oom(zFile); |
|
4f76459…
|
drh
|
7290 |
} |
|
4f76459…
|
drh
|
7291 |
if( bOnce ){ |
|
4f76459…
|
drh
|
7292 |
p->nPopOutput = 2; |
|
4f76459…
|
drh
|
7293 |
}else{ |
|
4f76459…
|
drh
|
7294 |
p->nPopOutput = 0; |
|
4f76459…
|
drh
|
7295 |
} |
|
4f76459…
|
drh
|
7296 |
if( !bKeep ) output_reset(p); |
|
4f76459…
|
drh
|
7297 |
#ifndef SQLITE_NOHAVE_SYSTEM |
|
4f76459…
|
drh
|
7298 |
if( eMode=='e' || eMode=='x' || eMode=='w' ){ |
|
4f76459…
|
drh
|
7299 |
p->doXdgOpen = 1; |
|
4f76459…
|
drh
|
7300 |
modePush(p); |
|
4f76459…
|
drh
|
7301 |
if( eMode=='x' ){ |
|
4f76459…
|
drh
|
7302 |
/* spreadsheet mode. Output as CSV. */ |
|
4f76459…
|
drh
|
7303 |
newTempFile(p, "csv"); |
|
4f76459…
|
drh
|
7304 |
p->mode.mFlags &= ~MFLG_ECHO; |
|
4f76459…
|
drh
|
7305 |
p->mode.eMode = MODE_Csv; |
|
4f76459…
|
drh
|
7306 |
modeSetStr(&p->mode.spec.zColumnSep, SEP_Comma); |
|
4f76459…
|
drh
|
7307 |
modeSetStr(&p->mode.spec.zRowSep, SEP_CrLf); |
|
4f76459…
|
drh
|
7308 |
#ifdef _WIN32 |
|
4f76459…
|
drh
|
7309 |
zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does |
|
4f76459…
|
drh
|
7310 |
** not work without it. */ |
|
4f76459…
|
drh
|
7311 |
#endif |
|
4f76459…
|
drh
|
7312 |
}else if( eMode=='w' ){ |
|
4f76459…
|
drh
|
7313 |
/* web-browser mode. */ |
|
4f76459…
|
drh
|
7314 |
newTempFile(p, "html"); |
|
4f76459…
|
drh
|
7315 |
if( !bPlain ) p->mode.eMode = MODE_Www; |
|
4f76459…
|
drh
|
7316 |
}else{ |
|
4f76459…
|
drh
|
7317 |
/* text editor mode */ |
|
4f76459…
|
drh
|
7318 |
newTempFile(p, "txt"); |
|
4f76459…
|
drh
|
7319 |
} |
|
4f76459…
|
drh
|
7320 |
sqlite3_free(zFile); |
|
4f76459…
|
drh
|
7321 |
zFile = sqlite3_mprintf("%s", p->zTempFile); |
|
4f76459…
|
drh
|
7322 |
} |
|
4f76459…
|
drh
|
7323 |
#endif /* SQLITE_NOHAVE_SYSTEM */ |
|
4f76459…
|
drh
|
7324 |
if( !bKeep ) shell_check_oom(zFile); |
|
4f76459…
|
drh
|
7325 |
if( bKeep ){ |
|
4f76459…
|
drh
|
7326 |
/* no-op */ |
|
4f76459…
|
drh
|
7327 |
}else if( cli_strcmp(zFile,"memory")==0 ){ |
|
4f76459…
|
drh
|
7328 |
if( cli_output_capture ){ |
|
4f76459…
|
drh
|
7329 |
sqlite3_str_free(cli_output_capture); |
|
4f76459…
|
drh
|
7330 |
} |
|
4f76459…
|
drh
|
7331 |
cli_output_capture = sqlite3_str_new(0); |
|
4f76459…
|
drh
|
7332 |
}else if( zFile[0]=='|' ){ |
|
4f76459…
|
drh
|
7333 |
#ifdef SQLITE_OMIT_POPEN |
|
4f76459…
|
drh
|
7334 |
eputz("Error: pipes are not supported in this OS\n"); |
|
4f76459…
|
drh
|
7335 |
output_redir(p, stdout); |
|
4f76459…
|
drh
|
7336 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7337 |
#else |
|
4f76459…
|
drh
|
7338 |
FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); |
|
4f76459…
|
drh
|
7339 |
if( pfPipe==0 ){ |
|
4f76459…
|
drh
|
7340 |
assert( stderr!=NULL ); |
|
4f76459…
|
drh
|
7341 |
cli_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); |
|
4f76459…
|
drh
|
7342 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7343 |
}else{ |
|
4f76459…
|
drh
|
7344 |
output_redir(p, pfPipe); |
|
4f76459…
|
drh
|
7345 |
if( zBom ) cli_puts(zBom, pfPipe); |
|
4f76459…
|
drh
|
7346 |
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
|
4f76459…
|
drh
|
7347 |
} |
|
4f76459…
|
drh
|
7348 |
#endif |
|
4f76459…
|
drh
|
7349 |
}else{ |
|
4f76459…
|
drh
|
7350 |
FILE *pfFile = output_file_open(p, zFile); |
|
4f76459…
|
drh
|
7351 |
if( pfFile==0 ){ |
|
4f76459…
|
drh
|
7352 |
if( cli_strcmp(zFile,"off")!=0 ){ |
|
4f76459…
|
drh
|
7353 |
assert( stderr!=NULL ); |
|
4f76459…
|
drh
|
7354 |
cli_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
|
4f76459…
|
drh
|
7355 |
} |
|
4f76459…
|
drh
|
7356 |
goto dotCmdOutput_error; |
|
4f76459…
|
drh
|
7357 |
} else { |
|
4f76459…
|
drh
|
7358 |
output_redir(p, pfFile); |
|
4f76459…
|
drh
|
7359 |
if( zBom ) cli_puts(zBom, pfFile); |
|
4f76459…
|
drh
|
7360 |
if( bPlain && eMode=='w' ){ |
|
4f76459…
|
drh
|
7361 |
cli_puts( |
|
4f76459…
|
drh
|
7362 |
"<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", |
|
4f76459…
|
drh
|
7363 |
pfFile |
|
4f76459…
|
drh
|
7364 |
); |
|
4f76459…
|
drh
|
7365 |
} |
|
4f76459…
|
drh
|
7366 |
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
|
4f76459…
|
drh
|
7367 |
} |
|
4f76459…
|
drh
|
7368 |
} |
|
4f76459…
|
drh
|
7369 |
sqlite3_free(zFile); |
|
4f76459…
|
drh
|
7370 |
return 0; |
|
4f76459…
|
drh
|
7371 |
|
|
4f76459…
|
drh
|
7372 |
dotCmdOutput_error: |
|
4f76459…
|
drh
|
7373 |
sqlite3_free(zFile); |
|
4f76459…
|
drh
|
7374 |
return 1; |
|
4f76459…
|
drh
|
7375 |
} |
|
9aee493…
|
drh
|
7376 |
|
|
9aee493…
|
drh
|
7377 |
/* |
|
5f65ed5…
|
drh
|
7378 |
** DOT-COMMAND: .check |
|
5f65ed5…
|
drh
|
7379 |
** USAGE: .check [OPTIONS] PATTERN |
|
5f65ed5…
|
drh
|
7380 |
** |
|
5f65ed5…
|
drh
|
7381 |
** Verify results of commands since the most recent .testcase command. |
|
5f65ed5…
|
drh
|
7382 |
** Restore output to the console, unless --keep is used. |
|
5f65ed5…
|
drh
|
7383 |
** |
|
5f65ed5…
|
drh
|
7384 |
** If PATTERN starts with "<<ENDMARK" then the actual pattern is taken from |
|
5f65ed5…
|
drh
|
7385 |
** subsequent lines of text up to the first line that begins with ENDMARK. |
|
5f65ed5…
|
drh
|
7386 |
** All pattern lines and the ENDMARK are discarded. |
|
5f65ed5…
|
drh
|
7387 |
** |
|
5f65ed5…
|
drh
|
7388 |
** Options: |
|
5f65ed5…
|
drh
|
7389 |
** --exact Do an exact comparison including leading and |
|
5f65ed5…
|
drh
|
7390 |
** trailing whitespace. |
|
5f65ed5…
|
drh
|
7391 |
** --glob Treat PATTERN as a GLOB |
|
5f65ed5…
|
drh
|
7392 |
** --keep Do not reset the testcase. More .check commands |
|
5f65ed5…
|
drh
|
7393 |
** will follow. |
|
5f65ed5…
|
drh
|
7394 |
** --notglob Output should not match PATTERN |
|
5f65ed5…
|
drh
|
7395 |
** --show Write testcase output to the screen, for debugging. |
|
5f65ed5…
|
drh
|
7396 |
*/ |
|
5f65ed5…
|
drh
|
7397 |
static int dotCmdCheck(ShellState *p){ |
|
5f65ed5…
|
drh
|
7398 |
int nArg = p->dot.nArg; /* Number of arguments */ |
|
5f65ed5…
|
drh
|
7399 |
char **azArg = p->dot.azArg; /* Text of the arguments */ |
|
5f65ed5…
|
drh
|
7400 |
int i; /* Loop counter */ |
|
5f65ed5…
|
drh
|
7401 |
int k; /* Result of pickStr() */ |
|
5f65ed5…
|
drh
|
7402 |
char *zTest; /* Textcase result */ |
|
5f65ed5…
|
drh
|
7403 |
int bKeep = 0; /* --keep option */ |
|
5f65ed5…
|
drh
|
7404 |
char *zCheck = 0; /* PATTERN argument */ |
|
5f65ed5…
|
drh
|
7405 |
char *zPattern = 0; /* Actual test pattern */ |
|
5f65ed5…
|
drh
|
7406 |
int eCheck = 0; /* 1: --glob, 2: --notglob, 3: --exact */ |
|
5f65ed5…
|
drh
|
7407 |
int isOk; /* True if results are OK */ |
|
5f65ed5…
|
drh
|
7408 |
sqlite3_int64 iStart = p->lineno; /* Line number of .check statement */ |
|
5f65ed5…
|
drh
|
7409 |
|
|
5f65ed5…
|
drh
|
7410 |
if( p->zTestcase[0]==0 ){ |
|
5f65ed5…
|
drh
|
7411 |
dotCmdError(p, 0, "no .testcase is active", 0); |
|
5f65ed5…
|
drh
|
7412 |
return 1; |
|
5f65ed5…
|
drh
|
7413 |
} |
|
5f65ed5…
|
drh
|
7414 |
for(i=1; i<nArg; i++){ |
|
5f65ed5…
|
drh
|
7415 |
char *z = azArg[i]; |
|
5f65ed5…
|
drh
|
7416 |
if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
|
5f65ed5…
|
drh
|
7417 |
if( cli_strcmp(z,"-keep")==0 ){ |
|
5f65ed5…
|
drh
|
7418 |
bKeep = 1; |
|
5f65ed5…
|
drh
|
7419 |
}else if( cli_strcmp(z,"-show")==0 ){ |
|
5f65ed5…
|
drh
|
7420 |
if( cli_output_capture ){ |
|
5f65ed5…
|
drh
|
7421 |
sqlite3_fprintf(stdout, "%s", sqlite3_str_value(cli_output_capture)); |
|
5f65ed5…
|
drh
|
7422 |
} |
|
5f65ed5…
|
drh
|
7423 |
bKeep = 1; |
|
5f65ed5…
|
drh
|
7424 |
}else if( z[0]=='-' |
|
5f65ed5…
|
drh
|
7425 |
&& (k = pickStr(&z[1],0,"glob","notglob","exact",""))>=0 |
|
5f65ed5…
|
drh
|
7426 |
){ |
|
5f65ed5…
|
drh
|
7427 |
if( eCheck && eCheck!=k+1 ){ |
|
5f65ed5…
|
drh
|
7428 |
dotCmdError(p, i, "incompatible with prior options",0); |
|
5f65ed5…
|
drh
|
7429 |
return 1; |
|
5f65ed5…
|
drh
|
7430 |
} |
|
5f65ed5…
|
drh
|
7431 |
eCheck = k+1; |
|
5f65ed5…
|
drh
|
7432 |
}else if( zCheck ){ |
|
5f65ed5…
|
drh
|
7433 |
dotCmdError(p, i, "unknown option", 0); |
|
5f65ed5…
|
drh
|
7434 |
return 1; |
|
5f65ed5…
|
drh
|
7435 |
}else{ |
|
5f65ed5…
|
drh
|
7436 |
zCheck = azArg[i]; |
|
5f65ed5…
|
drh
|
7437 |
} |
|
5f65ed5…
|
drh
|
7438 |
} |
|
5f65ed5…
|
drh
|
7439 |
if( zCheck==0 ){ |
|
5f65ed5…
|
drh
|
7440 |
dotCmdError(p, 0, "no PATTERN specified", 0); |
|
5f65ed5…
|
drh
|
7441 |
return 1; |
|
5f65ed5…
|
drh
|
7442 |
} |
|
b9ecacf…
|
drh
|
7443 |
if( cli_output_capture && sqlite3_str_length(cli_output_capture) ){ |
|
5f65ed5…
|
drh
|
7444 |
zTest = sqlite3_str_value(cli_output_capture); |
|
5f65ed5…
|
drh
|
7445 |
shell_check_oom(zTest); |
|
5f65ed5…
|
drh
|
7446 |
}else{ |
|
5f65ed5…
|
drh
|
7447 |
zTest = ""; |
|
5f65ed5…
|
drh
|
7448 |
} |
|
5f65ed5…
|
drh
|
7449 |
p->nTestRun++; |
|
5f65ed5…
|
drh
|
7450 |
if( zCheck[0]=='<' && zCheck[1]=='<' && zCheck[2]!=0 ){ |
|
5f65ed5…
|
drh
|
7451 |
int nCheck = strlen30(zCheck); |
|
5f65ed5…
|
drh
|
7452 |
sqlite3_str *pPattern = sqlite3_str_new(p->db); |
|
5f65ed5…
|
drh
|
7453 |
char zLine[2000]; |
|
5f65ed5…
|
drh
|
7454 |
while( sqlite3_fgets(zLine,sizeof(zLine),p->in) ){ |
|
5f65ed5…
|
drh
|
7455 |
if( strchr(zLine,'\n') ) p->lineno++; |
|
5f65ed5…
|
drh
|
7456 |
if( cli_strncmp(&zCheck[2],zLine,nCheck-2)==0 ) break; |
|
5f65ed5…
|
drh
|
7457 |
sqlite3_str_appendall(pPattern, zLine); |
|
5f65ed5…
|
drh
|
7458 |
} |
|
5f65ed5…
|
drh
|
7459 |
zPattern = sqlite3_str_finish(pPattern); |
|
5f65ed5…
|
drh
|
7460 |
if( zPattern==0 ){ |
|
5f65ed5…
|
drh
|
7461 |
zPattern = sqlite3_mprintf(""); |
|
5f65ed5…
|
drh
|
7462 |
} |
|
5f65ed5…
|
drh
|
7463 |
}else{ |
|
5f65ed5…
|
drh
|
7464 |
zPattern = zCheck; |
|
5f65ed5…
|
drh
|
7465 |
} |
|
5f65ed5…
|
drh
|
7466 |
shell_check_oom(zPattern); |
|
5f65ed5…
|
drh
|
7467 |
switch( eCheck ){ |
|
5f65ed5…
|
drh
|
7468 |
case 1: { |
|
5f65ed5…
|
drh
|
7469 |
char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
|
5f65ed5…
|
drh
|
7470 |
isOk = testcase_glob(zGlob, zTest)!=0; |
|
5f65ed5…
|
drh
|
7471 |
sqlite3_free(zGlob); |
|
5f65ed5…
|
drh
|
7472 |
break; |
|
5f65ed5…
|
drh
|
7473 |
} |
|
5f65ed5…
|
drh
|
7474 |
case 2: { |
|
5f65ed5…
|
drh
|
7475 |
char *zGlob = sqlite3_mprintf("*%s*", zPattern); |
|
5f65ed5…
|
drh
|
7476 |
isOk = testcase_glob(zGlob, zTest)==0; |
|
5f65ed5…
|
drh
|
7477 |
sqlite3_free(zGlob); |
|
5f65ed5…
|
drh
|
7478 |
break; |
|
5f65ed5…
|
drh
|
7479 |
} |
|
5f65ed5…
|
drh
|
7480 |
case 3: { |
|
5f65ed5…
|
drh
|
7481 |
isOk = cli_strcmp(zTest,zPattern)==0; |
|
5f65ed5…
|
drh
|
7482 |
break; |
|
5f65ed5…
|
drh
|
7483 |
} |
|
5f65ed5…
|
drh
|
7484 |
default: { |
|
5f65ed5…
|
drh
|
7485 |
/* Skip leading and trailing \n and \r on both pattern and test output */ |
|
5f65ed5…
|
drh
|
7486 |
const char *z1 = zPattern; |
|
5f65ed5…
|
drh
|
7487 |
const char *z2 = zTest; |
|
5f65ed5…
|
drh
|
7488 |
size_t n1, n2; |
|
5f65ed5…
|
drh
|
7489 |
while( z1[0]=='\n' || z1[0]=='\r' ) z1++; |
|
5f65ed5…
|
drh
|
7490 |
n1 = strlen(z1); |
|
5f65ed5…
|
drh
|
7491 |
while( n1>0 && (z1[n1-1]=='\n' || z1[n1-1]=='\r') ) n1--; |
|
5f65ed5…
|
drh
|
7492 |
while( z2[0]=='\n' || z2[0]=='\r' ) z2++; |
|
5f65ed5…
|
drh
|
7493 |
n2 = strlen(z2); |
|
5f65ed5…
|
drh
|
7494 |
while( n2>0 && (z2[n2-1]=='\n' || z2[n2-1]=='\r') ) n2--; |
|
5f65ed5…
|
drh
|
7495 |
isOk = n1==n2 && memcmp(z1,z2,n1)==0; |
|
5f65ed5…
|
drh
|
7496 |
break; |
|
5f65ed5…
|
drh
|
7497 |
} |
|
5f65ed5…
|
drh
|
7498 |
} |
|
5f65ed5…
|
drh
|
7499 |
if( !isOk ){ |
|
5f65ed5…
|
drh
|
7500 |
sqlite3_fprintf(stderr, |
|
5f65ed5…
|
drh
|
7501 |
"%s:%lld: .check failed for testcase %s\n", |
|
5f65ed5…
|
drh
|
7502 |
p->zInFile, iStart, p->zTestcase); |
|
5f65ed5…
|
drh
|
7503 |
p->nTestErr++; |
|
5f65ed5…
|
drh
|
7504 |
sqlite3_fprintf(stderr, "Expected: [%s]\n", zPattern); |
|
5f65ed5…
|
drh
|
7505 |
sqlite3_fprintf(stderr, "Got: [%s]\n", zTest); |
|
5f65ed5…
|
drh
|
7506 |
} |
|
5f65ed5…
|
drh
|
7507 |
if( zPattern!=zCheck ){ |
|
5f65ed5…
|
drh
|
7508 |
sqlite3_free(zPattern); |
|
5f65ed5…
|
drh
|
7509 |
} |
|
5f65ed5…
|
drh
|
7510 |
if( !bKeep ){ |
|
5f65ed5…
|
drh
|
7511 |
output_reset(p); |
|
5f65ed5…
|
drh
|
7512 |
p->zTestcase[0] = 0; |
|
5f65ed5…
|
drh
|
7513 |
} |
|
5f65ed5…
|
drh
|
7514 |
return 0; |
|
5f65ed5…
|
drh
|
7515 |
} |
|
5f65ed5…
|
drh
|
7516 |
|
|
5f65ed5…
|
drh
|
7517 |
/* |
|
5f65ed5…
|
drh
|
7518 |
** DOT-COMMAND: .testcase |
|
5f65ed5…
|
drh
|
7519 |
** USAGE: .testcase [OPTIONS] NAME |
|
5f65ed5…
|
drh
|
7520 |
** |
|
5f65ed5…
|
drh
|
7521 |
** Start a new test case identified by NAME. All output |
|
5f65ed5…
|
drh
|
7522 |
** through the next ".check" command is captured for comparison. See the |
|
5f65ed5…
|
drh
|
7523 |
** ".check" commandn for additional informatioon. |
|
5f65ed5…
|
drh
|
7524 |
** |
|
5f65ed5…
|
drh
|
7525 |
** Options: |
|
5f65ed5…
|
drh
|
7526 |
** --error-prefix TEXT Change error message prefix text to TEXT |
|
5f65ed5…
|
drh
|
7527 |
*/ |
|
5f65ed5…
|
drh
|
7528 |
static int dotCmdTestcase(ShellState *p){ |
|
5f65ed5…
|
drh
|
7529 |
int nArg = p->dot.nArg; /* Number of arguments */ |
|
5f65ed5…
|
drh
|
7530 |
char **azArg = p->dot.azArg; /* Text of the arguments */ |
|
5f65ed5…
|
drh
|
7531 |
int i; /* Loop counter */ |
|
5f65ed5…
|
drh
|
7532 |
const char *zName = 0; /* Testcase name */ |
|
5f65ed5…
|
drh
|
7533 |
|
|
5f65ed5…
|
drh
|
7534 |
for(i=1; i<nArg; i++){ |
|
5f65ed5…
|
drh
|
7535 |
char *z = azArg[i]; |
|
5f65ed5…
|
drh
|
7536 |
if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++; |
|
5f65ed5…
|
drh
|
7537 |
if( optionMatch(z,"error-prefix") ){ |
|
5f65ed5…
|
drh
|
7538 |
if( i+1>=nArg ){ |
|
5f65ed5…
|
drh
|
7539 |
dotCmdError(p, i, "missing argument", 0); |
|
5f65ed5…
|
drh
|
7540 |
return 1; |
|
5f65ed5…
|
drh
|
7541 |
} |
|
5f65ed5…
|
drh
|
7542 |
free(p->zErrPrefix); |
|
5f65ed5…
|
drh
|
7543 |
i++; |
|
5f65ed5…
|
drh
|
7544 |
p->zErrPrefix = azArg[i][0]==0 ? 0 : strdup(azArg[i]); |
|
5f65ed5…
|
drh
|
7545 |
}else if( zName ){ |
|
5f65ed5…
|
drh
|
7546 |
dotCmdError(p, i, "unknown option", 0); |
|
5f65ed5…
|
drh
|
7547 |
return 1; |
|
5f65ed5…
|
drh
|
7548 |
}else{ |
|
5f65ed5…
|
drh
|
7549 |
zName = azArg[i]; |
|
5f65ed5…
|
drh
|
7550 |
} |
|
5f65ed5…
|
drh
|
7551 |
} |
|
5f65ed5…
|
drh
|
7552 |
output_reset(p); |
|
5f65ed5…
|
drh
|
7553 |
if( cli_output_capture ){ |
|
5f65ed5…
|
drh
|
7554 |
sqlite3_str_free(cli_output_capture); |
|
5f65ed5…
|
drh
|
7555 |
} |
|
5f65ed5…
|
drh
|
7556 |
cli_output_capture = sqlite3_str_new(0); |
|
5f65ed5…
|
drh
|
7557 |
if( zName ){ |
|
5f65ed5…
|
drh
|
7558 |
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", zName); |
|
5f65ed5…
|
drh
|
7559 |
}else{ |
|
5f65ed5…
|
drh
|
7560 |
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s:%lld", |
|
5f65ed5…
|
drh
|
7561 |
p->zInFile, p->lineno); |
|
5f65ed5…
|
drh
|
7562 |
} |
|
5f65ed5…
|
drh
|
7563 |
return 0; |
|
5f65ed5…
|
drh
|
7564 |
} |
|
5f65ed5…
|
drh
|
7565 |
|
|
5f65ed5…
|
drh
|
7566 |
/* |
|
9aee493…
|
drh
|
7567 |
** Enlarge the space allocated in p->dot so that it can hold more |
|
9aee493…
|
drh
|
7568 |
** than nArg parsed command-line arguments. |
|
4f76459…
|
drh
|
7569 |
*/ |
|
4f76459…
|
drh
|
7570 |
static void parseDotRealloc(ShellState *p, int nArg){ |
|
4f76459…
|
drh
|
7571 |
p->dot.nAlloc = nArg+22; |
|
4f76459…
|
drh
|
7572 |
p->dot.azArg = realloc(p->dot.azArg,p->dot.nAlloc*sizeof(char*)); |
|
4f76459…
|
drh
|
7573 |
shell_check_oom(p->dot.azArg); |
|
4f76459…
|
drh
|
7574 |
p->dot.aiOfst = realloc(p->dot.aiOfst,p->dot.nAlloc*sizeof(int)); |
|
4f76459…
|
drh
|
7575 |
shell_check_oom(p->dot.aiOfst); |
|
4f76459…
|
drh
|
7576 |
p->dot.abQuot = realloc(p->dot.abQuot,p->dot.nAlloc); |
|
4f76459…
|
drh
|
7577 |
shell_check_oom(p->dot.abQuot); |
|
4f76459…
|
drh
|
7578 |
} |
|
9aee493…
|
drh
|
7579 |
|
|
9aee493…
|
drh
|
7580 |
|
|
9aee493…
|
drh
|
7581 |
/* |
|
9aee493…
|
drh
|
7582 |
** Parse input line zLine up into individual arguments. Retain the |
|
9aee493…
|
drh
|
7583 |
** parse in the p->dot substructure. |
|
9aee493…
|
drh
|
7584 |
*/ |
|
4f76459…
|
drh
|
7585 |
static void parseDotCmdArgs(const char *zLine, ShellState *p){ |
|
4f76459…
|
drh
|
7586 |
char *z; |
|
4f76459…
|
drh
|
7587 |
int h = 1; |
|
4f76459…
|
drh
|
7588 |
int nArg = 0; |
|
9aee493…
|
drh
|
7589 |
size_t szLine; |
|
4f76459…
|
drh
|
7590 |
|
|
4f76459…
|
drh
|
7591 |
p->dot.zOrig = zLine; |
|
4f76459…
|
drh
|
7592 |
free(p->dot.zCopy); |
|
4f76459…
|
drh
|
7593 |
z = p->dot.zCopy = strdup(zLine); |
|
4f76459…
|
drh
|
7594 |
shell_check_oom(z); |
|
9aee493…
|
drh
|
7595 |
szLine = strlen(z); |
|
9aee493…
|
drh
|
7596 |
while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
|
5f65ed5…
|
drh
|
7597 |
if( szLine>0 && z[szLine-1]==';' ){ |
|
9aee493…
|
drh
|
7598 |
szLine--; |
|
9aee493…
|
drh
|
7599 |
while( szLine>0 && IsSpace(z[szLine-1]) ) szLine--; |
|
9aee493…
|
drh
|
7600 |
} |
|
9aee493…
|
drh
|
7601 |
z[szLine] = 0; |
|
4f76459…
|
drh
|
7602 |
parseDotRealloc(p, 2); |
|
4f76459…
|
drh
|
7603 |
while( z[h] ){ |
|
4f76459…
|
drh
|
7604 |
while( IsSpace(z[h]) ){ h++; } |
|
4f76459…
|
drh
|
7605 |
if( z[h]==0 ) break; |
|
4f76459…
|
drh
|
7606 |
if( nArg+2>p->dot.nAlloc ){ |
|
4f76459…
|
drh
|
7607 |
parseDotRealloc(p, nArg); |
|
4f76459…
|
drh
|
7608 |
} |
|
4f76459…
|
drh
|
7609 |
if( z[h]=='\'' || z[h]=='"' ){ |
|
4f76459…
|
drh
|
7610 |
int delim = z[h++]; |
|
4f76459…
|
drh
|
7611 |
p->dot.abQuot[nArg] = 1; |
|
4f76459…
|
drh
|
7612 |
p->dot.azArg[nArg] = &z[h]; |
|
4f76459…
|
drh
|
7613 |
p->dot.aiOfst[nArg] = h; |
|
4f76459…
|
drh
|
7614 |
while( z[h] && z[h]!=delim ){ |
|
4f76459…
|
drh
|
7615 |
if( z[h]=='\\' && delim=='"' && z[h+1]!=0 ) h++; |
|
4f76459…
|
drh
|
7616 |
h++; |
|
4f76459…
|
drh
|
7617 |
} |
|
4f76459…
|
drh
|
7618 |
if( z[h]==delim ){ |
|
4f76459…
|
drh
|
7619 |
z[h++] = 0; |
|
4f76459…
|
drh
|
7620 |
} |
|
4f76459…
|
drh
|
7621 |
if( delim=='"' ) resolve_backslashes(p->dot.azArg[nArg]); |
|
4f76459…
|
drh
|
7622 |
}else{ |
|
4f76459…
|
drh
|
7623 |
p->dot.abQuot[nArg] = 0; |
|
4f76459…
|
drh
|
7624 |
p->dot.azArg[nArg] = &z[h]; |
|
4f76459…
|
drh
|
7625 |
p->dot.aiOfst[nArg] = h; |
|
4f76459…
|
drh
|
7626 |
while( z[h] && !IsSpace(z[h]) ){ h++; } |
|
4f76459…
|
drh
|
7627 |
if( z[h] ) z[h++] = 0; |
|
4f76459…
|
drh
|
7628 |
} |
|
4f76459…
|
drh
|
7629 |
nArg++; |
|
4f76459…
|
drh
|
7630 |
} |
|
4f76459…
|
drh
|
7631 |
p->dot.nArg = nArg; |
|
4f76459…
|
drh
|
7632 |
p->dot.azArg[nArg] = 0; |
|
4f76459…
|
drh
|
7633 |
} |
|
4f76459…
|
drh
|
7634 |
|
|
4f76459…
|
drh
|
7635 |
/* |
|
4f76459…
|
drh
|
7636 |
static int do_meta_command(const char *zLine, ShellState *p){ |
|
4f76459…
|
drh
|
7637 |
int nArg; |
|
4f76459…
|
drh
|
7638 |
char **azArg; |
|
4f76459…
|
drh
|
7639 |
/* Parse the input line into tokens stored in p->dot. |
|
4f76459…
|
drh
|
7640 |
parseDotCmdArgs(zLine, p); |
|
4f76459…
|
drh
|
7641 |
nArg = p->dot.nArg; |
|
4f76459…
|
drh
|
7642 |
azArg = p->dot.azArg; |
|
4f76459…
|
drh
|
7643 |
cli_printf(stderr, "Usage: .auth ON|OFF\n"); |
|
4f76459…
|
drh
|
7644 |
dotCmdError(p, j, "unknown option", "should be -append or -async"); |
|
4f76459…
|
drh
|
7645 |
cli_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); |
|
4f76459…
|
drh
|
7646 |
cli_printf(stderr, "missing FILENAME argument on .backup\n"); |
|
4f76459…
|
drh
|
7647 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", zDestFile); |
|
4f76459…
|
drh
|
7648 |
cli_printf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); |
|
5f65ed5…
|
drh
|
7649 |
rc = dotCmdCheck(p); |
|
4f76459…
|
drh
|
7650 |
cli_printf(stdout, "ACTIVE %d: %s\n", i, zFile); |
|
4f76459…
|
drh
|
7651 |
cli_printf(stdout, " %d: %s\n", i, zFile); |
|
4f76459…
|
drh
|
7652 |
if( booleanValue(azArg[1]) ){ |
|
4f76459…
|
drh
|
7653 |
p->mode.mFlags |= MFLG_CRLF; |
|
4f76459…
|
drh
|
7654 |
}else{ |
|
4f76459…
|
drh
|
7655 |
p->mode.mFlags &= ~MFLG_CRLF; |
|
4f76459…
|
drh
|
7656 |
} |
|
4f76459…
|
drh
|
7657 |
p->mode.mFlags &= ~MFLG_CRLF; |
|
4f76459…
|
drh
|
7658 |
cli_printf(stderr, "crlf is %s\n", |
|
4f76459…
|
drh
|
7659 |
(p->mode.mFlags & MFLG_CRLF)!=0 ? "ON" : "OFF"); |
|
4f76459…
|
drh
|
7660 |
cli_printf(p->out, "%s: %s %s%s\n", |
|
b8ab8b3…
|
drh
|
7661 |
{ "fp_digits", SQLITE_DBCONFIG_FP_DIGITS }, |
|
b8ab8b3…
|
drh
|
7662 |
if( aDbConfig[ii].op==SQLITE_DBCONFIG_FP_DIGITS ){ |
|
b8ab8b3…
|
drh
|
7663 |
sqlite3_db_config(p->db, aDbConfig[ii].op, atoi(azArg[2]), 0); |
|
b8ab8b3…
|
drh
|
7664 |
}else{ |
|
b8ab8b3…
|
drh
|
7665 |
sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); |
|
b8ab8b3…
|
drh
|
7666 |
} |
|
b8ab8b3…
|
drh
|
7667 |
if( aDbConfig[ii].op==SQLITE_DBCONFIG_FP_DIGITS ){ |
|
b8ab8b3…
|
drh
|
7668 |
cli_printf(p->out, "%19s %d\n", aDbConfig[ii].zName, v); |
|
b8ab8b3…
|
drh
|
7669 |
}else{ |
|
b8ab8b3…
|
drh
|
7670 |
cli_printf(p->out, "%19s %s\n", |
|
b8ab8b3…
|
drh
|
7671 |
aDbConfig[ii].zName, v ? "on" : "off"); |
|
b8ab8b3…
|
drh
|
7672 |
} |
|
4f76459…
|
drh
|
7673 |
dotCmdError(p, 1, "unknown dbconfig", |
|
4f76459…
|
drh
|
7674 |
"Enter \".dbconfig\" with no arguments for a list"); |
|
4f76459…
|
drh
|
7675 |
Mode saved_mode; |
|
4f76459…
|
drh
|
7676 |
SHFLG_PreserveRowid|SHFLG_DumpDataOnly|SHFLG_DumpNoSys); |
|
4f76459…
|
drh
|
7677 |
dotCmdError(p, i, "unable", |
|
4f76459…
|
drh
|
7678 |
"The --preserve-rowids option is not compatible" |
|
4f76459…
|
drh
|
7679 |
" with SQLITE_OMIT_VIRTUALTABLE"); |
|
4f76459…
|
drh
|
7680 |
/*ShellSetFlag(p, SHFLG_Newlines);*/ |
|
4f76459…
|
drh
|
7681 |
dotCmdError(p, i, "unknown option", 0); |
|
4f76459…
|
drh
|
7682 |
modeDup(&saved_mode, &p->mode); |
|
4f76459…
|
drh
|
7683 |
cli_puts("PRAGMA foreign_keys=OFF;\n", p->out); |
|
4f76459…
|
drh
|
7684 |
cli_puts("BEGIN TRANSACTION;\n", p->out); |
|
4f76459…
|
drh
|
7685 |
p->mode.spec.bTitles = QRF_No; |
|
4f76459…
|
drh
|
7686 |
cli_puts("PRAGMA writable_schema=OFF;\n", p->out); |
|
4f76459…
|
drh
|
7687 |
cli_puts(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); |
|
4f76459…
|
drh
|
7688 |
modeFree(&p->mode); |
|
4f76459…
|
drh
|
7689 |
p->mode = saved_mode; |
|
4f76459…
|
drh
|
7690 |
rc = p->nErr>0; |
|
4f76459…
|
drh
|
7691 |
if( booleanValue(azArg[1]) ){ |
|
4f76459…
|
drh
|
7692 |
p->mode.mFlags |= MFLG_ECHO; |
|
4f76459…
|
drh
|
7693 |
}else{ |
|
4f76459…
|
drh
|
7694 |
p->mode.mFlags &= ~MFLG_ECHO; |
|
4f76459…
|
drh
|
7695 |
} |
|
70539ee…
|
drh
|
7696 |
open_db(p, 0); |
|
4f76459…
|
drh
|
7697 |
if( p->mode.autoEQPtrace ){ |
|
4f76459…
|
drh
|
7698 |
p->mode.autoEQPtrace = 0; |
|
4f76459…
|
drh
|
7699 |
p->mode.autoEQP = AUTOEQP_full; |
|
4f76459…
|
drh
|
7700 |
p->mode.autoEQP = AUTOEQP_trigger; |
|
4f76459…
|
drh
|
7701 |
p->mode.autoEQP = AUTOEQP_full; |
|
4f76459…
|
drh
|
7702 |
p->mode.autoEQPtrace = 1; |
|
4f76459…
|
drh
|
7703 |
p->mode.autoEQP = (u8)booleanValue(azArg[1]); |
|
4f76459…
|
drh
|
7704 |
if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) cli_exit(rc); |
|
4f76459…
|
drh
|
7705 |
p->mode.autoExplain = 1; |
|
4f76459…
|
drh
|
7706 |
p->mode.autoExplain = booleanValue(azArg[1]); |
|
4f76459…
|
drh
|
7707 |
} |
|
4f76459…
|
drh
|
7708 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
7709 |
cli_puts("Available file-controls:\n", p->out); |
|
4f76459…
|
drh
|
7710 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
7711 |
cli_printf(stderr,"Error: ambiguous file-control: \"%s\"\n" |
|
4f76459…
|
drh
|
7712 |
cli_printf(stderr,"Error: unknown file-control: %s\n" |
|
4f76459…
|
drh
|
7713 |
cli_printf(p->out, "%s\n", z); |
|
4f76459…
|
drh
|
7714 |
cli_printf(p->out, "%d\n", x); |
|
4f76459…
|
drh
|
7715 |
cli_printf(p->out, "Usage: .filectrl %s %s\n", |
|
4f76459…
|
drh
|
7716 |
cli_printf(p->out, "%s\n", zBuf); |
|
4f76459…
|
drh
|
7717 |
int hasStat[5]; |
|
4f76459…
|
drh
|
7718 |
int flgs = 0; |
|
4f76459…
|
drh
|
7719 |
char *zSql; |
|
4f76459…
|
drh
|
7720 |
zSql = sqlite3_mprintf( |
|
4f76459…
|
drh
|
7721 |
"SELECT shell_format_schema(sql,%d) FROM" |
|
4f76459…
|
drh
|
7722 |
" AND name NOT LIKE 'sqlite__%%' ESCAPE '_' " |
|
4f76459…
|
drh
|
7723 |
"ORDER BY x", flgs); |
|
4f76459…
|
drh
|
7724 |
memcpy(&data, p, sizeof(data)); |
|
4f76459…
|
drh
|
7725 |
data.mode.spec.bTitles = QRF_No; |
|
4f76459…
|
drh
|
7726 |
data.mode.eMode = MODE_List; |
|
4f76459…
|
drh
|
7727 |
data.mode.spec.eText = QRF_TEXT_Plain; |
|
4f76459…
|
drh
|
7728 |
data.mode.spec.nCharLimit = 0; |
|
4f76459…
|
drh
|
7729 |
data.mode.spec.zRowSep = "\n"; |
|
4f76459…
|
drh
|
7730 |
rc = shell_exec(&data,zSql,0); |
|
4f76459…
|
drh
|
7731 |
sqlite3_free(zSql); |
|
0276100…
|
drh
|
7732 |
memset(hasStat, 0, sizeof(hasStat)); |
|
4f76459…
|
drh
|
7733 |
"SELECT substr(name,12,1) FROM sqlite_schema" |
|
4f76459…
|
drh
|
7734 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
4f76459…
|
drh
|
7735 |
int k = sqlite3_column_int(pStmt,0); |
|
4f76459…
|
drh
|
7736 |
assert( k==1 || k==3 || k==4 ); |
|
4f76459…
|
drh
|
7737 |
hasStat[k] = 1; |
|
4f76459…
|
drh
|
7738 |
doStats = 1; |
|
4f76459…
|
drh
|
7739 |
} |
|
4f76459…
|
drh
|
7740 |
sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
7741 |
cli_puts("/* No STAT tables available */\n", p->out); |
|
4f76459…
|
drh
|
7742 |
cli_puts("ANALYZE sqlite_schema;\n", p->out); |
|
4f76459…
|
drh
|
7743 |
data.mode.eMode = MODE_Insert; |
|
4f76459…
|
drh
|
7744 |
if( hasStat[1] ){ |
|
4f76459…
|
drh
|
7745 |
data.mode.spec.zTableName = "sqlite_stat1"; |
|
4f76459…
|
drh
|
7746 |
shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); |
|
4f76459…
|
drh
|
7747 |
} |
|
4f76459…
|
drh
|
7748 |
if( hasStat[4] ){ |
|
4f76459…
|
drh
|
7749 |
data.mode.spec.zTableName = "sqlite_stat4"; |
|
4f76459…
|
drh
|
7750 |
shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); |
|
4f76459…
|
drh
|
7751 |
} |
|
4f76459…
|
drh
|
7752 |
cli_puts("ANALYZE sqlite_schema;\n", p->out); |
|
4f76459…
|
drh
|
7753 |
p->mode.spec.bTitles = booleanValue(azArg[1]) ? QRF_Yes : QRF_No; |
|
12e4a0f…
|
drh
|
7754 |
p->mode.mFlags |= MFLG_HDR; |
|
4f76459…
|
drh
|
7755 |
p->mode.spec.eTitle = aModeInfo[p->mode.eMode].eHdr; |
|
4f76459…
|
drh
|
7756 |
cli_printf(p->out, "Nothing matches '%s'\n", azArg[1]); |
|
4f76459…
|
drh
|
7757 |
rc = dotCmdImport(p); |
|
5f65ed5…
|
drh
|
7758 |
" WHERE type='index' AND lower(name)=lower('%q')" |
|
5f65ed5…
|
drh
|
7759 |
" WHERE type='table' AND lower(name)=lower('%q')" |
|
4f76459…
|
drh
|
7760 |
cli_printf(stderr,"no such index: \"%s\"\n", azArg[1]); |
|
4f76459…
|
drh
|
7761 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
7762 |
cli_printf(stdout, "%s;\n", zSql); |
|
4f76459…
|
drh
|
7763 |
cli_printf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); |
|
b8ab8b3…
|
drh
|
7764 |
|
|
17f9878…
|
drh
|
7765 |
if( c=='i' && (cli_strncmp(azArg[0], "indices", n)==0 |
|
17f9878…
|
drh
|
7766 |
|| cli_strncmp(azArg[0], "indexes", n)==0) |
|
17f9878…
|
drh
|
7767 |
){ |
|
17f9878…
|
drh
|
7768 |
sqlite3_str *pSql; |
|
17f9878…
|
drh
|
7769 |
int i; |
|
17f9878…
|
drh
|
7770 |
int allFlag = 0; |
|
17f9878…
|
drh
|
7771 |
int sysFlag = 0; |
|
17f9878…
|
drh
|
7772 |
int exprFlag = 0; |
|
17f9878…
|
drh
|
7773 |
int debugFlag = 0; /* Undocument --debug flag */ |
|
17f9878…
|
drh
|
7774 |
const char *zPattern = 0; |
|
17f9878…
|
drh
|
7775 |
const char *zSep = "WHERE"; |
|
17f9878…
|
drh
|
7776 |
|
|
17f9878…
|
drh
|
7777 |
for(i=1; i<nArg; i++){ |
|
17f9878…
|
drh
|
7778 |
if( azArg[i][0]=='-' ){ |
|
17f9878…
|
drh
|
7779 |
const char *z = azArg[i]+1; |
|
17f9878…
|
drh
|
7780 |
if( z[0]=='-' ) z++; |
|
17f9878…
|
drh
|
7781 |
if( cli_strcmp(z,"all")==0 || cli_strcmp(z,"a")==0 ){ |
|
17f9878…
|
drh
|
7782 |
allFlag = 1; |
|
17f9878…
|
drh
|
7783 |
}else |
|
17f9878…
|
drh
|
7784 |
if( cli_strcmp(z,"sys")==0 ){ |
|
17f9878…
|
drh
|
7785 |
sysFlag = 1; |
|
17f9878…
|
drh
|
7786 |
allFlag = 0; |
|
17f9878…
|
drh
|
7787 |
}else |
|
17f9878…
|
drh
|
7788 |
if( cli_strcmp(z,"expr")==0 ){ |
|
17f9878…
|
drh
|
7789 |
exprFlag = 1; |
|
17f9878…
|
drh
|
7790 |
allFlag = 0; |
|
17f9878…
|
drh
|
7791 |
}else |
|
17f9878…
|
drh
|
7792 |
if( cli_strcmp(z,"debug")==0 ){ |
|
17f9878…
|
drh
|
7793 |
debugFlag = 1; |
|
17f9878…
|
drh
|
7794 |
}else |
|
17f9878…
|
drh
|
7795 |
{ |
|
17f9878…
|
drh
|
7796 |
dotCmdError(p, i, "unknown option", 0); |
|
17f9878…
|
drh
|
7797 |
rc = 1; |
|
17f9878…
|
drh
|
7798 |
goto meta_command_exit; |
|
17f9878…
|
drh
|
7799 |
} |
|
17f9878…
|
drh
|
7800 |
}else if( zPattern==0 ){ |
|
17f9878…
|
drh
|
7801 |
zPattern = azArg[i]; |
|
17f9878…
|
drh
|
7802 |
}else{ |
|
17f9878…
|
drh
|
7803 |
dotCmdError(p, i, "unknown argument", 0); |
|
17f9878…
|
drh
|
7804 |
rc = 1; |
|
17f9878…
|
drh
|
7805 |
goto meta_command_exit; |
|
17f9878…
|
drh
|
7806 |
} |
|
17f9878…
|
drh
|
7807 |
} |
|
17f9878…
|
drh
|
7808 |
|
|
17f9878…
|
drh
|
7809 |
open_db(p, 0); |
|
17f9878…
|
drh
|
7810 |
pSql = sqlite3_str_new(p->db); |
|
17f9878…
|
drh
|
7811 |
sqlite3_str_appendf(pSql, |
|
17f9878…
|
drh
|
7812 |
"SELECT if(t.schema='main',i.name,t.schema||'.'||i.name)\n" |
|
17f9878…
|
drh
|
7813 |
"FROM pragma_table_list t, pragma_index_list(t.name,t.schema) i\n" |
|
17f9878…
|
drh
|
7814 |
); |
|
17f9878…
|
drh
|
7815 |
if( exprFlag ){ |
|
17f9878…
|
drh
|
7816 |
allFlag = 0; |
|
17f9878…
|
drh
|
7817 |
sqlite3_str_appendf(pSql, |
|
17f9878…
|
drh
|
7818 |
"%s (EXISTS(SELECT 1 FROM pragma_index_xinfo(i.name) WHERE cid=-2)\n" |
|
17f9878…
|
drh
|
7819 |
" OR\n" |
|
17f9878…
|
drh
|
7820 |
" EXISTS(SELECT cid FROM pragma_table_xinfo(t.name) WHERE hidden=2" |
|
17f9878…
|
drh
|
7821 |
" INTERSECT " |
|
17f9878…
|
drh
|
7822 |
" SELECT cid FROM pragma_index_info(i.name)))\n", zSep); |
|
17f9878…
|
drh
|
7823 |
zSep = "AND"; |
|
17f9878…
|
drh
|
7824 |
} |
|
17f9878…
|
drh
|
7825 |
if( sysFlag ){ |
|
17f9878…
|
drh
|
7826 |
sqlite3_str_appendf(pSql, |
|
17f9878…
|
drh
|
7827 |
"%s i.name LIKE 'sqlite__autoindex__%%' ESCAPE '_'\n", zSep); |
|
17f9878…
|
drh
|
7828 |
zSep = "AND"; |
|
17f9878…
|
drh
|
7829 |
}else if( !allFlag ){ |
|
17f9878…
|
drh
|
7830 |
sqlite3_str_appendf(pSql, |
|
17f9878…
|
drh
|
7831 |
"%s i.name NOT LIKE 'sqlite__%%' ESCAPE '_'\n", zSep); |
|
17f9878…
|
drh
|
7832 |
zSep = "AND"; |
|
17f9878…
|
drh
|
7833 |
} |
|
17f9878…
|
drh
|
7834 |
if( zPattern ){ |
|
17f9878…
|
drh
|
7835 |
sqlite3_str_appendf(pSql, "%s i.name LIKE '%%%q%%'\n", zSep, zPattern); |
|
17f9878…
|
drh
|
7836 |
} |
|
17f9878…
|
drh
|
7837 |
sqlite3_str_appendf(pSql, "ORDER BY 1"); |
|
17f9878…
|
drh
|
7838 |
|
|
17f9878…
|
drh
|
7839 |
/* Run the SQL statement in "split" mode. */ |
|
17f9878…
|
drh
|
7840 |
if( debugFlag ){ |
|
17f9878…
|
drh
|
7841 |
cli_printf(stdout,"%s;\n", sqlite3_str_value(pSql)); |
|
17f9878…
|
drh
|
7842 |
}else{ |
|
17f9878…
|
drh
|
7843 |
modePush(p); |
|
17f9878…
|
drh
|
7844 |
modeChange(p, MODE_Split); |
|
17f9878…
|
drh
|
7845 |
shell_exec(p, sqlite3_str_value(pSql), 0); |
|
17f9878…
|
drh
|
7846 |
modePop(p); |
|
17f9878…
|
drh
|
7847 |
} |
|
17f9878…
|
drh
|
7848 |
sqlite3_str_free(pSql); |
|
17f9878…
|
drh
|
7849 |
}else |
|
17f9878…
|
drh
|
7850 |
|
|
4f76459…
|
drh
|
7851 |
cli_printf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); |
|
4f76459…
|
drh
|
7852 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
|
4f76459…
|
drh
|
7853 |
{ "parser_depth", SQLITE_LIMIT_PARSER_DEPTH }, |
|
4f76459…
|
drh
|
7854 |
cli_printf(stdout, "%20s %d\n", aLimit[i].zLimitName, |
|
4f76459…
|
drh
|
7855 |
cli_printf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); |
|
4f76459…
|
drh
|
7856 |
cli_printf(stderr,"unknown limit: \"%s\"\n" |
|
5f65ed5…
|
drh
|
7857 |
}else{ |
|
5f65ed5…
|
drh
|
7858 |
cli_printf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, |
|
5f65ed5…
|
drh
|
7859 |
sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); |
|
4f76459…
|
drh
|
7860 |
p->pLog = output_file_open(p, zFile); |
|
4f76459…
|
drh
|
7861 |
rc = dotCmdMode(p); |
|
4f76459…
|
drh
|
7862 |
cli_printf(stderr,"line %lld: incorrect nonce: \"%s\"\n", |
|
4f76459…
|
drh
|
7863 |
cli_exit(1); |
|
4f76459…
|
drh
|
7864 |
modeSetStr(&p->mode.spec.zNull, azArg[1]); |
|
d326547…
|
drh
|
7865 |
if( p->bSafeMode ) openFlags = SQLITE_OPEN_READONLY; |
|
d326547…
|
drh
|
7866 |
|
|
d326547…
|
drh
|
7867 |
}else if( optionMatch(z, "zip") && !p->bSafeMode ){ |
|
d326547…
|
drh
|
7868 |
}else if( optionMatch(z, "append") && !p->bSafeMode ){ |
|
92871e0…
|
drh
|
7869 |
}else if( optionMatch(z, "normal") ){ |
|
92871e0…
|
drh
|
7870 |
openMode = SHELL_OPEN_NORMAL; |
|
4f76459…
|
drh
|
7871 |
cli_printf(stderr,"unknown option: %s\n", z); |
|
4f76459…
|
drh
|
7872 |
cli_printf(stderr,"extra argument: \"%s\"\n", z); |
|
70539ee…
|
drh
|
7873 |
if( newFlag && zFN && !p->bSafeMode ){ |
|
70539ee…
|
drh
|
7874 |
if( cli_strncmp(zFN,"file:",5)==0 ){ |
|
70539ee…
|
drh
|
7875 |
char *zDel = shellFilenameFromUri(zFN); |
|
70539ee…
|
drh
|
7876 |
shell_check_oom(zDel); |
|
70539ee…
|
drh
|
7877 |
shellDeleteFile(zDel); |
|
70539ee…
|
drh
|
7878 |
sqlite3_free(zDel); |
|
70539ee…
|
drh
|
7879 |
}else{ |
|
70539ee…
|
drh
|
7880 |
shellDeleteFile(zFN); |
|
70539ee…
|
drh
|
7881 |
} |
|
70539ee…
|
drh
|
7882 |
} |
|
4f76459…
|
drh
|
7883 |
cli_printf(stderr,"Error: cannot open '%s'\n", zNewFilename); |
|
4f76459…
|
drh
|
7884 |
rc = dotCmdOutput(p); |
|
4f76459…
|
drh
|
7885 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
7886 |
cli_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); |
|
4f76459…
|
drh
|
7887 |
if( i>1 ) cli_puts(" ", p->out); |
|
4f76459…
|
drh
|
7888 |
cli_puts(azArg[i], p->out); |
|
4f76459…
|
drh
|
7889 |
cli_puts("\n", p->out); |
|
7b0960d…
|
drh
|
7890 |
continue; |
|
7b0960d…
|
drh
|
7891 |
} |
|
7b0960d…
|
drh
|
7892 |
if( cli_strcmp(z,"timeout")==0 ){ |
|
7b0960d…
|
drh
|
7893 |
if( i==nArg-1 ){ |
|
7b0960d…
|
drh
|
7894 |
dotCmdError(p, i, "missing argument", 0); |
|
7b0960d…
|
drh
|
7895 |
return 1; |
|
7b0960d…
|
drh
|
7896 |
} |
|
7b0960d…
|
drh
|
7897 |
i++; |
|
a10f931…
|
drh
|
7898 |
p->tmProgress = atof(azArg[i]); |
|
7b0960d…
|
drh
|
7899 |
if( p->tmProgress>0.0 ){ |
|
7b0960d…
|
drh
|
7900 |
p->flgProgress = SHELL_PROGRESS_QUIET|SHELL_PROGRESS_TMOUT; |
|
7b0960d…
|
drh
|
7901 |
if( nn==0 ) nn = 100; |
|
7b0960d…
|
drh
|
7902 |
} |
|
2b2530d…
|
drh
|
7903 |
continue; |
|
2b2530d…
|
drh
|
7904 |
} |
|
4f76459…
|
drh
|
7905 |
cli_printf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); |
|
92871e0…
|
drh
|
7906 |
i64 savedLineno = p->lineno; |
|
4f76459…
|
drh
|
7907 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
|
92871e0…
|
drh
|
7908 |
rc = process_input(p, "<pipe>"); |
|
4f76459…
|
drh
|
7909 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); |
|
4f76459…
|
drh
|
7910 |
char *zFilename = strdup(azArg[1]); |
|
4f76459…
|
drh
|
7911 |
rc = process_input(p, zFilename); |
|
4f76459…
|
drh
|
7912 |
free(zFilename); |
|
4f76459…
|
drh
|
7913 |
cli_printf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); |
|
4f76459…
|
drh
|
7914 |
p->mode.scanstatsOn = 3; |
|
4f76459…
|
drh
|
7915 |
p->mode.scanstatsOn = 2; |
|
4f76459…
|
drh
|
7916 |
p->mode.scanstatsOn = (u8)booleanValue(azArg[1]); |
|
4f76459…
|
drh
|
7917 |
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0 |
|
4f76459…
|
drh
|
7918 |
if( p->mode.scanstatsOn==3 ){ |
|
4f76459…
|
drh
|
7919 |
int bIndent = 0; |
|
4f76459…
|
drh
|
7920 |
sqlite3_str *pSql; |
|
4f76459…
|
drh
|
7921 |
sqlite3_stmt *pStmt = 0; |
|
4f76459…
|
drh
|
7922 |
|
|
4f76459…
|
drh
|
7923 |
data.mode.spec.bTitles = QRF_No; |
|
4f76459…
|
drh
|
7924 |
data.mode.eMode = MODE_List; |
|
4f76459…
|
drh
|
7925 |
data.mode.spec.eText = QRF_TEXT_Plain; |
|
4f76459…
|
drh
|
7926 |
data.mode.spec.nCharLimit = 0; |
|
4f76459…
|
drh
|
7927 |
data.mode.spec.zRowSep = "\n"; |
|
4f76459…
|
drh
|
7928 |
bIndent = 1; |
|
4f76459…
|
drh
|
7929 |
cli_printf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); |
|
4f76459…
|
drh
|
7930 |
cli_printf(p->out, |
|
2b2530d…
|
drh
|
7931 |
"CREATE TABLE %ssqlite_schema (\n" |
|
2b2530d…
|
drh
|
7932 |
");\n", |
|
2b2530d…
|
drh
|
7933 |
sqlite3_strlike("sqlite_t%",zName,0)==0 ? "temp." : "" |
|
2b2530d…
|
drh
|
7934 |
); |
|
4f76459…
|
drh
|
7935 |
} |
|
4f76459…
|
drh
|
7936 |
} |
|
b9ecacf…
|
drh
|
7937 |
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); |
|
4f76459…
|
drh
|
7938 |
if( rc ){ |
|
4f76459…
|
drh
|
7939 |
shellDatabaseError(p->db); |
|
4f76459…
|
drh
|
7940 |
sqlite3_finalize(pStmt); |
|
4f76459…
|
drh
|
7941 |
|
|
4f76459…
|
drh
|
7942 |
rc = 1; |
|
4f76459…
|
drh
|
7943 |
goto meta_command_exit; |
|
4f76459…
|
drh
|
7944 |
} |
|
4f76459…
|
drh
|
7945 |
pSql = sqlite3_str_new(p->db); |
|
4f76459…
|
drh
|
7946 |
sqlite3_str_appendf(pSql, "SELECT sql FROM", 0); |
|
4f76459…
|
drh
|
7947 |
iSchema = 0; |
|
4f76459…
|
drh
|
7948 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
b9ecacf…
|
drh
|
7949 |
const char *zDb = (const char*)sqlite3_column_text(pStmt, 1); |
|
4f76459…
|
drh
|
7950 |
char zScNum[30]; |
|
4f76459…
|
drh
|
7951 |
sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); |
|
4f76459…
|
drh
|
7952 |
sqlite3_str_appendall(pSql, zDiv); |
|
4f76459…
|
drh
|
7953 |
zDiv = " UNION ALL "; |
|
4f76459…
|
drh
|
7954 |
if( sqlite3_stricmp(zDb, "main")==0 ){ |
|
4f76459…
|
drh
|
7955 |
sqlite3_str_appendf(pSql, |
|
4f76459…
|
drh
|
7956 |
"SELECT shell_format_schema(shell_add_schema(sql,NULL,name),%d)", |
|
4f76459…
|
drh
|
7957 |
bIndent); |
|
4f76459…
|
drh
|
7958 |
}else{ |
|
4f76459…
|
drh
|
7959 |
sqlite3_str_appendf(pSql, |
|
f07aa62…
|
drh
|
7960 |
"SELECT shell_format_schema(shell_add_schema(sql,%Q,name),%d)", |
|
4f76459…
|
drh
|
7961 |
zDb, bIndent); |
|
4f76459…
|
drh
|
7962 |
} |
|
4f76459…
|
drh
|
7963 |
sqlite3_str_appendf(pSql, |
|
4f76459…
|
drh
|
7964 |
" AS sql, type, tbl_name, name, rowid, %d AS snum, %Q as sname", |
|
4f76459…
|
drh
|
7965 |
++iSchema, zDb); |
|
4f76459…
|
drh
|
7966 |
sqlite3_str_appendf(pSql," FROM \"%w\".sqlite_schema", zDb); |
|
4f76459…
|
drh
|
7967 |
} |
|
4f76459…
|
drh
|
7968 |
sqlite3_finalize(pStmt); |
|
b9ecacf…
|
drh
|
7969 |
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) \ |
|
b9ecacf…
|
drh
|
7970 |
&& !defined(SQLITE_OMIT_VIRTUALTABLE) |
|
4f76459…
|
drh
|
7971 |
if( zName ){ |
|
4f76459…
|
drh
|
7972 |
sqlite3_str_appendall(pSql, |
|
4f76459…
|
drh
|
7973 |
" UNION ALL SELECT shell_module_schema(name)," |
|
4f76459…
|
drh
|
7974 |
" 'table', name, name, name, 9e+99, 'main' FROM pragma_module_list"); |
|
4f76459…
|
drh
|
7975 |
} |
|
4f76459…
|
drh
|
7976 |
#endif |
|
4f76459…
|
drh
|
7977 |
sqlite3_str_appendf(pSql, ") WHERE ", 0); |
|
4f76459…
|
drh
|
7978 |
if( zName ){ |
|
4f76459…
|
drh
|
7979 |
int bGlob; |
|
4f76459…
|
drh
|
7980 |
bGlob = strchr(zName, '*') != 0 || strchr(zName, '?') != 0 || |
|
4f76459…
|
drh
|
7981 |
strchr(zName, '[') != 0; |
|
4f76459…
|
drh
|
7982 |
if( strchr(zName, '.') ){ |
|
4f76459…
|
drh
|
7983 |
sqlite3_str_appendall(pSql, "lower(format('%%s.%%s',sname,tbl_name))"); |
|
4f76459…
|
drh
|
7984 |
}else{ |
|
4f76459…
|
drh
|
7985 |
sqlite3_str_appendall(pSql, "lower(tbl_name)"); |
|
4f76459…
|
drh
|
7986 |
} |
|
4f76459…
|
drh
|
7987 |
if( bGlob ){ |
|
4f76459…
|
drh
|
7988 |
sqlite3_str_appendf(pSql, " GLOB %Q AND ", zName); |
|
4f76459…
|
drh
|
7989 |
}else{ |
|
4f76459…
|
drh
|
7990 |
sqlite3_str_appendf(pSql, " LIKE %Q ESCAPE '\\' AND ", zName); |
|
4f76459…
|
drh
|
7991 |
} |
|
4f76459…
|
drh
|
7992 |
} |
|
4f76459…
|
drh
|
7993 |
if( bNoSystemTabs ){ |
|
2b2530d…
|
drh
|
7994 |
sqlite3_str_appendf(pSql, " name NOT LIKE 'sqlite__%%' ESCAPE '_' AND "); |
|
4f76459…
|
drh
|
7995 |
} |
|
4f76459…
|
drh
|
7996 |
sqlite3_str_appendf(pSql, "sql IS NOT NULL ORDER BY snum, rowid"); |
|
4f76459…
|
drh
|
7997 |
if( bDebug ){ |
|
4f76459…
|
drh
|
7998 |
cli_printf(p->out, "SQL: %s;\n", sqlite3_str_value(pSql)); |
|
4f76459…
|
drh
|
7999 |
}else{ |
|
4f76459…
|
drh
|
8000 |
rc = shell_exec(&data, sqlite3_str_value(pSql), &zErrMsg); |
|
4f76459…
|
drh
|
8001 |
} |
|
4f76459…
|
drh
|
8002 |
sqlite3_str_free(pSql); |
|
4f76459…
|
drh
|
8003 |
|
|
4f76459…
|
drh
|
8004 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8005 |
cli_printf(stderr,"ERROR: cannot open \"%s\" for writing\n", |
|
4f76459…
|
drh
|
8006 |
cli_printf(stdout, "Error: error code %d\n", rc); |
|
4f76459…
|
drh
|
8007 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8008 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
8009 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
8010 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
8011 |
cli_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); |
|
4f76459…
|
drh
|
8012 |
cli_printf(stderr,"Session \"%s\" already exists\n", zName); |
|
4f76459…
|
drh
|
8013 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8014 |
cli_printf(stderr,"Cannot open session: error code=%d\n", rc); |
|
4f76459…
|
drh
|
8015 |
cli_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); |
|
4f76459…
|
drh
|
8016 |
cli_puts(zBuf, p->out); |
|
4f76459…
|
drh
|
8017 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8018 |
cli_puts("Should be one of: --init -v\n", stderr); |
|
4f76459…
|
drh
|
8019 |
cli_printf(stdout, "%d: %s %s\n", tno, zOp, zSql); |
|
4f76459…
|
drh
|
8020 |
cli_printf(p->out, "%s\n", zSql); |
|
4f76459…
|
drh
|
8021 |
cli_printf(p->out, "Result: %s\n", str.zTxt); |
|
4f76459…
|
drh
|
8022 |
cli_printf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); |
|
4f76459…
|
drh
|
8023 |
cli_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); |
|
4f76459…
|
drh
|
8024 |
cli_printf(p->out, "%d: Got: [%s]\n", tno, str.zTxt); |
|
4f76459…
|
drh
|
8025 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8026 |
cli_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); |
|
4f76459…
|
drh
|
8027 |
modeSetStr(&p->mode.spec.zColumnSep, azArg[1]); |
|
4f76459…
|
drh
|
8028 |
modeSetStr(&p->mode.spec.zRowSep,azArg[2]); |
|
4f76459…
|
drh
|
8029 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8030 |
cli_printf(p->out, "%s\n", zSql); |
|
4f76459…
|
drh
|
8031 |
if( bDebug ) cli_printf(p->out, "%s\n", zRevText); |
|
4f76459…
|
drh
|
8032 |
if( bDebug ) cli_printf(p->out, "%s\n", zGenQuery); |
|
4f76459…
|
drh
|
8033 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8034 |
if( x ) cli_printf(stderr,"System command returns %d\n", x); |
|
4f76459…
|
drh
|
8035 |
cli_printf(p->out, "%12.12s: %s\n","echo", |
|
4f76459…
|
drh
|
8036 |
azBool[(p->mode.mFlags & MFLG_ECHO)!=0]); |
|
4f76459…
|
drh
|
8037 |
cli_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->mode.autoEQP&3]); |
|
4f76459…
|
drh
|
8038 |
cli_printf(p->out, "%12.12s: %s\n","explain", |
|
4f76459…
|
drh
|
8039 |
p->mode.autoExplain ? "auto" : "off"); |
|
4f76459…
|
drh
|
8040 |
cli_printf(p->out, "%12.12s: %s\n","headers", |
|
4f76459…
|
drh
|
8041 |
azBool[p->mode.spec.bTitles==QRF_Yes]); |
|
4f76459…
|
drh
|
8042 |
if( p->mode.spec.eStyle==QRF_STYLE_Column |
|
4f76459…
|
drh
|
8043 |
|| p->mode.spec.eStyle==QRF_STYLE_Box |
|
4f76459…
|
drh
|
8044 |
|| p->mode.spec.eStyle==QRF_STYLE_Table |
|
4f76459…
|
drh
|
8045 |
|| p->mode.spec.eStyle==QRF_STYLE_Markdown |
|
4f76459…
|
drh
|
8046 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
8047 |
aModeInfo[p->mode.eMode].zName, p->mode.spec.nWrap, |
|
4f76459…
|
drh
|
8048 |
p->mode.spec.bWordWrap==QRF_Yes ? "on" : "off", |
|
4f76459…
|
drh
|
8049 |
p->mode.spec.eText==QRF_TEXT_Sql ? "" : "no"); |
|
4f76459…
|
drh
|
8050 |
cli_printf(p->out, "%12.12s: %s\n","mode", |
|
4f76459…
|
drh
|
8051 |
aModeInfo[p->mode.eMode].zName); |
|
4f76459…
|
drh
|
8052 |
cli_printf(p->out, "%12.12s: ", "nullvalue"); |
|
4f76459…
|
drh
|
8053 |
output_c_string(p->out, p->mode.spec.zNull); |
|
4f76459…
|
drh
|
8054 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8055 |
cli_printf(p->out, "%12.12s: %s\n","output", |
|
4f76459…
|
drh
|
8056 |
cli_printf(p->out, "%12.12s: ", "colseparator"); |
|
4f76459…
|
drh
|
8057 |
output_c_string(p->out, p->mode.spec.zColumnSep); |
|
4f76459…
|
drh
|
8058 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8059 |
cli_printf(p->out, "%12.12s: ", "rowseparator"); |
|
4f76459…
|
drh
|
8060 |
output_c_string(p->out, p->mode.spec.zRowSep); |
|
4f76459…
|
drh
|
8061 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8062 |
cli_printf(p->out, "%12.12s: %s\n","stats", zOut); |
|
4f76459…
|
drh
|
8063 |
cli_printf(p->out, "%12.12s: ", "width"); |
|
4f76459…
|
drh
|
8064 |
for(i=0; i<p->mode.spec.nWidth; i++){ |
|
4f76459…
|
drh
|
8065 |
cli_printf(p->out, "%d ", (int)p->mode.spec.aWidth[i]); |
|
4f76459…
|
drh
|
8066 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8067 |
cli_printf(p->out, "%12.12s: %s\n", "filename", |
|
17f9878…
|
drh
|
8068 |
if( (c=='t' && n>1 && cli_strncmp(azArg[0], "tables", n)==0) ){ |
|
9aee493…
|
drh
|
8069 |
sqlite3_str *pSql; |
|
9aee493…
|
drh
|
8070 |
const char *zPattern = nArg>1 ? azArg[1] : 0; |
|
9aee493…
|
drh
|
8071 |
|
|
9aee493…
|
drh
|
8072 |
pSql = sqlite3_str_new(p->db); |
|
6ae9c9a…
|
drh
|
8073 |
while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
9aee493…
|
drh
|
8074 |
if( sqlite3_str_length(pSql) ){ |
|
9aee493…
|
drh
|
8075 |
sqlite3_str_appendall(pSql, " UNION ALL "); |
|
9aee493…
|
drh
|
8076 |
} |
|
9aee493…
|
drh
|
8077 |
sqlite3_str_appendall(pSql, "SELECT name FROM "); |
|
9aee493…
|
drh
|
8078 |
}else{ |
|
9aee493…
|
drh
|
8079 |
sqlite3_str_appendf(pSql, "SELECT %Q||'.'||name FROM ", zDbName); |
|
9aee493…
|
drh
|
8080 |
} |
|
9aee493…
|
drh
|
8081 |
sqlite3_str_appendf(pSql, "\"%w\".sqlite_schema", zDbName); |
|
17f9878…
|
drh
|
8082 |
sqlite3_str_appendf(pSql, |
|
17f9878…
|
drh
|
8083 |
" WHERE type IN ('table','view')" |
|
17f9878…
|
drh
|
8084 |
" AND name NOT LIKE 'sqlite__%%' ESCAPE '_'" |
|
17f9878…
|
drh
|
8085 |
); |
|
17f9878…
|
drh
|
8086 |
if( zPattern ){ |
|
17f9878…
|
drh
|
8087 |
sqlite3_str_appendf(pSql," AND name LIKE %Q", zPattern); |
|
9aee493…
|
drh
|
8088 |
sqlite3_str_appendall(pSql, " ORDER BY 1"); |
|
9aee493…
|
drh
|
8089 |
|
|
9aee493…
|
drh
|
8090 |
/* Run the SQL statement in "split" mode. */ |
|
9aee493…
|
drh
|
8091 |
modePush(p); |
|
9aee493…
|
drh
|
8092 |
modeChange(p, MODE_Split); |
|
9aee493…
|
drh
|
8093 |
shell_exec(p, sqlite3_str_value(pSql), 0); |
|
9aee493…
|
drh
|
8094 |
sqlite3_str_free(pSql); |
|
9aee493…
|
drh
|
8095 |
modePop(p); |
|
5f65ed5…
|
drh
|
8096 |
/* Set the p->zTestcase name and begin redirecting output into |
|
5f65ed5…
|
drh
|
8097 |
** the cli_output_capture sqlite3_str */ |
|
5f65ed5…
|
drh
|
8098 |
rc = dotCmdTestcase(p); |
|
4f76459…
|
drh
|
8099 |
cli_puts("Available test-controls:\n", p->out); |
|
4f76459…
|
drh
|
8100 |
cli_printf(p->out, " .testctrl %s %s\n", |
|
4f76459…
|
drh
|
8101 |
cli_printf(stderr,"Error: ambiguous test-control: \"%s\"\n" |
|
4f76459…
|
drh
|
8102 |
cli_printf(stderr,"Error: unknown test-control: %s\n" |
|
4f76459…
|
drh
|
8103 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8104 |
cli_puts("Should be one of:", stderr); |
|
4f76459…
|
drh
|
8105 |
cli_printf(stderr," %s", aLabel[jj].zLabel); |
|
4f76459…
|
drh
|
8106 |
cli_puts("\n", stderr); |
|
4f76459…
|
drh
|
8107 |
cli_puts("+All", p->out); |
|
4f76459…
|
drh
|
8108 |
cli_printf(p->out, " -%s", aLabel[ii].zLabel); |
|
4f76459…
|
drh
|
8109 |
cli_puts("-All", p->out); |
|
4f76459…
|
drh
|
8110 |
cli_printf(p->out, " +%s", aLabel[ii].zLabel); |
|
4f76459…
|
drh
|
8111 |
cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8112 |
cli_printf(stdout, "-- random seed: %d\n", ii); |
|
4f76459…
|
drh
|
8113 |
cli_printf(p->out, "%llu\n", x); |
|
4f76459…
|
drh
|
8114 |
if( id>1 ) cli_puts(" ", p->out); |
|
4f76459…
|
drh
|
8115 |
cli_printf(p->out, "%d: %d", id, val); |
|
4f76459…
|
drh
|
8116 |
if( id>1 ) cli_puts("\n", p->out); |
|
4f76459…
|
drh
|
8117 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8118 |
cli_printf(p->out, "result: %d\n", x); |
|
4f76459…
|
drh
|
8119 |
cli_printf(p->out, "faultsim.iId: %d\n", |
|
4f76459…
|
drh
|
8120 |
cli_printf(p->out, "faultsim.iErr: %d\n", |
|
4f76459…
|
drh
|
8121 |
cli_printf(p->out, "faultsim.iCnt: %d\n", |
|
4f76459…
|
drh
|
8122 |
cli_printf(p->out, "faultsim.nHit: %d\n", |
|
4f76459…
|
drh
|
8123 |
cli_printf(p->out, "faultsim.iInterval: %d\n", |
|
4f76459…
|
drh
|
8124 |
cli_printf(p->out, "faultsim.eVerbose: %d\n", |
|
4f76459…
|
drh
|
8125 |
cli_printf(p->out, "faultsim.nRepeat: %d\n", |
|
4f76459…
|
drh
|
8126 |
cli_printf(p->out, "faultsim.nSkip: %d\n", |
|
4f76459…
|
drh
|
8127 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8128 |
cli_puts( |
|
4f76459…
|
drh
|
8129 |
cli_printf(p->out, |
|
4f76459…
|
drh
|
8130 |
cli_printf(p->out, "%d\n", rc2); |
|
4f76459…
|
drh
|
8131 |
cli_printf(p->out, "0x%08x\n", rc2); |
|
7b0960d…
|
drh
|
8132 |
if( cli_strcmp(azArg[1],"once")==0 ){ |
|
7b0960d…
|
drh
|
8133 |
p->enableTimer = 1; |
|
7b0960d…
|
drh
|
8134 |
}else{ |
|
7b0960d…
|
drh
|
8135 |
p->enableTimer = 2*booleanValue(azArg[1]); |
|
7b0960d…
|
drh
|
8136 |
} |
|
7b0960d…
|
drh
|
8137 |
if( p->enableTimer && !HAS_TIMER ){ |
|
7b0960d…
|
drh
|
8138 |
p->enableTimer = 0; |
|
7b0960d…
|
drh
|
8139 |
eputz("Usage: .timer on|off|once\n"); |
|
4f76459…
|
drh
|
8140 |
cli_printf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); |
|
4f76459…
|
drh
|
8141 |
p->traceOut = output_file_open(p, z); |
|
4f76459…
|
drh
|
8142 |
cli_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/, |
|
4f76459…
|
drh
|
8143 |
cli_printf(p->out, "zlib version %s\n", zlibVersion()); |
|
4f76459…
|
drh
|
8144 |
cli_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." |
|
4f76459…
|
drh
|
8145 |
cli_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); |
|
4f76459…
|
drh
|
8146 |
cli_printf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); |
|
4f76459…
|
drh
|
8147 |
cli_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); |
|
4f76459…
|
drh
|
8148 |
cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
|
4f76459…
|
drh
|
8149 |
cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
|
4f76459…
|
drh
|
8150 |
cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
|
4f76459…
|
drh
|
8151 |
cli_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, |
|
4f76459…
|
drh
|
8152 |
cli_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); |
|
4f76459…
|
drh
|
8153 |
cli_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); |
|
4f76459…
|
drh
|
8154 |
cli_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); |
|
4f76459…
|
drh
|
8155 |
cli_puts("-----------------------------------\n", p->out); |
|
4f76459…
|
drh
|
8156 |
cli_printf(p->out, "%s\n", zVfsName); |
|
4f76459…
|
drh
|
8157 |
p->mode.spec.nWidth = nArg-1; |
|
4f76459…
|
drh
|
8158 |
p->mode.spec.aWidth = realloc(p->mode.spec.aWidth, |
|
4f76459…
|
drh
|
8159 |
(p->mode.spec.nWidth+1)*sizeof(short int)); |
|
4f76459…
|
drh
|
8160 |
shell_check_oom(p->mode.spec.aWidth); |
|
70539ee…
|
drh
|
8161 |
i64 w = integerValue(azArg[j]); |
|
4f76459…
|
drh
|
8162 |
if( w < -QRF_MAX_WIDTH ) w = -QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
8163 |
if( w > QRF_MAX_WIDTH ) w = QRF_MAX_WIDTH; |
|
4f76459…
|
drh
|
8164 |
p->mode.spec.aWidth[j-1] = (short int)w; |
|
4f76459…
|
drh
|
8165 |
cli_printf(stderr,"Error: unknown command or invalid arguments: " |
|
4f76459…
|
drh
|
8166 |
if( p->nPopOutput ){ |
|
4f76459…
|
drh
|
8167 |
p->nPopOutput--; |
|
4f76459…
|
drh
|
8168 |
if( p->nPopOutput==0 ) output_reset(p); |
|
7b0960d…
|
drh
|
8169 |
p->dot.nArg = 0; |
|
17f9878…
|
drh
|
8170 |
static int runOneSqlLine( |
|
17f9878…
|
drh
|
8171 |
ShellState *p, /* Execution context */ |
|
17f9878…
|
drh
|
8172 |
char *zSql, /* SQL to be run */ |
|
17f9878…
|
drh
|
8173 |
const char *zFilename, /* Source file of the sql */ |
|
17f9878…
|
drh
|
8174 |
int startline /* linenumber */ |
|
17f9878…
|
drh
|
8175 |
){ |
|
7b0960d…
|
drh
|
8176 |
BEGIN_TIMER(p); |
|
7b0960d…
|
drh
|
8177 |
END_TIMER(p); |
|
17f9878…
|
drh
|
8178 |
if( zFilename || !stdin_is_interactive ){ |
|
17f9878…
|
drh
|
8179 |
if( cli_strcmp(zFilename,"cmdline")==0 ){ |
|
17f9878…
|
drh
|
8180 |
sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
|
17f9878…
|
drh
|
8181 |
"%s in %r command line argument:", zErrorType, startline); |
|
17f9878…
|
drh
|
8182 |
}else if( cli_strcmp(zFilename,"<stdin>")==0 ){ |
|
17f9878…
|
drh
|
8183 |
sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
|
17f9878…
|
drh
|
8184 |
"%s near line %d:", zErrorType, startline); |
|
17f9878…
|
drh
|
8185 |
}else{ |
|
17f9878…
|
drh
|
8186 |
sqlite3_snprintf(sizeof(zPrefix), zPrefix, |
|
17f9878…
|
drh
|
8187 |
"%s near line %d of %s:", zErrorType, startline, zFilename); |
|
17f9878…
|
drh
|
8188 |
} |
|
4f76459…
|
drh
|
8189 |
cli_printf(stderr,"%s %s\n", zPrefix, zErrorTail); |
|
4f76459…
|
drh
|
8190 |
cli_printf(p->out, "%s\n", zLineBuf); |
|
4f76459…
|
drh
|
8191 |
if( p->mode.mFlags & MFLG_ECHO ){ |
|
4f76459…
|
drh
|
8192 |
cli_printf(p->out, "%s\n", zDo); |
|
a10f931…
|
drh
|
8193 |
static char *one_input_line(ShellState *p, char *zPrior, int isContinuation){ |
|
a10f931…
|
drh
|
8194 |
FILE *in = p->in; |
|
92871e0…
|
drh
|
8195 |
static int process_input(ShellState *p, const char *zSrc){ |
|
4f76459…
|
drh
|
8196 |
const char *saved_zInFile; /* Prior value of p->zInFile */ |
|
4f76459…
|
drh
|
8197 |
i64 saved_lineno; /* Prior value of p->lineno */ |
|
4f76459…
|
drh
|
8198 |
cli_printf(stderr,"%s: Input nesting limit (%d) reached at line %lld." |
|
92871e0…
|
drh
|
8199 |
" Check recursion.\n", zSrc, MAX_INPUT_NESTING, p->lineno); |
|
4f76459…
|
drh
|
8200 |
saved_zInFile = p->zInFile; |
|
4f76459…
|
drh
|
8201 |
p->zInFile = zSrc; |
|
4f76459…
|
drh
|
8202 |
saved_lineno = p->lineno; |
|
a10f931…
|
drh
|
8203 |
zLine = one_input_line(p, zLine, nSql>0); |
|
4f76459…
|
drh
|
8204 |
if( p->in==0 && stdin_is_interactive ) cli_puts("\n", p->out); |
|
92871e0…
|
drh
|
8205 |
if( nSql>0x7fff0000 ){ |
|
92871e0…
|
drh
|
8206 |
char zSize[100]; |
|
92871e0…
|
drh
|
8207 |
sqlite3_snprintf(sizeof(zSize),zSize,"%,lld",nSql); |
|
4f76459…
|
drh
|
8208 |
cli_printf(stderr, "%s:%lld: Input SQL is too big: %s bytes\n", |
|
92871e0…
|
drh
|
8209 |
zSrc, startline, zSize); |
|
92871e0…
|
drh
|
8210 |
nSql = 0; |
|
92871e0…
|
drh
|
8211 |
errCnt++; |
|
92871e0…
|
drh
|
8212 |
break; |
|
92871e0…
|
drh
|
8213 |
}else if( nSql && QSS_SEMITERM(qss) && sqlite3_complete(zSql) ){ |
|
17f9878…
|
drh
|
8214 |
errCnt += runOneSqlLine(p, zSql, p->zInFile, startline); |
|
4f76459…
|
drh
|
8215 |
if( p->nPopOutput ){ |
|
4f76459…
|
drh
|
8216 |
p->nPopOutput = 0; |
|
4f76459…
|
drh
|
8217 |
} |
|
4f76459…
|
drh
|
8218 |
if( p->nPopMode ){ |
|
4f76459…
|
drh
|
8219 |
modePop(p); |
|
4f76459…
|
drh
|
8220 |
p->nPopMode = 0; |
|
17f9878…
|
drh
|
8221 |
errCnt += runOneSqlLine(p, zSql, p->zInFile, startline); |
|
4f76459…
|
drh
|
8222 |
p->zInFile = saved_zInFile; |
|
4f76459…
|
drh
|
8223 |
p->lineno = saved_lineno; |
|
92871e0…
|
drh
|
8224 |
i64 savedLineno = p->lineno; |
|
4f76459…
|
drh
|
8225 |
cli_printf(stderr,"-- Loading resources from %s\n", sqliterc); |
|
4f76459…
|
drh
|
8226 |
if( process_input(p, sqliterc) && bail_on_error ) cli_exit(1); |
|
4f76459…
|
drh
|
8227 |
cli_printf(stderr,"cannot open: \"%s\"\n", sqliterc); |
|
4f76459…
|
drh
|
8228 |
if( bail_on_error ) cli_exit(1); |
|
4f76459…
|
drh
|
8229 |
" -column set output mode to 'column'\n" |
|
4f76459…
|
drh
|
8230 |
" -noinit Do not read the ~/.sqliterc file at startup\n" |
|
4f76459…
|
drh
|
8231 |
" -screenwidth N use N as the default screenwidth \n" |
|
4f76459…
|
drh
|
8232 |
cli_printf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" |
|
4f76459…
|
drh
|
8233 |
cli_printf(stderr,"OPTIONS include:\n%s", zOptions); |
|
4f76459…
|
drh
|
8234 |
static void main_init(ShellState *p) { |
|
4f76459…
|
drh
|
8235 |
memset(p, 0, sizeof(*p)); |
|
4f76459…
|
drh
|
8236 |
p->pAuxDb = &p->aAuxDb[0]; |
|
4f76459…
|
drh
|
8237 |
p->shellFlgs = SHFLG_Lookaside; |
|
4f76459…
|
drh
|
8238 |
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, p); |
|
4f76459…
|
drh
|
8239 |
cli_printf(stdout, "\033[1m%s\033[0m", zText); |
|
4f76459…
|
drh
|
8240 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8241 |
cli_exit(1); |
|
4f76459…
|
drh
|
8242 |
cli_puts(z, p->out); |
|
0201a1e…
|
drh
|
8243 |
#if defined(SQLITE_DEBUG) && !defined(SQLITE_SHELL_FIDDLE) |
|
0201a1e…
|
drh
|
8244 |
/* Ensure that sqlite3_reset_auto_extension() clears auto-extension |
|
0201a1e…
|
drh
|
8245 |
** memory. https://sqlite.org/forum/forumpost/310cb231b07c80d1. |
|
0201a1e…
|
drh
|
8246 |
** Testing this: if we (inadvertently) remove the |
|
0201a1e…
|
drh
|
8247 |
** sqlite3_reset_auto_extension() call from main(), most shell tests |
|
0201a1e…
|
drh
|
8248 |
** will fail because of a leak message in their output. */ |
|
0201a1e…
|
drh
|
8249 |
static int auto_ext_leak_tester( |
|
0201a1e…
|
drh
|
8250 |
sqlite3 *db, const char **pzErrMsg, |
|
0201a1e…
|
drh
|
8251 |
const struct sqlite3_api_routines *pThunk |
|
0201a1e…
|
drh
|
8252 |
){ |
|
0201a1e…
|
drh
|
8253 |
(void)db; (void)pzErrMsg; (void)pThunk; |
|
0201a1e…
|
drh
|
8254 |
return SQLITE_OK; |
|
0201a1e…
|
drh
|
8255 |
} |
|
0201a1e…
|
drh
|
8256 |
#endif |
|
0201a1e…
|
drh
|
8257 |
|
|
b9ecacf…
|
drh
|
8258 |
/* Alternative name to the entry point for Fiddle */ |
|
b9ecacf…
|
drh
|
8259 |
/* Use the wmain() entry point on Windows. Translate arguments to |
|
b9ecacf…
|
drh
|
8260 |
** UTF8, then invoke the traditional main() entry point which is |
|
b9ecacf…
|
drh
|
8261 |
** renamed using a #define to utf8_main() . |
|
b9ecacf…
|
drh
|
8262 |
*/ |
|
17f9878…
|
drh
|
8263 |
#if defined(_WIN32) && !defined(__MINGW32__) && !defined(main) |
|
b9ecacf…
|
drh
|
8264 |
# define main utf8_main /* Rename entry point to utf_main() */ |
|
b9ecacf…
|
drh
|
8265 |
int SQLITE_CDECL utf8_main(int,char**); /* Forward declaration */ |
|
b9ecacf…
|
drh
|
8266 |
int rc, i; |
|
b9ecacf…
|
drh
|
8267 |
char **argv = malloc( sizeof(char*) * (argc+1) ); |
|
b9ecacf…
|
drh
|
8268 |
char **orig = argv; |
|
b9ecacf…
|
drh
|
8269 |
if( argv==0 ){ |
|
b9ecacf…
|
drh
|
8270 |
fprintf(stderr, "malloc failed\n"); |
|
b9ecacf…
|
drh
|
8271 |
exit(1); |
|
b9ecacf…
|
drh
|
8272 |
} |
|
b9ecacf…
|
drh
|
8273 |
for(i=0; i<argc; i++){ |
|
b9ecacf…
|
drh
|
8274 |
int nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0); |
|
b9ecacf…
|
drh
|
8275 |
if( nByte==0 ){ |
|
b9ecacf…
|
drh
|
8276 |
argv[i] = 0; |
|
b9ecacf…
|
drh
|
8277 |
}else{ |
|
b9ecacf…
|
drh
|
8278 |
argv[i] = malloc( nByte ); |
|
b9ecacf…
|
drh
|
8279 |
if( argv[i]==0 ){ |
|
b9ecacf…
|
drh
|
8280 |
fprintf(stderr, "malloc failed\n"); |
|
b9ecacf…
|
drh
|
8281 |
exit(1); |
|
b9ecacf…
|
drh
|
8282 |
} |
|
b9ecacf…
|
drh
|
8283 |
nByte = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i],nByte,0,0); |
|
b9ecacf…
|
drh
|
8284 |
if( nByte==0 ){ |
|
b9ecacf…
|
drh
|
8285 |
free(argv[i]); |
|
b9ecacf…
|
drh
|
8286 |
argv[i] = 0; |
|
b9ecacf…
|
drh
|
8287 |
} |
|
b9ecacf…
|
drh
|
8288 |
} |
|
b9ecacf…
|
drh
|
8289 |
} |
|
b9ecacf…
|
drh
|
8290 |
argv[argc] = 0; |
|
b9ecacf…
|
drh
|
8291 |
rc = utf8_main(argc, argv); |
|
b9ecacf…
|
drh
|
8292 |
for(i=0; i<argc; i++) free(orig[i]); |
|
b9ecacf…
|
drh
|
8293 |
free(argv); |
|
b9ecacf…
|
drh
|
8294 |
return rc; |
|
b9ecacf…
|
drh
|
8295 |
} |
|
b9ecacf…
|
drh
|
8296 |
#endif /* WIN32 */ |
|
b9ecacf…
|
drh
|
8297 |
|
|
b9ecacf…
|
drh
|
8298 |
/* |
|
b9ecacf…
|
drh
|
8299 |
** This is the main entry point for the process. Everything starts here. |
|
b9ecacf…
|
drh
|
8300 |
** |
|
b9ecacf…
|
drh
|
8301 |
** The "main" identifier may have been #defined to something else: |
|
b9ecacf…
|
drh
|
8302 |
** |
|
b9ecacf…
|
drh
|
8303 |
** utf8_main On Windows |
|
b9ecacf…
|
drh
|
8304 |
** fiddle_main In Fiddle |
|
b9ecacf…
|
drh
|
8305 |
** sqlite3_shell Other projects that use shell.c as a subroutine |
|
b9ecacf…
|
drh
|
8306 |
*/ |
|
b9ecacf…
|
drh
|
8307 |
int SQLITE_CDECL main(int argc, char **argv){ |
|
4f76459…
|
drh
|
8308 |
int noInit = 0; /* Do not read ~/.sqliterc if true */ |
|
4f76459…
|
drh
|
8309 |
int *aiCmd = 0; |
|
4f76459…
|
drh
|
8310 |
cli_printf(stderr, |
|
4f76459…
|
drh
|
8311 |
cli_printf(stderr, |
|
5f65ed5…
|
drh
|
8312 |
/* Do an initial pass through the command-line arguments to locate |
|
5f65ed5…
|
drh
|
8313 |
if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){ |
|
4f76459…
|
drh
|
8314 |
stdin_is_interactive = 0; |
|
4f76459…
|
drh
|
8315 |
aiCmd = realloc(aiCmd, sizeof(aiCmd[0])*nCmd); |
|
4f76459…
|
drh
|
8316 |
shell_check_oom(azCmd); |
|
4f76459…
|
drh
|
8317 |
aiCmd[nCmd-1] = i; |
|
4f76459…
|
drh
|
8318 |
stdout_is_console = 0; |
|
4f76459…
|
drh
|
8319 |
modeChange(&data, MODE_BATCH); |
|
4f76459…
|
drh
|
8320 |
}else if( cli_strcmp(z,"-screenwidth")==0 ){ |
|
4f76459…
|
drh
|
8321 |
int n = atoi(cmdline_option_value(argc, argv, ++i)); |
|
4f76459…
|
drh
|
8322 |
if( n<2 ){ |
|
4f76459…
|
drh
|
8323 |
sqlite3_fprintf(stderr,"minimum --screenwidth is 2\n"); |
|
4f76459…
|
drh
|
8324 |
exit(1); |
|
4f76459…
|
drh
|
8325 |
} |
|
4f76459…
|
drh
|
8326 |
stdout_tty_width = n; |
|
4f76459…
|
drh
|
8327 |
cli_printf(stdout, "Page cache size increased to %d to accommodate" |
|
70539ee…
|
drh
|
8328 |
if( (i64)sz*(i64)n==0 ) data.shellFlgs &= ~SHFLG_Lookaside; |
|
4f76459…
|
drh
|
8329 |
}else if( cli_strcmp(z,"-noinit")==0 ){ |
|
4f76459…
|
drh
|
8330 |
noInit = 1; |
|
b9ecacf…
|
drh
|
8331 |
}else if( cli_strcmp(z,"-test-argv")==0 ){ |
|
b9ecacf…
|
drh
|
8332 |
/* Undocumented test option. Print the values in argv[] and exit. |
|
b9ecacf…
|
drh
|
8333 |
** Use this to verify that any translation of the argv[], for example |
|
b9ecacf…
|
drh
|
8334 |
** on Windows that receives wargv[] from the OS and must convert |
|
b9ecacf…
|
drh
|
8335 |
** to UTF8 prior to calling this routine. */ |
|
b9ecacf…
|
drh
|
8336 |
int kk; |
|
b9ecacf…
|
drh
|
8337 |
for(kk=0; kk<argc; kk++){ |
|
b9ecacf…
|
drh
|
8338 |
sqlite3_fprintf(stdout,"argv[%d] = \"%s\"\n", kk, argv[kk]); |
|
b9ecacf…
|
drh
|
8339 |
} |
|
b9ecacf…
|
drh
|
8340 |
return 0; |
|
2b2530d…
|
drh
|
8341 |
} |
|
2b2530d…
|
drh
|
8342 |
#if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
|
2b2530d…
|
drh
|
8343 |
else if( access(zVfs,0)==0 ){ |
|
2b2530d…
|
drh
|
8344 |
/* If the VFS name is not the name of an existing VFS, but it is |
|
2b2530d…
|
drh
|
8345 |
** the name of a file, then try to load that file as an extension. |
|
2b2530d…
|
drh
|
8346 |
** Presumably the extension implements the desired VFS. */ |
|
2b2530d…
|
drh
|
8347 |
sqlite3 *db = 0; |
|
2b2530d…
|
drh
|
8348 |
char *zErr = 0; |
|
2b2530d…
|
drh
|
8349 |
sqlite3_open(":memory:", &db); |
|
2b2530d…
|
drh
|
8350 |
sqlite3_enable_load_extension(db, 1); |
|
2b2530d…
|
drh
|
8351 |
rc = sqlite3_load_extension(db, zVfs, 0, &zErr); |
|
2b2530d…
|
drh
|
8352 |
sqlite3_close(db); |
|
2b2530d…
|
drh
|
8353 |
if( (rc&0xff)!=SQLITE_OK ){ |
|
2b2530d…
|
drh
|
8354 |
cli_printf(stderr, "could not load extension VFS \"%s\": %s\n", |
|
2b2530d…
|
drh
|
8355 |
zVfs, zErr); |
|
2b2530d…
|
drh
|
8356 |
exit(1); |
|
2b2530d…
|
drh
|
8357 |
} |
|
2b2530d…
|
drh
|
8358 |
} |
|
2b2530d…
|
drh
|
8359 |
#endif |
|
2b2530d…
|
drh
|
8360 |
else{ |
|
4f76459…
|
drh
|
8361 |
cli_printf(stderr,"no such VFS: \"%s\"\n", zVfs); |
|
4f76459…
|
drh
|
8362 |
cli_printf(stderr, |
|
f4b3b59…
|
drh
|
8363 |
rc = 1; |
|
f4b3b59…
|
drh
|
8364 |
goto shell_main_exit; |
|
0201a1e…
|
drh
|
8365 |
#ifdef SQLITE_DEBUG |
|
0201a1e…
|
drh
|
8366 |
sqlite3_auto_extension( (void (*)())auto_ext_leak_tester ); |
|
0201a1e…
|
drh
|
8367 |
#endif |
|
4f76459…
|
drh
|
8368 |
modeDefault(&data); |
|
4f76459…
|
drh
|
8369 |
if( !noInit ) process_sqliterc(&data,zInitFile); |
|
5f65ed5…
|
drh
|
8370 |
/* Make a second pass through the command-line arguments and set |
|
4f76459…
|
drh
|
8371 |
modeChange(&data, MODE_Html); |
|
4f76459…
|
drh
|
8372 |
modeChange(&data, MODE_List); |
|
4f76459…
|
drh
|
8373 |
modeChange(&data, MODE_Quote); |
|
4f76459…
|
drh
|
8374 |
modeChange(&data, MODE_Line); |
|
4f76459…
|
drh
|
8375 |
modeChange(&data, MODE_Column); |
|
4f76459…
|
drh
|
8376 |
modeChange(&data, MODE_Json); |
|
4f76459…
|
drh
|
8377 |
modeChange(&data, MODE_Markdown); |
|
4f76459…
|
drh
|
8378 |
modeChange(&data, MODE_Table); |
|
f4b3b59…
|
drh
|
8379 |
}else if( cli_strcmp(z,"-psql")==0 ){ |
|
f4b3b59…
|
drh
|
8380 |
modeChange(&data, MODE_Psql); |
|
4f76459…
|
drh
|
8381 |
modeChange(&data, MODE_Box); |
|
4f76459…
|
drh
|
8382 |
modeChange(&data, MODE_Csv); |
|
4f76459…
|
drh
|
8383 |
for(k=0; k<ArraySize(qrfEscNames); k++){ |
|
4f76459…
|
drh
|
8384 |
if( sqlite3_stricmp(zEsc,qrfEscNames[k])==0 ){ |
|
4f76459…
|
drh
|
8385 |
data.mode.spec.eEsc = k; |
|
4f76459…
|
drh
|
8386 |
if( k>=ArraySize(qrfEscNames) ){ |
|
4f76459…
|
drh
|
8387 |
cli_printf(stderr, "unknown control character escape mode \"%s\"" |
|
4f76459…
|
drh
|
8388 |
for(k=0; k<ArraySize(qrfEscNames); k++){ |
|
4f76459…
|
drh
|
8389 |
cli_printf(stderr, " %s", qrfEscNames[k]); |
|
4f76459…
|
drh
|
8390 |
cli_printf(stderr, "\n"); |
|
4f76459…
|
drh
|
8391 |
}else if( cli_strcmp(z,"-noinit")==0 ){ |
|
4f76459…
|
drh
|
8392 |
/* No-op */ |
|
4f76459…
|
drh
|
8393 |
modeChange(&data, MODE_Ascii); |
|
4f76459…
|
drh
|
8394 |
modeChange(&data, MODE_Tabs); |
|
4f76459…
|
drh
|
8395 |
modeSetStr(&data.mode.spec.zColumnSep, |
|
4f76459…
|
drh
|
8396 |
cmdline_option_value(argc,argv,++i)); |
|
4f76459…
|
drh
|
8397 |
modeSetStr(&data.mode.spec.zRowSep, |
|
4f76459…
|
drh
|
8398 |
cmdline_option_value(argc,argv,++i)); |
|
4f76459…
|
drh
|
8399 |
modeSetStr(&data.mode.spec.zNull, |
|
4f76459…
|
drh
|
8400 |
cmdline_option_value(argc,argv,++i)); |
|
4f76459…
|
drh
|
8401 |
data.mode.spec.bTitles = QRF_Yes; |
|
4f76459…
|
drh
|
8402 |
data.mode.spec.bTitles = QRF_No; |
|
4f76459…
|
drh
|
8403 |
data.mode.mFlags |= MFLG_ECHO; |
|
4f76459…
|
drh
|
8404 |
data.mode.autoEQP = AUTOEQP_on; |
|
4f76459…
|
drh
|
8405 |
data.mode.autoEQP = AUTOEQP_full; |
|
4f76459…
|
drh
|
8406 |
data.mode.scanstatsOn = 1; |
|
4f76459…
|
drh
|
8407 |
cli_printf(stdout, "%s %s (%d-bit)\n", |
|
f4b3b59…
|
drh
|
8408 |
rc = 0; |
|
f4b3b59…
|
drh
|
8409 |
goto shell_main_exit; |
|
4f76459…
|
drh
|
8410 |
}else if( cli_strcmp(z,"-screenwidth")==0 ){ |
|
4f76459…
|
drh
|
8411 |
i++; |
|
b8ab8b3…
|
drh
|
8412 |
if( rc && (bail_on_error || rc==2) ){ |
|
f4b3b59…
|
drh
|
8413 |
if( rc==2 ) rc = 0; |
|
f4b3b59…
|
drh
|
8414 |
goto shell_main_exit; |
|
f4b3b59…
|
drh
|
8415 |
} |
|
17f9878…
|
drh
|
8416 |
rc = runOneSqlLine(&data, z, "cmdline", i); |
|
f4b3b59…
|
drh
|
8417 |
if( bail_on_error ) goto shell_main_exit; |
|
4f76459…
|
drh
|
8418 |
cli_printf(stderr,"Error: cannot mix regular SQL or dot-commands" |
|
f4b3b59…
|
drh
|
8419 |
rc = 1; |
|
f4b3b59…
|
drh
|
8420 |
goto shell_main_exit; |
|
4f76459…
|
drh
|
8421 |
stdin_is_interactive = 0; |
|
4f76459…
|
drh
|
8422 |
cli_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); |
|
f4b3b59…
|
drh
|
8423 |
rc = 1; |
|
f4b3b59…
|
drh
|
8424 |
goto shell_main_exit; |
|
5f65ed5…
|
drh
|
8425 |
if( isScriptFile(azCmd[i],0) ){ |
|
5f65ed5…
|
drh
|
8426 |
FILE *inSaved = data.in; |
|
5f65ed5…
|
drh
|
8427 |
i64 savedLineno = data.lineno; |
|
5f65ed5…
|
drh
|
8428 |
int res = 1; |
|
5f65ed5…
|
drh
|
8429 |
if( (data.in = openChrSource(azCmd[i]))!=0 ){ |
|
5f65ed5…
|
drh
|
8430 |
res = process_input(&data, azCmd[i]); |
|
5f65ed5…
|
drh
|
8431 |
fclose(data.in); |
|
5f65ed5…
|
drh
|
8432 |
} |
|
5f65ed5…
|
drh
|
8433 |
data.in = inSaved; |
|
5f65ed5…
|
drh
|
8434 |
data.lineno = savedLineno; |
|
5f65ed5…
|
drh
|
8435 |
if( res ) i = nCmd; |
|
5f65ed5…
|
drh
|
8436 |
}else if( azCmd[i][0]=='.' ){ |
|
4f76459…
|
drh
|
8437 |
char *zErrCtx = malloc( 64 ); |
|
4f76459…
|
drh
|
8438 |
shell_check_oom(zErrCtx); |
|
4f76459…
|
drh
|
8439 |
sqlite3_snprintf(64,zErrCtx,"argv[%i]:",aiCmd[i]); |
|
4f76459…
|
drh
|
8440 |
data.zInFile = "<cmdline>"; |
|
4f76459…
|
drh
|
8441 |
data.zErrPrefix = zErrCtx; |
|
4f76459…
|
drh
|
8442 |
free(data.zErrPrefix); |
|
4f76459…
|
drh
|
8443 |
data.zErrPrefix = 0; |
|
17f9878…
|
drh
|
8444 |
rc = runOneSqlLine(&data, azCmd[i], "cmdline", aiCmd[i]); |
|
9aee493…
|
drh
|
8445 |
if( data.nPopMode ){ |
|
9aee493…
|
drh
|
8446 |
modePop(&data); |
|
9aee493…
|
drh
|
8447 |
data.nPopMode = 0; |
|
9aee493…
|
drh
|
8448 |
} |
|
9aee493…
|
drh
|
8449 |
} |
|
17f9878…
|
drh
|
8450 |
if( data.nPopOutput && azCmd[i][0]!='.' ){ |
|
9aee493…
|
drh
|
8451 |
output_reset(&data); |
|
9aee493…
|
drh
|
8452 |
data.nPopOutput = 0; |
|
9aee493…
|
drh
|
8453 |
}else{ |
|
9aee493…
|
drh
|
8454 |
clearTempFile(&data); |
|
4f76459…
|
drh
|
8455 |
cli_printf(stdout, |
|
92871e0…
|
drh
|
8456 |
rc = process_input(&data, "<stdin>"); |
|
92871e0…
|
drh
|
8457 |
rc = process_input(&data, "<stdin>"); |
|
4f76459…
|
drh
|
8458 |
free(aiCmd); |
|
4f76459…
|
drh
|
8459 |
modeFree(&data.mode); |
|
4f76459…
|
drh
|
8460 |
if( data.nSavedModes ){ |
|
4f76459…
|
drh
|
8461 |
int ii; |
|
4f76459…
|
drh
|
8462 |
for(ii=0; ii<data.nSavedModes; ii++){ |
|
4f76459…
|
drh
|
8463 |
modeFree(&data.aSavedModes[ii].mode); |
|
4f76459…
|
drh
|
8464 |
free(data.aSavedModes[ii].zTag); |
|
4f76459…
|
drh
|
8465 |
} |
|
4f76459…
|
drh
|
8466 |
free(data.aSavedModes); |
|
4f76459…
|
drh
|
8467 |
} |
|
4f76459…
|
drh
|
8468 |
free(data.zErrPrefix); |
|
4f76459…
|
drh
|
8469 |
free(data.dot.zCopy); |
|
4f76459…
|
drh
|
8470 |
free(data.dot.azArg); |
|
4f76459…
|
drh
|
8471 |
free(data.dot.aiOfst); |
|
4f76459…
|
drh
|
8472 |
free(data.dot.abQuot); |
|
4f76459…
|
drh
|
8473 |
if( data.nTestRun ){ |
|
4f76459…
|
drh
|
8474 |
sqlite3_fprintf(stdout, "%d test%s run with %d error%s\n", |
|
4f76459…
|
drh
|
8475 |
data.nTestRun, data.nTestRun==1 ? "" : "s", |
|
4f76459…
|
drh
|
8476 |
data.nTestErr, data.nTestErr==1 ? "" : "s"); |
|
4f76459…
|
drh
|
8477 |
fflush(stdout); |
|
4f76459…
|
drh
|
8478 |
rc = data.nTestErr>0; |
|
4f76459…
|
drh
|
8479 |
} |
|
0201a1e…
|
drh
|
8480 |
sqlite3_reset_auto_extension(); |
|
4f76459…
|
drh
|
8481 |
cli_printf(stderr,"Memory leaked: %u bytes\n", |
|
4f76459…
|
drh
|
8482 |
cli_printf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); |
|
4f76459…
|
drh
|
8483 |
cli_puts("Rolling back in-progress transaction.\n", stdout); |
|
92871e0…
|
drh
|
8484 |
process_input(&shellState, "<stdin>"); |
|
4f76459…
|
drh
|
8485 |
/************************* End src/shell.c.in ******************/ |