colSeparator : p->rowSeparator, p->out);
- }
- break;
- }
- case MODE_Www:
- case MODE_Html: {
- if( p->cnt==0 && p->cMode==MODE_Www ){
- sqlite3_fputs(
- "\n"
- "\n"
- ,p->out
- );
- }
- if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){
- sqlite3_fputs("", p->out);
- for(i=0; i", p->out);
- output_html_string(p->out, azCol[i]);
- sqlite3_fputs("\n", p->out);
- }
- sqlite3_fputs("
\n", p->out);
- }
- p->cnt++;
- if( azArg==0 ) break;
- sqlite3_fputs("", p->out);
- for(i=0; i", p->out);
- output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
- sqlite3_fputs("\n", p->out);
- }
- sqlite3_fputs("
\n", p->out);
- break;
- }
- case MODE_Tcl: {
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; iout, azCol[i] ? azCol[i] : "");
- if(icolSeparator, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- if( azArg==0 ) break;
- for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue);
- if(icolSeparator, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_Csv: {
- sqlite3_fsetmode(p->out, _O_BINARY);
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; irowSeparator, p->out);
- }
- if( nArg>0 ){
- for(i=0; irowSeparator, p->out);
- }
- setCrlfMode(p);
- break;
- }
- case MODE_Insert: {
- if( azArg==0 ) break;
- sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable);
- if( p->showHeader ){
- sqlite3_fputs("(", p->out);
- for(i=0; i0 ) sqlite3_fputs(",", p->out);
- if( quoteChar(azCol[i]) ){
- char *z = sqlite3_mprintf("\"%w\"", azCol[i]);
- shell_check_oom(z);
- sqlite3_fputs(z, p->out);
- sqlite3_free(z);
- }else{
- sqlite3_fprintf(p->out, "%s", azCol[i]);
- }
- }
- sqlite3_fputs(")", p->out);
- }
- p->cnt++;
- for(i=0; i0 ? "," : " VALUES(", p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("NULL", p->out);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p, azArg[i]);
- }else{
- output_quoted_escaped_string(p, azArg[i]);
- }
- }else if( aiType && aiType[i]==SQLITE_INTEGER ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_uint64 ur;
- memcpy(&ur,&r,sizeof(r));
- if( ur==0x7ff0000000000000LL ){
- sqlite3_fputs("9.0e+999", p->out);
- }else if( ur==0xfff0000000000000LL ){
- sqlite3_fputs("-9.0e+999", p->out);
- }else{
- sqlite3_int64 ir = (sqlite3_int64)r;
- if( r==(double)ir ){
- sqlite3_snprintf(50,z,"%lld.0", ir);
- }else{
- sqlite3_snprintf(50,z,"%!.20g", r);
- }
- sqlite3_fputs(z, p->out);
- }
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
- }else if( isNumber(azArg[i], 0) ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p, azArg[i]);
- }else{
- output_quoted_escaped_string(p, azArg[i]);
- }
- }
- sqlite3_fputs(");\n", p->out);
- break;
- }
- case MODE_Json: {
- if( azArg==0 ) break;
- if( p->cnt==0 ){
- sqlite3_fputs("[{", p->out);
- }else{
- sqlite3_fputs(",\n{", p->out);
- }
- p->cnt++;
- for(i=0; iout, azCol[i], -1);
- sqlite3_fputs(":", p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("null", p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_uint64 ur;
- memcpy(&ur,&r,sizeof(r));
- if( ur==0x7ff0000000000000LL ){
- sqlite3_fputs("9.0e+999", p->out);
- }else if( ur==0xfff0000000000000LL ){
- sqlite3_fputs("-9.0e+999", p->out);
- }else{
- sqlite3_snprintf(50,z,"%!.20g", r);
- sqlite3_fputs(z, p->out);
- }
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_json_string(p->out, pBlob, nBlob);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_json_string(p->out, azArg[i], -1);
- }else{
- sqlite3_fputs(azArg[i], p->out);
- }
- if( iout);
- }
- }
- sqlite3_fputs("}", p->out);
- break;
- }
- case MODE_Quote: {
- if( azArg==0 ) break;
- if( p->cnt==0 && p->showHeader ){
- for(i=0; i0 ) sqlite3_fputs(p->colSeparator, p->out);
- output_quoted_string(p, azCol[i]);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- p->cnt++;
- for(i=0; i0 ) sqlite3_fputs(p->colSeparator, p->out);
- if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- sqlite3_fputs("NULL", p->out);
- }else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_quoted_string(p, azArg[i]);
- }else if( aiType && aiType[i]==SQLITE_INTEGER ){
- sqlite3_fputs(azArg[i], p->out);
- }else if( aiType && aiType[i]==SQLITE_FLOAT ){
- char z[50];
- double r = sqlite3_column_double(p->pStmt, i);
- sqlite3_snprintf(50,z,"%!.20g", r);
- sqlite3_fputs(z, p->out);
- }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
- const void *pBlob = sqlite3_column_blob(p->pStmt, i);
- int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
- }else if( isNumber(azArg[i], 0) ){
- sqlite3_fputs(azArg[i], p->out);
- }else{
- output_quoted_string(p, azArg[i]);
- }
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_Ascii: {
- if( p->cnt++==0 && p->showHeader ){
- for(i=0; i0 ) sqlite3_fputs(p->colSeparator, p->out);
- sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- }
- if( azArg==0 ) break;
- for(i=0; i0 ) sqlite3_fputs(p->colSeparator, p->out);
- sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out);
- }
- sqlite3_fputs(p->rowSeparator, p->out);
- break;
- }
- case MODE_EQP: {
- eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
- break;
- }
- }
- return 0;
-}
-
-/*
-** This is the callback routine that the SQLite library
-** invokes for each row of a query result.
-*/
-static int callback(void *pArg, int nArg, char **azArg, char **azCol){
- /* since we don't have type info, call the shell_callback with a NULL value */
- return shell_callback(pArg, nArg, azArg, azCol, NULL);
-}
-
/*
** This is the callback routine from sqlite3_exec() that appends all
** output onto the end of a ShellText object.
@@ -23369,7 +25870,7 @@ static void createSelftestTable(ShellState *p){
"DROP TABLE [_shell$self];"
,0,0,&zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
+ cli_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0);
@@ -23382,28 +25883,13 @@ static void createSelftestTable(ShellState *p){
** table name.
*/
static void set_table_name(ShellState *p, const char *zName){
- int i, n;
- char cQuote;
- char *z;
-
if( p->zDestTable ){
- free(p->zDestTable);
+ sqlite3_free(p->zDestTable);
p->zDestTable = 0;
}
if( zName==0 ) return;
- cQuote = quoteChar(zName);
- n = strlen30(zName);
- if( cQuote ) n += n+2;
- z = p->zDestTable = malloc( n+1 );
- shell_check_oom(z);
- n = 0;
- if( cQuote ) z[n++] = cQuote;
- for(i=0; zName[i]; i++){
- z[n++] = zName[i];
- if( zName[i]==cQuote ) z[n++] = cQuote;
- }
- if( cQuote ) z[n++] = cQuote;
- z[n] = 0;
+ p->zDestTable = sqlite3_mprintf("%s", zName);
+ shell_check_oom(p->zDestTable);
}
/*
@@ -23472,7 +25958,7 @@ static int run_table_dump_query(
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
char *zContext = shell_error_context(zSelect, p->db);
- sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s",
+ cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s",
rc, sqlite3_errmsg(p->db), zContext);
sqlite3_free(zContext);
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
@@ -23482,22 +25968,22 @@ static int run_table_dump_query(
nResult = sqlite3_column_count(pSelect);
while( rc==SQLITE_ROW ){
z = (const char*)sqlite3_column_text(pSelect, 0);
- sqlite3_fprintf(p->out, "%s", z);
+ cli_printf(p->out, "%s", z);
for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i));
+ cli_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
}
if( z==0 ) z = "";
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
if( z[0] ){
- sqlite3_fputs("\n;\n", p->out);
+ cli_puts("\n;\n", p->out);
}else{
- sqlite3_fputs(";\n", p->out);
+ cli_puts(";\n", p->out);
}
rc = sqlite3_step(pSelect);
}
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
- sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n",
+ cli_printf(p->out, "/**** ERROR: (%d) %s *****/\n",
rc, sqlite3_errmsg(p->db));
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
@@ -23557,7 +26043,7 @@ static void displayLinuxIoStats(FILE *out){
for(i=0; iout==0 ) return 0;
out = pArg->out;
@@ -23611,22 +26097,22 @@ static int display_stats(
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
nCol = sqlite3_column_count(pStmt);
- sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol);
+ cli_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
for(i=0; istatsOn==3 ){
if( pArg->pStmt ){
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset);
- sqlite3_fprintf(out, "VM-steps: %d\n", iCur);
+ cli_printf(out, "VM-steps: %d\n", iCur);
}
return 0;
}
@@ -23663,48 +26149,55 @@ static int display_stats(
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Successful lookaside attempts: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside failures due to size: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
&iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Lookaside failures due to OOM: %d\n", iHiwtr);
}
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Pager Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache misses: %d\n", iCur);
+ iHiwtr64 = iCur64 = -1;
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
+ 0);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Page cache spills: %d\n", iCur);
+ cli_printf(out,
+ "Temporary data spilled to disk: %lld\n", iCur64);
+ sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
+ 1);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Statement Heap/Lookaside Usage: %d bytes\n", iCur);
}
@@ -23712,33 +26205,33 @@ static int display_stats(
int iHit, iMiss;
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Fullscan Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Autoindex Inserts: %d\n", iCur);
iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT,
bReset);
iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS,
bReset);
if( iHit || iMiss ){
- sqlite3_fprintf(out,
+ cli_printf(out,
"Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss);
}
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Virtual Machine Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Reprepare operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Number of times run: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset);
- sqlite3_fprintf(out,
+ cli_printf(out,
"Memory used by prepared stmt: %d\n", iCur);
}
@@ -23751,267 +26244,6 @@ static int display_stats(
return 0;
}
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
-static int scanStatsHeight(sqlite3_stmt *p, int iEntry){
- int iPid = 0;
- int ret = 1;
- sqlite3_stmt_scanstatus_v2(p, iEntry,
- SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
- );
- while( iPid!=0 ){
- int ii;
- for(ii=0; 1; ii++){
- int iId;
- int res;
- res = sqlite3_stmt_scanstatus_v2(p, ii,
- SQLITE_SCANSTAT_SELECTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iId
- );
- if( res ) break;
- if( iId==iPid ){
- sqlite3_stmt_scanstatus_v2(p, ii,
- SQLITE_SCANSTAT_PARENTID, SQLITE_SCANSTAT_COMPLEX, (void*)&iPid
- );
- }
- }
- ret++;
- }
- return ret;
-}
-#endif
-
-#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
-static void display_explain_scanstats(
- sqlite3 *db, /* Database to query */
- ShellState *pArg /* Pointer to ShellState */
-){
- static const int f = SQLITE_SCANSTAT_COMPLEX;
- sqlite3_stmt *p = pArg->pStmt;
- int ii = 0;
- i64 nTotal = 0;
- int nWidth = 0;
- eqp_reset(pArg);
-
- for(ii=0; 1; ii++){
- const char *z = 0;
- int n = 0;
- if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
- break;
- }
- n = (int)strlen(z) + scanStatsHeight(p, ii)*3;
- if( n>nWidth ) nWidth = n;
- }
- nWidth += 4;
-
- sqlite3_stmt_scanstatus_v2(p, -1, SQLITE_SCANSTAT_NCYCLE, f, (void*)&nTotal);
- for(ii=0; 1; ii++){
- i64 nLoop = 0;
- i64 nRow = 0;
- i64 nCycle = 0;
- int iId = 0;
- int iPid = 0;
- const char *zo = 0;
- const char *zName = 0;
- char *zText = 0;
- double rEst = 0.0;
-
- if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){
- break;
- }
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NLOOP,f,(void*)&nLoop);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NVISIT,f,(void*)&nRow);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NCYCLE,f,(void*)&nCycle);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_SELECTID,f,(void*)&iId);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
- sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
-
- zText = sqlite3_mprintf("%s", zo);
- if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
- char *z = 0;
- if( nCycle>=0 && nTotal>0 ){
- z = sqlite3_mprintf("%zcycles=%lld [%d%%]", z,
- nCycle, ((nCycle*100)+nTotal/2) / nTotal
- );
- }
- if( nLoop>=0 ){
- z = sqlite3_mprintf("%z%sloops=%lld", z, z ? " " : "", nLoop);
- }
- if( nRow>=0 ){
- z = sqlite3_mprintf("%z%srows=%lld", z, z ? " " : "", nRow);
- }
-
- if( zName && pArg->scanstatsOn>1 ){
- double rpl = (double)nRow / (double)nLoop;
- z = sqlite3_mprintf("%z rpl=%.1f est=%.1f", z, rpl, rEst);
- }
-
- zText = sqlite3_mprintf(
- "% *z (%z)", -1*(nWidth-scanStatsHeight(p, ii)*3), zText, z
- );
- }
-
- eqp_append(pArg, iId, iPid, zText);
- sqlite3_free(zText);
- }
-
- eqp_render(pArg, nTotal);
-}
-#endif
-
-
-/*
-** Parameter azArray points to a zero-terminated array of strings. zStr
-** points to a single nul-terminated string. Return non-zero if zStr
-** is equal, according to strcmp(), to any of the strings in the array.
-** Otherwise, return zero.
-*/
-static int str_in_array(const char *zStr, const char **azArray){
- int i;
- for(i=0; azArray[i]; i++){
- if( 0==cli_strcmp(zStr, azArray[i]) ) return 1;
- }
- return 0;
-}
-
-/*
-** If compiled statement pSql appears to be an EXPLAIN statement, allocate
-** and populate the ShellState.aiIndent[] array with the number of
-** spaces each opcode should be indented before it is output.
-**
-** The indenting rules are:
-**
-** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
-** all opcodes that occur between the p2 jump destination and the opcode
-** itself by 2 spaces.
-**
-** * Do the previous for "Return" instructions for when P2 is positive.
-** See tag-20220407a in wherecode.c and vdbe.c.
-**
-** * For each "Goto", if the jump destination is earlier in the program
-** and ends on one of:
-** Yield SeekGt SeekLt RowSetRead Rewind
-** or if the P1 parameter is one instead of zero,
-** then indent all opcodes between the earlier instruction
-** and "Goto" by 2 spaces.
-*/
-static void explain_data_prepare(ShellState *p, sqlite3_stmt *pSql){
- int *abYield = 0; /* True if op is an OP_Yield */
- int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
- int iOp; /* Index of operation in p->aiIndent[] */
-
- const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
- "Return", 0 };
- const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead",
- "Rewind", 0 };
- const char *azGoto[] = { "Goto", 0 };
-
- /* The caller guarantees that the leftmost 4 columns of the statement
- ** passed to this function are equivalent to the leftmost 4 columns
- ** of EXPLAIN statement output. In practice the statement may be
- ** an EXPLAIN, or it may be a query on the bytecode() virtual table. */
- assert( sqlite3_column_count(pSql)>=4 );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 0), "addr" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 1), "opcode" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 2), "p1" ) );
- assert( 0==sqlite3_stricmp( sqlite3_column_name(pSql, 3), "p2" ) );
-
- for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
- int i;
- int iAddr = sqlite3_column_int(pSql, 0);
- const char *zOp = (const char*)sqlite3_column_text(pSql, 1);
- int p1 = sqlite3_column_int(pSql, 2);
- int p2 = sqlite3_column_int(pSql, 3);
-
- /* Assuming that p2 is an instruction address, set variable p2op to the
- ** index of that instruction in the aiIndent[] array. p2 and p2op may be
- ** different if the current instruction is part of a sub-program generated
- ** by an SQL trigger or foreign key. */
- int p2op = (p2 + (iOp-iAddr));
-
- /* Grow the p->aiIndent array as required */
- if( iOp>=nAlloc ){
- nAlloc += 100;
- p->aiIndent = (int*)sqlite3_realloc64(p->aiIndent, nAlloc*sizeof(int));
- shell_check_oom(p->aiIndent);
- abYield = (int*)sqlite3_realloc64(abYield, nAlloc*sizeof(int));
- shell_check_oom(abYield);
- }
-
- abYield[iOp] = str_in_array(zOp, azYield);
- p->aiIndent[iOp] = 0;
- p->nIndent = iOp+1;
- if( str_in_array(zOp, azNext) && p2op>0 ){
- for(i=p2op; iaiIndent[i] += 2;
- }
- if( str_in_array(zOp, azGoto) && p2opaiIndent[i] += 2;
- }
- }
-
- p->iIndent = 0;
- sqlite3_free(abYield);
- sqlite3_reset(pSql);
-}
-
-/*
-** Free the array allocated by explain_data_prepare().
-*/
-static void explain_data_delete(ShellState *p){
- sqlite3_free(p->aiIndent);
- p->aiIndent = 0;
- p->nIndent = 0;
- p->iIndent = 0;
-}
-
-static void exec_prepared_stmt(ShellState*, sqlite3_stmt*);
-
-/*
-** Display scan stats.
-*/
-static void display_scanstats(
- sqlite3 *db, /* Database to query */
- ShellState *pArg /* Pointer to ShellState */
-){
-#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
- UNUSED_PARAMETER(db);
- UNUSED_PARAMETER(pArg);
-#else
- if( pArg->scanstatsOn==3 ){
- const char *zSql =
- " SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
- " format('% 6s (%.2f%%)',"
- " CASE WHEN ncycle<100_000 THEN ncycle || ' '"
- " WHEN ncycle<100_000_000 THEN (ncycle/1_000) || 'K'"
- " WHEN ncycle<100_000_000_000 THEN (ncycle/1_000_000) || 'M'"
- " ELSE (ncycle/1000_000_000) || 'G' END,"
- " ncycle*100.0/(sum(ncycle) OVER ())"
- " ) AS cycles"
- " FROM bytecode(?)";
-
- int rc = SQLITE_OK;
- sqlite3_stmt *pStmt = 0;
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- if( rc==SQLITE_OK ){
- sqlite3_stmt *pSave = pArg->pStmt;
- pArg->pStmt = pStmt;
- sqlite3_bind_pointer(pStmt, 1, pSave, "stmt-pointer", 0);
-
- pArg->cnt = 0;
- pArg->cMode = MODE_ScanExp;
- explain_data_prepare(pArg, pStmt);
- exec_prepared_stmt(pArg, pStmt);
- explain_data_delete(pArg);
-
- sqlite3_finalize(pStmt);
- pArg->pStmt = pSave;
- }
- }else{
- display_explain_scanstats(db, pArg);
- }
-#endif
-}
-
/*
** Disable and restore .wheretrace and .treetrace/.selecttrace settings.
*/
@@ -24103,6 +26335,38 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
memcpy(zBuf, &zVar[6], szVar-5);
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
}
+ }else if( strcmp(zVar, "$TIMER")==0 ){
+ sqlite3_bind_double(pStmt, i, pArg->prevTimer);
+#ifdef SQLITE_ENABLE_CARRAY
+ }else if( strncmp(zVar, "$carray_", 8)==0 ){
+ static char *azColorNames[] = {
+ "azure", "black", "blue", "brown", "cyan", "fuchsia", "gold",
+ "gray", "green", "indigo", "khaki", "lime", "magenta", "maroon",
+ "navy", "olive", "orange", "pink", "purple", "red", "silver",
+ "tan", "teal", "violet", "white", "yellow"
+ };
+ static int aPrimes[] = {
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
+ 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
+ };
+ /* Special bindings: carray($carray_clr), carray($carray_primes)
+ ** with --unsafe-testing: carray($carray_clr_p,26,'char*'),
+ ** carray($carray_primes_p,26,'int32')
+ */
+ if( strcmp(zVar+8,"clr")==0 ){
+ sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);
+ }else if( strcmp(zVar+8,"primes")==0 ){
+ sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);
+ }else if( strcmp(zVar+8,"clr_p")==0
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
+ sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0);
+ }else if( strcmp(zVar+8,"primes_p")==0
+ && ShellHasFlag(pArg,SHFLG_TestingMode) ){
+ sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0);
+ }else{
+ sqlite3_bind_null(pStmt, i);
+ }
+#endif
}else{
sqlite3_bind_null(pStmt, i);
}
@@ -24111,581 +26375,7 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
sqlite3_finalize(pQ);
}
-/*
-** UTF8 box-drawing characters. Imagine box lines like this:
-**
-** 1
-** |
-** 4 --+-- 2
-** |
-** 3
-**
-** Each box characters has between 2 and 4 of the lines leading from
-** the center. The characters are here identified by the numbers of
-** their corresponding lines.
-*/
-#define BOX_24 "\342\224\200" /* U+2500 --- */
-#define BOX_13 "\342\224\202" /* U+2502 | */
-#define BOX_23 "\342\224\214" /* U+250c ,- */
-#define BOX_34 "\342\224\220" /* U+2510 -, */
-#define BOX_12 "\342\224\224" /* U+2514 '- */
-#define BOX_14 "\342\224\230" /* U+2518 -' */
-#define BOX_123 "\342\224\234" /* U+251c |- */
-#define BOX_134 "\342\224\244" /* U+2524 -| */
-#define BOX_234 "\342\224\254" /* U+252c -,- */
-#define BOX_124 "\342\224\264" /* U+2534 -'- */
-#define BOX_1234 "\342\224\274" /* U+253c -|- */
-
-/* Draw horizontal line N characters long using unicode box
-** characters
-*/
-static void print_box_line(FILE *out, int N){
- const char zDash[] =
- BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
- BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
- const int nDash = sizeof(zDash) - 1;
- N *= 3;
- while( N>nDash ){
- sqlite3_fputs(zDash, out);
- N -= nDash;
- }
- sqlite3_fprintf(out, "%.*s", N, zDash);
-}
-
-/*
-** Draw a horizontal separator for a MODE_Box table.
-*/
-static void print_box_row_separator(
- ShellState *p,
- int nArg,
- const char *zSep1,
- const char *zSep2,
- const char *zSep3
-){
- int i;
- if( nArg>0 ){
- sqlite3_fputs(zSep1, p->out);
- print_box_line(p->out, p->actualWidth[0]+2);
- for(i=1; iout);
- print_box_line(p->out, p->actualWidth[i]+2);
- }
- sqlite3_fputs(zSep3, p->out);
- }
- sqlite3_fputs("\n", p->out);
-}
-
-/*
-** z[] is a line of text that is to be displayed the .mode box or table or
-** similar tabular formats. z[] might contain control characters such
-** as \n, \t, \f, or \r.
-**
-** Compute characters to display on the first line of z[]. Stop at the
-** first \r, \n, or \f. Expand \t into spaces. Return a copy (obtained
-** from malloc()) of that first line, which caller should free sometime.
-** Write anything to display on the next line into *pzTail. If this is
-** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
-*/
-static char *translateForDisplayAndDup(
- ShellState *p, /* To access current settings */
- const unsigned char *z, /* Input text to be transformed */
- const unsigned char **pzTail, /* OUT: Tail of the input for next line */
- int mxWidth, /* Max width. 0 means no limit */
- u8 bWordWrap /* If true, avoid breaking mid-word */
-){
- int i; /* Input bytes consumed */
- int j; /* Output bytes generated */
- int k; /* Input bytes to be displayed */
- int n; /* Output column number */
- unsigned char *zOut; /* Output text */
-
- if( z==0 ){
- *pzTail = 0;
- return 0;
- }
- if( mxWidth<0 ) mxWidth = -mxWidth;
- if( mxWidth==0 ) mxWidth = 1000000;
- i = j = n = 0;
- while( n=0xc0 ){
- int u;
- int len = decodeUtf8(&z[i], &u);
- i += len;
- j += len;
- n += cli_wcwidth(u);
- continue;
- }
- if( c>=' ' ){
- n++;
- i++;
- j++;
- continue;
- }
- if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break;
- if( c=='\t' ){
- do{
- n++;
- j++;
- }while( (n&7)!=0 && neEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){
- i += k;
- j += k;
- }else{
- n++;
- j += 3;
- i++;
- }
- }
- if( n>=mxWidth && bWordWrap ){
- /* Perhaps try to back up to a better place to break the line */
- for(k=i; k>i/2; k--){
- if( IsSpace(z[k-1]) ) break;
- }
- if( k<=i/2 ){
- for(k=i; k>i/2; k--){
- if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break;
- }
- }
- if( k<=i/2 ){
- k = i;
- }else{
- i = k;
- while( z[i]==' ' ) i++;
- }
- }else{
- k = i;
- }
- if( n>=mxWidth && z[i]>=' ' ){
- *pzTail = &z[i];
- }else if( z[i]=='\r' && z[i+1]=='\n' ){
- *pzTail = z[i+2] ? &z[i+2] : 0;
- }else if( z[i]==0 || z[i+1]==0 ){
- *pzTail = 0;
- }else{
- *pzTail = &z[i+1];
- }
- zOut = malloc( j+1 );
- shell_check_oom(zOut);
- i = j = n = 0;
- while( i=0xc0 ){
- int u;
- int len = decodeUtf8(&z[i], &u);
- do{ zOut[j++] = z[i++]; }while( (--len)>0 );
- n += cli_wcwidth(u);
- continue;
- }
- if( c>=' ' ){
- n++;
- zOut[j++] = z[i++];
- continue;
- }
- if( c==0 ) break;
- if( z[i]=='\t' ){
- do{
- n++;
- zOut[j++] = ' ';
- }while( (n&7)!=0 && neEscMode ){
- case SHELL_ESC_SYMBOL:
- zOut[j++] = 0xe2;
- zOut[j++] = 0x90;
- zOut[j++] = 0x80 + c;
- break;
- case SHELL_ESC_ASCII:
- zOut[j++] = '^';
- zOut[j++] = 0x40 + c;
- break;
- case SHELL_ESC_OFF: {
- int nn;
- if( c==0x1b && (nn = isVt100(&z[i]))>0 ){
- memcpy(&zOut[j], &z[i], nn);
- j += nn;
- i += nn - 1;
- }else{
- zOut[j++] = c;
- }
- break;
- }
- }
- i++;
- }
- zOut[j] = 0;
- return (char*)zOut;
-}
-
-/* Return true if the text string z[] contains characters that need
-** unistr() escaping.
-*/
-static int needUnistr(const unsigned char *z){
- unsigned char c;
- if( z==0 ) return 0;
- while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; }
- return c!=0;
-}
-
-/* Extract the value of the i-th current column for pStmt as an SQL literal
-** value. Memory is obtained from sqlite3_malloc64() and must be freed by
-** the caller.
-*/
-static char *quoted_column(sqlite3_stmt *pStmt, int i){
- switch( sqlite3_column_type(pStmt, i) ){
- case SQLITE_NULL: {
- return sqlite3_mprintf("NULL");
- }
- case SQLITE_INTEGER:
- case SQLITE_FLOAT: {
- return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
- }
- case SQLITE_TEXT: {
- const unsigned char *zText = sqlite3_column_text(pStmt,i);
- return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText);
- }
- case SQLITE_BLOB: {
- int j;
- sqlite3_str *pStr = sqlite3_str_new(0);
- const unsigned char *a = sqlite3_column_blob(pStmt,i);
- int n = sqlite3_column_bytes(pStmt,i);
- sqlite3_str_append(pStr, "x'", 2);
- for(j=0; jcmOpts.bWordWrap;
- const char *zEmpty = "";
- const char *zShowNull = p->nullValue;
-
- rc = sqlite3_step(pStmt);
- if( rc!=SQLITE_ROW ) return;
- nColumn = sqlite3_column_count(pStmt);
- if( nColumn==0 ) goto columnar_end;
- nAlloc = nColumn*4;
- if( nAlloc<=0 ) nAlloc = 1;
- azData = sqlite3_malloc64( nAlloc*sizeof(char*) );
- shell_check_oom(azData);
- azNextLine = sqlite3_malloc64( nColumn*sizeof(char*) );
- shell_check_oom(azNextLine);
- memset((void*)azNextLine, 0, nColumn*sizeof(char*) );
- if( p->cmOpts.bQuote ){
- azQuoted = sqlite3_malloc64( nColumn*sizeof(char*) );
- shell_check_oom(azQuoted);
- memset(azQuoted, 0, nColumn*sizeof(char*) );
- }
- abRowDiv = sqlite3_malloc64( nAlloc/nColumn );
- shell_check_oom(abRowDiv);
- if( nColumn>p->nWidth ){
- p->colWidth = realloc(p->colWidth, (nColumn+1)*2*sizeof(int));
- shell_check_oom(p->colWidth);
- for(i=p->nWidth; icolWidth[i] = 0;
- p->nWidth = nColumn;
- p->actualWidth = &p->colWidth[nColumn];
- }
- memset(p->actualWidth, 0, nColumn*sizeof(int));
- for(i=0; icolWidth[i];
- if( w<0 ) w = -w;
- p->actualWidth[i] = w;
- }
- for(i=0; icolWidth[i];
- if( wx==0 ){
- wx = p->cmOpts.iWrap;
- }
- if( wx<0 ) wx = -wx;
- uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
- if( uz==0 ) uz = (u8*)"";
- azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw);
- }
- do{
- int useNextLine = bNextLine;
- bNextLine = 0;
- if( (nRow+2)*nColumn >= nAlloc ){
- nAlloc *= 2;
- azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*));
- shell_check_oom(azData);
- abRowDiv = sqlite3_realloc64(abRowDiv, nAlloc/nColumn);
- shell_check_oom(abRowDiv);
- }
- abRowDiv[nRow] = 1;
- nRow++;
- for(i=0; icolWidth[i];
- if( wx==0 ){
- wx = p->cmOpts.iWrap;
- }
- if( wx<0 ) wx = -wx;
- if( useNextLine ){
- uz = azNextLine[i];
- if( uz==0 ) uz = (u8*)zEmpty;
- }else if( p->cmOpts.bQuote ){
- assert( azQuoted!=0 );
- sqlite3_free(azQuoted[i]);
- azQuoted[i] = quoted_column(pStmt,i);
- uz = (const unsigned char*)azQuoted[i];
- }else{
- uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
- if( uz==0 ) uz = (u8*)zShowNull;
- }
- azData[nRow*nColumn + i]
- = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw);
- if( azNextLine[i] ){
- bNextLine = 1;
- abRowDiv[nRow-1] = 0;
- bMultiLineRowExists = 1;
- }
- }
- }while( bNextLine || sqlite3_step(pStmt)==SQLITE_ROW );
- nTotal = nColumn*(nRow+1);
- for(i=0; ip->actualWidth[j] ) p->actualWidth[j] = n;
- }
- if( seenInterrupt ) goto columnar_end;
- switch( p->cMode ){
- case MODE_Column: {
- colSep = " ";
- rowSep = "\n";
- if( p->showHeader ){
- for(i=0; iactualWidth[i];
- if( p->colWidth[i]<0 ) w = -w;
- utf8_width_print(p->out, w, azData[i]);
- sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
- }
- for(i=0; iout, p->actualWidth[i]);
- sqlite3_fputs(i==nColumn-1?"\n":" ", p->out);
- }
- }
- break;
- }
- case MODE_Table: {
- colSep = " | ";
- rowSep = " |\n";
- print_row_separator(p, nColumn, "+");
- sqlite3_fputs("| ", p->out);
- for(i=0; iactualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
- azData[i], (w-n+1)/2, "");
- sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
- }
- print_row_separator(p, nColumn, "+");
- break;
- }
- case MODE_Markdown: {
- colSep = " | ";
- rowSep = " |\n";
- sqlite3_fputs("| ", p->out);
- for(i=0; iactualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "",
- azData[i], (w-n+1)/2, "");
- sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out);
- }
- print_row_separator(p, nColumn, "|");
- break;
- }
- case MODE_Box: {
- colSep = " " BOX_13 " ";
- rowSep = " " BOX_13 "\n";
- print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34);
- sqlite3_fputs(BOX_13 " ", p->out);
- for(i=0; iactualWidth[i];
- n = strlenChar(azData[i]);
- sqlite3_fprintf(p->out, "%*s%s%*s%s",
- (w-n)/2, "", azData[i], (w-n+1)/2, "",
- i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
- }
- print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
- break;
- }
- }
- for(i=nColumn, j=0; icMode!=MODE_Column ){
- sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out);
- }
- z = azData[i];
- if( z==0 ) z = p->nullValue;
- w = p->actualWidth[j];
- if( p->colWidth[j]<0 ) w = -w;
- utf8_width_print(p->out, w, z);
- if( j==nColumn-1 ){
- sqlite3_fputs(rowSep, p->out);
- if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1cMode==MODE_Table ){
- print_row_separator(p, nColumn, "+");
- }else if( p->cMode==MODE_Box ){
- print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
- }else if( p->cMode==MODE_Column ){
- sqlite3_fputs("\n", p->out);
- }
- }
- j = -1;
- if( seenInterrupt ) goto columnar_end;
- }else{
- sqlite3_fputs(colSep, p->out);
- }
- }
- if( p->cMode==MODE_Table ){
- print_row_separator(p, nColumn, "+");
- }else if( p->cMode==MODE_Box ){
- print_box_row_separator(p, nColumn, BOX_12, BOX_124, BOX_14);
- }
-columnar_end:
- if( seenInterrupt ){
- sqlite3_fputs("Interrupt\n", p->out);
- }
- nData = (nRow+1)*nColumn;
- for(i=0; icMode==MODE_Column
- || pArg->cMode==MODE_Table
- || pArg->cMode==MODE_Box
- || pArg->cMode==MODE_Markdown
- ){
- exec_prepared_stmt_columnar(pArg, pStmt);
- return;
- }
-
- /* perform the first step. this will tell us if we
- ** have a result set or not and how wide it is.
- */
- rc = sqlite3_step(pStmt);
- /* if we have a result set... */
- if( SQLITE_ROW == rc ){
- /* allocate space for col name ptr, value ptr, and type */
- int nCol = sqlite3_column_count(pStmt);
- void *pData = sqlite3_malloc64(3*nCol*sizeof(const char*) + 1);
- if( !pData ){
- shell_out_of_memory();
- }else{
- char **azCols = (char **)pData; /* Names of result columns */
- char **azVals = &azCols[nCol]; /* Results */
- int *aiTypes = (int *)&azVals[nCol]; /* Result types */
- int i, x;
- assert(sizeof(int) <= sizeof(char *));
- /* save off ptrs to column names */
- for(i=0; icMode==MODE_Insert || pArg->cMode==MODE_Quote)
- ){
- azVals[i] = "";
- }else{
- azVals[i] = (char*)sqlite3_column_text(pStmt, i);
- }
- if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
- rc = SQLITE_NOMEM;
- break; /* from for */
- }
- } /* end for */
-
- /* if data and types extracted successfully... */
- if( SQLITE_ROW == rc ){
- /* call the supplied callback with the result row data */
- if( shell_callback(pArg, nCol, azVals, azCols, aiTypes) ){
- rc = SQLITE_ABORT;
- }else{
- rc = sqlite3_step(pStmt);
- }
- }
- } while( SQLITE_ROW == rc );
- sqlite3_free(pData);
- if( pArg->cMode==MODE_Json ){
- sqlite3_fputs("]\n", pArg->out);
- }else if( pArg->cMode==MODE_Www ){
- sqlite3_fputs("
\n\n", pArg->out);
- }else if( pArg->cMode==MODE_Count ){
- char zBuf[200];
- sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n",
- nRow, nRow!=1 ? "s" : "");
- printf("%s", zBuf);
- }
- }
- }
-}
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
/*
** This function is called to process SQL if the previous shell command
** was ".expert". It passes the SQL in the second argument directly to
@@ -24736,8 +26426,8 @@ static int expertFinish(
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
- sqlite3_fputs("-- Candidates -----------------------------\n", out);
- sqlite3_fprintf(out, "%s\n", zCand);
+ cli_puts("-- Candidates -----------------------------\n", out);
+ cli_printf(out, "%s\n", zCand);
}
for(i=0; i=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
- sqlite3_fprintf(stderr, "option requires an argument: %s\n", z);
+ cli_printf(stderr, "option requires an argument: %s\n", z);
rc = SQLITE_ERROR;
}else{
iSample = (int)integerValue(azArg[++i]);
if( iSample<0 || iSample>100 ){
- sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]);
+ cli_printf(stderr,"value out of range: %s\n", azArg[i]);
rc = SQLITE_ERROR;
}
}
}
else{
- sqlite3_fprintf(stderr,"unknown option: %s\n", z);
+ cli_printf(stderr,"unknown option: %s\n", z);
rc = SQLITE_ERROR;
}
}
@@ -24804,7 +26494,7 @@ static int expertDotCommand(
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory");
rc = SQLITE_ERROR;
}else{
@@ -24817,7 +26507,16 @@ static int expertDotCommand(
return rc;
}
-#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
+#endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */
+
+/*
+** QRF write callback
+*/
+static int shellWriteQR(void *pX, const char *z, sqlite3_int64 n){
+ ShellState *pArg = (ShellState*)pX;
+ cli_printf(pArg->out, "%.*s", (int)n, z);
+ return SQLITE_OK;
+}
/*
** Execute a statement or set of statements. Print
@@ -24838,19 +26537,42 @@ static int shell_exec(
int rc2;
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3 *db = pArg->db;
+ unsigned char eStyle;
+ sqlite3_qrf_spec spec;
if( pzErrMsg ){
*pzErrMsg = NULL;
}
+ memcpy(&spec, &pArg->mode.spec, sizeof(spec));
+ spec.xWrite = shellWriteQR;
+ spec.pWriteArg = (void*)pArg;
+ if( pArg->mode.eMode==MODE_Insert && ShellHasFlag(pArg,SHFLG_PreserveRowid) ){
+ spec.bTitles = QRF_SW_On;
+ }
+ /* ,- This is true, but it is omitted
+ ** vvvvvvvvvvvvvvvvvvv ----- to avoid compiler warnings. */
+ assert( /*pArg->mode.eMode>=0 &&*/ pArg->mode.eModemode.eMode].eStyle;
+ if( pArg->mode.bAutoScreenWidth ){
+ spec.nScreenWidth = shellScreenWidth();
+ }
+ if( spec.eBlob==QRF_BLOB_Auto ){
+ switch( spec.eText ){
+ case QRF_TEXT_Relaxed: /* fall through */
+ case QRF_TEXT_Sql: spec.eBlob = QRF_BLOB_Sql; break;
+ case QRF_TEXT_Json: spec.eBlob = QRF_BLOB_Json; break;
+ default: spec.eBlob = QRF_BLOB_Text; break;
+ }
+ }
-#ifndef SQLITE_OMIT_VIRTUALTABLE
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( pArg->expert.pExpert ){
rc = expertHandleSQL(pArg, zSql, pzErrMsg);
return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
}
#endif
- while( zSql[0] && (SQLITE_OK == rc) ){
+ while( zSql && zSql[0] && (SQLITE_OK == rc) ){
static const char *zStmtSql;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
if( SQLITE_OK != rc ){
@@ -24858,6 +26580,7 @@ static int shell_exec(
*pzErrMsg = save_err_msg(db, "in prepare", rc, zSql);
}
}else{
+ int isExplain;
if( !pStmt ){
/* this happens for a comment or white-space */
zSql = zLeftover;
@@ -24868,80 +26591,58 @@ static int shell_exec(
if( zStmtSql==0 ) zStmtSql = "";
while( IsSpace(zStmtSql[0]) ) zStmtSql++;
- /* save off the prepared statement handle and reset row count */
+ /* save off the prepared statement handle */
if( pArg ){
pArg->pStmt = pStmt;
- pArg->cnt = 0;
}
-
+
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
- if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
- sqlite3_stmt *pExplain;
+ isExplain = sqlite3_stmt_isexplain(pStmt);
+ if( pArg && pArg->mode.autoEQP && isExplain==0 && pArg->dot.nArg==0 ){
int triggerEQP = 0;
+ u8 savedEnableTimer = pArg->enableTimer;
+ pArg->enableTimer = 0;
disable_debug_trace_modes();
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
- if( pArg->autoEQP>=AUTOEQP_trigger ){
+ if( pArg->mode.autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
}
- pExplain = pStmt;
- sqlite3_reset(pExplain);
- rc = sqlite3_stmt_explain(pExplain, 2);
- if( rc==SQLITE_OK ){
- bind_prepared_stmt(pArg, pExplain);
- while( sqlite3_step(pExplain)==SQLITE_ROW ){
- const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
- int iEqpId = sqlite3_column_int(pExplain, 0);
- int iParentId = sqlite3_column_int(pExplain, 1);
- if( zEQPLine==0 ) zEQPLine = "";
- if( zEQPLine[0]=='-' ) eqp_render(pArg, 0);
- eqp_append(pArg, iEqpId, iParentId, zEQPLine);
- }
- eqp_render(pArg, 0);
+ sqlite3_reset(pStmt);
+ spec.eStyle = QRF_STYLE_Auto;
+ sqlite3_stmt_explain(pStmt, 2);
+ sqlite3_format_query_result(pStmt, &spec, 0);
+ if( pArg->mode.autoEQP>=AUTOEQP_full ){
+ sqlite3_reset(pStmt);
+ sqlite3_stmt_explain(pStmt, 1);
+ sqlite3_format_query_result(pStmt, &spec, 0);
}
- if( pArg->autoEQP>=AUTOEQP_full ){
- /* Also do an EXPLAIN for ".eqp full" mode */
- sqlite3_reset(pExplain);
- rc = sqlite3_stmt_explain(pExplain, 1);
- if( rc==SQLITE_OK ){
- pArg->cMode = MODE_Explain;
- assert( sqlite3_stmt_isexplain(pExplain)==1 );
- bind_prepared_stmt(pArg, pExplain);
- explain_data_prepare(pArg, pExplain);
- exec_prepared_stmt(pArg, pExplain);
- explain_data_delete(pArg);
- }
- }
- if( pArg->autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
+
+ if( pArg->mode.autoEQP>=AUTOEQP_trigger && triggerEQP==0 ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 0, 0);
}
sqlite3_reset(pStmt);
sqlite3_stmt_explain(pStmt, 0);
restore_debug_trace_modes();
- }
-
- if( pArg ){
- int bIsExplain = (sqlite3_stmt_isexplain(pStmt)==1);
- pArg->cMode = pArg->mode;
- if( pArg->autoExplain ){
- if( bIsExplain ){
- pArg->cMode = MODE_Explain;
- }
- if( sqlite3_stmt_isexplain(pStmt)==2 ){
- pArg->cMode = MODE_EQP;
- }
- }
-
- /* If the shell is currently in ".explain" mode, gather the extra
- ** data required to add indents to the output.*/
- if( pArg->cMode==MODE_Explain && bIsExplain ){
- explain_data_prepare(pArg, pStmt);
- }
+ pArg->enableTimer = savedEnableTimer;
}
bind_prepared_stmt(pArg, pStmt);
- exec_prepared_stmt(pArg, pStmt);
- explain_data_delete(pArg);
- eqp_render(pArg, 0);
+ if( isExplain && pArg->mode.autoExplain ){
+ spec.eStyle = isExplain==1 ? QRF_STYLE_Explain : QRF_STYLE_Eqp;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ }else if( pArg->mode.eMode==MODE_Www ){
+ cli_printf(pArg->out,
+ "\n"
+ "\n");
+ spec.eStyle = QRF_STYLE_Html;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ cli_printf(pArg->out,
+ "
\n"
+ "");
+ }else{
+ spec.eStyle = eStyle;
+ sqlite3_format_query_result(pStmt, &spec, pzErrMsg);
+ }
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
@@ -24949,8 +26650,19 @@ static int shell_exec(
}
/* print loop-counters if required */
- if( pArg && pArg->scanstatsOn ){
- display_scanstats(db, pArg);
+ if( pArg && pArg->mode.scanstatsOn ){
+ char *zErr = 0;
+ switch( pArg->mode.scanstatsOn ){
+ case 1: spec.eStyle = QRF_STYLE_Stats; break;
+ case 2: spec.eStyle = QRF_STYLE_StatsEst; break;
+ default: spec.eStyle = QRF_STYLE_StatsVm; break;
+ }
+ sqlite3_reset(pStmt);
+ rc = sqlite3_format_query_result(pStmt, &spec, &zErr);
+ if( rc ){
+ cli_printf(stderr, "Stats query failed: %s\n", zErr);
+ sqlite3_free(zErr);
+ }
}
/* Finalize the statement just executed. If this fails, save a
@@ -25005,7 +26717,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
sqlite3_stmt *pStmt;
char *zSql;
int nCol = 0;
- int nAlloc = 0;
+ i64 nAlloc = 0;
int nPK = 0; /* Number of PRIMARY KEY columns seen */
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
@@ -25019,7 +26731,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
- azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
+ azCol = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
shell_check_oom(azCol);
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
@@ -25108,6 +26820,9 @@ static void toggleSelectOrder(sqlite3 *db){
sqlite3_exec(db, zStmt, 0, 0, 0);
}
+/* Forward reference */
+static int db_int(sqlite3 *db, const char *zSql, ...);
+
/*
** This is a different callback routine used for dumping the database.
** Each row received by this callback consists of a table name,
@@ -25134,9 +26849,23 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
- /* no-op */
+ /* The sqlite_sequence table is repopulated last. Delete content
+ ** in the sqlite_sequence table added by prior repopulations prior to
+ ** repopulating sqlite_sequence itself. But only do this if the
+ ** table is non-empty, because if it is empty the table might not
+ ** have been recreated by prior repopulations. See forum posts:
+ ** 2024-10-13T17:10:01z and 2025-10-29T19:38:43z
+ */
+ if( db_int(p->db, "SELECT count(*) FROM sqlite_sequence")>0 ){
+ if( !p->writableSchema ){
+ cli_puts("PRAGMA writable_schema=ON;\n", p->out);
+ p->writableSchema = 1;
+ }
+ cli_puts("CREATE TABLE IF NOT EXISTS sqlite_sequence(name,seq);\n"
+ "DELETE FROM sqlite_sequence;\n", p->out);
+ }
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
- if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out);
+ if( !dataOnly ) cli_puts("ANALYZE sqlite_schema;\n", p->out);
}else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( dataOnly ){
@@ -25144,7 +26873,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
}else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !p->writableSchema ){
- sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out);
+ cli_puts("PRAGMA writable_schema=ON;\n", p->out);
p->writableSchema = 1;
}
zIns = sqlite3_mprintf(
@@ -25152,7 +26881,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
shell_check_oom(zIns);
- sqlite3_fprintf(p->out, "%s\n", zIns);
+ cli_printf(p->out, "%s\n", zIns);
sqlite3_free(zIns);
return 0;
}else{
@@ -25164,8 +26893,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
ShellText sTable;
char **azCol;
int i;
- char *savedDestTable;
- int savedMode;
+ Mode savedMode;
azCol = tableColumnList(p, zTable);
if( azCol==0 ){
@@ -25208,18 +26936,21 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
appendText(&sSelect, " FROM ", 0);
appendText(&sSelect, zTable, quoteChar(zTable));
- savedDestTable = p->zDestTable;
+
savedMode = p->mode;
- p->zDestTable = sTable.z;
- p->mode = p->cMode = MODE_Insert;
- rc = shell_exec(p, sSelect.z, 0);
+ p->mode.spec.zTableName = (char*)zTable;
+ p->mode.eMode = MODE_Insert;
+ p->mode.spec.eText = QRF_TEXT_Sql;
+ p->mode.spec.eBlob = QRF_BLOB_Sql;
+ p->mode.spec.bTitles = QRF_No;
+ p->mode.spec.nCharLimit = 0;
+ rc = shell_exec(p, sSelect.zTxt, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
- sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
+ cli_puts("/****** CORRUPTION ERROR *******/\n", p->out);
toggleSelectOrder(p->db);
- shell_exec(p, sSelect.z, 0);
+ shell_exec(p, sSelect.zTxt, 0);
toggleSelectOrder(p->db);
}
- p->zDestTable = savedDestTable;
p->mode = savedMode;
freeText(&sTable);
freeText(&sSelect);
@@ -25245,9 +26976,9 @@ static int run_schema_dump_query(
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
- sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
+ cli_puts("/****** CORRUPTION ERROR *******/\n", p->out);
if( zErr ){
- sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr);
+ cli_printf(p->out, "/****** %s ******/\n", zErr);
sqlite3_free(zErr);
zErr = 0;
}
@@ -25256,7 +26987,7 @@ static int run_schema_dump_query(
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
- sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
+ cli_printf(p->out, "/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
@@ -25314,8 +27045,8 @@ static const char *(azHelp[]) = {
".cd DIRECTORY Change the working directory to DIRECTORY",
#endif
".changes on|off Show number of rows changed by SQL",
+ ".check OPTIONS ... Verify the results of a .testcase",
#ifndef SQLITE_SHELL_FIDDLE
- ".check GLOB Fail if output since .testcase does not match",
".clone NEWDB Clone data into NEWDB from the existing database",
#endif
".connection [close] [#] Open or close an auxiliary database connection",
@@ -25349,7 +27080,9 @@ static const char *(azHelp[]) = {
#ifndef SQLITE_SHELL_FIDDLE
".exit ?CODE? Exit this program with return-code CODE",
#endif
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
".expert EXPERIMENTAL. Suggest indexes for queries",
+#endif
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
".filectrl CMD ... Run various sqlite3_file_control() operations",
" --schema SCHEMA Use SCHEMA instead of \"main\"",
@@ -25359,26 +27092,14 @@ static const char *(azHelp[]) = {
".help ?-all? ?PATTERN? Show help text for PATTERN",
#ifndef SQLITE_SHELL_FIDDLE
".import FILE TABLE Import data from FILE into TABLE",
- " Options:",
- " --ascii Use \\037 and \\036 as column and row separators",
- " --csv Use , and \\n as column and row separators",
- " --skip N Skip the first N rows of input",
- " --schema S Target table to be S.TABLE",
- " -v \"Verbose\" - increase auxiliary output",
- " Notes:",
- " * If TABLE does not exist, it is created. The first row of input",
- " determines the column names.",
- " * If neither --csv or --ascii are used, the input mode is derived",
- " from the \".mode\" output mode",
- " * If FILE begins with \"|\" then it is a command that generates the",
- " input text.",
#endif
#ifndef SQLITE_OMIT_TEST_CONTROL
- ",imposter INDEX TABLE Create imposter table TABLE on index INDEX",
+ ".imposter INDEX TABLE Create imposter table TABLE on index INDEX",
#endif
- ".indexes ?TABLE? Show names of indexes",
- " If TABLE is specified, only show indexes for",
- " tables matching TABLE using the LIKE operator.",
+ ".indexes ?PATTERN? Show names of indexes matching PATTERN",
+ " -a|--all Also show system-generated indexes",
+ " --expr Show only expression indexes",
+ " --sys Show only system-generated indexes",
".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
#ifdef SQLITE_ENABLE_IOTRACE
",iotrace FILE Enable I/O diagnostic logging to FILE",
@@ -25396,42 +27117,12 @@ static const char *(azHelp[]) = {
".log on|off Turn logging on or off.",
#endif
".mode ?MODE? ?OPTIONS? Set output mode",
- " MODE is one of:",
- " ascii Columns/rows delimited by 0x1F and 0x1E",
- " box Tables using unicode box-drawing characters",
- " csv Comma-separated values",
- " column Output in columns. (See .width)",
- " html HTML code",
- " insert SQL insert statements for TABLE",
- " json Results in a JSON array",
- " line One value per line",
- " list Values delimited by \"|\"",
- " markdown Markdown table format",
- " qbox Shorthand for \"box --wrap 60 --quote\"",
- " quote Escape answers as for SQL",
- " table ASCII-art table",
- " tabs Tab-separated values",
- " tcl TCL list elements",
- " OPTIONS: (for columnar modes or insert mode):",
- " --escape T ctrl-char escape; T is one of: symbol, ascii, off",
- " --wrap N Wrap output lines to no longer than N characters",
- " --wordwrap B Wrap or not at word boundaries per B (on/off)",
- " --ww Shorthand for \"--wordwrap 1\"",
- " --quote Quote output text as SQL literals",
- " --noquote Do not quote output text",
- " TABLE The name of SQL table used for \"insert\" mode",
#ifndef SQLITE_SHELL_FIDDLE
".nonce STRING Suspend safe mode for one command if nonce matches",
#endif
".nullvalue STRING Use STRING in place of NULL values",
#ifndef SQLITE_SHELL_FIDDLE
".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
- " If FILE begins with '|' then open as a pipe",
- " --bom Put a UTF8 byte-order mark at the beginning",
- " -e Send output to the system text editor",
- " --plain Use text/plain output instead of HTML for -w option",
- " -w Send output as HTML to a web browser (same as \".www\")",
- " -x Send output as CSV to a spreadsheet (same as \".excel\")",
/* Note that .open is (partially) available in WASM builds but is
** currently only intended to be used by the fiddle tool, not
** end users, so is "undocumented." */
@@ -25441,23 +27132,22 @@ static const char *(azHelp[]) = {
#endif
#ifndef SQLITE_OMIT_DESERIALIZE
" --deserialize Load into memory using sqlite3_deserialize()",
+#endif
+/*" --exclusive Set the SQLITE_OPEN_EXCLUSIVE flag", UNDOCUMENTED */
+#ifndef SQLITE_OMIT_DESERIALIZE
" --hexdb Load the output of \"dbtotxt\" as an in-memory db",
+#endif
+ " --ifexist Only open if FILE already exists",
+#ifndef SQLITE_OMIT_DESERIALIZE
" --maxsize N Maximum size for --hexdb or --deserialized database",
#endif
" --new Initialize FILE to an empty database",
+ " --normal FILE is an ordinary SQLite database",
" --nofollow Do not follow symbolic links",
" --readonly Open FILE readonly",
" --zip FILE is a ZIP archive",
#ifndef SQLITE_SHELL_FIDDLE
".output ?FILE? Send output to FILE or stdout if FILE is omitted",
- " If FILE begins with '|' then open it as a pipe.",
- " If FILE is 'off' then output is disabled.",
- " Options:",
- " --bom Prefix output with a UTF8 byte-order mark",
- " -e Send output to the system text editor",
- " --plain Use text/plain for -w option",
- " -w Send output to a web browser",
- " -x Send output as CSV to a spreadsheet",
#endif
".parameter CMD ... Manage SQL parameter bindings",
" clear Erase all bindings",
@@ -25473,6 +27163,7 @@ static const char *(azHelp[]) = {
" --once Do no more than one progress interrupt",
" --quiet|-q No output except at interrupts",
" --reset Reset the count for each input and interrupt",
+ " --timeout S Halt after running for S seconds",
#endif
".prompt MAIN CONTINUE Replace the standard prompts",
#ifndef SQLITE_SHELL_FIDDLE
@@ -25500,7 +27191,7 @@ static const char *(azHelp[]) = {
" Options:",
" --init Create a new SELFTEST table",
" -v Verbose output",
- ".separator COL ?ROW? Change the column and row separators",
+ ",separator COL ?ROW? Change the column and row separators",
#if defined(SQLITE_ENABLE_SESSION)
".session ?NAME? CMD ... Create or control sessions",
" Subcommands:",
@@ -25527,7 +27218,7 @@ static const char *(azHelp[]) = {
#if !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE)
".shell CMD ARGS... Run CMD ARGS... in a system shell",
#endif
- ".show Show the current values for various settings",
+ ",show Show the current values for various settings",
".stats ?ARG? Show stats or turn stats on or off",
" off Turn off automatic stat display",
" on Turn on automatic stat display",
@@ -25537,13 +27228,11 @@ static const char *(azHelp[]) = {
".system CMD ARGS... Run CMD ARGS... in a system shell",
#endif
".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
-#ifndef SQLITE_SHELL_FIDDLE
- ",testcase NAME Begin redirecting output to 'testcase-out.txt'",
-#endif
+ ".testcase NAME Begin a test case.",
",testctrl CMD ... Run various sqlite3_test_control() operations",
" Run \".testctrl\" with no arguments for details",
".timeout MS Try opening locked tables for MS milliseconds",
- ".timer on|off Turn SQL timer on or off",
+ ".timer on|off|once Turn SQL timer on or off.",
#ifndef SQLITE_OMIT_TRACE
".trace ?OPTIONS? Output each SQL statement as it is run",
" FILE Send output to FILE",
@@ -25568,7 +27257,7 @@ static const char *(azHelp[]) = {
".vfsinfo ?AUX? Information about the top-level VFS",
".vfslist List all available VFSes",
".vfsname ?AUX? Print the name of the VFS stack",
- ".width NUM1 NUM2 ... Set minimum column widths for columnar output",
+ ",width NUM1 NUM2 ... Set minimum column widths for columnar output",
" Negative values right-justify",
#ifndef SQLITE_SHELL_FIDDLE
".www Display output of the next command in web browser",
@@ -25576,6 +27265,204 @@ static const char *(azHelp[]) = {
#endif
};
+/**************************************************************
+** "Usage" help text automatically generated from comments */
+static const struct {
+ const char *zCmd; /* Name of the dot-command */
+ const char *zUsage; /* Documentation */
+} aUsage[] = {
+ { ".import",
+"USAGE: .import [OPTIONS] FILE TABLE\n"
+"\n"
+"Import CSV or similar text from FILE into TABLE. If TABLE does\n"
+"not exist, it is created using the first row of FILE as the column\n"
+"names. If FILE begins with \"|\" then it is a command that is run\n"
+"and the output from the command is used as the input data. If\n"
+"FILE begins with \"<<\" followed by a label, then content is read from\n"
+"the script until the first line that matches the label.\n"
+"\n"
+"The content of FILE is interpreted using RFC-4180 (\"CSV\") quoting\n"
+"rules unless the current mode is \"ascii\" or \"tabs\" or unless one\n"
+"the --ascii option is used.\n"
+"\n"
+"The column and row separators must be single ASCII characters. If\n"
+"multiple characters or a Unicode character are specified for the\n"
+"separators, then only the first byte of the separator is used. Except,\n"
+"if the row separator is \\n and the mode is not --ascii, then \\r\\n is\n"
+"understood as a row separator too.\n"
+"\n"
+"Options:\n"
+" --ascii Do not use RFC-4180 quoting. Use \\037 and \\036\n"
+" as column and row separators on input, unless other\n"
+" delimiters are specified using --colsep and/or --rowsep\n"
+" --colsep CHAR Use CHAR as the column separator.\n"
+" --csv Input is standard RFC-4180 CSV.\n"
+" --esc CHAR Use CHAR as an escape character in unquoted CSV inputs.\n"
+" --qesc CHAR Use CHAR as an escape character in quoted CSV inputs.\n"
+" --rowsep CHAR Use CHAR as the row separator.\n"
+" --schema S When creating TABLE, put it in schema S\n"
+" --skip N Ignore the first N rows of input\n"
+" -v Verbose mode\n"
+ },
+ { ".mode",
+"USAGE: .mode [MODE] [OPTIONS]\n"
+"\n"
+"Change the output mode to MODE and/or apply OPTIONS to the output mode.\n"
+"Arguments are processed from left to right. If no arguments, show the\n"
+"current output mode and relevant options.\n"
+"\n"
+"Options:\n"
+" --align STRING Set the alignment of text in columnar modes\n"
+" String consists of characters 'L', 'C', 'R'\n"
+" meaning \"left\", \"centered\", and \"right\", with\n"
+" one letter per column starting from the left.\n"
+" Unspecified alignment defaults to 'L'.\n"
+" --blob-quote ARG ARG can be \"auto\", \"text\", \"sql\", \"hex\", \"tcl\",\n"
+" \"json\", or \"size\". Default is \"auto\".\n"
+" --border on|off Show outer border on \"box\" and \"table\" modes.\n"
+" --charlimit N Set the maximum number of output characters to\n"
+" show for any single SQL value to N. Longer values\n"
+" truncated. Zero means \"no limit\".\n"
+" --colsep STRING Use STRING as the column separator\n"
+" --escape ESC Enable/disable escaping of control characters\n"
+" found in the output. ESC can be \"off\", \"ascii\",\n"
+" or \"symbol\".\n"
+" --linelimit N Set the maximum number of output lines to show for\n"
+" any single SQL value to N. Longer values are\n"
+" truncated. Zero means \"no limit\". Only works\n"
+" in \"line\" mode and in columnar modes.\n"
+" --limits L,C,T Shorthand for \"--linelimit L --charlimit C\n"
+" --titlelimit T\". The \",T\" can be omitted in which\n"
+" case the --titlelimit is unchanged. The argument\n"
+" can also be \"off\" to mean \"0,0,0\" or \"on\" to\n"
+" mean \"5,300,20\".\n"
+" --list List available modes\n"
+" --multiinsert N In \"insert\" mode, put multiple rows on a single\n"
+" INSERT statement until the size exceeds N bytes.\n"
+" --null STRING Render SQL NULL values as the given string\n"
+" --once Setting changes to the right are reverted after\n"
+" the next SQL command.\n"
+" --quote ARG Enable/disable quoting of text. ARG can be\n"
+" \"off\", \"on\", \"sql\", \"relaxed\", \"csv\", \"html\",\n"
+" \"tcl\", or \"json\". \"off\" means show the text as-is.\n"
+" \"on\" is an alias for \"sql\".\n"
+" --reset Changes all mode settings back to their default.\n"
+" --rowsep STRING Use STRING as the row separator\n"
+" --sw|--screenwidth N Declare the screen width of the output device\n"
+" to be N characters. An attempt may be made to\n"
+" wrap output text to fit within this limit. Zero\n"
+" means \"no limit\". Or N can be \"auto\" to set the\n"
+" width automatically.\n"
+" --tablename NAME Set the name of the table for \"insert\" mode.\n"
+" --tag NAME Save mode to the left as NAME.\n"
+" --textjsonb BOOLEAN If enabled, JSONB text is displayed as text JSON.\n"
+" --title ARG Whether or not to show column headers, and if so\n"
+" how to encode them. ARG can be \"off\", \"on\",\n"
+" \"sql\", \"csv\", \"html\", \"tcl\", or \"json\".\n"
+" --titlelimit N Limit the length of column titles to N characters.\n"
+" -v|--verbose Verbose output\n"
+" --widths LIST Set the columns widths for columnar modes. The\n"
+" argument is a list of integers, one for each\n"
+" column. A \"0\" width means use a dynamic width\n"
+" based on the actual width of data. If there are\n"
+" fewer entries in LIST than columns, \"0\" is used\n"
+" for the unspecified widths.\n"
+" --wordwrap BOOLEAN Enable/disable word wrapping\n"
+" --wrap N Wrap columns wider than N characters\n"
+" --ww Shorthand for \"--wordwrap on\"\n"
+ },
+ { ".output",
+"USAGE: .output [OPTIONS] [FILE]\n"
+"\n"
+"Begin redirecting output to FILE. Or if FILE is omitted, revert\n"
+"to sending output to the console. If FILE begins with \"|\" then\n"
+"the remainder of file is taken as a pipe and output is directed\n"
+"into that pipe. If FILE is \"memory\" then output is captured in an\n"
+"internal memory buffer. If FILE is \"off\" then output is redirected\n"
+"into /dev/null or the equivalent.\n"
+"\n"
+"Options:\n"
+" --bom Prepend a byte-order mark to the output\n"
+" -e Accumulate output in a temporary text file then\n"
+" launch a text editor when the redirection ends.\n"
+" --error-prefix X Use X as the left-margin prefix for error messages.\n"
+" Set to an empty string to restore the default.\n"
+" --keep Keep redirecting output to its current destination.\n"
+" Use this option in combination with --show or\n"
+" with --error-prefix when you do not want to stop\n"
+" a current redirection.\n"
+" --plain Use plain text rather than HTML tables with -w\n"
+" --show Show output text captured by .testcase or by\n"
+" redirecting to \"memory\".\n"
+" -w Show the output in a web browser. Output is\n"
+" written into a temporary HTML file until the\n"
+" redirect ends, then the web browser is launched.\n"
+" Query results are shown as HTML tables, unless\n"
+" the --plain is used too.\n"
+" -x Show the output in a spreadsheet. Output is\n"
+" written to a temp file as CSV then the spreadsheet\n"
+" is launched when\n"
+ },
+ { ".once",
+"USAGE: .once [OPTIONS] FILE ...\n"
+"\n"
+"Write the output for the next line of SQL or the next dot-command into\n"
+"FILE. If FILE begins with \"|\" then it is a program into which output\n"
+"is written. The FILE argument should be omitted if one of the -e, -w,\n"
+"or -x options is used.\n"
+"\n"
+"Options:\n"
+" -e Capture output into a temporary file then bring up\n"
+" a text editor on that temporary file.\n"
+" --plain Use plain text rather than HTML tables with -w\n"
+" -w Capture output into an HTML file then bring up that\n"
+" file in a web browser\n"
+" -x Show the output in a spreadsheet. Output is\n"
+" written to a temp file as CSV then the spreadsheet\n"
+" is launched when\n"
+ },
+ { ".check",
+"USAGE: .check [OPTIONS] PATTERN\n"
+"\n"
+"Verify results of commands since the most recent .testcase command.\n"
+"Restore output to the console, unless --keep is used.\n"
+"\n"
+"If PATTERN starts with \"< return false
+** * If the size of the file is not a multiple of 512 -> return false
+** * If sqlite3_open() fails -> return false
+** * if sqlite3_prepare() or sqlite3_step() fails -> return false
+** * Otherwise -> return true
+*/
+static int isDatabaseFile(const char *zFile, int openFlags){
+ sqlite3 *db = 0;
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ sqlite3_int64 sz = fileSize(zFile);
+ if( sz<512 || (sz%512)!=0 ) return 0;
+ if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK
+ && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0)
+ ==SQLITE_OK
+ && sqlite3_step(pStmt)==SQLITE_ROW
+ ){
+ rc = 1;
+ }else{
+ rc = 0;
+ }
+ sqlite3_finalize(pStmt);
+ sqlite3_close(db);
+ return rc;
+}
+
/*
** Try to deduce the type of file for zName based on its content. Return
** one of the SHELL_OPEN_* constants.
@@ -25786,18 +27729,18 @@ static int session_filter(void *pCtx, const char *zTab){
** Otherwise, assume an ordinary database regardless of the filename if
** the type cannot be determined from content.
*/
-int deduceDatabaseType(const char *zName, int dfltZip){
- FILE *f = sqlite3_fopen(zName, "rb");
+int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){
+ FILE *f;
size_t n;
int rc = SHELL_OPEN_UNSPEC;
char zBuf[100];
- if( f==0 ){
- if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
- return SHELL_OPEN_ZIPFILE;
- }else{
- return SHELL_OPEN_NORMAL;
- }
+ if( access(zName,0)!=0 ) goto database_type_by_name;
+ if( isDatabaseFile(zName, openFlags) ){
+ rc = SHELL_OPEN_NORMAL;
}
+ if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL;
+ f = sqlite3_fopen(zName, "rb");
+ if( f==0 ) goto database_type_by_name;
n = fread(zBuf, 16, 1, f);
if( n==1 && memcmp(zBuf, "SQLite format 3", 16)==0 ){
fclose(f);
@@ -25819,6 +27762,43 @@ int deduceDatabaseType(const char *zName, int dfltZip){
}
fclose(f);
return rc;
+
+database_type_by_name:
+ if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
+ rc = SHELL_OPEN_ZIPFILE;
+ }else{
+ rc = SHELL_OPEN_NORMAL;
+ }
+ return rc;
+}
+
+/*
+** If the text in z[] is the name of a readable file and that file appears
+** to contain SQL text and/or dot-commands, then return true. If z[] is
+** not a file, or if the file is unreadable, or if the file is a database
+** or anything else that is not SQL text and dot-commands, then return false.
+**
+** If the bLeaveUninit flag is set, then be sure to leave SQLite in an
+** uninitialized state. This means invoking sqlite3_shutdown() after any
+** SQLite API is used.
+**
+** Some amount of guesswork is involved in this decision.
+*/
+static int isScriptFile(const char *z, int bLeaveUninit){
+ sqlite3_int64 sz = fileSize(z);
+ if( sz<=0 ) return 0;
+ if( (sz%512)==0 ){
+ int rc;
+ sqlite3_initialize();
+ rc = isDatabaseFile(z, SQLITE_OPEN_READONLY);
+ if( bLeaveUninit ){
+ sqlite3_shutdown();
+ }
+ if( rc ) return 0; /* Is a database */
+ }
+ if( sqlite3_strlike("%.sql",z,0)==0 ) return 1;
+ if( sqlite3_strlike("%.txt",z,0)==0 ) return 1;
+ return 0;
}
#ifndef SQLITE_OMIT_DESERIALIZE
@@ -25829,11 +27809,11 @@ int deduceDatabaseType(const char *zName, int dfltZip){
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
- int nLine;
- int n = 0;
+ i64 nLine;
+ int n = 0; /* Size of db per first line of hex dump */
+ i64 sz = 0; /* n rounded up to nearest page boundary */
int pgsz = 0;
- int iOffset = 0;
- int j, k;
+ i64 iOffset = 0;
int rc;
FILE *in;
const char *zDbFilename = p->pAuxDb->zDbFilename;
@@ -25842,7 +27822,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
if( zDbFilename ){
in = sqlite3_fopen(zDbFilename, "r");
if( in==0 ){
- sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename);
+ cli_printf(stderr,"cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
@@ -25857,16 +27837,21 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<0 ) goto readHexDb_error;
- if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ) goto readHexDb_error;
- n = (n+pgsz-1)&~(pgsz-1); /* Round n up to the next multiple of pgsz */
- a = sqlite3_malloc( n ? n : 1 );
- shell_check_oom(a);
- memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
- sqlite3_fputs("invalid pagesize\n", stderr);
+ cli_puts("invalid pagesize\n", stderr);
goto readHexDb_error;
}
+ sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */
+ a = sqlite3_malloc64( sz ? sz : 1 );
+ shell_check_oom(a);
+ memset(a, 0, sz);
for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
+ int j = 0; /* Page number from "| page" line */
+ int k = 0; /* Offset from "| page" line */
+ if( nLine>=2000000000 ){
+ cli_printf(stderr, "input too big\n");
+ goto readHexDb_error;
+ }
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
@@ -25879,14 +27864,14 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
- k = iOffset+j;
- if( k+16<=n && k>=0 ){
+ i64 iOff = iOffset+j;
+ if( iOff+16<=sz && iOff>=0 ){
int ii;
- for(ii=0; ii<16; ii++) a[k+ii] = x[ii]&0xff;
+ for(ii=0; ii<16; ii++) a[iOff+ii] = x[ii]&0xff;
}
}
}
- *pnData = n;
+ *pnData = sz;
if( in!=p->in ){
fclose(in);
}else{
@@ -25905,7 +27890,7 @@ readHexDb_error:
p->lineno = nLine;
}
sqlite3_free(a);
- sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine);
+ cli_printf(stderr,"Error on line %lld of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
@@ -25953,7 +27938,7 @@ static void shellModuleSchema(
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
- free(zFake);
+ sqlite3_free(zFake);
}
}
@@ -25982,13 +27967,16 @@ static void open_db(ShellState *p, int openFlags){
p->openMode = SHELL_OPEN_NORMAL;
}else{
p->openMode = (u8)deduceDatabaseType(zDbFilename,
- (openFlags & OPEN_DB_ZIPFILE)!=0);
+ (openFlags & OPEN_DB_ZIPFILE)!=0, p->openFlags);
}
}
+ if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
+ if( p->openFlags==0 ) p->openFlags = SQLITE_OPEN_CREATE;
+ p->openFlags |= SQLITE_OPEN_READWRITE;
+ }
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, "apndvfs");
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, "apndvfs");
break;
}
case SHELL_OPEN_HEXDB:
@@ -26000,32 +27988,26 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_open(":memory:", &p->db);
break;
}
- case SHELL_OPEN_READONLY: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READONLY|p->openFlags, 0);
- break;
- }
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
- sqlite3_open_v2(zDbFilename, &p->db,
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
+ sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
+ cli_printf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
- exit(1);
+ cli_exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- sqlite3_fputs("Also: unable to open substitute in-memory database.\n",
+ cli_puts("Also: unable to open substitute in-memory database.\n",
stderr);
- exit(1);
+ cli_exit(1);
}else{
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Notice: using substitute in-memory database instead of \"%s\"\n",
zDbFilename);
}
@@ -26048,7 +28030,6 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
- sqlite3_percentile_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
@@ -26104,6 +28085,8 @@ static void open_db(ShellState *p, int openFlags){
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
+ sqlite3_create_function(p->db, "shell_format_schema", 2, SQLITE_UTF8, p,
+ shellFormatSchema, 0, 0);
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
shellUSleepFunc, 0, 0);
#ifndef SQLITE_NOHAVE_SYSTEM
@@ -26138,7 +28121,7 @@ static void open_db(ShellState *p, int openFlags){
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
- sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc);
+ cli_printf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
@@ -26147,11 +28130,13 @@ static void open_db(ShellState *p, int openFlags){
#endif
}
if( p->db!=0 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
+#endif
sqlite3_db_config(
- p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
+ p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->mode.scanstatsOn, (int*)0
);
}
}
@@ -26162,7 +28147,7 @@ static void open_db(ShellState *p, int openFlags){
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
@@ -26335,7 +28320,7 @@ static int booleanValue(const char *zArg){
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
return 0;
}
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
return 0;
}
@@ -26363,18 +28348,18 @@ static void output_file_close(FILE *f){
** recognized and do the right thing. NULL is returned if the output
** filename is "off".
*/
-static FILE *output_file_open(const char *zFile){
+static FILE *output_file_open(ShellState *p, const char *zFile){
FILE *f;
if( cli_strcmp(zFile,"stdout")==0 ){
f = stdout;
}else if( cli_strcmp(zFile, "stderr")==0 ){
f = stderr;
- }else if( cli_strcmp(zFile, "off")==0 ){
+ }else if( cli_strcmp(zFile, "off")==0 || p->bSafeMode ){
f = 0;
}else{
f = sqlite3_fopen(zFile, "w");
if( f==0 ){
- sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile);
+ cli_printf(stderr,"Error: cannot open \"%s\"\n", zFile);
}
}
return f;
@@ -26427,12 +28412,12 @@ static int sql_trace_callback(
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
- sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
+ cli_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
- sqlite3_fprintf(p->traceOut,
+ cli_printf(p->traceOut,
"%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
break;
}
@@ -26461,9 +28446,11 @@ struct ImportCtx {
const char *zFile; /* Name of the input file */
FILE *in; /* Read the CSV text from this input stream */
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
+ char *zIn; /* Input text */
char *z; /* Accumulated text for a field */
- int n; /* Number of bytes in z */
- int nAlloc; /* Space allocated for z[] */
+ i64 nUsed; /* Bytes of zIn[] used so far */
+ i64 n; /* Number of bytes in z */
+ i64 nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int nRow; /* Number of rows imported */
int nErr; /* Number of errors encountered */
@@ -26471,6 +28458,8 @@ struct ImportCtx {
int cTerm; /* Character that terminated the most recent field */
int cColSep; /* The column separator character. (Usually ",") */
int cRowSep; /* The row separator character. (Usually "\n") */
+ int cQEscape; /* Escape character with "...". 0 for none */
+ int cUQEscape; /* Escape character not with "...". 0 for none */
};
/* Clean up resourced used by an ImportCtx */
@@ -26481,9 +28470,28 @@ static void import_cleanup(ImportCtx *p){
}
sqlite3_free(p->z);
p->z = 0;
+ if( p->zIn ){
+ sqlite3_free(p->zIn);
+ p->zIn = 0;
+ }
}
-/* Append a single byte to z[] */
+/* Read a single character of the .import input text. Return EOF
+** at end-of-file.
+*/
+static int import_getc(ImportCtx *p){
+ if( p->in ){
+ return fgetc(p->in);
+ }else if( p->zIn && p->zIn[p->nUsed]!=0 ){
+ return p->zIn[p->nUsed++];
+ }else{
+ return EOF;
+ }
+}
+
+/* Append a single byte to the field value begin constructed
+** in the p->z[] buffer
+*/
static void import_append_char(ImportCtx *p, int c){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
@@ -26499,8 +28507,8 @@ static void import_append_char(ImportCtx *p, int c){
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc64().
-** + Use p->cSep as the column separator. The default is ",".
-** + Use p->rSep as the row separator. The default is "\n".
+** + Use p->cColSep as the column separator. The default is ",".
+** + Use p->cRowSep as the row separator. The default is "\n".
** + Keep track of the line number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
@@ -26511,7 +28519,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
@@ -26520,10 +28528,17 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
int pc, ppc;
int startLine = p->nLine;
int cQuote = c;
+ int cEsc = (u8)p->cQEscape;
pc = ppc = 0;
while( 1 ){
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==rSep ) p->nLine++;
+ if( c==cEsc && cEsc!=0 ){
+ c = import_getc(p);
+ import_append_char(p, c);
+ ppc = pc = 0;
+ continue;
+ }
if( c==cQuote ){
if( pc==cQuote ){
pc = 0;
@@ -26540,11 +28555,11 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
break;
}
if( pc==cQuote && c!='\r' ){
- sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n",
- p->zFile, p->nLine, cQuote);
+ cli_printf(stderr,"%s:%d: unescaped %c character\n",
+ p->zFile, p->nLine, cQuote);
}
if( c==EOF ){
- sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n",
+ cli_printf(stderr,"%s:%d: unterminated %c-quoted field\n",
p->zFile, startLine, cQuote);
p->cTerm = c;
break;
@@ -26556,12 +28571,13 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
}else{
/* If this is the first field being parsed and it begins with the
** UTF-8 BOM (0xEF BB BF) then skip the BOM */
+ int cEsc = p->cUQEscape;
if( (c&0xff)==0xef && p->bNotFirst==0 ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
if( (c&0xff)==0xbb ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
if( (c&0xff)==0xbf ){
p->bNotFirst = 1;
p->n = 0;
@@ -26570,8 +28586,9 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
}
}
while( c!=EOF && c!=cSep && c!=rSep ){
+ if( c==cEsc && cEsc!=0 ) c = import_getc(p);
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
}
if( c==rSep ){
p->nLine++;
@@ -26589,8 +28606,8 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
** + Input comes from p->in.
** + Store results in p->z of length p->n. Space to hold p->z comes
** from sqlite3_malloc64().
-** + Use p->cSep as the column separator. The default is "\x1F".
-** + Use p->rSep as the row separator. The default is "\x1E".
+** + Use p->cColSep as the column separator. The default is "\x1F".
+** + Use p->cRowSep as the row separator. The default is "\x1E".
** + Keep track of the row number in p->nLine.
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
@@ -26601,14 +28618,14 @@ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){
int cSep = (u8)p->cColSep;
int rSep = (u8)p->cRowSep;
p->n = 0;
- c = fgetc(p->in);
+ c = import_getc(p);
if( c==EOF || seenInterrupt ){
p->cTerm = EOF;
return 0;
}
while( c!=EOF && c!=cSep && c!=rSep ){
import_append_char(p, c);
- c = fgetc(p->in);
+ c = import_getc(p);
}
if( c==rSep ){
p->nLine++;
@@ -26643,7 +28660,7 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
+ cli_printf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_data_xfer;
}
@@ -26660,7 +28677,7 @@ static void tryToCloneData(
memcpy(zInsert+i, ");", 3);
rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n",
+ cli_printf(stderr,"Error %d: %s on [%s]\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert);
goto end_data_xfer;
}
@@ -26696,7 +28713,7 @@ static void tryToCloneData(
} /* End for */
rc = sqlite3_step(pInsert);
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
- sqlite3_fprintf(stderr,"Error %d: %s\n",
+ cli_printf(stderr,"Error %d: %s\n",
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb));
}
sqlite3_reset(pInsert);
@@ -26714,7 +28731,7 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable);
+ cli_printf(stderr,"Warning: cannot step \"%s\" backwards", zTable);
break;
}
} /* End for(k=0...) */
@@ -26751,7 +28768,7 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db),
sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
@@ -26761,10 +28778,10 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){
- sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
+ cli_printf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
@@ -26782,7 +28799,7 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n",
+ cli_printf(stderr,"Error: (%d) %s on [%s]\n",
sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
@@ -26791,10 +28808,10 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue;
- sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout);
+ cli_printf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
@@ -26818,12 +28835,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){
int rc;
sqlite3 *newDb = 0;
if( access(zNewDb,0)==0 ){
- sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb);
+ cli_printf(stderr,"File \"%s\" already exists.\n", zNewDb);
return;
}
rc = sqlite3_open(zNewDb, &newDb);
if( rc ){
- sqlite3_fprintf(stderr,
+ cli_printf(stderr,
"Cannot create output database: %s\n", sqlite3_errmsg(newDb));
}else{
sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
@@ -26842,12 +28859,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){
*/
static void output_redir(ShellState *p, FILE *pfNew){
if( p->out != stdout ){
- sqlite3_fputs("Output already redirected.\n", stderr);
+ cli_puts("Output already redirected.\n", stderr);
}else{
p->out = pfNew;
setCrlfMode(p);
- if( p->mode==MODE_Www ){
- sqlite3_fputs(
+ if( p->mode.eMode==MODE_Www ){
+ cli_puts(
"\n"
"\n",
p->out
@@ -26869,8 +28886,8 @@ static void output_reset(ShellState *p){
pclose(p->out);
#endif
}else{
- if( p->mode==MODE_Www ){
- sqlite3_fputs("\n", p->out);
+ if( p->mode.eMode==MODE_Www ){
+ cli_puts("