Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge trunk changes with this branch. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | begin-concurrent |
Files: | files | file ages | folders |
SHA1: |
deaf3b18569025d8d168da29fae76142 |
User & Date: | dan 2015-08-21 16:22:04.553 |
Context
2015-08-21
| ||
17:57 | Fix a problem with UNLOCKED transactions that free pages allocated within the same transaction. (check-in: 227bb8a181 user: dan tags: begin-concurrent) | |
16:22 | Merge trunk changes with this branch. (check-in: deaf3b1856 user: dan tags: begin-concurrent) | |
14:21 | Add extra tests and a fix for rollbacks of UNLOCKED transactions. (check-in: 82cd837e72 user: dan tags: begin-concurrent) | |
12:37 | Fix typo in comment. No changes to code. (check-in: 7b8d17dd84 user: drh tags: trunk) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | TESTSRC += \ $(TOP)/ext/misc/amatch.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c # Source code to the library files needed by the test fixture # TESTSRC2 = \ | > > | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | TESTSRC += \ $(TOP)/ext/misc/amatch.c \ $(TOP)/ext/misc/closure.c \ $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c # Source code to the library files needed by the test fixture # TESTSRC2 = \ |
︙ | ︙ |
Changes to Makefile.msc.
︙ | ︙ | |||
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 | $(TOP)\ext\misc\amatch.c \ $(TOP)\ext\misc\closure.c \ $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\fuzzer.c \ fts5.c \ $(TOP)\ext\fts5\fts5_tcl.c \ $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\wholenumber.c # Source code to the library files needed by the test fixture # | > > | 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 | $(TOP)\ext\misc\amatch.c \ $(TOP)\ext\misc\closure.c \ $(TOP)\ext\misc\eval.c \ $(TOP)\ext\misc\fileio.c \ $(TOP)\ext\misc\fuzzer.c \ fts5.c \ $(TOP)\ext\fts5\fts5_tcl.c \ $(TOP)\ext\fts5\fts5_test_mi.c \ $(TOP)\ext\misc\ieee754.c \ $(TOP)\ext\misc\nextchar.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\series.c \ $(TOP)\ext\misc\spellfix.c \ $(TOP)\ext\misc\totype.c \ $(TOP)\ext\misc\wholenumber.c # Source code to the library files needed by the test fixture # |
︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 3.8.12 |
Changes to configure.
1 2 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. | | | 1 2 3 4 5 6 7 8 9 10 | #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for sqlite 3.8.12. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. |
︙ | ︙ | |||
722 723 724 725 726 727 728 | subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' | | | | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' PACKAGE_VERSION='3.8.12' PACKAGE_STRING='sqlite 3.8.12' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H |
︙ | ︙ | |||
1450 1451 1452 1453 1454 1455 1456 | # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF | | | 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 | # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures sqlite 3.8.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. |
︙ | ︙ | |||
1515 1516 1517 1518 1519 1520 1521 | --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in | | | 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 | --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of sqlite 3.8.12:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] |
︙ | ︙ | |||
1630 1631 1632 1633 1634 1635 1636 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF sqlite configure 3.8.12 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit |
︙ | ︙ | |||
2049 2050 2051 2052 2053 2054 2055 | eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. | | | 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 | eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by sqlite $as_me 3.8.12, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
11804 11805 11806 11807 11808 11809 11810 | test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" | | | 11804 11805 11806 11807 11808 11809 11810 11811 11812 11813 11814 11815 11816 11817 11818 | test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by sqlite $as_me 3.8.12, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ |
︙ | ︙ | |||
11870 11871 11872 11873 11874 11875 11876 | Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ | | | 11870 11871 11872 11873 11874 11875 11876 11877 11878 11879 11880 11881 11882 11883 11884 | Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ sqlite config.status 3.8.12 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." |
︙ | ︙ |
Changes to ext/async/sqlite3async.c.
︙ | ︙ | |||
1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 | asyncWriterThread(); } /* ** Control/configure the asynchronous IO system. */ int sqlite3async_control(int op, ...){ va_list ap; va_start(ap, op); switch( op ){ case SQLITEASYNC_HALT: { int eWhen = va_arg(ap, int); if( eWhen!=SQLITEASYNC_HALT_NEVER && eWhen!=SQLITEASYNC_HALT_NOW && eWhen!=SQLITEASYNC_HALT_IDLE ){ | > | > | > | > | 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 | asyncWriterThread(); } /* ** Control/configure the asynchronous IO system. */ int sqlite3async_control(int op, ...){ int rc = SQLITE_OK; va_list ap; va_start(ap, op); switch( op ){ case SQLITEASYNC_HALT: { int eWhen = va_arg(ap, int); if( eWhen!=SQLITEASYNC_HALT_NEVER && eWhen!=SQLITEASYNC_HALT_NOW && eWhen!=SQLITEASYNC_HALT_IDLE ){ rc = SQLITE_MISUSE; break; } async.eHalt = eWhen; async_mutex_enter(ASYNC_MUTEX_QUEUE); async_cond_signal(ASYNC_COND_QUEUE); async_mutex_leave(ASYNC_MUTEX_QUEUE); break; } case SQLITEASYNC_DELAY: { int iDelay = va_arg(ap, int); if( iDelay<0 ){ rc = SQLITE_MISUSE; break; } async.ioDelay = iDelay; break; } case SQLITEASYNC_LOCKFILES: { int bLock = va_arg(ap, int); async_mutex_enter(ASYNC_MUTEX_QUEUE); if( async.nFile || async.pQueueFirst ){ async_mutex_leave(ASYNC_MUTEX_QUEUE); rc = SQLITE_MISUSE; break; } async.bLockFiles = bLock; async_mutex_leave(ASYNC_MUTEX_QUEUE); break; } case SQLITEASYNC_GET_HALT: { |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 | case SQLITEASYNC_GET_LOCKFILES: { int *piDelay = va_arg(ap, int *); *piDelay = async.bLockFiles; break; } default: | | > > | | 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 | case SQLITEASYNC_GET_LOCKFILES: { int *piDelay = va_arg(ap, int *); *piDelay = async.bLockFiles; break; } default: rc = SQLITE_ERROR; break; } va_end(ap); return rc; } #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ASYNCIO) */ |
Changes to ext/fts5/fts5.h.
︙ | ︙ | |||
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | ** ** Virtual table implementations may overload SQL functions by implementing ** the sqlite3_module.xFindFunction() method. */ typedef struct Fts5ExtensionApi Fts5ExtensionApi; typedef struct Fts5Context Fts5Context; typedef void (*fts5_extension_function)( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning result/error */ int nVal, /* Number of values in apVal[] array */ sqlite3_value **apVal /* Array of trailing arguments */ ); /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): ** Return a copy of the context pointer the extension function was ** registered with. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken ** to the total number of tokens in the FTS5 table. Or, if iCol is ** non-negative but less than the number of columns in the table, return ** the total number of tokens in column iCol, considering all rows in ** the FTS5 table. ** ** If parameter iCol is greater than or equal to the number of columns ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** | > > > > > > | | | > | > > > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | ** ** Virtual table implementations may overload SQL functions by implementing ** the sqlite3_module.xFindFunction() method. */ typedef struct Fts5ExtensionApi Fts5ExtensionApi; typedef struct Fts5Context Fts5Context; typedef struct Fts5PhraseIter Fts5PhraseIter; typedef void (*fts5_extension_function)( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning result/error */ int nVal, /* Number of values in apVal[] array */ sqlite3_value **apVal /* Array of trailing arguments */ ); struct Fts5PhraseIter { const unsigned char *a; const unsigned char *b; }; /* ** EXTENSION API FUNCTIONS ** ** xUserData(pFts): ** Return a copy of the context pointer the extension function was ** registered with. ** ** xColumnTotalSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken ** to the total number of tokens in the FTS5 table. Or, if iCol is ** non-negative but less than the number of columns in the table, return ** the total number of tokens in column iCol, considering all rows in ** the FTS5 table. ** ** If parameter iCol is greater than or equal to the number of columns ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** xColumnCount(pFts): ** Return the number of columns in the table. ** ** xColumnSize(pFts, iCol, pnToken): ** If parameter iCol is less than zero, set output variable *pnToken ** to the total number of tokens in the current row. Or, if iCol is ** non-negative but less than the number of columns in the table, set ** *pnToken to the number of tokens in column iCol of the current row. ** ** If parameter iCol is greater than or equal to the number of columns ** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g. ** an OOM condition or IO error), an appropriate SQLite error code is ** returned. ** ** xColumnText: ** This function attempts to retrieve the text of column iCol of the ** current document. If successful, (*pz) is set to point to a buffer ** containing the text in utf-8 encoding, (*pn) is set to the size in bytes ** (not characters) of the buffer and SQLITE_OK is returned. Otherwise, ** if an error occurs, an SQLite error code is returned and the final values |
︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 | ** ** xRowCount(pFts5, pnRow) ** ** This function is used to retrieve the total number of rows in the table. ** In other words, the same value that would be returned by: ** ** SELECT count(*) FROM ftstable; */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 1 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); | > > > > > > > > > > > > > > > > > > > > > > > > | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | ** ** xRowCount(pFts5, pnRow) ** ** This function is used to retrieve the total number of rows in the table. ** In other words, the same value that would be returned by: ** ** SELECT count(*) FROM ftstable; ** ** xPhraseFirst() ** This function is used, along with type Fts5PhraseIter and the xPhraseNext ** method, to iterate through all instances of a single query phrase within ** the current row. This is the same information as is accessible via the ** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient ** to use, this API may be faster under some circumstances. To iterate ** through instances of phrase iPhrase, use the following code: ** ** Fts5PhraseIter iter; ** int iCol, iOff; ** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); ** iOff>=0; ** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ** ){ ** // An instance of phrase iPhrase at offset iOff of column iCol ** } ** ** The Fts5PhraseIter structure is defined above. Applications should not ** modify this structure directly - it should only be used as shown above ** with the xPhraseFirst() and xPhraseNext() API methods. ** ** xPhraseNext() ** See xPhraseFirst above. */ struct Fts5ExtensionApi { int iVersion; /* Currently always set to 1 */ void *(*xUserData)(Fts5Context*); int (*xColumnCount)(Fts5Context*); |
︙ | ︙ | |||
193 194 195 196 197 198 199 200 201 202 203 204 205 206 | int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData, int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) ); int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); void *(*xGetAuxdata)(Fts5Context*, int bClear); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* | > > > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken); int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData, int(*)(const Fts5ExtensionApi*,Fts5Context*,void*) ); int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*)); void *(*xGetAuxdata)(Fts5Context*, int bClear); void (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*); void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff); }; /* ** CUSTOM AUXILIARY FUNCTIONS *************************************************************************/ /************************************************************************* |
︙ | ︙ |
Changes to ext/fts5/fts5Int.h.
︙ | ︙ | |||
297 298 299 300 301 302 303 | ** sqlite3Fts5IterNext(pIter) ** ){ ** i64 iRowid = sqlite3Fts5IterRowid(pIter); ** } */ /* | | | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | ** sqlite3Fts5IterNext(pIter) ** ){ ** i64 iRowid = sqlite3Fts5IterRowid(pIter); ** } */ /* ** Open a new iterator to iterate though all rowids that match the ** specified token or token prefix. */ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ const char *pToken, int nToken, /* Token (or prefix) to query for */ int flags, /* Mask of FTS5INDEX_QUERY_X flags */ Fts5IndexIter **ppIter |
︙ | ︙ |
Changes to ext/fts5/fts5_expr.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 | void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64)); void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*)); void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*); struct Fts5Expr { Fts5Index *pIndex; Fts5ExprNode *pRoot; | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64)); void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*)); void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*); struct Fts5Expr { Fts5Index *pIndex; Fts5ExprNode *pRoot; int bDesc; /* Iterate in descending rowid order */ int nPhrase; /* Number of phrases in expression */ Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */ }; /* ** eType: ** Expression node type. Always one of: |
︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 | int i; const char **azConfig; /* Array of arguments for Fts5Config */ const char *zNearsetCmd = "nearset"; int nConfig; /* Size of azConfig[] */ Fts5Config *pConfig = 0; int iArg = 1; if( bTcl && nArg>1 ){ zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]); iArg = 2; } nConfig = 3 + (nArg-iArg); | > > > > > > > > > | 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 | int i; const char **azConfig; /* Array of arguments for Fts5Config */ const char *zNearsetCmd = "nearset"; int nConfig; /* Size of azConfig[] */ Fts5Config *pConfig = 0; int iArg = 1; if( nArg<1 ){ zErr = sqlite3_mprintf("wrong number of arguments to function %s", bTcl ? "fts5_expr_tcl" : "fts5_expr" ); sqlite3_result_error(pCtx, zErr, -1); sqlite3_free(zErr); return; } if( bTcl && nArg>1 ){ zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]); iArg = 2; } nConfig = 3 + (nArg-iArg); |
︙ | ︙ | |||
1999 2000 2001 2002 2003 2004 2005 | return rc; } /* ** Return the number of phrases in expression pExpr. */ int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){ | | | 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 | return rc; } /* ** Return the number of phrases in expression pExpr. */ int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){ return (pExpr ? pExpr->nPhrase : 0); } /* ** Return the number of terms in the iPhrase'th phrase in pExpr. */ int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0; |
︙ | ︙ |
Changes to ext/fts5/fts5_index.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 | ** * all segment b-tree leaf data is stored in fixed size page records ** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is ** taken to ensure it is possible to iterate in either direction through ** the entries in a doclist, or to seek to a specific entry within a ** doclist, without loading it into memory. ** ** * large doclists that span many pages have associated "doclist index" | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ** * all segment b-tree leaf data is stored in fixed size page records ** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is ** taken to ensure it is possible to iterate in either direction through ** the entries in a doclist, or to seek to a specific entry within a ** doclist, without loading it into memory. ** ** * large doclists that span many pages have associated "doclist index" ** records that contain a copy of the first rowid on each page spanned by ** the doclist. This is used to speed up seek operations, and merges of ** large doclists with very small doclists. ** ** * extra fields in the "structure record" record the state of ongoing ** incremental merge operations. ** */ |
︙ | ︙ | |||
206 207 208 209 210 211 212 | ** is: ** ** * Flags byte. Bits are: ** 0x01: Clear if leaf is also the root page, otherwise set. ** ** * Page number of fts index leaf page. As a varint. ** | | | | | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | ** is: ** ** * Flags byte. Bits are: ** 0x01: Clear if leaf is also the root page, otherwise set. ** ** * Page number of fts index leaf page. As a varint. ** ** * First rowid on page indicated by previous field. As a varint. ** ** * A list of varints, one for each subsequent termless page. A ** positive delta if the termless page contains at least one rowid, ** or an 0x00 byte otherwise. ** ** Internal doclist index nodes are: ** ** * Flags byte. Bits are: ** 0x01: Clear for root page, otherwise set. ** ** * Page number of first child page. As a varint. ** ** * Copy of first rowid on page indicated by previous field. As a varint. ** ** * A list of delta-encoded varints - the first rowid on each subsequent ** child page. ** */ /* ** Rowids for the averages and structure records in the %_data table. */ |
︙ | ︙ | |||
380 381 382 383 384 385 386 | int pgno; /* Page number for this page */ Fts5Buffer buf; /* Buffer containing page data */ Fts5Buffer term; /* Buffer containing previous term on page */ }; struct Fts5DlidxWriter { int pgno; /* Page number for this page */ int bPrevValid; /* True if iPrev is valid */ | | | | | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 | int pgno; /* Page number for this page */ Fts5Buffer buf; /* Buffer containing page data */ Fts5Buffer term; /* Buffer containing previous term on page */ }; struct Fts5DlidxWriter { int pgno; /* Page number for this page */ int bPrevValid; /* True if iPrev is valid */ i64 iPrev; /* Previous rowid value written to page */ Fts5Buffer buf; /* Buffer containing page data */ }; struct Fts5SegWriter { int iSegid; /* Segid to write to */ Fts5PageWriter writer; /* PageWriter object */ i64 iPrevRowid; /* Previous rowid written to current leaf */ u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */ u8 bFirstRowidInPage; /* True if next rowid is first in page */ u8 bFirstTermInPage; /* True if next term will be first in leaf */ int nLeafWritten; /* Number of leaf pages written */ int nEmpty; /* Number of contiguous term-less nodes */ int nDlidx; /* Allocated size of aDlidx[] array */ Fts5DlidxWriter *aDlidx; /* Array of Fts5DlidxWriter objects */ /* Values to insert into the %_idx table */ Fts5Buffer btterm; /* Next term to insert into %_idx table */ int iBtPage; /* Page number corresponding to btterm */ }; /* ** Object for iterating through the merged results of one or more segments, ** visiting each term/rowid pair in the merged data. ** ** nSeg is always a power of two greater than or equal to the number of ** segments that this object is merging data from. Both the aSeg[] and ** aFirst[] arrays are sized at nSeg entries. The aSeg[] array is padded ** with zeroed objects - these are handled as if they were iterators opened ** on empty segments. ** |
︙ | ︙ | |||
428 429 430 431 432 433 434 | typedef struct Fts5CResult Fts5CResult; struct Fts5CResult { u16 iFirst; /* aSeg[] index of firstest iterator */ u8 bTermEq; /* True if the terms are equal */ }; /* | | | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | typedef struct Fts5CResult Fts5CResult; struct Fts5CResult { u16 iFirst; /* aSeg[] index of firstest iterator */ u8 bTermEq; /* True if the terms are equal */ }; /* ** Object for iterating through a single segment, visiting each term/rowid ** pair in the segment. ** ** pSeg: ** The segment to iterate through. ** ** iLeafPgno: ** Current leaf page number within segment. |
︙ | ︙ | |||
460 461 462 463 464 465 466 | ** ** FTS5_SEGITER_ONETERM: ** If set, set the iterator to point to EOF after the current doclist ** has been exhausted. Do not proceed to the next term in the segment. ** ** FTS5_SEGITER_REVERSE: ** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | ** ** FTS5_SEGITER_ONETERM: ** If set, set the iterator to point to EOF after the current doclist ** has been exhausted. Do not proceed to the next term in the segment. ** ** FTS5_SEGITER_REVERSE: ** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If ** it is set, iterate through rowid in descending order instead of the ** default ascending order. ** ** iRowidOffset/nRowidOffset/aRowidOffset: ** These are used if the FTS5_SEGITER_REVERSE flag is set. ** ** For each rowid on the page corresponding to the current term, the ** corresponding aRowidOffset[] entry is set to the byte offset of the |
︙ | ︙ | |||
2235 2236 2237 2238 2239 2240 2241 | } iOff += nNew; /* Skip past the doclist. If the end of the page is reached, bail out. */ while( 1 ){ int nPos; | | | 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 | } iOff += nNew; /* Skip past the doclist. If the end of the page is reached, bail out. */ while( 1 ){ int nPos; /* Skip past rowid delta */ fts5IndexSkipVarint(a, iOff); /* Skip past position list */ fts5IndexGetVarint32(a, iOff, nPos); iOff += (nPos >> 1); if( iOff>=(n-1) ){ iOff = n; |
︙ | ︙ | |||
2308 2309 2310 2311 2312 2313 2314 | Fts5Buffer *pBuf, /* Buffer to use for loading pages */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5StructureSegment *pSeg, /* Description of segment */ Fts5SegIter *pIter /* Object to populate */ ){ int iPg = 1; | < | 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 | Fts5Buffer *pBuf, /* Buffer to use for loading pages */ const u8 *pTerm, int nTerm, /* Term to seek to */ int flags, /* Mask of FTS5INDEX_XXX flags */ Fts5StructureSegment *pSeg, /* Description of segment */ Fts5SegIter *pIter /* Object to populate */ ){ int iPg = 1; int bGe = (flags & FTS5INDEX_QUERY_SCAN); int bDlidx = 0; /* True if there is a doclist-index */ static int nCall = 0; nCall++; assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 ); |
︙ | ︙ | |||
2335 2336 2337 2338 2339 2340 2341 | )); } if( p->rc ) return; sqlite3_bind_int(p->pIdxSelect, 1, pSeg->iSegid); sqlite3_bind_blob(p->pIdxSelect, 2, pTerm, nTerm, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(p->pIdxSelect) ){ i64 val = sqlite3_column_int(p->pIdxSelect, 0); | | | 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 | )); } if( p->rc ) return; sqlite3_bind_int(p->pIdxSelect, 1, pSeg->iSegid); sqlite3_bind_blob(p->pIdxSelect, 2, pTerm, nTerm, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(p->pIdxSelect) ){ i64 val = sqlite3_column_int(p->pIdxSelect, 0); iPg = (int)(val>>1); bDlidx = (val & 0x0001); } p->rc = sqlite3_reset(p->pIdxSelect); if( iPg<pSeg->pgnoFirst ){ iPg = pSeg->pgnoFirst; bDlidx = 0; |
︙ | ︙ | |||
3373 3374 3375 3376 3377 3378 3379 | int nTerm, const u8 *pTerm ){ int nPrefix; /* Bytes of prefix compression for term */ Fts5PageWriter *pPage = &pWriter->writer; assert( pPage->buf.n==0 || pPage->buf.n>4 ); if( pPage->buf.n==0 ){ | | | 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 | int nTerm, const u8 *pTerm ){ int nPrefix; /* Bytes of prefix compression for term */ Fts5PageWriter *pPage = &pWriter->writer; assert( pPage->buf.n==0 || pPage->buf.n>4 ); if( pPage->buf.n==0 ){ /* Zero the first term and first rowid fields */ static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 }; fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero); assert( pWriter->bFirstTermInPage ); } if( p->rc ) return; if( pWriter->bFirstTermInPage ){ |
︙ | ︙ | |||
3433 3434 3435 3436 3437 3438 3439 | /* If the current leaf page is full, flush it to disk. */ if( pPage->buf.n>=p->pConfig->pgsz ){ fts5WriteFlushLeaf(p, pWriter); } } /* | | | | | | 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 | /* If the current leaf page is full, flush it to disk. */ if( pPage->buf.n>=p->pConfig->pgsz ){ fts5WriteFlushLeaf(p, pWriter); } } /* ** Append a rowid and position-list size field to the writers output. */ static void fts5WriteAppendRowid( Fts5Index *p, Fts5SegWriter *pWriter, i64 iRowid, int nPos ){ if( p->rc==SQLITE_OK ){ Fts5PageWriter *pPage = &pWriter->writer; /* If this is to be the first rowid written to the page, set the ** rowid-pointer in the page-header. Also append a value to the dlidx ** buffer, in case a doclist-index is required. */ if( pWriter->bFirstRowidInPage ){ fts5PutU16(pPage->buf.p, pPage->buf.n); fts5WriteDlidxAppend(p, pWriter, iRowid); } /* Write the rowid. */ if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); }else{ assert( p->rc || iRowid>pWriter->iPrevRowid ); fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); } pWriter->iPrevRowid = iRowid; |
︙ | ︙ | |||
3940 3941 3942 3943 3944 3945 3946 | int nSuffix; /* Size of term suffix */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); nTerm = strlen(zTerm); /* Decide if the term will fit on the current leaf. If it will not, ** flush the leaf to disk here. */ | | | 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 | int nSuffix; /* Size of term suffix */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); nTerm = strlen(zTerm); /* Decide if the term will fit on the current leaf. If it will not, ** flush the leaf to disk here. */ if( pBuf->n>4 && (pBuf->n + nTerm + 2) > pgsz ){ fts5WriteFlushLeaf(p, &writer); pBuf = &writer.writer.buf; if( (nTerm + 32) > pBuf->nSpace ){ fts5BufferGrow(&p->rc, pBuf, nTerm + 32 - pBuf->n); if( p->rc ) break; } } |
︙ | ︙ | |||
3999 4000 4001 4002 4003 4004 4005 | int bDummy; iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta); nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); nCopy += nPos; iRowid += iDelta; if( writer.bFirstRowidInPage ){ | | | 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 | int bDummy; iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta); nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy); nCopy += nPos; iRowid += iDelta; if( writer.bFirstRowidInPage ){ fts5PutU16(&pBuf->p[0], pBuf->n); /* first rowid on page */ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); writer.bFirstRowidInPage = 0; fts5WriteDlidxAppend(p, &writer, iRowid); }else{ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); } assert( pBuf->n<=pBuf->nSpace ); |
︙ | ︙ | |||
4605 4606 4607 4608 4609 4610 4611 | } } return rc; } /* | | | 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 | } } return rc; } /* ** Open a new iterator to iterate though all rowid that match the ** specified token or token prefix. */ int sqlite3Fts5IndexQuery( Fts5Index *p, /* FTS index to query */ const char *pToken, int nToken, /* Token (or prefix) to query for */ int flags, /* Mask of FTS5INDEX_QUERY_X flags */ Fts5IndexIter **ppIter /* OUT: New iterator object */ |
︙ | ︙ | |||
5072 5073 5074 5075 5076 5077 5078 | )); /* Iterate through the b-tree hierarchy. */ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ i64 iRow; /* Rowid for this leaf */ Fts5Data *pLeaf; /* Data for this leaf */ int iOff; /* Offset of first term on leaf */ | < | 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 | )); /* Iterate through the b-tree hierarchy. */ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ i64 iRow; /* Rowid for this leaf */ Fts5Data *pLeaf; /* Data for this leaf */ int iOff; /* Offset of first term on leaf */ int nIdxTerm = sqlite3_column_bytes(pStmt, 1); const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1); int iIdxLeaf = sqlite3_column_int(pStmt, 2); int bIdxDlidx = sqlite3_column_int(pStmt, 3); /* If the leaf in question has already been trimmed from the segment, |
︙ | ︙ | |||
5124 5125 5126 5127 5128 5129 5130 | if( p->rc ) break; /* If there is a doclist-index, check that it looks right. */ if( bIdxDlidx ){ Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */ int iPrevLeaf = iIdxLeaf; int iSegid = pSeg->iSegid; | | | 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 | if( p->rc ) break; /* If there is a doclist-index, check that it looks right. */ if( bIdxDlidx ){ Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */ int iPrevLeaf = iIdxLeaf; int iSegid = pSeg->iSegid; int iPg = 0; i64 iKey; for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iIdxLeaf); fts5DlidxIterEof(p, pDlidx)==0; fts5DlidxIterNext(p, pDlidx) ){ |
︙ | ︙ |
Changes to ext/fts5/fts5_main.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | ** only true if it is guaranteed that the fts5 database is not corrupt. */ int sqlite3_fts5_may_be_corrupt = 1; typedef struct Fts5Table Fts5Table; typedef struct Fts5Cursor Fts5Cursor; | < | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** only true if it is guaranteed that the fts5 database is not corrupt. */ int sqlite3_fts5_may_be_corrupt = 1; typedef struct Fts5Table Fts5Table; typedef struct Fts5Cursor Fts5Cursor; typedef struct Fts5Auxiliary Fts5Auxiliary; typedef struct Fts5Auxdata Fts5Auxdata; typedef struct Fts5TokenizerModule Fts5TokenizerModule; /* ** NOTES ON TRANSACTIONS: |
︙ | ︙ | |||
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | ** ** If the cursor iterates in descending order of rowid, iFirstRowid ** is the upper limit (i.e. the "first" rowid visited) and iLastRowid ** the lower. */ struct Fts5Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ int ePlan; /* FTS5_PLAN_XXX value */ int bDesc; /* True for "ORDER BY rowid DESC" queries */ i64 iFirstRowid; /* Return no rowids earlier than this */ i64 iLastRowid; /* Return no rowids later than this */ sqlite3_stmt *pStmt; /* Statement used to read %_content */ Fts5Expr *pExpr; /* Expression for MATCH queries */ Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */ int csrflags; /* Mask of cursor flags (see below) */ | > > > > > < | < < > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | ** ** If the cursor iterates in descending order of rowid, iFirstRowid ** is the upper limit (i.e. the "first" rowid visited) and iLastRowid ** the lower. */ struct Fts5Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */ int *aColumnSize; /* Values for xColumnSize() */ i64 iCsrId; /* Cursor id */ /* Zero from this point onwards on cursor reset */ int ePlan; /* FTS5_PLAN_XXX value */ int bDesc; /* True for "ORDER BY rowid DESC" queries */ i64 iFirstRowid; /* Return no rowids earlier than this */ i64 iLastRowid; /* Return no rowids later than this */ sqlite3_stmt *pStmt; /* Statement used to read %_content */ Fts5Expr *pExpr; /* Expression for MATCH queries */ Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */ int csrflags; /* Mask of cursor flags (see below) */ i64 iSpecial; /* Result of special query */ /* "rank" function. Populated on demand from vtab.xColumn(). */ char *zRank; /* Custom rank function */ char *zRankArgs; /* Custom rank function args */ Fts5Auxiliary *pRank; /* Rank callback (or NULL) */ int nRankArg; /* Number of trailing arguments for rank() */ sqlite3_value **apRankArg; /* Array of trailing arguments */ sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */ /* Auxiliary data storage */ Fts5Auxiliary *pAux; /* Currently executing extension function */ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */ /* Cache used by auxiliary functions xInst() and xInstCount() */ Fts5PoslistReader *aInstIter; /* One for each phrase */ int nInstAlloc; /* Size of aInst[] array (entries / 3) */ int nInstCount; /* Number of phrase instances */ int *aInst; /* 3 integers per phrase instance */ }; /* ** Bits that make up the "idxNum" parameter passed indirectly by ** xBestIndex() to xFilter(). |
︙ | ︙ | |||
426 427 428 429 430 431 432 | ){ return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); } /* ** The different query plans. */ | | | | | | | | 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 | ){ return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); } /* ** The different query plans. */ #define FTS5_PLAN_MATCH 1 /* (<tbl> MATCH ?) */ #define FTS5_PLAN_SOURCE 2 /* A source cursor for SORTED_MATCH */ #define FTS5_PLAN_SPECIAL 3 /* An internal query */ #define FTS5_PLAN_SORTED_MATCH 4 /* (<tbl> MATCH ? ORDER BY rank) */ #define FTS5_PLAN_SCAN 5 /* No usable constraint */ #define FTS5_PLAN_ROWID 6 /* (rowid = ?) */ /* ** Implementation of the xBestIndex method for FTS5 tables. Within the ** WHERE constraint, it searches for the following: ** ** 1. A MATCH constraint against the special column. ** 2. A MATCH constraint against the "rank" column. |
︙ | ︙ | |||
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE | FTS5CSR_REQUIRE_INST ); } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ if( pCursor ){ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; Fts5Cursor **pp; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | static void fts5CsrNewrow(Fts5Cursor *pCsr){ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_CONTENT | FTS5CSR_REQUIRE_DOCSIZE | FTS5CSR_REQUIRE_INST ); } static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); Fts5Auxdata *pData; Fts5Auxdata *pNext; sqlite3_free(pCsr->aInstIter); sqlite3_free(pCsr->aInst); if( pCsr->pStmt ){ int eStmt = fts5StmtType(pCsr); sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt); } if( pCsr->pSorter ){ Fts5Sorter *pSorter = pCsr->pSorter; sqlite3_finalize(pSorter->pStmt); sqlite3_free(pSorter); } if( pCsr->ePlan!=FTS5_PLAN_SOURCE ){ sqlite3Fts5ExprFree(pCsr->pExpr); } for(pData=pCsr->pAuxdata; pData; pData=pNext){ pNext = pData->pNext; if( pData->xDelete ) pData->xDelete(pData->pPtr); sqlite3_free(pData); } sqlite3_finalize(pCsr->pRankArgStmt); sqlite3_free(pCsr->apRankArg); if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){ sqlite3_free(pCsr->zRank); sqlite3_free(pCsr->zRankArgs); } memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr)); } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ if( pCursor ){ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; Fts5Cursor **pp; fts5FreeCursorComponents(pCsr); /* Remove the cursor from the Fts5Global.pCsr list */ for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext); *pp = pCsr->pNext; sqlite3_free(pCsr); } return SQLITE_OK; } static int fts5SorterNext(Fts5Cursor *pCsr){ Fts5Sorter *pSorter = pCsr->pSorter; |
︙ | ︙ | |||
754 755 756 757 758 759 760 | ** even if we reach end-of-file. The fts5EofMethod() will be called ** subsequently to determine whether or not an EOF was hit. */ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; | | | | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | ** even if we reach end-of-file. The fts5EofMethod() will be called ** subsequently to determine whether or not an EOF was hit. */ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; assert( (pCsr->ePlan<3)== (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE) ); if( pCsr->ePlan<3 ){ int bSkip = 0; if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc; rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid); if( sqlite3Fts5ExprEof(pCsr->pExpr) ){ CsrFlagSet(pCsr, FTS5CSR_EOF); } fts5CsrNewrow(pCsr); |
︙ | ︙ | |||
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 | int bOrderByRank; /* True if ORDER BY rank */ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ char **pzErrmsg = pConfig->pzErrmsg; assert( pCsr->pStmt==0 ); assert( pCsr->pExpr==0 ); assert( pCsr->csrflags==0 ); assert( pCsr->pRank==0 ); assert( pCsr->zRank==0 ); assert( pCsr->zRankArgs==0 ); | > > > > > | 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 | int bOrderByRank; /* True if ORDER BY rank */ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */ char **pzErrmsg = pConfig->pzErrmsg; if( pCsr->ePlan ){ fts5FreeCursorComponents(pCsr); memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr)); } assert( pCsr->pStmt==0 ); assert( pCsr->pExpr==0 ); assert( pCsr->csrflags==0 ); assert( pCsr->pRank==0 ); assert( pCsr->zRank==0 ); assert( pCsr->zRankArgs==0 ); |
︙ | ︙ | |||
1170 1171 1172 1173 1174 1175 1176 | return sqlite3Fts5ExprRowid(pCsr->pExpr); } } /* ** This is the xRowid method. The SQLite core calls this routine to ** retrieve the rowid for the current row of the result set. fts5 | | | 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 | return sqlite3Fts5ExprRowid(pCsr->pExpr); } } /* ** This is the xRowid method. The SQLite core calls this routine to ** retrieve the rowid for the current row of the result set. fts5 ** exposes %_content.rowid as the rowid for the virtual table. The ** rowid should be written to *pRowid. */ static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int ePlan = pCsr->ePlan; assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 ); |
︙ | ︙ | |||
1513 1514 1515 1516 1517 1518 1519 | /* ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated ** correctly for the current view. Return SQLITE_OK if successful, or an ** SQLite error code otherwise. */ static int fts5CacheInstArray(Fts5Cursor *pCsr){ int rc = SQLITE_OK; | < | | < | | > | | > > > | < | | | | | | | | | | | | | | | | | | < | > | > > > | > > > > > > | > > | | | | | | < < | < | < | > | | > | > | 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 | /* ** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated ** correctly for the current view. Return SQLITE_OK if successful, or an ** SQLite error code otherwise. */ static int fts5CacheInstArray(Fts5Cursor *pCsr){ int rc = SQLITE_OK; Fts5PoslistReader *aIter; /* One iterator for each phrase */ int nIter; /* Number of iterators/phrases */ nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); if( pCsr->aInstIter==0 ){ int nByte = sizeof(Fts5PoslistReader) * nIter; pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); } aIter = pCsr->aInstIter; if( aIter ){ int nInst = 0; /* Number instances seen so far */ int i; /* Initialize all iterators */ for(i=0; i<nIter; i++){ const u8 *a; int n = fts5CsrPoslist(pCsr, i, &a); sqlite3Fts5PoslistReaderInit(-1, a, n, &aIter[i]); } while( 1 ){ int *aInst; int iBest = -1; for(i=0; i<nIter; i++){ if( (aIter[i].bEof==0) && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos) ){ iBest = i; } } if( iBest<0 ) break; nInst++; if( nInst>=pCsr->nInstAlloc ){ pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; aInst = (int*)sqlite3_realloc( pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 ); if( aInst ){ pCsr->aInst = aInst; }else{ rc = SQLITE_NOMEM; break; } } aInst = &pCsr->aInst[3 * (nInst-1)]; aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); sqlite3Fts5PoslistReaderNext(&aIter[iBest]); } pCsr->nInstCount = nInst; CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST); } return rc; } static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ *pnInst = pCsr->nInstCount; } return rc; } static int fts5ApiInst( Fts5Context *pCtx, int iIdx, int *piPhrase, int *piCol, int *piOff ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0 || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){ if( iIdx<0 || iIdx>=pCsr->nInstCount ){ rc = SQLITE_RANGE; }else{ *piPhrase = pCsr->aInst[iIdx*3]; *piCol = pCsr->aInst[iIdx*3 + 1]; *piOff = pCsr->aInst[iIdx*3 + 2]; } |
︙ | ︙ | |||
1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 | pData->pPtr = 0; pData->xDelete = 0; } } return pRet; } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 | pData->pPtr = 0; pData->xDelete = 0; } } return pRet; } static void fts5ApiPhraseNext( Fts5Context *pCtx, Fts5PhraseIter *pIter, int *piCol, int *piOff ){ if( pIter->a>=pIter->b ){ *piCol = -1; *piOff = -1; }else{ int iVal; pIter->a += fts5GetVarint32(pIter->a, iVal); if( iVal==1 ){ pIter->a += fts5GetVarint32(pIter->a, iVal); *piCol = iVal; *piOff = 0; pIter->a += fts5GetVarint32(pIter->a, iVal); } *piOff += (iVal-2); } } static void fts5ApiPhraseFirst( Fts5Context *pCtx, int iPhrase, Fts5PhraseIter *pIter, int *piCol, int *piOff ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; int n = fts5CsrPoslist(pCsr, iPhrase, &pIter->a); pIter->b = &pIter->a[n]; *piCol = 0; *piOff = 0; fts5ApiPhraseNext(pCtx, pIter, piCol, piOff); } static int fts5ApiQueryPhrase(Fts5Context*, int, void*, int(*)(const Fts5ExtensionApi*, Fts5Context*, void*) ); static const Fts5ExtensionApi sFts5Api = { 2, /* iVersion */ fts5ApiUserData, fts5ApiColumnCount, fts5ApiRowCount, fts5ApiColumnTotalSize, fts5ApiTokenize, fts5ApiPhraseCount, fts5ApiPhraseSize, fts5ApiInstCount, fts5ApiInst, fts5ApiRowid, fts5ApiColumnText, fts5ApiColumnSize, fts5ApiQueryPhrase, fts5ApiSetAuxdata, fts5ApiGetAuxdata, fts5ApiPhraseFirst, fts5ApiPhraseNext, }; /* ** Implementation of API function xQueryPhrase(). */ static int fts5ApiQueryPhrase( |
︙ | ︙ |
Changes to ext/fts5/fts5_tcl.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #ifdef SQLITE_ENABLE_FTS5 #include "fts5.h" #include <string.h> #include <assert.h> extern int sqlite3_fts5_may_be_corrupt; /************************************************************************* ** This is a copy of the first part of the SqliteDb structure in ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine ** can extract the sqlite3* pointer from an existing Tcl SQLite ** connection. */ | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #ifdef SQLITE_ENABLE_FTS5 #include "fts5.h" #include <string.h> #include <assert.h> extern int sqlite3_fts5_may_be_corrupt; extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *); /************************************************************************* ** This is a copy of the first part of the SqliteDb structure in ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine ** can extract the sqlite3* pointer from an existing Tcl SQLite ** connection. */ |
︙ | ︙ | |||
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | } z = Tcl_GetStringFromObj(objv[2], &n); iVal = f5t_fts5HashKey(nSlot, z, n); Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); return TCL_OK; } /* ** Entry point. */ int Fts5tcl_Init(Tcl_Interp *interp){ static struct Cmd { char *zName; Tcl_ObjCmdProc *xProc; int bTokenizeCtx; } aCmd[] = { | > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | > | 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 | } z = Tcl_GetStringFromObj(objv[2], &n); iVal = f5t_fts5HashKey(nSlot, z, n); Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); return TCL_OK; } static int f5tRegisterMatchinfo( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite3 *db = 0; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( f5tDbPointer(interp, objv[1], &db) ){ return TCL_ERROR; } rc = sqlite3Fts5TestRegisterMatchinfo(db); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char*)sqlite3ErrName(rc), TCL_VOLATILE); return TCL_ERROR; } return TCL_OK; } /* ** Entry point. */ int Fts5tcl_Init(Tcl_Interp *interp){ static struct Cmd { char *zName; Tcl_ObjCmdProc *xProc; int bTokenizeCtx; } aCmd[] = { { "sqlite3_fts5_create_tokenizer", f5tCreateTokenizer, 1 }, { "sqlite3_fts5_token", f5tTokenizerReturn, 1 }, { "sqlite3_fts5_tokenize", f5tTokenize, 0 }, { "sqlite3_fts5_create_function", f5tCreateFunction, 0 }, { "sqlite3_fts5_may_be_corrupt", f5tMayBeCorrupt, 0 }, { "sqlite3_fts5_token_hash", f5tTokenHash, 0 }, { "sqlite3_fts5_register_matchinfo", f5tRegisterMatchinfo, 0 } }; int i; F5tTokenizerContext *pContext; pContext = (F5tTokenizerContext*)ckalloc(sizeof(F5tTokenizerContext)); memset(pContext, 0, sizeof(*pContext)); |
︙ | ︙ |
Added ext/fts5/fts5_test_mi.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | /* ** 2015 Aug 04 ** ** 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. ** ****************************************************************************** ** ** This file contains test code only, it is not included in release ** versions of FTS5. It contains the implementation of an FTS5 auxiliary ** function very similar to the FTS4 function matchinfo(): ** ** https://www.sqlite.org/fts3.html#matchinfo ** ** Known differences are that: ** ** 1) this function uses the FTS5 definition of "matchable phrase", which ** excludes any phrases that are part of an expression sub-tree that ** does not match the current row. This comes up for MATCH queries ** such as: ** ** "a OR (b AND c)" ** ** In FTS4, if a single row contains instances of tokens "a" and "c", ** but not "b", all instances of "c" are considered matches. In FTS5, ** they are not (as the "b AND c" sub-tree does not match the current ** row. ** ** 2) For the values returned by 'x' that apply to all rows of the table, ** NEAR constraints are not considered. But for the number of hits in ** the current row, they are. ** ** This file exports a single function that may be called to register the ** matchinfo() implementation with a database handle: ** ** int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db); */ #ifdef SQLITE_TEST #ifdef SQLITE_ENABLE_FTS5 #include "fts5.h" #include <tcl.h> #include <assert.h> #include <string.h> typedef struct Fts5MatchinfoCtx Fts5MatchinfoCtx; typedef unsigned int u32; struct Fts5MatchinfoCtx { int nCol; /* Number of cols in FTS5 table */ int nPhrase; /* Number of phrases in FTS5 query */ char *zArg; /* nul-term'd copy of 2nd arg */ int nRet; /* Number of elements in aRet[] */ u32 *aRet; /* Array of 32-bit unsigned ints to return */ }; /* ** Return a pointer to the fts5_api pointer for database connection db. ** If an error occurs, return NULL and leave an error in the database ** handle (accessible using sqlite3_errcode()/errmsg()). */ static fts5_api *fts5_api_from_db(sqlite3 *db){ fts5_api *pRet = 0; sqlite3_stmt *pStmt = 0; if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5()", -1, &pStmt, 0) && SQLITE_ROW==sqlite3_step(pStmt) && sizeof(pRet)==sqlite3_column_bytes(pStmt, 0) ){ memcpy(&pRet, sqlite3_column_blob(pStmt, 0), sizeof(pRet)); } sqlite3_finalize(pStmt); return pRet; } /* ** Argument f should be a flag accepted by matchinfo() (a valid character ** in the string passed as the second argument). If it is not, -1 is ** returned. Otherwise, if f is a valid matchinfo flag, the value returned ** is the number of 32-bit integers added to the output array if the ** table has nCol columns and the query nPhrase phrases. */ static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){ int ret = -1; switch( f ){ case 'p': ret = 1; break; case 'c': ret = 1; break; case 'x': ret = 3 * nCol * nPhrase; break; case 'y': ret = nCol * nPhrase; break; case 'b': ret = ((nCol + 31) / 32) * nPhrase; break; case 'n': ret = 1; break; case 'a': ret = nCol; break; case 'l': ret = nCol; break; case 's': ret = nCol; break; } return ret; } static int fts5MatchinfoIter( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ Fts5MatchinfoCtx *p, int(*x)(const Fts5ExtensionApi*,Fts5Context*,Fts5MatchinfoCtx*,char,u32*) ){ int i; int n = 0; int rc = SQLITE_OK; char f; for(i=0; (f = p->zArg[i]); i++){ rc = x(pApi, pFts, p, f, &p->aRet[n]); if( rc!=SQLITE_OK ) break; n += fts5MatchinfoFlagsize(p->nCol, p->nPhrase, f); } return rc; } static int fts5MatchinfoXCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, void *pUserData ){ Fts5PhraseIter iter; int iCol, iOff; u32 *aOut = (u32*)pUserData; int iPrev = -1; for(pApi->xPhraseFirst(pFts, 0, &iter, &iCol, &iOff); iOff>=0; pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ){ aOut[iCol*3+1]++; if( iCol!=iPrev ) aOut[iCol*3 + 2]++; iPrev = iCol; } return SQLITE_OK; } static int fts5MatchinfoGlobalCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, Fts5MatchinfoCtx *p, char f, u32 *aOut ){ int rc = SQLITE_OK; switch( f ){ case 'p': aOut[0] = p->nPhrase; break; case 'c': aOut[0] = p->nCol; break; case 'x': { int i; for(i=0; i<p->nPhrase && rc==SQLITE_OK; i++){ void *pPtr = (void*)&aOut[i * p->nCol * 3]; rc = pApi->xQueryPhrase(pFts, i, pPtr, fts5MatchinfoXCb); } break; } case 'n': { sqlite3_int64 nRow; rc = pApi->xRowCount(pFts, &nRow); aOut[0] = (u32)nRow; break; } case 'a': { sqlite3_int64 nRow = 0; rc = pApi->xRowCount(pFts, &nRow); if( nRow==0 ){ memset(aOut, 0, sizeof(u32) * p->nCol); }else{ int i; for(i=0; rc==SQLITE_OK && i<p->nCol; i++){ sqlite3_int64 nToken; rc = pApi->xColumnTotalSize(pFts, i, &nToken); if( rc==SQLITE_OK){ aOut[i] = (u32)((2*nToken + nRow) / (2*nRow)); } } } break; } } return rc; } static int fts5MatchinfoLocalCb( const Fts5ExtensionApi *pApi, Fts5Context *pFts, Fts5MatchinfoCtx *p, char f, u32 *aOut ){ int i; int rc = SQLITE_OK; switch( f ){ case 'b': case 'x': case 'y': { int nMul = (f=='x' ? 3 : 1); int iPhrase; if( f=='b' ){ int nInt = ((p->nCol + 31) / 32) * p->nPhrase; for(i=0; i<nInt; i++) aOut[i] = 0; }else{ for(i=0; i<(p->nCol*p->nPhrase); i++) aOut[i*nMul] = 0; } for(iPhrase=0; iPhrase<p->nPhrase; iPhrase++){ Fts5PhraseIter iter; int iOff, iCol; for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff); iOff>=0; pApi->xPhraseNext(pFts, &iter, &iCol, &iOff) ){ if( f=='b' ){ aOut[iPhrase * ((p->nCol+31)/32) + iCol/32] |= ((u32)1 << iCol%32); }else{ aOut[nMul * (iCol + iPhrase * p->nCol)]++; } } } break; } case 'l': { for(i=0; rc==SQLITE_OK && i<p->nCol; i++){ int nToken; rc = pApi->xColumnSize(pFts, i, &nToken); aOut[i] = (u32)nToken; } break; } case 's': { int nInst; memset(aOut, 0, sizeof(u32) * p->nCol); rc = pApi->xInstCount(pFts, &nInst); for(i=0; rc==SQLITE_OK && i<nInst; i++){ int iPhrase, iOff, iCol = 0; int iNextPhrase; int iNextOff; u32 nSeq = 1; int j; rc = pApi->xInst(pFts, i, &iPhrase, &iCol, &iOff); iNextPhrase = iPhrase+1; iNextOff = iOff+pApi->xPhraseSize(pFts, 0); for(j=i+1; rc==SQLITE_OK && j<nInst; j++){ int ip, ic, io; rc = pApi->xInst(pFts, j, &ip, &ic, &io); if( ic!=iCol || io>iNextOff ) break; if( ip==iNextPhrase && io==iNextOff ){ nSeq++; iNextPhrase = ip+1; iNextOff = io + pApi->xPhraseSize(pFts, ip); } } if( nSeq>aOut[iCol] ) aOut[iCol] = nSeq; } break; } } return rc; } static Fts5MatchinfoCtx *fts5MatchinfoNew( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning error message */ const char *zArg /* Matchinfo flag string */ ){ Fts5MatchinfoCtx *p; int nCol; int nPhrase; int i; int nInt; int nByte; int rc; nCol = pApi->xColumnCount(pFts); nPhrase = pApi->xPhraseCount(pFts); nInt = 0; for(i=0; zArg[i]; i++){ int n = fts5MatchinfoFlagsize(nCol, nPhrase, zArg[i]); if( n<0 ){ char *zErr = sqlite3_mprintf("unrecognized matchinfo flag: %c", zArg[i]); sqlite3_result_error(pCtx, zErr, -1); sqlite3_free(zErr); return 0; } nInt += n; } nByte = sizeof(Fts5MatchinfoCtx) /* The struct itself */ + sizeof(u32) * nInt /* The p->aRet[] array */ + (i+1); /* The p->zArg string */ p = (Fts5MatchinfoCtx*)sqlite3_malloc(nByte); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; } memset(p, 0, nByte); p->nCol = nCol; p->nPhrase = nPhrase; p->aRet = (u32*)&p[1]; p->nRet = nInt; p->zArg = (char*)&p->aRet[nInt]; memcpy(p->zArg, zArg, i); rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoGlobalCb); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(p); p = 0; } return p; } static void fts5MatchinfoFunc( const Fts5ExtensionApi *pApi, /* API offered by current FTS version */ Fts5Context *pFts, /* First arg to pass to pApi functions */ sqlite3_context *pCtx, /* Context for returning result/error */ int nVal, /* Number of values in apVal[] array */ sqlite3_value **apVal /* Array of trailing arguments */ ){ const char *zArg; Fts5MatchinfoCtx *p; int rc; if( nVal>0 ){ zArg = (const char*)sqlite3_value_text(apVal[0]); }else{ zArg = "pcx"; } p = (Fts5MatchinfoCtx*)pApi->xGetAuxdata(pFts, 0); if( p==0 || sqlite3_stricmp(zArg, p->zArg) ){ p = fts5MatchinfoNew(pApi, pFts, pCtx, zArg); pApi->xSetAuxdata(pFts, p, sqlite3_free); if( p==0 ) return; } rc = fts5MatchinfoIter(pApi, pFts, p, fts5MatchinfoLocalCb); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); }else{ /* No errors has occured, so return a copy of the array of integers. */ int nByte = p->nRet * sizeof(u32); sqlite3_result_blob(pCtx, (void*)p->aRet, nByte, SQLITE_TRANSIENT); } } int sqlite3Fts5TestRegisterMatchinfo(sqlite3 *db){ int rc; /* Return code */ fts5_api *pApi; /* FTS5 API functions */ /* Extract the FTS5 API pointer from the database handle. The ** fts5_api_from_db() function above is copied verbatim from the ** FTS5 documentation. Refer there for details. */ pApi = fts5_api_from_db(db); /* If fts5_api_from_db() returns NULL, then either FTS5 is not registered ** with this database handle, or an error (OOM perhaps?) has occurred. ** ** Also check that the fts5_api object is version 2 or newer. */ if( pApi==0 || pApi->iVersion<1 ){ return SQLITE_ERROR; } /* Register the implementation of matchinfo() */ rc = pApi->xCreateFunction(pApi, "matchinfo", 0, fts5MatchinfoFunc, 0); return rc; } #endif /* SQLITE_ENABLE_FTS5 */ #endif /* SQLITE_TEST */ |
Changes to ext/fts5/fts5_tokenize.c.
︙ | ︙ | |||
533 534 535 536 537 538 539 | if( pRet ){ memset(pRet, 0, sizeof(PorterTokenizer)); rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer); }else{ rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ | > > | | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | if( pRet ){ memset(pRet, 0, sizeof(PorterTokenizer)); rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer); }else{ rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ int nArg2 = (nArg>0 ? nArg-1 : 0); const char **azArg2 = (nArg2 ? &azArg[1] : 0); rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer); } if( rc!=SQLITE_OK ){ fts5PorterDelete((Fts5Tokenizer*)pRet); pRet = 0; } *ppOut = (Fts5Tokenizer*)pRet; |
︙ | ︙ |
Changes to ext/fts5/fts5_vocab.c.
︙ | ︙ | |||
438 439 440 441 442 443 444 | break; } return SQLITE_OK; } /* ** This is the xRowid method. The SQLite core calls this routine to | | < | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | break; } return SQLITE_OK; } /* ** This is the xRowid method. The SQLite core calls this routine to ** retrieve the rowid for the current row of the result set. The ** rowid should be written to *pRowid. */ static int fts5VocabRowidMethod( sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid ){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; |
︙ | ︙ |
Changes to ext/fts5/test/fts5_common.tcl.
︙ | ︙ | |||
88 89 90 91 92 93 94 95 96 97 98 99 100 101 | set cnt [list] for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } $cmd xQueryPhrase $i [list test_queryphrase_cb cnt] lappend res $cnt } set res } proc fts5_test_all {cmd} { set res [list] lappend res columnsize [fts5_test_columnsize $cmd] lappend res columntext [fts5_test_columntext $cmd] lappend res columntotalsize [fts5_test_columntotalsize $cmd] lappend res poslist [fts5_test_poslist $cmd] | > > > > | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | set cnt [list] for {set j 0} {$j < [$cmd xColumnCount]} {incr j} { lappend cnt 0 } $cmd xQueryPhrase $i [list test_queryphrase_cb cnt] lappend res $cnt } set res } proc fts5_test_phrasecount {cmd} { $cmd xPhraseCount } proc fts5_test_all {cmd} { set res [list] lappend res columnsize [fts5_test_columnsize $cmd] lappend res columntext [fts5_test_columntext $cmd] lappend res columntotalsize [fts5_test_columntotalsize $cmd] lappend res poslist [fts5_test_poslist $cmd] |
︙ | ︙ | |||
111 112 113 114 115 116 117 118 119 120 121 122 123 124 | fts5_test_columntotalsize fts5_test_poslist fts5_test_tokenize fts5_test_rowcount fts5_test_all fts5_test_queryphrase } { sqlite3_fts5_create_function $db $f $f } } proc fts5_level_segs {tbl} { set sql "SELECT fts5_decode(rowid,block) aS r FROM ${tbl}_data WHERE rowid=10" | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | fts5_test_columntotalsize fts5_test_poslist fts5_test_tokenize fts5_test_rowcount fts5_test_all fts5_test_queryphrase fts5_test_phrasecount } { sqlite3_fts5_create_function $db $f $f } } proc fts5_level_segs {tbl} { set sql "SELECT fts5_decode(rowid,block) aS r FROM ${tbl}_data WHERE rowid=10" |
︙ | ︙ |
Changes to ext/fts5/test/fts5ae.test.
︙ | ︙ | |||
272 273 274 275 276 277 278 279 280 281 | SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY +rank; } $res do_execsql_test 8.2.$tn.3 { SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY rank; } $res } finish_test | > > > > > > > > > > > > > > > > > > > > > > > | 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY +rank; } $res do_execsql_test 8.2.$tn.3 { SELECT rowid FROM t8 WHERE t8 MATCH $q ORDER BY rank; } $res } #------------------------------------------------------------------------- # Test xPhraseCount() for some different queries. # do_test 9.1 { execsql { CREATE VIRTUAL TABLE t9 USING fts5(x) } foreach x { "a b c" "d e f" } { execsql { INSERT INTO t9 VALUES($x) } } } {} foreach {tn q cnt} { 1 {a AND b} 2 2 {a OR b} 2 3 {a OR b OR c} 3 4 {NEAR(a b)} 2 } { do_execsql_test 9.2.$tn { SELECT fts5_test_phrasecount(t9) FROM t9 WHERE t9 MATCH $q LIMIT 1 } $cnt } finish_test |
Changes to ext/fts5/test/fts5eb.test.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 | 10 {abc + . + def} {"abc" + "def"} 11 {abc . def} {"abc" AND "def"} 12 {r+e OR w} {"r" + "e" OR "w"} } { do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res] } finish_test | > > > > > > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 10 {abc + . + def} {"abc" + "def"} 11 {abc . def} {"abc" AND "def"} 12 {r+e OR w} {"r" + "e" OR "w"} } { do_execsql_test 1.$tn {SELECT fts5_expr($expr)} [list $res] } do_catchsql_test 2.1 { SELECT fts5_expr() } {1 {wrong number of arguments to function fts5_expr}} do_catchsql_test 2.1 { SELECT fts5_expr_tcl() } {1 {wrong number of arguments to function fts5_expr_tcl}} finish_test |
Added ext/fts5/test/fts5matchinfo.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 | # 2015 August 05 # # 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. # #*********************************************************************** # source [file join [file dirname [info script]] fts5_common.tcl] set testprefix fts5matchinfo # If SQLITE_ENABLE_FTS5 is not defined, omit this file. ifcapable !fts5 { finish_test ; return } proc mit {blob} { set scan(littleEndian) i* set scan(bigEndian) I* binary scan $blob $scan($::tcl_platform(byteOrder)) r return $r } db func mit mit sqlite3_fts5_register_matchinfo db do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts5(content); } do_execsql_test 1.1 { INSERT INTO t1(content) VALUES('I wandered lonely as a cloud'); INSERT INTO t1(content) VALUES('That floats on high o''er vales and hills,'); INSERT INTO t1(content) VALUES('When all at once I saw a crowd,'); INSERT INTO t1(content) VALUES('A host, of golden daffodils,'); SELECT mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'I'; } {{1 1 1 2 2} {1 1 1 2 2}} # Now create an FTS4 table that does not specify matchinfo=fts3. # do_execsql_test 1.2 { CREATE VIRTUAL TABLE t2 USING fts5(content); INSERT INTO t2 SELECT * FROM t1; SELECT mit(matchinfo(t2)) FROM t2 WHERE t2 MATCH 'I'; } {{1 1 1 2 2} {1 1 1 2 2}} #-------------------------------------------------------------------------- # Proc [do_matchinfo_test] is used to test the FTSX matchinfo() function. # # The first argument - $tn - is a test identifier. This may be either a # full identifier (i.e. "fts3matchinfo-1.1") or, if global var $testprefix # is set, just the numeric component (i.e. "1.1"). # # The second argument is the name of an FTSX table. The third is the # full text of a WHERE/MATCH expression to query the table for # (i.e. "t1 MATCH 'abc'"). The final argument - $results - should be a # key-value list (serialized array) with matchinfo() format specifiers # as keys, and the results of executing the statement: # # SELECT matchinfo($tbl, '$key') FROM $tbl WHERE $expr # # For example: # # CREATE VIRTUAL TABLE t1 USING fts4; # INSERT INTO t1 VALUES('abc'); # INSERT INTO t1 VALUES('def'); # INSERT INTO t1 VALUES('abc abc'); # # do_matchinfo_test 1.1 t1 "t1 MATCH 'abc'" { # n {3 3} # p {1 1} # c {1 1} # x {{1 3 2} {2 3 2}} # } # # If the $results list contains keys mapped to "-" instead of a matchinfo() # result, then this command computes the expected results based on other # mappings to test the matchinfo() function. For example, the command above # could be changed to: # # do_matchinfo_test 1.1 t1 "t1 MATCH 'abc'" { # n {3 3} p {1 1} c {1 1} x {{1 3 2} {2 3 2}} # pcx - # } # # And this command would compute the expected results for matchinfo(t1, 'pcx') # based on the results of matchinfo(t1, 'p'), matchinfo(t1, 'c') and # matchinfo(t1, 'x') in order to test 'pcx'. # proc do_matchinfo_test {tn tbl expr results} { foreach {fmt res} $results { if {$res == "-"} continue set resarray($fmt) $res } set nRow 0 foreach {fmt res} [array get resarray] { if {[llength $res]>$nRow} { set nRow [llength $res] } } # Construct expected results for any formats for which the caller # supplied result is "-". # foreach {fmt res} $results { if {$res == "-"} { set res [list] for {set iRow 0} {$iRow<$nRow} {incr iRow} { set rowres [list] foreach c [split $fmt ""] { set rowres [concat $rowres [lindex $resarray($c) $iRow]] } lappend res $rowres } set resarray($fmt) $res } } # Test each matchinfo() request individually. # foreach {fmt res} [array get resarray] { set sql "SELECT mit(matchinfo($tbl, '$fmt')) FROM $tbl WHERE $expr" do_execsql_test $tn.$fmt $sql [normalize2 $res] } # Test them all executed together (multiple invocations of matchinfo()). # set exprlist [list] foreach {format res} [array get resarray] { lappend exprlist "mit(matchinfo($tbl, '$format'))" } set allres [list] for {set iRow 0} {$iRow<$nRow} {incr iRow} { foreach {format res} [array get resarray] { lappend allres [lindex $res $iRow] } } set sql "SELECT [join $exprlist ,] FROM $tbl WHERE $expr" do_execsql_test $tn.multi $sql [normalize2 $allres] } proc normalize2 {list_of_lists} { set res [list] foreach elem $list_of_lists { lappend res [list {*}$elem] } return $res } do_execsql_test 4.1.0 { CREATE VIRTUAL TABLE t4 USING fts5(x, y); INSERT INTO t4 VALUES('a b c d e', 'f g h i j'); INSERT INTO t4 VALUES('f g h i j', 'a b c d e'); } do_matchinfo_test 4.1.1 t4 {t4 MATCH 'a b c'} { s {{3 0} {0 3}} } do_matchinfo_test 4.1.1 t4 {t4 MATCH 'a b c'} { p {3 3} x { {1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1} {0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1} } } do_matchinfo_test 4.1.1 t4 {t4 MATCH 'a b c'} { p {3 3} c {2 2} x { {1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1} {0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1} } n {2 2} l {{5 5} {5 5}} a {{5 5} {5 5}} s {{3 0} {0 3}} xxxxxxxxxxxxxxxxxx - pcx - xpc - ccc - pppxpcpcx - laxnpc - xpxsscplax - } do_matchinfo_test 4.1.2 t4 {t4 MATCH '"g h i"'} { p {1 1} c {2 2} x { {0 1 1 1 1 1} {1 1 1 0 1 1} } n {2 2} l {{5 5} {5 5}} a {{5 5} {5 5}} s {{0 1} {1 0}} xxxxxxxxxxxxxxxxxx - pcx - xpc - ccc - pppxpcpcx - laxnpc - sxsxs - } do_matchinfo_test 4.1.3 t4 {t4 MATCH 'a b'} { s {{2 0} {0 2}} } do_matchinfo_test 4.1.4 t4 {t4 MATCH '"a b" c'} { s {{2 0} {0 2}} } do_matchinfo_test 4.1.5 t4 {t4 MATCH 'a "b c"'} { s {{2 0} {0 2}} } do_matchinfo_test 4.1.6 t4 {t4 MATCH 'd d'} { s {{1 0} {0 1}} } do_matchinfo_test 4.1.7 t4 {t4 MATCH 'f OR abcd'} { x { {0 1 1 1 1 1 0 0 0 0 0 0} {1 1 1 0 1 1 0 0 0 0 0 0} } } do_matchinfo_test 4.1.8 t4 {t4 MATCH 'f NOT abcd'} { x { {0 1 1 1 1 1 0 0 0 0 0 0} {1 1 1 0 1 1 0 0 0 0 0 0} } } do_execsql_test 4.2.0 { CREATE VIRTUAL TABLE t5 USING fts5(content); INSERT INTO t5 VALUES('a a a a a'); INSERT INTO t5 VALUES('a b a b a'); INSERT INTO t5 VALUES('c b c b c'); INSERT INTO t5 VALUES('x x x x x'); } do_matchinfo_test 4.2.1 t5 {t5 MATCH 'a a'} { x {{5 8 2 5 8 2} {3 8 2 3 8 2}} s {2 1} } do_matchinfo_test 4.2.2 t5 {t5 MATCH 'a b'} { s {2} } do_matchinfo_test 4.2.3 t5 {t5 MATCH 'a b a'} { s {3} } do_matchinfo_test 4.2.4 t5 {t5 MATCH 'a a a'} { s {3 1} } do_matchinfo_test 4.2.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} } do_matchinfo_test 4.2.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1} } do_execsql_test 4.3.0 "INSERT INTO t5 VALUES('x y [string repeat {b } 50000]')"; # It used to be that the second 'a' token would be deferred. That doesn't # work any longer. if 0 { do_matchinfo_test 4.3.1 t5 {t5 MATCH 'a a'} { x {{5 8 2 5 5 5} {3 8 2 3 5 5}} s {2 1} } } do_matchinfo_test 4.3.2 t5 {t5 MATCH 'a b'} { s {2} } do_matchinfo_test 4.3.3 t5 {t5 MATCH 'a b a'} { s {3} } do_matchinfo_test 4.3.4 t5 {t5 MATCH 'a a a'} { s {3 1} } do_matchinfo_test 4.3.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} } do_matchinfo_test 4.3.6 t5 {t5 MATCH 'a OR b'} { s {1 2 1 1} } do_execsql_test 4.4.0.1 { INSERT INTO t5(t5) VALUES('optimize') } do_matchinfo_test 4.4.2 t5 {t5 MATCH 'a b'} { s {2} } do_matchinfo_test 4.4.1 t5 {t5 MATCH 'a a'} { s {2 1} } do_matchinfo_test 4.4.2 t5 {t5 MATCH 'a b'} { s {2} } do_matchinfo_test 4.4.3 t5 {t5 MATCH 'a b a'} { s {3} } do_matchinfo_test 4.4.4 t5 {t5 MATCH 'a a a'} { s {3 1} } do_matchinfo_test 4.4.5 t5 {t5 MATCH '"a b" "a b"'} { s {2} } do_execsql_test 4.5.0 { CREATE VIRTUAL TABLE t6 USING fts5(a, b, c); INSERT INTO t6 VALUES('a', 'b', 'c'); } do_matchinfo_test 4.5.1 t6 {t6 MATCH 'a b c'} { s {{1 1 1}} } #------------------------------------------------------------------------- # Test the outcome of matchinfo() when used within a query that does not # use the full-text index (i.e. lookup by rowid or full-table scan). # do_execsql_test 7.1 { CREATE VIRTUAL TABLE t10 USING fts5(content); INSERT INTO t10 VALUES('first record'); INSERT INTO t10 VALUES('second record'); } do_execsql_test 7.2 { SELECT typeof(matchinfo(t10)), length(matchinfo(t10)) FROM t10; } {blob 8 blob 8} do_execsql_test 7.3 { SELECT typeof(matchinfo(t10)), length(matchinfo(t10)) FROM t10 WHERE rowid=1; } {blob 8} do_execsql_test 7.4 { SELECT typeof(matchinfo(t10)), length(matchinfo(t10)) FROM t10 WHERE t10 MATCH 'record' } {blob 20 blob 20} #------------------------------------------------------------------------- # Test a special case - matchinfo('nxa') with many zero length documents. # Special because "x" internally uses a statement used by both "n" and "a". # This was causing a problem at one point in the obscure case where the # total number of bytes of data stored in an fts3 table was greater than # the number of rows. i.e. when the following query returns true: # # SELECT sum(length(content)) < count(*) FROM fts4table; # do_execsql_test 8.1 { CREATE VIRTUAL TABLE t11 USING fts5(content); INSERT INTO t11(t11, rank) VALUES('pgsz', 32); INSERT INTO t11 VALUES('quitealongstringoftext'); INSERT INTO t11 VALUES('anotherquitealongstringoftext'); INSERT INTO t11 VALUES('athirdlongstringoftext'); INSERT INTO t11 VALUES('andonemoreforgoodluck'); } do_test 8.2 { for {set i 0} {$i < 200} {incr i} { execsql { INSERT INTO t11 VALUES('') } } execsql { INSERT INTO t11(t11) VALUES('optimize') } } {} do_execsql_test 8.3 { SELECT mit(matchinfo(t11, 'nxa')) FROM t11 WHERE t11 MATCH 'a*' } {{204 1 3 3 0} {204 1 3 3 0} {204 1 3 3 0}} #------------------------------------------------------------------------- do_execsql_test 9.1 { CREATE VIRTUAL TABLE t12 USING fts5(content); INSERT INTO t12 VALUES('a b c d'); SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a'; } {{0 1 1 0 1 1 1 1 1}} do_execsql_test 9.2 { INSERT INTO t12 VALUES('a d c d'); SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a'; } { {0 2 2 0 3 2 1 2 2} {1 2 2 1 3 2 1 2 2} } do_execsql_test 9.3 { INSERT INTO t12 VALUES('a d d a'); SELECT mit(matchinfo(t12, 'x')) FROM t12 WHERE t12 MATCH 'NEAR(a d, 1) OR a'; } { {0 4 3 0 5 3 1 4 3} {1 4 3 1 5 3 1 4 3} {2 4 3 2 5 3 2 4 3} } #--------------------------------------------------------------------------- # Test for a memory leak # do_execsql_test 10.1 { DROP TABLE t10; CREATE VIRTUAL TABLE t10 USING fts5(idx, value); INSERT INTO t10 values (1, 'one'),(2, 'two'),(3, 'three'); SELECT t10.rowid, t10.* FROM t10 JOIN (SELECT 1 AS idx UNION SELECT 2 UNION SELECT 3) AS x WHERE t10 MATCH x.idx AND matchinfo(t10) not null GROUP BY t10.rowid ORDER BY 1; } {1 1 one 2 2 two 3 3 three} #--------------------------------------------------------------------------- # Test the 'y' matchinfo flag # set sqlite_fts3_enable_parentheses 1 reset_db do_execsql_test 11.0 { CREATE VIRTUAL TABLE tt USING fts3(x, y); INSERT INTO tt VALUES('c d a c d d', 'e a g b d a'); -- 1 INSERT INTO tt VALUES('c c g a e b', 'c g d g e c'); -- 2 INSERT INTO tt VALUES('b e f d e g', 'b a c b c g'); -- 3 INSERT INTO tt VALUES('a c f f g d', 'd b f d e g'); -- 4 INSERT INTO tt VALUES('g a c f c f', 'd g g b c c'); -- 5 INSERT INTO tt VALUES('g a c e b b', 'd b f b g g'); -- 6 INSERT INTO tt VALUES('f d a a f c', 'e e a d c f'); -- 7 INSERT INTO tt VALUES('a c b b g f', 'a b a e d f'); -- 8 INSERT INTO tt VALUES('b a f e c c', 'f d b b a b'); -- 9 INSERT INTO tt VALUES('f d c e a c', 'f a f a a f'); -- 10 } db func mit mit foreach {tn expr res} { 1 "a" { 1 {1 2} 2 {1 0} 3 {0 1} 4 {1 0} 5 {1 0} 6 {1 0} 7 {2 1} 8 {1 2} 9 {1 1} 10 {1 3} } 2 "b" { 1 {0 1} 2 {1 0} 3 {1 2} 4 {0 1} 5 {0 1} 6 {2 2} 8 {2 1} 9 {1 3} } 3 "y:a" { 1 {0 2} 3 {0 1} 7 {0 1} 8 {0 2} 9 {0 1} 10 {0 3} } 4 "x:a" { 1 {1 0} 2 {1 0} 4 {1 0} 5 {1 0} 6 {1 0} 7 {2 0} 8 {1 0} 9 {1 0} 10 {1 0} } 5 "a OR b" { 1 {1 2 0 1} 2 {1 0 1 0} 3 {0 1 1 2} 4 {1 0 0 1} 5 {1 0 0 1} 6 {1 0 2 2} 7 {2 1 0 0} 8 {1 2 2 1} 9 {1 1 1 3} 10 {1 3 0 0} } 6 "a AND b" { 1 {1 2 0 1} 2 {1 0 1 0} 3 {0 1 1 2} 4 {1 0 0 1} 5 {1 0 0 1} 6 {1 0 2 2} 8 {1 2 2 1} 9 {1 1 1 3} } 7 "a OR (a AND b)" { 1 {1 2 1 2 0 1} 2 {1 0 1 0 1 0} 3 {0 1 0 1 1 2} 4 {1 0 1 0 0 1} 5 {1 0 1 0 0 1} 6 {1 0 1 0 2 2} 7 {2 1 0 0 0 0} 8 {1 2 1 2 2 1} 9 {1 1 1 1 1 3} 10 {1 3 0 0 0 0} } } { do_execsql_test 11.1.$tn.1 { SELECT rowid, mit(matchinfo(tt, 'y')) FROM tt WHERE tt MATCH $expr } $res set r2 [list] foreach {rowid L} $res { lappend r2 $rowid set M [list] foreach {a b} $L { lappend M [expr ($a ? 1 : 0) + ($b ? 2 : 0)] } lappend r2 $M } do_execsql_test 11.1.$tn.2 { SELECT rowid, mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH $expr } $r2 do_execsql_test 11.1.$tn.2 { SELECT rowid, mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH $expr } $r2 } set sqlite_fts3_enable_parentheses 0 #--------------------------------------------------------------------------- # Test the 'b' matchinfo flag # set sqlite_fts3_enable_parentheses 1 reset_db db func mit mit do_test 12.0 { set cols [list] for {set i 0} {$i < 50} {incr i} { lappend cols "c$i" } execsql "CREATE VIRTUAL TABLE tt USING fts3([join $cols ,])" } {} do_execsql_test 12.1 { INSERT INTO tt (rowid, c4, c45) VALUES(1, 'abc', 'abc'); SELECT mit(matchinfo(tt, 'b')) FROM tt WHERE tt MATCH 'abc'; } [list [list [expr 1<<4] [expr 1<<(45-32)]]] set sqlite_fts3_enable_parentheses 0 finish_test |
Changes to ext/fts5/test/fts5tokenizer.test.
︙ | ︙ | |||
242 243 244 245 246 247 248 249 250 251 | INSERT INTO e6 VALUES('\u0E08\u0E07\u0E09'); CREATE VIRTUAL TABLE e7 USING fts5vocab(e6, 'row'); SELECT term FROM e7; ROLLBACK; }] [subst { brown dog fox jumped lazy over quick the \u0E08 \u0E09 }] finish_test | > > > > > > > > > > > > > > > | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | INSERT INTO e6 VALUES('\u0E08\u0E07\u0E09'); CREATE VIRTUAL TABLE e7 USING fts5vocab(e6, 'row'); SELECT term FROM e7; ROLLBACK; }] [subst { brown dog fox jumped lazy over quick the \u0E08 \u0E09 }] # Test that the porter tokenizer correctly passes arguments through to # its parent tokenizer. do_execsql_test 8.3 { BEGIN; CREATE VIRTUAL TABLE e6 USING fts5(x, tokenize="porter unicode61 separators ABCDEFGHIJKLMNOPQRSTUVWXYZ" ); INSERT INTO e6 VALUES('theAquickBbrownCfoxDjumpedWoverXtheYlazyZdog'); CREATE VIRTUAL TABLE e7 USING fts5vocab(e6, 'row'); SELECT term FROM e7; ROLLBACK; } { brown dog fox jump lazi over quick the } finish_test |
Added ext/misc/series.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | /* ** 2015-08-18 ** ** 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. ** ************************************************************************* ** ** This file demonstrates how to create a table-valued-function using ** a virtual table. This demo implements the generate_series() function ** which gives similar results to the eponymous function in PostgreSQL. ** Examples: ** ** SELECT * FROM generate_series(0,100,5); ** ** The query above returns integers from 0 through 100 counting by steps ** of 5. ** ** SELECT * FROM generate_series(0,100); ** ** Integers from 0 through 100 with a step size of 1. ** ** SELECT * FROM generate_series(20) LIMIT 10; ** ** Integers 20 through 29. ** ** HOW IT WORKS ** ** The generate_series "function" is really a virtual table with the ** following schema: ** ** CREATE FUNCTION generate_series( ** value, ** start HIDDEN, ** stop HIDDEN, ** step HIDDEN ** ); ** ** Function arguments in queries against this virtual table are translated ** into equality constraints against successive hidden columns. In other ** words, the following pairs of queries are equivalent to each other: ** ** SELECT * FROM generate_series(0,100,5); ** SELECT * FROM generate_series WHERE start=0 AND stop=100 AND step=5; ** ** SELECT * FROM generate_series(0,100); ** SELECT * FROM generate_series WHERE start=0 AND stop=100; ** ** SELECT * FROM generate_series(20) LIMIT 10; ** SELECT * FROM generate_series WHERE start=20 LIMIT 10; ** ** The generate_series virtual table implementation leaves the xCreate method ** set to NULL. This means that it is not possible to do a CREATE VIRTUAL ** TABLE command with "generate_series" as the USING argument. Instead, there ** is a single generate_series virtual table that is always available without ** having to be created first. ** ** The xBestIndex method looks for equality constraints against the hidden ** start, stop, and step columns, and if present, it uses those constraints ** to bound the sequence of generated values. If the equality constraints ** are missing, it uses 0 for start, 4294967295 for stop, and 1 for step. ** xBestIndex returns a small cost when both start and stop are available, ** and a very large cost if either start or stop are unavailable. This ** encourages the query planner to order joins such that the bounds of the ** series are well-defined. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* series_cursor is a subclass of sqlite3_vtab_cursor which will ** serve as the underlying representation of a cursor that scans ** over rows of the result */ typedef struct series_cursor series_cursor; struct series_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ int isDesc; /* True to count down rather than up */ sqlite3_int64 iRowid; /* The rowid */ sqlite3_int64 iValue; /* Current value ("value") */ sqlite3_int64 mnValue; /* Mimimum value ("start") */ sqlite3_int64 mxValue; /* Maximum value ("stop") */ sqlite3_int64 iStep; /* Increment ("step") */ }; /* ** The seriesConnect() method is invoked to create a new ** series_vtab that describes the generate_series virtual table. ** ** Think of this routine as the constructor for series_vtab objects. ** ** All this routine needs to do is: ** ** (1) Allocate the series_vtab object and initialize all fields. ** ** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the ** result set of queries against generate_series will look like. */ static int seriesConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ sqlite3_vtab *pNew; int rc; /* Column numbers */ #define SERIES_COLUMN_VALUE 0 #define SERIES_COLUMN_START 1 #define SERIES_COLUMN_STOP 2 #define SERIES_COLUMN_STEP 3 rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value,start hidden,stop hidden,step hidden)"); if( rc==SQLITE_OK ){ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(*pNew)); } return rc; } /* ** This method is the destructor for series_cursor objects. */ static int seriesDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* ** Constructor for a new series_cursor object. */ static int seriesOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ series_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Destructor for a series_cursor. */ static int seriesClose(sqlite3_vtab_cursor *cur){ sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a series_cursor to its next row of output. */ static int seriesNext(sqlite3_vtab_cursor *cur){ series_cursor *pCur = (series_cursor*)cur; if( pCur->isDesc ){ pCur->iValue -= pCur->iStep; }else{ pCur->iValue += pCur->iStep; } pCur->iRowid++; return SQLITE_OK; } /* ** Return values of columns for the row at which the series_cursor ** is currently pointing. */ static int seriesColumn( sqlite3_vtab_cursor *cur, /* The cursor */ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ series_cursor *pCur = (series_cursor*)cur; sqlite3_int64 x = 0; switch( i ){ case SERIES_COLUMN_START: x = pCur->mnValue; break; case SERIES_COLUMN_STOP: x = pCur->mxValue; break; case SERIES_COLUMN_STEP: x = pCur->iStep; break; default: x = pCur->iValue; break; } sqlite3_result_int64(ctx, x); return SQLITE_OK; } /* ** Return the rowid for the current row. In this implementation, the ** rowid is the same as the output value. */ static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ series_cursor *pCur = (series_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** Return TRUE if the cursor has been moved off of the last ** row of output. */ static int seriesEof(sqlite3_vtab_cursor *cur){ series_cursor *pCur = (series_cursor*)cur; if( pCur->isDesc ){ return pCur->iValue < pCur->mnValue; }else{ return pCur->iValue > pCur->mxValue; } } /* ** This method is called to "rewind" the series_cursor object back ** to the first row of output. This method is always called at least ** once prior to any call to seriesColumn() or seriesRowid() or ** seriesEof(). ** ** The query plan selected by seriesBestIndex is passed in the idxNum ** parameter. (idxStr is not used in this implementation.) idxNum ** is a bitmask showing which constraints are available: ** ** 1: start=VALUE ** 2: stop=VALUE ** 4: step=VALUE ** ** Also, if bit 8 is set, that means that the series should be output ** in descending order rather than in ascending order. ** ** This routine should initialize the cursor and position it so that it ** is pointing at the first row, or pointing off the end of the table ** (so that seriesEof() will return true) if the table is empty. */ static int seriesFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ series_cursor *pCur = (series_cursor *)pVtabCursor; int i = 0; if( idxNum & 1 ){ pCur->mnValue = sqlite3_value_int64(argv[i++]); }else{ pCur->mnValue = 0; } if( idxNum & 2 ){ pCur->mxValue = sqlite3_value_int64(argv[i++]); }else{ pCur->mxValue = 0xffffffff; } if( idxNum & 4 ){ pCur->iStep = sqlite3_value_int64(argv[i++]); if( pCur->iStep<1 ) pCur->iStep = 1; }else{ pCur->iStep = 1; } if( idxNum & 8 ){ pCur->isDesc = 1; pCur->iValue = pCur->mxValue; if( pCur->iStep>0 ){ pCur->iValue -= (pCur->mxValue - pCur->mnValue)%pCur->iStep; } }else{ pCur->isDesc = 0; pCur->iValue = pCur->mnValue; } pCur->iRowid = 1; return SQLITE_OK; } /* ** SQLite will invoke this method one or more times while planning a query ** that uses the generate_series virtual table. This routine needs to create ** a query plan for each invocation and compute an estimated cost for that ** plan. ** ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** ** The query plan is represented by bits in idxNum: ** ** (1) start = $value -- constraint exists ** (2) stop = $value -- constraint exists ** (4) step = $value -- constraint exists ** (8) output in descending order */ static int seriesBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ int idxNum = 0; /* The query plan bitmask */ int startIdx = -1; /* Index of the start= constraint, or -1 if none */ int stopIdx = -1; /* Index of the stop= constraint, or -1 if none */ int stepIdx = -1; /* Index of the step= constraint, or -1 if none */ int nArg = 0; /* Number of arguments that seriesFilter() expects */ const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; switch( pConstraint->iColumn ){ case SERIES_COLUMN_START: startIdx = i; idxNum |= 1; break; case SERIES_COLUMN_STOP: stopIdx = i; idxNum |= 2; break; case SERIES_COLUMN_STEP: stepIdx = i; idxNum |= 4; break; } } if( startIdx>=0 ){ pIdxInfo->aConstraintUsage[startIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[startIdx].omit = 1; } if( stopIdx>=0 ){ pIdxInfo->aConstraintUsage[stopIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[stopIdx].omit = 1; } if( stepIdx>=0 ){ pIdxInfo->aConstraintUsage[stepIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[stepIdx].omit = 1; } if( pIdxInfo->nOrderBy==1 ){ if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8; pIdxInfo->orderByConsumed = 1; } if( (idxNum & 3)==3 ){ /* Both start= and stop= boundaries are available. This is the ** the preferred case */ pIdxInfo->estimatedCost = (double)1; }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ pIdxInfo->estimatedCost = (double)2000000000; } pIdxInfo->idxNum = idxNum; return SQLITE_OK; } /* ** This following structure defines all the methods for the ** generate_series virtual table. */ static sqlite3_module seriesModule = { 0, /* iVersion */ 0, /* xCreate */ seriesConnect, /* xConnect */ seriesBestIndex, /* xBestIndex */ seriesDisconnect, /* xDisconnect */ 0, /* xDestroy */ seriesOpen, /* xOpen - open a cursor */ seriesClose, /* xClose - close a cursor */ seriesFilter, /* xFilter - configure scan constraints */ seriesNext, /* xNext - advance a cursor */ seriesEof, /* xEof - check for end of scan */ seriesColumn, /* xColumn - read data */ seriesRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_series_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc = SQLITE_OK; SQLITE_EXTENSION_INIT2(pApi); #ifndef SQLITE_OMIT_VIRTUALTABLE if( sqlite3_libversion_number()<3008012 ){ *pzErrMsg = sqlite3_mprintf( "generate_series() requires SQLite 3.8.12 or later"); return SQLITE_ERROR; } rc = sqlite3_create_module(db, "generate_series", &seriesModule, 0); #endif return rc; } |
Changes to ext/misc/spellfix.c.
︙ | ︙ | |||
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 | }; /* Fuzzy-search cursor object */ struct spellfix1_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ char *zPattern; /* rhs of MATCH clause */ int nRow; /* Number of rows of content */ int nAlloc; /* Number of allocated rows */ int iRow; /* Current row of content */ int iLang; /* Value of the langid= constraint */ int iTop; /* Value of the top= constraint */ int iScope; /* Value of the scope= constraint */ int nSearch; /* Number of vocabulary items checked */ | > | 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 | }; /* Fuzzy-search cursor object */ struct spellfix1_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ char *zPattern; /* rhs of MATCH clause */ int idxNum; /* idxNum value passed to xFilter() */ int nRow; /* Number of rows of content */ int nAlloc; /* Number of allocated rows */ int iRow; /* Current row of content */ int iLang; /* Value of the langid= constraint */ int iTop; /* Value of the top= constraint */ int iScope; /* Value of the scope= constraint */ int nSearch; /* Number of vocabulary items checked */ |
︙ | ︙ | |||
2036 2037 2038 2039 2040 2041 2042 | spellfix1ResetCursor(pCur); spellfix1ResizeCursor(pCur, 0); sqlite3_free(pCur->zPattern); sqlite3_free(pCur); return SQLITE_OK; } | < < < | | | | | | > > | > | | < < < < < < < | | | | | | | | | | > > > > | | | | | | | | | | 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | spellfix1ResetCursor(pCur); spellfix1ResizeCursor(pCur, 0); sqlite3_free(pCur->zPattern); sqlite3_free(pCur); return SQLITE_OK; } #define SPELLFIX_IDXNUM_MATCH 0x01 /* word MATCH $str */ #define SPELLFIX_IDXNUM_LANGID 0x02 /* langid == $langid */ #define SPELLFIX_IDXNUM_TOP 0x04 /* top = $top */ #define SPELLFIX_IDXNUM_SCOPE 0x08 /* scope = $scope */ #define SPELLFIX_IDXNUM_DISTLT 0x10 /* distance < $distance */ #define SPELLFIX_IDXNUM_DISTLE 0x20 /* distance <= $distance */ #define SPELLFIX_IDXNUM_ROWID 0x40 /* rowid = $rowid */ #define SPELLFIX_IDXNUM_DIST (0x10|0x20) /* DISTLT and DISTLE */ /* ** ** The plan number is a bitmask of the SPELLFIX_IDXNUM_* values defined ** above. ** ** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid ** if specified and in that order. */ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int iPlan = 0; int iLangTerm = -1; int iTopTerm = -1; int iScopeTerm = -1; int iDistTerm = -1; int iRowidTerm = -1; int i; const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; /* Terms of the form: word MATCH $str */ if( (iPlan & SPELLFIX_IDXNUM_MATCH)==0 && pConstraint->iColumn==SPELLFIX_COL_WORD && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ iPlan |= SPELLFIX_IDXNUM_MATCH; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; } /* Terms of the form: langid = $langid */ if( (iPlan & SPELLFIX_IDXNUM_LANGID)==0 && pConstraint->iColumn==SPELLFIX_COL_LANGID && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ iPlan |= SPELLFIX_IDXNUM_LANGID; iLangTerm = i; } /* Terms of the form: top = $top */ if( (iPlan & SPELLFIX_IDXNUM_TOP)==0 && pConstraint->iColumn==SPELLFIX_COL_TOP && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ iPlan |= SPELLFIX_IDXNUM_TOP; iTopTerm = i; } /* Terms of the form: scope = $scope */ if( (iPlan & SPELLFIX_IDXNUM_SCOPE)==0 && pConstraint->iColumn==SPELLFIX_COL_SCOPE && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ iPlan |= SPELLFIX_IDXNUM_SCOPE; iScopeTerm = i; } /* Terms of the form: distance < $dist or distance <= $dist */ if( (iPlan & SPELLFIX_IDXNUM_DIST)==0 && pConstraint->iColumn==SPELLFIX_COL_DISTANCE && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) ){ if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ iPlan |= SPELLFIX_IDXNUM_DISTLT; }else{ iPlan |= SPELLFIX_IDXNUM_DISTLE; } iDistTerm = i; } /* Terms of the form: distance < $dist or distance <= $dist */ if( (iPlan & SPELLFIX_IDXNUM_ROWID)==0 && pConstraint->iColumn<0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ iPlan |= SPELLFIX_IDXNUM_ROWID; iRowidTerm = i; } } if( iPlan&SPELLFIX_IDXNUM_MATCH ){ int idx = 2; pIdxInfo->idxNum = iPlan; if( pIdxInfo->nOrderBy==1 && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE && pIdxInfo->aOrderBy[0].desc==0 ){ pIdxInfo->orderByConsumed = 1; /* Default order by iScore */ } if( iPlan&SPELLFIX_IDXNUM_LANGID ){ pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++; pIdxInfo->aConstraintUsage[iLangTerm].omit = 1; } if( iPlan&SPELLFIX_IDXNUM_TOP ){ pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++; pIdxInfo->aConstraintUsage[iTopTerm].omit = 1; } if( iPlan&SPELLFIX_IDXNUM_SCOPE ){ pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++; pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1; } if( iPlan&SPELLFIX_IDXNUM_DIST ){ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++; pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; } pIdxInfo->estimatedCost = 1e5; }else if( (iPlan & SPELLFIX_IDXNUM_ROWID) ){ pIdxInfo->idxNum = SPELLFIX_IDXNUM_ROWID; pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; pIdxInfo->estimatedCost = 5; }else{ pIdxInfo->idxNum = 0; pIdxInfo->estimatedCost = 1e50; } |
︙ | ︙ | |||
2307 2308 2309 2310 2311 2312 2313 | iDist = editdist1(p->zPattern, zK1, 0); } if( iDist<0 ){ p->rc = SQLITE_NOMEM; break; } pCur->nSearch++; | | > > > > > > > | > | > | > | 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 | iDist = editdist1(p->zPattern, zK1, 0); } if( iDist<0 ){ p->rc = SQLITE_NOMEM; break; } pCur->nSearch++; /* If there is a "distance < $dist" or "distance <= $dist" constraint, ** check if this row meets it. If not, jump back up to the top of the ** loop to process the next row. Otherwise, if the row does match the ** distance constraint, check if the pCur->a[] array is already full. ** If it is and no explicit "top = ?" constraint was present in the ** query, grow the array to ensure there is room for the new entry. */ assert( (p->iMaxDist>=0)==((pCur->idxNum & SPELLFIX_IDXNUM_DIST) ? 1 : 0) ); if( p->iMaxDist>=0 ){ if( iDist>p->iMaxDist ) continue; if( pCur->nRow>=pCur->nAlloc && (pCur->idxNum & SPELLFIX_IDXNUM_TOP)==0 ){ spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10); if( pCur->a==0 ) break; } } iScore = spellfix1Score(iDist,iRank); if( pCur->nRow<pCur->nAlloc ){ idx = pCur->nRow; }else if( iScore<iWorst ){ idx = idxWorst; sqlite3_free(pCur->a[idx].zWord); }else{ continue; } pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); if( pCur->a[idx].zWord==0 ){ p->rc = SQLITE_NOMEM; break; } pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0); pCur->a[idx].iRank = iRank; |
︙ | ︙ | |||
2357 2358 2359 2360 2361 2362 2363 | /* ** This version of the xFilter method work if the MATCH term is present ** and we are doing a scan. */ static int spellfix1FilterForMatch( spellfix1_cursor *pCur, | < > | 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 | /* ** This version of the xFilter method work if the MATCH term is present ** and we are doing a scan. */ static int spellfix1FilterForMatch( spellfix1_cursor *pCur, int argc, sqlite3_value **argv ){ int idxNum = pCur->idxNum; const unsigned char *zMatchThis; /* RHS of the MATCH operator */ EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */ char *zPattern; /* Transliteration of zMatchThis */ int nPattern; /* Length of zPattern */ int iLimit = 20; /* Max number of rows of output */ int iScope = 3; /* Use this many characters of zClass */ int iLang = 0; /* Language code */ |
︙ | ︙ | |||
2472 2473 2474 2475 2476 2477 2478 | } /* ** This version of xFilter handles a full-table scan case */ static int spellfix1FilterForFullScan( spellfix1_cursor *pCur, | < > | 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 | } /* ** This version of xFilter handles a full-table scan case */ static int spellfix1FilterForFullScan( spellfix1_cursor *pCur, int argc, sqlite3_value **argv ){ int rc = SQLITE_OK; int idxNum = pCur->idxNum; char *zSql; spellfix1_vtab *pVTab = pCur->pVTab; spellfix1ResetCursor(pCur); assert( idxNum==0 || idxNum==64 ); zSql = sqlite3_mprintf( "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s", pVTab->zDbName, pVTab->zTableName, |
︙ | ︙ | |||
2517 2518 2519 2520 2521 2522 2523 2524 | static int spellfix1Filter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ spellfix1_cursor *pCur = (spellfix1_cursor *)cur; int rc; if( idxNum & 1 ){ | > | | | 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 | static int spellfix1Filter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ spellfix1_cursor *pCur = (spellfix1_cursor *)cur; int rc; pCur->idxNum = idxNum; if( idxNum & 1 ){ rc = spellfix1FilterForMatch(pCur, argc, argv); }else{ rc = spellfix1FilterForFullScan(pCur, argc, argv); } return rc; } /* ** Advance a cursor to its next row of output |
︙ | ︙ |
Added ext/rbu/rbuB.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # 2014 August 30 # # 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. # #*********************************************************************** # # source [file join [file dirname [info script]] rbu_common.tcl] set ::testprefix rbuB db close sqlite3_shutdown test_sqlite3_log xLog reset_db proc xLog {args} { } set db_sql { CREATE TABLE t1(a PRIMARY KEY, b, c); } set rbu_sql { CREATE TABLE data_t1(a, b, c, rbu_control); INSERT INTO data_t1 VALUES(1, 2, 3, 0); INSERT INTO data_t1 VALUES(4, 5, 6, 0); INSERT INTO data_t1 VALUES(7, 8, 9, 0); } do_test 1.1 { forcedelete rbu.db sqlite3 rbu rbu.db rbu eval $rbu_sql rbu close db eval $db_sql } {} set ::errlog [list] proc xLog {err msg} { lappend ::errlog $err } do_test 1.2 { run_rbu test.db rbu.db } {SQLITE_DONE} do_test 1.3 { set ::errlog } {SQLITE_NOTICE_RECOVER_WAL SQLITE_INTERNAL} do_execsql_test 1.4 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8 9} db close sqlite3_shutdown test_sqlite3_log sqlite3_initialize finish_test |
Added ext/rbu/rbu_common.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | # 2015 Aug 8 # # 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. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl # Run the RBU in file $rbu on target database $target until completion. # proc run_rbu {target rbu} { sqlite3rbu rbu $target $rbu while 1 { set rc [rbu step] if {$rc!="SQLITE_OK"} break } rbu close } proc step_rbu {target rbu} { while 1 { sqlite3rbu rbu $target $rbu set rc [rbu step] rbu close if {$rc != "SQLITE_OK"} break } set rc } |
Added ext/rbu/rbudiff.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | # 2015-07-31 # # 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. # #*********************************************************************** # # Tests for the [sqldiff --rbu] command. # # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set testprefix rbudiff if {$tcl_platform(platform)=="windows"} { set PROG "sqldiff.exe" } else { set PROG "./sqldiff" } if {![file exe $PROG]} { puts "rbudiff.test cannot run because $PROG is not available" finish_test return } db close proc get_rbudiff_sql {db1 db2} { exec $::PROG --rbu $db1 $db2 } proc step_rbu {target rbu} { while 1 { sqlite3rbu rbu $target $rbu set rc [rbu step] rbu close if {$rc != "SQLITE_OK"} break } set rc } proc apply_rbudiff {sql target} { forcedelete rbu.db sqlite3 rbudb rbu.db rbudb eval $sql rbudb close step_rbu $target rbu.db } proc rbudiff_cksum {db1} { set txt "" sqlite3 dbtmp $db1 foreach tbl [dbtmp eval {SELECT name FROM sqlite_master WHERE type='table'}] { set cols [list] dbtmp eval "PRAGMA table_info = $tbl" { lappend cols "quote( $name )" } append txt [dbtmp eval \ "SELECT [join $cols {||'.'||}] FROM $tbl ORDER BY 1" ] } dbtmp close md5 $txt } foreach {tn init mod} { 1 { CREATE TABLE t1(a PRIMARY KEY, b, c); INSERT INTO t1 VALUES(1, 2, 3); INSERT INTO t1 VALUES(4, 5, 6); CREATE TABLE t2(a, b, c, PRIMARY KEY(b, c)); INSERT INTO t2 VALUES(1, 2, 3); INSERT INTO t2 VALUES(4, 5, 6); } { INSERT INTO t1 VALUES(7, 8, 9); DELETE FROM t1 WHERE a=4; UPDATE t1 SET c = 11 WHERE a = 1; INSERT INTO t2 VALUES(7, 8, 9); DELETE FROM t2 WHERE a=4; UPDATE t2 SET c = 11 WHERE a = 1; } 2 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b, c)); INSERT INTO t1 VALUES('u', 'v', 'w'); INSERT INTO t1 VALUES('x', 'y', 'z'); } { DELETE FROM t1 WHERE a='u'; INSERT INTO t1 VALUES('a', 'b', 'c'); } 3 { CREATE TABLE t1(i INTEGER PRIMARY KEY, x); INSERT INTO t1 VALUES(1, X'0000000000000000111111111111111122222222222222223333333333333333' ); CREATE TABLE t2(y INTEGER PRIMARY KEY, x); INSERT INTO t2 VALUES(1, X'0000000000000000111111111111111122222222222222223333333333333333' ); } { DELETE FROM t1; INSERT INTO t1 VALUES(1, X'0000000000000000111111111111111122222555555552223333333333333333' ); DELETE FROM t2; INSERT INTO t2 VALUES(1, X'0000000000000000111111111111111122222222222222223333333FFF333333' ); } } { catch { db close } forcedelete test.db test.db2 sqlite3 db test.db db eval "$init" sqlite3 db test.db2 db eval "$init ; $mod" db close do_test 1.$tn.2 { set sql [get_rbudiff_sql test.db test.db2] apply_rbudiff $sql test.db } {SQLITE_DONE} do_test 1.$tn.3 { rbudiff_cksum test.db } [rbudiff_cksum test.db2] forcedelete test.db test.db2 sqlite3 db test.db db eval "$init ; $mod" sqlite3 db test.db2 db eval "$init" db close do_test 1.$tn.4 { set sql [get_rbudiff_sql test.db test.db2] apply_rbudiff $sql test.db } {SQLITE_DONE} do_test 1.$tn.5 { rbudiff_cksum test.db } [rbudiff_cksum test.db2] } finish_test |
Added ext/rbu/rbufts.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | # 2014 August 30 # # 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. # #*********************************************************************** # # This file contains tests for the RBU module. More specifically, it # contains tests to ensure that RBU works with FTS tables. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set ::testprefix rbufts ifcapable !fts3 { finish_test return } proc step_rbu {target rbu} { while 1 { sqlite3rbu rbu $target $rbu set rc [rbu step] rbu close if {$rc != "SQLITE_OK"} break } set rc } proc apply_rbu_update {target sql} { forcedelete rbu.db sqlite3 dbrbu rbu.db execsql $sql dbrbu dbrbu close step_rbu $target rbu.db } do_execsql_test 1.1.0 { CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b); CREATE VIRTUAL TABLE xx USING fts4(content=t1, a, b); INSERT INTO t1(rowid, a, b) VALUES(10, 'a b c', 'c b a'); INSERT INTO t1(rowid, a, b) VALUES(20, 'a b c', 'd e f'); INSERT INTO t1(rowid, a, b) VALUES(30, 'd e f', 'a b c'); INSERT INTO t1(rowid, a, b) VALUES(40, 'd e f', 'd e f'); } do_execsql_test 1.1.1 { INSERT INTO xx(xx) VALUES('rebuild'); INSERT INTO xx(xx) VALUES('integrity-check'); } do_test 1.1.2 { apply_rbu_update test.db { CREATE TABLE data_t1(i, a, b, rbu_control); INSERT INTO data_t1 VALUES(20, NULL, NULL, 1); -- delete INSERT INTO data_t1 VALUES(30, 'x y z', NULL, '.x.'); -- update INSERT INTO data_t1 VALUES(50, '1 2 3', 'x y z', 0); -- insert CREATE VIEW data0_xx AS SELECT i AS rbu_rowid, a, b, CASE WHEN rbu_control IN (0, 1) THEN rbu_control ELSE substr(rbu_control, 2) END AS rbu_control FROM data_t1; } } {SQLITE_DONE} do_execsql_test 1.1.3 { INSERT INTO xx(xx) VALUES('integrity-check'); } reset_db do_execsql_test 1.2.1 { CREATE TABLE ccc(addr, text); CREATE VIRTUAL TABLE ccc_fts USING fts4(addr, text, content=ccc); INSERT INTO ccc VALUES('a b c', 'd e f'); INSERT INTO ccc VALUES('a b c', 'd e f'); INSERT INTO ccc_fts(ccc_fts) VALUES('rebuild'); INSERT INTO ccc_fts(ccc_fts) VALUES('integrity-check'); } do_test 1.2.2 { apply_rbu_update test.db { CREATE TABLE data_ccc(addr, text, rbu_rowid, rbu_control); CREATE VIEW data0_ccc_fts AS SELECT * FROM data_ccc; INSERT INTO data_ccc VALUES(NULL, NULL, 1, 1); INSERT INTO data_ccc VALUES('x y z', NULL, 2, 'x.'); INSERT INTO data_ccc VALUES('y y y', '1 1 1', 3, 0); } } {SQLITE_DONE} do_execsql_test 1.2.3 { INSERT INTO ccc_fts(ccc_fts) VALUES('integrity-check'); } do_execsql_test 1.2.4 { SELECT rowid, * FROM ccc_fts; } {2 {x y z} {d e f} 3 {y y y} {1 1 1}} #------------------------------------------------------------------------- # Test the outcome of attempting to delete or update a row within a # contentless FTS table using RBU. An error. # reset_db do_execsql_test 3.1 { CREATE VIRTUAL TABLE ft USING fts4(x, content=); INSERT INTO ft(rowid, x) VALUES(1, '1 2 3'); INSERT INTO ft(rowid, x) VALUES(2, '4 5 6'); } do_test 3.2 { list [catch { apply_rbu_update test.db { CREATE TABLE data_ft(x, rbu_rowid, rbu_control); INSERT INTO data_ft VALUES(NULL, 2, 1); } } msg] $msg] } {1 {SQLITE_ERROR - SQL logic error or missing database]}} do_test 3.3 { list [catch { apply_rbu_update test.db { CREATE TABLE data_ft(x, rbu_rowid, rbu_control); INSERT INTO data_ft VALUES('7 8 9', 1, 'x'); } } msg] $msg] } {1 {SQLITE_ERROR - SQL logic error or missing database]}} finish_test |
Added ext/rbu/rbusave.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | # 2015 August 14 # # 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. # #*********************************************************************** # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl set ::testprefix rbusave do_execsql_test 1.0 { CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID; CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b); CREATE INDEX i2 ON t2(c, b); INSERT INTO t1 VALUES(1, 1, 1); INSERT INTO t1 VALUES(2, 2, 2); INSERT INTO t1 VALUES(3, 3, 3); INSERT INTO t2 VALUES(1, 1, 1); INSERT INTO t2 VALUES(2, 2, 2); INSERT INTO t2 VALUES(3, 3, 3); } do_test 1.1 { forcedelete test.db2 sqlite3 db2 test.db2 db2 eval { CREATE TABLE data_t1(a, b, c, rbu_control); INSERT INTO data_t1 VALUES(4, 4, 4, 0); INSERT INTO data_t1 VALUES(2, NULL, NULL, 1); INSERT INTO data_t1 VALUES(1, 'one', NULL, '.x.'); CREATE TABLE data_t2(a, b, c, rbu_control); INSERT INTO data_t2 VALUES(4, 4, 4, 0); INSERT INTO data_t2 VALUES(2, NULL, NULL, 1); INSERT INTO data_t2 VALUES(1, 'one', NULL, '.x.'); } } {} proc test_to_bak {} { foreach f { test.db test.db-wal test.db-oal test.db-journal test.db2 test.db2-wal test.db2-oal test.db2-journal } { set t [string map {test bak} $f] forcedelete $t if {[file exists $f]} { forcecopy $f $t } } } do_test 1.2 { test_to_bak sqlite3rbu rrr bak.db bak.db2 set nStep 0 while {[rrr step]=="SQLITE_OK"} {incr nStep} set res2 [rrr close] } {SQLITE_DONE} sqlite3rbu rbu test.db test.db2 set res "SQLITE_OK" for {set i 1} {$res=="SQLITE_OK"} {incr i} { set res [rbu step] do_test 1.3.$i.1 { rbu savestate test_to_bak sqlite3rbu rrr bak.db bak.db2 set nRem 0 while {[rrr step]=="SQLITE_OK"} {incr nRem} set res2 [rrr close] } {SQLITE_DONE} do_test 1.3.$i.3 { expr $nRem+$i } [expr {$nStep + ($res=="SQLITE_DONE")}] do_test 1.3.$i.3 { sqlite3 bak bak.db bak eval { SELECT * FROM t1; SELECT * FROM t2; } } {1 one 1 3 3 3 4 4 4 1 one 1 3 3 3 4 4 4} bak close } do_test 1.4 { rbu close } {SQLITE_DONE} do_execsql_test 1.5 { SELECT * FROM t1; SELECT * FROM t2; } {1 one 1 3 3 3 4 4 4 1 one 1 3 3 3 4 4 4} finish_test |
Changes to ext/rbu/sqlite3rbu.c.
︙ | ︙ | |||
80 81 82 83 84 85 86 | ** */ #include <assert.h> #include <string.h> #include <stdio.h> | < < < < | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | ** */ #include <assert.h> #include <string.h> #include <stdio.h> #include "sqlite3.h" #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) #include "sqlite3rbu.h" /* Maximum number of prepared UPDATE statements held by this module */ #define SQLITE_RBU_UPDATE_CACHESIZE 16 |
︙ | ︙ | |||
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | u8 *abNotNull; /* Array of flags, set on NOT NULL columns */ u8 *abIndexed; /* Array of flags, set on indexed & PK cols */ int eType; /* Table type - an RBU_PK_XXX value */ /* Output variables. zTbl==0 implies EOF. */ int bCleanup; /* True in "cleanup" state */ const char *zTbl; /* Name of target db table */ const char *zIdx; /* Name of target db index (or null) */ int iTnum; /* Root page of current object */ int iPkTnum; /* If eType==EXTERNAL, root of PK index */ int bUnique; /* Current index is unique */ /* Statements created by rbuObjIterPrepareAll() */ int nCol; /* Number of columns in current object */ sqlite3_stmt *pSelect; /* Source data */ sqlite3_stmt *pInsert; /* Statement for INSERT operations */ sqlite3_stmt *pDelete; /* Statement for DELETE ops */ | > | | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | u8 *abNotNull; /* Array of flags, set on NOT NULL columns */ u8 *abIndexed; /* Array of flags, set on indexed & PK cols */ int eType; /* Table type - an RBU_PK_XXX value */ /* Output variables. zTbl==0 implies EOF. */ int bCleanup; /* True in "cleanup" state */ const char *zTbl; /* Name of target db table */ const char *zDataTbl; /* Name of rbu db table (or null) */ const char *zIdx; /* Name of target db index (or null) */ int iTnum; /* Root page of current object */ int iPkTnum; /* If eType==EXTERNAL, root of PK index */ int bUnique; /* Current index is unique */ /* Statements created by rbuObjIterPrepareAll() */ int nCol; /* Number of columns in current object */ sqlite3_stmt *pSelect; /* Source data */ sqlite3_stmt *pInsert; /* Statement for INSERT operations */ sqlite3_stmt *pDelete; /* Statement for DELETE ops */ sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */ /* Last UPDATE used (for PK b-tree updates only), or NULL. */ RbuUpdateStmt *pRbuUpdate; }; /* ** Values for RbuObjIter.eType |
︙ | ︙ | |||
355 356 357 358 359 360 361 362 363 364 365 366 367 368 | char *zDel; /* Delete this when closing file */ const char *zWal; /* Wal filename for this main db file */ rbu_file *pWalFd; /* Wal file descriptor for this main db */ rbu_file *pMainNext; /* Next MAIN_DB file */ }; /* ** Prepare the SQL statement in buffer zSql against database handle db. ** If successful, set *ppStmt to point to the new statement and return ** SQLITE_OK. ** ** Otherwise, if an error does occur, set *ppStmt to NULL and return | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 | char *zDel; /* Delete this when closing file */ const char *zWal; /* Wal filename for this main db file */ rbu_file *pWalFd; /* Wal file descriptor for this main db */ rbu_file *pMainNext; /* Next MAIN_DB file */ }; /************************************************************************* ** The following three functions, found below: ** ** rbuDeltaGetInt() ** rbuDeltaChecksum() ** rbuDeltaApply() ** ** are lifted from the fossil source code (http://fossil-scm.org). They ** are used to implement the scalar SQL function rbu_fossil_delta(). */ /* ** Read bytes from *pz and convert them into a positive integer. When ** finished, leave *pz pointing to the first character past the end of ** the integer. The *pLen parameter holds the length of the string ** in *pz and is decremented once for each character in the integer. */ static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){ static const signed char zValue[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36, -1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1, }; unsigned int v = 0; int c; unsigned char *z = (unsigned char*)*pz; unsigned char *zStart = z; while( (c = zValue[0x7f&*(z++)])>=0 ){ v = (v<<6) + c; } z--; *pLen -= z - zStart; *pz = (char*)z; return v; } /* ** Compute a 32-bit checksum on the N-byte buffer. Return the result. */ static unsigned int rbuDeltaChecksum(const char *zIn, size_t N){ const unsigned char *z = (const unsigned char *)zIn; unsigned sum0 = 0; unsigned sum1 = 0; unsigned sum2 = 0; unsigned sum3 = 0; while(N >= 16){ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]); z += 16; N -= 16; } while(N >= 4){ sum0 += z[0]; sum1 += z[1]; sum2 += z[2]; sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); case 2: sum3 += (z[1] << 16); case 1: sum3 += (z[0] << 24); default: ; } return sum3; } /* ** Apply a delta. ** ** The output buffer should be big enough to hold the whole output ** file and a NUL terminator at the end. The delta_output_size() ** routine will determine this size for you. ** ** The delta string should be null-terminated. But the delta string ** may contain embedded NUL characters (if the input and output are ** binary files) so we also have to pass in the length of the delta in ** the lenDelta parameter. ** ** This function returns the size of the output file in bytes (excluding ** the final NUL terminator character). Except, if the delta string is ** malformed or intended for use with a source file other than zSrc, ** then this routine returns -1. ** ** Refer to the delta_create() documentation above for a description ** of the delta file format. */ static int rbuDeltaApply( const char *zSrc, /* The source or pattern file */ int lenSrc, /* Length of the source file */ const char *zDelta, /* Delta to apply to the pattern */ int lenDelta, /* Length of the delta */ char *zOut /* Write the output into this preallocated buffer */ ){ unsigned int limit; unsigned int total = 0; #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST char *zOrigOut = zOut; #endif limit = rbuDeltaGetInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } zDelta++; lenDelta--; while( *zDelta && lenDelta>0 ){ unsigned int cnt, ofst; cnt = rbuDeltaGetInt(&zDelta, &lenDelta); switch( zDelta[0] ){ case '@': { zDelta++; lenDelta--; ofst = rbuDeltaGetInt(&zDelta, &lenDelta); if( lenDelta>0 && zDelta[0]!=',' ){ /* ERROR: copy command not terminated by ',' */ return -1; } zDelta++; lenDelta--; total += cnt; if( total>limit ){ /* ERROR: copy exceeds output file size */ return -1; } if( ofst+cnt > lenSrc ){ /* ERROR: copy extends past end of input */ return -1; } memcpy(zOut, &zSrc[ofst], cnt); zOut += cnt; break; } case ':': { zDelta++; lenDelta--; total += cnt; if( total>limit ){ /* ERROR: insert command gives an output larger than predicted */ return -1; } if( cnt>lenDelta ){ /* ERROR: insert count exceeds size of delta */ return -1; } memcpy(zOut, zDelta, cnt); zOut += cnt; zDelta += cnt; lenDelta -= cnt; break; } case ';': { zDelta++; lenDelta--; zOut[0] = 0; #ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST if( cnt!=rbuDeltaChecksum(zOrigOut, total) ){ /* ERROR: bad checksum */ return -1; } #endif if( total!=limit ){ /* ERROR: generated size does not match predicted size */ return -1; } return total; } default: { /* ERROR: unknown delta operator */ return -1; } } } /* ERROR: unterminated delta */ return -1; } static int rbuDeltaOutputSize(const char *zDelta, int lenDelta){ int size; size = rbuDeltaGetInt(&zDelta, &lenDelta); if( *zDelta!='\n' ){ /* ERROR: size integer not terminated by "\n" */ return -1; } return size; } /* ** End of code taken from fossil. *************************************************************************/ /* ** Implementation of SQL scalar function rbu_fossil_delta(). ** ** This function applies a fossil delta patch to a blob. Exactly two ** arguments must be passed to this function. The first is the blob to ** patch and the second the patch to apply. If no error occurs, this ** function returns the patched blob. */ static void rbuFossilDeltaFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *aDelta; int nDelta; const char *aOrig; int nOrig; int nOut; int nOut2; char *aOut; assert( argc==2 ); nOrig = sqlite3_value_bytes(argv[0]); aOrig = (const char*)sqlite3_value_blob(argv[0]); nDelta = sqlite3_value_bytes(argv[1]); aDelta = (const char*)sqlite3_value_blob(argv[1]); /* Figure out the size of the output */ nOut = rbuDeltaOutputSize(aDelta, nDelta); if( nOut<0 ){ sqlite3_result_error(context, "corrupt fossil delta", -1); return; } aOut = sqlite3_malloc(nOut+1); if( aOut==0 ){ sqlite3_result_error_nomem(context); }else{ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut); if( nOut2!=nOut ){ sqlite3_result_error(context, "corrupt fossil delta", -1); }else{ sqlite3_result_blob(context, aOut, nOut, sqlite3_free); } } } /* ** Prepare the SQL statement in buffer zSql against database handle db. ** If successful, set *ppStmt to point to the new statement and return ** SQLITE_OK. ** ** Otherwise, if an error does occur, set *ppStmt to NULL and return |
︙ | ︙ | |||
522 523 524 525 526 527 528 | pIter->bCleanup = 0; rc = sqlite3_step(pIter->pTblIter); if( rc!=SQLITE_ROW ){ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); pIter->zTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); | > | | 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 | pIter->bCleanup = 0; rc = sqlite3_step(pIter->pTblIter); if( rc!=SQLITE_ROW ){ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg); pIter->zTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM; } }else{ if( pIter->zIdx==0 ){ sqlite3_stmt *pIdx = pIter->pIdxIter; rc = sqlite3_bind_text(pIdx, 1, pIter->zTbl, -1, SQLITE_STATIC); } if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 | if( rc!=SQLITE_OK ){ rbuObjIterFinalize(pIter); p->rc = rc; } return rc; } /* ** Initialize the iterator structure passed as the second argument. ** ** If no error occurs, SQLITE_OK is returned and the iterator is left ** pointing to the first entry. Otherwise, an error code and message is ** left in the RBU handle passed as the first argument. A copy of the ** error code is returned. */ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){ int rc; memset(pIter, 0, sizeof(RbuObjIter)); rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | if( rc!=SQLITE_OK ){ rbuObjIterFinalize(pIter); p->rc = rc; } return rc; } /* ** The implementation of the rbu_target_name() SQL function. This function ** accepts one argument - the name of a table in the RBU database. If the ** table name matches the pattern: ** ** data[0-9]_<name> ** ** where <name> is any sequence of 1 or more characters, <name> is returned. ** Otherwise, if the only argument does not match the above pattern, an SQL ** NULL is returned. ** ** "data_t1" -> "t1" ** "data0123_t2" -> "t2" ** "dataAB_t3" -> NULL */ static void rbuTargetNameFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zIn; assert( argc==1 ); zIn = (const char*)sqlite3_value_text(argv[0]); if( zIn && strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){ int i; for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++); if( zIn[i]=='_' && zIn[i+1] ){ sqlite3_result_text(context, &zIn[i+1], -1, SQLITE_STATIC); } } } /* ** Initialize the iterator structure passed as the second argument. ** ** If no error occurs, SQLITE_OK is returned and the iterator is left ** pointing to the first entry. Otherwise, an error code and message is ** left in the RBU handle passed as the first argument. A copy of the ** error code is returned. */ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){ int rc; memset(pIter, 0, sizeof(RbuObjIter)); rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, "SELECT rbu_target_name(name) AS target, name FROM sqlite_master " "WHERE type IN ('table', 'view') AND target IS NOT NULL " "ORDER BY name" ); if( rc==SQLITE_OK ){ rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg, "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' " " FROM main.sqlite_master " " WHERE type='index' AND tbl_name = ?" |
︙ | ︙ | |||
913 914 915 916 917 918 919 | || pIter->eType==RBU_PK_VTAB ); /* Populate the azTblCol[] and nTblCol variables based on the columns ** of the input table. Ignore any input table columns that begin with ** "rbu_". */ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, | | | 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 | || pIter->eType==RBU_PK_VTAB ); /* Populate the azTblCol[] and nTblCol variables based on the columns ** of the input table. Ignore any input table columns that begin with ** "rbu_". */ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl) ); if( p->rc==SQLITE_OK ){ nCol = sqlite3_column_count(pStmt); rbuAllocateIterArrays(p, pIter, nCol); } for(i=0; p->rc==SQLITE_OK && i<nCol; i++){ const char *zName = (const char*)sqlite3_column_name(pStmt, i); |
︙ | ︙ | |||
938 939 940 941 942 943 944 | pStmt = 0; if( p->rc==SQLITE_OK && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) ){ p->rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf( | | | | | 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | pStmt = 0; if( p->rc==SQLITE_OK && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) ){ p->rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf( "table %q %s rbu_rowid column", pIter->zDataTbl, (bRbuRowid ? "may not have" : "requires") ); } /* Check that all non-HIDDEN columns in the destination table are also ** present in the input table. Populate the abTblPk[], azTblType[] and ** aiTblOrder[] arrays at the same time. */ if( p->rc==SQLITE_OK ){ p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg, sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl) ); } while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zName = (const char*)sqlite3_column_text(pStmt, 1); if( zName==0 ) break; /* An OOM - finalize() below returns S_NOMEM */ for(i=iOrder; i<pIter->nTblCol; i++){ if( 0==strcmp(zName, pIter->azTblCol[i]) ) break; } if( i==pIter->nTblCol ){ p->rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf("column missing from %q: %s", pIter->zDataTbl, zName ); }else{ int iPk = sqlite3_column_int(pStmt, 5); int bNotNull = sqlite3_column_int(pStmt, 3); const char *zType = (const char*)sqlite3_column_text(pStmt, 2); if( i!=iOrder ){ |
︙ | ︙ | |||
1259 1260 1261 1262 1263 1264 1265 | char c = zMask[pIter->aiSrcOrder[i]]; if( c=='x' ){ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d", zList, zSep, pIter->azTblCol[i], i+1 ); zSep = ", "; } | | > > > > > > | 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | char c = zMask[pIter->aiSrcOrder[i]]; if( c=='x' ){ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d", zList, zSep, pIter->azTblCol[i], i+1 ); zSep = ", "; } else if( c=='d' ){ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)", zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1 ); zSep = ", "; } else if( c=='f' ){ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)", zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1 ); zSep = ", "; } } } } return zList; } |
︙ | ︙ | |||
1515 1516 1517 1518 1519 1520 1521 | int bRbuRowid = (pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE); char *zBind = rbuObjIterGetBindlist(p, pIter->nTblCol + 1 + bRbuRowid); if( zBind ){ assert( pIter->pTmpInsert==0 ); p->rc = prepareFreeAndCollectError( p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf( "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)", | | | 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 | int bRbuRowid = (pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE); char *zBind = rbuObjIterGetBindlist(p, pIter->nTblCol + 1 + bRbuRowid); if( zBind ){ assert( pIter->pTmpInsert==0 ); p->rc = prepareFreeAndCollectError( p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf( "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)", p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind )); } } static void rbuTmpInsertFunc( sqlite3_context *pCtx, int nVal, |
︙ | ︙ | |||
1611 1612 1613 1614 1615 1616 1617 | /* Create the SELECT statement to read keys in sorted order */ if( p->rc==SQLITE_OK ){ char *zSql; if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", | | | | | | 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 | /* Create the SELECT statement to read keys in sorted order */ if( p->rc==SQLITE_OK ){ char *zSql; if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", zCollist, p->zStateDb, pIter->zDataTbl, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( "SELECT %s, rbu_control FROM '%q' " "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " "UNION ALL " "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " "ORDER BY %s%s", zCollist, pIter->zDataTbl, zCollist, p->zStateDb, pIter->zDataTbl, zCollist, zLimit ); } p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql); } sqlite3_free(zImposterCols); |
︙ | ︙ | |||
1646 1647 1648 1649 1650 1651 1652 | char *zWhere = rbuObjIterGetWhere(p, pIter); char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old"); char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new"); zCollist = rbuObjIterGetCollist(p, pIter); pIter->nCol = pIter->nTblCol; | < < < < < < < < < < | 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 | char *zWhere = rbuObjIterGetWhere(p, pIter); char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old"); char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new"); zCollist = rbuObjIterGetCollist(p, pIter); pIter->nCol = pIter->nTblCol; /* Create the imposter table or tables (if required). */ rbuCreateImposterTable(p, pIter); rbuCreateImposterTable2(p, pIter); zWrite = (pIter->eType==RBU_PK_VTAB ? "" : "rbu_imp_"); /* Create the INSERT statement to write to the target PK b-tree */ if( p->rc==SQLITE_OK ){ |
︙ | ︙ | |||
1689 1690 1691 1692 1693 1694 1695 | if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zRbuRowid = ", rbu_rowid"; } /* Create the rbu_tmp_xxx table and the triggers to populate it. */ rbuMPrintfExec(p, p->dbRbu, "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS " | | | | | | 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 | if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zRbuRowid = ", rbu_rowid"; } /* Create the rbu_tmp_xxx table and the triggers to populate it. */ rbuMPrintfExec(p, p->dbRbu, "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS " "SELECT *%s FROM '%q' WHERE 0;" , p->zStateDb, pIter->zDataTbl , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "") , pIter->zDataTbl ); rbuMPrintfExec(p, p->dbMain, "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" " "BEGIN " " SELECT rbu_tmp_insert(2, %s);" "END;" |
︙ | ︙ | |||
1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 | "END;", zWrite, zTbl, zNewlist ); } rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid); } sqlite3_free(zWhere); sqlite3_free(zOldlist); sqlite3_free(zNewlist); sqlite3_free(zBindings); } sqlite3_free(zCollist); | > > > > > > > > > > > | 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 | "END;", zWrite, zTbl, zNewlist ); } rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid); } /* Create the SELECT statement to read keys from data_xxx */ if( p->rc==SQLITE_OK ){ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, sqlite3_mprintf( "SELECT %s, rbu_control%s FROM '%q'%s", zCollist, (bRbuRowid ? ", rbu_rowid" : ""), pIter->zDataTbl, zLimit ) ); } sqlite3_free(zWhere); sqlite3_free(zOldlist); sqlite3_free(zNewlist); sqlite3_free(zBindings); } sqlite3_free(zCollist); |
︙ | ︙ | |||
1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 | } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_create_function(p->dbMain, "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0 ); } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p); } rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master"); /* Mark the database file just opened as an RBU target database. If | > > > > > > > > > > > > | 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 | } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_create_function(p->dbMain, "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0 ); } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_create_function(p->dbMain, "rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0 ); } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_create_function(p->dbRbu, "rbu_target_name", 1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0 ); } if( p->rc==SQLITE_OK ){ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p); } rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master"); /* Mark the database file just opened as an RBU target database. If |
︙ | ︙ | |||
2287 2288 2289 2290 2291 2292 2293 | sqlite3_stmt *pUpdate = 0; assert( eType==RBU_UPDATE ); rbuGetUpdateStmt(p, pIter, zMask, &pUpdate); if( pUpdate ){ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){ char c = zMask[pIter->aiSrcOrder[i]]; pVal = sqlite3_column_value(pIter->pSelect, i); | | | 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 | sqlite3_stmt *pUpdate = 0; assert( eType==RBU_UPDATE ); rbuGetUpdateStmt(p, pIter, zMask, &pUpdate); if( pUpdate ){ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){ char c = zMask[pIter->aiSrcOrder[i]]; pVal = sqlite3_column_value(pIter->pSelect, i); if( pIter->abTblPk[i] || c!='.' ){ p->rc = sqlite3_bind_value(pUpdate, i+1, pVal); } } if( p->rc==SQLITE_OK && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) ){ /* Bind the rbu_rowid value to column _rowid_ */ |
︙ | ︙ | |||
2399 2400 2401 2402 2403 2404 2405 | if( pIter->bCleanup ){ /* Clean up the rbu_tmp_xxx table for the previous table. It ** cannot be dropped as there are currently active SQL statements. ** But the contents can be deleted. */ if( pIter->abIndexed ){ rbuMPrintfExec(p, p->dbRbu, | | | 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 | if( pIter->bCleanup ){ /* Clean up the rbu_tmp_xxx table for the previous table. It ** cannot be dropped as there are currently active SQL statements. ** But the contents can be deleted. */ if( pIter->abIndexed ){ rbuMPrintfExec(p, p->dbRbu, "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl ); } }else{ rbuObjIterPrepareAll(p, pIter, 0); /* Advance to the next row to process. */ if( p->rc==SQLITE_OK ){ |
︙ | ︙ | |||
2622 2623 2624 2625 2626 2627 2628 | /* ** If there is a "*-oal" file in the file-system corresponding to the ** target database in the file-system, delete it. If an error occurs, ** leave an error code and error message in the rbu handle. */ static void rbuDeleteOalFile(sqlite3rbu *p){ | | > > | | | > | 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 | /* ** If there is a "*-oal" file in the file-system corresponding to the ** target database in the file-system, delete it. If an error occurs, ** leave an error code and error message in the rbu handle. */ static void rbuDeleteOalFile(sqlite3rbu *p){ char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget); if( zOal ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(0); assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 ); pVfs->xDelete(pVfs, zOal, 0); sqlite3_free(zOal); } } /* ** Allocate a private rbu VFS for the rbu handle passed as the only ** argument. This VFS will be used unless the call to sqlite3rbu_open() ** specified a URI with a vfs=? option in place of a target database ** file name. |
︙ | ︙ | |||
2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 | ** page 1 of the database file. */ p->rc = SQLITE_BUSY; p->zErrmsg = sqlite3_mprintf("database modified during rbu update"); } if( p->rc==SQLITE_OK ){ if( p->eStage==RBU_STAGE_OAL ){ /* Open transactions both databases. The *-oal file is opened or ** created at this point. */ | > | | > > > > > > > > > > | 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 | ** page 1 of the database file. */ p->rc = SQLITE_BUSY; p->zErrmsg = sqlite3_mprintf("database modified during rbu update"); } if( p->rc==SQLITE_OK ){ if( p->eStage==RBU_STAGE_OAL ){ sqlite3 *db = p->dbMain; /* Open transactions both databases. The *-oal file is opened or ** created at this point. */ p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg); if( p->rc==SQLITE_OK ){ p->rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg); } /* Check if the main database is a zipvfs db. If it is, set the upper ** level pager to use "journal_mode=off". This prevents it from ** generating a large journal using a temp file. */ if( p->rc==SQLITE_OK ){ int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0); if( frc==SQLITE_OK ){ p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg); } } /* Point the object iterator at the first object */ if( p->rc==SQLITE_OK ){ p->rc = rbuObjIterFirst(p, &p->objiter); } /* If the RBU database contains no data_xxx tables, declare the RBU ** update finished. */ |
︙ | ︙ | |||
2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 | ** Return the total number of key-value operations (inserts, deletes or ** updates) that have been performed on the target database since the ** current RBU update was started. */ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu){ return pRbu->nProgress; } /************************************************************************** ** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour ** of a standard VFS in the following ways: ** ** 1. Whenever the first page of a main database file is read or ** written, the value of the change-counter cookie is stored in | > > > > > > > > > > > > > > > > > > > > > > > > > > | 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 | ** Return the total number of key-value operations (inserts, deletes or ** updates) that have been performed on the target database since the ** current RBU update was started. */ sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu){ return pRbu->nProgress; } int sqlite3rbu_savestate(sqlite3rbu *p){ int rc = p->rc; if( rc==SQLITE_DONE ) return SQLITE_OK; assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE ); if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0); } p->rc = rc; rbuSaveState(p, p->eStage); rc = p->rc; if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0); } p->rc = rc; return rc; } /************************************************************************** ** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour ** of a standard VFS in the following ways: ** ** 1. Whenever the first page of a main database file is read or ** written, the value of the change-counter cookie is stored in |
︙ | ︙ |
Changes to ext/rbu/sqlite3rbu.h.
︙ | ︙ | |||
93 94 95 96 97 98 99 100 101 102 103 104 105 106 | ** ** Then the RBU database should contain: ** ** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control); ** ** The order of the columns in the data_% table does not matter. ** ** If the target database table is a virtual table or a table that has no ** PRIMARY KEY declaration, the data_% table must also contain a column ** named "rbu_rowid". This column is mapped to the tables implicit primary ** key column - "rowid". Virtual tables for which the "rowid" column does ** not function like a primary key value cannot be updated using RBU. For ** example, if the target db contains either of the following: ** | > > > > > > > > > > > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | ** ** Then the RBU database should contain: ** ** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control); ** ** The order of the columns in the data_% table does not matter. ** ** Instead of a regular table, the RBU database may also contain virtual ** tables or view named using the data_<target> naming scheme. ** ** Instead of the plain data_<target> naming scheme, RBU database tables ** may also be named data<integer>_<target>, where <integer> is any sequence ** of zero or more numeric characters (0-9). This can be significant because ** tables within the RBU database are always processed in order sorted by ** name. By judicious selection of the the <integer> portion of the names ** of the RBU tables the user can therefore control the order in which they ** are processed. This can be useful, for example, to ensure that "external ** content" FTS4 tables are updated before their underlying content tables. ** ** If the target database table is a virtual table or a table that has no ** PRIMARY KEY declaration, the data_% table must also contain a column ** named "rbu_rowid". This column is mapped to the tables implicit primary ** key column - "rowid". Virtual tables for which the "rowid" column does ** not function like a primary key value cannot be updated using RBU. For ** example, if the target db contains either of the following: ** |
︙ | ︙ | |||
172 173 174 175 176 177 178 179 180 181 182 183 184 185 | ** For example, this row: ** ** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d'); ** ** is similar to an UPDATE statement such as: ** ** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4; ** ** If the target database table is a virtual table or a table with no PRIMARY ** KEY, the rbu_control value should not include a character corresponding ** to the rbu_rowid value. For example, this: ** ** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control) ** VALUES(NULL, 'usa', 12, '.x'); | > > > > > > > > | 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | ** For example, this row: ** ** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d'); ** ** is similar to an UPDATE statement such as: ** ** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4; ** ** Finally, if an 'f' character appears in place of a 'd' or 's' in an ** ota_control string, the contents of the data_xxx table column is assumed ** to be a "fossil delta" - a patch to be applied to a blob value in the ** format used by the fossil source-code management system. In this case ** the existing value within the target database table must be of type BLOB. ** It is replaced by the result of applying the specified fossil delta to ** itself. ** ** If the target database table is a virtual table or a table with no PRIMARY ** KEY, the rbu_control value should not include a character corresponding ** to the rbu_rowid value. For example, this: ** ** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control) ** VALUES(NULL, 'usa', 12, '.x'); |
︙ | ︙ | |||
330 331 332 333 334 335 336 337 338 339 340 341 342 343 | ** ** Once a call to sqlite3rbu_step() has returned a value other than ** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops ** that immediately return the same value. */ int sqlite3rbu_step(sqlite3rbu *pRbu); /* ** Close an RBU handle. ** ** If the RBU update has been completely applied, mark the RBU database ** as fully applied. Otherwise, assuming no error has occurred, save the ** current state of the RBU update appliation to the RBU database. ** | > > > > > > > > > > > > | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | ** ** Once a call to sqlite3rbu_step() has returned a value other than ** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops ** that immediately return the same value. */ int sqlite3rbu_step(sqlite3rbu *pRbu); /* ** Force RBU to save its state to disk. ** ** If a power failure or application crash occurs during an update, following ** system recovery RBU may resume the update from the point at which the state ** was last saved. In other words, from the most recent successful call to ** sqlite3rbu_close() or this function. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. */ int sqlite3rbu_savestate(sqlite3rbu *pRbu); /* ** Close an RBU handle. ** ** If the RBU update has been completely applied, mark the RBU database ** as fully applied. Otherwise, assuming no error has occurred, save the ** current state of the RBU update appliation to the RBU database. ** |
︙ | ︙ |
Changes to ext/rbu/test_rbu.c.
︙ | ︙ | |||
52 53 54 55 56 57 58 | ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int ret = TCL_OK; sqlite3rbu *pRbu = (sqlite3rbu*)clientData; | | > > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int ret = TCL_OK; sqlite3rbu *pRbu = (sqlite3rbu*)clientData; const char *azMethod[] = { "step", "close", "create_rbu_delta", "savestate", 0 }; int iMethod; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "METHOD"); return TCL_ERROR; } if( Tcl_GetIndexFromObj(interp, objv[1], azMethod, "method", 0, &iMethod) ){ |
︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 | } case 2: /* create_rbu_delta */ { sqlite3 *db = sqlite3rbu_db(pRbu, 0); int rc = sqlite3_create_function( db, "rbu_delta", -1, SQLITE_UTF8, (void*)interp, test_rbu_delta, 0, 0 ); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); break; } default: /* seems unlikely */ assert( !"cannot happen" ); | > > > > > > > | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | } case 2: /* create_rbu_delta */ { sqlite3 *db = sqlite3rbu_db(pRbu, 0); int rc = sqlite3_create_function( db, "rbu_delta", -1, SQLITE_UTF8, (void*)interp, test_rbu_delta, 0, 0 ); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); break; } case 3: /* savestate */ { int rc = sqlite3rbu_savestate(pRbu); Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); break; } default: /* seems unlikely */ assert( !"cannot happen" ); |
︙ | ︙ |
Changes to main.mk.
︙ | ︙ | |||
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 | $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ fts5.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ | > > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | $(TOP)/ext/misc/eval.c \ $(TOP)/ext/misc/fileio.c \ $(TOP)/ext/misc/fuzzer.c \ $(TOP)/ext/misc/ieee754.c \ $(TOP)/ext/misc/nextchar.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ fts5.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ |
︙ | ︙ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
706 707 708 709 710 711 712 | /* ** get2byteAligned(), unlike get2byte(), requires that its argument point to a ** two-byte aligned address. get2bytea() is only used for accessing the ** cell addresses in a btree header. */ #if SQLITE_BYTEORDER==4321 # define get2byteAligned(x) (*(u16*)(x)) | | > > | | 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 | /* ** get2byteAligned(), unlike get2byte(), requires that its argument point to a ** two-byte aligned address. get2bytea() is only used for accessing the ** cell addresses in a btree header. */ #if SQLITE_BYTEORDER==4321 # define get2byteAligned(x) (*(u16*)(x)) #elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ && GCC_VERSION>=4008000 # define get2byteAligned(x) __builtin_bswap16(*(u16*)(x)) #elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ && defined(_MSC_VER) && _MSC_VER>=1300 # define get2byteAligned(x) _byteswap_ushort(*(u16*)(x)) #else # define get2byteAligned(x) ((x)[0]<<8 | (x)[1]) #endif |
Changes to src/build.c.
︙ | ︙ | |||
352 353 354 355 356 357 358 359 360 361 362 363 364 365 | if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ return 0; } p = sqlite3FindTable(pParse->db, zName, zDbase); if( p==0 ){ const char *zMsg = isView ? "no such view" : "no such table"; if( zDbase ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } pParse->checkSchema = 1; } | > > > > > > > > > | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ return 0; } p = sqlite3FindTable(pParse->db, zName, zDbase); if( p==0 ){ const char *zMsg = isView ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUAL_TABLE /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName); if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ return pMod->pEpoTab; } #endif if( zDbase ){ sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); }else{ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } pParse->checkSchema = 1; } |
︙ | ︙ | |||
556 557 558 559 560 561 562 | db->flags &= ~SQLITE_InternChanges; } /* ** Delete memory allocated for the column names of a table or view (the ** Table.aCol[] array). */ | | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | db->flags &= ~SQLITE_InternChanges; } /* ** Delete memory allocated for the column names of a table or view (the ** Table.aCol[] array). */ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){ int i; Column *pCol; assert( pTable!=0 ); if( (pCol = pTable->aCol)!=0 ){ for(i=0; i<pTable->nCol; i++, pCol++){ sqlite3DbFree(db, pCol->zName); sqlite3ExprDelete(db, pCol->pDflt); |
︙ | ︙ | |||
623 624 625 626 627 628 629 | } /* Delete any foreign keys attached to this table. */ sqlite3FkDelete(db, pTable); /* Delete the Table structure itself. */ | | | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 | } /* Delete any foreign keys attached to this table. */ sqlite3FkDelete(db, pTable); /* Delete the Table structure itself. */ sqlite3DeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); sqlite3SelectDelete(db, pTable->pSelect); #ifndef SQLITE_OMIT_CHECK sqlite3ExprListDelete(db, pTable->pCheck); #endif #ifndef SQLITE_OMIT_VIRTUALTABLE |
︙ | ︙ | |||
1812 1813 1814 1815 1816 1817 1818 | Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ sqlite3 *db = pParse->db; /* The database connection */ int iDb; /* Database in which the table lives */ Index *pIdx; /* An implied index of the table */ | | > | 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 | Select *pSelect /* Select from a "CREATE ... AS SELECT" */ ){ Table *p; /* The new table */ sqlite3 *db = pParse->db; /* The database connection */ int iDb; /* Database in which the table lives */ Index *pIdx; /* An implied index of the table */ if( pEnd==0 && pSelect==0 ){ return; } assert( !db->mallocFailed ); p = pParse->pNewTable; if( p==0 ) return; assert( !db->init.busy || !pSelect ); /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. |
︙ | ︙ | |||
2084 2085 2086 2087 2088 2089 2090 | ** they will persist after the current sqlite3_exec() call returns. */ p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); sqlite3SelectDelete(db, pSelect); if( db->mallocFailed ){ return; } | < < < | 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 | ** they will persist after the current sqlite3_exec() call returns. */ p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); sqlite3SelectDelete(db, pSelect); if( db->mallocFailed ){ return; } /* Locate the end of the CREATE VIEW statement. Make sEnd point to ** the end. */ sEnd = pParse->sLastToken; if( ALWAYS(sEnd.z[0]!=0) && sEnd.z[0]!=';' ){ sEnd.z += sEnd.n; |
︙ | ︙ | |||
2216 2217 2218 2219 2220 2221 2222 | static void sqliteViewResetAll(sqlite3 *db, int idx){ HashElem *i; assert( sqlite3SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); if( pTab->pSelect ){ | | | 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 | static void sqliteViewResetAll(sqlite3 *db, int idx){ HashElem *i; assert( sqlite3SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); if( pTab->pSelect ){ sqlite3DeleteColumnNames(db, pTab); pTab->aCol = 0; pTab->nCol = 0; } } DbClearProperty(db, idx, DB_UnresetViews); } #else |
︙ | ︙ | |||
3698 3699 3700 3701 3702 3703 3704 | int i; struct SrcList_item *pItem; if( pList==0 ) return; for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){ sqlite3DbFree(db, pItem->zDatabase); sqlite3DbFree(db, pItem->zName); sqlite3DbFree(db, pItem->zAlias); | | > | 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 | int i; struct SrcList_item *pItem; if( pList==0 ) return; for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){ sqlite3DbFree(db, pItem->zDatabase); sqlite3DbFree(db, pItem->zName); sqlite3DbFree(db, pItem->zAlias); if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy); if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg); sqlite3DeleteTable(db, pItem->pTab); sqlite3SelectDelete(db, pItem->pSelect); sqlite3ExprDelete(db, pItem->pOn); sqlite3IdListDelete(db, pItem->pUsing); } sqlite3DbFree(db, pList); } |
︙ | ︙ | |||
3771 3772 3773 3774 3775 3776 3777 | ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. */ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ assert( pIndexedBy!=0 ); if( p && ALWAYS(p->nSrc>0) ){ struct SrcList_item *pItem = &p->a[p->nSrc-1]; | | > > | | > > > > > > > > > > > > > > > > > > | | | 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 | ** Add an INDEXED BY or NOT INDEXED clause to the most recently added ** element of the source-list passed as the second argument. */ void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pIndexedBy){ assert( pIndexedBy!=0 ); if( p && ALWAYS(p->nSrc>0) ){ struct SrcList_item *pItem = &p->a[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); assert( pItem->fg.isIndexedBy==0 ); assert( pItem->fg.isTabFunc==0 ); if( pIndexedBy->n==1 && !pIndexedBy->z ){ /* A "NOT INDEXED" clause was supplied. See parse.y ** construct "indexed_opt" for details. */ pItem->fg.notIndexed = 1; }else{ pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy); pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0); } } } /* ** Add the list of function arguments to the SrcList entry for a ** table-valued-function. */ void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){ if( p && pList ){ struct SrcList_item *pItem = &p->a[p->nSrc-1]; assert( pItem->fg.notIndexed==0 ); assert( pItem->fg.isIndexedBy==0 ); assert( pItem->fg.isTabFunc==0 ); pItem->u1.pFuncArg = pList; pItem->fg.isTabFunc = 1; }else{ sqlite3ExprListDelete(pParse->db, pList); } } /* ** When building up a FROM clause in the parser, the join operator ** is initially attached to the left operand. But the code generator ** expects the join operator to be on the right operand. This routine ** Shifts all join operators from left to right for an entire FROM ** clause. ** ** Example: Suppose the join is like this: ** ** A natural cross join B ** ** The operator is "natural cross join". The A and B operands are stored ** in p->a[0] and p->a[1], respectively. The parser initially stores the ** operator with A. This routine shifts that operator over to B. */ void sqlite3SrcListShiftJoinType(SrcList *p){ if( p ){ int i; for(i=p->nSrc-1; i>0; i--){ p->a[i].fg.jointype = p->a[i-1].fg.jointype; } p->a[0].fg.jointype = 0; } } /* ** Begin a transaction */ void sqlite3BeginTransaction(Parse *pParse, int type){ |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 | struct SrcList_item *pNewItem = &pNew->a[i]; struct SrcList_item *pOldItem = &p->a[i]; Table *pTab; pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); | | | < < | > | > | > > | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | struct SrcList_item *pNewItem = &pNew->a[i]; struct SrcList_item *pOldItem = &p->a[i]; Table *pTab; pNewItem->pSchema = pOldItem->pSchema; pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase); pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias); pNewItem->fg = pOldItem->fg; pNewItem->iCursor = pOldItem->iCursor; pNewItem->addrFillSub = pOldItem->addrFillSub; pNewItem->regReturn = pOldItem->regReturn; if( pNewItem->fg.isIndexedBy ){ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy); } pNewItem->pIBIndex = pOldItem->pIBIndex; if( pNewItem->fg.isTabFunc ){ pNewItem->u1.pFuncArg = sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags); } pTab = pNewItem->pTab = pOldItem->pTab; if( pTab ){ pTab->nRef++; } pNewItem->pSelect = sqlite3SelectDup(db, pOldItem->pSelect, flags); pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
1992 1993 1994 1995 1996 1997 1998 | sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); }else{ sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName); sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ | | | 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 | sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); }else{ sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName); sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ u8 idxInsFlags = 0; for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } assert( pSrcIdx ); sqlite3VdbeAddOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc); sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx); VdbeComment((v, "%s", pSrcIdx->zName)); |
︙ | ︙ | |||
2027 2028 2029 2030 2031 2032 2033 | ** sorted order. */ for(i=0; i<pSrcIdx->nColumn; i++){ char *zColl = pSrcIdx->azColl[i]; assert( zColl!=0 ); if( sqlite3_stricmp("BINARY", zColl) ) break; } if( i==pSrcIdx->nColumn ){ | | > > > | | 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 | ** sorted order. */ for(i=0; i<pSrcIdx->nColumn; i++){ char *zColl = pSrcIdx->azColl[i]; assert( zColl!=0 ); if( sqlite3_stricmp("BINARY", zColl) ) break; } if( i==pSrcIdx->nColumn ){ idxInsFlags = OPFLAG_USESEEKRESULT; sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1); } } if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){ idxInsFlags |= OPFLAG_NCHANGE; } sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1); sqlite3VdbeChangeP5(v, idxInsFlags); sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); } if( emptySrcTest ) sqlite3VdbeJumpHere(v, emptySrcTest); sqlite3ReleaseTempReg(pParse, regRowid); |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
928 929 930 931 932 933 934 935 936 937 938 | /* ** Disconnect all sqlite3_vtab objects that belong to database connection ** db. This is called when db is being closed. */ static void disconnectAllVtab(sqlite3 *db){ #ifndef SQLITE_OMIT_VIRTUALTABLE int i; sqlite3BtreeEnterAll(db); for(i=0; i<db->nDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( db->aDb[i].pSchema ){ | > < > > > > > > | 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 | /* ** Disconnect all sqlite3_vtab objects that belong to database connection ** db. This is called when db is being closed. */ static void disconnectAllVtab(sqlite3 *db){ #ifndef SQLITE_OMIT_VIRTUALTABLE int i; HashElem *p; sqlite3BtreeEnterAll(db); for(i=0; i<db->nDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( db->aDb[i].pSchema ){ for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ Table *pTab = (Table *)sqliteHashData(p); if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab); } } } for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){ Module *pMod = (Module *)sqliteHashData(p); if( pMod->pEpoTab ){ sqlite3VtabDisconnect(db, pMod->pEpoTab); } } sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); #else UNUSED_PARAMETER(db); #endif } |
︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 | sqlite3HashClear(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){ Module *pMod = (Module *)sqliteHashData(i); if( pMod->xDestroy ){ pMod->xDestroy(pMod->pAux); } sqlite3DbFree(db, pMod); } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); | > | 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 | sqlite3HashClear(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){ Module *pMod = (Module *)sqliteHashData(i); if( pMod->xDestroy ){ pMod->xDestroy(pMod->pAux); } sqlite3VtabEponymousTableClear(db, pMod); sqlite3DbFree(db, pMod); } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
583 584 585 586 587 588 589 | } // "seltablist" is a "Select Table List" - the content of the FROM clause // in a SELECT statement. "stl_prefix" is a prefix of this list. // stl_prefix(A) ::= seltablist(X) joinop(Y). { A = X; | | > > > > > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | } // "seltablist" is a "Select Table List" - the content of the FROM clause // in a SELECT statement. "stl_prefix" is a prefix of this list. // stl_prefix(A) ::= seltablist(X) joinop(Y). { A = X; if( ALWAYS(A && A->nSrc>0) ) A->a[A->nSrc-1].fg.jointype = (u8)Y; } stl_prefix(A) ::= . {A = 0;} seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) indexed_opt(I) on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); sqlite3SrcListIndexedBy(pParse, A, &I); } seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) LP exprlist(E) RP as(Z) on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,&Y,&D,&Z,0,N,U); sqlite3SrcListFuncArgs(pParse, A, E); } %ifndef SQLITE_OMIT_SUBQUERY seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). { A = sqlite3SrcListAppendFromTerm(pParse,X,0,0,&Z,S,N,U); } seltablist(A) ::= stl_prefix(X) LP seltablist(F) RP |
︙ | ︙ |
Changes to src/pcache1.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 | ** SQLITE_CONFIG_PAGECACHE. ** (3) PCache-local bulk allocation. ** ** The third case is a chunk of heap memory (defaulting to 100 pages worth) ** that is allocated when the page cache is created. The size of the local ** bulk allocation can be adjusted using ** | | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | ** SQLITE_CONFIG_PAGECACHE. ** (3) PCache-local bulk allocation. ** ** The third case is a chunk of heap memory (defaulting to 100 pages worth) ** that is allocated when the page cache is created. The size of the local ** bulk allocation can be adjusted using ** ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, 0, 0, N). ** ** If N is positive, then N pages worth of memory are allocated using a single ** sqlite3Malloc() call and that memory is used for the first N pages allocated. ** Or if N is negative, then -1024*N bytes of memory are allocated and used ** for as many pages as can be accomodated. ** ** Only one of (2) or (3) can be used. Once the memory available to (2) or |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
723 724 725 726 727 728 729 730 731 732 733 734 735 736 | if( !zRight ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ int size = sqlite3Atoi(zRight); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } break; } /* ** PRAGMA [database.]mmap_size(N) ** | > | 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 | if( !zRight ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ int size = sqlite3Atoi(zRight); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); if( sqlite3ReadSchema(pParse) ) goto pragma_out; } break; } /* ** PRAGMA [database.]mmap_size(N) ** |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
302 303 304 305 306 307 308 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ /* If there has been exactly one prior match and this match ** is for the right-hand table of a NATURAL JOIN or is in a ** USING clause, then skip this match. */ if( cnt==1 ){ | | | | | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ /* If there has been exactly one prior match and this match ** is for the right-hand table of a NATURAL JOIN or is in a ** USING clause, then skip this match. */ if( cnt==1 ){ if( pItem->fg.jointype & JT_NATURAL ) continue; if( nameInUsingClause(pItem->pUsing, zCol) ) continue; } cnt++; pMatch = pItem; /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j; break; } } } if( pMatch ){ pExpr->iTable = pMatch->iCursor; pExpr->pTab = pMatch->pTab; /* RIGHT JOIN not (yet) supported */ assert( (pMatch->fg.jointype & JT_RIGHT)==0 ); if( (pMatch->fg.jointype & JT_LEFT)!=0 ){ ExprSetProperty(pExpr, EP_CanBeNull); } pSchema = pExpr->pTab->pSchema; } } /* if( pSrcList ) */ #ifndef SQLITE_OMIT_TRIGGER |
︙ | ︙ | |||
1138 1139 1140 1141 1142 1143 1144 | */ static int resolveSelectStep(Walker *pWalker, Select *p){ NameContext *pOuterNC; /* Context that contains this SELECT */ NameContext sNC; /* Name context of this SELECT */ int isCompound; /* True if p is a compound select */ int nCompound; /* Number of compound terms processed so far */ Parse *pParse; /* Parsing context */ | < | 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | */ static int resolveSelectStep(Walker *pWalker, Select *p){ NameContext *pOuterNC; /* Context that contains this SELECT */ NameContext sNC; /* Name context of this SELECT */ int isCompound; /* True if p is a compound select */ int nCompound; /* Number of compound terms processed so far */ Parse *pParse; /* Parsing context */ int i; /* Loop counter */ ExprList *pGroupBy; /* The GROUP BY clause */ Select *pLeftmost; /* Left-most of SELECT of a compound */ sqlite3 *db; /* Database connection */ assert( p!=0 ); |
︙ | ︙ | |||
1211 1212 1213 1214 1215 1216 1217 | int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; /* Count the total number of references to pOuterNC and all of its ** parent contexts. After resolving references to expressions in ** pItem->pSelect, check if this value has changed. If so, then ** SELECT statement pItem->pSelect must be correlated. Set the | | | | < < < < | < < < | 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; /* Count the total number of references to pOuterNC and all of its ** parent contexts. After resolving references to expressions in ** pItem->pSelect, check if this value has changed. If so, then ** SELECT statement pItem->pSelect must be correlated. Set the ** pItem->fg.isCorrelated flag if this is the case. */ for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef; if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; if( pParse->nErr || db->mallocFailed ) return WRC_Abort; for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef; assert( pItem->fg.isCorrelated==0 && nRef<=0 ); pItem->fg.isCorrelated = (nRef!=0); } } /* Set up the local name-context to pass to sqlite3ResolveExprNames() to ** resolve the result-set expression list. */ sNC.ncFlags = NC_AllowAgg; sNC.pSrcList = p->pSrc; sNC.pNext = pOuterNC; /* Resolve names in the result set. */ if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort; /* If there are no aggregate functions in the result-set, and no GROUP BY ** expression, do not allow aggregates in any of the other expressions. */ assert( (p->selFlags & SF_Aggregate)==0 ); pGroupBy = p->pGroupBy; if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){ |
︙ | ︙ | |||
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 | ** ** Minor point: If this is the case, then the expression will be ** re-evaluated for each reference to it. */ sNC.pEList = p->pEList; if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries */ sNC.pNext = 0; sNC.ncFlags |= NC_AllowAgg; | > > > > > > > > > > | 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 | ** ** Minor point: If this is the case, then the expression will be ** re-evaluated for each reference to it. */ sNC.pEList = p->pEList; if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort; if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort; /* Resolve names in table-valued-function arguments */ for(i=0; i<p->pSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; if( pItem->fg.isTabFunc && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg) ){ return WRC_Abort; } } /* The ORDER BY and GROUP BY clauses may not refer to terms in ** outer queries */ sNC.pNext = 0; sNC.ncFlags |= NC_AllowAgg; |
︙ | ︙ | |||
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 | if( pNC->ncFlags & NC_HasAgg ){ ExprSetProperty(pExpr, EP_Agg); } pNC->ncFlags |= savedHasAgg; return ExprHasProperty(pExpr, EP_Error); } /* ** Resolve all names in all expressions of a SELECT and in all ** decendents of the SELECT, including compounds off of p->pPrior, ** subqueries in expressions, and subqueries used as FROM clause ** terms. ** | > > > > > > > > > > > > > > > > | 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 | if( pNC->ncFlags & NC_HasAgg ){ ExprSetProperty(pExpr, EP_Agg); } pNC->ncFlags |= savedHasAgg; return ExprHasProperty(pExpr, EP_Error); } /* ** Resolve all names for all expression in an expression list. This is ** just like sqlite3ResolveExprNames() except that it works for an expression ** list rather than a single expression. */ int sqlite3ResolveExprListNames( NameContext *pNC, /* Namespace to resolve expressions in. */ ExprList *pList /* The expression list to be analyzed. */ ){ int i; assert( pList!=0 ); for(i=0; i<pList->nExpr; i++){ if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort; } return WRC_Continue; } /* ** Resolve all names in all expressions of a SELECT and in all ** decendents of the SELECT, including compounds off of p->pPrior, ** subqueries in expressions, and subqueries used as FROM clause ** terms. ** |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
402 403 404 405 406 407 408 | pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ Table *pLeftTab = pLeft->pTab; Table *pRightTab = pRight->pTab; int isOuter; if( NEVER(pLeftTab==0 || pRightTab==0) ) continue; | | | | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 | pRight = &pLeft[1]; for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){ Table *pLeftTab = pLeft->pTab; Table *pRightTab = pRight->pTab; int isOuter; if( NEVER(pLeftTab==0 || pRightTab==0) ) continue; isOuter = (pRight->fg.jointype & JT_OUTER)!=0; /* When the NATURAL keyword is present, add WHERE clause terms for ** every column that the two tables have in common. */ if( pRight->fg.jointype & JT_NATURAL ){ if( pRight->pOn || pRight->pUsing ){ sqlite3ErrorMsg(pParse, "a NATURAL join may not have " "an ON or USING clause", 0); return 1; } for(j=0; j<pRightTab->nCol; j++){ char *zName; /* Name of column in the right table */ |
︙ | ︙ | |||
1929 1930 1931 1932 1933 1934 1935 | ** ** <recursive-table> AS (<setup-query> UNION [ALL] <recursive-query>) ** \___________/ \_______________/ ** p->pPrior p ** ** ** There is exactly one reference to the recursive-table in the FROM clause | | | 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 | ** ** <recursive-table> AS (<setup-query> UNION [ALL] <recursive-query>) ** \___________/ \_______________/ ** p->pPrior p ** ** ** There is exactly one reference to the recursive-table in the FROM clause ** of recursive-query, marked with the SrcList->a[].fg.isRecursive flag. ** ** The setup-query runs once to generate an initial set of rows that go ** into a Queue table. Rows are extracted from the Queue table one by ** one. Each row extracted from Queue is output to pDest. Then the single ** extracted row (now in the iCurrent table) becomes the content of the ** recursive-table for a recursive-query run. The output of the recursive-query ** is added back into the Queue table. Then another row is extracted from Queue |
︙ | ︙ | |||
1994 1995 1996 1997 1998 1999 2000 | regOffset = p->iOffset; p->pLimit = p->pOffset = 0; p->iLimit = p->iOffset = 0; pOrderBy = p->pOrderBy; /* Locate the cursor number of the Current table */ for(i=0; ALWAYS(i<pSrc->nSrc); i++){ | | | 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 | regOffset = p->iOffset; p->pLimit = p->pOffset = 0; p->iLimit = p->iOffset = 0; pOrderBy = p->pOrderBy; /* Locate the cursor number of the Current table */ for(i=0; ALWAYS(i<pSrc->nSrc); i++){ if( pSrc->a[i].fg.isRecursive ){ iCurrent = pSrc->a[i].iCursor; break; } } /* Allocate cursors numbers for Queue and Distinct. The cursor number for ** the Distinct table must be exactly one greater than Queue in order |
︙ | ︙ | |||
3409 3410 3411 3412 3413 3414 3415 | ** effectively converts the OUTER JOIN into an INNER JOIN. ** ** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE: ** Ticket #3300 shows that flattening the right term of a LEFT JOIN ** is fraught with danger. Best to avoid the whole thing. If the ** subquery is the right term of a LEFT JOIN, then do not flatten. */ | | | 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 | ** effectively converts the OUTER JOIN into an INNER JOIN. ** ** THIS OVERRIDES OBSOLETE COMMENTS 1 AND 2 ABOVE: ** Ticket #3300 shows that flattening the right term of a LEFT JOIN ** is fraught with danger. Best to avoid the whole thing. If the ** subquery is the right term of a LEFT JOIN, then do not flatten. */ if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){ return 0; } /* Restriction 17: If the sub-query is a compound SELECT, then it must ** use only the UNION ALL operator. And none of the simple select queries ** that make up the compound SELECT are allowed to be aggregate or distinct ** queries. |
︙ | ︙ | |||
3580 3581 3582 3583 3584 3585 3586 | u8 jointype = 0; pSubSrc = pSub->pSrc; /* FROM clause of subquery */ nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ pSrc = pParent->pSrc; /* FROM clause of the outer query */ if( pSrc ){ assert( pParent==p ); /* First time through the loop */ | | | 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 | u8 jointype = 0; pSubSrc = pSub->pSrc; /* FROM clause of subquery */ nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */ pSrc = pParent->pSrc; /* FROM clause of the outer query */ if( pSrc ){ assert( pParent==p ); /* First time through the loop */ jointype = pSubitem->fg.jointype; }else{ assert( pParent!=p ); /* 2nd and subsequent times through the loop */ pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); if( pSrc==0 ){ assert( db->mallocFailed ); break; } |
︙ | ︙ | |||
3620 3621 3622 3623 3624 3625 3626 | ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } | | | 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 | ** outer query. */ for(i=0; i<nSubSrc; i++){ sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing); pSrc->a[i+iFrom] = pSubSrc->a[i]; memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i])); } pSrc->a[iFrom].fg.jointype = jointype; /* Now begin substituting subquery result set expressions for ** references to the iParent in the outer query. ** ** Example: ** ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b; |
︙ | ︙ | |||
3871 3872 3873 3874 3875 3876 3877 | ** If the source-list item passed as an argument was augmented with an ** INDEXED BY clause, then try to locate the specified index. If there ** was such a clause and the named index cannot be found, return ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate ** pFrom->pIndex and return SQLITE_OK. */ int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ | | | | | 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 | ** If the source-list item passed as an argument was augmented with an ** INDEXED BY clause, then try to locate the specified index. If there ** was such a clause and the named index cannot be found, return ** SQLITE_ERROR and leave an error in pParse. Otherwise, populate ** pFrom->pIndex and return SQLITE_OK. */ int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){ if( pFrom->pTab && pFrom->fg.isIndexedBy ){ Table *pTab = pFrom->pTab; char *zIndexedBy = pFrom->u1.zIndexedBy; Index *pIdx; for(pIdx=pTab->pIndex; pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy); pIdx=pIdx->pNext ); if( !pIdx ){ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0); pParse->checkSchema = 1; return SQLITE_ERROR; } pFrom->pIBIndex = pIdx; } return SQLITE_OK; } /* ** Detect compound SELECT statements that use an ORDER BY clause with ** an alternative collating sequence. ** |
︙ | ︙ | |||
4079 4080 4081 4082 4083 4084 4085 | for(i=0; i<pSrc->nSrc; i++){ struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ pItem->pTab = pTab; | | | 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 | for(i=0; i<pSrc->nSrc; i++){ struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->zDatabase==0 && pItem->zName!=0 && 0==sqlite3StrICmp(pItem->zName, pCte->zName) ){ pItem->pTab = pTab; pItem->fg.isRecursive = 1; pTab->nRef++; pSel->selFlags |= SF_Recursive; } } } /* Only one recursive reference is permitted. */ |
︙ | ︙ | |||
4209 4210 4211 4212 4213 4214 4215 | /* Look up every table named in the FROM clause of the select. If ** an entry of the FROM clause is a subquery instead of a table or view, ** then create a transient table structure to describe the subquery. */ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab; | | | | 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 | /* Look up every table named in the FROM clause of the select. If ** an entry of the FROM clause is a subquery instead of a table or view, ** then create a transient table structure to describe the subquery. */ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){ Table *pTab; assert( pFrom->fg.isRecursive==0 || pFrom->pTab ); if( pFrom->fg.isRecursive ) continue; if( pFrom->pTab!=0 ){ /* This statement has already been prepared. There is no need ** to go further. */ assert( i==0 ); #ifndef SQLITE_OMIT_CTE selectPopWith(pWalker, p); #endif |
︙ | ︙ | |||
4373 4374 4375 4376 4377 4378 4379 | if( IsHiddenColumn(&pTab->aCol[j]) ){ assert(IsVirtual(pTab)); continue; } tableSeen = 1; if( i>0 && zTName==0 ){ | | | 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 | if( IsHiddenColumn(&pTab->aCol[j]) ){ assert(IsVirtual(pTab)); continue; } tableSeen = 1; if( i>0 && zTName==0 ){ if( (pFrom->fg.jointype & JT_NATURAL)!=0 && tableAndColumnIndex(pTabList, i, zName, 0, 0) ){ /* In a NATURAL join, omit the join columns from the ** table to the right of the join */ continue; } if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ |
︙ | ︙ | |||
4900 4901 4902 4903 4904 4905 4906 | /* Sometimes the code for a subquery will be generated more than ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, ** for example. In that case, do not regenerate the code to manifest ** a view or the co-routine to implement a view. The first instance ** is sufficient, though the subroutine to manifest the view does need ** to be invoked again. */ if( pItem->addrFillSub ){ | | | | 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 | /* Sometimes the code for a subquery will be generated more than ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, ** for example. In that case, do not regenerate the code to manifest ** a view or the co-routine to implement a view. The first instance ** is sufficient, though the subroutine to manifest the view does need ** to be invoked again. */ if( pItem->addrFillSub ){ if( pItem->fg.viaCoroutine==0 ){ sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); } continue; } /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select ** may contain expression trees of at most ** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit ** more conservative than necessary, but much easier than enforcing ** an exact limit. */ pParse->nHeight += sqlite3SelectExprHeight(p); /* Make copies of constant WHERE-clause terms in the outer query down ** inside the subquery. This can help the subquery to run more efficiently. */ if( (pItem->fg.jointype & JT_OUTER)==0 && pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor) ){ #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x100 ){ SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n")); sqlite3TreeViewSelect(0, p, 0); } |
︙ | ︙ | |||
4947 4948 4949 4950 4951 4952 4953 | sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); VdbeComment((v, "%s", pItem->pTab->zName)); pItem->addrFillSub = addrTop; sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow); | | | | 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 | sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop); VdbeComment((v, "%s", pItem->pTab->zName)); pItem->addrFillSub = addrTop; sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow); pItem->fg.viaCoroutine = 1; pItem->regResult = dest.iSdst; sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn); sqlite3VdbeJumpHere(v, addrTop-1); sqlite3ClearTempRegCache(pParse); }else{ /* Generate a subroutine that will fill an ephemeral table with ** the content of this subquery. pItem->addrFillSub will point ** to the address of the generated subroutine. pItem->regReturn ** is a register allocated to hold the subroutine return address */ int topAddr; int onceAddr = 0; int retAddr; assert( pItem->addrFillSub==0 ); pItem->regReturn = ++pParse->nMem; topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn); pItem->addrFillSub = topAddr+1; if( pItem->fg.isCorrelated==0 ){ /* If the subquery is not correlated and if we are not inside of ** a trigger, then we only need to compute the value of the subquery ** once. */ onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v); VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName)); }else{ VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName)); |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); } return 0; } /* ** Display scan stats. */ | > > | 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 | iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); fprintf(pArg->out, "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur); } /* Do not remove this machine readable comment: extra-stats-output-here */ return 0; } /* ** Display scan stats. */ |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
3369 3370 3371 3372 3373 3374 3375 | /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using | | > | 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 | /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using ** [sqlite3_step(S)] but has neither run to completion (returned ** [SQLITE_DONE] from [sqlite3_step(S)]) nor ** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) ** interface returns false if S is a NULL pointer. If S is not a ** NULL pointer and is not a pointer to a valid [prepared statement] ** object, then the behavior is undefined and probably undesirable. ** ** This interface can be used in combination [sqlite3_next_stmt()] ** to locate all prepared statements associated with a database |
︙ | ︙ | |||
4531 4532 4533 4534 4535 4536 4537 | ** Refer to the [SQL parameter] documentation for additional information. ** ** ^The sqlite3_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** | | | | | 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 | ** Refer to the [SQL parameter] documentation for additional information. ** ** ^The sqlite3_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** ** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N) ** interfaces set the result of the application-defined function to be ** a BLOB containing all zero bytes and N bytes in size. ** ** ^The sqlite3_result_double() interface sets the result from ** an application-defined function to be a floating point value specified ** by its 2nd argument. ** ** ^The sqlite3_result_error() and sqlite3_result_error16() functions ** cause the implemented SQL function to throw an exception. |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
183 184 185 186 187 188 189 | # define SQLITE_NOINLINE __declspec(noinline) #else # define SQLITE_NOINLINE #endif /* ** Make sure that the compiler intrinsics we desire are enabled when | | > > | | | | | | | > | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | # define SQLITE_NOINLINE __declspec(noinline) #else # define SQLITE_NOINLINE #endif /* ** Make sure that the compiler intrinsics we desire are enabled when ** compiling with an appropriate version of MSVC unless prevented by ** the SQLITE_DISABLE_INTRINSIC define. */ #if !defined(SQLITE_DISABLE_INTRINSIC) # if defined(_MSC_VER) && _MSC_VER>=1300 # if !defined(_WIN32_WCE) # include <intrin.h> # pragma intrinsic(_byteswap_ushort) # pragma intrinsic(_byteswap_ulong) # else # include <cmnintrin.h> # endif # endif #endif /* ** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. ** 0 means mutexes are permanently disable and the library is never ** threadsafe. 1 means the library is serialized which is the highest |
︙ | ︙ | |||
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 | ** hash table. */ struct Module { const sqlite3_module *pModule; /* Callback pointers */ const char *zName; /* Name passed to create_module() */ void *pAux; /* pAux passed to create_module() */ void (*xDestroy)(void *); /* Module destructor function */ }; /* ** information about each column of an SQL table is held in an instance ** of this structure. */ struct Column { | > | 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 | ** hash table. */ struct Module { const sqlite3_module *pModule; /* Callback pointers */ const char *zName; /* Name passed to create_module() */ void *pAux; /* pAux passed to create_module() */ void (*xDestroy)(void *); /* Module destructor function */ Table *pEpoTab; /* Eponymous table for this module */ }; /* ** information about each column of an SQL table is held in an instance ** of this structure. */ struct Column { |
︙ | ︙ | |||
1643 1644 1645 1646 1647 1648 1649 | u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE int nModuleArg; /* Number of arguments to the module */ | | | 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 | u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */ VTable *pVTable; /* List of VTable objects. */ #endif Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */ }; |
︙ | ︙ | |||
2278 2279 2280 2281 2282 2283 2284 | char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pTab; /* An SQL table corresponding to zName */ Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ int regResult; /* Registers holding results of a co-routine */ | > | | > > | | | > > | > > | | 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 | char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pTab; /* An SQL table corresponding to zName */ Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ int regResult; /* Registers holding results of a co-routine */ struct { u8 jointype; /* Type of join between this able and the previous */ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */ unsigned isTabFunc :1; /* True if table-valued-function syntax */ unsigned isCorrelated :1; /* True if sub-query is correlated */ unsigned viaCoroutine :1; /* Implemented as a co-routine */ unsigned isRecursive :1; /* True for recursive reference in WITH */ } fg; #ifndef SQLITE_OMIT_EXPLAIN u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ IdList *pUsing; /* The USING clause of a join */ Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */ union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ } u1; Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */ } a[1]; /* One entry for each identifier on the list */ }; /* ** Permitted values of the SrcList.a.jointype field */ #define JT_INNER 0x0001 /* Any kind of inner or cross join */ |
︙ | ︙ | |||
3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 | int sqlite3InitCallback(void*, int, char**, char**); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); void sqlite3ResetAllSchemasOfConnection(sqlite3*); void sqlite3ResetOneSchema(sqlite3*,int); void sqlite3CollapseDatabaseArray(sqlite3*); void sqlite3BeginParse(Parse*,int); void sqlite3CommitInternalChanges(sqlite3*); Table *sqlite3ResultSetOfSelect(Parse*,Select*); void sqlite3OpenMasterTable(Parse *, int); Index *sqlite3PrimaryKeyIndex(Table*); i16 sqlite3ColumnOfIndex(Index*, i16); void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int); void sqlite3AddColumn(Parse*,Token*); void sqlite3AddNotNull(Parse*, int); | > | 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 | int sqlite3InitCallback(void*, int, char**, char**); void sqlite3Pragma(Parse*,Token*,Token*,Token*,int); void sqlite3ResetAllSchemasOfConnection(sqlite3*); void sqlite3ResetOneSchema(sqlite3*,int); void sqlite3CollapseDatabaseArray(sqlite3*); void sqlite3BeginParse(Parse*,int); void sqlite3CommitInternalChanges(sqlite3*); void sqlite3DeleteColumnNames(sqlite3*,Table*); Table *sqlite3ResultSetOfSelect(Parse*,Select*); void sqlite3OpenMasterTable(Parse *, int); Index *sqlite3PrimaryKeyIndex(Table*); i16 sqlite3ColumnOfIndex(Index*, i16); void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int); void sqlite3AddColumn(Parse*,Token*); void sqlite3AddNotNull(Parse*, int); |
︙ | ︙ | |||
3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 | IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, Expr*, IdList*); void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); void sqlite3SrcListShiftJoinType(SrcList*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(sqlite3*, IdList*); void sqlite3SrcListDelete(sqlite3*, SrcList*); Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, | > | 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 | IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*); int sqlite3IdListIndex(IdList*,const char*); SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, Expr*, IdList*); void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*); int sqlite3IndexedByLookup(Parse *, struct SrcList_item *); void sqlite3SrcListShiftJoinType(SrcList*); void sqlite3SrcListAssignCursors(Parse*, SrcList*); void sqlite3IdListDelete(sqlite3*, IdList*); void sqlite3SrcListDelete(sqlite3*, SrcList*); Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, |
︙ | ︙ | |||
3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 | void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); int sqlite3ResolveExprNames(NameContext*, Expr*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); | > | 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 | void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); int sqlite3CodeSubselect(Parse *, Expr *, int, int); void sqlite3SelectPrep(Parse*, Select*, NameContext*); void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); int sqlite3ResolveExprNames(NameContext*, Expr*); int sqlite3ResolveExprListNames(NameContext*, ExprList*); void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); void sqlite3ColumnDefault(Vdbe *, Table *, int, int); void sqlite3AlterFinishAddColumn(Parse *, Token *); void sqlite3AlterBeginAddColumn(Parse *, SrcList *); CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*); |
︙ | ︙ | |||
3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 | void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*); VTable *sqlite3GetVTable(sqlite3*, Table*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); void sqlite3VtabFinishParse(Parse*, Token*); void sqlite3VtabArgInit(Parse*); void sqlite3VtabArgExtend(Parse*, Token*); int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); int sqlite3VtabCallConnect(Parse*, Table*); | > > | 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 | void sqlite3VtabUnlock(VTable *); void sqlite3VtabUnlockList(sqlite3*); int sqlite3VtabSavepoint(sqlite3 *, int, int); void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*); VTable *sqlite3GetVTable(sqlite3*, Table*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif int sqlite3VtabEponymousTableInit(Parse*,Module*); void sqlite3VtabEponymousTableClear(sqlite3*,Module*); void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); void sqlite3VtabFinishParse(Parse*, Token*); void sqlite3VtabArgInit(Parse*); void sqlite3VtabArgExtend(Parse*, Token*); int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); int sqlite3VtabCallConnect(Parse*, Table*); |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
334 335 336 337 338 339 340 | char **argv /* Text of each argument */ ){ sqlite3 *db; Tcl_DString str; int rc, i, j; char *zErr = 0; char *zHex; | | | | 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 | char **argv /* Text of each argument */ ){ sqlite3 *db; Tcl_DString str; int rc, i, j; char *zErr = 0; char *zHex; char zSql[501]; char zBuf[30]; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB HEX", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; zHex = argv[2]; for(i=j=0; i<(sizeof(zSql)-1) && zHex[j]; i++, j++){ if( zHex[j]=='%' && zHex[j+2] && zHex[j+2] ){ zSql[i] = (testHexToInt(zHex[j+1])<<4) + testHexToInt(zHex[j+2]); j += 2; }else{ zSql[i] = zHex[j]; } } |
︙ | ︙ | |||
6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 | extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fts5_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "closure", sqlite3_closure_init }, { "eval", sqlite3_eval_init }, #ifdef SQLITE_ENABLE_FTS5 { "fts5", sqlite3_fts5_init }, #endif { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, { "regexp", sqlite3_regexp_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, { "wholenumber", sqlite3_wholenumber_init }, }; sqlite3 *db; const char *zName; int i, j, rc; | > > | 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 | extern int sqlite3_eval_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_fts5_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); } aExtension[] = { { "amatch", sqlite3_amatch_init }, { "closure", sqlite3_closure_init }, { "eval", sqlite3_eval_init }, #ifdef SQLITE_ENABLE_FTS5 { "fts5", sqlite3_fts5_init }, #endif { "fileio", sqlite3_fileio_init }, { "fuzzer", sqlite3_fuzzer_init }, { "ieee754", sqlite3_ieee_init }, { "nextchar", sqlite3_nextchar_init }, { "percentile", sqlite3_percentile_init }, { "regexp", sqlite3_regexp_init }, { "series", sqlite3_series_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, { "wholenumber", sqlite3_wholenumber_init }, }; sqlite3 *db; const char *zName; int i, j, rc; |
︙ | ︙ |
Changes to src/treeview.c.
︙ | ︙ | |||
116 117 118 119 120 121 122 | } if( pItem->pTab ){ sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName); } if( pItem->zAlias ){ sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias); } | | > > > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | } if( pItem->pTab ){ sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName); } if( pItem->zAlias ){ sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias); } if( pItem->fg.jointype & JT_LEFT ){ sqlite3XPrintf(&x, 0, " LEFT-JOIN"); } sqlite3StrAccumFinish(&x); sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1); if( pItem->pSelect ){ sqlite3TreeViewSelect(pView, pItem->pSelect, 0); } if( pItem->fg.isTabFunc ){ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); } sqlite3TreeViewPop(pView); } sqlite3TreeViewPop(pView); } if( p->pWhere ){ sqlite3TreeViewItem(pView, "WHERE", (n--)>0); sqlite3TreeViewExpr(pView, p->pWhere, 0); |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
1078 1079 1080 1081 1082 1083 1084 | ** Read or write a four-byte big-endian integer value. */ u32 sqlite3Get4byte(const u8 *p){ #if SQLITE_BYTEORDER==4321 u32 x; memcpy(&x,p,4); return x; | > | > | | 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 | ** Read or write a four-byte big-endian integer value. */ u32 sqlite3Get4byte(const u8 *p){ #if SQLITE_BYTEORDER==4321 u32 x; memcpy(&x,p,4); return x; #elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ && defined(__GNUC__) && GCC_VERSION>=4003000 u32 x; memcpy(&x,p,4); return __builtin_bswap32(x); #elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ && defined(_MSC_VER) && _MSC_VER>=1300 u32 x; memcpy(&x,p,4); return _byteswap_ulong(x); #else testcase( p[0]&0x80 ); return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; #endif |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
567 568 569 570 571 572 573 | assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ goto no_mem; } | | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ goto no_mem; } assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY ); assert( p->bIsReader || p->readOnly!=0 ); p->rc = SQLITE_OK; p->iCurrentTime = 0; assert( p->explain==0 ); p->pResultSet = 0; db->busyHandler.nBusy = 0; if( db->u1.isInterrupted ) goto abort_due_to_interrupt; |
︙ | ︙ | |||
2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 | }else{ /* Determine whether or not this is a transaction savepoint. If so, ** and this is a RELEASE command, then the current transaction ** is committed. */ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; if( isTransaction && p1==SAVEPOINT_RELEASE ){ if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; } | > < | 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 | }else{ /* Determine whether or not this is a transaction savepoint. If so, ** and this is a RELEASE command, then the current transaction ** is committed. */ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint; assert( db->bUnlocked==0 || db->isTransactionSavepoint==0 ); if( isTransaction && p1==SAVEPOINT_RELEASE ){ if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = (int)(pOp - aOp); db->autoCommit = 0; p->rc = rc = SQLITE_BUSY; goto vdbe_return; } |
︙ | ︙ | |||
2997 2998 2999 3000 3001 3002 3003 | assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); assert( desiredAutoCommit==1 || iRollback==0 ); assert( desiredAutoCommit==0 || bUnlocked==0 ); assert( db->autoCommit==0 || db->bUnlocked==0 ); assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( p->bIsReader ); | | | > > > > | > | < > > | | | | | | | | | < | 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 | assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); assert( desiredAutoCommit==1 || iRollback==0 ); assert( desiredAutoCommit==0 || bUnlocked==0 ); assert( db->autoCommit==0 || db->bUnlocked==0 ); assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( p->bIsReader ); if( turnOnAC && !iRollback && (db->nVdbeWrite>0 || (db->bUnlocked && db->nVdbeActive>1)) ){ /* A transaction may only be committed if there are no other active ** writer VMs. If the transaction is UNLOCKED, then it may only be ** committed if there are no active VMs at all (readers or writers). ** ** If this instruction is a COMMIT and the transaction may not be ** committed due to one of the conditions above, return an error ** indicating that other VMs must complete before the COMMIT can ** be processed. */ sqlite3VdbeError(p, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; }else if( desiredAutoCommit!=db->autoCommit ){ if( iRollback ){ assert( desiredAutoCommit==1 ); sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); db->autoCommit = 1; db->bUnlocked = 0; }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ goto vdbe_return; }else{ db->autoCommit = (u8)desiredAutoCommit; } hrc = sqlite3VdbeHalt(p); if( (hrc & 0xFF)==SQLITE_BUSY ){ p->pc = (int)(pOp - aOp); db->autoCommit = (u8)(1-desiredAutoCommit); p->rc = hrc; rc = SQLITE_BUSY; goto vdbe_return; } db->bUnlocked = (u8)bUnlocked; assert( db->nStatement==0 ); sqlite3CloseSavepoints(db); if( p->rc==SQLITE_OK ){ rc = SQLITE_DONE; }else{ rc = SQLITE_ERROR; } |
︙ | ︙ | |||
3096 3097 3098 3099 3100 3101 3102 | rc = SQLITE_READONLY; goto abort_due_to_error; } pBt = db->aDb[pOp->p1].pBt; if( pBt ){ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); | > > | | | 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 | rc = SQLITE_READONLY; goto abort_due_to_error; } pBt = db->aDb[pOp->p1].pBt; if( pBt ){ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); testcase( rc==SQLITE_BUSY_SNAPSHOT ); testcase( rc==SQLITE_BUSY_RECOVERY ); if( (rc&0xff)==SQLITE_BUSY ){ p->pc = (int)(pOp - aOp); p->rc = rc; goto vdbe_return; } if( rc!=SQLITE_OK ){ goto abort_due_to_error; } if( pOp->p2 && p->usesStmtJournal |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
607 608 609 610 611 612 613 | ** returned if this statement was compiled using the legacy ** sqlite3_prepare() interface. According to the docs, this can only ** be one of the values in the first assert() below. Variable p->rc ** contains the value that would be returned if sqlite3_finalize() ** were called on statement p. */ assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR | | | 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | ** returned if this statement was compiled using the legacy ** sqlite3_prepare() interface. According to the docs, this can only ** be one of the values in the first assert() below. Variable p->rc ** contains the value that would be returned if sqlite3_finalize() ** were called on statement p. */ assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ /* If this statement was prepared using sqlite3_prepare_v2(), and an ** error has occurred, then return the error code in p->rc to the ** caller. Set the error code in the database handle to the same value. */ |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1090 1091 1092 1093 1094 1095 1096 | CollSeq *pColl = pKeyInfo->aColl[j]; const char *zColl = pColl ? pColl->zName : "nil"; int n = sqlite3Strlen30(zColl); if( n==6 && memcmp(zColl,"BINARY",6)==0 ){ zColl = "B"; n = 1; } | | > | 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 | CollSeq *pColl = pKeyInfo->aColl[j]; const char *zColl = pColl ? pColl->zName : "nil"; int n = sqlite3Strlen30(zColl); if( n==6 && memcmp(zColl,"BINARY",6)==0 ){ zColl = "B"; n = 1; } if( i+n>nTemp-7 ){ memcpy(&zTemp[i],",...",4); i += 4; break; } zTemp[i++] = ','; if( pKeyInfo->aSortOrder[j] ){ zTemp[i++] = '-'; } memcpy(&zTemp[i], zColl, n+1); |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | Module *pDel; char *zCopy = (char *)(&pMod[1]); memcpy(zCopy, zName, nName+1); pMod->zName = zCopy; pMod->pModule = pModule; pMod->pAux = pAux; pMod->xDestroy = xDestroy; pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod); assert( pDel==0 || pDel==pMod ); if( pDel ){ db->mallocFailed = 1; sqlite3DbFree(db, pDel); } } | > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | Module *pDel; char *zCopy = (char *)(&pMod[1]); memcpy(zCopy, zName, nName+1); pMod->zName = zCopy; pMod->pModule = pModule; pMod->pAux = pAux; pMod->xDestroy = xDestroy; pMod->pEpoTab = 0; pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod); assert( pDel==0 || pDel==pMod ); if( pDel ){ db->mallocFailed = 1; sqlite3DbFree(db, pDel); } } |
︙ | ︙ | |||
281 282 283 284 285 286 287 | /* ** Add a new module argument to pTable->azModuleArg[]. ** The string is not copied - the pointer is stored. The ** string will be freed automatically when the table is ** deleted. */ static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ | < | < < < < < < > < | > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | /* ** Add a new module argument to pTable->azModuleArg[]. ** The string is not copied - the pointer is stored. The ** string will be freed automatically when the table is ** deleted. */ static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ int nBytes = sizeof(char *)*(2+pTable->nModuleArg); char **azModuleArg; azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); if( azModuleArg==0 ){ sqlite3DbFree(db, zArg); }else{ int i = pTable->nModuleArg++; azModuleArg[i] = zArg; azModuleArg[i+1] = 0; pTable->azModuleArg = azModuleArg; } } /* ** The parser calls this routine when it first sees a CREATE VIRTUAL TABLE ** statement. The module name has been parsed, but the optional list ** of parameters that follow the module name are still pending. */ |
︙ | ︙ | |||
700 701 702 703 704 705 706 | zMod = pTab->azModuleArg[0]; pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an ** error. Otherwise, do nothing. */ | | | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 | zMod = pTab->azModuleArg[0]; pMod = (Module*)sqlite3HashFind(&db->aModule, zMod); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an ** error. Otherwise, do nothing. */ if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){ *pzErr = sqlite3MPrintf(db, "no such module: %s", zMod); rc = SQLITE_ERROR; }else{ rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); } /* Justification of ALWAYS(): The xConstructor method is required to |
︙ | ︙ | |||
802 803 804 805 806 807 808 809 810 811 812 813 814 815 | int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ int rc = SQLITE_OK; Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ VTable *p; for(p=pTab->pVTable; p; p=p->pNext){ assert( p->pVtab ); if( p->pVtab->nRef>0 ){ return SQLITE_LOCKED; } } p = vtabDisconnectAll(db, pTab); | > | > > | 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab){ int rc = SQLITE_OK; Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ VTable *p; int (*xDestroy)(sqlite3_vtab *); for(p=pTab->pVTable; p; p=p->pNext){ assert( p->pVtab ); if( p->pVtab->nRef>0 ){ return SQLITE_LOCKED; } } p = vtabDisconnectAll(db, pTab); xDestroy = p->pMod->pModule->xDestroy; assert( xDestroy!=0 ); /* Checked before the virtual table is created */ rc = xDestroy(p->pVtab); /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ assert( pTab->pVTable==p && p->pNext==0 ); p->pVtab = 0; pTab->pVTable = 0; sqlite3VtabUnlock(p); } |
︙ | ︙ | |||
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | if( apVtabLock ){ pToplevel->apVtabLock = apVtabLock; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; }else{ pToplevel->db->mallocFailed = 1; } } /* ** Return the ON CONFLICT resolution mode in effect for the virtual ** table update operation currently in progress. ** ** The results of this routine are undefined unless it is called from ** within an xUpdate method. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 | if( apVtabLock ){ pToplevel->apVtabLock = apVtabLock; pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; }else{ pToplevel->db->mallocFailed = 1; } } /* ** Check to see if virtual tale module pMod can be have an eponymous ** virtual table instance. If it can, create one if one does not already ** exist. Return non-zero if the eponymous virtual table instance exists ** when this routine returns, and return zero if it does not exist. ** ** An eponymous virtual table instance is one that is named after its ** module, and more importantly, does not require a CREATE VIRTUAL TABLE ** statement in order to come into existance. Eponymous virtual table ** instances always exist. They cannot be DROP-ed. ** ** Any virtual table module for which xConnect and xCreate are the same ** method can have an eponymous virtual table instance. */ int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){ const sqlite3_module *pModule = pMod->pModule; Table *pTab; char *zErr = 0; int nName; int rc; sqlite3 *db = pParse->db; if( pMod->pEpoTab ) return 1; if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0; nName = sqlite3Strlen30(pMod->zName) + 1; pTab = sqlite3DbMallocZero(db, sizeof(Table) + nName); if( pTab==0 ) return 0; pMod->pEpoTab = pTab; pTab->zName = (char*)&pTab[1]; memcpy(pTab->zName, pMod->zName, nName); pTab->nRef = 1; pTab->pSchema = db->aDb[0].pSchema; pTab->tabFlags |= TF_Virtual; pTab->nModuleArg = 0; pTab->iPKey = -1; addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); addModuleArgument(db, pTab, 0); addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr); if( rc ){ sqlite3ErrorMsg(pParse, "%s", zErr); sqlite3DbFree(db, zErr); sqlite3VtabEponymousTableClear(db, pMod); return 0; } return 1; } /* ** Erase the eponymous virtual table instance associated with ** virtual table module pMod, if it exists. */ void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){ Table *pTab = pMod->pEpoTab; if( (pTab = pMod->pEpoTab)!=0 ){ sqlite3DeleteColumnNames(db, pTab); sqlite3VtabClear(db, pTab); sqlite3DbFree(db, pTab); pMod->pEpoTab = 0; } } /* ** Return the ON CONFLICT resolution mode in effect for the virtual ** table update operation currently in progress. ** ** The results of this routine are undefined unless it is called from ** within an xUpdate method. |
︙ | ︙ |
Changes to src/walker.c.
︙ | ︙ | |||
100 101 102 103 104 105 106 107 108 109 110 111 112 113 | struct SrcList_item *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){ return WRC_Abort; } } } return WRC_Continue; } /* | > > > > > | 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | struct SrcList_item *pItem; pSrc = p->pSrc; if( ALWAYS(pSrc) ){ for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){ if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){ return WRC_Abort; } if( pItem->fg.isTabFunc && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg) ){ return WRC_Abort; } } } return WRC_Continue; } /* |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
705 706 707 708 709 710 711 | sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ sqlite3ExprCachePush(pParse); pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; | | | | | 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "for %s", pTable->zName)); /* Fill the automatic index with content */ sqlite3ExprCachePush(pParse); pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom]; if( pTabItem->fg.viaCoroutine ){ int regYield = pTabItem->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield); VdbeCoverage(v); VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName)); }else{ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } if( pPartial ){ iContinue = sqlite3VdbeMakeLabel(v); sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL); pLoop->wsFlags |= WHERE_PARTIALIDX; } regRecord = sqlite3GetTempReg(pParse); sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0); sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue); if( pTabItem->fg.viaCoroutine ){ translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); pTabItem->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX); sqlite3VdbeJumpHere(v, addrTop); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3ExprCachePop(pParse); |
︙ | ︙ | |||
2124 2125 2126 2127 2128 2129 2130 | pNew = pBuilder->pNew; if( db->mallocFailed ) return SQLITE_NOMEM; assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; | | | 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 | pNew = pBuilder->pNew; if( db->mallocFailed ) return SQLITE_NOMEM; assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 ); assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 ); if( pNew->wsFlags & WHERE_BTM_LIMIT ){ opMask = WO_LT|WO_LE; }else if( /*pProbe->tnum<=0 ||*/ (pSrc->fg.jointype & JT_LEFT)!=0 ){ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE; }else{ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; } if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE); assert( pNew->u.btree.nEq<pProbe->nColumn ); |
︙ | ︙ | |||
2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 | /* Check to see if a partial index with pPartIndexWhere can be used ** in the current query. Return true if it can be and false if not. */ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr = pTerm->pExpr; if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab) && (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) ){ return 1; } | > > > > | 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 | /* Check to see if a partial index with pPartIndexWhere can be used ** in the current query. Return true if it can be and false if not. */ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){ int i; WhereTerm *pTerm; while( pWhere->op==TK_AND ){ if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0; pWhere = pWhere->pRight; } for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){ Expr *pExpr = pTerm->pExpr; if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab) && (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab) ){ return 1; } |
︙ | ︙ | |||
2494 2495 2496 2497 2498 2499 2500 | pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; pTab = pSrc->pTab; pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pTab) ); | | | | | | | | | | | 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 | pWInfo = pBuilder->pWInfo; pTabList = pWInfo->pTabList; pSrc = pTabList->a + pNew->iTab; pTab = pSrc->pTab; pWC = pBuilder->pWC; assert( !IsVirtual(pSrc->pTab) ); if( pSrc->pIBIndex ){ /* An INDEXED BY clause specifies a particular index to use */ pProbe = pSrc->pIBIndex; }else if( !HasRowid(pTab) ){ pProbe = pTab->pIndex; }else{ /* There is no INDEXED BY clause. Create a fake Index object in local ** variable sPk to represent the rowid primary key index. Make this ** fake index the first in a chain of Index objects with all of the real ** indices to follow */ Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nKeyCol = 1; sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowLogEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = pTab->szTabRow; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; pFirst = pSrc->pTab->pIndex; if( pSrc->fg.notIndexed==0 ){ /* The real indices of the table are only considered if the ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; } rSize = pTab->nRowLogEst; rLogSize = estLog(rSize); #ifndef SQLITE_OMIT_AUTOMATIC_INDEX /* Automatic indexes */ if( !pBuilder->pOrSet /* Not part of an OR optimization */ && (pWInfo->wctrlFlags & WHERE_NO_AUTOINDEX)==0 && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0 && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */ && HasRowid(pTab) /* Is not a WITHOUT ROWID table. (FIXME: Why not?) */ && !pSrc->fg.isCorrelated /* Not a correlated subquery */ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */ ){ /* Generate auto-index WhereLoops */ WhereTerm *pTerm; WhereTerm *pWCEnd = pWC->a + pWC->nTerm; for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){ if( pTerm->prereqRight & pNew->maskSelf ) continue; if( termCanDriveIndex(pTerm, pSrc, 0) ){ |
︙ | ︙ | |||
2656 2657 2658 2659 2660 2661 2662 | sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif /* If there was an INDEXED BY clause, then only that one index is ** considered. */ | | | 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 | sqlite3Stat4ProbeFree(pBuilder->pRec); pBuilder->nRecValid = 0; pBuilder->pRec = 0; #endif /* If there was an INDEXED BY clause, then only that one index is ** considered. */ if( pSrc->pIBIndex ) break; } return rc; } #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Add all WhereLoop objects for a table of the join identified by |
︙ | ︙ | |||
3002 3003 3004 3005 3006 3007 3008 | /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); | | | | | 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 | /* Loop over the tables in the join, from left to right */ pNew = pBuilder->pNew; whereLoopInit(pNew); for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){ Bitmask mUnusable = 0; pNew->iTab = iTab; pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){ /* This condition is true when pItem is the FROM clause term on the ** right-hand-side of a LEFT or CROSS JOIN. */ mExtra = mPrior; } priorJointype = pItem->fg.jointype; if( IsVirtual(pItem->pTab) ){ struct SrcList_item *p; for(p=&pItem[1]; p<pEnd; p++){ if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor); } } rc = whereLoopAddVirtual(pBuilder, mExtra, mUnusable); }else{ rc = whereLoopAddBtree(pBuilder, mExtra); } |
︙ | ︙ | |||
3741 3742 3743 3744 3745 3746 3747 | pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if( IsVirtual(pTab) ) return 0; | | | 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 | pWInfo = pBuilder->pWInfo; if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0; assert( pWInfo->pTabList->nSrc>=1 ); pItem = pWInfo->pTabList->a; pTab = pItem->pTab; if( IsVirtual(pTab) ) return 0; if( pItem->fg.isIndexedBy ) return 0; iCur = pItem->iCursor; pWC = &pWInfo->sWC; pLoop = pBuilder->pNew; pLoop->wsFlags = 0; pLoop->nSkip = 0; pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0); if( pTerm ){ |
︙ | ︙ | |||
4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 | ** Note that bitmasks are created for all pTabList->nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the ** WHERE_ONETABLE_ONLY flag is set. */ for(ii=0; ii<pTabList->nSrc; ii++){ createMask(pMaskSet, pTabList->a[ii].iCursor); } #ifndef NDEBUG { Bitmask toTheLeft = 0; for(ii=0; ii<pTabList->nSrc; ii++){ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); assert( (m-1)==toTheLeft ); | > | 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 | ** Note that bitmasks are created for all pTabList->nSrc tables in ** pTabList, not just the first nTabList tables. nTabList is normally ** equal to pTabList->nSrc but might be shortened to 1 if the ** WHERE_ONETABLE_ONLY flag is set. */ for(ii=0; ii<pTabList->nSrc; ii++){ createMask(pMaskSet, pTabList->a[ii].iCursor); sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC); } #ifndef NDEBUG { Bitmask toTheLeft = 0; for(ii=0; ii<pTabList->nSrc; ii++){ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); assert( (m-1)==toTheLeft ); |
︙ | ︙ | |||
4128 4129 4130 4131 4132 4133 4134 | Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); if( sWLB.pOrderBy ){ tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); } while( pWInfo->nLevel>=2 ){ WhereTerm *pTerm, *pEnd; pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop; | | | 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 | Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pResultSet); if( sWLB.pOrderBy ){ tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); } while( pWInfo->nLevel>=2 ){ WhereTerm *pTerm, *pEnd; pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop; if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break; if( (wctrlFlags & WHERE_WANT_DISTINCT)==0 && (pLoop->wsFlags & WHERE_ONEROW)==0 ){ break; } if( (tabUsed & pLoop->maskSelf)!=0 ) break; pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; |
︙ | ︙ | |||
4421 4422 4423 4424 4425 4426 4427 | assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ | | | 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 | assert( pTab!=0 ); pLoop = pLevel->pWLoop; /* For a co-routine, change all OP_Column references to the table of ** the co-routine into OP_Copy of result contained in a register. ** OP_Rowid becomes OP_Null. */ if( pTabItem->fg.viaCoroutine && !db->mallocFailed ){ translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur, pTabItem->regResult); continue; } /* Close all of the cursors that were opened by sqlite3WhereBegin. ** Except, do not close cursors that will be reused by the OR optimization |
︙ | ︙ |
Changes to src/whereInt.h.
︙ | ︙ | |||
471 472 473 474 475 476 477 478 479 480 481 482 483 484 | /* whereexpr.c: */ void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); void sqlite3WhereClauseClear(WhereClause*); void sqlite3WhereSplit(WhereClause*,Expr*,u8); Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); void sqlite3WhereExprAnalyze(SrcList*, WhereClause*); /* ** Bitmasks for the operators on WhereTerm objects. These are all | > | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | /* whereexpr.c: */ void sqlite3WhereClauseInit(WhereClause*,WhereInfo*); void sqlite3WhereClauseClear(WhereClause*); void sqlite3WhereSplit(WhereClause*,Expr*,u8); Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*); Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*); void sqlite3WhereExprAnalyze(SrcList*, WhereClause*); void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*); /* ** Bitmasks for the operators on WhereTerm objects. These are all |
︙ | ︙ |
Changes to src/wherecode.c.
︙ | ︙ | |||
642 643 644 645 646 647 648 | addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ | | | | 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 | addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any ** row of the left table of the join. */ if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } /* Special case of a FROM clause subquery implemented as a co-routine */ if( pTabItem->fg.viaCoroutine ){ int regYield = pTabItem->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub); pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName)); pLevel->op = OP_Goto; }else |
︙ | ︙ | |||
1391 1392 1393 1394 1395 1396 1397 | { /* Case 6: There is no usable index. We must do a complete ** scan of the entire table. */ static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); | | | 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 | { /* Case 6: There is no usable index. We must do a complete ** scan of the entire table. */ static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; assert( bRev==0 || bRev==1 ); if( pTabItem->fg.isRecursive ){ /* Tables marked isRecursive have only a single row that is stored in ** a pseudo-cursor. No need to Rewind or Next such cursors. */ pLevel->op = OP_Noop; }else{ pLevel->op = aStep[bRev]; pLevel->p1 = iCur; pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk); |
︙ | ︙ |
Changes to src/whereexpr.c.
︙ | ︙ | |||
1243 1244 1245 1246 1247 1248 1249 | WhereClause *pWC /* the WHERE clause to be analyzed */ ){ int i; for(i=pWC->nTerm-1; i>=0; i--){ exprAnalyze(pTabList, pWC, i); } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 | WhereClause *pWC /* the WHERE clause to be analyzed */ ){ int i; for(i=pWC->nTerm-1; i>=0; i--){ exprAnalyze(pTabList, pWC, i); } } /* ** For table-valued-functions, transform the function arguments into ** new WHERE clause terms. ** ** Each function argument translates into an equality constraint against ** a HIDDEN column in the table. */ void sqlite3WhereTabFuncArgs( Parse *pParse, /* Parsing context */ struct SrcList_item *pItem, /* The FROM clause term to process */ WhereClause *pWC /* Xfer function arguments to here */ ){ Table *pTab; int j, k; ExprList *pArgs; Expr *pColRef; Expr *pTerm; if( pItem->fg.isTabFunc==0 ) return; pTab = pItem->pTab; assert( pTab!=0 ); pArgs = pItem->u1.pFuncArg; assert( pArgs!=0 ); for(j=k=0; j<pArgs->nExpr; j++){ while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){ k++; } if( k>=pTab->nCol ){ sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d", pTab->zName, j); return; } pColRef = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0); if( pColRef==0 ) return; pColRef->iTable = pItem->iCursor; pColRef->iColumn = k++; pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef, sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0); whereClauseInsert(pWC, pTerm, TERM_DYNAMIC); } } |
Changes to test/capi3d.test.
︙ | ︙ | |||
157 158 159 160 161 162 163 | do_test capi3d-4.2.1 { set ::s1 [sqlite3_prepare_v2 db "ROLLBACK" -1 notused] sqlite3_step $::s1 } {SQLITE_DONE} do_test capi3d-4.2.2 { sqlite3_stmt_busy $::s1 | | | | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | do_test capi3d-4.2.1 { set ::s1 [sqlite3_prepare_v2 db "ROLLBACK" -1 notused] sqlite3_step $::s1 } {SQLITE_DONE} do_test capi3d-4.2.2 { sqlite3_stmt_busy $::s1 } {0} do_catchsql_test capi3d-4.2.3 { VACUUM } {0 {}} do_test capi3d-4.2.4 { sqlite3_reset $::s1 } {SQLITE_OK} do_catchsql_test capi3d-4.2.5 { VACUUM |
︙ | ︙ |
Changes to test/index6.test.
︙ | ︙ | |||
341 342 343 344 345 346 347 348 349 | CREATE TABLE t9(a int, b int, c int, PRIMARY KEY(a)) WITHOUT ROWID; CREATE INDEX t9ca ON t9(c,a) WHERE a in (10,12,20); INSERT INTO t9 VALUES(1,1,9),(10,2,35),(11,15,82),(20,19,5); UPDATE t9 SET b=c WHERE a in (10,12,20); SELECT a,b,c,'|' FROM t9 ORDER BY a; } {1 1 9 | 10 35 35 | 11 15 82 | 20 5 5 |} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | CREATE TABLE t9(a int, b int, c int, PRIMARY KEY(a)) WITHOUT ROWID; CREATE INDEX t9ca ON t9(c,a) WHERE a in (10,12,20); INSERT INTO t9 VALUES(1,1,9),(10,2,35),(11,15,82),(20,19,5); UPDATE t9 SET b=c WHERE a in (10,12,20); SELECT a,b,c,'|' FROM t9 ORDER BY a; } {1 1 9 | 10 35 35 | 11 15 82 | 20 5 5 |} # AND-connected terms in the WHERE clause of a partial index # do_execsql_test index6-10.1 { CREATE TABLE t10(a,b,c,d,e INTEGER PRIMARY KEY); INSERT INTO t10 VALUES (1,2,3,4,5), (2,3,4,5,6), (3,4,5,6,7), (1,2,3,8,9); CREATE INDEX t10x ON t10(d) WHERE a=1 AND b=2 AND c=3; SELECT e FROM t10 WHERE a=1 AND b=2 AND c=3 ORDER BY d; } {5 9} do_execsql_test index6-10.1eqp { EXPLAIN QUERY PLAN SELECT e FROM t10 WHERE a=1 AND b=2 AND c=3 ORDER BY d; } {/USING INDEX t10x/} do_execsql_test index6-10.2 { SELECT e FROM t10 WHERE c=3 AND 2=b AND a=1 ORDER BY d DESC; } {9 5} do_execsql_test index6-10.2eqp { EXPLAIN QUERY PLAN SELECT e FROM t10 WHERE c=3 AND 2=b AND a=1 ORDER BY d DESC; } {/USING INDEX t10x/} do_execsql_test index6-10.3 { SELECT e FROM t10 WHERE a=1 AND b=2 ORDER BY d DESC; } {9 5} do_execsql_test index6-10.3eqp { EXPLAIN QUERY PLAN SELECT e FROM t10 WHERE a=1 AND b=2 ORDER BY d DESC; } {~/USING INDEX t10x/} finish_test |
Changes to test/releasetest.tcl.
︙ | ︙ | |||
282 283 284 285 286 287 288 289 290 291 292 293 294 295 | if {0==[info exists ::Configs($v)]} { puts stderr "No such configuration: \"$v\"" exit -1 } } } # Open the file $logfile and look for a report on the number of errors # and the number of test cases run. Add these values to the global # $::NERRCASE and $::NTESTCASE variables. # # If any errors occur, then write into $errmsgVar the text of an appropriate # one-line error message to show on the output. # | > > > > > > > > > > > > > > > > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | if {0==[info exists ::Configs($v)]} { puts stderr "No such configuration: \"$v\"" exit -1 } } } # Output log # set LOG [open releasetest-out.txt w] proc PUTS {args} { if {[llength $args]==2} { puts [lindex $args 0] [lindex $args 1] puts [lindex $args 0] $::LOG [lindex $args 1] } else { puts [lindex $args 0] puts $::LOG [lindex $args 0] } } puts $LOG "$argv0 $argv" set tm0 [clock format [clock seconds] -format {%Y-%m-%d %H:%M:%S} -gmt 1] puts $LOG "start-time: $tm0 UTC" # Open the file $logfile and look for a report on the number of errors # and the number of test cases run. Add these values to the global # $::NERRCASE and $::NTESTCASE variables. # # If any errors occur, then write into $errmsgVar the text of an appropriate # one-line error message to show on the output. # |
︙ | ︙ | |||
306 307 308 309 310 311 312 | set seen 1 if {$nerr>0} { set rc 1 set errmsg $line } } if {[regexp {runtime error: +(.*)} $line all msg]} { | > > > > | | | | > | 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | set seen 1 if {$nerr>0} { set rc 1 set errmsg $line } } if {[regexp {runtime error: +(.*)} $line all msg]} { # skip over "value is outside range" errors if {[regexp {value .* is outside the range of representable} $line]} { # noop } else { incr ::NERRCASE if {$rc==0} { set rc 1 set errmsg $msg } } } if {[regexp {fatal error +(.*)} $line all msg]} { incr ::NERRCASE if {$rc==0} { set rc 1 set errmsg $msg |
︙ | ︙ | |||
399 400 401 402 403 404 405 | append opts " -DSQLITE_OS_WIN=1" } else { append opts " -DSQLITE_OS_UNIX=1" } if {!$::TRACE} { set n [string length $title] | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | append opts " -DSQLITE_OS_WIN=1" } else { append opts " -DSQLITE_OS_UNIX=1" } if {!$::TRACE} { set n [string length $title] PUTS -nonewline "${title}[string repeat . [expr {63-$n}]]" flush stdout } set rc 0 set tm1 [clock seconds] set origdir [pwd] trace_cmd file mkdir $dir |
︙ | ︙ | |||
424 425 426 427 428 429 430 | if {!$::TRACE} { set hours [expr {($tm2-$tm1)/3600}] set minutes [expr {(($tm2-$tm1)/60)%60}] set seconds [expr {($tm2-$tm1)%60}] set tm [format (%02d:%02d:%02d) $hours $minutes $seconds] if {$rc} { | | | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | if {!$::TRACE} { set hours [expr {($tm2-$tm1)/3600}] set minutes [expr {(($tm2-$tm1)/60)%60}] set seconds [expr {($tm2-$tm1)%60}] set tm [format (%02d:%02d:%02d) $hours $minutes $seconds] if {$rc} { PUTS " FAIL $tm" incr ::NERR } else { PUTS " Ok $tm" } if {$errmsg!=""} {PUTS " $errmsg"} } } # The following procedure returns the "configure" command to be exectued for # the current platform, which may be Windows (via MinGW, etc). # proc configureCommand {opts} { |
︙ | ︙ | |||
471 472 473 474 475 476 477 | # The following procedure prints its arguments if ::TRACE is true. # And it executes the command of its arguments in the calling context # if ::DRYRUN is false. # proc trace_cmd {args} { if {$::TRACE} { | | | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 | # The following procedure prints its arguments if ::TRACE is true. # And it executes the command of its arguments in the calling context # if ::DRYRUN is false. # proc trace_cmd {args} { if {$::TRACE} { PUTS $args } if {!$::DRYRUN} { uplevel 1 $args } } |
︙ | ︙ | |||
539 540 541 542 543 544 545 | } -trace { set ::TRACE 1 } -info { | | | | | | | | | | | | | | | | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | } -trace { set ::TRACE 1 } -info { PUTS "Command-line Options:" PUTS " --srcdir $::SRCDIR" PUTS " --platform [list $platform]" PUTS " --config [list $config]" if {$::QUICK} { if {$::QUICK==1} {PUTS " --quick"} if {$::QUICK==2} {PUTS " --veryquick"} } if {$::MSVC} {PUTS " --msvc"} if {$::BUILDONLY} {PUTS " --buildonly"} if {$::DRYRUN} {PUTS " --dryrun"} if {$::TRACE} {PUTS " --trace"} PUTS "\nAvailable --platform options:" foreach y [lsort [array names ::Platforms]] { PUTS " [list $y]" } PUTS "\nAvailable --config options:" foreach y [lsort [array names ::Configs]] { PUTS " [list $y]" } exit } -g { if {$::MSVC} { lappend ::EXTRACONFIG -Zi |
︙ | ︙ | |||
583 584 585 586 587 588 589 | -enable-* - -disable-* - *=* { lappend ::EXTRACONFIG [lindex $argv $i] } default { | | | | | | | | | | | | | | | | | | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | -enable-* - -disable-* - *=* { lappend ::EXTRACONFIG [lindex $argv $i] } default { PUTS stderr "" PUTS stderr [string trim $::USAGE_MESSAGE] exit -1 } } } if {0==[info exists ::Platforms($platform)]} { PUTS "Unknown platform: $platform" PUTS -nonewline "Set the -platform option to " set print [list] foreach p [array names ::Platforms] { lappend print "\"$p\"" } lset print end "or [lindex $print end]" PUTS "[join $print {, }]." exit } if {$config!=""} { if {[llength $config]==1} {lappend config fulltest} set ::CONFIGLIST $config } else { set ::CONFIGLIST $::Platforms($platform) } PUTS "Running the following test configurations for $platform:" PUTS " [string trim $::CONFIGLIST]" PUTS -nonewline "Flags:" if {$::DRYRUN} {PUTS -nonewline " --dryrun"} if {$::BUILDONLY} {PUTS -nonewline " --buildonly"} if {$::MSVC} {PUTS -nonewline " --msvc"} switch -- $::QUICK { 1 {PUTS -nonewline " --quick"} 2 {PUTS -nonewline " --veryquick"} } PUTS "" } # Main routine. # proc main {argv} { # Process any command line options. set ::EXTRACONFIG {} process_options $argv PUTS [string repeat * 79] set ::NERR 0 set ::NTEST 0 set ::NTESTCASE 0 set ::NERRCASE 0 set ::SQLITE_VERSION {} set STARTTIME [clock seconds] foreach {zConfig target} $::CONFIGLIST { if {$::MSVC && ($zConfig eq "Sanitize" || "checksymbols" in $target || "valgrindtest" in $target)} { PUTS "Skipping $zConfig / $target for MSVC..." continue } if {$target ne "checksymbols"} { switch -- $::QUICK { 1 {set target quicktest} 2 {set target smoketest} } |
︙ | ︙ | |||
684 685 686 687 688 689 690 | } set elapsetime [expr {[clock seconds]-$STARTTIME}] set hr [expr {$elapsetime/3600}] set min [expr {($elapsetime/60)%60}] set sec [expr {$elapsetime%60}] set etime [format (%02d:%02d:%02d) $hr $min $sec] | | | | | 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 | } set elapsetime [expr {[clock seconds]-$STARTTIME}] set hr [expr {$elapsetime/3600}] set min [expr {($elapsetime/60)%60}] set sec [expr {$elapsetime%60}] set etime [format (%02d:%02d:%02d) $hr $min $sec] PUTS [string repeat * 79] PUTS "$::NERRCASE failures out of $::NTESTCASE tests in $etime" if {$::SQLITE_VERSION ne ""} { PUTS "SQLite $::SQLITE_VERSION" } } main $argv |
Added test/spellfix2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | # 2012 July 12 # # 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix spellfix2 ifcapable !vtab { finish_test ; return } load_static_extension db spellfix nextchar do_execsql_test 1.0 { CREATE VIRTUAL TABLE demo USING spellfix1; INSERT INTO demo(word) VALUES ('amsterdam'); INSERT INTO demo(word) VALUES ('amsterdammetje'); INSERT INTO demo(word) VALUES ('amsterdamania'); INSERT INTO demo(word) VALUES ('amsterdamweg'); INSERT INTO demo(word) VALUES ('amsterdamsestraat'); INSERT INTO demo(word) VALUES ('amsterdamlaan'); } do_execsql_test 1.1 { SELECT word, distance, matchlen FROM demo WHERE word MATCH 'amstedam*' AND top=3; } { amsterdam 100 9 amsterdammetje 100 9 amsterdamania 100 9 } do_execsql_test 1.2 { SELECT word, distance, matchlen FROM demo WHERE word MATCH 'amstedam*' AND top=3 AND distance <= 100; } { amsterdam 100 9 amsterdammetje 100 9 amsterdamania 100 9 } do_execsql_test 1.3 { SELECT word, distance, matchlen FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100; } { amsterdam 100 9 amsterdammetje 100 9 amsterdamania 100 9 amsterdamweg 100 9 amsterdamsestraat 100 9 amsterdamlaan 100 9 } do_test 1.4 { foreach l {a b c d e f g h i j k l m n o p q r s t u v w x y z} { execsql { INSERT INTO demo(word) VALUES ('amsterdam' || $l) } } } {} do_execsql_test 1.5 { SELECT count(*) FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100; SELECT count(*) FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20; } { 32 20 } do_execsql_test 1.6 { SELECT word, distance, matchlen FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100; } { amsterdam 100 9 amsterdamh 100 9 amsterdamm 100 9 amsterdamn 100 9 amsterdama 100 9 amsterdame 100 9 amsterdami 100 9 amsterdamo 100 9 amsterdamu 100 9 amsterdamy 100 9 amsterdammetje 100 9 amsterdamania 100 9 amsterdamb 100 9 amsterdamf 100 9 amsterdamp 100 9 amsterdamv 100 9 amsterdamw 100 9 amsterdamweg 100 9 amsterdamc 100 9 amsterdamg 100 9 amsterdamj 100 9 amsterdamk 100 9 amsterdamq 100 9 amsterdams 100 9 amsterdamx 100 9 amsterdamz 100 9 amsterdamsestraat 100 9 amsterdamd 100 9 amsterdamt 100 9 amsterdaml 100 9 amsterdamlaan 100 9 amsterdamr 100 9 } do_execsql_test 1.7 { SELECT word, distance, matchlen FROM demo WHERE word MATCH 'amstedam*' AND distance <= 100 AND top=20; } { amsterdam 100 9 amsterdamh 100 9 amsterdamm 100 9 amsterdamn 100 9 amsterdama 100 9 amsterdame 100 9 amsterdami 100 9 amsterdamo 100 9 amsterdamu 100 9 amsterdamy 100 9 amsterdammetje 100 9 amsterdamania 100 9 amsterdamb 100 9 amsterdamf 100 9 amsterdamp 100 9 amsterdamv 100 9 amsterdamw 100 9 amsterdamweg 100 9 amsterdamc 100 9 amsterdamg 100 9 } finish_test |
Added test/tabfunc01.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | # 2015-08-19 # # 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. # #*********************************************************************** # # This file implements tests for table-valued-functions implemented using # eponymous virtual tables. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix tabfunc01 ifcapable !vtab { finish_test return } load_static_extension db series do_execsql_test tabfunc01-1.1 { SELECT *, '|' FROM generate_series WHERE start=1 AND stop=9 AND step=2; } {1 | 3 | 5 | 7 | 9 |} do_execsql_test tabfunc01-1.2 { SELECT *, '|' FROM generate_series LIMIT 5; } {0 | 1 | 2 | 3 | 4 |} do_catchsql_test tabfunc01-1.3 { CREATE VIRTUAL TABLE t1 USING generate_series; } {1 {no such module: generate_series}} do_execsql_test tabfunc01-1.4 { SELECT * FROM generate_series(1,9,2); } {1 3 5 7 9} do_execsql_test tabfunc01-1.5 { SELECT * FROM generate_series(1,9); } {1 2 3 4 5 6 7 8 9} do_execsql_test tabfunc01-1.6 { SELECT * FROM generate_series(1,10) WHERE step=3; } {1 4 7 10} do_catchsql_test tabfunc01-1.7 { SELECT * FROM generate_series(1,9,2,11); } {1 {too many arguments on generate_series() - max 3}} do_execsql_test tabfunc01-1.8 { SELECT * FROM generate_series(0,32,5) ORDER BY rowid DESC; } {30 25 20 15 10 5 0} do_execsql_test tabfunc01-1.9 { SELECT rowid, * FROM generate_series(0,32,5) ORDER BY value DESC; } {1 30 2 25 3 20 4 15 5 10 6 5 7 0} do_execsql_test tabfunc01-1.10 { SELECT rowid, * FROM generate_series(0,32,5) ORDER BY +value DESC; } {7 30 6 25 5 20 4 15 3 10 2 5 1 0} do_execsql_test tabfunc01-2.1 { CREATE TABLE t1(x); INSERT INTO t1(x) VALUES(2),(3); SELECT *, '|' FROM t1, generate_series(1,x) ORDER BY 1, 2 } {2 1 | 2 2 | 3 1 | 3 2 | 3 3 |} do_execsql_test tabfunc01-2.2 { SELECT * FROM generate_series() LIMIT 5; } {0 1 2 3 4} finish_test |
Changes to test/threadtest2.c.
︙ | ︙ | |||
39 40 41 42 43 44 45 | ** global variable to stop all other activity. Print the error message ** or print OK if the string "ok" is seen. */ int check_callback(void *pid, int argc, char **argv, char **notUsed2){ int id = (int)pid; if( strcmp(argv[0],"ok") ){ all_stop = 1; | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | ** global variable to stop all other activity. Print the error message ** or print OK if the string "ok" is seen. */ int check_callback(void *pid, int argc, char **argv, char **notUsed2){ int id = (int)pid; if( strcmp(argv[0],"ok") ){ all_stop = 1; fprintf(stderr,"%d: %s\n", id, argv[0]); }else{ /* fprintf(stderr,"%d: OK\n", id); */ } return 0; } /* |
︙ | ︙ |
Changes to test/view.test.
︙ | ︙ | |||
595 596 597 598 599 600 601 | CREATE VIEW v512 AS SELECT * FROM v256 UNION SELECT * FROM v256; CREATE VIEW v1024 AS SELECT * FROM v512 UNION SELECT * FROM v512; CREATE VIEW v2048 AS SELECT * FROM v1024 UNION SELECT * FROM v1024; CREATE VIEW v4096 AS SELECT * FROM v2048 UNION SELECT * FROM v2048; CREATE VIEW v8192 AS SELECT * FROM v4096 UNION SELECT * FROM v4096; CREATE VIEW v16384 AS SELECT * FROM v8192 UNION SELECT * FROM v8192; CREATE VIEW v32768 AS SELECT * FROM v16384 UNION SELECT * FROM v16384; | | | 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 | CREATE VIEW v512 AS SELECT * FROM v256 UNION SELECT * FROM v256; CREATE VIEW v1024 AS SELECT * FROM v512 UNION SELECT * FROM v512; CREATE VIEW v2048 AS SELECT * FROM v1024 UNION SELECT * FROM v1024; CREATE VIEW v4096 AS SELECT * FROM v2048 UNION SELECT * FROM v2048; CREATE VIEW v8192 AS SELECT * FROM v4096 UNION SELECT * FROM v4096; CREATE VIEW v16384 AS SELECT * FROM v8192 UNION SELECT * FROM v8192; CREATE VIEW v32768 AS SELECT * FROM v16384 UNION SELECT * FROM v16384; SELECT * FROM v32768 UNION SELECT * FROM v32768; } } {1 {too many references to "v1": max 65535}} ifcapable progress { do_test view-21.2 { db progress 1000 {expr 1} catchsql { SELECT * FROM v32768; |
︙ | ︙ |
Changes to test/without_rowid3.test.
︙ | ︙ | |||
2076 2077 2078 2079 2080 2081 2082 2083 2084 | } } {1 {FOREIGN KEY constraint failed}} do_test without_rowid3-ce7c13.1.6 { catchsql { UPDATE tce73 set a = 101 where a = 100; } } {1 {FOREIGN KEY constraint failed}} finish_test | > > > > > > > > > > > > > > > > | 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 | } } {1 {FOREIGN KEY constraint failed}} do_test without_rowid3-ce7c13.1.6 { catchsql { UPDATE tce73 set a = 101 where a = 100; } } {1 {FOREIGN KEY constraint failed}} # Confirm that changes() works on WITHOUT ROWID tables that use the # xfer optimization. # db close sqlite3 db :memory: do_execsql_test without_rowid3-30.1 { CREATE TABLE t1(a,b,PRIMARY KEY(a,b)) WITHOUT ROWID; CREATE TABLE t2(a,b,PRIMARY KEY(a,b)) WITHOUT ROWID; INSERT INTO t1 VALUES(1,2),(3,4),(5,6); SELECT changes(); } {3} do_execsql_test without_rowid3-30.2 { INSERT INTO t2 SELECT * FROM t1; SELECT changes(); } {3} finish_test |
Changes to tool/build-all-msvc.bat.
︙ | ︙ | |||
308 309 310 311 312 313 314 315 316 317 318 319 320 321 | REM it to something else later on. REM IF NOT DEFINED NUCRTLIBPATH ( SET SET_NUCRTLIBPATH=1 ) ) REM REM NOTE: Check if this is the Windows Phone SDK. If so, a different batch REM file is necessary to setup the build environment. Since the variable REM values involved here may contain parenthesis, using GOTO instead of REM an IF block is required. REM IF DEFINED WindowsPhoneKitDir GOTO set_vcvarsall_phone | > > > > > > > | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | REM it to something else later on. REM IF NOT DEFINED NUCRTLIBPATH ( SET SET_NUCRTLIBPATH=1 ) ) REM REM NOTE: This is the name of the sub-directory where the Windows 10.0 SDK REM libraries may be found. It is only used when compiling with the REM Windows 10.0 SDK. REM SET WIN10LIBDIR=10.0.10240.0 REM REM NOTE: Check if this is the Windows Phone SDK. If so, a different batch REM file is necessary to setup the build environment. Since the variable REM values involved here may contain parenthesis, using GOTO instead of REM an IF block is required. REM IF DEFINED WindowsPhoneKitDir GOTO set_vcvarsall_phone |
︙ | ︙ | |||
478 479 480 481 482 483 484 | CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH REM REM NOTE: The Windows 8.x and Windows 10.0 SDKs have a slightly REM different directory naming conventions. REM IF DEFINED USE_WINV100_NSDKLIBPATH ( | | | | 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 | CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH REM REM NOTE: The Windows 8.x and Windows 10.0 SDKs have a slightly REM different directory naming conventions. REM IF DEFINED USE_WINV100_NSDKLIBPATH ( CALL :fn_AppendVariable NSDKLIBPATH \..\10\lib\%WIN10LIBDIR%\um\x86 CALL :fn_CopyVariable UniversalCRTSdkDir PSDKLIBPATH CALL :fn_AppendVariable PSDKLIBPATH Lib\%WIN10LIBDIR%\um\%%D ) ELSE IF DEFINED USE_WINV63_NSDKLIBPATH ( CALL :fn_AppendVariable NSDKLIBPATH \lib\winv6.3\um\x86 ) ELSE IF "%VisualStudioVersion%" == "12.0" ( CALL :fn_AppendVariable NSDKLIBPATH \..\8.0\lib\win8\um\x86 ) ELSE IF "%VisualStudioVersion%" == "14.0" ( CALL :fn_AppendVariable NSDKLIBPATH \..\8.0\lib\win8\um\x86 ) ELSE ( |
︙ | ︙ | |||
503 504 505 506 507 508 509 | REM each iteration because it relies upon the UniversalCRTSdkDir REM environment variable being set by the batch file used to REM setup the MSVC environment. REM IF DEFINED SET_NUCRTLIBPATH ( IF DEFINED UniversalCRTSdkDir ( CALL :fn_CopyVariable UniversalCRTSdkDir NUCRTLIBPATH | | | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | REM each iteration because it relies upon the UniversalCRTSdkDir REM environment variable being set by the batch file used to REM setup the MSVC environment. REM IF DEFINED SET_NUCRTLIBPATH ( IF DEFINED UniversalCRTSdkDir ( CALL :fn_CopyVariable UniversalCRTSdkDir NUCRTLIBPATH CALL :fn_AppendVariable NUCRTLIBPATH \lib\%WIN10LIBDIR%\ucrt\x86 ) ) REM REM NOTE: Unless prevented from doing so, invoke NMAKE with the MSVC REM makefile to clean any stale build output from previous REM iterations of this loop and/or previous runs of this batch |
︙ | ︙ |
Changes to tool/mkvsix.tcl.
︙ | ︙ | |||
257 258 259 260 261 262 263 | } 2013 { return [appendArgs \ "\r\n " AppliesTo=\" $appliesTo \" \ "\r\n " {DependsOn="Microsoft.VCLibs, version=12.0"}] } 2015 { | < < < | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | } 2013 { return [appendArgs \ "\r\n " AppliesTo=\" $appliesTo \" \ "\r\n " {DependsOn="Microsoft.VCLibs, version=12.0"}] } 2015 { return [appendArgs \ "\r\n " AppliesTo=\" $appliesTo \" \ "\r\n " {DependsOn="Microsoft.VCLibs, version=14.0"}] } default { return "" } } } |
︙ | ︙ |
Changes to tool/showdb.c.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 | #include <unistd.h> #else #include <io.h> #endif #include <stdlib.h> #include <string.h> #include "sqlite3.h" | > > | | | | > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <unistd.h> #else #include <io.h> #endif #include <stdlib.h> #include <string.h> #include <assert.h> #include "sqlite3.h" static struct GlobalData { int pagesize; /* Size of a database page */ int dbfd; /* File descriptor for reading the DB */ int mxPage; /* Last page number */ int perLine; /* HEX elements to print per line */ int bRaw; /* True to access db file via OS APIs */ sqlite3_file *pFd; /* File descriptor for non-raw mode */ sqlite3 *pDb; /* Database handle that owns pFd */ } g = {1024, -1, 0, 16, 0, 0, 0}; typedef long long int i64; /* Datatype for 64-bit integers */ /* ** Convert the var-int format into i64. Return the number of bytes ** in the var-int. Write the var-int value into *pVal. |
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 | /* Report an out-of-memory error and die. */ static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } /* ** Read content from the file. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | /* Report an out-of-memory error and die. */ static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } /* ** Open a database connection. */ static sqlite3 *openDatabase(const char *zPrg, const char *zName){ sqlite3 *db = 0; int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI; int rc = sqlite3_open_v2(zName, &db, flags, 0); if( rc!=SQLITE_OK ){ const char *zErr = sqlite3_errmsg(db); fprintf(stderr, "%s: can't open %s (%s)\n", zPrg, zName, zErr); sqlite3_close(db); exit(1); } return db; } /************************************************************************** ** Beginning of low-level file access functions. ** ** All low-level access to the database file read by this program is ** performed using the following four functions: ** ** fileOpen() - open the db file ** fileClose() - close the db file ** fileRead() - read raw data from the db file ** fileGetsize() - return the size of the db file in bytes */ /* ** Open the database file. */ static void fileOpen(const char *zPrg, const char *zName){ assert( g.dbfd<0 ); if( g.bRaw==0 ){ int rc; void *pArg = (void *)(&g.pFd); g.pDb = openDatabase(zPrg, zName); rc = sqlite3_file_control(g.pDb, "main", SQLITE_FCNTL_FILE_POINTER, pArg); if( rc!=SQLITE_OK ){ fprintf(stderr, "%s: failed to obtain fd for %s (SQLite too old?)\n", zPrg, zName ); exit(1); } }else{ g.dbfd = open(zName, O_RDONLY); if( g.dbfd<0 ){ fprintf(stderr,"%s: can't open %s\n", zPrg, zName); exit(1); } } } /* ** Close the database file opened by fileOpen() */ static void fileClose(){ if( g.bRaw==0 ){ sqlite3_close(g.pDb); g.pDb = 0; g.pFd = 0; }else{ close(g.dbfd); g.dbfd = -1; } } /* ** Read content from the file. ** ** Space to hold the content is obtained from sqlite3_malloc() and needs ** to be freed by the caller. */ static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){ unsigned char *aData; int got; aData = sqlite3_malloc(nByte+32); if( aData==0 ) out_of_memory(); memset(aData, 0, nByte+32); if( g.bRaw==0 ){ int rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst); if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ fprintf(stderr, "error in xRead() - %d\n", rc); exit(1); } }else{ lseek(g.dbfd, ofst, SEEK_SET); got = read(g.dbfd, aData, nByte); if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got); } return aData; } /* ** Return the size of the file in byte. */ static sqlite3_int64 fileGetsize(void){ sqlite3_int64 res = 0; if( g.bRaw==0 ){ int rc = g.pFd->pMethods->xFileSize(g.pFd, &res); if( rc!=SQLITE_OK ){ fprintf(stderr, "error in xFileSize() - %d\n", rc); exit(1); } }else{ struct stat sbuf; fstat(g.dbfd, &sbuf); res = (sqlite3_int64)(sbuf.st_size); } return res; } /* ** End of low-level file access functions. **************************************************************************/ /* ** Print a range of bytes as hex and as ascii. */ static unsigned char *print_byte_range( int ofst, /* First byte in the range of bytes to print */ int nByte, /* Number of bytes to print */ int printOfst /* Add this amount to the index on the left column */ |
︙ | ︙ | |||
94 95 96 97 98 99 100 | zOfstFmt = " %05x: "; }else if( ((printOfst+nByte)&~0xffffff)==0 ){ zOfstFmt = " %06x: "; }else{ zOfstFmt = " %08x: "; } | | | | | | | | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | zOfstFmt = " %05x: "; }else if( ((printOfst+nByte)&~0xffffff)==0 ){ zOfstFmt = " %06x: "; }else{ zOfstFmt = " %08x: "; } aData = fileRead(ofst, nByte); for(i=0; i<nByte; i += g.perLine){ fprintf(stdout, zOfstFmt, i+printOfst); for(j=0; j<g.perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%02x ", aData[i+j]); } } for(j=0; j<g.perLine; j++){ if( i+j>nByte ){ fprintf(stdout, " "); }else{ fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); } } fprintf(stdout,"\n"); } return aData; } /* ** Print an entire page of content as hex */ static void print_page(int iPg){ int iStart; unsigned char *aData; iStart = (iPg-1)*g.pagesize; fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n", iPg, iStart, iStart+g.pagesize-1); aData = print_byte_range(iStart, g.pagesize, 0); sqlite3_free(aData); } /* Print a line of decode output showing a 4-byte integer. */ static void print_decode_line( unsigned char *aData, /* Content being decoded */ |
︙ | ︙ | |||
263 264 265 266 267 268 269 | static i64 localPayload(i64 nPayload, char cType){ i64 maxLocal; i64 minLocal; i64 surplus; i64 nLocal; if( cType==13 ){ /* Table leaf */ | | | | | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | static i64 localPayload(i64 nPayload, char cType){ i64 maxLocal; i64 minLocal; i64 surplus; i64 nLocal; if( cType==13 ){ /* Table leaf */ maxLocal = g.pagesize-35; minLocal = (g.pagesize-12)*32/255-23; }else{ maxLocal = (g.pagesize-12)*64/255-23; minLocal = (g.pagesize-12)*32/255-23; } if( nPayload>maxLocal ){ surplus = minLocal + (nPayload-minLocal)%(g.pagesize-4); if( surplus<=maxLocal ){ nLocal = surplus; }else{ nLocal = minLocal; } }else{ nLocal = nPayload; |
︙ | ︙ | |||
577 578 579 580 581 582 583 | if( a[0]==2 || a[0]==5 ){ print_decode_line(a, 8, 4, "Right child"); } if( cellToDecode==(-2) && nCell>0 ){ printf(" key: lx=left-child n=payload-size r=rowid\n"); } if( showMap ){ | | | | 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | if( a[0]==2 || a[0]==5 ){ print_decode_line(a, 8, 4, "Right child"); } if( cellToDecode==(-2) && nCell>0 ){ printf(" key: lx=left-child n=payload-size r=rowid\n"); } if( showMap ){ zMap = sqlite3_malloc(g.pagesize); memset(zMap, '.', g.pagesize); memset(zMap, '1', hdrSize); memset(&zMap[hdrSize], 'H', iCellPtr); memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell); } for(i=0; i<nCell; i++){ int cofst = iCellPtr + i*2; char *zDesc; |
︙ | ︙ | |||
607 608 609 610 611 612 613 | printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); }else if( cellToDecode==(-1) || cellToDecode==i ){ decodeCell(a, pgno, i, hdrSize, cofst-hdrSize); } } if( showMap ){ printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n"); | | | < | | | | | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); }else if( cellToDecode==(-1) || cellToDecode==i ){ decodeCell(a, pgno, i, hdrSize, cofst-hdrSize); } } if( showMap ){ printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n"); for(i=0; i<g.pagesize; i+=64){ printf(" %03x: %.64s\n", i, &zMap[i]); } sqlite3_free(zMap); } } /* ** Decode a freelist trunk page. */ static void decode_trunk_page( int pgno, /* The page number */ int detail, /* Show leaf pages if true */ int recursive /* Follow the trunk change if true */ ){ int n, i; unsigned char *a; while( pgno>0 ){ a = fileRead((pgno-1)*g.pagesize, g.pagesize); printf("Decode of freelist trunk page %d:\n", pgno); print_decode_line(a, 0, 4, "Next freelist trunk page"); print_decode_line(a, 4, 4, "Number of entries on this page"); if( detail ){ n = (int)decodeInt32(&a[4]); for(i=0; i<n; i++){ unsigned int x = decodeInt32(&a[8+4*i]); char zIdx[10]; sprintf(zIdx, "[%d]", i); printf(" %5s %7u", zIdx, x); if( i%5==4 ) printf("\n"); } if( i%5!=0 ) printf("\n"); } if( !recursive ){ pgno = 0; }else{ pgno = (int)decodeInt32(&a[0]); } sqlite3_free(a); } } /* ** A short text comment on the use of each page. */ static char **zPageUse; /* ** Add a comment on the use of a page. */ static void page_usage_msg(int pgno, const char *zFormat, ...){ va_list ap; char *zMsg; va_start(ap, zFormat); zMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); if( pgno<=0 || pgno>g.mxPage ){ printf("ERROR: page %d out of range 1..%d: %s\n", pgno, g.mxPage, zMsg); sqlite3_free(zMsg); return; } if( zPageUse[pgno]!=0 ){ printf("ERROR: page %d used multiple times:\n", pgno); printf("ERROR: previous: %s\n", zPageUse[pgno]); printf("ERROR: current: %s\n", zMsg); |
︙ | ︙ | |||
715 716 717 718 719 720 721 | i = decodeVarint(a, &rowid); a += i; n += i; } if( nLocal<nPayload ){ int ovfl = decodeInt32(a+nLocal); int cnt = 0; | | | | | | | 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | i = decodeVarint(a, &rowid); a += i; n += i; } if( nLocal<nPayload ){ int ovfl = decodeInt32(a+nLocal); int cnt = 0; while( ovfl && (cnt++)<g.mxPage ){ page_usage_msg(ovfl, "overflow %d from cell %d of page %d", cnt, cellno, pgno); a = fileRead((ovfl-1)*g.pagesize, 4); ovfl = decodeInt32(a); sqlite3_free(a); } } } /* ** Describe the usages of a b-tree page */ static void page_usage_btree( int pgno, /* Page to describe */ int parent, /* Parent of this page. 0 for root pages */ int idx, /* Which child of the parent */ const char *zName /* Name of the table */ ){ unsigned char *a; const char *zType = "corrupt node"; int nCell; int i; int hdr = pgno==1 ? 100 : 0; if( pgno<=0 || pgno>g.mxPage ) return; a = fileRead((pgno-1)*g.pagesize, g.pagesize); switch( a[hdr] ){ case 2: zType = "interior node of index"; break; case 5: zType = "interior node of table"; break; case 10: zType = "leaf of index"; break; case 13: zType = "leaf of table"; break; } if( parent ){ |
︙ | ︙ | |||
779 780 781 782 783 784 785 | for(i=0; i<nCell; i++){ int ofst; ofst = cellstart + i*2; ofst = a[ofst]*256 + a[ofst+1]; page_usage_cell(a[hdr], a+ofst, pgno, i); } } | | | | | | | | | < < | < < | < | | | | | | 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 | for(i=0; i<nCell; i++){ int ofst; ofst = cellstart + i*2; ofst = a[ofst]*256 + a[ofst+1]; page_usage_cell(a[hdr], a+ofst, pgno, i); } } sqlite3_free(a); } /* ** Determine page usage by the freelist */ static void page_usage_freelist(int pgno){ unsigned char *a; int cnt = 0; int i; int n; int iNext; int parent = 1; while( pgno>0 && pgno<=g.mxPage && (cnt++)<g.mxPage ){ page_usage_msg(pgno, "freelist trunk #%d child of %d", cnt, parent); a = fileRead((pgno-1)*g.pagesize, g.pagesize); iNext = decodeInt32(a); n = decodeInt32(a+4); for(i=0; i<n; i++){ int child = decodeInt32(a + (i*4+8)); page_usage_msg(child, "freelist leaf, child %d of trunk page %d", i, pgno); } sqlite3_free(a); parent = pgno; pgno = iNext; } } /* ** Determine pages used as PTRMAP pages */ static void page_usage_ptrmap(unsigned char *a){ if( a[55] ){ int usable = g.pagesize - a[20]; int pgno = 2; int perPage = usable/5; while( pgno<=g.mxPage ){ page_usage_msg(pgno, "PTRMAP page covering %d..%d", pgno+1, pgno+perPage); pgno += perPage + 1; } } } /* ** Try to figure out how every page in the database file is being used. */ static void page_usage_report(const char *zPrg, const char *zDbName){ int i, j; int rc; sqlite3 *db; sqlite3_stmt *pStmt; unsigned char *a; char zQuery[200]; /* Avoid the pathological case */ if( g.mxPage<1 ){ printf("empty database\n"); return; } /* Open the database file */ db = openDatabase(zPrg, zDbName); /* Set up global variables zPageUse[] and g.mxPage to record page ** usages */ zPageUse = sqlite3_malloc( sizeof(zPageUse[0])*(g.mxPage+1) ); if( zPageUse==0 ) out_of_memory(); memset(zPageUse, 0, sizeof(zPageUse[0])*(g.mxPage+1)); /* Discover the usage of each page */ a = fileRead(0, 100); page_usage_freelist(decodeInt32(a+32)); page_usage_ptrmap(a); sqlite3_free(a); page_usage_btree(1, 0, 0, "sqlite_master"); sqlite3_exec(db, "PRAGMA writable_schema=ON", 0, 0, 0); for(j=0; j<2; j++){ sqlite3_snprintf(sizeof(zQuery), zQuery, "SELECT type, name, rootpage FROM SQLITE_MASTER WHERE rootpage" " ORDER BY rowid %s", j?"DESC":""); rc = sqlite3_prepare_v2(db, zQuery, -1, &pStmt, 0); |
︙ | ︙ | |||
882 883 884 885 886 887 888 | } rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ) break; } sqlite3_close(db); /* Print the report and free memory used */ | | | | | | | | | | | > > | | > > | | > > > > | > > > | > | | < > > | | | | | | | > > | > | < | | | | | | | | | | | | | | | | | | | | | | | | | 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 | } rc = sqlite3_finalize(pStmt); if( rc==SQLITE_OK ) break; } sqlite3_close(db); /* Print the report and free memory used */ for(i=1; i<=g.mxPage; i++){ printf("%5d: %s\n", i, zPageUse[i] ? zPageUse[i] : "???"); sqlite3_free(zPageUse[i]); } sqlite3_free(zPageUse); zPageUse = 0; } /* ** Try to figure out how every page in the database file is being used. */ static void ptrmap_coverage_report(const char *zDbName){ int pgno; unsigned char *aHdr; unsigned char *a; int usable; int perPage; int i; /* Avoid the pathological case */ if( g.mxPage<1 ){ printf("empty database\n"); return; } /* Make sure PTRMAPs are used in this database */ aHdr = fileRead(0, 100); if( aHdr[55]==0 ){ printf("database does not use PTRMAP pages\n"); return; } usable = g.pagesize - aHdr[20]; perPage = usable/5; sqlite3_free(aHdr); printf("%5d: root of sqlite_master\n", 1); for(pgno=2; pgno<=g.mxPage; pgno += perPage+1){ printf("%5d: PTRMAP page covering %d..%d\n", pgno, pgno+1, pgno+perPage); a = fileRead((pgno-1)*g.pagesize, usable); for(i=0; i+5<=usable && pgno+1+i/5<=g.mxPage; i+=5){ const char *zType = "???"; unsigned int iFrom = decodeInt32(&a[i+1]); switch( a[i] ){ case 1: zType = "b-tree root page"; break; case 2: zType = "freelist page"; break; case 3: zType = "first page of overflow"; break; case 4: zType = "later page of overflow"; break; case 5: zType = "b-tree non-root page"; break; } printf("%5d: %s, parent=%u\n", pgno+1+i/5, zType, iFrom); } sqlite3_free(a); } } /* ** Print a usage comment */ static void usage(const char *argv0){ fprintf(stderr, "Usage %s ?--uri? FILENAME ?args...?\n\n", argv0); fprintf(stderr, "switches:\n" " --raw Read db file directly, bypassing SQLite VFS\n" "args:\n" " dbheader Show database header\n" " pgidx Index of how each page is used\n" " ptrmap Show all PTRMAP page content\n" " NNN..MMM Show hex of pages NNN through MMM\n" " NNN..end Show hex of pages NNN through end of file\n" " NNNb Decode btree page NNN\n" " NNNbc Decode btree page NNN and show content\n" " NNNbm Decode btree page NNN and show a layout map\n" " NNNbdCCC Decode cell CCC on btree page NNN\n" " NNNt Decode freelist trunk page NNN\n" " NNNtd Show leaf freelist pages on the decode\n" " NNNtr Recursively decode freelist starting at NNN\n" ); } int main(int argc, char **argv){ sqlite3_int64 szFile; unsigned char *zPgSz; const char *zPrg = argv[0]; /* Name of this executable */ char **azArg = argv; int nArg = argc; /* Check for the "--uri" or "-uri" switch. */ if( nArg>1 ){ if( sqlite3_stricmp("-raw", azArg[1])==0 || sqlite3_stricmp("--raw", azArg[1])==0 ){ g.bRaw = 1; azArg++; nArg--; } } if( nArg<2 ){ usage(zPrg); exit(1); } fileOpen(zPrg, azArg[1]); szFile = fileGetsize(); zPgSz = fileRead(16, 2); g.pagesize = zPgSz[0]*256 + zPgSz[1]*65536; if( g.pagesize==0 ) g.pagesize = 1024; sqlite3_free(zPgSz); printf("Pagesize: %d\n", g.pagesize); g.mxPage = (szFile+g.pagesize-1)/g.pagesize; printf("Available pages: 1..%d\n", g.mxPage); if( nArg==2 ){ int i; for(i=1; i<=g.mxPage; i++) print_page(i); }else{ int i; for(i=2; i<nArg; i++){ int iStart, iEnd; char *zLeft; if( strcmp(azArg[i], "dbheader")==0 ){ print_db_header(); continue; } if( strcmp(azArg[i], "pgidx")==0 ){ page_usage_report(zPrg, azArg[1]); continue; } if( strcmp(azArg[i], "ptrmap")==0 ){ ptrmap_coverage_report(azArg[1]); continue; } if( strcmp(azArg[i], "help")==0 ){ usage(zPrg); continue; } if( !isdigit(azArg[i][0]) ){ fprintf(stderr, "%s: unknown option: [%s]\n", zPrg, azArg[i]); continue; } iStart = strtol(azArg[i], &zLeft, 0); if( zLeft && strcmp(zLeft,"..end")==0 ){ iEnd = g.mxPage; }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){ iEnd = strtol(&zLeft[2], 0, 0); }else if( zLeft && zLeft[0]=='b' ){ int ofst, nByte, hdrSize; unsigned char *a; if( iStart==1 ){ ofst = hdrSize = 100; nByte = g.pagesize-100; }else{ hdrSize = 0; ofst = (iStart-1)*g.pagesize; nByte = g.pagesize; } a = fileRead(ofst, nByte); decode_btree_page(a, iStart, hdrSize, &zLeft[1]); sqlite3_free(a); continue; }else if( zLeft && zLeft[0]=='t' ){ int detail = 0; int recursive = 0; int i; for(i=1; zLeft[i]; i++){ if( zLeft[i]=='r' ) recursive = 1; if( zLeft[i]=='d' ) detail = 1; } decode_trunk_page(iStart, detail, recursive); continue; }else{ iEnd = iStart; } if( iStart<1 || iEnd<iStart || iEnd>g.mxPage ){ fprintf(stderr, "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", g.mxPage); exit(1); } while( iStart<=iEnd ){ print_page(iStart); iStart++; } } } fileClose(); return 0; } |
Changes to tool/spaceanal.tcl.
︙ | ︙ | |||
22 23 24 25 26 27 28 | return 0 } # Get the name of the database to analyze # proc usage {} { set argv0 [file rootname [file tail [info nameofexecutable]]] | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | return 0 } # Get the name of the database to analyze # proc usage {} { set argv0 [file rootname [file tail [info nameofexecutable]]] puts stderr "Usage: $argv0 \[--pageinfo] \[--stats] database-name" exit 1 } set file_to_analyze {} set flags(-pageinfo) 0 set flags(-stats) 0 append argv {} foreach arg $argv { |
︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 | sqlite3 mem :memory: set tabledef {CREATE TABLE space_used( name clob, -- Name of a table or index in the database file tblname clob, -- Name of associated table is_index boolean, -- TRUE if it is an index, false for a table nentry int, -- Number of entries in the BTree leaf_entries int, -- Number of leaf entries payload int, -- Total amount of data stored in this table or index ovfl_payload int, -- Total amount of data stored on overflow pages ovfl_cnt int, -- Number of entries that use overflow mx_payload int, -- Maximum payload size int_pages int, -- Number of interior pages used leaf_pages int, -- Number of leaf pages used ovfl_pages int, -- Number of overflow pages used | > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | sqlite3 mem :memory: set tabledef {CREATE TABLE space_used( name clob, -- Name of a table or index in the database file tblname clob, -- Name of associated table is_index boolean, -- TRUE if it is an index, false for a table nentry int, -- Number of entries in the BTree leaf_entries int, -- Number of leaf entries depth int, -- Depth of the b-tree payload int, -- Total amount of data stored in this table or index ovfl_payload int, -- Total amount of data stored on overflow pages ovfl_cnt int, -- Number of entries that use overflow mx_payload int, -- Maximum payload size int_pages int, -- Number of interior pages used leaf_pages int, -- Number of leaf pages used ovfl_pages int, -- Number of overflow pages used |
︙ | ︙ | |||
160 161 162 163 164 165 166 | # Create a temporary "dbstat" virtual table. # db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat} db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path} db eval {DROP TABLE temp.stat} | < < < < < < < < < < < < < < > | | | | | | | | | > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | # Create a temporary "dbstat" virtual table. # db eval {CREATE VIRTUAL TABLE temp.stat USING dbstat} db eval {CREATE TEMP TABLE dbstat AS SELECT * FROM temp.stat ORDER BY name, path} db eval {DROP TABLE temp.stat} set isCompressed 0 set compressOverhead 0 set depth 0 set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 } foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] { set is_index [expr {$name!=$tblname}] set idx_btree [expr {$is_index || [is_without_rowid $name]}] db eval { SELECT sum(ncell) AS nentry, sum((pagetype=='leaf')*ncell) AS leaf_entries, sum(payload) AS payload, sum((pagetype=='overflow') * payload) AS ovfl_payload, sum(path LIKE '%+000000') AS ovfl_cnt, max(mx_payload) AS mx_payload, sum(pagetype=='internal') AS int_pages, sum(pagetype=='leaf') AS leaf_pages, sum(pagetype=='overflow') AS ovfl_pages, sum((pagetype=='internal') * unused) AS int_unused, sum((pagetype=='leaf') * unused) AS leaf_unused, sum((pagetype=='overflow') * unused) AS ovfl_unused, sum(pgsize) AS compressed_size, max((length(CASE WHEN path LIKE '%+%' THEN '' ELSE path END)+3)/4) AS depth FROM temp.dbstat WHERE name = $name } break set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}] set storage [expr {$total_pages*$pageSize}] if {!$isCompressed && $storage>$compressed_size} { set isCompressed 1 |
︙ | ︙ | |||
231 232 233 234 235 236 237 238 239 240 241 242 243 244 | mem eval { INSERT INTO space_used VALUES( $name, $tblname, $is_index, $nentry, $leaf_entries, $payload, $ovfl_payload, $ovfl_cnt, $mx_payload, $int_pages, $leaf_pages, $ovfl_pages, | > | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 | mem eval { INSERT INTO space_used VALUES( $name, $tblname, $is_index, $nentry, $leaf_entries, $depth, $payload, $ovfl_payload, $ovfl_cnt, $mx_payload, $int_pages, $leaf_pages, $ovfl_pages, |
︙ | ︙ | |||
340 341 342 343 344 345 346 | int(sum(leaf_pages)) AS leaf_pages, int(sum(int_pages)) AS int_pages, int(sum(ovfl_pages)) AS ovfl_pages, int(sum(leaf_unused)) AS leaf_unused, int(sum(int_unused)) AS int_unused, int(sum(ovfl_unused)) AS ovfl_unused, int(sum(gap_cnt)) AS gap_cnt, | | > > | 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | int(sum(leaf_pages)) AS leaf_pages, int(sum(int_pages)) AS int_pages, int(sum(ovfl_pages)) AS ovfl_pages, int(sum(leaf_unused)) AS leaf_unused, int(sum(int_unused)) AS int_unused, int(sum(ovfl_unused)) AS ovfl_unused, int(sum(gap_cnt)) AS gap_cnt, int(sum(compressed_size)) AS compressed_size, int(max(depth)) AS depth, count(*) AS cnt FROM space_used WHERE $where" {} {} # Output the sub-report title, nicely decorated with * characters. # puts "" titleline $title puts "" |
︙ | ︙ | |||
377 378 379 380 381 382 383 | set nTab [mem eval " SELECT count(*) FROM ( SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0 ) "] set avg_fanout [mem eval " SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used | | > | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | set nTab [mem eval " SELECT count(*) FROM ( SELECT DISTINCT tblname FROM space_used WHERE $where AND is_index=0 ) "] set avg_fanout [mem eval " SELECT (sum(leaf_pages+int_pages)-$nTab)/sum(int_pages) FROM space_used WHERE $where "] set avg_fanout [format %.2f $avg_fanout] } set ovfl_cnt_percent [percent $ovfl_cnt $nleaf {of all entries}] # Print out the sub-report statistics. # statline {Percentage of total database} $total_pages_percent statline {Number of entries} $nleaf statline {Bytes of storage consumed} $storage if {$compressed_size!=$storage} { set compressed_size [expr {$compressed_size+$compressOverhead*$total_pages}] set pct [expr {$compressed_size*100.0/$storage}] set pct [format {%5.1f%%} $pct] statline {Bytes used after compression} $compressed_size $pct } statline {Bytes of payload} $payload $payload_percent if {$cnt==1} {statline {B-tree depth} $depth} statline {Average payload per entry} $avg_payload statline {Average unused bytes per entry} $avg_unused if {[info exists avg_fanout]} { statline {Average fanout} $avg_fanout } if {$showFrag && $total_pages>1} { set fragmentation [percent $gap_cnt [expr {$total_pages-1}]] |
︙ | ︙ |
Changes to tool/sqldiff.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ** run the utility. */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <ctype.h> #include <string.h> #include "sqlite3.h" /* ** All global variables are gathered into the "g" singleton. */ struct GlobalVars { const char *zArgv0; /* Name of program */ | > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | ** run the utility. */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <ctype.h> #include <string.h> #include <assert.h> #include "sqlite3.h" /* ** All global variables are gathered into the "g" singleton. */ struct GlobalVars { const char *zArgv0; /* Name of program */ |
︙ | ︙ | |||
255 256 257 258 259 260 261 | ** CREATE TABLE t4(x,y,z,PRIMARY KEY(y,z)) WITHOUT ROWID; ** *pnPKey = 2 ** az = { "y", "z", "x", 0 } ** ** CREATE TABLE t5(rowid,_rowid_,oid); ** az = 0 // The rowid is not accessible */ | | > > > > > | 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | ** CREATE TABLE t4(x,y,z,PRIMARY KEY(y,z)) WITHOUT ROWID; ** *pnPKey = 2 ** az = { "y", "z", "x", 0 } ** ** CREATE TABLE t5(rowid,_rowid_,oid); ** az = 0 // The rowid is not accessible */ static char **columnNames( const char *zDb, /* Database ("main" or "aux") to query */ const char *zTab, /* Name of table to return details of */ int *pnPKey, /* OUT: Number of PK columns */ int *pbRowid /* OUT: True if PK is an implicit rowid */ ){ char **az = 0; /* List of column names to be returned */ int naz = 0; /* Number of entries in az[] */ sqlite3_stmt *pStmt; /* SQL statement being run */ char *zPkIdxName = 0; /* Name of the PRIMARY KEY index */ int truePk = 0; /* PRAGMA table_info indentifies the PK to use */ int nPK = 0; /* Number of PRIMARY KEY columns */ int i, j; /* Loop counters */ |
︙ | ︙ | |||
334 335 336 337 338 339 340 341 342 343 344 345 346 347 | az = sqlite3_realloc(az, sizeof(char*)*(naz+2) ); if( az==0 ) runtimeError("out of memory"); az[naz++] = safeId((char*)sqlite3_column_text(pStmt,1)); } } sqlite3_finalize(pStmt); if( az ) az[naz] = 0; if( az[0]==0 ){ const char *azRowid[] = { "rowid", "_rowid_", "oid" }; for(i=0; i<sizeof(azRowid)/sizeof(azRowid[0]); i++){ for(j=1; j<naz; j++){ if( sqlite3_stricmp(az[j], azRowid[i])==0 ) break; } if( j>=naz ){ | > > > > > > > > > | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | az = sqlite3_realloc(az, sizeof(char*)*(naz+2) ); if( az==0 ) runtimeError("out of memory"); az[naz++] = safeId((char*)sqlite3_column_text(pStmt,1)); } } sqlite3_finalize(pStmt); if( az ) az[naz] = 0; /* If it is non-NULL, set *pbRowid to indicate whether or not the PK of ** this table is an implicit rowid (*pbRowid==1) or not (*pbRowid==0). */ if( pbRowid ) *pbRowid = (az[0]==0); /* If this table has an implicit rowid for a PK, figure out how to refer ** to it. There are three options - "rowid", "_rowid_" and "oid". Any ** of these will work, unless the table has an explicit column of the ** same name. */ if( az[0]==0 ){ const char *azRowid[] = { "rowid", "_rowid_", "oid" }; for(i=0; i<sizeof(azRowid)/sizeof(azRowid[0]); i++){ for(j=1; j<naz; j++){ if( sqlite3_stricmp(az[j], azRowid[i])==0 ) break; } if( j>=naz ){ |
︙ | ︙ | |||
430 431 432 433 434 435 436 | pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab); if( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); if( !g.bSchemaOnly ){ | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | pStmt = db_prepare("SELECT sql FROM aux.sqlite_master WHERE name=%Q", zTab); if( SQLITE_ROW==sqlite3_step(pStmt) ){ fprintf(out, "%s;\n", sqlite3_column_text(pStmt,0)); } sqlite3_finalize(pStmt); if( !g.bSchemaOnly ){ az = columnNames("aux", zTab, &nPk, 0); strInit(&ins); if( az==0 ){ pStmt = db_prepare("SELECT * FROM aux.%s", zId); strPrintf(&ins,"INSERT INTO %s VALUES", zId); }else{ Str sql; strInit(&sql); |
︙ | ︙ | |||
507 508 509 510 511 512 513 | strInit(&sql); if( g.fDebug==DEBUG_COLUMN_NAMES ){ /* Simply run columnNames() on all tables of the origin ** database and show the results. This is used for testing ** and debugging of the columnNames() function. */ | | | 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 | strInit(&sql); if( g.fDebug==DEBUG_COLUMN_NAMES ){ /* Simply run columnNames() on all tables of the origin ** database and show the results. This is used for testing ** and debugging of the columnNames() function. */ az = columnNames("aux",zTab, &nPk, 0); if( az==0 ){ printf("Rowid not accessible for %s\n", zId); }else{ printf("%s:", zId); for(i=0; az[i]; i++){ printf(" %s", az[i]); if( i+1==nPk ) printf(" *"); |
︙ | ︙ | |||
536 537 538 539 540 541 542 | if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ dump_table(zTab, out); goto end_diff_one_table; } | | | | 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 | if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ dump_table(zTab, out); goto end_diff_one_table; } az = columnNames("main", zTab, &nPk, 0); az2 = columnNames("aux", zTab, &nPk2, 0); if( az && az2 ){ for(n=0; az[n]; n++){ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break; } } if( az==0 || az2==0 |
︙ | ︙ | |||
714 715 716 717 718 719 720 721 722 723 724 725 726 727 | end_diff_one_table: strFree(&sql); sqlite3_free(zId); namelistFree(az); namelistFree(az2); return; } /* ** Display a summary of differences between two versions of the same ** table table. ** ** * Number of rows changed ** * Number of rows added | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 | end_diff_one_table: strFree(&sql); sqlite3_free(zId); namelistFree(az); namelistFree(az2); return; } /* ** Check that table zTab exists and has the same schema in both the "main" ** and "aux" databases currently opened by the global db handle. If they ** do not, output an error message on stderr and exit(1). Otherwise, if ** the schemas do match, return control to the caller. */ static void checkSchemasMatch(const char *zTab){ sqlite3_stmt *pStmt = db_prepare( "SELECT A.sql=B.sql FROM main.sqlite_master A, aux.sqlite_master B" " WHERE A.name=%Q AND B.name=%Q", zTab, zTab ); if( SQLITE_ROW==sqlite3_step(pStmt) ){ if( sqlite3_column_int(pStmt,0)==0 ){ runtimeError("schema changes for table %s", safeId(zTab)); } }else{ runtimeError("table %s missing from one or both databases", safeId(zTab)); } sqlite3_finalize(pStmt); } /************************************************************************** ** The following code is copied from fossil. It is used to generate the ** fossil delta blobs sometimes used in RBU update records. */ typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u8; /* ** The width of a hash window in bytes. The algorithm only works if this ** is a power of 2. */ #define NHASH 16 /* ** The current state of the rolling hash. ** ** z[] holds the values that have been hashed. z[] is a circular buffer. ** z[i] is the first entry and z[(i+NHASH-1)%NHASH] is the last entry of ** the window. ** ** Hash.a is the sum of all elements of hash.z[]. Hash.b is a weighted ** sum. Hash.b is z[i]*NHASH + z[i+1]*(NHASH-1) + ... + z[i+NHASH-1]*1. ** (Each index for z[] should be module NHASH, of course. The %NHASH operator ** is omitted in the prior expression for brevity.) */ typedef struct hash hash; struct hash { u16 a, b; /* Hash values */ u16 i; /* Start of the hash window */ char z[NHASH]; /* The values that have been hashed */ }; /* ** Initialize the rolling hash using the first NHASH characters of z[] */ static void hash_init(hash *pHash, const char *z){ u16 a, b, i; a = b = 0; for(i=0; i<NHASH; i++){ a += z[i]; b += (NHASH-i)*z[i]; pHash->z[i] = z[i]; } pHash->a = a & 0xffff; pHash->b = b & 0xffff; pHash->i = 0; } /* ** Advance the rolling hash by a single character "c" */ static void hash_next(hash *pHash, int c){ u16 old = pHash->z[pHash->i]; pHash->z[pHash->i] = (char)c; pHash->i = (pHash->i+1)&(NHASH-1); pHash->a = pHash->a - old + (char)c; pHash->b = pHash->b - NHASH*old + pHash->a; } /* ** Return a 32-bit hash value */ static u32 hash_32bit(hash *pHash){ return (pHash->a & 0xffff) | (((u32)(pHash->b & 0xffff))<<16); } /* ** Write an base-64 integer into the given buffer. */ static void putInt(unsigned int v, char **pz){ static const char zDigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"; /* 123456789 123456789 123456789 123456789 123456789 123456789 123 */ int i, j; char zBuf[20]; if( v==0 ){ *(*pz)++ = '0'; return; } for(i=0; v>0; i++, v>>=6){ zBuf[i] = zDigits[v&0x3f]; } for(j=i-1; j>=0; j--){ *(*pz)++ = zBuf[j]; } } /* ** Return the number digits in the base-64 representation of a positive integer */ static int digit_count(int v){ unsigned int i, x; for(i=1, x=64; (unsigned int)v>=x; i++, x <<= 6){} return i; } /* ** Compute a 32-bit checksum on the N-byte buffer. Return the result. */ static unsigned int checksum(const char *zIn, size_t N){ const unsigned char *z = (const unsigned char *)zIn; unsigned sum0 = 0; unsigned sum1 = 0; unsigned sum2 = 0; unsigned sum3 = 0; while(N >= 16){ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]); sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]); sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]); sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]); z += 16; N -= 16; } while(N >= 4){ sum0 += z[0]; sum1 += z[1]; sum2 += z[2]; sum3 += z[3]; z += 4; N -= 4; } sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24); switch(N){ case 3: sum3 += (z[2] << 8); case 2: sum3 += (z[1] << 16); case 1: sum3 += (z[0] << 24); default: ; } return sum3; } /* ** Create a new delta. ** ** The delta is written into a preallocated buffer, zDelta, which ** should be at least 60 bytes longer than the target file, zOut. ** The delta string will be NUL-terminated, but it might also contain ** embedded NUL characters if either the zSrc or zOut files are ** binary. This function returns the length of the delta string ** in bytes, excluding the final NUL terminator character. ** ** Output Format: ** ** The delta begins with a base64 number followed by a newline. This ** number is the number of bytes in the TARGET file. Thus, given a ** delta file z, a program can compute the size of the output file ** simply by reading the first line and decoding the base-64 number ** found there. The delta_output_size() routine does exactly this. ** ** After the initial size number, the delta consists of a series of ** literal text segments and commands to copy from the SOURCE file. ** A copy command looks like this: ** ** NNN@MMM, ** ** where NNN is the number of bytes to be copied and MMM is the offset ** into the source file of the first byte (both base-64). If NNN is 0 ** it means copy the rest of the input file. Literal text is like this: ** ** NNN:TTTTT ** ** where NNN is the number of bytes of text (base-64) and TTTTT is the text. ** ** The last term is of the form ** ** NNN; ** ** In this case, NNN is a 32-bit bigendian checksum of the output file ** that can be used to verify that the delta applied correctly. All ** numbers are in base-64. ** ** Pure text files generate a pure text delta. Binary files generate a ** delta that may contain some binary data. ** ** Algorithm: ** ** The encoder first builds a hash table to help it find matching ** patterns in the source file. 16-byte chunks of the source file ** sampled at evenly spaced intervals are used to populate the hash ** table. ** ** Next we begin scanning the target file using a sliding 16-byte ** window. The hash of the 16-byte window in the target is used to ** search for a matching section in the source file. When a match ** is found, a copy command is added to the delta. An effort is ** made to extend the matching section to regions that come before ** and after the 16-byte hash window. A copy command is only issued ** if the result would use less space that just quoting the text ** literally. Literal text is added to the delta for sections that ** do not match or which can not be encoded efficiently using copy ** commands. */ static int rbuDeltaCreate( const char *zSrc, /* The source or pattern file */ unsigned int lenSrc, /* Length of the source file */ const char *zOut, /* The target file */ unsigned int lenOut, /* Length of the target file */ char *zDelta /* Write the delta into this buffer */ ){ unsigned int i, base; char *zOrigDelta = zDelta; hash h; int nHash; /* Number of hash table entries */ int *landmark; /* Primary hash table */ int *collide; /* Collision chain */ int lastRead = -1; /* Last byte of zSrc read by a COPY command */ /* Add the target file size to the beginning of the delta */ putInt(lenOut, &zDelta); *(zDelta++) = '\n'; /* If the source file is very small, it means that we have no ** chance of ever doing a copy command. Just output a single ** literal segment for the entire target and exit. */ if( lenSrc<=NHASH ){ putInt(lenOut, &zDelta); *(zDelta++) = ':'; memcpy(zDelta, zOut, lenOut); zDelta += lenOut; putInt(checksum(zOut, lenOut), &zDelta); *(zDelta++) = ';'; return zDelta - zOrigDelta; } /* Compute the hash table used to locate matching sections in the ** source file. */ nHash = lenSrc/NHASH; collide = sqlite3_malloc( nHash*2*sizeof(int) ); landmark = &collide[nHash]; memset(landmark, -1, nHash*sizeof(int)); memset(collide, -1, nHash*sizeof(int)); for(i=0; i<lenSrc-NHASH; i+=NHASH){ int hv; hash_init(&h, &zSrc[i]); hv = hash_32bit(&h) % nHash; collide[i/NHASH] = landmark[hv]; landmark[hv] = i/NHASH; } /* Begin scanning the target file and generating copy commands and ** literal sections of the delta. */ base = 0; /* We have already generated everything before zOut[base] */ while( base+NHASH<lenOut ){ int iSrc, iBlock; int bestCnt, bestOfst=0, bestLitsz=0; hash_init(&h, &zOut[base]); i = 0; /* Trying to match a landmark against zOut[base+i] */ bestCnt = 0; while( 1 ){ int hv; int limit = 250; hv = hash_32bit(&h) % nHash; iBlock = landmark[hv]; while( iBlock>=0 && (limit--)>0 ){ /* ** The hash window has identified a potential match against ** landmark block iBlock. But we need to investigate further. ** ** Look for a region in zOut that matches zSrc. Anchor the search ** at zSrc[iSrc] and zOut[base+i]. Do not include anything prior to ** zOut[base] or after zOut[outLen] nor anything after zSrc[srcLen]. ** ** Set cnt equal to the length of the match and set ofst so that ** zSrc[ofst] is the first element of the match. litsz is the number ** of characters between zOut[base] and the beginning of the match. ** sz will be the overhead (in bytes) needed to encode the copy ** command. Only generate copy command if the overhead of the ** copy command is less than the amount of literal text to be copied. */ int cnt, ofst, litsz; int j, k, x, y; int sz; /* Beginning at iSrc, match forwards as far as we can. j counts ** the number of characters that match */ iSrc = iBlock*NHASH; for( j=0, x=iSrc, y=base+i; (unsigned int)x<lenSrc && (unsigned int)y<lenOut; j++, x++, y++ ){ if( zSrc[x]!=zOut[y] ) break; } j--; /* Beginning at iSrc-1, match backwards as far as we can. k counts ** the number of characters that match */ for(k=1; k<iSrc && (unsigned int)k<=i; k++){ if( zSrc[iSrc-k]!=zOut[base+i-k] ) break; } k--; /* Compute the offset and size of the matching region */ ofst = iSrc-k; cnt = j+k+1; litsz = i-k; /* Number of bytes of literal text before the copy */ /* sz will hold the number of bytes needed to encode the "insert" ** command and the copy command, not counting the "insert" text */ sz = digit_count(i-k)+digit_count(cnt)+digit_count(ofst)+3; if( cnt>=sz && cnt>bestCnt ){ /* Remember this match only if it is the best so far and it ** does not increase the file size */ bestCnt = cnt; bestOfst = iSrc-k; bestLitsz = litsz; } /* Check the next matching block */ iBlock = collide[iBlock]; } /* We have a copy command that does not cause the delta to be larger ** than a literal insert. So add the copy command to the delta. */ if( bestCnt>0 ){ if( bestLitsz>0 ){ /* Add an insert command before the copy */ putInt(bestLitsz,&zDelta); *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], bestLitsz); zDelta += bestLitsz; base += bestLitsz; } base += bestCnt; putInt(bestCnt, &zDelta); *(zDelta++) = '@'; putInt(bestOfst, &zDelta); *(zDelta++) = ','; if( bestOfst + bestCnt -1 > lastRead ){ lastRead = bestOfst + bestCnt - 1; } bestCnt = 0; break; } /* If we reach this point, it means no match is found so far */ if( base+i+NHASH>=lenOut ){ /* We have reached the end of the file and have not found any ** matches. Do an "insert" for everything that does not match */ putInt(lenOut-base, &zDelta); *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], lenOut-base); zDelta += lenOut-base; base = lenOut; break; } /* Advance the hash by one character. Keep looking for a match */ hash_next(&h, zOut[base+i+NHASH]); i++; } } /* Output a final "insert" record to get all the text at the end of ** the file that does not match anything in the source file. */ if( base<lenOut ){ putInt(lenOut-base, &zDelta); *(zDelta++) = ':'; memcpy(zDelta, &zOut[base], lenOut-base); zDelta += lenOut-base; } /* Output the final checksum record. */ putInt(checksum(zOut, lenOut), &zDelta); *(zDelta++) = ';'; sqlite3_free(collide); return zDelta - zOrigDelta; } /* ** End of code copied from fossil. **************************************************************************/ static void strPrintfArray( Str *pStr, /* String object to append to */ const char *zSep, /* Separator string */ const char *zFmt, /* Format for each entry */ char **az, int n /* Array of strings & its size (or -1) */ ){ int i; for(i=0; az[i] && (i<n || n<0); i++){ if( i!=0 ) strPrintf(pStr, "%s", zSep); strPrintf(pStr, zFmt, az[i], az[i], az[i]); } } static void getRbudiffQuery( const char *zTab, char **azCol, int nPK, int bOtaRowid, Str *pSql ){ int i; /* First the newly inserted rows: **/ strPrintf(pSql, "SELECT "); strPrintfArray(pSql, ", ", "%s", azCol, -1); strPrintf(pSql, ", 0, "); /* Set ota_control to 0 for an insert */ strPrintfArray(pSql, ", ", "NULL", azCol, -1); strPrintf(pSql, " FROM aux.%Q AS n WHERE NOT EXISTS (\n", zTab); strPrintf(pSql, " SELECT 1 FROM ", zTab); strPrintf(pSql, " main.%Q AS o WHERE ", zTab); strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK); strPrintf(pSql, "\n)"); /* Deleted rows: */ strPrintf(pSql, "\nUNION ALL\nSELECT "); strPrintfArray(pSql, ", ", "%s", azCol, nPK); if( azCol[nPK] ){ strPrintf(pSql, ", "); strPrintfArray(pSql, ", ", "NULL", &azCol[nPK], -1); } strPrintf(pSql, ", 1, "); /* Set ota_control to 1 for a delete */ strPrintfArray(pSql, ", ", "NULL", azCol, -1); strPrintf(pSql, " FROM main.%Q AS n WHERE NOT EXISTS (\n", zTab); strPrintf(pSql, " SELECT 1 FROM ", zTab); strPrintf(pSql, " aux.%Q AS o WHERE ", zTab); strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK); strPrintf(pSql, "\n) "); /* Updated rows. If all table columns are part of the primary key, there ** can be no updates. In this case this part of the compound SELECT can ** be omitted altogether. */ if( azCol[nPK] ){ strPrintf(pSql, "\nUNION ALL\nSELECT "); strPrintfArray(pSql, ", ", "n.%s", azCol, nPK); strPrintf(pSql, ",\n"); strPrintfArray(pSql, " ,\n", " CASE WHEN n.%s IS o.%s THEN NULL ELSE n.%s END", &azCol[nPK], -1 ); if( bOtaRowid==0 ){ strPrintf(pSql, ", '"); strPrintfArray(pSql, "", ".", azCol, nPK); strPrintf(pSql, "' ||\n"); }else{ strPrintf(pSql, ",\n"); } strPrintfArray(pSql, " ||\n", " CASE WHEN n.%s IS o.%s THEN '.' ELSE 'x' END", &azCol[nPK], -1 ); strPrintf(pSql, "\nAS ota_control, "); strPrintfArray(pSql, ", ", "NULL", azCol, nPK); strPrintf(pSql, ",\n"); strPrintfArray(pSql, " ,\n", " CASE WHEN n.%s IS o.%s THEN NULL ELSE o.%s END", &azCol[nPK], -1 ); strPrintf(pSql, "\nFROM main.%Q AS o, aux.%Q AS n\nWHERE ", zTab, zTab); strPrintfArray(pSql, " AND ", "(n.%Q IS o.%Q)", azCol, nPK); strPrintf(pSql, " AND ota_control LIKE '%%x%%'"); } /* Now add an ORDER BY clause to sort everything by PK. */ strPrintf(pSql, "\nORDER BY "); for(i=1; i<=nPK; i++) strPrintf(pSql, "%s%d", ((i>1)?", ":""), i); } static void rbudiff_one_table(const char *zTab, FILE *out){ int bOtaRowid; /* True to use an ota_rowid column */ int nPK; /* Number of primary key columns in table */ char **azCol; /* NULL terminated array of col names */ int i; int nCol; Str ct = {0, 0, 0}; /* The "CREATE TABLE data_xxx" statement */ Str sql = {0, 0, 0}; /* Query to find differences */ Str insert = {0, 0, 0}; /* First part of output INSERT statement */ sqlite3_stmt *pStmt = 0; /* --rbu mode must use real primary keys. */ g.bSchemaPK = 1; /* Check that the schemas of the two tables match. Exit early otherwise. */ checkSchemasMatch(zTab); /* Grab the column names and PK details for the table(s). If no usable PK ** columns are found, bail out early. */ azCol = columnNames("main", zTab, &nPK, &bOtaRowid); if( azCol==0 ){ runtimeError("table %s has no usable PK columns", zTab); } for(nCol=0; azCol[nCol]; nCol++); /* Build and output the CREATE TABLE statement for the data_xxx table */ strPrintf(&ct, "CREATE TABLE IF NOT EXISTS 'data_%q'(", zTab); if( bOtaRowid ) strPrintf(&ct, "rbu_rowid, "); strPrintfArray(&ct, ", ", "%s", &azCol[bOtaRowid], -1); strPrintf(&ct, ", rbu_control);"); /* Get the SQL for the query to retrieve data from the two databases */ getRbudiffQuery(zTab, azCol, nPK, bOtaRowid, &sql); /* Build the first part of the INSERT statement output for each row ** in the data_xxx table. */ strPrintf(&insert, "INSERT INTO 'data_%q' (", zTab); if( bOtaRowid ) strPrintf(&insert, "rbu_rowid, "); strPrintfArray(&insert, ", ", "%s", &azCol[bOtaRowid], -1); strPrintf(&insert, ", rbu_control) VALUES("); pStmt = db_prepare("%s", sql.z); while( sqlite3_step(pStmt)==SQLITE_ROW ){ /* If this is the first row output, print out the CREATE TABLE ** statement first. And then set ct.z to NULL so that it is not ** printed again. */ if( ct.z ){ fprintf(out, "%s\n", ct.z); strFree(&ct); } /* Output the first part of the INSERT statement */ fprintf(out, "%s", insert.z); if( sqlite3_column_type(pStmt, nCol)==SQLITE_INTEGER ){ for(i=0; i<=nCol; i++){ if( i>0 ) fprintf(out, ", "); printQuoted(out, sqlite3_column_value(pStmt, i)); } }else{ char *zOtaControl; int nOtaControl = sqlite3_column_bytes(pStmt, nCol); zOtaControl = (char*)sqlite3_malloc(nOtaControl); memcpy(zOtaControl, sqlite3_column_text(pStmt, nCol), nOtaControl+1); for(i=0; i<nCol; i++){ int bDone = 0; if( i>=nPK && sqlite3_column_type(pStmt, i)==SQLITE_BLOB && sqlite3_column_type(pStmt, nCol+1+i)==SQLITE_BLOB ){ const char *aSrc = sqlite3_column_blob(pStmt, nCol+1+i); int nSrc = sqlite3_column_bytes(pStmt, nCol+1+i); const char *aFinal = sqlite3_column_blob(pStmt, i); int nFinal = sqlite3_column_bytes(pStmt, i); char *aDelta; int nDelta; aDelta = sqlite3_malloc(nFinal + 60); nDelta = rbuDeltaCreate(aSrc, nSrc, aFinal, nFinal, aDelta); if( nDelta<nFinal ){ int j; fprintf(out, "x'"); for(j=0; j<nDelta; j++) fprintf(out, "%02x", (u8)aDelta[j]); fprintf(out, "'"); zOtaControl[i-bOtaRowid] = 'f'; bDone = 1; } sqlite3_free(aDelta); } if( bDone==0 ){ printQuoted(out, sqlite3_column_value(pStmt, i)); } fprintf(out, ", "); } fprintf(out, "'%s'", zOtaControl); sqlite3_free(zOtaControl); } /* And the closing bracket of the insert statement */ fprintf(out, ");\n"); } sqlite3_finalize(pStmt); strFree(&ct); strFree(&sql); strFree(&insert); } /* ** Display a summary of differences between two versions of the same ** table table. ** ** * Number of rows changed ** * Number of rows added |
︙ | ︙ | |||
756 757 758 759 760 761 762 | if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ fprintf(out, "%s: missing from first database\n", zTab); goto end_summarize_one_table; } | | | | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 | if( sqlite3_table_column_metadata(g.db,"main",zTab,0,0,0,0,0,0) ){ /* Table missing from source */ fprintf(out, "%s: missing from first database\n", zTab); goto end_summarize_one_table; } az = columnNames("main", zTab, &nPk, 0); az2 = columnNames("aux", zTab, &nPk2, 0); if( az && az2 ){ for(n=0; az[n]; n++){ if( sqlite3_stricmp(az[n],az2[n])!=0 ) break; } } if( az==0 || az2==0 |
︙ | ︙ | |||
927 928 929 930 931 932 933 | int *aiFlg = 0; /* 0 if column is not part of PK */ int *aiPk = 0; /* Column numbers for each PK column */ int nPk = 0; /* Number of PRIMARY KEY columns */ Str sql; /* SQL for the diff query */ int i, k; /* Loop counters */ const char *zSep; /* List separator */ | | < < | < < < | < < < < | 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 | int *aiFlg = 0; /* 0 if column is not part of PK */ int *aiPk = 0; /* Column numbers for each PK column */ int nPk = 0; /* Number of PRIMARY KEY columns */ Str sql; /* SQL for the diff query */ int i, k; /* Loop counters */ const char *zSep; /* List separator */ /* Check that the schemas of the two tables match. Exit early otherwise. */ checkSchemasMatch(zTab); pStmt = db_prepare("PRAGMA main.table_info=%Q", zTab); while( SQLITE_ROW==sqlite3_step(pStmt) ){ nCol++; azCol = sqlite3_realloc(azCol, sizeof(char*)*nCol); if( azCol==0 ) runtimeError("out of memory"); aiFlg = sqlite3_realloc(aiFlg, sizeof(int)*nCol); if( aiFlg==0 ) runtimeError("out of memory"); |
︙ | ︙ | |||
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 | printf("Usage: %s [options] DB1 DB2\n", g.zArgv0); printf( "Output SQL text that would transform DB1 into DB2.\n" "Options:\n" " --changeset FILE Write a CHANGESET into FILE\n" " -L|--lib LIBRARY Load an SQLite extension library\n" " --primarykey Use schema-defined PRIMARY KEYs\n" " --schema Show only differences in the schema\n" " --summary Show only a summary of the differences\n" " --table TAB Show only differences in table TAB\n" ); } int main(int argc, char **argv){ | > | 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 | printf("Usage: %s [options] DB1 DB2\n", g.zArgv0); printf( "Output SQL text that would transform DB1 into DB2.\n" "Options:\n" " --changeset FILE Write a CHANGESET into FILE\n" " -L|--lib LIBRARY Load an SQLite extension library\n" " --primarykey Use schema-defined PRIMARY KEYs\n" " --rbu Output SQL to create/populate RBU table(s)\n" " --schema Show only differences in the schema\n" " --summary Show only a summary of the differences\n" " --table TAB Show only differences in table TAB\n" ); } int main(int argc, char **argv){ |
︙ | ︙ | |||
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 | if( azExt==0 ) cmdlineError("out of memory"); azExt[nExt++] = argv[++i]; }else #endif if( strcmp(z,"primarykey")==0 ){ g.bSchemaPK = 1; }else if( strcmp(z,"schema")==0 ){ g.bSchemaOnly = 1; }else if( strcmp(z,"summary")==0 ){ xDiff = summarize_one_table; }else if( strcmp(z,"table")==0 ){ | > > > | 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 | if( azExt==0 ) cmdlineError("out of memory"); azExt[nExt++] = argv[++i]; }else #endif if( strcmp(z,"primarykey")==0 ){ g.bSchemaPK = 1; }else if( strcmp(z,"rbu")==0 ){ xDiff = rbudiff_one_table; }else if( strcmp(z,"schema")==0 ){ g.bSchemaOnly = 1; }else if( strcmp(z,"summary")==0 ){ xDiff = summarize_one_table; }else if( strcmp(z,"table")==0 ){ |
︙ | ︙ |
Changes to tool/tostr.awk.
1 2 3 4 5 6 7 8 | #!/usr/bin/awk # # Convert input text into a C string # { gsub(/\"/,"\\\""); print "\"" $0 "\\n\""; } | > | 1 2 3 4 5 6 7 8 9 | #!/usr/bin/awk # # Convert input text into a C string # { gsub(/\\/,"\\\\"); gsub(/\"/,"\\\""); print "\"" $0 "\\n\""; } |