# define raw_printf fprintf
#endif
+/* Indicate out-of-memory and exit. */
+static void shell_out_of_memory(void){
+ raw_printf(stderr,"Error: out of memory\n");
+ exit(1);
+}
+
/*
** Write I/O traces to the following stream.
*/
int bVerbose;
};
+/* A single line in the EQP output */
+typedef struct EQPGraphRow EQPGraphRow;
+struct EQPGraphRow {
+ int iSelectId; /* The SelectID for this row */
+ EQPGraphRow *pNext; /* Next row in sequence */
+ char zText[1]; /* Text to display for this row */
+};
+
+/* All EQP output is collected into an instance of the following */
+typedef struct EQPGraph EQPGraph;
+struct EQPGraph {
+ EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
+ EQPGraphRow *pLast; /* Last element of the pRow list */
+ char zPrefix[100]; /* Graph prefix */
+};
+
/*
** State information about the database connection is contained in an
** instance of the following structure.
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
+ u8 nEqpLevel; /* Depth of the EQP output graph */
+ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
+ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
#if defined(SQLITE_ENABLE_SESSION)
int nSession; /* Number of active sessions */
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */
#define MODE_Pretty 11 /* Pretty-print schemas */
+#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */
static const char *modeDescr[] = {
"line",
"explain",
"ascii",
"prettyprint",
+ "eqp"
};
/*
}
return 1;
}
-
+
+/*
+** Add a new entry to the EXPLAIN QUERY PLAN data
+*/
+static void eqp_append(ShellState *p, int iSelectId, const char *zText){
+ EQPGraphRow *pNew;
+ int nText = strlen30(zText);
+ pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
+ if( pNew==0 ) shell_out_of_memory();
+ pNew->iSelectId = iSelectId;
+ memcpy(pNew->zText, zText, nText+1);
+ pNew->pNext = 0;
+ if( p->sGraph.pLast ){
+ p->sGraph.pLast->pNext = pNew;
+ }else{
+ p->sGraph.pRow = pNew;
+ }
+ p->sGraph.pLast = pNew;
+}
+
+/*
+** Free and reset the EXPLAIN QUERY PLAN data that has been collected
+** in p->sGraph.
+*/
+static void eqp_reset(ShellState *p){
+ EQPGraphRow *pRow, *pNext;
+ for(pRow = p->sGraph.pRow; pRow; pRow = pNext){
+ pNext = pRow->pNext;
+ sqlite3_free(pRow);
+ }
+ memset(&p->sGraph, 0, sizeof(p->sGraph));
+}
+
+/* Return the next EXPLAIN QUERY PLAN line with iSelectId that occurs after
+** pOld, or return the first such line if pOld is NULL
+*/
+static EQPGraphRow *eqp_next_row(ShellState *p, int iSelectId, EQPGraphRow *pOld){
+ EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
+ while( pRow && pRow->iSelectId!=iSelectId ) pRow = pRow->pNext;
+ return pRow;
+}
+
+/* Render a single level of the graph shell having iSelectId. Called
+** recursively to render sublevels.
+*/
+static void eqp_render_level(ShellState *p, int iSelectId){
+ EQPGraphRow *pRow, *pNext;
+ int i;
+ int n = strlen30(p->sGraph.zPrefix);
+ char *z;
+ for(pRow = eqp_next_row(p, iSelectId, 0); pRow; pRow = pNext){
+ pNext = eqp_next_row(p, iSelectId, pRow);
+ z = pRow->zText;
+ utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
+ if( n<sizeof(p->sGraph.zPrefix)-7 && (z = strstr(z, " SUBQUER"))!=0 ){
+ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
+ if( strncmp(z, " SUBQUERY ", 9)==0 && (i = atoi(z+10))>iSelectId ){
+ eqp_render_level(p, i);
+ }else if( strncmp(z, " SUBQUERIES ", 12)==0 ){
+ i = atoi(z+12);
+ if( i>iSelectId ){
+ utf8_printf(p->out, "%s|--SUBQUERY %d\n", p->sGraph.zPrefix, i);
+ memcpy(&p->sGraph.zPrefix[n+3],"| ",4);
+ eqp_render_level(p, i);
+ }
+ z = strstr(z, " AND ");
+ if( z && (i = atoi(z+5))>iSelectId ){
+ p->sGraph.zPrefix[n+3] = 0;
+ utf8_printf(p->out, "%s`--SUBQUERY %d\n", p->sGraph.zPrefix, i);
+ memcpy(&p->sGraph.zPrefix[n+3]," ",4);
+ eqp_render_level(p, i);
+ }
+ }
+ p->sGraph.zPrefix[n] = 0;
+ }
+ }
+}
+
+/*
+** Display and reset the EXPLAIN QUERY PLAN data
+*/
+static void eqp_render(ShellState *p){
+ EQPGraphRow *pRow = p->sGraph.pRow;
+ if( pRow ){
+ if( pRow->zText[0]=='-' ){
+ if( pRow->pNext==0 ){
+ eqp_reset(p);
+ return;
+ }
+ utf8_printf(p->out, "%s\n", pRow->zText+3);
+ p->sGraph.pRow = pRow->pNext;
+ sqlite3_free(pRow);
+ }else{
+ utf8_printf(p->out, "QUERY PLAN\n");
+ }
+ p->sGraph.zPrefix[0] = 0;
+ eqp_render_level(p, 0);
+ eqp_reset(p);
+ }
+}
/*
** This is the callback routine that the shell
utf8_printf(p->out, "%s", p->rowSeparator);
break;
}
+ case MODE_EQP: {
+ eqp_append(p, atoi(azArg[0]), azArg[3]);
+ break;
+ }
}
return 0;
}
n = strlen30(zName);
if( cQuote ) n += n+2;
z = p->zDestTable = malloc( n+1 );
- if( z==0 ){
- raw_printf(stderr,"Error: out of memory\n");
- exit(1);
- }
+ if( z==0 ) shell_out_of_memory();
n = 0;
if( cQuote ) z[n++] = cQuote;
for(i=0; zName[i]; i++){
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc==SQLITE_OK ){
while( sqlite3_step(pExplain)==SQLITE_ROW ){
- raw_printf(pArg->out,"--EQP-- %d,",sqlite3_column_int(pExplain, 0));
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1));
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
- utf8_printf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
+ const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
+ int iSelectId = sqlite3_column_int(pExplain, 0);
+ if( zEQPLine[0]=='-' ) eqp_render(pArg);
+ eqp_append(pArg, iSelectId, zEQPLine);
}
+ eqp_render(pArg);
}
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
if( pArg ){
pArg->cMode = pArg->mode;
- if( pArg->autoExplain
- && sqlite3_column_count(pStmt)==8
- && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
- ){
- pArg->cMode = MODE_Explain;
+ if( pArg->autoExplain ){
+ if( sqlite3_column_count(pStmt)==8
+ && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
+ ){
+ pArg->cMode = MODE_Explain;
+ }
+ if( sqlite3_column_count(pStmt)==4
+ && sqlite3_strlike("EXPLAIN QUERY PLAN%", zStmtSql,0)==0 ){
+ pArg->cMode = MODE_EQP;
+ }
}
/* If the shell is currently in ".explain" mode, gather the extra
exec_prepared_stmt(pArg, pStmt);
explain_data_delete(pArg);
+ eqp_render(pArg);
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
- if( azCol==0 ){
- raw_printf(stderr, "Error: out of memory\n");
- exit(1);
- }
+ if( azCol==0 ) shell_out_of_memory();
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
if( sqlite3_column_int(pStmt, 5) ){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
p->z = sqlite3_realloc64(p->z, p->nAlloc);
- if( p->z==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( p->z==0 ) shell_out_of_memory();
}
p->z[p->n++] = (char)c;
}
}
n = sqlite3_column_count(pQuery);
zInsert = sqlite3_malloc64(200 + nTable + n*3);
- if( zInsert==0 ){
- raw_printf(stderr, "out of memory\n");
- goto end_data_xfer;
- }
+ if( zInsert==0 ) shell_out_of_memory();
sqlite3_snprintf(200+nTable,zInsert,
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
i = strlen30(zInsert);
return 1;
}
-/*
-** Print an out-of-memory message to stderr and return 1.
-*/
-static int shellNomemError(void){
- raw_printf(stderr, "Error: out of memory\n");
- return 1;
-}
-
/*
** Compare the pattern in zGlob[] against the text in z[]. Return TRUE
** if they match and FALSE (0) if they do not match.
sCtx.cRowSep = p->rowSeparator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
xCloser(sCtx.in);
- return 1;
+ shell_out_of_memory();
}
nByte = strlen30(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
if( nCol==0 ) return 0; /* no columns, no error */
zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
xCloser(sCtx.in);
- return 1;
+ shell_out_of_memory();
}
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
j = strlen30(zSql);
char **azNew;
int n2 = nAlloc*2 + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
- if( azNew==0 ){
- rc = shellNomemError();
- break;
- }
+ if( azNew==0 ) shell_out_of_memory();
nAlloc = n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
- if( 0==azResult[nRow] ){
- rc = shellNomemError();
- break;
- }
+ if( 0==azResult[nRow] ) shell_out_of_memory();
nRow++;
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
if( nSql+nLine+2>=nAlloc ){
nAlloc = nSql+nLine+100;
zSql = realloc(zSql, nAlloc);
- if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
- exit(1);
- }
+ if( zSql==0 ) shell_out_of_memory();
}
nSqlPrior = nSql;
if( nSql==0 ){
#if !SQLITE_SHELL_IS_UTF8
sqlite3_initialize();
argv = malloc(sizeof(argv[0])*argc);
- if( argv==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( argv==0 ) shell_out_of_memory();
for(i=0; i<argc; i++){
char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
int n;
- if( z==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( z==0 ) shell_out_of_memory();
n = (int)strlen(z);
argv[i] = malloc( n+1 );
- if( argv[i]==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( argv[i]==0 ) shell_out_of_memory();
memcpy(argv[i], z, n+1);
sqlite3_free(z);
}
readStdin = 0;
nCmd++;
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
- if( azCmd==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( azCmd==0 ) shell_out_of_memory();
azCmd[nCmd-1] = z;
}
}