SQLite

Check-in Differences
Login

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

Difference From 066febe8931c5d90 To 2da1f8e45eb0cb1e

2022-02-11
13:40
Upon .import column renaming, issue message saying so. Test this. (check-in: 8b6ca9304b user: larrybr tags: auto-column)
01:21
CLI auto-column rename revamped after forum discussion. (check-in: 2da1f8e45e user: larrybr tags: auto-column)
2022-02-10
03:21
A CLI feature. auto .import (new table) columns. WIP (check-in: 7e3be36dfc user: larrybr tags: auto-column)
02:09
Create new branch named "auto-column" (check-in: 066febe893 user: larrybr tags: auto-column)
01:01
Fix a typo in documentation. No code changes. (check-in: e97c6ad4c9 user: drh tags: trunk)

Changes to src/shell.c.in.
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
  p->outfile[0] = 0;
  p->out = stdout;
}

/*
** Run an SQL command and return the single integer result.
*/
static int db_int(ShellState *p, const char *zSql){
  sqlite3_stmt *pStmt;
  int res = 0;
  sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
  if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
    res = sqlite3_column_int(pStmt,0);
  }
  sqlite3_finalize(pStmt);
  return res;
}








|


|







5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
5764
  p->outfile[0] = 0;
  p->out = stdout;
}

/*
** Run an SQL command and return the single integer result.
*/
static int db_int(sqlite3 *db, const char *zSql){
  sqlite3_stmt *pStmt;
  int res = 0;
  sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
    res = sqlite3_column_int(pStmt,0);
  }
  sqlite3_finalize(pStmt);
  return res;
}

5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
  }else if( strcmp(zDb,"temp")==0 ){
    zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
  }else{
    zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
  }
  for(i=0; i<ArraySize(aQuery); i++){
    char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
    int val = db_int(p, zSql);
    sqlite3_free(zSql);
    utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
  }
  sqlite3_free(zSchemaTab);
  sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
  utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
  return 0;







|







5855
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
  }else if( strcmp(zDb,"temp")==0 ){
    zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
  }else{
    zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
  }
  for(i=0; i<ArraySize(aQuery); i++){
    char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
    int val = db_int(p->db, zSql);
    sqlite3_free(zSql);
    utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
  }
  sqlite3_free(zSchemaTab);
  sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
  utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
  return 0;
7218
7219
7220
7221
7222
7223
7224






























































































































































7225
7226
7227
7228
7229
7230
7231
    va_end(ap);
    if( z==0 ){
      *pRc = SQLITE_NOMEM;
    }
  }
  return z;
}































































































































































/*
** When running the ".recover" command, each output table, and the special
** orphaned row table if it is required, is represented by an instance
** of the following struct.
*/
typedef struct RecoverTable RecoverTable;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7218
7219
7220
7221
7222
7223
7224
7225
7226
7227
7228
7229
7230
7231
7232
7233
7234
7235
7236
7237
7238
7239
7240
7241
7242
7243
7244
7245
7246
7247
7248
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
7271
7272
7273
7274
7275
7276
7277
7278
7279
7280
7281
7282
7283
7284
7285
7286
7287
7288
7289
7290
7291
7292
7293
7294
7295
7296
7297
7298
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
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
    va_end(ap);
    if( z==0 ){
      *pRc = SQLITE_NOMEM;
    }
  }
  return z;
}


/* 
 * zAutoColumn(zCol, &db) => Maybe init db, add column zCol to it.
 * zAutoColumn(0, &db) => (db!=0) Form columns spec for CREATE TABLE,
 *   close db and set it to 0, and return the columns spec, to later
 *   be sqlite3_free()'ed by the caller.
 * The return is 0 when either:
 *   (a) The db was not initialized and zCol==0 (There are no columns.)
 *   (b) zCol!=0  (Column was added, db initialized as needed.)
 */
#ifdef SHELL_DEBUG
#define rc_err_oom_die(rc) \
  if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
  else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
    fprintf(stderr,"E:%d\n",rc), assert(0)
#else
static void rc_err_oom_die(int rc){
  if( rc==SQLITE_NOMEM ) shell_check_oom(0);
  assert(rc==SQLITE_OK||rc==SQLITE_DONE);
}
#endif

#ifdef SHELL_COLFIX_DB /* If this is set, the DB can be in a file. */
static char zCOL_DB[] = SHELL_COLFIX_DB;
#else  /* Otherwise, memory is faster/better for the transient DB. */
static const char *zCOL_DB = ":memory:";
#endif

static char *zAutoColumn(const char *zColNew, sqlite3 **pDb){
  /* Queries and D{D,M}L used here */
  static const char const *zTabMake = "\
CREATE TABLE ColNames(\
 cpos INTEGER PRIMARY KEY,\
 name TEXT, nlen INT, chop INT, reps INT, suff TEXT)\
";
  static const char const *zTabFill = "\
INSERT INTO ColNames(name,nlen,chop,reps,suff)\
 VALUES(iif(length(?1)>0,?1,'?'),max(length(?1),1),0,0,'')\
";
  static const char const *zHasDupes = "\
SELECT count(DISTINCT substring(name,1,nlen-chop)||suff)\
 <count(name) FROM ColNames\
";
  static const char const *zDedoctor = "\
WITH chopped AS ( \
 WITH RECURSIVE chopping(cpos, name, chop, nlen) AS ( \
  SELECT cpos, name, 0, nlen FROM ColNames \
  WHERE cpos IN ( \
    WITH RECURSIVE choppable(nc, name, cpos) AS \
     (SELECT nlen AS nc, name, cpos \
      FROM ColNames \
      UNION ALL \
      SELECT nc-1 AS nc, name, cpos \
      FROM choppable \
      WHERE substring(name, nc, 1) BETWEEN '0' AND '9'\
     ) SELECT cpos /*name*/ FROM choppable \
       WHERE nc<length(name) and substring(name, nc, 1)='_'\
  )\
  UNION ALL\
  SELECT cpos, name, chop+1 AS chop, nlen \
  FROM chopping \
  WHERE instr('_0123456789', substring(name, nlen-chop, 1))>0 \
 )\
 SELECT cpos, name, max(chop) AS chop FROM chopping s \
 GROUP BY cpos \
) UPDATE ColNames AS c \
  SET chop=n.chop FROM chopped n WHERE c.cpos=n.cpos \
";
  static const char const *zSetReps = "\
UPDATE ColNames AS t SET reps=\
(SELECT count(*) FROM ColNames d \
 WHERE substring(t.name,1,t.nlen-t.chop)=substring(d.name,1,d.nlen-d.chop)\
)\
";
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
  static const char const *zColDigits = "\
SELECT CAST(ceil(log(count(*)+0.5)) AS INT) FROM ColNames \
";
#endif
  static const char const *zRenameRank = "\
UPDATE ColNames AS t SET suff=iif(reps>1, printf('_%0*d', $1, cpos), '')\
";
  static const char const *zCollectVar = "\
SELECT\
 '('||\
 group_concat(\
  cname||' TEXT',\
  ','||iif((cpos-1)%4>0, ' ', x'0a'||' '))\
 ||')' AS ColsSpec \
FROM (\
 SELECT cpos, printf('\"%w\"',printf('%.*s%s', nlen-chop,name,suff)) AS cname \
 FROM ColNames ORDER BY cpos\
)";

  int rc;
  sqlite3_stmt *pStmt = 0;
  assert(pDb!=0);
  if( zColNew ){
    /* Add initial or additional column. Init db if necessary. */
    if( *pDb==0 ){
      if( SQLITE_OK!=sqlite3_open(zCOL_DB, pDb) ) return 0;
#ifdef SHELL_COLFIX_DB
      if(*zCOL_DB!=':')
        sqlite3_exec(*pDb,"drop table if exists ColNames",0,0,0);
#endif
      rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0);
      rc_err_oom_die(rc);
    }
    assert(*pDb!=0);
    rc = sqlite3_prepare_v2(*pDb, zTabFill, -1, &pStmt, 0);
    rc_err_oom_die(rc);
    rc = sqlite3_bind_text(pStmt, 1, zColNew, -1, 0);
    rc_err_oom_die(rc);
    rc = sqlite3_step(pStmt);
    rc_err_oom_die(rc);
    sqlite3_finalize(pStmt);
    return 0;
  }else if( *pDb==0 ){
    return 0;
  }else{
    /* Formulate the columns spec, close the DB, zero *pDb. */
    char *zColsSpec = 0;
    int hasDupes = db_int(*pDb, zHasDupes);
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
    int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0;
#else
# define nDigits 2
#endif
    if( hasDupes ){
      rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
      rc_err_oom_die(rc);
      rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
      rc_err_oom_die(rc);
      rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
      rc_err_oom_die(rc);
      rc = sqlite3_bind_int(pStmt, 1, nDigits);
      rc_err_oom_die(rc);
      rc = sqlite3_step(pStmt);
      sqlite3_finalize(pStmt);
      assert(rc==SQLITE_DONE);
    }
    assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */
    rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
    rc_err_oom_die(rc);
    rc = sqlite3_step(pStmt);
    if( rc==SQLITE_ROW ){
      zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
    }else{
      zColsSpec = 0;
    }
    sqlite3_finalize(pStmt);
    sqlite3_close(*pDb);
    *pDb = 0;
    return zColsSpec;
  }
}


/*
** When running the ".recover" command, each output table, and the special
** orphaned row table if it is required, is represented by an instance
** of the following struct.
*/
typedef struct RecoverTable RecoverTable;
8763
8764
8765
8766
8767
8768
8769

8770
8771
8772
8773
8774
8775


8776
8777
8778
8779
8780
8781
8782
8783
8784
8785
8786
8787
8788
8789
8790
    }
    nByte = strlen30(zSql);
    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
    import_append_char(&sCtx, 0);    /* To ensure sCtx.z is allocated */
    if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
      char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
                                      zSchema, zTable);

      char cSep = '(';
      while( xRead(&sCtx) ){
        zCreate = sqlite3_mprintf("%z%c\n  \"%w\" TEXT", zCreate, cSep, sCtx.z);
        cSep = ',';
        if( sCtx.cTerm!=sCtx.cColSep ) break;
      }


      if( cSep=='(' ){
        sqlite3_free(zCreate);
        import_cleanup(&sCtx);
        utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
        rc = 1;
        goto meta_command_exit;
      }
      zCreate = sqlite3_mprintf("%z\n)", zCreate);
      if( eVerbose>=1 ){
        utf8_printf(p->out, "%s\n", zCreate);
      }
      rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
      if( rc ){
        utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
        sqlite3_free(zCreate);







>
|

|
<


>
>
|






|







8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931

8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
8945
8946
8947
8948
8949
8950
    }
    nByte = strlen30(zSql);
    rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
    import_append_char(&sCtx, 0);    /* To ensure sCtx.z is allocated */
    if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(p->db))==0 ){
      char *zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
                                      zSchema, zTable);
      sqlite3 *dbCols = 0;
      char *zColDefs;
      while( xRead(&sCtx) ){
        zAutoColumn(sCtx.z, &dbCols);

        if( sCtx.cTerm!=sCtx.cColSep ) break;
      }
      zColDefs = zAutoColumn(0, &dbCols);
      assert(dbCols==0);
      if( zColDefs==0 ){
        sqlite3_free(zCreate);
        import_cleanup(&sCtx);
        utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
        rc = 1;
        goto meta_command_exit;
      }
      zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
      if( eVerbose>=1 ){
        utf8_printf(p->out, "%s\n", zCreate);
      }
      rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
      if( rc ){
        utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
        sqlite3_free(zCreate);
Changes to test/shell1.test.
589
590
591
592
593
594
595

596
597
598
599
600
601
602
603
  }
  catchcmd "test.db" ".schema"
} {0 {CREATE TABLE t1(x);
CREATE VIEW v2 AS SELECT x+1 AS y FROM t1
/* v2(y) */;
CREATE VIEW v1 AS SELECT y+1 FROM v2
/* v1("y+1") */;}}

db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}
}

# .separator STRING  Change column separator used by output and .import
do_test shell1-3.22.1 {
  catchcmd "test.db" ".separator"
} {1 {Usage: .separator COL ?ROW?}}
do_test shell1-3.22.2 {







>
|







589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
  }
  catchcmd "test.db" ".schema"
} {0 {CREATE TABLE t1(x);
CREATE VIEW v2 AS SELECT x+1 AS y FROM t1
/* v2(y) */;
CREATE VIEW v1 AS SELECT y+1 FROM v2
/* v1("y+1") */;}}

  catch {db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}}
}

# .separator STRING  Change column separator used by output and .import
do_test shell1-3.22.1 {
  catchcmd "test.db" ".separator"
} {1 {Usage: .separator COL ?ROW?}}
do_test shell1-3.22.2 {