]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
CLI cleanup, mode push/pop, parameter list beautification
authorlarrybr <larrybr@noemail.net>
Sat, 5 Feb 2022 23:45:07 +0000 (23:45 +0000)
committerlarrybr <larrybr@noemail.net>
Sat, 5 Feb 2022 23:45:07 +0000 (23:45 +0000)
FossilOrigin-Name: 53d4582a0e9dab594264a5125324e1fab476e5fd806ebc61bd5fc6ffff10e34b

manifest
manifest.uuid
src/shell.c.in
test/shell9.test [new file with mode: 0644]

index 413eccbebeffe3e61a38981e42461b748c244d91..0c7489652a71edbb7b3bf9ac11321394dc411c59 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sCLI\sWindows\sbuild,\srefine\sits\shelp,\sre-org\sShellState
-D 2022-02-05T15:26:39.028
+C CLI\scleanup,\smode\spush/pop,\sparameter\slist\sbeautification
+D 2022-02-05T23:45:07.085
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -553,7 +553,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 0dd8e23fda88411d63b2e6650f2380fdb08e88112e9a095fc23d5a08de4b2641
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c a6d2d4bed279d7fe4fcedaf297eaf6441e8e17c6e3947a32d24d23be52ac02f2
-F src/shell.c.in 7c1978cfec3647870d7b2fe45178ec323a44e5549041be7db0ed190c4701579b x
+F src/shell.c.in 537c1cfe3e7b90c94dbddf6226b951154cdb239f755a883008223296f5e07f28 x
 F src/sqlite.h.in d4daed3b2deb2e43df7f12be119b1b17a2954feadc33d521565bacbd072bfcb6
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
@@ -1395,6 +1395,7 @@ F test/shell5.test b85069bfcf3159b225228629ab2c3e69aa923d098fea8ea074b5dcd743522
 F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
 F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
 F test/shell8.test 388471d16e4de767333107e30653983f186232c0e863f4490bb230419e830aae
+F test/shell9.test 05f656a4c61b6e17b36a8cf7a70edffa6a77348bf4d633fb5cefb86b0127ce6b
 F test/shmlock.test 3dbf017d34ab0c60abe6a44e447d3552154bd0c87b41eaf5ceacd408dd13fda5
 F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
@@ -1943,8 +1944,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 6d415ac2c10da26e97164cb0846683a57e6a7f3717519c5fe889e077321a2cbd
-R caf017840a4e87c78151d22dc2043bdc
+P 991a06a4801270fddea7375d141863dc29fa87b737c728af60c81eea8445a0ea
+R 0d98364e4220742b71a54a3a478fca95
 U larrybr
-Z 304046c8b2c6fad5a59c02f068067d82
+Z 19e18e7127c918582442483575f4c74f
 # Remove this line to create a well-formed Fossil manifest.
index 6ec5f92c17db91b03181125cd38ed03fd17a5a74..9a5620d41d75db86612da6448b2def2b2a3f5356 100644 (file)
@@ -1 +1 @@
-991a06a4801270fddea7375d141863dc29fa87b737c728af60c81eea8445a0ea
\ No newline at end of file
+53d4582a0e9dab594264a5125324e1fab476e5fd806ebc61bd5fc6ffff10e34b
\ No newline at end of file
index 19f828370c6f92ca574dba8a83ff5e26c2501cc1..7ddd2dc7ce6ee6a1aca7487428aefe9ff6e9fa44 100755 (executable)
@@ -1141,6 +1141,11 @@ struct EQPGraph {
   char zPrefix[100];    /* Graph prefix */
 };
 
+/* By default, omit the extension options that are not done yet. */
+#ifndef SHELL_OMIT_EXTENSIONS
+# define SHELL_OMIT_EXTENSIONS 6
+#endif
+
 /* Selectively omit features with one PP variable. Value is true iff
 ** either x is not defined or defined with 0 in bitnum bit position.
 */
@@ -1190,14 +1195,21 @@ typedef struct ColModeOpts {
 #define ColModeOpts_default { 60, 0, 0 }
 #define ColModeOpts_default_qbox { 60, 1, 0 }
 
-typedef struct OutModeState {
-  u8 showHeader;         /* True to show column names in List or Column mode */
-  u8 mode;               /* An output mode setting */
-  u16 shellFlgs;         /* Various flags */
-  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
-  char colSeparator[20]; /* Column separator character for several modes */
-  char rowSeparator[20]; /* Row separator character for MODE_Ascii */
-} OutModeState;
+/*
+** Stored output mode state, for partial save and later restore.
+** Returned by:
+**   outputModeSave *outputModeSave(ShellState *p, SaveModeWhat ws).
+** Accepted by: 
+**   outputModeRestore(ShellState *p, OutputModeSave *pSaved).
+** See enum SaveWhatMode regarding what to save and restore.
+** Also see outputModePush(...), outputModePushSome(...) and
+** outputModePop(...) for usages spanning more than one call.
+*/
+typedef struct OutputModeSave{
+  u16 what;           /* Set upon creation. See SaveWhatMode for values. */
+  char itsValues[1];  /* This size is inaccurate unless nothing is saved. */
+} OutputModeSave;
+#define MODE_STACK_MAX 3 /* How many levels of saved output mode to allow. */
 
 /*
 ** State information about the database connection is contained in an
@@ -1206,13 +1218,13 @@ typedef struct OutModeState {
 typedef struct ShellState ShellState;
 struct ShellState {
   sqlite3 *db;           /* The database */
+  int openFlags;         /* Additional flags to open. (SQLITE_OPEN_NOFOLLOW) */
+  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
   u8 autoExplain;        /* Automatically turn on .explain mode */
   u8 autoEQP;            /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
   u8 autoEQPtest;        /* autoEQP is in test mode */
   u8 autoEQPtrace;       /* autoEQP is in trace mode */
   u8 scanstatsOn;        /* True to display scan stats before each finalize */
-  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
-  int openFlags;         /* Additional flags to open.  (SQLITE_OPEN_NOFOLLOW) */
   u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
   u8 nEqpLevel;          /* Depth of the EQP output graph */
   u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
@@ -1220,19 +1232,19 @@ struct ShellState {
   u8 bSafeModePersist;   /* The long-term value of bSafeMode */
   u8 bAllowSysDump;      /* Allow .dump use for sqlite_* tables. */
   u8 bExtendedDotCmds;   /* Bits set to enable various shell extensions */
-
+  /* Output mode state subject to systematic save/restore: (See OM_STATE.) */
   u8 showHeader;         /* True to show column names in List or Column mode */
   u16 shellFlgs;         /* Various flags */
-  u16 priorShFlgs;       /* Saved copy of flags */
   u8 mode;               /* An output mode setting */
-  u8 modePrior;          /* Saved mode */
-  u8 cMode;              /* temporary output mode for the current query */
-  u8 normalMode;         /* Output mode before ".explain on" */
   ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
   char colSeparator[20]; /* Column separator character for several modes */
-  char colSepPrior[20];  /* Saved column separator */
   char rowSeparator[20]; /* Row separator character for MODE_Ascii */
-  char rowSepPrior[20];  /* Saved row separator */
+  /* Output mode state-keep for systematic save/restore: (See OM_STATE.) */
+  u8 nSavedModes;        /* number of valid items in next array */
+  OutputModeSave *pModeStack[MODE_STACK_MAX]; /* saved mode data buffers */
+  /* Output mode state-keep for certain ad-hoc save/restore ops: */
+  u8 cMode;              /* temporary output mode for the current query */
+  u8 normalMode;         /* Output mode before ".explain on" */
 
   int *colWidth;         /* Requested width of each column in columnar modes */
   int *actualWidth;      /* Actual width of each column */
@@ -1587,19 +1599,97 @@ edit_func_end:
 #endif /* SQLITE_NOHAVE_SYSTEM */
 
 /*
-** Save or restore the current output mode
+** Save or restore the current output mode, partially per spec. (OM_STATE)
+*/
+typedef enum {
+  SWM_showHeader = 1, SWM_shellFlags = 2, SWM_mode = 4, SWM_cmOpts = 8,
+  SWM_colSeparator = 0x10, SWM_rowSeparator = 0x20, SWM_everything = 0x3F,
+  SWM_CountOf = 6
+} SaveWhatMode;
+
+/* This is available in most C89+ C compilers as offsetof(...), but since we
+ * cater to the most arcane C89-like compilers around, define it for sure:
+ */
+#define MEMBER_OFFSET(stype, member) ((size_t)&(((stype*)0)->member))
+#define MEMBER_SIZEOF(stype, member) (sizeof(((stype*)0)->member))
+static struct {
+  size_t offset;
+  size_t size;
+} outputModeCopy[] = {
+#define SS_MEMBER_COPY(mn) \
+  { MEMBER_OFFSET(ShellState,mn), MEMBER_SIZEOF(ShellState,mn) }
+  SS_MEMBER_COPY(showHeader), SS_MEMBER_COPY(shellFlgs),
+  SS_MEMBER_COPY(mode), SS_MEMBER_COPY(cmOpts),
+  SS_MEMBER_COPY(colSeparator), SS_MEMBER_COPY(rowSeparator)
+#undef SS_MEMBER_COPY
+};
+
+/* Allocate a buffer, copy requested output mode data to it, and return it. */
+static OutputModeSave *outputModeSave(ShellState *p, SaveWhatMode w){
+  u16 what = (u16)w;
+  int i, nAlloc = sizeof(what)+1, mask = 1;
+  char *pSaved = 0, *pFill;
+  for( i=0; i<SWM_CountOf; mask<<=1, ++i ){
+    if( (what & mask)!=0 ) nAlloc += (int)outputModeCopy[i].size;
+  }
+  assert(i==ArraySize(outputModeCopy));
+  pSaved = sqlite3_malloc(nAlloc);
+  if( pSaved==0 ) return 0;
+  *(u16 *)pSaved = what;
+  pFill = pSaved + sizeof(what);
+  for( mask=1, i=0; i<SWM_CountOf; mask<<=1, ++i ){
+    if( (what & mask)!=0 ){
+      size_t nb = outputModeCopy[i].size;
+      memcpy(pFill, (char*)p+outputModeCopy[i].offset, nb);
+      pFill += nb;
+    }
+  }
+  *pFill = 0xA5;
+  return (OutputModeSave *)pSaved;
+}
+
+/* From a buffer returned by outputModeSave, restore output mode data.
+ * The buffer is freed and its pointer is invalidated. 
+ * If called with some other buffer, results are undefined, likely bad.
+ */
+static void outputModeRestore(ShellState *p, OutputModeSave *pSaved){
+  sqlite3_uint64 nA = sqlite3_msize(pSaved);
+  u16 what = (nA>sizeof(what))? *((u16 *)pSaved) : 0;
+  int i, nAlloc = sizeof(what)+1, mask = 1;
+  char *pTake = (char *)pSaved + sizeof(what);
+  for( i=0; i<SWM_CountOf && nAlloc<nA; mask<<=1, ++i ){
+    if( (what & mask)!=0 ){
+      size_t nb = outputModeCopy[i].size;
+      memcpy((char*)p+outputModeCopy[i].offset, pTake, nb);
+      pTake += nb;
+    }
+  }
+  assert(*pTake==0xA5);
+  sqlite3_free(pSaved);
+}
+
+/*
+** Save or restore the current output mode, in whole or in part.
 */
+static void outputModePushSome(ShellState *p, SaveWhatMode w){
+  OutputModeSave *pOMS;
+  assert(p->nSavedModes<MODE_STACK_MAX); /* Fail hard for this logic error. */
+  if( p->nSavedModes>=MODE_STACK_MAX ) return;
+  pOMS = outputModeSave(p, w);
+  shell_check_oom(pOMS);
+  p->pModeStack[p->nSavedModes++] = pOMS;
+}
 static void outputModePush(ShellState *p){
-  p->modePrior = p->mode;
-  p->priorShFlgs = p->shellFlgs;
-  memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
-  memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
+  outputModePushSome(p, SWM_everything);
 }
 static void outputModePop(ShellState *p){
-  p->mode = p->modePrior;
-  p->shellFlgs = p->priorShFlgs;
-  memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
-  memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
+  OutputModeSave *pOMS;
+  assert(p->nSavedModes>0); /* Should not be here unless something pushed. */
+  if( p->nSavedModes==0 ) return;
+  pOMS = p->pModeStack[--p->nSavedModes];
+  assert(pOMS!=0);
+  p->pModeStack[p->nSavedModes] = 0;
+  outputModeRestore(p, pOMS);
 }
 
 /*
@@ -8525,18 +8615,27 @@ static void list_params(ShellState *p, ParamTableUse ptu){
     if( sqlite3_step(pStmt)==SQLITE_ROW ){
       len = sqlite3_column_int(pStmt, 0);
       if( len>40 ) len = 40;
+      if( len<4 ) len = 4;
     }
   }
   sqlite3_finalize(pStmt);
   pStmt = 0;
   if( len ){
+    utf8_printf(p->out, "%-*s %-8s %s\n", len, "name", "usage", "value");
     rc = sqlite3_prepare_v2
-      (p->db, "SELECT key, quote(value) FROM "
+      (p->db, "SELECT key, uses, quote(value) FROM "
        PARAM_TABLE_SNAME" WHERE ?1=3 OR uses=?1", -1, &pStmt, 0);
     sqlite3_bind_int(pStmt, 1, ptu);
     while( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
-      utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
-                  sqlite3_column_text(pStmt,1));
+      ParamTableUse ptux = sqlite3_column_int(pStmt,1);
+      const char *zUse;
+      switch( ptux ){
+      case PTU_Binding: zUse = "binding"; break;
+      case PTU_Script:  zUse = "script"; break;
+      default:          zUse = "unknown";
+      }
+      utf8_printf(p->out, "%-*s %-8s %s\n", len, sqlite3_column_text(pStmt,0),
+                  zUse, sqlite3_column_text(pStmt,2));
     }
     sqlite3_finalize(pStmt);
   }
@@ -13316,6 +13415,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
 #endif
   free(data.colWidth);
   free(data.zNonce);
+  for(i=0; i<data.nSavedModes; ++i) sqlite3_free(data.pModeStack[i]);
   /* Clear the global data structure so that valgrind will detect memory
   ** leaks */
   memset(&data, 0, sizeof(data));
diff --git a/test/shell9.test b/test/shell9.test
new file mode 100644 (file)
index 0000000..8d3bbd4
--- /dev/null
@@ -0,0 +1,69 @@
+# 2022 Feb 5
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the CLI shell tool enhanced parsing,
+# new .parameter subcommands and uses, and the new .x meta-command.
+#
+#
+
+# Test plan:
+#
+#   shell9-1.*: command line parsing and acting accordingly
+#   shell9-2.*: Basic "dot" command, cross-line token parsing
+#   shell9-3.*: .parameter set options and types
+#   shell9-4.*: .parameter save/load operation
+#   shell9-5.*: Ensure "dot" commands and SQL intermix ok.
+#   shell9-6.*: .x command operation and refusal
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set CLI [test_find_cli]
+db close
+forcedelete test.db test.db-journal test.db-wal
+sqlite3 db test.db
+
+#----------------------------------------------------------------------------
+# Test cases shell9-1.*: command line parsing and acting accordingly
+
+do_test shell9-1.1 {
+  set res [catchcmd ":memory: -cmd .quit" ""]
+} {0 {}}
+
+do_test shell9-1.2 {
+  set res [catchcmd ":memory: -shxopts 1 -cmd .shxopts -cmd .quit" ""]
+} {0 {     name    value  "-shxopts set"
+   --------  -----  ---------------
+    parsing    1    "-shxopts 0x01"
+   all_opts    0    "-shxopts 0x07"}}
+
+do_test shell9-1.3 {
+  set res [catchcmd ":memory: -cmd .shxopts -cmd .quit" ""]
+} {0 {     name    value  "-shxopts set"
+   --------  -----  ---------------
+    parsing    0    "-shxopts 0x01"
+   all_opts    0    "-shxopts 0x07"}}
+
+#----------------------------------------------------------------------------
+# Test cases shell9-2.*: Basic "dot" command, cross-line token parsing
+
+#----------------------------------------------------------------------------
+# Test cases shell9-3.*: .parameter set options and types
+
+#----------------------------------------------------------------------------
+# Test cases shell9-4.*: .parameter save/load operation
+
+#----------------------------------------------------------------------------
+# Test cases shell9-5.*: Ensure "dot" commands and SQL intermix ok.
+
+#----------------------------------------------------------------------------
+# Test cases shell9-6.*: .x command operation and refusal
+
+finish_test