summaryrefslogtreecommitdiff
path: root/usr.bin/sqlite3/shell.c
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2012-05-25 14:08:49 +0000
committerMarc Espie <espie@cvs.openbsd.org>2012-05-25 14:08:49 +0000
commitccdb82dfeaa35d647dd23ba7e0afbe92fe845a81 (patch)
tree87c1f44f52c7b3bbc55398732c3511017b46ba9d /usr.bin/sqlite3/shell.c
parentca852ec007e22402e4ee247574c405f90df49824 (diff)
grr... another C99-only assignment.
Diffstat (limited to 'usr.bin/sqlite3/shell.c')
-rw-r--r--usr.bin/sqlite3/shell.c271
1 files changed, 179 insertions, 92 deletions
diff --git a/usr.bin/sqlite3/shell.c b/usr.bin/sqlite3/shell.c
index d3ddfa95360..c58738bdb4f 100644
--- a/usr.bin/sqlite3/shell.c
+++ b/usr.bin/sqlite3/shell.c
@@ -68,6 +68,8 @@
# include <io.h>
#define isatty(h) _isatty(h)
#define access(f,m) _access((f),(m))
+#define popen(a,b) _popen((a),(b))
+#define pclose(x) _pclose(x)
#else
/* Make sure isatty() has a prototype.
*/
@@ -419,6 +421,7 @@ struct callback_data {
int statsOn; /* True to display memory stats before each finalize */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
+ FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */
int mode; /* An output mode setting */
int writableSchema; /* True if PRAGMA writable_schema=ON */
@@ -496,7 +499,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
char *zBlob = (char *)pBlob;
fprintf(out,"X'");
- for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]); }
+ for(i=0; i<nBlob; i++){ fprintf(out,"%02x",zBlob[i]&0xff); }
fprintf(out,"'");
}
@@ -1077,6 +1080,9 @@ static int display_stats(
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
fprintf(pArg->out, "Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
+ fprintf(pArg->out, "Page cache writes: %d\n", iCur);
+ iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
@@ -1287,7 +1293,6 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
char *zTableInfo = 0;
char *zTmp = 0;
int nRow = 0;
- int kk;
zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
zTableInfo = appendText(zTableInfo, zTable, '"');
@@ -1300,14 +1305,12 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
}
zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
- if( !isalpha(zTable[0]) ){
- kk = 0;
- }else{
- for(kk=1; isalnum(zTable[kk]); kk++){}
- }
- zTmp = appendText(zTmp, zTable, zTable[kk] ? '"' : 0);
+ /* Always quote the table name, even if it appears to be pure ascii,
+ ** in case it is a keyword. Ex: INSERT INTO "table" ... */
+ zTmp = appendText(zTmp, zTable, '"');
if( zTmp ){
zSelect = appendText(zSelect, zTmp, '\'');
+ free(zTmp);
}
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
rc = sqlite3_step(pTableInfo);
@@ -1336,7 +1339,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
run_table_dump_query(p, zSelect, 0);
}
- if( zSelect ) free(zSelect);
+ free(zSelect);
}
return 0;
}
@@ -1366,7 +1369,7 @@ static int run_schema_dump_query(
}
zQ2 = malloc( len+100 );
if( zQ2==0 ) return rc;
- sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery);
+ sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
@@ -1432,6 +1435,7 @@ static char zHelp[] =
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
+ ".trace FILE|off Output each SQL statement as it is run\n"
".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
;
@@ -1522,6 +1526,52 @@ static int booleanValue(char *zArg){
}
/*
+** Close an output file, assuming it is not stderr or stdout
+*/
+static void output_file_close(FILE *f){
+ if( f && f!=stdout && f!=stderr ) fclose(f);
+}
+
+/*
+** Try to open an output file. The names "stdout" and "stderr" are
+** recognized and do the right thing. NULL is returned if the output
+** filename is "off".
+*/
+static FILE *output_file_open(const char *zFile){
+ FILE *f;
+ if( strcmp(zFile,"stdout")==0 ){
+ f = stdout;
+ }else if( strcmp(zFile, "stderr")==0 ){
+ f = stderr;
+ }else if( strcmp(zFile, "off")==0 ){
+ f = 0;
+ }else{
+ f = fopen(zFile, "wb");
+ if( f==0 ){
+ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ }
+ }
+ return f;
+}
+
+/*
+** A routine for handling output from sqlite3_trace().
+*/
+static void sql_trace_callback(void *pArg, const char *z){
+ FILE *f = (FILE*)pArg;
+ if( f ) fprintf(f, "%s\n", z);
+}
+
+/*
+** A no-op routine that runs with the ".breakpoint" doc-command. This is
+** a useful spot to set a debugger breakpoint.
+*/
+static void test_breakpoint(void){
+ static int nCall = 0;
+ nCall++;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -1600,6 +1650,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
bail_on_error = booleanValue(azArg[1]);
}else
+ /* The undocumented ".breakpoint" command causes a call to the no-op
+ ** routine named test_breakpoint().
+ */
+ if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){
+ test_breakpoint();
+ }else
+
if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
struct callback_data data;
char *zErrMsg = 0;
@@ -1931,22 +1988,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
const char *zFile = azArg[1];
- if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){
- fclose(p->pLog);
- p->pLog = 0;
- }
- if( strcmp(zFile,"stdout")==0 ){
- p->pLog = stdout;
- }else if( strcmp(zFile, "stderr")==0 ){
- p->pLog = stderr;
- }else if( strcmp(zFile, "off")==0 ){
- p->pLog = 0;
- }else{
- p->pLog = fopen(zFile, "w");
- if( p->pLog==0 ){
- fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
- }
- }
+ output_file_close(p->pLog);
+ p->pLog = output_file_open(zFile);
}else
if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
@@ -1999,20 +2042,31 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
- if( p->out!=stdout ){
- fclose(p->out);
+ if( p->outfile[0]=='|' ){
+ pclose(p->out);
+ }else{
+ output_file_close(p->out);
}
- if( strcmp(azArg[1],"stdout")==0 ){
- p->out = stdout;
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout");
+ p->outfile[0] = 0;
+ if( azArg[1][0]=='|' ){
+ p->out = popen(&azArg[1][1], "w");
+ if( p->out==0 ){
+ fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
+ p->out = stdout;
+ rc = 1;
+ }else{
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ }
}else{
- p->out = fopen(azArg[1], "wb");
+ p->out = output_file_open(azArg[1]);
if( p->out==0 ){
- fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
+ if( strcmp(azArg[1],"off")!=0 ){
+ fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
+ }
p->out = stdout;
rc = 1;
} else {
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
}
}
}else
@@ -2194,46 +2248,71 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
+ sqlite3_stmt *pStmt;
char **azResult;
- int nRow;
- char *zErrMsg;
+ int nRow, nAlloc;
+ char *zSql = 0;
+ int ii;
open_db(p);
- if( nArg==1 ){
- rc = sqlite3_get_table(p->db,
- "SELECT name FROM sqlite_master "
- "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' "
- "UNION ALL "
- "SELECT name FROM sqlite_temp_master "
- "WHERE type IN ('table','view') "
- "ORDER BY 1",
- &azResult, &nRow, 0, &zErrMsg
- );
- }else{
- zShellStatic = azArg[1];
- rc = sqlite3_get_table(p->db,
- "SELECT name FROM sqlite_master "
- "WHERE type IN ('table','view') AND name LIKE shellstatic() "
- "UNION ALL "
- "SELECT name FROM sqlite_temp_master "
- "WHERE type IN ('table','view') AND name LIKE shellstatic() "
- "ORDER BY 1",
- &azResult, &nRow, 0, &zErrMsg
- );
- zShellStatic = 0;
+ rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
+ if( rc ) return rc;
+ zSql = sqlite3_mprintf(
+ "SELECT name FROM sqlite_master"
+ " WHERE type IN ('table','view')"
+ " AND name NOT LIKE 'sqlite_%%'"
+ " AND name LIKE ?1");
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
+ if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue;
+ if( strcmp(zDbName,"temp")==0 ){
+ zSql = sqlite3_mprintf(
+ "%z UNION ALL "
+ "SELECT 'temp.' || name FROM sqlite_temp_master"
+ " WHERE type IN ('table','view')"
+ " AND name NOT LIKE 'sqlite_%%'"
+ " AND name LIKE ?1", zSql);
+ }else{
+ zSql = sqlite3_mprintf(
+ "%z UNION ALL "
+ "SELECT '%q.' || name FROM \"%w\".sqlite_master"
+ " WHERE type IN ('table','view')"
+ " AND name NOT LIKE 'sqlite_%%'"
+ " AND name LIKE ?1", zSql, zDbName, zDbName);
+ }
}
- if( zErrMsg ){
- fprintf(stderr,"Error: %s\n", zErrMsg);
- sqlite3_free(zErrMsg);
- rc = 1;
- }else if( rc != SQLITE_OK ){
- fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n");
- rc = 1;
+ sqlite3_finalize(pStmt);
+ zSql = sqlite3_mprintf("%z ORDER BY 1", zSql);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc ) return rc;
+ nRow = nAlloc = 0;
+ azResult = 0;
+ if( nArg>1 ){
+ sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
}else{
+ sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
+ }
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ if( nRow>=nAlloc ){
+ char **azNew;
+ int n = nAlloc*2 + 10;
+ azNew = sqlite3_realloc(azResult, sizeof(azResult[0])*n);
+ if( azNew==0 ){
+ fprintf(stderr, "Error: out of memory\n");
+ break;
+ }
+ nAlloc = n;
+ azResult = azNew;
+ }
+ azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
+ if( azResult[nRow] ) nRow++;
+ }
+ sqlite3_finalize(pStmt);
+ if( nRow>0 ){
int len, maxlen = 0;
int i, j;
int nPrintCol, nPrintRow;
- for(i=1; i<=nRow; i++){
- if( azResult[i]==0 ) continue;
+ for(i=0; i<nRow; i++){
len = strlen30(azResult[i]);
if( len>maxlen ) maxlen = len;
}
@@ -2241,14 +2320,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( nPrintCol<1 ) nPrintCol = 1;
nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
for(i=0; i<nPrintRow; i++){
- for(j=i+1; j<=nRow; j+=nPrintRow){
- char *zSp = j<=nPrintRow ? "" : " ";
+ for(j=i; j<nRow; j+=nPrintRow){
+ char *zSp = j<nPrintRow ? "" : " ";
printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
}
printf("\n");
}
}
- sqlite3_free_table(azResult);
+ for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
+ sqlite3_free(azResult);
}else
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
@@ -2382,6 +2462,19 @@ static int do_meta_command(char *zLine, struct callback_data *p){
enableTimer = booleanValue(azArg[1]);
}else
+ if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
+ open_db(p);
+ output_file_close(p->traceOut);
+ p->traceOut = output_file_open(azArg[1]);
+#ifndef SQLITE_OMIT_TRACE
+ if( p->traceOut==0 ){
+ sqlite3_trace(p->db, 0, 0);
+ }else{
+ sqlite3_trace(p->db, sql_trace_callback, p->traceOut);
+ }
+#endif
+ }else
+
if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
printf("SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
@@ -2506,7 +2599,9 @@ static int process_input(struct callback_data *p, FILE *in){
free(zLine);
zLine = one_input_line(zSql, in);
if( zLine==0 ){
- break; /* We have reached EOF */
+ /* End of input */
+ if( stdin_is_interactive ) printf("\n");
+ break;
}
if( seenInterrupt ){
if( in!=0 ) break;
@@ -2593,25 +2688,26 @@ static int process_input(struct callback_data *p, FILE *in){
/*
** Return a pathname which is the user's home directory. A
-** 0 return indicates an error of some kind. Space to hold the
-** resulting string is obtained from malloc(). The calling
-** function should free the result.
+** 0 return indicates an error of some kind.
*/
static char *find_home_dir(void){
- char *home_dir = NULL;
+ static char *home_dir = NULL;
+ if( home_dir ) return home_dir;
#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL)
- struct passwd *pwent;
- uid_t uid = getuid();
- if( (pwent=getpwuid(uid)) != NULL) {
- home_dir = pwent->pw_dir;
+ {
+ struct passwd *pwent;
+ uid_t uid = getuid();
+ if( (pwent=getpwuid(uid)) != NULL) {
+ home_dir = pwent->pw_dir;
+ }
}
#endif
#if defined(_WIN32_WCE)
/* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv()
*/
- home_dir = strdup("/");
+ home_dir = "/";
#else
#if defined(_WIN32) || defined(WIN32) || defined(__OS2__)
@@ -2667,7 +2763,6 @@ static int process_sqliterc(
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *in = NULL;
- int nBuf;
int rc = 0;
if (sqliterc == NULL) {
@@ -2678,15 +2773,8 @@ static int process_sqliterc(
#endif
return 1;
}
- nBuf = strlen30(home_dir) + 16;
- zBuf = malloc( nBuf );
- if( zBuf==0 ){
- fprintf(stderr,"%s: Error: out of memory\n",Argv0);
- return 1;
- }
- sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir);
- free(home_dir);
- sqliterc = (const char*)zBuf;
+ zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
+ sqliterc = zBuf;
}
in = fopen(sqliterc,"rb");
if( in ){
@@ -2696,7 +2784,7 @@ static int process_sqliterc(
rc = process_input(p,in);
fclose(in);
}
- free(zBuf);
+ sqlite3_free(zBuf);
return rc;
}
@@ -3037,7 +3125,6 @@ int main(int argc, char **argv){
write_history(zHistory);
free(zHistory);
}
- free(zHome);
}else{
rc = process_input(&data, stdin);
}