diff options
Diffstat (limited to 'src/shell.c')
-rw-r--r-- | src/shell.c | 209 |
1 files changed, 153 insertions, 56 deletions
diff --git a/src/shell.c b/src/shell.c index 36f6970d1..ec255a673 100644 --- a/src/shell.c +++ b/src/shell.c @@ -729,6 +729,61 @@ static char quoteChar(const char *zName){ return 0; } +/* +** SQL function: shell_add_schema(S,X) +** +** Add the schema name X to the CREATE statement in S and return the result. +** Examples: +** +** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); +** +** Also works on +** +** CREATE INDEX +** CREATE UNIQUE INDEX +** CREATE VIEW +** CREATE TRIGGER +** CREATE VIRTUAL TABLE +** +** This UDF is used by the .schema command to insert the schema name of +** attached databases into the middle of the sqlite_master.sql field. +*/ +static void shellAddSchemaName( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + static const char *aPrefix[] = { + "TABLE", + "INDEX", + "UNIQUE INDEX", + "VIEW", + "TRIGGER", + "VIRTUAL TABLE" + }; + int i = 0; + const char *zIn = (const char*)sqlite3_value_text(apVal[0]); + const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); + assert( nVal==2 ); + if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ + for(i=0; i<sizeof(aPrefix)/sizeof(aPrefix[0]); i++){ + int n = strlen30(aPrefix[i]); + if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){ + char cQuote = quoteChar(zSchema); + char *z; + if( cQuote ){ + z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8); + }else{ + z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8); + } + sqlite3_result_text(pCtx, z, -1, sqlite3_free); + return; + } + } + } + sqlite3_result_value(pCtx, apVal[0]); +} + /****************************************************************************** ** SHA3 hash implementation copied from ../ext/misc/shathree.c */ @@ -2308,7 +2363,7 @@ static void set_table_name(ShellState *p, const char *zName){ if( zName==0 ) return; cQuote = quoteChar(zName); n = strlen30(zName); - if( cQuote ) n += 2; + if( cQuote ) n += n+2; z = p->zDestTable = malloc( n+1 ); if( z==0 ){ raw_printf(stderr,"Error: out of memory\n"); @@ -3536,6 +3591,9 @@ static void open_db(ShellState *p, int keepAlive){ sha3QueryFunc, 0, 0); sqlite3_create_function(p->db, "sha3_query", 2, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); + sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, + shellAddSchemaName, 0, 0); + } } @@ -3764,6 +3822,7 @@ struct ImportCtx { int n; /* Number of bytes in z */ int nAlloc; /* Space allocated for z[] */ int nLine; /* Current line number */ + int bNotFirst; /* True if one or more bytes already read */ int cTerm; /* Character that terminated the most recent field */ int cColSep; /* The column separator character. (Usually ",") */ int cRowSep; /* The row separator character. (Usually "\n") */ @@ -3843,6 +3902,21 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ pc = c; } }else{ + /* If this is the first field being parsed and it begins with the + ** UTF-8 BOM (0xEF BB BF) then skip the BOM */ + if( (c&0xff)==0xef && p->bNotFirst==0 ){ + import_append_char(p, c); + c = fgetc(p->in); + if( (c&0xff)==0xbb ){ + import_append_char(p, c); + c = fgetc(p->in); + if( (c&0xff)==0xbf ){ + p->bNotFirst = 1; + p->n = 0; + return csv_read_one_field(p); + } + } + } while( c!=EOF && c!=cSep && c!=rSep ){ import_append_char(p, c); c = fgetc(p->in); @@ -3854,6 +3928,7 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ p->cTerm = c; } if( p->z ) p->z[p->n] = 0; + p->bNotFirst = 1; return p->z; } @@ -5689,12 +5764,17 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ + ShellText sSelect; ShellState data; char *zErrMsg = 0; + const char *zDiv = 0; + int iSchema = 0; + open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; + initText(&sSelect); if( nArg>=2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg--; @@ -5732,33 +5812,62 @@ static int do_meta_command(char *zLine, ShellState *p){ callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ - char *zSql; - zSql = sqlite3_mprintf( - "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " - "WHERE lower(tbl_name) LIKE %Q" - " AND type!='meta' AND sql NOTNULL " - "ORDER BY rowid", azArg[1]); - rc = sqlite3_exec(p->db, zSql, callback, &data, &zErrMsg); - sqlite3_free(zSql); + zDiv = "("; } }else if( nArg==1 ){ - rc = sqlite3_exec(p->db, - "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " - "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " - "ORDER BY rowid", - callback, &data, &zErrMsg - ); + zDiv = "("; }else{ raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } + if( zDiv ){ + sqlite3_stmt *pStmt = 0; + sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", + -1, &pStmt, 0); + appendText(&sSelect, "SELECT sql FROM", 0); + iSchema = 0; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); + char zScNum[30]; + sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); + appendText(&sSelect, zDiv, 0); + zDiv = " UNION ALL "; + if( strcmp(zDb, "main")!=0 ){ + appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); + appendText(&sSelect, zDb, '"'); + appendText(&sSelect, ") AS sql, type, tbl_name, name, rowid,", 0); + appendText(&sSelect, zScNum, 0); + appendText(&sSelect, " AS snum, ", 0); + appendText(&sSelect, zDb, '\''); + appendText(&sSelect, " AS sname FROM ", 0); + appendText(&sSelect, zDb, '"'); + appendText(&sSelect, ".sqlite_master", 0); + }else{ + appendText(&sSelect, "SELECT sql, type, tbl_name, name, rowid, ", 0); + appendText(&sSelect, zScNum, 0); + appendText(&sSelect, " AS snum, 'main' AS sname FROM sqlite_master",0); + } + } + sqlite3_finalize(pStmt); + appendText(&sSelect, ") WHERE ", 0); + if( nArg>1 ){ + char *zQarg = sqlite3_mprintf("%Q", azArg[1]); + if( strchr(azArg[1], '.') ){ + appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); + }else{ + appendText(&sSelect, "lower(tbl_name)", 0); + } + appendText(&sSelect, strchr(azArg[1], '*') ? " GLOB " : " LIKE ", 0); + appendText(&sSelect, zQarg, 0); + appendText(&sSelect, " AND ", 0); + sqlite3_free(zQarg); + } + appendText(&sSelect, "type!='meta' AND sql IS NOT NULL" + " ORDER BY snum, rowid", 0); + rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); + freeText(&sSelect); + } if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); @@ -6307,59 +6416,47 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_stmt *pStmt; char **azResult; int nRow, nAlloc; - char *zSql = 0; int ii; + ShellText s; + initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ) return shellDatabaseError(p->db); - /* Create an SQL statement to query for the list of tables in the - ** main and all attached databases where the table name matches the - ** LIKE pattern bound to variable "?1". */ - if( c=='t' ){ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_master" - " WHERE type IN ('table','view')" - " AND name NOT LIKE 'sqlite_%%'" - " AND name LIKE ?1"); - }else if( nArg>2 ){ + if( nArg>2 && c=='i' ){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; - }else{ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_master" - " WHERE type='index'" - " AND tbl_name LIKE ?1"); } - for(ii=0; zSql && sqlite3_step(pStmt)==SQLITE_ROW; ii++){ + for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); - if( zDbName==0 || ii==0 ) continue; + if( zDbName==0 ) continue; + if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); + if( sqlite3_stricmp(zDbName, "main")==0 ){ + appendText(&s, "SELECT name FROM ", 0); + }else{ + appendText(&s, "SELECT ", 0); + appendText(&s, zDbName, '\''); + appendText(&s, "||'.'||name FROM ", 0); + } + appendText(&s, zDbName, '"'); + appendText(&s, ".sqlite_master ", 0); if( c=='t' ){ - 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); + appendText(&s," WHERE type IN ('table','view')" + " AND name NOT LIKE 'sqlite_%'" + " AND name LIKE ?1", 0); }else{ - zSql = sqlite3_mprintf( - "%z UNION ALL " - "SELECT '%q.' || name FROM \"%w\".sqlite_master" - " WHERE type='index'" - " AND tbl_name LIKE ?1", zSql, zDbName, zDbName); + appendText(&s," WHERE type='index'" + " AND tbl_name LIKE ?1", 0); } } rc = sqlite3_finalize(pStmt); - if( zSql && rc==SQLITE_OK ){ - zSql = sqlite3_mprintf("%z ORDER BY 1", zSql); - if( zSql ) rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - } - sqlite3_free(zSql); - if( !zSql ) return shellNomemError(); + appendText(&s, " ORDER BY 1", 0); + rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); + freeText(&s); if( rc ) return shellDatabaseError(p->db); /* Run the SQL statement prepared by the above block. Store the results |