]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Prototype sqlite3_incomplete() - a variant of sqlite3_complete() that returns
authordrh <>
Fri, 10 Apr 2026 13:43:48 +0000 (13:43 +0000)
committerdrh <>
Fri, 10 Apr 2026 13:43:48 +0000 (13:43 +0000)
additional information about what is needed to complete the statement.

FossilOrigin-Name: a698fbfb8bb256ee0d9802c01562c11492ca2800a45360b268ac6cf397a7fad7

manifest
manifest.uuid
src/complete.c
src/sqlite.h.in

index a9c024d8301f7d36ecdf03a975d307069be4b580..1af3a09a0faaba5f9e6727067d306ff46163e3ef 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improvements\sto\sthe\sway\s&#92;x\sworks\sso\sthat\sit\scan\sbe\sused\sto\schange\nthe\sprompt\scolor.\s\sThis\sreally\smesses\swith\slinenoise,\sthough.
-D 2026-04-10T00:55:40.521
+C Prototype\ssqlite3_incomplete()\s-\sa\svariant\sof\ssqlite3_complete()\sthat\sreturns\nadditional\sinformation\sabout\swhat\sis\sneeded\sto\scomplete\sthe\sstatement.
+D 2026-04-10T13:43:48.738
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -680,7 +680,7 @@ F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc9388
 F src/build.c 8581de0af3b6c448f5d64e2d18a91ac1e7057b3bcb8b8827e1240f80d87486a4
 F src/callback.c 3605bbf02bd7ed46c79cd48346db4a32fc51d67624400539c0532f4eead804ad
 F src/carray.c 3efe3982d5fb323334c29328a4e189ccaef6b95612a6084ad5fa124fd5db1179
-F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
+F src/complete.c 9304071c5f2ddd040cd786ec5d157c6a5592561674816e4a9e74164a0ba40647
 F src/date.c 61e92f1f7e2e88e1cd91e91dc69eb2b2854e7877254470f9fabd776bfac922b8
 F src/dbpage.c c9ea81c11727f27e02874611e92773e68e2a90a875ef2404b084564c235fd91f
 F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
@@ -736,7 +736,7 @@ F src/resolve.c 928ff887f2a7c64275182060d94d06fdddbe32226c569781cf7e7edc6f58d7fd
 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 F src/select.c ffe199f025a0dd74670d2a77232bdea364a4d7b36f32c64a6572d39ba6a11576
 F src/shell.c.in 2bbbb04153354fe3d11677d69ce7b6d5bfe8113e2d3693791915590eaa4f1bce
-F src/sqlite.h.in e2915e4a86d5e0783afb5cb72411df38d987c7f3c5aa2d5441b8e74d30b649d8
+F src/sqlite.h.in a5605faa9479bbaac16c4ab43eb09ff50632004a8e05084d3fde56063ef73766
 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
 F src/sqlite3ext.h 1b7a0ee438bb5c2896d0609c537e917d8057b3340f6ad004d2de44f03e3d3cca
 F src/sqliteInt.h bc1cbc0c23dba35b324ae85a7dbb5fb182321bbd30857fb21f3d0cba049001a5
@@ -2197,8 +2197,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
 F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
 F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 9cf28959671920b4909521bb1db6f0cbbc85448fbf3594c43b4000505b4e055b
-R 1af695b623ae09a49f6867ddfb6fa639
+P bbee56f970ade40734c8a278f46d8a268b305fe349c3739c977fa9141d2c3b6a
+R 12cd07af606e611ea7f41e67ab10d130
 U drh
-Z 856f286521a626031b862b8cbac557c5
+Z 547b407658ddec4e488d7004be7d0be4
 # Remove this line to create a well-formed Fossil manifest.
index e75a3aba4b466d040b3ecf86a985d6dbc4012185..4c993035c517f330fb576041f1381083f1605245 100644 (file)
@@ -1 +1 @@
-bbee56f970ade40734c8a278f46d8a268b305fe349c3739c977fa9141d2c3b6a
+a698fbfb8bb256ee0d9802c01562c11492ca2800a45360b268ac6cf397a7fad7
index bb2c03098e7634a6e45012fe4c55d0aa387f1bc3..08496366a8bb9c379e754fe1e2bf9d19f9eb4f0a 100644 (file)
@@ -49,12 +49,51 @@ extern const char sqlite3IsEbcdicIdChar[];
 #endif
 
 /*
-** Return TRUE if the given SQL string ends in a semicolon.
+** Return zero if the given SQL string is complete - if all comments,
+** string and blob literals, and quoted identifiers have been closed and
+** if the entire string ends with ";" and possible with ";END;" if the
+** string is a CREATE TRIGGER statement.  A non-zero return indicates
+** that the string is incomplete.  Bits of the return value indicate
+** what is missing and is needed to close out the statement.
 **
 ** Special handling is require for CREATE TRIGGER statements.
 ** Whenever the CREATE TRIGGER keywords are seen, the statement
 ** must end with ";END;".
 **
+** Let the return code be a value R.  R is split up into various
+** subfields, at byte boundaries:
+**
+**    R = 0xwwwwwwww00xxyyzz
+**
+** In other words, zz is the least significant byte, yy is the next
+** most significant byte, xx is the third byte, wwwwwwww is a 32-bit
+** value from the middle.
+**
+**   zz == SQLITE_OK       Input is complete
+**   zz == SQLITE_ERROR    Input is incomplete
+**   zz == SQLITE_MISUSE   Input is a NULL pointer
+**   zz != 0               New values for zz may be added in the future
+**
+**   yy == 0x01            Need a semicolon at the end
+**   yy == 0x02            Need "END" and a semicolon
+**   yy == 0x03            Need semicolon, "END", and semicolon
+**   yy != 0               New values for yy may be added in the future
+**
+**   xx == '\''            Incomplete string or blob literal
+**   xx == '"'             Incomplete quoted identifier
+**   xx == '`'             Incompelte MySQL-style quoted identifier
+**   xx == ']'             Incomplete SQLServer-style quoted identifer
+**   xx == '-'             Incomplete SQL-style comment
+**   xx == '/'             Incomplete C-style comment
+**   xx != 0               New values of xx may be added in the future
+**
+**   wwwwwwww              Interpret as a signed integer, the number
+**                         of unmatched "(".  Negative means there are
+**                         more ")" and "(".
+**
+**   ((R>>24)&0xff)!=0     New uses for the 4th byte may be added
+**                         in the future
+**
 ** This implementation uses a state machine with 8 states:
 **
 **   (0) INVALID   We have not yet seen a non-whitespace character.
@@ -101,9 +140,11 @@ extern const char sqlite3IsEbcdicIdChar[];
 ** to recognize the end of a trigger can be omitted.  All we have to do
 ** is look for a semicolon that is not part of an string or comment.
 */
-int sqlite3_complete(const char *zSql){
+sqlite3_int64 sqlite3_incomplete(const char *zSql){
   u8 state = 0;   /* Current state, using numbers defined in header comment */
   u8 token;       /* Value of the next token */
+  u8 pending = 0; /* unmatched structure character */
+  int nParen = 0; /* Nested parentheses */
 
 #ifndef SQLITE_OMIT_TRIGGER
   /* A complex statement machine used to detect the end of a CREATE TRIGGER
@@ -133,11 +174,21 @@ int sqlite3_complete(const char *zSql){
      /* 2  NORMAL: */ {    1,  2,     2, },
   };
 #endif /* SQLITE_OMIT_TRIGGER */
+  /* Mapping state number to yy value for the return */
+  static const u8 statemap[8] = {
+     /* 0 INVALID */ 1,
+     /* 1 START   */ 0,
+     /* 2 NORMAL  */ 1,
+     /* 3 EXPLAIN */ 1,
+     /* 4 CREATE  */ 1,
+     /* 5 TRIGGER */ 3,
+     /* 6 SEMI    */ 2,
+     /* 7 END     */ 1,
+  };
 
 #ifdef SQLITE_ENABLE_API_ARMOR
   if( zSql==0 ){
-    (void)SQLITE_MISUSE_BKPT;
-    return 0;
+    return SQLITE_MISUSE_BKPT;
   }
 #endif
 
@@ -162,7 +213,10 @@ int sqlite3_complete(const char *zSql){
         }
         zSql += 2;
         while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
-        if( zSql[0]==0 ) return 0;
+        if( zSql[0]==0 ){
+          pending = '/';
+          goto incomplete_finish;
+        }
         zSql++;
         token = tkWS;
         break;
@@ -173,14 +227,20 @@ int sqlite3_complete(const char *zSql){
           break;
         }
         while( *zSql && *zSql!='\n' ){ zSql++; }
-        if( *zSql==0 ) return state==1;
+        if( *zSql==0 ){
+          if( state!=1 ) pending = '-';
+          goto incomplete_finish;
+        }
         token = tkWS;
         break;
       }
       case '[': {   /* Microsoft-style identifiers in [...] */
         zSql++;
         while( *zSql && *zSql!=']' ){ zSql++; }
-        if( *zSql==0 ) return 0;
+        if( *zSql==0 ){
+          pending = ']';
+          goto incomplete_finish;
+        }
         token = tkOTHER;
         break;
       }
@@ -190,7 +250,20 @@ int sqlite3_complete(const char *zSql){
         int c = *zSql;
         zSql++;
         while( *zSql && *zSql!=c ){ zSql++; }
-        if( *zSql==0 ) return 0;
+        if( *zSql==0 ){
+          pending = c;
+          goto incomplete_finish;
+        }
+        token = tkOTHER;
+        break;
+      }
+      case '(': {
+        nParen++;
+        token = tkOTHER;
+        break;
+      }
+      case ')': {
+        nParen--;
         token = tkOTHER;
         break;
       }
@@ -257,7 +330,15 @@ int sqlite3_complete(const char *zSql){
     state = trans[state][token];
     zSql++;
   }
-  return state==1;
+incomplete_finish:
+  if( state==1 ) nParen = 0;
+  return (((i64)nParen)<<32) |
+          ((i64)pending<<16) |
+          ((i64)statemap[state]<<8) |
+          (state!=1);
+}
+int sqlite3_complete(const char *zSql){
+  return sqlite3_incomplete(zSql)==0;
 }
 
 #ifndef SQLITE_OMIT_UTF16
@@ -279,7 +360,7 @@ int sqlite3_complete16(const void *zSql){
   sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
   zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
   if( zSql8 ){
-    rc = sqlite3_complete(zSql8);
+    rc = sqlite3_incomplete(zSql8)==0;
   }else{
     rc = SQLITE_NOMEM_BKPT;
   }
index 9323ce5eeae50703b00139ab0f91409c67149b71..b4ed739a611d9ebbbc06d7c202d8e5310b168cb9 100644 (file)
@@ -2960,8 +2960,9 @@ int sqlite3_is_interrupted(sqlite3*);
 ** These routines are useful during command-line input to determine if the
 ** currently entered text seems to form a complete SQL statement or
 ** if additional input is needed before sending the text into
-** SQLite for parsing.  ^These routines return 1 if the input string
-** appears to be a complete SQL statement.  ^A statement is judged to be
+** SQLite for parsing.  ^The sqlite3_complete(X) and sqlite3_complete16(X)
+** routines return 1 if the input string X appears to be a complete SQL
+** statement.  ^A statement is judged to be
 ** complete if it ends with a semicolon token and is not a prefix of a
 ** well-formed CREATE TRIGGER statement.  ^Semicolons that are embedded within
 ** string literals or quoted identifier names or comments are not
@@ -2969,11 +2970,21 @@ int sqlite3_is_interrupted(sqlite3*);
 ** embedded) and thus do not count as a statement terminator.  ^Whitespace
 ** and comments that follow the final semicolon are ignored.
 **
-** ^These routines return 0 if the statement is incomplete.  ^If a
-** memory allocation fails, then SQLITE_NOMEM is returned.
+** ^The sqlite3_complete(X) and sqlite3_complete16(X) routines return 0
+** if the statement is incomplete.  ^If a memory allocation fails, then
+** SQLITE_NOMEM is returned.
 **
-** ^These routines do not parse the SQL statements and thus
-** will not detect syntactically incorrect SQL.
+** The [sqlite3_incomplete(X)] routine is similar to [sqlite3_complete(X)]
+** except that sqlite3_incomplete(X) returns 0 if the input X is complete
+** and non-zero if X is incomplete.  The non-zero return from
+** sqlite3_incomplete(X) contains additional information about what is
+** needed to complete the input X.  The sqlite3_incomplete(X) interface
+** is only available for UTF-8 text.
+**
+** ^None of these routines do a full parse the SQL statements and thus
+** will not detect syntactically incorrect SQL.  They only determine if
+** input text has properly terminated comments, string literals, and
+** quoted identifiers, and if the statement ends with a semicolon.
 **
 ** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
 ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked
@@ -2981,14 +2992,15 @@ int sqlite3_is_interrupted(sqlite3*);
 ** then the return value from sqlite3_complete16() will be non-zero
 ** regardless of whether or not the input SQL is complete.)^
 **
-** The input to [sqlite3_complete()] must be a zero-terminated
-** UTF-8 string.
+** The X input to [sqlite3_complete(X)] and [sqlite3_incomplete(X)
+** must be a zero-terminated UTF-8 string.
 **
 ** The input to [sqlite3_complete16()] must be a zero-terminated
 ** UTF-16 string in native byte order.
 */
 int sqlite3_complete(const char *sql);
 int sqlite3_complete16(const void *sql);
+sqlite3_int64 sqlite3_incomplete(const char *sql);
 
 /*
 ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors