libs: updated to SQLite3 3.16.2

This commit is contained in:
Remy Marquis 2017-01-22 09:00:19 +01:00
parent adbba12182
commit 5c7d42e40f
4 changed files with 3351 additions and 1719 deletions

View file

@ -2,4 +2,4 @@ Compile ET: L specific SQLite3 for several plattforms & architectures
Download sqlite3 amalgamation sources from https://www.sqlite.org/download.html Download sqlite3 amalgamation sources from https://www.sqlite.org/download.html
- tested with sqlite-amalgamation-3150200 - tested with sqlite-amalgamation-3160200

View file

@ -668,11 +668,12 @@ struct ShellState {
#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */ #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
#define MODE_Html 4 /* Generate an XHTML table */ #define MODE_Html 4 /* Generate an XHTML table */
#define MODE_Insert 5 /* Generate SQL "insert" statements */ #define MODE_Insert 5 /* Generate SQL "insert" statements */
#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */ #define MODE_Quote 6 /* Quote values as for SQL */
#define MODE_Csv 7 /* Quote strings, numbers are plain */ #define MODE_Tcl 7 /* Generate ANSI-C or TCL quoted elements */
#define MODE_Explain 8 /* Like MODE_Column, but do not truncate data */ #define MODE_Csv 8 /* Quote strings, numbers are plain */
#define MODE_Ascii 9 /* Use ASCII unit and record separators (0x1F/0x1E) */ #define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
#define MODE_Pretty 10 /* Pretty-print schemas */ #define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */
#define MODE_Pretty 11 /* Pretty-print schemas */
static const char *modeDescr[] = { static const char *modeDescr[] = {
"line", "line",
@ -681,6 +682,7 @@ static const char *modeDescr[] = {
"semi", "semi",
"html", "html",
"insert", "insert",
"quote",
"tcl", "tcl",
"csv", "csv",
"explain", "explain",
@ -944,6 +946,25 @@ static int shellAuth(
} }
#endif #endif
/*
** Print a schema statement. Part of MODE_Semi and MODE_Pretty output.
**
** This routine converts some CREATE TABLE statements for shadow tables
** in FTS3/4/5 into CREATE TABLE IF NOT EXISTS statements.
*/
static void printSchemaLine(FILE *out, const char *z, const char *zTail){
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
}else{
utf8_printf(out, "%s%s", z, zTail);
}
}
static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){
char c = z[n];
z[n] = 0;
printSchemaLine(out, z, zTail);
z[n] = c;
}
/* /*
** This is the callback routine that the shell ** This is the callback routine that the shell
@ -1062,7 +1083,7 @@ static int shell_callback(
break; break;
} }
case MODE_Semi: { /* .schema and .fullschema output */ case MODE_Semi: { /* .schema and .fullschema output */
utf8_printf(p->out, "%s;\n", azArg[0]); printSchemaLine(p->out, azArg[0], ";\n");
break; break;
} }
case MODE_Pretty: { /* .schema and .fullschema with --indent */ case MODE_Pretty: { /* .schema and .fullschema with --indent */
@ -1106,14 +1127,14 @@ static int shell_callback(
}else if( c==')' ){ }else if( c==')' ){
nParen--; nParen--;
if( nLine>0 && nParen==0 && j>0 ){ if( nLine>0 && nParen==0 && j>0 ){
utf8_printf(p->out, "%.*s\n", j, z); printSchemaLineN(p->out, z, j, "\n");
j = 0; j = 0;
} }
} }
z[j++] = c; z[j++] = c;
if( nParen==1 && (c=='(' || c==',' || c=='\n') ){ if( nParen==1 && (c=='(' || c==',' || c=='\n') ){
if( c=='\n' ) j--; if( c=='\n' ) j--;
utf8_printf(p->out, "%.*s\n ", j, z); printSchemaLineN(p->out, z, j, "\n ");
j = 0; j = 0;
nLine++; nLine++;
while( IsSpace(z[i+1]) ){ i++; } while( IsSpace(z[i+1]) ){ i++; }
@ -1121,7 +1142,7 @@ static int shell_callback(
} }
z[j] = 0; z[j] = 0;
} }
utf8_printf(p->out, "%s;\n", z); printSchemaLine(p->out, z, ";\n");
sqlite3_free(z); sqlite3_free(z);
break; break;
} }
@ -1198,19 +1219,28 @@ static int shell_callback(
setTextMode(p->out, 1); setTextMode(p->out, 1);
break; break;
} }
case MODE_Quote:
case MODE_Insert: { case MODE_Insert: {
p->cnt++;
if( azArg==0 ) break; if( azArg==0 ) break;
utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->cMode==MODE_Insert ){
if( p->showHeader ){ utf8_printf(p->out,"INSERT INTO %s",p->zDestTable);
raw_printf(p->out,"("); if( p->showHeader ){
for(i=0; i<nArg; i++){ raw_printf(p->out,"(");
char *zSep = i>0 ? ",": ""; for(i=0; i<nArg; i++){
utf8_printf(p->out, "%s%s", zSep, azCol[i]); char *zSep = i>0 ? ",": "";
utf8_printf(p->out, "%s%s", zSep, azCol[i]);
}
raw_printf(p->out,")");
} }
raw_printf(p->out,")"); raw_printf(p->out," VALUES(");
}else if( p->cnt==0 && p->showHeader ){
for(i=0; i<nArg; i++){
if( i>0 ) raw_printf(p->out, ",");
output_quoted_string(p->out, azCol[i]);
}
raw_printf(p->out,"\n");
} }
raw_printf(p->out," VALUES("); p->cnt++;
for(i=0; i<nArg; i++){ for(i=0; i<nArg; i++){
char *zSep = i>0 ? ",": ""; char *zSep = i>0 ? ",": "";
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
@ -1233,7 +1263,7 @@ static int shell_callback(
output_quoted_string(p->out, azArg[i]); output_quoted_string(p->out, azArg[i]);
} }
} }
raw_printf(p->out,");\n"); raw_printf(p->out,p->cMode==MODE_Quote?"\n":");\n");
break; break;
} }
case MODE_Ascii: { case MODE_Ascii: {
@ -1896,6 +1926,7 @@ static int shell_exec(
continue; continue;
} }
zStmtSql = sqlite3_sql(pStmt); zStmtSql = sqlite3_sql(pStmt);
if( zStmtSql==0 ) zStmtSql = "";
while( IsSpace(zStmtSql[0]) ) zStmtSql++; while( IsSpace(zStmtSql[0]) ) zStmtSql++;
/* save off the prepared statment handle and reset row count */ /* save off the prepared statment handle and reset row count */
@ -2034,7 +2065,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
sqlite3_free(zIns); sqlite3_free(zIns);
return 0; return 0;
}else{ }else{
utf8_printf(p->out, "%s;\n", zSql); printSchemaLine(p->out, zSql, ";\n");
} }
if( strcmp(zType, "table")==0 ){ if( strcmp(zType, "table")==0 ){
@ -2158,6 +2189,9 @@ static char zHelp[] =
".headers on|off Turn display of headers on or off\n" ".headers on|off Turn display of headers on or off\n"
".help Show this message\n" ".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n" ".import FILE TABLE Import data from FILE into TABLE\n"
#ifndef SQLITE_OMIT_TEST_CONTROL
".imposter INDEX TABLE Create imposter table TABLE on index INDEX\n"
#endif
".indexes ?TABLE? Show names of all indexes\n" ".indexes ?TABLE? Show names of all indexes\n"
" If TABLE specified, only show indexes for tables\n" " If TABLE specified, only show indexes for tables\n"
" matching LIKE pattern TABLE.\n" " matching LIKE pattern TABLE.\n"
@ -2165,6 +2199,8 @@ static char zHelp[] =
".iotrace FILE Enable I/O diagnostic logging to FILE\n" ".iotrace FILE Enable I/O diagnostic logging to FILE\n"
#endif #endif
".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT\n" ".limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT\n"
".lint OPTIONS Report potential schema issues. Options:\n"
" fkey-indexes Find missing foreign key indexes\n"
#ifndef SQLITE_OMIT_LOAD_EXTENSION #ifndef SQLITE_OMIT_LOAD_EXTENSION
".load FILE ?ENTRY? Load an extension library\n" ".load FILE ?ENTRY? Load an extension library\n"
#endif #endif
@ -2177,6 +2213,7 @@ static char zHelp[] =
" insert SQL insert statements for TABLE\n" " insert SQL insert statements for TABLE\n"
" line One value per line\n" " line One value per line\n"
" list Values delimited by .separator strings\n" " list Values delimited by .separator strings\n"
" quote Escape answers as for SQL\n"
" tabs Tab-separated values\n" " tabs Tab-separated values\n"
" tcl TCL list elements\n" " tcl TCL list elements\n"
".nullvalue STRING Use STRING in place of NULL values\n" ".nullvalue STRING Use STRING in place of NULL values\n"
@ -2243,14 +2280,22 @@ void session_help(ShellState *p){
/* Forward reference */ /* Forward reference */
static int process_input(ShellState *p, FILE *in); static int process_input(ShellState *p, FILE *in);
/* /*
** Read the content of a file into memory obtained from sqlite3_malloc64(). ** Read the content of file zName into memory obtained from sqlite3_malloc64()
** The caller is responsible for freeing the memory. ** and return a pointer to the buffer. The caller is responsible for freeing
** the memory.
** **
** NULL is returned if any error is encountered. ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
** read.
**
** For convenience, a nul-terminator byte is always appended to the data read
** from the file before the buffer is returned. This byte is not included in
** the final value of (*pnByte), if applicable.
**
** NULL is returned if any error is encountered. The final value of *pnByte
** is undefined in this case.
*/ */
static char *readFile(const char *zName){ static char *readFile(const char *zName, int *pnByte){
FILE *in = fopen(zName, "rb"); FILE *in = fopen(zName, "rb");
long nIn; long nIn;
size_t nRead; size_t nRead;
@ -2268,6 +2313,7 @@ static char *readFile(const char *zName){
return 0; return 0;
} }
pBuf[nIn] = 0; pBuf[nIn] = 0;
if( pnByte ) *pnByte = nIn;
return pBuf; return pBuf;
} }
@ -2283,12 +2329,13 @@ static void readfileFunc(
){ ){
const char *zName; const char *zName;
void *pBuf; void *pBuf;
int nBuf;
UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argc);
zName = (const char*)sqlite3_value_text(argv[0]); zName = (const char*)sqlite3_value_text(argv[0]);
if( zName==0 ) return; if( zName==0 ) return;
pBuf = readFile(zName); pBuf = readFile(zName, &nBuf);
if( pBuf ) sqlite3_result_blob(context, pBuf, -1, sqlite3_free); if( pBuf ) sqlite3_result_blob(context, pBuf, nBuf, sqlite3_free);
} }
/* /*
@ -2567,6 +2614,8 @@ static FILE *output_file_open(const char *zFile){
return f; return f;
} }
#if !defined(SQLITE_UNTESTABLE)
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
/* /*
** A routine for handling output from sqlite3_trace(). ** A routine for handling output from sqlite3_trace().
*/ */
@ -2587,6 +2636,8 @@ static int sql_trace_callback(
} }
return 0; return 0;
} }
#endif
#endif
/* /*
** A no-op routine that runs with the ".breakpoint" doc-command. This is ** A no-op routine that runs with the ".breakpoint" doc-command. This is
@ -3214,6 +3265,253 @@ int shellDeleteFile(const char *zFilename){
return rc; return rc;
} }
/*
** The implementation of SQL scalar function fkey_collate_clause(), used
** by the ".lint fkey-indexes" command. This scalar function is always
** called with four arguments - the parent table name, the parent column name,
** the child table name and the child column name.
**
** fkey_collate_clause('parent-tab', 'parent-col', 'child-tab', 'child-col')
**
** If either of the named tables or columns do not exist, this function
** returns an empty string. An empty string is also returned if both tables
** and columns exist but have the same default collation sequence. Or,
** if both exist but the default collation sequences are different, this
** function returns the string " COLLATE <parent-collation>", where
** <parent-collation> is the default collation sequence of the parent column.
*/
static void shellFkeyCollateClause(
sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal
){
sqlite3 *db = sqlite3_context_db_handle(pCtx);
const char *zParent;
const char *zParentCol;
const char *zParentSeq;
const char *zChild;
const char *zChildCol;
const char *zChildSeq = 0; /* Initialize to avoid false-positive warning */
int rc;
assert( nVal==4 );
zParent = (const char*)sqlite3_value_text(apVal[0]);
zParentCol = (const char*)sqlite3_value_text(apVal[1]);
zChild = (const char*)sqlite3_value_text(apVal[2]);
zChildCol = (const char*)sqlite3_value_text(apVal[3]);
sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
rc = sqlite3_table_column_metadata(
db, "main", zParent, zParentCol, 0, &zParentSeq, 0, 0, 0
);
if( rc==SQLITE_OK ){
rc = sqlite3_table_column_metadata(
db, "main", zChild, zChildCol, 0, &zChildSeq, 0, 0, 0
);
}
if( rc==SQLITE_OK && sqlite3_stricmp(zParentSeq, zChildSeq) ){
char *z = sqlite3_mprintf(" COLLATE %s", zParentSeq);
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
sqlite3_free(z);
}
}
/*
** The implementation of dot-command ".lint fkey-indexes".
*/
static int lintFkeyIndexes(
ShellState *pState, /* Current shell tool state */
char **azArg, /* Array of arguments passed to dot command */
int nArg /* Number of entries in azArg[] */
){
sqlite3 *db = pState->db; /* Database handle to query "main" db of */
FILE *out = pState->out; /* Stream to write non-error output to */
int bVerbose = 0; /* If -verbose is present */
int bGroupByParent = 0; /* If -groupbyparent is present */
int i; /* To iterate through azArg[] */
const char *zIndent = ""; /* How much to indent CREATE INDEX by */
int rc; /* Return code */
sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */
/*
** This SELECT statement returns one row for each foreign key constraint
** in the schema of the main database. The column values are:
**
** 0. The text of an SQL statement similar to:
**
** "EXPLAIN QUERY PLAN SELECT rowid FROM child_table WHERE child_key=?"
**
** This is the same SELECT that the foreign keys implementation needs
** to run internally on child tables. If there is an index that can
** be used to optimize this query, then it can also be used by the FK
** implementation to optimize DELETE or UPDATE statements on the parent
** table.
**
** 1. A GLOB pattern suitable for sqlite3_strglob(). If the plan output by
** the EXPLAIN QUERY PLAN command matches this pattern, then the schema
** contains an index that can be used to optimize the query.
**
** 2. Human readable text that describes the child table and columns. e.g.
**
** "child_table(child_key1, child_key2)"
**
** 3. Human readable text that describes the parent table and columns. e.g.
**
** "parent_table(parent_key1, parent_key2)"
**
** 4. A full CREATE INDEX statement for an index that could be used to
** optimize DELETE or UPDATE statements on the parent table. e.g.
**
** "CREATE INDEX child_table_child_key ON child_table(child_key)"
**
** 5. The name of the parent table.
**
** These six values are used by the C logic below to generate the report.
*/
const char *zSql =
"SELECT "
" 'EXPLAIN QUERY PLAN SELECT rowid FROM ' || quote(s.name) || ' WHERE '"
" || group_concat(quote(s.name) || '.' || quote(f.[from]) || '=?' "
" || fkey_collate_clause(f.[table], f.[to], s.name, f.[from]),' AND ')"
", "
" 'SEARCH TABLE ' || s.name || ' USING COVERING INDEX*('"
" || group_concat('*=?', ' AND ') || ')'"
", "
" s.name || '(' || group_concat(f.[from], ', ') || ')'"
", "
" f.[table] || '(' || group_concat(COALESCE(f.[to], "
" (SELECT name FROM pragma_table_info(f.[table]) WHERE pk=seq+1)"
" )) || ')'"
", "
" 'CREATE INDEX ' || quote(s.name ||'_'|| group_concat(f.[from], '_'))"
" || ' ON ' || quote(s.name) || '('"
" || group_concat(quote(f.[from]) ||"
" fkey_collate_clause(f.[table], f.[to], s.name, f.[from]), ', ')"
" || ');'"
", "
" f.[table] "
"FROM sqlite_master AS s, pragma_foreign_key_list(s.name) AS f "
"GROUP BY s.name, f.id "
"ORDER BY (CASE WHEN ? THEN f.[table] ELSE s.name END)"
;
for(i=2; i<nArg; i++){
int n = (int)strlen(azArg[i]);
if( n>1 && sqlite3_strnicmp("-verbose", azArg[i], n)==0 ){
bVerbose = 1;
}
else if( n>1 && sqlite3_strnicmp("-groupbyparent", azArg[i], n)==0 ){
bGroupByParent = 1;
zIndent = " ";
}
else{
raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n",
azArg[0], azArg[1]
);
return SQLITE_ERROR;
}
}
/* Register the fkey_collate_clause() SQL function */
rc = sqlite3_create_function(db, "fkey_collate_clause", 4, SQLITE_UTF8,
0, shellFkeyCollateClause, 0, 0
);
if( rc==SQLITE_OK ){
rc = sqlite3_prepare_v2(db, zSql, -1, &pSql, 0);
}
if( rc==SQLITE_OK ){
sqlite3_bind_int(pSql, 1, bGroupByParent);
}
if( rc==SQLITE_OK ){
int rc2;
char *zPrev = 0;
while( SQLITE_ROW==sqlite3_step(pSql) ){
int res = -1;
sqlite3_stmt *pExplain = 0;
const char *zEQP = (const char*)sqlite3_column_text(pSql, 0);
const char *zGlob = (const char*)sqlite3_column_text(pSql, 1);
const char *zFrom = (const char*)sqlite3_column_text(pSql, 2);
const char *zTarget = (const char*)sqlite3_column_text(pSql, 3);
const char *zCI = (const char*)sqlite3_column_text(pSql, 4);
const char *zParent = (const char*)sqlite3_column_text(pSql, 5);
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc!=SQLITE_OK ) break;
if( SQLITE_ROW==sqlite3_step(pExplain) ){
const char *zPlan = (const char*)sqlite3_column_text(pExplain, 3);
res = (0==sqlite3_strglob(zGlob, zPlan));
}
rc = sqlite3_finalize(pExplain);
if( rc!=SQLITE_OK ) break;
if( res<0 ){
raw_printf(stderr, "Error: internal error");
break;
}else{
if( bGroupByParent
&& (bVerbose || res==0)
&& (zPrev==0 || sqlite3_stricmp(zParent, zPrev))
){
raw_printf(out, "-- Parent table %s\n", zParent);
sqlite3_free(zPrev);
zPrev = sqlite3_mprintf("%s", zParent);
}
if( res==0 ){
raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget);
}else if( bVerbose ){
raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n",
zIndent, zFrom, zTarget
);
}
}
}
sqlite3_free(zPrev);
if( rc!=SQLITE_OK ){
raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
}
rc2 = sqlite3_finalize(pSql);
if( rc==SQLITE_OK && rc2!=SQLITE_OK ){
rc = rc2;
raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
}
}else{
raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
}
return rc;
}
/*
** Implementation of ".lint" dot command.
*/
static int lintDotCommand(
ShellState *pState, /* Current shell tool state */
char **azArg, /* Array of arguments passed to dot command */
int nArg /* Number of entries in azArg[] */
){
int n;
n = (nArg>=2 ? (int)strlen(azArg[1]) : 0);
if( n<1 || sqlite3_strnicmp(azArg[1], "fkey-indexes", n) ) goto usage;
return lintFkeyIndexes(pState, azArg, nArg);
usage:
raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]);
raw_printf(stderr, "Where sub-commands are:\n");
raw_printf(stderr, " fkey-indexes\n");
return SQLITE_ERROR;
}
/* /*
** If an input line begins with "." then invoke this routine to ** If an input line begins with "." then invoke this routine to
** process that line. ** process that line.
@ -3377,7 +3675,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg!=2 ){ if( nArg!=2 ){
raw_printf(stderr, "Usage: .check GLOB-PATTERN\n"); raw_printf(stderr, "Usage: .check GLOB-PATTERN\n");
rc = 2; rc = 2;
}else if( (zRes = readFile("testcase-out.txt"))==0 ){ }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n"); raw_printf(stderr, "Error: cannot read 'testcase-out.txt'\n");
rc = 2; rc = 2;
}else if( testcase_glob(azArg[1],zRes)==0 ){ }else if( testcase_glob(azArg[1],zRes)==0 ){
@ -3406,13 +3704,12 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zErrMsg = 0; char *zErrMsg = 0;
open_db(p, 0); open_db(p, 0);
memcpy(&data, p, sizeof(data)); memcpy(&data, p, sizeof(data));
data.showHeader = 1; data.showHeader = 0;
data.cMode = data.mode = MODE_Column; data.cMode = data.mode = MODE_List;
data.colWidth[0] = 3; sqlite3_snprintf(sizeof(data.colSeparator),data.colSeparator,": ");
data.colWidth[1] = 15;
data.colWidth[2] = 58;
data.cnt = 0; data.cnt = 0;
sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg); sqlite3_exec(p->db, "SELECT name, file FROM pragma_database_list",
callback, &data, &zErrMsg);
if( zErrMsg ){ if( zErrMsg ){
utf8_printf(stderr,"Error: %s\n", zErrMsg); utf8_printf(stderr,"Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);
@ -3793,51 +4090,78 @@ static int do_meta_command(char *zLine, ShellState *p){
if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
}else }else
if( c=='i' && (strncmp(azArg[0], "indices", n)==0 #ifndef SQLITE_UNTESTABLE
|| strncmp(azArg[0], "indexes", n)==0) ){ if( c=='i' && strncmp(azArg[0], "imposter", n)==0 ){
ShellState data; char *zSql;
char *zErrMsg = 0; char *zCollist = 0;
open_db(p, 0); sqlite3_stmt *pStmt;
memcpy(&data, p, sizeof(data)); int tnum = 0;
data.showHeader = 0; int i;
data.cMode = data.mode = MODE_List; if( nArg!=3 ){
if( nArg==1 ){ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n");
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND name NOT LIKE 'sqlite_%' "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type='index' "
"ORDER BY 1",
callback, &data, &zErrMsg
);
}else if( nArg==2 ){
zShellStatic = azArg[1];
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
"WHERE type='index' AND tbl_name LIKE shellstatic() "
"UNION ALL "
"SELECT name FROM sqlite_temp_master "
"WHERE type='index' AND tbl_name LIKE shellstatic() "
"ORDER BY 1",
callback, &data, &zErrMsg
);
zShellStatic = 0;
}else{
raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
rc = 1; rc = 1;
goto meta_command_exit; goto meta_command_exit;
} }
if( zErrMsg ){ open_db(p, 0);
utf8_printf(stderr,"Error: %s\n", zErrMsg); zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master"
sqlite3_free(zErrMsg); " WHERE name='%q' AND type='index'", azArg[1]);
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
tnum = sqlite3_column_int(pStmt, 0);
}
sqlite3_finalize(pStmt);
if( tnum==0 ){
utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]);
rc = 1; rc = 1;
}else if( rc != SQLITE_OK ){ goto meta_command_exit;
raw_printf(stderr, }
"Error: querying sqlite_master and sqlite_temp_master\n"); zSql = sqlite3_mprintf("PRAGMA index_xinfo='%q'", azArg[1]);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
i = 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
char zLabel[20];
const char *zCol = (const char*)sqlite3_column_text(pStmt,2);
i++;
if( zCol==0 ){
if( sqlite3_column_int(pStmt,1)==-1 ){
zCol = "_ROWID_";
}else{
sqlite3_snprintf(sizeof(zLabel),zLabel,"expr%d",i);
zCol = zLabel;
}
}
if( zCollist==0 ){
zCollist = sqlite3_mprintf("\"%w\"", zCol);
}else{
zCollist = sqlite3_mprintf("%z,\"%w\"", zCollist, zCol);
}
}
sqlite3_finalize(pStmt);
zSql = sqlite3_mprintf(
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%s))WITHOUT ROWID",
azArg[2], zCollist, zCollist);
sqlite3_free(zCollist);
rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 1, tnum);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
if( rc ){
utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
}else{
utf8_printf(stdout, "%s;\n", zSql);
raw_printf(stdout,
"WARNING: writing to an imposter table will corrupt the index!\n"
);
}
}else{
raw_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1; rc = 1;
} }
sqlite3_free(zSql);
}else }else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
#ifdef SQLITE_ENABLE_IOTRACE #ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){ if( c=='i' && strncmp(azArg[0], "iotrace", n)==0 ){
@ -3861,6 +4185,7 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
}else }else
#endif #endif
if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){ if( c=='l' && n>=5 && strncmp(azArg[0], "limits", n)==0 ){
static const struct { static const struct {
const char *zLimitName; /* Name of a limit */ const char *zLimitName; /* Name of a limit */
@ -3920,6 +4245,11 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
}else }else
if( c=='l' && n>2 && strncmp(azArg[0], "lint", n)==0 ){
open_db(p, 0);
lintDotCommand(p, azArg, nArg);
}else
#ifndef SQLITE_OMIT_LOAD_EXTENSION #ifndef SQLITE_OMIT_LOAD_EXTENSION
if( c=='l' && strncmp(azArg[0], "load", n)==0 ){ if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc; const char *zFile, *zProc;
@ -3977,13 +4307,15 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){ }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert; p->mode = MODE_Insert;
set_table_name(p, nArg>=3 ? azArg[2] : "table"); set_table_name(p, nArg>=3 ? azArg[2] : "table");
}else if( c2=='q' && strncmp(azArg[1],"quote",n2)==0 ){
p->mode = MODE_Quote;
}else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){ }else if( c2=='a' && strncmp(azArg[1],"ascii",n2)==0 ){
p->mode = MODE_Ascii; p->mode = MODE_Ascii;
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit); sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Unit);
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Record);
}else { }else {
raw_printf(stderr, "Error: mode should be one of: " raw_printf(stderr, "Error: mode should be one of: "
"ascii column csv html insert line list tabs tcl\n"); "ascii column csv html insert line list quote tabs tcl\n");
rc = 1; rc = 1;
} }
p->cMode = p->mode; p->cMode = p->mode;
@ -4581,7 +4913,10 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
}else }else
if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){ if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0)
|| (c=='i' && (strncmp(azArg[0], "indices", n)==0
|| strncmp(azArg[0], "indexes", n)==0) )
){
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
char **azResult; char **azResult;
int nRow, nAlloc; int nRow, nAlloc;
@ -4594,28 +4929,41 @@ static int do_meta_command(char *zLine, ShellState *p){
/* Create an SQL statement to query for the list of tables in the /* Create an SQL statement to query for the list of tables in the
** main and all attached databases where the table name matches the ** main and all attached databases where the table name matches the
** LIKE pattern bound to variable "?1". */ ** LIKE pattern bound to variable "?1". */
zSql = sqlite3_mprintf( if( c=='t' ){
"SELECT name FROM sqlite_master" zSql = sqlite3_mprintf(
" WHERE type IN ('table','view')" "SELECT name FROM sqlite_master"
" AND name NOT LIKE 'sqlite_%%'" " WHERE type IN ('table','view')"
" AND name LIKE ?1"); " AND name NOT LIKE 'sqlite_%%'"
while( zSql && sqlite3_step(pStmt)==SQLITE_ROW ){ " AND name LIKE ?1");
}else if( nArg>2 ){
/* It is an historical accident that the .indexes command shows an error
** when called with the wrong number of arguments whereas the .tables
** command does not. */
raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
rc = 1;
goto meta_command_exit;
}else{
zSql = sqlite3_mprintf(
"SELECT name FROM sqlite_master"
" WHERE type='index'"
" AND tbl_name LIKE ?1");
}
for(ii=0; zSql && sqlite3_step(pStmt)==SQLITE_ROW; ii++){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue; if( zDbName==0 || ii==0 ) continue;
if( strcmp(zDbName,"temp")==0 ){ if( c=='t' ){
zSql = sqlite3_mprintf(
"%z UNION ALL "
"SELECT 'temp.' || name FROM sqlite_temp_master"
" WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite_%%'"
" AND name LIKE ?1", zSql);
}else{
zSql = sqlite3_mprintf( zSql = sqlite3_mprintf(
"%z UNION ALL " "%z UNION ALL "
"SELECT '%q.' || name FROM \"%w\".sqlite_master" "SELECT '%q.' || name FROM \"%w\".sqlite_master"
" WHERE type IN ('table','view')" " WHERE type IN ('table','view')"
" AND name NOT LIKE 'sqlite_%%'" " AND name NOT LIKE 'sqlite_%%'"
" AND name LIKE ?1", zSql, zDbName, zDbName); " AND name LIKE ?1", zSql, zDbName, zDbName);
}else{
zSql = sqlite3_mprintf(
"%z UNION ALL "
"SELECT '%q.' || name FROM \"%w\".sqlite_master"
" WHERE type='index'"
" AND tbl_name LIKE ?1", zSql, zDbName, zDbName);
} }
} }
rc = sqlite3_finalize(pStmt); rc = sqlite3_finalize(pStmt);
@ -4690,7 +5038,7 @@ static int do_meta_command(char *zLine, ShellState *p){
output_reset(p); output_reset(p);
p->out = output_file_open("testcase-out.txt"); p->out = output_file_open("testcase-out.txt");
if( p->out==0 ){ if( p->out==0 ){
utf8_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n");
} }
if( nArg>=2 ){ if( nArg>=2 ){
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]);
@ -4699,6 +5047,7 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
}else }else
#ifndef SQLITE_UNTESTABLE
if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){
static const struct { static const struct {
const char *zCtrlName; /* Name of a test-control option */ const char *zCtrlName; /* Name of a test-control option */
@ -4874,6 +5223,7 @@ static int do_meta_command(char *zLine, ShellState *p){
} }
#endif #endif
}else }else
#endif /* !defined(SQLITE_UNTESTABLE) */
#if SQLITE_USER_AUTHENTICATION #if SQLITE_USER_AUTHENTICATION
if( c=='u' && strncmp(azArg[0], "user", n)==0 ){ if( c=='u' && strncmp(azArg[0], "user", n)==0 ){
@ -5082,6 +5432,42 @@ static int line_is_complete(char *zSql, int nSql){
return rc; return rc;
} }
/*
** Run a single line of SQL
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
char *zErrMsg = 0;
open_db(p, 0);
if( p->backslashOn ) resolve_backslashes(zSql);
BEGIN_TIMER;
rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"Error: near line %d:", startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
}
if( zErrMsg!=0 ){
utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}else{
utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
}
return 1;
}else if( p->countChanges ){
raw_printf(p->out, "changes: %3d total_changes: %d\n",
sqlite3_changes(p->db), sqlite3_total_changes(p->db));
}
return 0;
}
/* /*
** Read input from *in and process it. If *in==0 then input ** Read input from *in and process it. If *in==0 then input
** is interactive - the user is typing it it. Otherwise, input ** is interactive - the user is typing it it. Otherwise, input
@ -5098,7 +5484,6 @@ static int process_input(ShellState *p, FILE *in){
int nSql = 0; /* Bytes of zSql[] used */ int nSql = 0; /* Bytes of zSql[] used */
int nAlloc = 0; /* Allocated zSql[] space */ int nAlloc = 0; /* Allocated zSql[] space */
int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
char *zErrMsg; /* Error message returned */
int rc; /* Error code */ int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */ int errCnt = 0; /* Number of errors seen */
int lineno = 0; /* Current line number */ int lineno = 0; /* Current line number */
@ -5158,32 +5543,7 @@ static int process_input(ShellState *p, FILE *in){
} }
if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior) if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
&& sqlite3_complete(zSql) ){ && sqlite3_complete(zSql) ){
p->cnt = 0; errCnt += runOneSqlLine(p, zSql, in, startline);
open_db(p, 0);
if( p->backslashOn ) resolve_backslashes(zSql);
BEGIN_TIMER;
rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
"Error: near line %d:", startline);
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "Error:");
}
if( zErrMsg!=0 ){
utf8_printf(stderr, "%s %s\n", zPrefix, zErrMsg);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}else{
utf8_printf(stderr, "%s %s\n", zPrefix, sqlite3_errmsg(p->db));
}
errCnt++;
}else if( p->countChanges ){
raw_printf(p->out, "changes: %3d total_changes: %d\n",
sqlite3_changes(p->db), sqlite3_total_changes(p->db));
}
nSql = 0; nSql = 0;
if( p->outCount ){ if( p->outCount ){
output_reset(p); output_reset(p);
@ -5194,11 +5554,8 @@ static int process_input(ShellState *p, FILE *in){
nSql = 0; nSql = 0;
} }
} }
if( nSql ){ if( nSql && !_all_whitespace(zSql) ){
if( !_all_whitespace(zSql) ){ runOneSqlLine(p, zSql, in, startline);
utf8_printf(stderr, "Error: incomplete SQL: %s\n", zSql);
errCnt++;
}
} }
free(zSql); free(zSql);
free(zLine); free(zLine);

File diff suppressed because it is too large Load diff

View file

@ -121,13 +121,13 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()]. ** [sqlite_version()] and [sqlite_source_id()].
*/ */
#define SQLITE_VERSION "3.15.2" #define SQLITE_VERSION "3.16.2"
#define SQLITE_VERSION_NUMBER 3015002 #define SQLITE_VERSION_NUMBER 3016002
#define SQLITE_SOURCE_ID "2016-11-28 19:13:37 bbd85d235f7037c6a033a9690534391ffeacecc8" #define SQLITE_SOURCE_ID "2017-01-06 16:32:41 a65a62893ca8319e89e48b8a38cf8a59c69a8209"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version, sqlite3_sourceid ** KEYWORDS: sqlite3_version sqlite3_sourceid
** **
** These interfaces provide the same information as the [SQLITE_VERSION], ** These interfaces provide the same information as the [SQLITE_VERSION],
** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
@ -1035,6 +1035,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_VFS_POINTER 27 #define SQLITE_FCNTL_VFS_POINTER 27
#define SQLITE_FCNTL_JOURNAL_POINTER 28 #define SQLITE_FCNTL_JOURNAL_POINTER 28
#define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_WIN32_GET_HANDLE 29
#define SQLITE_FCNTL_PDB 30
/* deprecated names */ /* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@ -1987,6 +1988,18 @@ struct sqlite3_mem_methods {
** until after the database connection closes. ** until after the database connection closes.
** </dd> ** </dd>
** **
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
** override this behaviour. The first parameter passed to this operation
** is an integer - non-zero to disable checkpoints-on-close, or zero (the
** default) to enable them. The second parameter is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
** </dl> ** </dl>
*/ */
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@ -1995,6 +2008,7 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
/* /*
@ -3596,6 +3610,10 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
** sqlite3_stmt_readonly() to return true since, while those statements ** sqlite3_stmt_readonly() to return true since, while those statements
** change the configuration of a database connection, they do not make ** change the configuration of a database connection, they do not make
** changes to the content of the database files on disk. ** changes to the content of the database files on disk.
** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since
** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and
** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so
** sqlite3_stmt_readonly() returns false for those commands.
*/ */
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
@ -3878,8 +3896,12 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
** METHOD: sqlite3_stmt ** METHOD: sqlite3_stmt
** **
** ^Return the number of columns in the result set returned by the ** ^Return the number of columns in the result set returned by the
** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** [prepared statement]. ^If this routine returns 0, that means the
** statement that does not return data (for example an [UPDATE]). ** [prepared statement] returns no data (for example an [UPDATE]).
** ^However, just because this routine returns a positive number does not
** mean that one or more rows of data will be returned. ^A SELECT statement
** will always have a positive sqlite3_column_count() but depending on the
** WHERE clause constraints and the table content, it might return no rows.
** **
** See also: [sqlite3_data_count()] ** See also: [sqlite3_data_count()]
*/ */
@ -8211,7 +8233,8 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
** **
** See also: [sqlite3_update_hook()] ** See also: [sqlite3_update_hook()]
*/ */
SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook( #if defined(SQLITE_ENABLE_PREUPDATE_HOOK)
SQLITE_API void *sqlite3_preupdate_hook(
sqlite3 *db, sqlite3 *db,
void(*xPreUpdate)( void(*xPreUpdate)(
void *pCtx, /* Copy of third arg to preupdate_hook() */ void *pCtx, /* Copy of third arg to preupdate_hook() */
@ -8224,10 +8247,11 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
), ),
void* void*
); );
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **); SQLITE_API int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *); SQLITE_API int sqlite3_preupdate_count(sqlite3 *);
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *); SQLITE_API int sqlite3_preupdate_depth(sqlite3 *);
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **); SQLITE_API int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
#endif
/* /*
** CAPI3REF: Low-level system error code ** CAPI3REF: Low-level system error code
@ -8243,7 +8267,7 @@ SQLITE_API int sqlite3_system_errno(sqlite3*);
/* /*
** CAPI3REF: Database Snapshot ** CAPI3REF: Database Snapshot
** KEYWORDS: {snapshot} ** KEYWORDS: {snapshot} {sqlite3_snapshot}
** EXPERIMENTAL ** EXPERIMENTAL
** **
** An instance of the snapshot object records the state of a [WAL mode] ** An instance of the snapshot object records the state of a [WAL mode]
@ -8267,7 +8291,9 @@ SQLITE_API int sqlite3_system_errno(sqlite3*);
** to an historical snapshot (if possible). The destructor for ** to an historical snapshot (if possible). The destructor for
** sqlite3_snapshot objects is [sqlite3_snapshot_free()]. ** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
*/ */
typedef struct sqlite3_snapshot sqlite3_snapshot; typedef struct sqlite3_snapshot {
unsigned char hidden[48];
} sqlite3_snapshot;
/* /*
** CAPI3REF: Record A Database Snapshot ** CAPI3REF: Record A Database Snapshot
@ -8278,9 +8304,32 @@ typedef struct sqlite3_snapshot sqlite3_snapshot;
** schema S in database connection D. ^On success, the ** schema S in database connection D. ^On success, the
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK. ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
** ^If schema S of [database connection] D is not a [WAL mode] database ** If there is not already a read-transaction open on schema S when
** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)] ** this function is called, one is opened automatically.
** leaves the *P value unchanged and returns an appropriate [error code]. **
** The following must be true for this function to succeed. If any of
** the following statements are false when sqlite3_snapshot_get() is
** called, SQLITE_ERROR is returned. The final value of *P is undefined
** in this case.
**
** <ul>
** <li> The database handle must be in [autocommit mode].
**
** <li> Schema S of [database connection] D must be a [WAL mode] database.
**
** <li> There must not be a write transaction open on schema S of database
** connection D.
**
** <li> One or more transactions must have been written to the current wal
** file since it was created on disk (by any connection). This means
** that a snapshot cannot be taken on a wal mode database with no wal
** file immediately after it is first opened. At least one transaction
** must be written to it first.
** </ul>
**
** This function may also return SQLITE_NOMEM. If it is called with the
** database handle in autocommit mode but fails for some other reason,
** whether or not a read transaction is opened on schema S is undefined.
** **
** The [sqlite3_snapshot] object returned from a successful call to ** The [sqlite3_snapshot] object returned from a successful call to
** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
@ -8373,6 +8422,28 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
sqlite3_snapshot *p2 sqlite3_snapshot *p2
); );
/*
** CAPI3REF: Recover snapshots from a wal file
** EXPERIMENTAL
**
** If all connections disconnect from a database file but do not perform
** a checkpoint, the existing wal file is opened along with the database
** file the next time the database is opened. At this point it is only
** possible to successfully call sqlite3_snapshot_open() to open the most
** recent snapshot of the database (the one at the head of the wal file),
** even though the wal file may contain other valid snapshots for which
** clients have sqlite3_snapshot handles.
**
** This function attempts to scan the wal file associated with database zDb
** of database handle db and make all valid snapshots available to
** sqlite3_snapshot_open(). It is an error if there is already a read
** transaction open on the database, or if the database is not a wal mode
** database.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
/* /*
** Undo the hack that converts floating point types to integer for ** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support. ** builds on processors without floating point support.