SQLite

Check-in [45fba3d7]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Enhance the ".dump" command in the CLI so that it accepts multiple LIKE pattern arguments and shows the union of all matching tables.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 45fba3d7055f90f67005ca740687f060a2311db40c8f6a161bfea0424e6ebf6d
User & Date: drh 2020-03-25 12:50:13
Context
2020-03-25
20:31
For the LIKE pattern in the .dump command of the CLI, accept backslash as an escape character. (check-in: 029ceea6 user: drh tags: trunk)
12:50
Enhance the ".dump" command in the CLI so that it accepts multiple LIKE pattern arguments and shows the union of all matching tables. (check-in: 45fba3d7 user: drh tags: trunk)
2020-03-21
23:10
Fix to the recomputation of the colUsed field added by check-in [a9bb71ba708ba722]. This fixes ticket [5829597ac43811e3]. (check-in: 5d14a1c4 user: drh tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/shell.c.in.

2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
** If the number of columns is 1 and that column contains text "--"
** then write the semicolon on a separate line.  That way, if a
** "--" comment occurs at the end of the statement, the comment
** won't consume the semicolon terminator.
*/
static int run_table_dump_query(
  ShellState *p,           /* Query context */
  const char *zSelect,     /* SELECT statement to extract content */
  const char *zFirstRow    /* Print before first row, if not NULL */
){
  sqlite3_stmt *pSelect;
  int rc;
  int nResult;
  int i;
  const char *z;
  rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
  if( rc!=SQLITE_OK || !pSelect ){
    utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc,
                sqlite3_errmsg(p->db));
    if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
    return rc;
  }
  rc = sqlite3_step(pSelect);
  nResult = sqlite3_column_count(pSelect);
  while( rc==SQLITE_ROW ){
    if( zFirstRow ){
      utf8_printf(p->out, "%s", zFirstRow);
      zFirstRow = 0;
    }
    z = (const char*)sqlite3_column_text(pSelect, 0);
    utf8_printf(p->out, "%s", z);
    for(i=1; i<nResult; i++){
      utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
    }
    if( z==0 ) z = "";
    while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;







|
<
















<
<
<
<







2354
2355
2356
2357
2358
2359
2360
2361

2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377




2378
2379
2380
2381
2382
2383
2384
** If the number of columns is 1 and that column contains text "--"
** then write the semicolon on a separate line.  That way, if a
** "--" comment occurs at the end of the statement, the comment
** won't consume the semicolon terminator.
*/
static int run_table_dump_query(
  ShellState *p,           /* Query context */
  const char *zSelect      /* SELECT statement to extract content */

){
  sqlite3_stmt *pSelect;
  int rc;
  int nResult;
  int i;
  const char *z;
  rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
  if( rc!=SQLITE_OK || !pSelect ){
    utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc,
                sqlite3_errmsg(p->db));
    if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
    return rc;
  }
  rc = sqlite3_step(pSelect);
  nResult = sqlite3_column_count(pSelect);
  while( rc==SQLITE_ROW ){




    z = (const char*)sqlite3_column_text(pSelect, 0);
    utf8_printf(p->out, "%s", z);
    for(i=1; i<nResult; i++){
      utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
    }
    if( z==0 ) z = "";
    while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559

3560
3561
3562
3563
3564
3565
3566
  ".cd DIRECTORY            Change the working directory to DIRECTORY",
  ".changes on|off          Show number of rows changed by SQL",
  ".check GLOB              Fail if output since .testcase does not match",
  ".clone NEWDB             Clone data into NEWDB from the existing database",
  ".databases               List names and files of attached databases",
  ".dbconfig ?op? ?val?     List or change sqlite3_db_config() options",
  ".dbinfo ?DB?             Show status information about the database",
  ".dump ?TABLE? ...        Render all database content as SQL",
  "   Options:",
  "     --preserve-rowids      Include ROWID values in the output",
  "     --newlines             Allow unescaped newline characters in output",
  "   TABLE is a LIKE pattern for the tables to dump",

  ".echo on|off             Turn command echo on or off",
  ".eqp on|off|full|...     Enable or disable automatic EXPLAIN QUERY PLAN",
  "   Other Modes:",
#ifdef SQLITE_DEBUG
  "      test                  Show raw EXPLAIN QUERY PLAN output",
  "      trace                 Like \"full\" but enable \"PRAGMA vdbe_trace\"",
#endif







|




>







3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
  ".cd DIRECTORY            Change the working directory to DIRECTORY",
  ".changes on|off          Show number of rows changed by SQL",
  ".check GLOB              Fail if output since .testcase does not match",
  ".clone NEWDB             Clone data into NEWDB from the existing database",
  ".databases               List names and files of attached databases",
  ".dbconfig ?op? ?val?     List or change sqlite3_db_config() options",
  ".dbinfo ?DB?             Show status information about the database",
  ".dump ?TABLE?            Render database content as SQL",
  "   Options:",
  "     --preserve-rowids      Include ROWID values in the output",
  "     --newlines             Allow unescaped newline characters in output",
  "   TABLE is a LIKE pattern for the tables to dump",
  "   Additional LIKE patterns can be given in subsequent arguments",
  ".echo on|off             Turn command echo on or off",
  ".eqp on|off|full|...     Enable or disable automatic EXPLAIN QUERY PLAN",
  "   Other Modes:",
#ifdef SQLITE_DEBUG
  "      test                  Show raw EXPLAIN QUERY PLAN output",
  "      trace                 Like \"full\" but enable \"PRAGMA vdbe_trace\"",
#endif
7274
7275
7276
7277
7278
7279
7280
7281

7282
7283
7284
7285
7286
7287
7288
  if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
    open_db(p, 0);
    rc = recoverDatabaseCmd(p, nArg, azArg);
  }else
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */

  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    const char *zLike = 0;

    int i;
    int savedShowHeader = p->showHeader;
    int savedShellFlags = p->shellFlgs;
    ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
    for(i=1; i<nArg; i++){
      if( azArg[i][0]=='-' ){
        const char *z = azArg[i]+1;







|
>







7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
  if( c=='r' && strncmp(azArg[0], "recover", n)==0 ){
    open_db(p, 0);
    rc = recoverDatabaseCmd(p, nArg, azArg);
  }else
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */

  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    char *zLike = 0;
    char *zSql;
    int i;
    int savedShowHeader = p->showHeader;
    int savedShellFlags = p->shellFlgs;
    ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
    for(i=1; i<nArg; i++){
      if( azArg[i][0]=='-' ){
        const char *z = azArg[i]+1;
7302
7303
7304
7305
7306
7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350



7351
7352
7353
7354
7355
7356
7357

7358
7359
7360

7361
7362
7363
7364
7365
7366
7367
        }else
        {
          raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
          rc = 1;
          goto meta_command_exit;
        }
      }else if( zLike ){
        raw_printf(stderr, "Usage: .dump ?--preserve-rowids? "
                           "?--newlines? ?LIKE-PATTERN?\n");
        rc = 1;
        goto meta_command_exit;
      }else{
        zLike = azArg[i];
      }
    }

    open_db(p, 0);

    /* When playing back a "dump", the content might appear in an order
    ** which causes immediate foreign key constraints to be violated.
    ** So disable foreign-key constraint enforcement to prevent problems. */
    raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
    raw_printf(p->out, "BEGIN TRANSACTION;\n");
    p->writableSchema = 0;
    p->showHeader = 0;
    /* Set writable_schema=ON since doing so forces SQLite to initialize
    ** as much of the schema as it can even if the sqlite_master table is
    ** corrupt. */
    sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
    p->nErr = 0;
    if( zLike==0 ){
      run_schema_dump_query(p,
        "SELECT name, type, sql FROM sqlite_master "
        "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
      );
      run_schema_dump_query(p,
        "SELECT name, type, sql FROM sqlite_master "
        "WHERE name=='sqlite_sequence'"
      );
      run_table_dump_query(p,
        "SELECT sql FROM sqlite_master "
        "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
      );
    }else{
      char *zSql;
      zSql = sqlite3_mprintf(
        "SELECT name, type, sql FROM sqlite_master "
        "WHERE tbl_name LIKE %Q AND type=='table'"
        "  AND sql NOT NULL", zLike);



      run_schema_dump_query(p,zSql);
      sqlite3_free(zSql);
      zSql = sqlite3_mprintf(
        "SELECT sql FROM sqlite_master "
        "WHERE sql NOT NULL"
        "  AND type IN ('index','trigger','view')"
        "  AND tbl_name LIKE %Q", zLike);

      run_table_dump_query(p, zSql, 0);
      sqlite3_free(zSql);
    }

    if( p->writableSchema ){
      raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
      p->writableSchema = 0;
    }
    sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
    sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
    raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");







|
<
<
<

|

















|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
>
>
>
|
|
|
|
|
|
|
>
|
|
<
>







7299
7300
7301
7302
7303
7304
7305
7306



7307
7308
7309
7310
7311
7312
7313
7314
7315
7316
7317
7318
7319
7320
7321
7322
7323
7324
7325
7326














7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343

7344
7345
7346
7347
7348
7349
7350
7351
        }else
        {
          raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
          rc = 1;
          goto meta_command_exit;
        }
      }else if( zLike ){
        zLike = sqlite3_mprintf("%z OR name LIKE %Q", zLike, azArg[i]);



      }else{
        zLike = sqlite3_mprintf("name LIKE %Q", azArg[i]);
      }
    }

    open_db(p, 0);

    /* When playing back a "dump", the content might appear in an order
    ** which causes immediate foreign key constraints to be violated.
    ** So disable foreign-key constraint enforcement to prevent problems. */
    raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
    raw_printf(p->out, "BEGIN TRANSACTION;\n");
    p->writableSchema = 0;
    p->showHeader = 0;
    /* Set writable_schema=ON since doing so forces SQLite to initialize
    ** as much of the schema as it can even if the sqlite_master table is
    ** corrupt. */
    sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
    p->nErr = 0;
    if( zLike==0 ) zLike = sqlite3_mprintf("true");














    zSql = sqlite3_mprintf(
      "SELECT name, type, sql FROM sqlite_master "
      "WHERE (%s) AND type=='table'"
      "  AND sql NOT NULL"
      " ORDER BY tbl_name='sqlite_sequence', rowid",
      zLike
    );
    run_schema_dump_query(p,zSql);
    sqlite3_free(zSql);
    zSql = sqlite3_mprintf(
      "SELECT sql FROM sqlite_master "
      "WHERE (%s) AND sql NOT NULL"
      "  AND type IN ('index','trigger','view')",
      zLike
    );
    run_table_dump_query(p, zSql);
    sqlite3_free(zSql);

    sqlite3_free(zLike);
    if( p->writableSchema ){
      raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
      p->writableSchema = 0;
    }
    sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
    sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
    raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");

Changes to test/shell1.test.

293
294
295
296
297
298
299

300
301
302
303
304
305
306
307
308
309
310
       [regexp {COMMIT;} $res]
} {1 1}
do_test shell1-3.4.2 {
  set res [catchcmd "test.db" ".dump FOO"]
  list [regexp {BEGIN TRANSACTION;} $res] \
       [regexp {COMMIT;} $res]
} {1 1}

do_test shell1-3.4.3 {
  # too many arguments
  catchcmd "test.db" ".dump FOO BAD"
} {1 {Usage: .dump ?--preserve-rowids? ?--newlines? ?LIKE-PATTERN?}}

# .echo ON|OFF           Turn command echo on or off
do_test shell1-3.5.1 {
  catchcmd "test.db" ".echo"
} {1 {Usage: .echo on|off}}
do_test shell1-3.5.2 {
  catchcmd "test.db" ".echo ON"







>
|
|
|
|







293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
       [regexp {COMMIT;} $res]
} {1 1}
do_test shell1-3.4.2 {
  set res [catchcmd "test.db" ".dump FOO"]
  list [regexp {BEGIN TRANSACTION;} $res] \
       [regexp {COMMIT;} $res]
} {1 1}
# The .dump command now accepts multiple arguments
#do_test shell1-3.4.3 {
#  # too many arguments
#  catchcmd "test.db" ".dump FOO BAD"
#} {1 {Usage: .dump ?--preserve-rowids? ?--newlines? ?LIKE-PATTERN?}}

# .echo ON|OFF           Turn command echo on or off
do_test shell1-3.5.1 {
  catchcmd "test.db" ".echo"
} {1 {Usage: .echo on|off}}
do_test shell1-3.5.2 {
  catchcmd "test.db" ".echo ON"