The SQLite command line shell will count your unclosed parentheses
If the prompt starts with (x1 or (x2, it means you’ve opened some parentheses and not closed them yet.
While writing my previous note, I noticed an unexpected prefix in the SQLite shell:
sqlite> CREATE TABLE KeyValuePairs (
(x1...> Key TEXT NOT NULL PRIMARY KEY,
(x1...> Value TEXT NOT NULL
(x1...> );What does that (x1 prefix mean? I couldn’t find any reference to it online, so I had to read the SQLite source code.
The relevant code is in shell.c.in. First I found the default prompts, and two variables where the main and continuation prompts are stored:
/*
** Prompt strings. Initialized in main. Settable with
** .prompt main continue
*/
#define PROMPT_LEN_MAX 128
/* First line prompt. default: "sqlite> " */
static char mainPrompt[PROMPT_LEN_MAX];
/* Continuation prompt. default: " ...> " */
static char continuePrompt[PROMPT_LEN_MAX];src/shell.c.in at 15285c21ccLooking at where those variables get used leads to another interesting snippet, which names this feature as “dynamic continuation prompt”:
/*
** Optionally disable dynamic continuation prompt.
** Unless disabled, the continuation prompt shows open SQL lexemes if any,
** or open parentheses level if non-zero, or continuation prompt as set.
** This facility interacts with the scanner and process_input() where the
** below 5 macros are used.
*/
#ifdef SQLITE_OMIT_DYNAPROMPT
# define CONTINUATION_PROMPT continuePrompt
…
#else
# define CONTINUATION_PROMPT dynamicContinuePrompt()src/shell.c.in at 15285c21ccAnd looking for the definition of that dynamicContinuePrompt function, I can see it updating a dynPrompt.dynamicPrompt variable with expressions like the (x1 I saw in my SQLite shell:
/* Upon demand, derive the continuation prompt to display. */
static char *dynamicContinuePrompt(void){
if( continuePrompt[0]==0
|| (dynPrompt.zScannerAwaits==0 && dynPrompt.inParenLevel == 0) ){
return continuePrompt;
}else{
if( dynPrompt.zScannerAwaits ){
size_t ncp = strlen(continuePrompt);
size_t ndp = strlen(dynPrompt.zScannerAwaits);
if( ndp > ncp-3 ) return continuePrompt;
shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits);
while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' ';
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
PROMPT_LEN_MAX-4);
}else{
if( dynPrompt.inParenLevel>9 ){
shell_strncpy(dynPrompt.dynamicPrompt, "(..", 4);
}else if( dynPrompt.inParenLevel<0 ){
shell_strncpy(dynPrompt.dynamicPrompt, ")x!", 4);
}else{
shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
}
shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
PROMPT_LEN_MAX-4);
}
}
return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */src/shell.c.in at 15285c21ccI don’t completely understand this function, but I think I get the general gist. The first branch is looking for “open SQL lexemes”, or unterminated strings, while the second branch is counting open parentheses. I can compare this to what I see in the SQLite shell:
- If you have between 1 to 9 unclosed parentheses, the prompt starts with
(xand the number of unclosed parens:sqlite> ( (x1...> sqlite> ((((((((( (x9...> - If you have 10 or more unclosed parentheses, the prompt starts with prints
(..:sqlite> (((((((((( (x.....> If you have more closed parentheses than you've opened, the prompt starts with prints
)x!:sqlite> ))) )x!...>If you have an unfinished string, square bracket, or multi-line comment, the prompt starts with the quote character you need to close the string:
sqlite> SELECT ' ' ...> hello world ' ...> ' hello world sqlite> SELECT " " ...> hello world " ...> " hello world sqlite> [ [ ...> sqlite> /* /* ...>
If you’re in this state, I’m not sure if it's ever possible to get back to a valid SQL expression?
I wonder where this behaviour came from? It feels like the sort of thing that might have come from Lisp, which is famous for having lots of brackets and exactly where this sort of indicator might be useful, whereas I imagine writing a heavily nested expression in the SQLite shell interface is comparatively rare.