Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge all the latest changes from the trunk into the apple-osx branch. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | apple-osx |
Files: | files | file ages | folders |
SHA1: |
c8bc057c7def6bbbebd89ee52b582bad |
User & Date: | drh 2010-11-19 23:50:48.000 |
Context
2010-12-07
| ||
15:49 | Merge in all changes to the trunk through version 3.7.4rc3. (check-in: d2ccf7fc06 user: drh tags: apple-osx) | |
2010-11-19
| ||
23:50 | Merge all the latest changes from the trunk into the apple-osx branch. (check-in: c8bc057c7d user: drh tags: apple-osx) | |
18:48 | Add tests for "PRAGMA checkpoint_fullfsync". (check-in: 765aa1b862 user: dan tags: trunk) | |
2010-11-09
| ||
00:43 | Integrated proxy locking file support for WAL journal mode and double free fix (check-in: fd4d38fa66 user: adam tags: apple-osx) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
22 23 24 25 26 27 28 | # BCC = @BUILD_CC@ @BUILD_CFLAGS@ # C Compile and options for use in building executables that # will run on the target platform. (BCC and TCC are usually the # same unless your are cross-compiling.) # | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | # BCC = @BUILD_CC@ @BUILD_CFLAGS@ # C Compile and options for use in building executables that # will run on the target platform. (BCC and TCC are usually the # same unless your are cross-compiling.) # TCC = @CC@ @CPPFLAGS@ @CFLAGS@ -I. -I${TOP}/src -I${TOP}/ext/rtree # Define this for the autoconf-based build, so that the code knows it can # include the generated config.h # TCC += -D_HAVE_SQLITE_CONFIG_H # Define -DNDEBUG to compile without debugging (i.e., for production usage) |
︙ | ︙ | |||
163 164 165 166 167 168 169 | USE_AMALGAMATION = @USE_AMALGAMATION@ # Object files for the SQLite library (non-amalgamation). # LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ backup.lo bitvec.lo btmutex.lo btree.lo build.lo \ | | > | | 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 | USE_AMALGAMATION = @USE_AMALGAMATION@ # Object files for the SQLite library (non-amalgamation). # LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ backup.lo bitvec.lo btmutex.lo btree.lo build.lo \ callback.lo complete.lo ctime.lo date.lo delete.lo \ expr.lo fault.lo fkey.lo \ fts3.lo fts3_expr.lo fts3_hash.lo fts3_icu.lo fts3_porter.lo \ fts3_snippet.lo fts3_tokenizer.lo fts3_tokenizer1.lo fts3_write.lo \ func.lo global.lo hash.lo \ icu.lo insert.lo journal.lo legacy.lo loadext.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memjournal.lo \ mutex.lo mutex_noop.lo mutex_os2.lo mutex_unix.lo mutex_w32.lo \ notify.lo opcodes.lo os.lo os_os2.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo rtree.lo select.lo status.lo \ table.lo tokenize.lo trigger.lo \ update.lo util.lo vacuum.lo \ vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbetrace.lo \ wal.lo walker.lo where.lo utf.lo vtab.lo # Object files for the amalgamation. # LIBOBJS1 = sqlite3.lo # Determine the real value of LIBOBJ based on the 'configure' script # |
︙ | ︙ | |||
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c | > > > | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_multiplex.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_quota.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c |
︙ | ︙ | |||
459 460 461 462 463 464 465 466 467 468 469 470 471 472 | $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_tokenizer.h EXTHDR += \ $(TOP)/ext/rtree/rtree.h EXTHDR += \ $(TOP)/ext/icu/sqliteicu.h EXTHDR += \ $(TOP)/ext/sqlrr/sqlrr.h # If using the amalgamation, use sqlite3.c directly to build the test # fixture. Otherwise link against libsqlite3.la. (This distinction is # necessary because the test fixture requires non-API symbols which are # hidden when the library is built via the amalgamation). | > > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_tokenizer.h EXTHDR += \ $(TOP)/ext/rtree/rtree.h EXTHDR += \ $(TOP)/ext/icu/sqliteicu.h EXTHDR += \ $(TOP)/ext/rtree/sqlite3rtree.h EXTHDR += \ $(TOP)/ext/sqlrr/sqlrr.h # If using the amalgamation, use sqlite3.c directly to build the test # fixture. Otherwise link against libsqlite3.la. (This distinction is # necessary because the test fixture requires non-API symbols which are # hidden when the library is built via the amalgamation). |
︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 3.7.4 |
Added art/sqlite370.ico.
cannot compute difference between binary files
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.62 for sqlite 3.7.4. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## |
︙ | ︙ | |||
739 740 741 742 743 744 745 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' | | | | 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 | MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' PACKAGE_VERSION='3.7.4' PACKAGE_STRING='sqlite 3.7.4' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> |
︙ | ︙ | |||
1483 1484 1485 1486 1487 1488 1489 | # # 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 | | | 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | # # 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.7.4 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. |
︙ | ︙ | |||
1548 1549 1550 1551 1552 1553 1554 | --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 | | | 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 | --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.7.4:";; 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] |
︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | | 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 | 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.7.4 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 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 fi 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.7.4, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
13968 13969 13970 13971 13972 13973 13974 | exec 6>&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=" | | | 13968 13969 13970 13971 13972 13973 13974 13975 13976 13977 13978 13979 13980 13981 13982 | exec 6>&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.7.4, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ |
︙ | ︙ | |||
14021 14022 14023 14024 14025 14026 14027 | $config_commands Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ | | | 14021 14022 14023 14024 14025 14026 14027 14028 14029 14030 14031 14032 14033 14034 14035 | $config_commands Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ sqlite config.status 3.7.4 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 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/fts2/fts2.c.
︙ | ︙ | |||
302 303 304 305 306 307 308 | # define SQLITE_CORE 1 #endif #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> | < < | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | # define SQLITE_CORE 1 #endif #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include "fts2.h" #include "fts2_hash.h" #include "fts2_tokenizer.h" #include "sqlite3.h" #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 |
︙ | ︙ | |||
341 342 343 344 345 346 347 | */ /* TODO(shess) The snippet-generation code should be using the ** tokenizer-generated tokens rather than doing its own local ** tokenization. */ /* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */ static int safe_isspace(char c){ | | | | | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | */ /* TODO(shess) The snippet-generation code should be using the ** tokenizer-generated tokens rather than doing its own local ** tokenization. */ /* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */ static int safe_isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } static int safe_tolower(char c){ return (c>='A' && c<='Z') ? (c - 'A' + 'a') : c; } static int safe_isalnum(char c){ return (c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z'); } typedef enum DocListType { DL_DOCIDS, /* docids only */ DL_POSITIONS, /* docids + positions */ DL_POSITIONS_OFFSETS /* docids + positions + offsets */ } DocListType; |
︙ | ︙ |
Changes to ext/fts2/fts2_porter.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> | < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include "fts2_tokenizer.h" /* ** Class derived from sqlite3_tokenizer */ typedef struct porter_tokenizer { |
︙ | ︙ |
Changes to ext/fts2/fts2_tokenizer1.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> | < | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS2) #include <assert.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include "fts2_tokenizer.h" typedef struct simple_tokenizer { sqlite3_tokenizer base; char delim[128]; /* flag ASCII delimiters */ } simple_tokenizer; |
︙ | ︙ | |||
85 86 87 88 89 90 91 | } t->delim[ch] = 1; } } else { /* Mark non-alphanumeric ASCII characters as delimiters */ int i; for(i=1; i<0x80; i++){ | | > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | } t->delim[ch] = 1; } } else { /* Mark non-alphanumeric ASCII characters as delimiters */ int i; for(i=1; i<0x80; i++){ t->delim[i] = !((i>='0' && i<='9') || (i>='A' && i<='Z') || (i>='a' && i<='z')); } } *ppTokenizer = &t->base; return SQLITE_OK; } |
︙ | ︙ | |||
187 188 189 190 191 192 193 | if( c->pToken==NULL ) return SQLITE_NOMEM; } for(i=0; i<n; i++){ /* TODO(shess) This needs expansion to handle UTF-8 ** case-insensitivity. */ unsigned char ch = p[iStartOffset+i]; | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | if( c->pToken==NULL ) return SQLITE_NOMEM; } for(i=0; i<n; i++){ /* TODO(shess) This needs expansion to handle UTF-8 ** case-insensitivity. */ unsigned char ch = p[iStartOffset+i]; c->pToken[i] = (ch>='A' && ch<='Z') ? (ch - 'A' + 'a') : ch; } *ppToken = c->pToken; *pnBytes = n; *piStartOffset = iStartOffset; *piEndOffset = c->iOffset; *piPosition = c->iToken++; |
︙ | ︙ |
Changes to ext/fts3/fts3.c.
︙ | ︙ | |||
437 438 439 440 441 442 443 444 445 446 447 448 | ** The xDisconnect() virtual table method. */ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); /* Free any prepared statements held */ for(i=0; i<SizeofArray(p->aStmt); i++){ sqlite3_finalize(p->aStmt[i]); } | > < < < | < | | 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 | ** The xDisconnect() virtual table method. */ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); assert( p->pSegments==0 ); /* Free any prepared statements held */ for(i=0; i<SizeofArray(p->aStmt); i++){ sqlite3_finalize(p->aStmt[i]); } sqlite3_free(p->zSegmentsTbl); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); sqlite3_free(p); return SQLITE_OK; } /* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is written ** into *pRc. ** ** If *pRc is initially non-zero then this routine is a no-op. */ static void fts3DbExec( int *pRc, /* Success code */ sqlite3 *db, /* Database in which to run SQL */ |
︙ | ︙ | |||
509 510 511 512 513 514 515 516 | } /* ** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table ** passed as the first argument. This is done as part of the xConnect() ** and xCreate() methods. */ | > > > > | > | | | | | | | | | | | | | < | | | | | | | | > < | | | | | | | | | | | | < | 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 | } /* ** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table ** passed as the first argument. This is done as part of the xConnect() ** and xCreate() methods. ** ** If *pRc is non-zero when this function is called, it is a no-op. ** Otherwise, if an error occurs, an SQLite error code is stored in *pRc ** before returning. */ static void fts3DeclareVtab(int *pRc, Fts3Table *p){ if( *pRc==SQLITE_OK ){ int i; /* Iterator variable */ int rc; /* Return code */ char *zSql; /* SQL statement passed to declare_vtab() */ char *zCols; /* List of user defined columns */ /* Create a list of user columns for the virtual table */ zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); for(i=1; zCols && i<p->nColumn; i++){ zCols = sqlite3_mprintf("%z%Q, ", zCols, p->azColumn[i]); } /* Create the whole "CREATE TABLE" statement to pass to SQLite */ zSql = sqlite3_mprintf( "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN)", zCols, p->zName ); if( !zCols || !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_declare_vtab(p->db, zSql); } sqlite3_free(zSql); sqlite3_free(zCols); *pRc = rc; } } /* ** Create the backing store tables (%_content, %_segments and %_segdir) ** required by the FTS3 table passed as the only argument. This is done ** as part of the vtab xCreate() method. ** ** If the p->bHasDocsize boolean is true (indicating that this is an ** FTS4 table, not an FTS3 table) then also create the %_docsize and ** %_stat tables required by FTS4. */ static int fts3CreateTables(Fts3Table *p){ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ char *zContentCols; /* Columns of %_content table */ sqlite3 *db = p->db; /* The database connection */ /* Create a list of user columns for the content table */ zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); for(i=0; zContentCols && i<p->nColumn; i++){ char *z = p->azColumn[i]; zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); } if( zContentCols==0 ) rc = SQLITE_NOMEM; /* Create the content table */ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_content'(%s)", p->zDb, p->zName, zContentCols ); sqlite3_free(zContentCols); /* Create other tables */ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", p->zDb, p->zName ); fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_segdir'(" |
︙ | ︙ | |||
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | p->zDb, p->zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", p->zDb, p->zName ); fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);", p->zDb, p->zName ); } return rc; } /* | > > | > > > > | | > > > | > > | > > > > > | > > > | > | | > < > > > > > > | < < < | > | < < < | | | > > > > | | < | > | > | < > | 591 592 593 594 595 596 597 598 599 600 601 602 603 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 | p->zDb, p->zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", p->zDb, p->zName ); } if( p->bHasStat ){ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);", p->zDb, p->zName ); } return rc; } /* ** Store the current database page-size in bytes in p->nPgsz. ** ** If *pRc is non-zero when this function is called, it is a no-op. ** Otherwise, if an error occurs, an SQLite error code is stored in *pRc ** before returning. */ static void fts3DatabasePageSize(int *pRc, Fts3Table *p){ if( *pRc==SQLITE_OK ){ int rc; /* Return code */ char *zSql; /* SQL text "PRAGMA %Q.page_size" */ sqlite3_stmt *pStmt; /* Compiled "PRAGMA %Q.page_size" statement */ zSql = sqlite3_mprintf("PRAGMA %Q.page_size", p->zDb); if( !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_step(pStmt); p->nPgsz = sqlite3_column_int(pStmt, 0); rc = sqlite3_finalize(pStmt); } } assert( p->nPgsz>0 || rc!=SQLITE_OK ); sqlite3_free(zSql); *pRc = rc; } } /* ** "Special" FTS4 arguments are column specifications of the following form: ** ** <key> = <value> ** ** There may not be whitespace surrounding the "=" character. The <value> ** term may be quoted, but the <key> may not. */ static int fts3IsSpecialColumn( const char *z, int *pnKey, char **pzValue ){ char *zValue; const char *zCsr = z; while( *zCsr!='=' ){ if( *zCsr=='\0' ) return 0; zCsr++; } *pnKey = zCsr-z; zValue = sqlite3_mprintf("%s", &zCsr[1]); if( zValue ){ sqlite3Fts3Dequote(zValue); } *pzValue = zValue; return 1; } /* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. ** ** The argv[] array contains the following: |
︙ | ︙ | |||
656 657 658 659 660 661 662 | void *pAux, /* Hash table containing tokenizers */ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ Fts3Hash *pHash = (Fts3Hash *)pAux; | | | | | > > > > > > > > > > > > > > > > > > > > > > | < < | > | > | | > > > > | > > > | > > > > | > | | > > > | | > > | > > > > | > > > > > > > > > > > > < | | > | < < | | | | | | | | | < < < < < < < < < < < < > > > > | < > | | > | > > | 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 | void *pAux, /* Hash table containing tokenizers */ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ Fts3Hash *pHash = (Fts3Hash *)pAux; Fts3Table *p = 0; /* Pointer to allocated vtab */ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ int nByte; /* Size of allocation used for *p */ int iCol; /* Column index */ int nString = 0; /* Bytes required to hold all column names */ int nCol = 0; /* Number of columns in the FTS table */ char *zCsr; /* Space for holding column names */ int nDb; /* Bytes required to hold database name */ int nName; /* Bytes required to hold table name */ int isFts4 = (argv[0][3]=='4'); /* True for FTS4, false for FTS3 */ int bNoDocsize = 0; /* True to omit %_docsize table */ const char **aCol; /* Array of column names */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ assert( strlen(argv[0])==4 ); assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4) || (sqlite3_strnicmp(argv[0], "fts3", 4)==0 && !isFts4) ); nDb = (int)strlen(argv[1]) + 1; nName = (int)strlen(argv[2]) + 1; aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) ); if( !aCol ) return SQLITE_NOMEM; memset(aCol, 0, sizeof(const char *) * (argc-2)); /* Loop through all of the arguments passed by the user to the FTS3/4 ** module (i.e. all the column names and special arguments). This loop ** does the following: ** ** + Figures out the number of columns the FTSX table will have, and ** the number of bytes of space that must be allocated to store copies ** of the column names. ** ** + If there is a tokenizer specification included in the arguments, ** initializes the tokenizer pTokenizer. */ for(i=3; rc==SQLITE_OK && i<argc; i++){ char const *z = argv[i]; int nKey; char *zVal; /* Check if this is a tokenizer specification */ if( !pTokenizer && strlen(z)>8 && 0==sqlite3_strnicmp(z, "tokenize", 8) && 0==sqlite3Fts3IsIdChar(z[8]) ){ rc = sqlite3Fts3InitTokenizer(pHash, &z[9], &pTokenizer, pzErr); } /* Check if it is an FTS4 special argument. */ else if( isFts4 && fts3IsSpecialColumn(z, &nKey, &zVal) ){ if( !zVal ){ rc = SQLITE_NOMEM; goto fts3_init_out; } if( nKey==9 && 0==sqlite3_strnicmp(z, "matchinfo", 9) ){ if( strlen(zVal)==4 && 0==sqlite3_strnicmp(zVal, "fts3", 4) ){ bNoDocsize = 1; }else{ *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal); rc = SQLITE_ERROR; } }else{ *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); rc = SQLITE_ERROR; } sqlite3_free(zVal); } /* Otherwise, the argument is a column name. */ else { nString += (int)(strlen(z) + 1); aCol[nCol++] = z; } } if( rc!=SQLITE_OK ) goto fts3_init_out; if( nCol==0 ){ assert( nString==0 ); aCol[0] = "content"; nString = 8; nCol = 1; } if( pTokenizer==0 ){ rc = sqlite3Fts3InitTokenizer(pHash, "simple", &pTokenizer, pzErr); if( rc!=SQLITE_OK ) goto fts3_init_out; } assert( pTokenizer ); /* Allocate and populate the Fts3Table structure. */ nByte = sizeof(Fts3Table) + /* Fts3Table */ nCol * sizeof(char *) + /* azColumn */ nName + /* zName */ nDb + /* zDb */ nString; /* Space for azColumn strings */ p = (Fts3Table*)sqlite3_malloc(nByte); if( p==0 ){ rc = SQLITE_NOMEM; goto fts3_init_out; } memset(p, 0, nByte); p->db = db; p->nColumn = nCol; p->nPendingData = 0; p->azColumn = (char **)&p[1]; p->pTokenizer = pTokenizer; p->nNodeSize = 1000; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; p->bHasDocsize = (isFts4 && bNoDocsize==0); p->bHasStat = isFts4; fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1); /* Fill in the zName and zDb fields of the vtab structure. */ zCsr = (char *)&p->azColumn[nCol]; p->zName = zCsr; memcpy(zCsr, argv[2], nName); zCsr += nName; p->zDb = zCsr; memcpy(zCsr, argv[1], nDb); zCsr += nDb; /* Fill in the azColumn array */ for(iCol=0; iCol<nCol; iCol++){ char *z; int n; z = (char *)sqlite3Fts3NextToken(aCol[iCol], &n); memcpy(zCsr, z, n); zCsr[n] = '\0'; sqlite3Fts3Dequote(zCsr); p->azColumn[iCol] = zCsr; zCsr += n+1; assert( zCsr <= &((char *)p)[nByte] ); } /* If this is an xCreate call, create the underlying tables in the ** database. TODO: For xConnect(), it could verify that said tables exist. */ if( isCreate ){ rc = fts3CreateTables(p); } /* Figure out the page-size for the database. This is required in order to ** estimate the cost of loading large doclists from the database (see ** function sqlite3Fts3SegReaderCost() for details). */ fts3DatabasePageSize(&rc, p); /* Declare the table schema to SQLite. */ fts3DeclareVtab(&rc, p); fts3_init_out: sqlite3_free(aCol); if( rc!=SQLITE_OK ){ if( p ){ fts3DisconnectMethod((sqlite3_vtab *)p); }else if( pTokenizer ){ pTokenizer->pModule->xDestroy(pTokenizer); } }else{ *ppVTab = &p->base; } return rc; } /* ** The xConnect() and xCreate() methods for the virtual table. All the ** work is done in function fts3InitVtab(). |
︙ | ︙ | |||
885 886 887 888 889 890 891 | return SQLITE_OK; } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ | | > > | 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | return SQLITE_OK; } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 ); sqlite3_finalize(pCsr->pStmt); sqlite3Fts3ExprFree(pCsr->pExpr); sqlite3Fts3FreeDeferredTokens(pCsr); sqlite3_free(pCsr->aDoclist); sqlite3_free(pCsr->aMatchinfo); sqlite3_free(pCsr); return SQLITE_OK; } /* |
︙ | ︙ | |||
927 928 929 930 931 932 933 | } }else{ return SQLITE_OK; } } /* | | | | | > > | > > > > | < < | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | > > > > > | > > > > > > > | > | > > > > > | | > > | > > > > | > > > > > > > > > > > > > > | > > > > | > > | > > | > > | | | | | | > > > > > | > | | | < | > | < < < | | < < < < | < > > | < < < > | | | < | | < < < < < < < < < | > | > > | | < < < | < < < < < < < < < < | < < > | < < < | | < > | < < < < | < | 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 | } }else{ return SQLITE_OK; } } /* ** This function is used to process a single interior node when searching ** a b-tree for a term or term prefix. The node data is passed to this ** function via the zNode/nNode parameters. The term to search for is ** passed in zTerm/nTerm. ** ** If piFirst is not NULL, then this function sets *piFirst to the blockid ** of the child node that heads the sub-tree that may contain the term. ** ** If piLast is not NULL, then *piLast is set to the right-most child node ** that heads a sub-tree that may contain a term for which zTerm/nTerm is ** a prefix. ** ** If an OOM error occurs, SQLITE_NOMEM is returned. Otherwise, SQLITE_OK. */ static int fts3ScanInteriorNode( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to select leaves for */ int nTerm, /* Size of term zTerm in bytes */ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piFirst, /* OUT: Selected child node */ sqlite3_int64 *piLast /* OUT: Selected child node */ ){ int rc = SQLITE_OK; /* Return code */ const char *zCsr = zNode; /* Cursor to iterate through node */ const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ char *zBuffer = 0; /* Buffer to load terms into */ int nAlloc = 0; /* Size of allocated buffer */ int isFirstTerm = 1; /* True when processing first term on page */ sqlite3_int64 iChild; /* Block id of child node to descend to */ /* Skip over the 'height' varint that occurs at the start of every ** interior node. Then load the blockid of the left-child of the b-tree ** node into variable iChild. ** ** Even if the data structure on disk is corrupted, this (reading two ** varints from the buffer) does not risk an overread. If zNode is a ** root node, then the buffer comes from a SELECT statement. SQLite does ** not make this guarantee explicitly, but in practice there are always ** either more than 20 bytes of allocated space following the nNode bytes of ** contents, or two zero bytes. Or, if the node is read from the %_segments ** table, then there are always 20 bytes of zeroed padding following the ** nNode bytes of content (see sqlite3Fts3ReadBlock() for details). */ zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); if( zCsr>=zEnd ){ return SQLITE_CORRUPT; } while( zCsr<zEnd && (piFirst || piLast) ){ int cmp; /* memcmp() result */ int nSuffix; /* Size of term suffix */ int nPrefix = 0; /* Size of term prefix */ int nBuffer; /* Total term size */ /* Load the next term on the node into zBuffer. Use realloc() to expand ** the size of zBuffer if required. */ if( !isFirstTerm ){ zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){ rc = SQLITE_CORRUPT; goto finish_scan; } if( nPrefix+nSuffix>nAlloc ){ char *zNew; nAlloc = (nPrefix+nSuffix) * 2; zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); if( !zNew ){ rc = SQLITE_NOMEM; goto finish_scan; } zBuffer = zNew; } memcpy(&zBuffer[nPrefix], zCsr, nSuffix); nBuffer = nPrefix + nSuffix; zCsr += nSuffix; /* Compare the term we are searching for with the term just loaded from ** the interior node. If the specified term is greater than or equal ** to the term from the interior node, then all terms on the sub-tree ** headed by node iChild are smaller than zTerm. No need to search ** iChild. ** ** If the interior node term is larger than the specified term, then ** the tree headed by iChild may contain the specified term. */ cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); if( piFirst && (cmp<0 || (cmp==0 && nBuffer>nTerm)) ){ *piFirst = iChild; piFirst = 0; } if( piLast && cmp<0 ){ *piLast = iChild; piLast = 0; } iChild++; }; if( piFirst ) *piFirst = iChild; if( piLast ) *piLast = iChild; finish_scan: sqlite3_free(zBuffer); return rc; } /* ** The buffer pointed to by argument zNode (size nNode bytes) contains an ** interior node of a b-tree segment. The zTerm buffer (size nTerm bytes) ** contains a term. This function searches the sub-tree headed by the zNode ** node for the range of leaf nodes that may contain the specified term ** or terms for which the specified term is a prefix. ** ** If piLeaf is not NULL, then *piLeaf is set to the blockid of the ** left-most leaf node in the tree that may contain the specified term. ** If piLeaf2 is not NULL, then *piLeaf2 is set to the blockid of the ** right-most leaf node that may contain a term for which the specified ** term is a prefix. ** ** It is possible that the range of returned leaf nodes does not contain ** the specified term or any terms for which it is a prefix. However, if the ** segment does contain any such terms, they are stored within the identified ** range. Because this function only inspects interior segment nodes (and ** never loads leaf nodes into memory), it is not possible to be sure. ** ** If an error occurs, an error code other than SQLITE_OK is returned. */ static int fts3SelectLeaf( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to select leaves for */ int nTerm, /* Size of term zTerm in bytes */ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piLeaf, /* Selected leaf node */ sqlite3_int64 *piLeaf2 /* Selected leaf node */ ){ int rc; /* Return code */ int iHeight; /* Height of this node in tree */ assert( piLeaf || piLeaf2 ); sqlite3Fts3GetVarint32(zNode, &iHeight); rc = fts3ScanInteriorNode(p, zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2); assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) ); if( rc==SQLITE_OK && iHeight>1 ){ char *zBlob = 0; /* Blob read from %_segments table */ int nBlob; /* Size of zBlob in bytes */ if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){ rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob); if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, 0); } sqlite3_free(zBlob); piLeaf = 0; zBlob = 0; } if( rc==SQLITE_OK ){ rc = sqlite3Fts3ReadBlock(p, piLeaf ? *piLeaf : *piLeaf2, &zBlob, &nBlob); } if( rc==SQLITE_OK ){ rc = fts3SelectLeaf(p, zTerm, nTerm, zBlob, nBlob, piLeaf, piLeaf2); } sqlite3_free(zBlob); } return rc; } /* ** This function is used to create delta-encoded serialized lists of FTS3 ** varints. Each call to this function appends a single varint to a list. */ |
︙ | ︙ | |||
1282 1283 1284 1285 1286 1287 1288 1289 1290 | *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; } /* ** nToken==1 searches for adjacent positions. */ static int fts3PoslistPhraseMerge( | > > > > > > > > > > > > > > > > > > > > | > | | < > > > > | 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; } /* ** nToken==1 searches for adjacent positions. ** ** This function is used to merge two position lists into one. When it is ** called, *pp1 and *pp2 must both point to position lists. A position-list is ** the part of a doclist that follows each document id. For example, if a row ** contains: ** ** 'a b c'|'x y z'|'a b b a' ** ** Then the position list for this row for token 'b' would consist of: ** ** 0x02 0x01 0x02 0x03 0x03 0x00 ** ** When this function returns, both *pp1 and *pp2 are left pointing to the ** byte following the 0x00 terminator of their respective position lists. ** ** If isSaveLeft is 0, an entry is added to the output position list for ** each position in *pp2 for which there exists one or more positions in ** *pp1 so that (pos(*pp2)>pos(*pp1) && pos(*pp2)-pos(*pp1)<=nToken). i.e. ** when the *pp1 token appears before the *pp2 token, but not more than nToken ** slots before it. */ static int fts3PoslistPhraseMerge( char **pp, /* IN/OUT: Preallocated output buffer */ int nToken, /* Maximum difference in token positions */ int isSaveLeft, /* Save the left position */ int isExact, /* If *pp1 is exactly nTokens before *pp2 */ char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ char *p = (pp ? *pp : 0); char *p1 = *pp1; char *p2 = *pp2; int iCol1 = 0; int iCol2 = 0; /* Never set both isSaveLeft and isExact for the same invocation. */ assert( isSaveLeft==0 || isExact==0 ); assert( *p1!=0 && *p2!=0 ); if( *p1==POS_COLUMN ){ p1++; p1 += sqlite3Fts3GetVarint32(p1, &iCol1); } if( *p2==POS_COLUMN ){ p2++; |
︙ | ︙ | |||
1324 1325 1326 1327 1328 1329 1330 | assert( *p1!=POS_END && *p1!=POS_COLUMN ); assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; while( 1 ){ | > | > | 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 | assert( *p1!=POS_END && *p1!=POS_COLUMN ); assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; while( 1 ){ if( iPos2==iPos1+nToken || (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken) ){ sqlite3_int64 iSave; if( !pp ){ fts3PoslistCopy(0, &p2); fts3PoslistCopy(0, &p1); *pp1 = p1; *pp2 = p2; return 1; |
︙ | ︙ | |||
1407 1408 1409 1410 1411 1412 1413 | char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ char *p1 = *pp1; char *p2 = *pp2; if( !pp ){ | | | | | | 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 | char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ char *p1 = *pp1; char *p2 = *pp2; if( !pp ){ if( fts3PoslistPhraseMerge(0, nRight, 0, 0, pp1, pp2) ) return 1; *pp1 = p1; *pp2 = p2; return fts3PoslistPhraseMerge(0, nLeft, 0, 0, pp2, pp1); }else{ char *pTmp1 = aTmp; char *pTmp2; char *aTmp2; int res = 1; fts3PoslistPhraseMerge(&pTmp1, nRight, 0, 0, pp1, pp2); aTmp2 = pTmp2 = pTmp1; *pp1 = p1; *pp2 = p2; fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, 0, pp2, pp1); if( pTmp1!=aTmp && pTmp2!=aTmp2 ){ fts3PoslistMerge(pp, &aTmp, &aTmp2); }else if( pTmp1!=aTmp ){ fts3PoslistCopy(pp, &aTmp); }else if( pTmp2!=aTmp2 ){ fts3PoslistCopy(pp, &aTmp2); }else{ |
︙ | ︙ | |||
1467 1468 1469 1470 1471 1472 1473 | int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ char *aBuffer, /* Pre-allocated output buffer */ int *pnBuffer, /* OUT: Bytes written to aBuffer */ char *a1, /* Buffer containing first doclist */ int n1, /* Size of buffer a1 */ char *a2, /* Buffer containing second doclist */ | | > > | 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 | int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ char *aBuffer, /* Pre-allocated output buffer */ int *pnBuffer, /* OUT: Bytes written to aBuffer */ char *a1, /* Buffer containing first doclist */ int n1, /* Size of buffer a1 */ char *a2, /* Buffer containing second doclist */ int n2, /* Size of buffer a2 */ int *pnDoc /* OUT: Number of docids in output */ ){ sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; char *p = aBuffer; char *p1 = a1; char *p2 = a2; char *pEnd1 = &a1[n1]; char *pEnd2 = &a2[n2]; int nDoc = 0; assert( mergetype==MERGE_OR || mergetype==MERGE_POS_OR || mergetype==MERGE_AND || mergetype==MERGE_NOT || mergetype==MERGE_PHRASE || mergetype==MERGE_POS_PHRASE || mergetype==MERGE_NEAR || mergetype==MERGE_POS_NEAR ); |
︙ | ︙ | |||
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 | case MERGE_AND: while( p1 && p2 ){ if( i1==i2 ){ fts3PutDeltaVarint(&p, &iPrev, i1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); }else if( i1<i2 ){ fts3GetDeltaVarint2(&p1, pEnd1, &i1); }else{ fts3GetDeltaVarint2(&p2, pEnd2, &i2); } } break; | > | 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 | case MERGE_AND: while( p1 && p2 ){ if( i1==i2 ){ fts3PutDeltaVarint(&p, &iPrev, i1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); nDoc++; }else if( i1<i2 ){ fts3GetDeltaVarint2(&p1, pEnd1, &i1); }else{ fts3GetDeltaVarint2(&p2, pEnd2, &i2); } } break; |
︙ | ︙ | |||
1551 1552 1553 1554 1555 1556 1557 | case MERGE_PHRASE: { char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); while( p1 && p2 ){ if( i1==i2 ){ char *pSave = p; sqlite3_int64 iPrevSave = iPrev; fts3PutDeltaVarint(&p, &iPrev, i1); | | > > | 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 | case MERGE_PHRASE: { char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); while( p1 && p2 ){ if( i1==i2 ){ char *pSave = p; sqlite3_int64 iPrevSave = iPrev; fts3PutDeltaVarint(&p, &iPrev, i1); if( 0==fts3PoslistPhraseMerge(ppPos, nParam1, 0, 1, &p1, &p2) ){ p = pSave; iPrev = iPrevSave; }else{ nDoc++; } fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); }else if( i1<i2 ){ fts3PoslistCopy(0, &p1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); }else{ |
︙ | ︙ | |||
1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 | } } sqlite3_free(aTmp); break; } } *pnBuffer = (int)(p-aBuffer); return SQLITE_OK; } /* ** A pointer to an instance of this structure is used as the context ** argument to sqlite3Fts3SegReaderIterate() | > | 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 | } } sqlite3_free(aTmp); break; } } if( pnDoc ) *pnDoc = nDoc; *pnBuffer = (int)(p-aBuffer); return SQLITE_OK; } /* ** A pointer to an instance of this structure is used as the context ** argument to sqlite3Fts3SegReaderIterate() |
︙ | ︙ | |||
1644 1645 1646 1647 1648 1649 1650 | ** into a single doclist. */ for(i=0; i<SizeofArray(pTS->aaOutput); i++){ if( pTS->aaOutput[i] ){ if( !aOut ){ aOut = pTS->aaOutput[i]; nOut = pTS->anOutput[i]; | | | | 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 | ** into a single doclist. */ for(i=0; i<SizeofArray(pTS->aaOutput); i++){ if( pTS->aaOutput[i] ){ if( !aOut ){ aOut = pTS->aaOutput[i]; nOut = pTS->anOutput[i]; pTS->aaOutput[i] = 0; }else{ int nNew = nOut + pTS->anOutput[i]; char *aNew = sqlite3_malloc(nNew); if( !aNew ){ sqlite3_free(aOut); return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut, 0 ); sqlite3_free(pTS->aaOutput[i]); sqlite3_free(aOut); pTS->aaOutput[i] = 0; aOut = aNew; nOut = nNew; } |
︙ | ︙ | |||
1724 1725 1726 1727 1728 1729 1730 | aNew = sqlite3_malloc(nNew); if( !aNew ){ if( aMerge!=aDoclist ){ sqlite3_free(aMerge); } return SQLITE_NOMEM; } | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | < < < < < < < < < < < < < | < < < < < < < < < | | | | | | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > | > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | > > > > > > > > > > | | > > > > > | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > | > > > > | | < < < > | | | | > > > > > > > > > > > > > > > | > > | < > > > > > > > > > > > > | | | > > > > > > > > > > | > > > > > | | | 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 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 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 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 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 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 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 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 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 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 | aNew = sqlite3_malloc(nNew); if( !aNew ){ if( aMerge!=aDoclist ){ sqlite3_free(aMerge); } return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[iOut], pTS->anOutput[iOut], aMerge, nMerge, 0 ); if( iOut>0 ) sqlite3_free(aMerge); sqlite3_free(pTS->aaOutput[iOut]); pTS->aaOutput[iOut] = 0; aMerge = aNew; nMerge = nNew; if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ pTS->aaOutput[iOut] = aMerge; pTS->anOutput[iOut] = nMerge; } } } return SQLITE_OK; } static int fts3DeferredTermSelect( Fts3DeferredToken *pToken, /* Phrase token */ int isTermPos, /* True to include positions */ int *pnOut, /* OUT: Size of list */ char **ppOut /* OUT: Body of list */ ){ char *aSource; int nSource; aSource = sqlite3Fts3DeferredDoclist(pToken, &nSource); if( !aSource ){ *pnOut = 0; *ppOut = 0; }else if( isTermPos ){ *ppOut = sqlite3_malloc(nSource); if( !*ppOut ) return SQLITE_NOMEM; memcpy(*ppOut, aSource, nSource); *pnOut = nSource; }else{ sqlite3_int64 docid; *pnOut = sqlite3Fts3GetVarint(aSource, &docid); *ppOut = sqlite3_malloc(*pnOut); if( !*ppOut ) return SQLITE_NOMEM; sqlite3Fts3PutVarint(*ppOut, docid); } return SQLITE_OK; } /* ** An Fts3SegReaderArray is used to store an array of Fts3SegReader objects. ** Elements are added to the array using fts3SegReaderArrayAdd(). */ struct Fts3SegReaderArray { int nSegment; /* Number of valid entries in apSegment[] */ int nAlloc; /* Allocated size of apSegment[] */ int nCost; /* The cost of executing SegReaderIterate() */ Fts3SegReader *apSegment[1]; /* Array of seg-reader objects */ }; /* ** Free an Fts3SegReaderArray object. Also free all seg-readers in the ** array (using sqlite3Fts3SegReaderFree()). */ static void fts3SegReaderArrayFree(Fts3SegReaderArray *pArray){ if( pArray ){ int i; for(i=0; i<pArray->nSegment; i++){ sqlite3Fts3SegReaderFree(0, pArray->apSegment[i]); } sqlite3_free(pArray); } } static int fts3SegReaderArrayAdd( Fts3SegReaderArray **ppArray, Fts3SegReader *pNew ){ Fts3SegReaderArray *pArray = *ppArray; if( !pArray || pArray->nAlloc==pArray->nSegment ){ int nNew = (pArray ? pArray->nAlloc+16 : 16); pArray = (Fts3SegReaderArray *)sqlite3_realloc(pArray, sizeof(Fts3SegReaderArray) + (nNew-1) * sizeof(Fts3SegReader*) ); if( !pArray ){ sqlite3Fts3SegReaderFree(0, pNew); return SQLITE_NOMEM; } if( nNew==16 ){ pArray->nSegment = 0; pArray->nCost = 0; } pArray->nAlloc = nNew; *ppArray = pArray; } pArray->apSegment[pArray->nSegment++] = pNew; return SQLITE_OK; } static int fts3TermSegReaderArray( Fts3Cursor *pCsr, /* Virtual table cursor handle */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ int isPrefix, /* True for a prefix search */ Fts3SegReaderArray **ppArray /* OUT: Allocated seg-reader array */ ){ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; int rc; /* Return code */ Fts3SegReaderArray *pArray = 0; /* Array object to build */ Fts3SegReader *pReader = 0; /* Seg-reader to add to pArray */ sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */ int iAge = 0; /* Used to assign ages to segments */ /* Allocate a seg-reader to scan the pending terms, if any. */ rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pReader); if( rc==SQLITE_OK && pReader ) { rc = fts3SegReaderArrayAdd(&pArray, pReader); } /* Loop through the entire %_segdir table. For each segment, create a ** Fts3SegReader to iterate through the subset of the segment leaves ** that may contain a term that matches zTerm/nTerm. For non-prefix ** searches, this is always a single leaf. For prefix searches, this ** may be a contiguous block of leaves. */ if( rc==SQLITE_OK ){ rc = sqlite3Fts3AllSegdirs(p, &pStmt); } while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ Fts3SegReader *pNew = 0; int nRoot = sqlite3_column_bytes(pStmt, 4); char const *zRoot = sqlite3_column_blob(pStmt, 4); if( sqlite3_column_int64(pStmt, 1)==0 ){ /* The entire segment is stored on the root node (which must be a ** leaf). Do not bother inspecting any data in this case, just ** create a Fts3SegReader to scan the single leaf. */ rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew); }else{ sqlite3_int64 i1; /* First leaf that may contain zTerm */ sqlite3_int64 i2; /* Final leaf that may contain zTerm */ rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1, (isPrefix?&i2:0)); if( isPrefix==0 ) i2 = i1; if( rc==SQLITE_OK ){ rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew); } } assert( (pNew==0)==(rc!=SQLITE_OK) ); /* If a new Fts3SegReader was allocated, add it to the array. */ if( rc==SQLITE_OK ){ rc = fts3SegReaderArrayAdd(&pArray, pNew); } if( rc==SQLITE_OK ){ rc = sqlite3Fts3SegReaderCost(pCsr, pNew, &pArray->nCost); } iAge++; } if( rc==SQLITE_DONE ){ rc = sqlite3_reset(pStmt); }else{ sqlite3_reset(pStmt); } if( rc!=SQLITE_OK ){ fts3SegReaderArrayFree(pArray); pArray = 0; } *ppArray = pArray; return rc; } /* ** This function retreives the doclist for the specified term (or term ** prefix) from the database. ** ** The returned doclist may be in one of two formats, depending on the ** value of parameter isReqPos. If isReqPos is zero, then the doclist is ** a sorted list of delta-compressed docids (a bare doclist). If isReqPos ** is non-zero, then the returned list is in the same format as is stored ** in the database without the found length specifier at the start of on-disk ** doclists. */ static int fts3TermSelect( Fts3Table *p, /* Virtual table handle */ Fts3PhraseToken *pTok, /* Token to query for */ int iColumn, /* Column to query (or -ve for all columns) */ int isReqPos, /* True to include position lists in output */ int *pnOut, /* OUT: Size of buffer at *ppOut */ char **ppOut /* OUT: Malloced result buffer */ ){ int rc; /* Return code */ Fts3SegReaderArray *pArray; /* Seg-reader array for this term */ TermSelect tsc; /* Context object for fts3TermSelectCb() */ Fts3SegFilter filter; /* Segment term filter configuration */ pArray = pTok->pArray; memset(&tsc, 0, sizeof(TermSelect)); tsc.isReqPos = isReqPos; filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0) | (isReqPos ? FTS3_SEGMENT_REQUIRE_POS : 0) | (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); filter.iCol = iColumn; filter.zTerm = pTok->z; filter.nTerm = pTok->n; rc = sqlite3Fts3SegReaderIterate(p, pArray->apSegment, pArray->nSegment, &filter, fts3TermSelectCb, (void *)&tsc ); if( rc==SQLITE_OK ){ rc = fts3TermSelectMerge(&tsc); } if( rc==SQLITE_OK ){ *ppOut = tsc.aaOutput[0]; *pnOut = tsc.anOutput[0]; }else{ int i; for(i=0; i<SizeofArray(tsc.aaOutput); i++){ sqlite3_free(tsc.aaOutput[i]); } } fts3SegReaderArrayFree(pArray); pTok->pArray = 0; return rc; } /* ** This function counts the total number of docids in the doclist stored ** in buffer aList[], size nList bytes. ** ** If the isPoslist argument is true, then it is assumed that the doclist ** contains a position-list following each docid. Otherwise, it is assumed ** that the doclist is simply a list of docids stored as delta encoded ** varints. */ static int fts3DoclistCountDocids(int isPoslist, char *aList, int nList){ int nDoc = 0; /* Return value */ if( aList ){ char *aEnd = &aList[nList]; /* Pointer to one byte after EOF */ char *p = aList; /* Cursor */ if( !isPoslist ){ /* The number of docids in the list is the same as the number of ** varints. In FTS3 a varint consists of a single byte with the 0x80 ** bit cleared and zero or more bytes with the 0x80 bit set. So to ** count the varints in the buffer, just count the number of bytes ** with the 0x80 bit clear. */ while( p<aEnd ) nDoc += (((*p++)&0x80)==0); }else{ while( p<aEnd ){ nDoc++; while( (*p++)&0x80 ); /* Skip docid varint */ fts3PoslistCopy(0, &p); /* Skip over position list */ } } } return nDoc; } /* ** Call sqlite3Fts3DeferToken() for each token in the expression pExpr. */ static int fts3DeferExpression(Fts3Cursor *pCsr, Fts3Expr *pExpr){ int rc = SQLITE_OK; if( pExpr ){ rc = fts3DeferExpression(pCsr, pExpr->pLeft); if( rc==SQLITE_OK ){ rc = fts3DeferExpression(pCsr, pExpr->pRight); } if( pExpr->eType==FTSQUERY_PHRASE ){ int iCol = pExpr->pPhrase->iColumn; int i; for(i=0; rc==SQLITE_OK && i<pExpr->pPhrase->nToken; i++){ Fts3PhraseToken *pToken = &pExpr->pPhrase->aToken[i]; if( pToken->pDeferred==0 ){ rc = sqlite3Fts3DeferToken(pCsr, pToken, iCol); } } } } return rc; } /* ** This function removes the position information from a doclist. When ** called, buffer aList (size *pnList bytes) contains a doclist that includes ** position information. This function removes the position information so ** that aList contains only docids, and adjusts *pnList to reflect the new ** (possibly reduced) size of the doclist. */ static void fts3DoclistStripPositions( char *aList, /* IN/OUT: Buffer containing doclist */ int *pnList /* IN/OUT: Size of doclist in bytes */ ){ if( aList ){ char *aEnd = &aList[*pnList]; /* Pointer to one byte after EOF */ char *p = aList; /* Input cursor */ char *pOut = aList; /* Output cursor */ while( p<aEnd ){ sqlite3_int64 delta; p += sqlite3Fts3GetVarint(p, &delta); fts3PoslistCopy(0, &p); pOut += sqlite3Fts3PutVarint(pOut, delta); } *pnList = (pOut - aList); } } /* ** Return a DocList corresponding to the phrase *pPhrase. ** ** If this function returns SQLITE_OK, but *pnOut is set to a negative value, ** then no tokens in the phrase were looked up in the full-text index. This ** is only possible when this function is called from within xFilter(). The ** caller should assume that all documents match the phrase. The actual ** filtering will take place in xNext(). */ static int fts3PhraseSelect( Fts3Cursor *pCsr, /* Virtual table cursor handle */ Fts3Phrase *pPhrase, /* Phrase to return a doclist for */ int isReqPos, /* True if output should contain positions */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut /* OUT: Size of buffer at *paOut */ ){ char *pOut = 0; int nOut = 0; int rc = SQLITE_OK; int ii; int iCol = pPhrase->iColumn; int isTermPos = (pPhrase->nToken>1 || isReqPos); Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; int isFirst = 1; int iPrevTok = 0; int nDoc = 0; /* If this is an xFilter() evaluation, create a segment-reader for each ** phrase token. Or, if this is an xNext() or snippet/offsets/matchinfo ** evaluation, only create segment-readers if there are no Fts3DeferredToken ** objects attached to the phrase-tokens. */ for(ii=0; ii<pPhrase->nToken; ii++){ Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; if( pTok->pArray==0 ){ if( (pCsr->eEvalmode==FTS3_EVAL_FILTER) || (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0) || (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext) ){ rc = fts3TermSegReaderArray( pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray ); if( rc!=SQLITE_OK ) return rc; } } } for(ii=0; ii<pPhrase->nToken; ii++){ Fts3PhraseToken *pTok; /* Token to find doclist for */ int iTok; /* The token being queried this iteration */ char *pList; /* Pointer to token doclist */ int nList; /* Size of buffer at pList */ /* Select a token to process. If this is an xFilter() call, then tokens ** are processed in order from least to most costly. Otherwise, tokens ** are processed in the order in which they occur in the phrase. */ if( pCsr->eEvalmode==FTS3_EVAL_MATCHINFO ){ assert( isReqPos ); iTok = ii; pTok = &pPhrase->aToken[iTok]; if( pTok->bFulltext==0 ) continue; }else if( pCsr->eEvalmode==FTS3_EVAL_NEXT || isReqPos ){ iTok = ii; pTok = &pPhrase->aToken[iTok]; }else{ int nMinCost = 0x7FFFFFFF; int jj; /* Find the remaining token with the lowest cost. */ for(jj=0; jj<pPhrase->nToken; jj++){ Fts3SegReaderArray *pArray = pPhrase->aToken[jj].pArray; if( pArray && pArray->nCost<nMinCost ){ iTok = jj; nMinCost = pArray->nCost; } } pTok = &pPhrase->aToken[iTok]; /* This branch is taken if it is determined that loading the doclist ** for the next token would require more IO than loading all documents ** currently identified by doclist pOut/nOut. No further doclists will ** be loaded from the full-text index for this phrase. */ if( nMinCost>nDoc && ii>0 ){ rc = fts3DeferExpression(pCsr, pCsr->pExpr); break; } } if( pCsr->eEvalmode==FTS3_EVAL_NEXT && pTok->pDeferred ){ rc = fts3DeferredTermSelect(pTok->pDeferred, isTermPos, &nList, &pList); }else{ assert( pTok->pArray ); rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList); pTok->bFulltext = 1; } assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pArray==0 ); if( rc!=SQLITE_OK ) break; if( isFirst ){ pOut = pList; nOut = nList; if( pCsr->eEvalmode==FTS3_EVAL_FILTER && pPhrase->nToken>1 ){ nDoc = fts3DoclistCountDocids(1, pOut, nOut); } isFirst = 0; iPrevTok = iTok; }else{ /* Merge the new term list and the current output. */ char *aLeft, *aRight; int nLeft, nRight; int nDist; int mt; /* If this is the final token of the phrase, and positions were not ** requested by the caller, use MERGE_PHRASE instead of POS_PHRASE. ** This drops the position information from the output list. */ mt = MERGE_POS_PHRASE; if( ii==pPhrase->nToken-1 && !isReqPos ) mt = MERGE_PHRASE; assert( iPrevTok!=iTok ); if( iPrevTok<iTok ){ aLeft = pOut; nLeft = nOut; aRight = pList; nRight = nList; nDist = iTok-iPrevTok; iPrevTok = iTok; }else{ aRight = pOut; nRight = nOut; aLeft = pList; nLeft = nList; nDist = iPrevTok-iTok; } pOut = aRight; fts3DoclistMerge( mt, nDist, 0, pOut, &nOut, aLeft, nLeft, aRight, nRight, &nDoc ); sqlite3_free(aLeft); } assert( nOut==0 || pOut!=0 ); } if( rc==SQLITE_OK ){ if( ii!=pPhrase->nToken ){ assert( pCsr->eEvalmode==FTS3_EVAL_FILTER && isReqPos==0 ); fts3DoclistStripPositions(pOut, &nOut); } *paOut = pOut; *pnOut = nOut; }else{ sqlite3_free(pOut); } return rc; } /* ** This function merges two doclists according to the requirements of a ** NEAR operator. ** ** Both input doclists must include position information. The output doclist ** includes position information if the first argument to this function ** is MERGE_POS_NEAR, or does not if it is MERGE_NEAR. */ static int fts3NearMerge( int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */ int nNear, /* Parameter to NEAR operator */ int nTokenLeft, /* Number of tokens in LHS phrase arg */ char *aLeft, /* Doclist for LHS (incl. positions) */ int nLeft, /* Size of LHS doclist in bytes */ int nTokenRight, /* As nTokenLeft */ char *aRight, /* As aLeft */ int nRight, /* As nRight */ char **paOut, /* OUT: Results of merge (malloced) */ int *pnOut /* OUT: Sized of output buffer */ ){ char *aOut; /* Buffer to write output doclist to */ int rc; /* Return code */ assert( mergetype==MERGE_POS_NEAR || MERGE_NEAR ); aOut = sqlite3_malloc(nLeft+nRight+1); if( aOut==0 ){ rc = SQLITE_NOMEM; }else{ rc = fts3DoclistMerge(mergetype, nNear+nTokenRight, nNear+nTokenLeft, aOut, pnOut, aLeft, nLeft, aRight, nRight, 0 ); if( rc!=SQLITE_OK ){ sqlite3_free(aOut); aOut = 0; } } *paOut = aOut; return rc; } /* ** This function is used as part of the processing for the snippet() and ** offsets() functions. ** ** Both pLeft and pRight are expression nodes of type FTSQUERY_PHRASE. Both ** have their respective doclists (including position information) loaded ** in Fts3Expr.aDoclist/nDoclist. This function removes all entries from ** each doclist that are not within nNear tokens of a corresponding entry ** in the other doclist. */ int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ int rc; /* Return code */ assert( pLeft->eType==FTSQUERY_PHRASE ); assert( pRight->eType==FTSQUERY_PHRASE ); assert( pLeft->isLoaded && pRight->isLoaded ); if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){ sqlite3_free(pLeft->aDoclist); sqlite3_free(pRight->aDoclist); pRight->aDoclist = 0; pLeft->aDoclist = 0; rc = SQLITE_OK; }else{ char *aOut; /* Buffer in which to assemble new doclist */ int nOut; /* Size of buffer aOut in bytes */ rc = fts3NearMerge(MERGE_POS_NEAR, nNear, pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, &aOut, &nOut ); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
2014 2015 2016 2017 2018 2019 2020 2021 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < | | > > | > > > > | < | 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 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 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } /* ** Allocate an Fts3SegReaderArray for each token in the expression pExpr. ** The allocated objects are stored in the Fts3PhraseToken.pArray member ** variables of each token structure. */ static int fts3ExprAllocateSegReaders( Fts3Cursor *pCsr, /* FTS3 table */ Fts3Expr *pExpr, /* Expression to create seg-readers for */ int *pnExpr /* OUT: Number of AND'd expressions */ ){ int rc = SQLITE_OK; /* Return code */ assert( pCsr->eEvalmode==FTS3_EVAL_FILTER ); if( pnExpr && pExpr->eType!=FTSQUERY_AND ){ (*pnExpr)++; pnExpr = 0; } if( pExpr->eType==FTSQUERY_PHRASE ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; for(ii=0; rc==SQLITE_OK && ii<pPhrase->nToken; ii++){ Fts3PhraseToken *pTok = &pPhrase->aToken[ii]; if( pTok->pArray==0 ){ rc = fts3TermSegReaderArray( pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pArray ); } } }else{ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pLeft, pnExpr); if( rc==SQLITE_OK ){ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pRight, pnExpr); } } return rc; } /* ** Free the Fts3SegReaderArray objects associated with each token in the ** expression pExpr. In other words, this function frees the resources ** allocated by fts3ExprAllocateSegReaders(). */ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ int kk; for(kk=0; kk<pPhrase->nToken; kk++){ fts3SegReaderArrayFree(pPhrase->aToken[kk].pArray); pPhrase->aToken[kk].pArray = 0; } } fts3ExprFreeSegReaders(pExpr->pLeft); fts3ExprFreeSegReaders(pExpr->pRight); } } /* ** Return the sum of the costs of all tokens in the expression pExpr. This ** function must be called after Fts3SegReaderArrays have been allocated ** for all tokens using fts3ExprAllocateSegReaders(). */ static int fts3ExprCost(Fts3Expr *pExpr){ int nCost; /* Return value */ if( pExpr->eType==FTSQUERY_PHRASE ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; nCost = 0; for(ii=0; ii<pPhrase->nToken; ii++){ nCost += pPhrase->aToken[ii].pArray->nCost; } }else{ nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight); } return nCost; } /* ** The following is a helper function (and type) for fts3EvalExpr(). It ** must be called after Fts3SegReaders have been allocated for every token ** in the expression. See the context it is called from in fts3EvalExpr() ** for further explanation. */ typedef struct ExprAndCost ExprAndCost; struct ExprAndCost { Fts3Expr *pExpr; int nCost; }; static void fts3ExprAssignCosts( Fts3Expr *pExpr, /* Expression to create seg-readers for */ ExprAndCost **ppExprCost /* OUT: Write to *ppExprCost */ ){ if( pExpr->eType==FTSQUERY_AND ){ fts3ExprAssignCosts(pExpr->pLeft, ppExprCost); fts3ExprAssignCosts(pExpr->pRight, ppExprCost); }else{ (*ppExprCost)->pExpr = pExpr; (*ppExprCost)->nCost = fts3ExprCost(pExpr);; (*ppExprCost)++; } } /* ** Evaluate the full-text expression pExpr against FTS3 table pTab. Store ** the resulting doclist in *paOut and *pnOut. This routine mallocs for ** the space needed to store the output. The caller is responsible for ** freeing the space when it has finished. ** ** This function is called in two distinct contexts: ** ** * From within the virtual table xFilter() method. In this case, the ** output doclist contains entries for all rows in the table, based on ** data read from the full-text index. ** ** In this case, if the query expression contains one or more tokens that ** are very common, then the returned doclist may contain a superset of ** the documents that actually match the expression. ** ** * From within the virtual table xNext() method. This call is only made ** if the call from within xFilter() found that there were very common ** tokens in the query expression and did return a superset of the ** matching documents. In this case the returned doclist contains only ** entries that correspond to the current row of the table. Instead of ** reading the data for each token from the full-text index, the data is ** already available in-memory in the Fts3PhraseToken.pDeferred structures. ** See fts3EvalDeferred() for how it gets there. ** ** In the first case above, Fts3Cursor.doDeferred==0. In the second (if it is ** required) Fts3Cursor.doDeferred==1. ** ** If the SQLite invokes the snippet(), offsets() or matchinfo() function ** as part of a SELECT on an FTS3 table, this function is called on each ** individual phrase expression in the query. If there were very common tokens ** found in the xFilter() call, then this function is called once for phrase ** for each row visited, and the returned doclist contains entries for the ** current row only. Otherwise, if there were no very common tokens, then this ** function is called once only for each phrase in the query and the returned ** doclist contains entries for all rows of the table. ** ** Fts3Cursor.doDeferred==1 when this function is called on phrases as a ** result of a snippet(), offsets() or matchinfo() invocation. */ static int fts3EvalExpr( Fts3Cursor *p, /* Virtual table cursor handle */ Fts3Expr *pExpr, /* Parsed fts3 expression */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut, /* OUT: Size of buffer at *paOut */ int isReqPos /* Require positions in output buffer */ ){ int rc = SQLITE_OK; /* Return code */ /* Zero the output parameters. */ *paOut = 0; *pnOut = 0; if( pExpr ){ assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR || pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT || pExpr->eType==FTSQUERY_PHRASE ); assert( pExpr->eType==FTSQUERY_PHRASE || isReqPos==0 ); if( pExpr->eType==FTSQUERY_PHRASE ){ rc = fts3PhraseSelect(p, pExpr->pPhrase, isReqPos || (pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR), paOut, pnOut ); fts3ExprFreeSegReaders(pExpr); }else if( p->eEvalmode==FTS3_EVAL_FILTER && pExpr->eType==FTSQUERY_AND ){ ExprAndCost *aExpr = 0; /* Array of AND'd expressions and costs */ int nExpr = 0; /* Size of aExpr[] */ char *aRet = 0; /* Doclist to return to caller */ int nRet = 0; /* Length of aRet[] in bytes */ int nDoc = 0x7FFFFFFF; assert( !isReqPos ); rc = fts3ExprAllocateSegReaders(p, pExpr, &nExpr); if( rc==SQLITE_OK ){ assert( nExpr>1 ); aExpr = sqlite3_malloc(sizeof(ExprAndCost) * nExpr); if( !aExpr ) rc = SQLITE_NOMEM; } if( rc==SQLITE_OK ){ int ii; /* Used to iterate through expressions */ fts3ExprAssignCosts(pExpr, &aExpr); aExpr -= nExpr; for(ii=0; ii<nExpr; ii++){ char *aNew; int nNew; int jj; ExprAndCost *pBest = 0; for(jj=0; jj<nExpr; jj++){ ExprAndCost *pCand = &aExpr[jj]; if( pCand->pExpr && (pBest==0 || pCand->nCost<pBest->nCost) ){ pBest = pCand; } } if( pBest->nCost>nDoc ){ rc = fts3DeferExpression(p, p->pExpr); break; }else{ rc = fts3EvalExpr(p, pBest->pExpr, &aNew, &nNew, 0); if( rc!=SQLITE_OK ) break; pBest->pExpr = 0; if( ii==0 ){ aRet = aNew; nRet = nNew; nDoc = fts3DoclistCountDocids(0, aRet, nRet); }else{ fts3DoclistMerge( MERGE_AND, 0, 0, aRet, &nRet, aRet, nRet, aNew, nNew, &nDoc ); sqlite3_free(aNew); } } } } *paOut = aRet; *pnOut = nRet; sqlite3_free(aExpr); fts3ExprFreeSegReaders(pExpr); }else{ char *aLeft; char *aRight; int nLeft; int nRight; assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR || pExpr->eType==FTSQUERY_NOT || (pExpr->eType==FTSQUERY_AND && p->eEvalmode==FTS3_EVAL_NEXT) ); if( 0==(rc = fts3EvalExpr(p, pExpr->pRight, &aRight, &nRight, isReqPos)) && 0==(rc = fts3EvalExpr(p, pExpr->pLeft, &aLeft, &nLeft, isReqPos)) ){ switch( pExpr->eType ){ case FTSQUERY_NEAR: { Fts3Expr *pLeft; Fts3Expr *pRight; int mergetype = MERGE_NEAR; if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ mergetype = MERGE_POS_NEAR; } pLeft = pExpr->pLeft; while( pLeft->eType==FTSQUERY_NEAR ){ pLeft=pLeft->pRight; } |
︙ | ︙ | |||
2089 2090 2091 2092 2093 2094 2095 | /* Allocate a buffer for the output. The maximum size is the ** sum of the sizes of the two input buffers. The +1 term is ** so that a buffer of zero bytes is never allocated - this can ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. */ char *aBuffer = sqlite3_malloc(nRight+nLeft+1); rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < | 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 | /* Allocate a buffer for the output. The maximum size is the ** sum of the sizes of the two input buffers. The +1 term is ** so that a buffer of zero bytes is never allocated - this can ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. */ char *aBuffer = sqlite3_malloc(nRight+nLeft+1); rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, aLeft, nLeft, aRight, nRight, 0 ); *paOut = aBuffer; sqlite3_free(aLeft); break; } default: { assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND ); fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut, aLeft, nLeft, aRight, nRight, 0 ); *paOut = aLeft; break; } } } sqlite3_free(aRight); } } return rc; } /* ** This function is called from within xNext() for each row visited by ** an FTS3 query. If evaluating the FTS3 query expression within xFilter() ** was able to determine the exact set of matching rows, this function sets ** *pbRes to true and returns SQLITE_IO immediately. ** ** Otherwise, if evaluating the query expression within xFilter() returned a ** superset of the matching documents instead of an exact set (this happens ** when the query includes very common tokens and it is deemed too expensive to ** load their doclists from disk), this function tests if the current row ** really does match the FTS3 query. ** ** If an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK ** is returned and *pbRes is set to true if the current row matches the ** FTS3 query (and should be included in the results returned to SQLite), or ** false otherwise. */ static int fts3EvalDeferred( Fts3Cursor *pCsr, /* FTS3 cursor pointing at row to test */ int *pbRes /* OUT: Set to true if row is a match */ ){ int rc = SQLITE_OK; if( pCsr->pDeferred==0 ){ *pbRes = 1; }else{ rc = fts3CursorSeek(0, pCsr); if( rc==SQLITE_OK ){ sqlite3Fts3FreeDeferredDoclists(pCsr); rc = sqlite3Fts3CacheDeferredDoclists(pCsr); } if( rc==SQLITE_OK ){ char *a = 0; int n = 0; rc = fts3EvalExpr(pCsr, pCsr->pExpr, &a, &n, 0); assert( n>=0 ); *pbRes = (n>0); sqlite3_free(a); } } return rc; } /* ** Advance the cursor to the next row in the %_content table that ** matches the search criteria. For a MATCH search, this will be ** the next row that matches. For a full-table scan, this will be ** simply the next row in the %_content table. For a docid lookup, ** this routine simply sets the EOF flag. ** ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned ** even if we reach end-of-file. The fts3EofMethod() will be called ** subsequently to determine whether or not an EOF was hit. */ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ int res; int rc = SQLITE_OK; /* Return code */ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; pCsr->eEvalmode = FTS3_EVAL_NEXT; do { if( pCsr->aDoclist==0 ){ if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ pCsr->isEof = 1; rc = sqlite3_reset(pCsr->pStmt); break; } pCsr->iPrevId = sqlite3_column_int64(pCsr->pStmt, 0); }else{ if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){ pCsr->isEof = 1; break; } sqlite3_reset(pCsr->pStmt); fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId); pCsr->isRequireSeek = 1; pCsr->isMatchinfoNeeded = 1; } }while( SQLITE_OK==(rc = fts3EvalDeferred(pCsr, &res)) && res==0 ); return rc; } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against ** the %_content table. ** ** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry ** in the %_content table. ** ** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The ** column on the left-hand side of the MATCH operator is column ** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand ** side of the MATCH operator. */ static int fts3FilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ |
︙ | ︙ | |||
2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( nVal==0 || nVal==1 ); assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); /* In case the cursor has been used before, clear it now. */ sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr->aDoclist); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); | > < < < < < < < < < < < < < < < | < < > > > | > > > > > > > > > > > > > > > > > > > | 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( nVal==0 || nVal==1 ); assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); assert( p->pSegments==0 ); /* In case the cursor has been used before, clear it now. */ sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr->aDoclist); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){ int iCol = idxNum-FTS3_FULLTEXT_SEARCH; const char *zQuery = (const char *)sqlite3_value_text(apVal[0]); if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ return SQLITE_NOMEM; } rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr ); if( rc!=SQLITE_OK ){ if( rc==SQLITE_ERROR ){ p->base.zErrMsg = sqlite3_mprintf("malformed MATCH expression: [%s]", zQuery); } return rc; } rc = sqlite3Fts3ReadLock(p); if( rc!=SQLITE_OK ) return rc; rc = fts3EvalExpr(pCsr, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); sqlite3Fts3SegmentsClose(p); if( rc!=SQLITE_OK ) return rc; pCsr->pNextId = pCsr->aDoclist; pCsr->iPrevId = 0; } /* Compile a SELECT statement for this cursor. For a full-table-scan, the ** statement loops through all rows of the %_content table. For a ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName); if( !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); } if( rc==SQLITE_OK && idxNum==FTS3_DOCID_SEARCH ){ rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); } pCsr->eSearch = (i16)idxNum; if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } /* ** This is the xEof method of the virtual table. SQLite calls this |
︙ | ︙ | |||
2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } return SQLITE_OK; } /* ** This is the xColumn method, called by SQLite to request a value from | > > > > > | 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ /* This branch runs if the query is implemented using a full-table scan ** (not using the full-text index). In this case grab the rowid from the ** SELECT statement. */ assert( pCsr->isRequireSeek==0 ); *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } return SQLITE_OK; } /* ** This is the xColumn method, called by SQLite to request a value from |
︙ | ︙ | |||
2289 2290 2291 2292 2293 2294 2295 | } /* ** Implementation of xSync() method. Flush the contents of the pending-terms ** hash-table to the database. */ static int fts3SyncMethod(sqlite3_vtab *pVtab){ | | > > | 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 | } /* ** Implementation of xSync() method. Flush the contents of the pending-terms ** hash-table to the database. */ static int fts3SyncMethod(sqlite3_vtab *pVtab){ int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab); sqlite3Fts3SegmentsClose((Fts3Table *)pVtab); return rc; } /* ** Implementation of xBegin() method. This is a no-op. */ static int fts3BeginMethod(sqlite3_vtab *pVtab){ UNUSED_PARAMETER(pVtab); |
︙ | ︙ | |||
2327 2328 2329 2330 2331 2332 2333 | /* ** Load the doclist associated with expression pExpr to pExpr->aDoclist. ** The loaded doclist contains positions as well as the document ids. ** This is used by the matchinfo(), snippet() and offsets() auxillary ** functions. */ | | > > > | > > > > > > > > > > > > > > > > | 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 | /* ** Load the doclist associated with expression pExpr to pExpr->aDoclist. ** The loaded doclist contains positions as well as the document ids. ** This is used by the matchinfo(), snippet() and offsets() auxillary ** functions. */ int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *pCsr, Fts3Expr *pExpr){ int rc; assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase ); assert( pCsr->eEvalmode==FTS3_EVAL_NEXT ); rc = fts3EvalExpr(pCsr, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1); return rc; } int sqlite3Fts3ExprLoadFtDoclist( Fts3Cursor *pCsr, Fts3Expr *pExpr, char **paDoclist, int *pnDoclist ){ int rc; assert( pCsr->eEvalmode==FTS3_EVAL_NEXT ); assert( pExpr->eType==FTSQUERY_PHRASE && pExpr->pPhrase ); pCsr->eEvalmode = FTS3_EVAL_MATCHINFO; rc = fts3EvalExpr(pCsr, pExpr, paDoclist, pnDoclist, 1); pCsr->eEvalmode = FTS3_EVAL_NEXT; return rc; } /* ** After ExprLoadDoclist() (see above) has been called, this function is ** used to iterate/search through the position lists that make up the doclist ** stored in pExpr->aDoclist. */ |
︙ | ︙ | |||
2394 2395 2396 2397 2398 2399 2400 | ** message is written to context pContext and SQLITE_ERROR returned. The ** string passed via zFunc is used as part of the error message. */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ | | | 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 | ** message is written to context pContext and SQLITE_ERROR returned. The ** string passed via zFunc is used as part of the error message. */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ ){ Fts3Cursor *pRet; if( sqlite3_value_type(pVal)!=SQLITE_BLOB || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *) ){ char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); sqlite3_result_error(pContext, zErr, -1); |
︙ | ︙ | |||
2520 2521 2522 2523 2524 2525 2526 | */ static void fts3MatchinfoFunc( sqlite3_context *pContext, /* SQLite function call context */ int nVal, /* Size of argument array */ sqlite3_value **apVal /* Array of arguments */ ){ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ | < | < < < < < | 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 | */ static void fts3MatchinfoFunc( sqlite3_context *pContext, /* SQLite function call context */ int nVal, /* Size of argument array */ sqlite3_value **apVal /* Array of arguments */ ){ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ assert( nVal==1 ); if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){ sqlite3Fts3Matchinfo(pContext, pCsr); } } /* ** This routine implements the xFindFunction method for the FTS3 |
︙ | ︙ | |||
2577 2578 2579 2580 2581 2582 2583 | ** Implementation of FTS3 xRename method. Rename an fts3 table. */ static int fts3RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ Fts3Table *p = (Fts3Table *)pVtab; | | | | | > > > < > > | 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 | ** Implementation of FTS3 xRename method. Rename an fts3 table. */ static int fts3RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ Fts3Table *p = (Fts3Table *)pVtab; sqlite3 *db = p->db; /* Database connection */ int rc; /* Return Code */ rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ){ return rc; } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", p->zDb, p->zName, zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", p->zDb, p->zName, zName ); } if( p->bHasStat ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_stat' RENAME TO '%q_stat';", p->zDb, p->zName, zName ); } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';", |
︙ | ︙ | |||
2616 2617 2618 2619 2620 2621 2622 | /* iVersion */ 0, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, /* xDisconnect */ fts3DisconnectMethod, /* xDestroy */ fts3DestroyMethod, /* xOpen */ fts3OpenMethod, | | | 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 | /* iVersion */ 0, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, /* xDisconnect */ fts3DisconnectMethod, /* xDestroy */ fts3DestroyMethod, /* xOpen */ fts3OpenMethod, /* xClose */ fts3CloseMethod, /* xFilter */ fts3FilterMethod, /* xNext */ fts3NextMethod, /* xEof */ fts3EofMethod, /* xColumn */ fts3ColumnMethod, /* xRowid */ fts3RowidMethod, /* xUpdate */ fts3UpdateMethod, /* xBegin */ fts3BeginMethod, |
︙ | ︙ | |||
2643 2644 2645 2646 2647 2648 2649 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* | | | | | < | > > | 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* ** The fts3 built-in tokenizers - "simple", "porter" and "icu"- are ** implemented in files fts3_tokenizer1.c, fts3_porter.c and fts3_icu.c ** respectively. The following three forward declarations are for functions ** declared in these files used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed ** to by the argument to point to the "simple" tokenizer implementation. ** And so on. */ void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); #ifdef SQLITE_ENABLE_ICU void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); #endif /* ** Initialise the fts3 extension. If this extension is built as part ** of the sqlite library, then this function is called directly by ** SQLite. If fts3 is built as a dynamically loadable extension, this ** function is called by the sqlite3_extension_init() entry point. */ |
︙ | ︙ | |||
2711 2712 2713 2714 2715 2716 2717 | ** the two scalar functions. If this is successful, register the ** module with sqlite. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) | | | 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 | ** the two scalar functions. If this is successful, register the ** module with sqlite. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) ){ rc = sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); if( rc==SQLITE_OK ){ rc = sqlite3_create_module_v2( |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
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 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ # define ALWAYS(x) (x) # define NEVER(X) (x) /* ** Internal types used by SQLite. */ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ typedef short int i16; /* 2-byte (or larger) signed integer */ typedef unsigned int u32; /* 4-byte unsigned integer */ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) #endif typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3SegReader Fts3SegReader; | > > > > > > > > > > | | < < < < < < < < < < < | > > > | 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 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ #ifdef SQLITE_COVERAGE_TEST # define ALWAYS(x) (1) # define NEVER(X) (0) #else # define ALWAYS(x) (x) # define NEVER(X) (x) #endif /* ** Internal types used by SQLite. */ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ typedef short int i16; /* 2-byte (or larger) signed integer */ typedef unsigned int u32; /* 4-byte unsigned integer */ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) #endif typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3PhraseToken Fts3PhraseToken; typedef struct Fts3SegFilter Fts3SegFilter; typedef struct Fts3DeferredToken Fts3DeferredToken; typedef struct Fts3SegReader Fts3SegReader; typedef struct Fts3SegReaderArray Fts3SegReaderArray; /* ** A connection to a fulltext index is an instance of the following ** structure. The xCreate and xConnect methods create an instance ** of this structure and xDestroy and xDisconnect free that instance. ** All other methods receive a pointer to the structure as one of their ** arguments. */ struct Fts3Table { sqlite3_vtab base; /* Base class used by SQLite core */ sqlite3 *db; /* The database connection */ const char *zDb; /* logical database name */ const char *zName; /* virtual table name */ int nColumn; /* number of named columns in virtual table */ char **azColumn; /* column names. malloced */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[24]; int nNodeSize; /* Soft limit for node size */ u8 bHasStat; /* True if %_stat table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ int nPgsz; /* Page size for host database */ char *zSegmentsTbl; /* Name of %_segments table */ sqlite3_blob *pSegments; /* Blob handle open on %_segments table */ /* The following hash table is used to buffer pending index updates during ** transactions. Variable nPendingData estimates the memory size of the ** pending data, including hash table overhead, but not malloc overhead. ** When nPendingData exceeds nMaxPendingData, the buffer is flushed ** automatically. Variable iPrevDocid is the docid of the most recently ** inserted record. |
︙ | ︙ | |||
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ }; /* ** The Fts3Cursor.eSearch member is always set to one of the following. ** Actualy, Fts3Cursor.eSearch can be greater than or equal to ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index ** of the column to be searched. For example, in ** | > > > > > > > | 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 | struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ int eEvalmode; /* An FTS3_EVAL_XX constant */ int nRowAvg; /* Average size of database rows, in pages */ }; #define FTS3_EVAL_FILTER 0 #define FTS3_EVAL_NEXT 1 #define FTS3_EVAL_MATCHINFO 2 /* ** The Fts3Cursor.eSearch member is always set to one of the following. ** Actualy, Fts3Cursor.eSearch can be greater than or equal to ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index ** of the column to be searched. For example, in ** |
︙ | ︙ | |||
186 187 188 189 190 191 192 | #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. | | | > > > > > > > > > > > > > > > > < < < < | | 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 | #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in double-quotes (i.e. "one two three") ** nToken will be the number of tokens in the string. ** ** The nDocMatch and nMatch variables contain data that may be used by the ** matchinfo() function. They are populated when the full-text index is ** queried for hits on the phrase. If one or more tokens in the phrase ** are deferred, the nDocMatch and nMatch variables are populated based ** on the assumption that the */ struct Fts3PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer z */ int isPrefix; /* True if token ends with a "*" character */ int bFulltext; /* True if full-text index was used */ Fts3SegReaderArray *pArray; /* Segment-reader for this token */ Fts3DeferredToken *pDeferred; /* Deferred token object for this token */ }; struct Fts3Phrase { /* Variables populated by fts3_expr.c when parsing a MATCH expression */ int nToken; /* Number of tokens in the phrase */ int iColumn; /* Index of column this phrase must match */ int isNot; /* Phrase prefixed by unary not (-) operator */ Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ }; /* ** A tree of these objects forms the RHS of a MATCH operator. ** ** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded ** is true, then aDoclist points to a malloced buffer, size nDoclist bytes, |
︙ | ︙ | |||
247 248 249 250 251 252 253 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 | < < < < < | > > > > > > > > > > | 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 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 /* fts3_write.c */ int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); int sqlite3Fts3PendingTermsFlush(Fts3Table *); void sqlite3Fts3PendingTermsClear(Fts3Table *); int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *); int sqlite3Fts3SegReaderIterate( Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*); int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*); int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *); int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *); void sqlite3Fts3SegmentsClose(Fts3Table *); /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 #define FTS3_SEGMENT_PREFIX 0x00000008 |
︙ | ︙ | |||
292 293 294 295 296 297 298 | int sqlite3Fts3PutVarint(char *, sqlite3_int64); int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); int sqlite3Fts3GetVarint32(const char *, int *); int sqlite3Fts3VarintLen(sqlite3_uint64); void sqlite3Fts3Dequote(char *); char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); | | > | | > | 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 | int sqlite3Fts3PutVarint(char *, sqlite3_int64); int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); int sqlite3Fts3GetVarint32(const char *, int *); int sqlite3Fts3VarintLen(sqlite3_uint64); void sqlite3Fts3Dequote(char *); char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); int sqlite3Fts3ExprLoadDoclist(Fts3Cursor *, Fts3Expr *); int sqlite3Fts3ExprLoadFtDoclist(Fts3Cursor *, Fts3Expr *, char **, int *); int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int); /* fts3_tokenizer.c */ const char *sqlite3Fts3NextToken(const char *, int *); int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, sqlite3_tokenizer **, char ** ); int sqlite3Fts3IsIdChar(char); /* fts3_snippet.c */ void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*); void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *, const char *, const char *, int, int ); void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *); |
︙ | ︙ |
Changes to ext/fts3/fts3_expr.c.
︙ | ︙ | |||
101 102 103 104 105 106 107 108 109 110 111 112 113 114 | ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* ** Extract the next token from buffer z (length n) using the tokenizer ** and other information (column names etc.) in pParse. Create an Fts3Expr ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this ** single token and set *ppExpr to point to it. If the end of the buffer is ** reached before a token is found, set *ppExpr to zero. It is the | > > > > > > > > > > > > | 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 | ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* ** Allocate nByte bytes of memory using sqlite3_malloc(). If successful, ** zero the memory before returning a pointer to it. If unsuccessful, ** return NULL. */ static void *fts3MallocZero(int nByte){ void *pRet = sqlite3_malloc(nByte); if( pRet ) memset(pRet, 0, nByte); return pRet; } /* ** Extract the next token from buffer z (length n) using the tokenizer ** and other information (column names etc.) in pParse. Create an Fts3Expr ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this ** single token and set *ppExpr to point to it. If the end of the buffer is ** reached before a token is found, set *ppExpr to zero. It is the |
︙ | ︙ | |||
139 140 141 142 143 144 145 | int nByte; /* total space to allocate */ pCursor->pTokenizer = pTokenizer; rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; | | < | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | int nByte; /* total space to allocate */ pCursor->pTokenizer = pTokenizer; rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; pRet = (Fts3Expr *)fts3MallocZero(nByte); if( !pRet ){ rc = SQLITE_NOMEM; }else{ pRet->eType = FTSQUERY_PHRASE; pRet->pPhrase = (Fts3Phrase *)&pRet[1]; pRet->pPhrase->nToken = 1; pRet->pPhrase->iColumn = iCol; pRet->pPhrase->aToken[0].n = nToken; pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
︙ | ︙ | |||
219 220 221 222 223 224 225 | pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ const char *zToken; int nToken, iBegin, iEnd, iPos; rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | | > | 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 | pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ const char *zToken; int nToken, iBegin, iEnd, iPos; rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); p = fts3ReallocOrFree(p, nByte+ii*sizeof(Fts3PhraseToken)); zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); if( !p || !zTemp ){ goto no_mem; } if( ii==0 ){ memset(p, 0, nByte); p->pPhrase = (Fts3Phrase *)&p[1]; } p->pPhrase = (Fts3Phrase *)&p[1]; memset(&p->pPhrase->aToken[ii], 0, sizeof(Fts3PhraseToken)); p->pPhrase->nToken = ii+1; p->pPhrase->aToken[ii].n = nToken; memcpy(&zTemp[nTemp], zToken, nToken); nTemp += nToken; if( iEnd<nInput && zInput[iEnd]=='*' ){ p->pPhrase->aToken[ii].isPrefix = 1; }else{ |
︙ | ︙ | |||
250 251 252 253 254 255 256 | } if( rc==SQLITE_DONE ){ int jj; char *zNew = NULL; int nNew = 0; int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | | | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | } if( rc==SQLITE_DONE ){ int jj; char *zNew = NULL; int nNew = 0; int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(Fts3PhraseToken); p = fts3ReallocOrFree(p, nByte + nTemp); if( !p ){ goto no_mem; } if( zTemp ){ zNew = &(((char *)p)[nByte]); memcpy(zNew, zTemp, nTemp); |
︙ | ︙ | |||
368 369 370 371 372 373 374 | ** the next byte must contain either whitespace, an open or close ** parenthesis, a quote character, or EOF. */ cNext = zInput[nKey]; if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ | | < | 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | ** the next byte must contain either whitespace, an open or close ** parenthesis, a quote character, or EOF. */ cNext = zInput[nKey]; if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ pRet = (Fts3Expr *)fts3MallocZero(sizeof(Fts3Expr)); if( !pRet ){ return SQLITE_NOMEM; } pRet->eType = pKey->eType; pRet->nNear = nNear; *ppExpr = pRet; *pnConsumed = (int)((zInput - z) + nKey); return SQLITE_OK; } |
︙ | ︙ | |||
548 549 550 551 552 553 554 | if( rc==SQLITE_OK ){ int isPhrase; if( !sqlite3_fts3_enable_parentheses && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot ){ /* Create an implicit NOT operator. */ | | < | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | if( rc==SQLITE_OK ){ int isPhrase; if( !sqlite3_fts3_enable_parentheses && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot ){ /* Create an implicit NOT operator. */ Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr)); if( !pNot ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } pNot->eType = FTSQUERY_NOT; pNot->pRight = p; if( pNotBranch ){ pNot->pLeft = pNotBranch; } pNotBranch = pNot; p = pPrev; |
︙ | ︙ | |||
582 583 584 585 586 587 588 | goto exprparse_out; } if( isPhrase && !isRequirePhrase ){ /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); | | < | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 | goto exprparse_out; } if( isPhrase && !isRequirePhrase ){ /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); pAnd = fts3MallocZero(sizeof(Fts3Expr)); if( !pAnd ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } pAnd->eType = FTSQUERY_AND; insertBinaryOperator(&pRet, pPrev, pAnd); pPrev = pAnd; } /* This test catches attempts to make either operand of a NEAR ** operator something other than a phrase. For example, either of |
︙ | ︙ |
Changes to ext/fts3/fts3_snippet.c.
︙ | ︙ | |||
20 21 22 23 24 25 26 | /* ** Used as an fts3ExprIterate() context when loading phrase doclists to ** Fts3Expr.aDoclist[]/nDoclist. */ typedef struct LoadDoclistCtx LoadDoclistCtx; struct LoadDoclistCtx { | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* ** Used as an fts3ExprIterate() context when loading phrase doclists to ** Fts3Expr.aDoclist[]/nDoclist. */ typedef struct LoadDoclistCtx LoadDoclistCtx; struct LoadDoclistCtx { Fts3Cursor *pCsr; /* FTS3 Cursor */ int nPhrase; /* Number of phrases seen so far */ int nToken; /* Number of tokens seen so far */ }; /* ** The following types are used as part of the implementation of the ** fts3BestSnippet() routine. |
︙ | ︙ | |||
214 215 216 217 218 219 220 | UNUSED_PARAMETER(iPhrase); p->nPhrase++; p->nToken += pExpr->pPhrase->nToken; if( pExpr->isLoaded==0 ){ | | | 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | UNUSED_PARAMETER(iPhrase); p->nPhrase++; p->nToken += pExpr->pPhrase->nToken; if( pExpr->isLoaded==0 ){ rc = sqlite3Fts3ExprLoadDoclist(p->pCsr, pExpr); pExpr->isLoaded = 1; if( rc==SQLITE_OK ){ rc = fts3ExprNearTrim(pExpr); } } return rc; |
︙ | ︙ | |||
257 258 259 260 261 262 263 | static int fts3ExprLoadDoclists( Fts3Cursor *pCsr, /* Fts3 cursor for current query */ int *pnPhrase, /* OUT: Number of phrases in query */ int *pnToken /* OUT: Number of tokens in query */ ){ int rc; /* Return Code */ LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | static int fts3ExprLoadDoclists( Fts3Cursor *pCsr, /* Fts3 cursor for current query */ int *pnPhrase, /* OUT: Number of phrases in query */ int *pnToken /* OUT: Number of tokens in query */ ){ int rc; /* Return Code */ LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ sCtx.pCsr = pCsr; rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb1, (void *)&sCtx); if( rc==SQLITE_OK ){ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb2, 0); } if( pnPhrase ) *pnPhrase = sCtx.nPhrase; if( pnToken ) *pnToken = sCtx.nToken; return rc; |
︙ | ︙ | |||
788 789 790 791 792 793 794 | */ static int fts3ExprGlobalMatchinfoCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number (numbered from zero) */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; | > | > > > > > > > > | > > > > > > > > > > > > > > | | > > > | | | > | 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 | */ static int fts3ExprGlobalMatchinfoCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number (numbered from zero) */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; Fts3Cursor *pCsr = p->pCursor; char *pIter; char *pEnd; char *pFree = 0; const int iStart = 2 + (iPhrase * p->nCol * 3) + 1; assert( pExpr->isLoaded ); assert( pExpr->eType==FTSQUERY_PHRASE ); if( pCsr->pDeferred ){ Fts3Phrase *pPhrase = pExpr->pPhrase; int ii; for(ii=0; ii<pPhrase->nToken; ii++){ if( pPhrase->aToken[ii].bFulltext ) break; } if( ii<pPhrase->nToken ){ int nFree = 0; int rc = sqlite3Fts3ExprLoadFtDoclist(pCsr, pExpr, &pFree, &nFree); if( rc!=SQLITE_OK ) return rc; pIter = pFree; pEnd = &pFree[nFree]; }else{ int nDoc = p->aMatchinfo[2 + 3*p->nCol*p->aMatchinfo[0]]; for(ii=0; ii<p->nCol; ii++){ p->aMatchinfo[iStart + ii*3] = nDoc; p->aMatchinfo[iStart + ii*3 + 1] = nDoc; } return SQLITE_OK; } }else{ pIter = pExpr->aDoclist; pEnd = &pExpr->aDoclist[pExpr->nDoclist]; } /* Fill in the global hit count matrix row for this phrase. */ while( pIter<pEnd ){ while( *pIter++ & 0x80 ); /* Skip past docid. */ fts3LoadColumnlistCounts(&pIter, &p->aMatchinfo[iStart], 1); } sqlite3_free(pFree); return SQLITE_OK; } /* ** fts3ExprIterate() callback used to collect the "local" matchinfo stats ** for a single query. The "local" stats are those elements of the matchinfo ** array that are different for each row returned by the query. |
︙ | ︙ | |||
870 871 872 873 874 875 876 | sInfo.aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo); if( !sInfo.aMatchinfo ){ return SQLITE_NOMEM; } memset(sInfo.aMatchinfo, 0, sizeof(u32)*nMatchinfo); | < < > | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 | sInfo.aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo); if( !sInfo.aMatchinfo ){ return SQLITE_NOMEM; } memset(sInfo.aMatchinfo, 0, sizeof(u32)*nMatchinfo); /* First element of match-info is the number of phrases in the query */ sInfo.aMatchinfo[0] = nPhrase; sInfo.aMatchinfo[1] = sInfo.nCol; if( pTab->bHasDocsize ){ int ofst = 2 + 3*sInfo.aMatchinfo[0]*sInfo.aMatchinfo[1]; rc = sqlite3Fts3MatchinfoDocsizeGlobal(pCsr, &sInfo.aMatchinfo[ofst]); } (void)fts3ExprIterate(pCsr->pExpr, fts3ExprGlobalMatchinfoCb,(void*)&sInfo); pCsr->aMatchinfo = sInfo.aMatchinfo; pCsr->isMatchinfoNeeded = 1; } sInfo.aMatchinfo = pCsr->aMatchinfo; if( rc==SQLITE_OK && pCsr->isMatchinfoNeeded ){ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLocalMatchinfoCb, (void*)&sInfo); |
︙ | ︙ | |||
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 | for(i=0; i<nSnippet && rc==SQLITE_OK; i++){ rc = fts3SnippetText(pCsr, &aSnippet[i], i, (i==nSnippet-1), nFToken, zStart, zEnd, zEllipsis, &res ); } snippet_out: if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, -1, sqlite3_free); } } | > | 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | for(i=0; i<nSnippet && rc==SQLITE_OK; i++){ rc = fts3SnippetText(pCsr, &aSnippet[i], i, (i==nSnippet-1), nFToken, zStart, zEnd, zEllipsis, &res ); } snippet_out: sqlite3Fts3SegmentsClose(pTab); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, -1, sqlite3_free); } } |
︙ | ︙ | |||
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 | pMod->xClose(pC); if( rc!=SQLITE_OK ) goto offsets_out; } offsets_out: sqlite3_free(sCtx.aTerm); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, res.n-1, sqlite3_free); } return; } /* ** Implementation of matchinfo() function. */ void sqlite3Fts3Matchinfo(sqlite3_context *pContext, Fts3Cursor *pCsr){ int rc; if( !pCsr->pExpr ){ sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC); return; } rc = fts3GetMatchinfo(pCsr); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pContext, rc); }else{ Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab; int n = sizeof(u32)*(2+pCsr->aMatchinfo[0]*pCsr->aMatchinfo[1]*3); if( pTab->bHasDocsize ){ n += sizeof(u32)*(1 + 2*pTab->nColumn); } sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT); } } #endif | > > | 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 | pMod->xClose(pC); if( rc!=SQLITE_OK ) goto offsets_out; } offsets_out: sqlite3_free(sCtx.aTerm); assert( rc!=SQLITE_DONE ); sqlite3Fts3SegmentsClose(pTab); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, res.n-1, sqlite3_free); } return; } /* ** Implementation of matchinfo() function. */ void sqlite3Fts3Matchinfo(sqlite3_context *pContext, Fts3Cursor *pCsr){ int rc; if( !pCsr->pExpr ){ sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC); return; } rc = fts3GetMatchinfo(pCsr); sqlite3Fts3SegmentsClose((Fts3Table *)pCsr->base.pVtab ); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pContext, rc); }else{ Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab; int n = sizeof(u32)*(2+pCsr->aMatchinfo[0]*pCsr->aMatchinfo[1]*3); if( pTab->bHasDocsize ){ n += sizeof(u32)*(1 + 2*pTab->nColumn); } sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT); } } #endif |
Changes to ext/fts3/fts3_tokenizer.c.
︙ | ︙ | |||
93 94 95 96 97 98 99 | return; } } sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | return; } } sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } int sqlite3Fts3IsIdChar(char c){ static const char isFtsIdChar[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ |
︙ | ︙ | |||
131 132 133 134 135 136 137 | case '[': z2 = &z1[1]; while( *z2 && z2[0]!=']' ) z2++; if( *z2 ) z2++; break; default: | | | | < < | < < < < < < < | < < < | | 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 | case '[': z2 = &z1[1]; while( *z2 && z2[0]!=']' ) z2++; if( *z2 ) z2++; break; default: if( sqlite3Fts3IsIdChar(*z1) ){ z2 = &z1[1]; while( sqlite3Fts3IsIdChar(*z2) ) z2++; }else{ z1++; } } } *pn = (int)(z2-z1); return z1; } int sqlite3Fts3InitTokenizer( Fts3Hash *pHash, /* Tokenizer hash table */ const char *zArg, /* Tokenizer name */ sqlite3_tokenizer **ppTok, /* OUT: Tokenizer (if applicable) */ char **pzErr /* OUT: Set to malloced error message */ ){ int rc; char *z = (char *)zArg; int n; char *zCopy; char *zEnd; /* Pointer to nul-term of zCopy */ sqlite3_tokenizer_module *m; zCopy = sqlite3_mprintf("%s", zArg); if( !zCopy ) return SQLITE_NOMEM; zEnd = &zCopy[strlen(zCopy)]; z = (char *)sqlite3Fts3NextToken(zCopy, &n); z[n] = '\0'; sqlite3Fts3Dequote(z); m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1); if( !m ){ *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z); rc = SQLITE_ERROR; }else{ char const **aArg = 0; int iArg = 0; z = &z[n+1]; |
︙ | ︙ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
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 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) #include "fts3Int.h" #include <string.h> #include <assert.h> #include <stdlib.h> typedef struct PendingList PendingList; typedef struct SegmentNode SegmentNode; typedef struct SegmentWriter SegmentWriter; /* ** Data structure used while accumulating terms in the pending-terms hash ** table. The hash table entry maps from term (a string) to a malloc'd ** instance of this structure. */ struct PendingList { int nData; char *aData; int nSpace; sqlite3_int64 iLastDocid; sqlite3_int64 iLastCol; sqlite3_int64 iLastPos; }; /* ** An instance of this structure is used to iterate through the terms on ** a contiguous set of segment b-tree leaf nodes. Although the details of ** this structure are only manipulated by code in this file, opaque handles ** of type Fts3SegReader* are also used by code in fts3.c to iterate through ** terms when querying the full-text index. See functions: ** ** sqlite3Fts3SegReaderNew() ** sqlite3Fts3SegReaderFree() ** sqlite3Fts3SegReaderIterate() ** ** Methods used to manipulate Fts3SegReader structures: ** ** fts3SegReaderNext() ** fts3SegReaderFirstDocid() ** fts3SegReaderNextDocid() */ struct Fts3SegReader { int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ | > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | < > > | 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 | #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) #include "fts3Int.h" #include <string.h> #include <assert.h> #include <stdlib.h> /* ** When full-text index nodes are loaded from disk, the buffer that they ** are loaded into has the following number of bytes of padding at the end ** of it. i.e. if a full-text index node is 900 bytes in size, then a buffer ** of 920 bytes is allocated for it. ** ** This means that if we have a pointer into a buffer containing node data, ** it is always safe to read up to two varints from it without risking an ** overread, even if the node data is corrupted. */ #define FTS3_NODE_PADDING (FTS3_VARINT_MAX*2) typedef struct PendingList PendingList; typedef struct SegmentNode SegmentNode; typedef struct SegmentWriter SegmentWriter; /* ** Data structure used while accumulating terms in the pending-terms hash ** table. The hash table entry maps from term (a string) to a malloc'd ** instance of this structure. */ struct PendingList { int nData; char *aData; int nSpace; sqlite3_int64 iLastDocid; sqlite3_int64 iLastCol; sqlite3_int64 iLastPos; }; /* ** Each cursor has a (possibly empty) linked list of the following objects. */ struct Fts3DeferredToken { Fts3PhraseToken *pToken; /* Pointer to corresponding expr token */ int iCol; /* Column token must occur in */ Fts3DeferredToken *pNext; /* Next in list of deferred tokens */ PendingList *pList; /* Doclist is assembled here */ }; /* ** An instance of this structure is used to iterate through the terms on ** a contiguous set of segment b-tree leaf nodes. Although the details of ** this structure are only manipulated by code in this file, opaque handles ** of type Fts3SegReader* are also used by code in fts3.c to iterate through ** terms when querying the full-text index. See functions: ** ** sqlite3Fts3SegReaderNew() ** sqlite3Fts3SegReaderFree() ** sqlite3Fts3SegReaderCost() ** sqlite3Fts3SegReaderIterate() ** ** Methods used to manipulate Fts3SegReader structures: ** ** fts3SegReaderNext() ** fts3SegReaderFirstDocid() ** fts3SegReaderNextDocid() */ struct Fts3SegReader { int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */ sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */ sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */ sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */ char *aNode; /* Pointer to node data (or NULL) */ int nNode; /* Size of buffer at aNode (or 0) */ Fts3HashElem **ppNextElem; /* Variables set by fts3SegReaderNext(). These may be read directly ** by the caller. They are valid from the time SegmentReaderNew() returns ** until SegmentReaderNext() returns something other than SQLITE_OK ** (i.e. SQLITE_DONE). */ int nTerm; /* Number of bytes in current term */ char *zTerm; /* Pointer to current term */ int nTermAlloc; /* Allocated size of zTerm buffer */ char *aDoclist; /* Pointer to doclist of current entry */ int nDoclist; /* Size of doclist in current entry */ /* The following variables are used to iterate through the current doclist */ char *pOffsetList; sqlite3_int64 iDocid; }; #define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0) #define fts3SegReaderIsRootOnly(p) ((p)->aNode==(char *)&(p)[1]) /* ** An instance of this structure is used to create a segment b-tree in the ** database. The internal details of this type are only accessed by the ** following functions: ** ** fts3SegWriterAdd() |
︙ | ︙ | |||
149 150 151 152 153 154 155 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 | < | | | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_DELETE_DOCSIZE 19 #define SQL_REPLACE_DOCSIZE 20 #define SQL_SELECT_DOCSIZE 21 #define SQL_SELECT_DOCTOTAL 22 #define SQL_REPLACE_DOCTOTAL 23 /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, ** *pp is set to the requested statement handle and SQLITE_OK returned. ** Otherwise, an SQLite error code is returned and *pp is set to 0. ** |
︙ | ︙ | |||
199 200 201 202 203 204 205 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", | < | | | | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", /* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", /* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", /* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0", /* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)", }; int rc = SQLITE_OK; sqlite3_stmt *pStmt; assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); assert( eStmt<SizeofArray(azSql) && eStmt>=0 ); |
︙ | ︙ | |||
280 281 282 283 284 285 286 | rc = sqlite3_reset(pStmt); } *pRC = rc; } /* | > > > | > > | > | < > > > | < < < < > | < < < | < | < < < > | | | < < < | < < < < | < | | 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 | rc = sqlite3_reset(pStmt); } *pRC = rc; } /* ** This function ensures that the caller has obtained a shared-cache ** table-lock on the %_content table. This is required before reading ** data from the fts3 table. If this lock is not acquired first, then ** the caller may end up holding read-locks on the %_segments and %_segdir ** tables, but no read-lock on the %_content table. If this happens ** a second connection will be able to write to the fts3 table, but ** attempting to commit those writes might return SQLITE_LOCKED or ** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain ** write-locks on the %_segments and %_segdir ** tables). ** ** We try to avoid this because if FTS3 returns any error when committing ** a transaction, the whole transaction will be rolled back. And this is ** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can ** still happen if the user reads data directly from the %_segments or ** %_segdir tables instead of going through FTS3 though. */ int sqlite3Fts3ReadLock(Fts3Table *p){ int rc; /* Return code */ sqlite3_stmt *pStmt; /* Statement used to obtain lock */ rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0); if( rc==SQLITE_OK ){ sqlite3_bind_null(pStmt, 1); sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } return rc; } /* ** Set *ppStmt to a statement handle that may be used to iterate through ** all rows in the %_segdir table, from oldest to newest. If successful, ** return SQLITE_OK. If an error occurs while preparing the statement, ** return an SQLite error code. |
︙ | ︙ | |||
456 457 458 459 460 461 462 | ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in ** p->iPrevDocid, and the column is specified by argument iCol. ** ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. */ static int fts3PendingTermsAdd( | | | | | | 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in ** p->iPrevDocid, and the column is specified by argument iCol. ** ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. */ static int fts3PendingTermsAdd( Fts3Table *p, /* Table into which text will be inserted */ const char *zText, /* Text of document to be inserted */ int iCol, /* Column into which text is being inserted */ u32 *pnWord /* OUT: Number of tokens inserted */ ){ int rc; int iStart; int iEnd; int iPos; int nWord = 0; |
︙ | ︙ | |||
544 545 546 547 548 549 550 551 552 553 554 555 556 557 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } void sqlite3Fts3PendingTermsClear(Fts3Table *p){ Fts3HashElem *pElem; for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ sqlite3_free(fts3HashData(pElem)); } fts3HashClear(&p->pendingTerms); p->nPendingData = 0; | > > > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } /* ** Discard the contents of the pending-terms hash table. */ void sqlite3Fts3PendingTermsClear(Fts3Table *p){ Fts3HashElem *pElem; for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ sqlite3_free(fts3HashData(pElem)); } fts3HashClear(&p->pendingTerms); p->nPendingData = 0; |
︙ | ︙ | |||
571 572 573 574 575 576 577 578 579 580 581 582 583 584 | const char *zText = (const char *)sqlite3_value_text(apVal[i]); if( zText ){ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); if( rc!=SQLITE_OK ){ return rc; } } } return SQLITE_OK; } /* ** This function is called by the xUpdate() method for an INSERT operation. ** The apVal parameter is passed a copy of the apVal argument passed by | > | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 | const char *zText = (const char *)sqlite3_value_text(apVal[i]); if( zText ){ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); if( rc!=SQLITE_OK ){ return rc; } } aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]); } return SQLITE_OK; } /* ** This function is called by the xUpdate() method for an INSERT operation. ** The apVal parameter is passed a copy of the apVal argument passed by |
︙ | ︙ | |||
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | /* Delete everything from the %_content, %_segments and %_segdir tables. */ fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_STAT, 0); } return rc; } /* ** The first element in the apVal[] array is assumed to contain the docid ** (an integer) of a row about to be deleted. Remove all terms from the ** full-text index. */ | > > | | 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | /* Delete everything from the %_content, %_segments and %_segdir tables. */ fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); } if( p->bHasStat ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_STAT, 0); } return rc; } /* ** The first element in the apVal[] array is assumed to contain the docid ** (an integer) of a row about to be deleted. Remove all terms from the ** full-text index. */ static void fts3DeleteTerms( int *pRC, /* Result code */ Fts3Table *p, /* The FTS table to delete from */ sqlite3_value **apVal, /* apVal[] contains the docid to be deleted */ u32 *aSz /* Sizes of deleted document written here */ ){ int rc; sqlite3_stmt *pSelect; |
︙ | ︙ | |||
690 691 692 693 694 695 696 697 698 699 700 701 702 703 | const char *zText = (const char *)sqlite3_column_text(pSelect, i); rc = fts3PendingTermsAdd(p, zText, -1, &aSz[i-1]); if( rc!=SQLITE_OK ){ sqlite3_reset(pSelect); *pRC = rc; return; } } } rc = sqlite3_reset(pSelect); }else{ sqlite3_reset(pSelect); } *pRC = rc; | > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 | const char *zText = (const char *)sqlite3_column_text(pSelect, i); rc = fts3PendingTermsAdd(p, zText, -1, &aSz[i-1]); if( rc!=SQLITE_OK ){ sqlite3_reset(pSelect); *pRC = rc; return; } aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i); } } rc = sqlite3_reset(pSelect); }else{ sqlite3_reset(pSelect); } *pRC = rc; |
︙ | ︙ | |||
751 752 753 754 755 756 757 758 759 760 761 762 763 | }else{ *piIdx = iNext; } } return rc; } /* ** Move the iterator passed as the first argument to the next term in the ** segment. If successful, SQLITE_OK is returned. If there is no next term, ** SQLITE_DONE. Otherwise, an SQLite error code. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | > | > > > > > < < < < | | | > > > > > > > > > > < > > > > > > > > > > | 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 | }else{ *piIdx = iNext; } } return rc; } /* ** The %_segments table is declared as follows: ** ** CREATE TABLE %_segments(blockid INTEGER PRIMARY KEY, block BLOB) ** ** This function reads data from a single row of the %_segments table. The ** specific row is identified by the iBlockid parameter. If paBlob is not ** NULL, then a buffer is allocated using sqlite3_malloc() and populated ** with the contents of the blob stored in the "block" column of the ** identified table row is. Whether or not paBlob is NULL, *pnBlob is set ** to the size of the blob in bytes before returning. ** ** If an error occurs, or the table does not contain the specified row, ** an SQLite error code is returned. Otherwise, SQLITE_OK is returned. If ** paBlob is non-NULL, then it is the responsibility of the caller to ** eventually free the returned buffer. ** ** This function may leave an open sqlite3_blob* handle in the ** Fts3Table.pSegments variable. This handle is reused by subsequent calls ** to this function. The handle may be closed by calling the ** sqlite3Fts3SegmentsClose() function. Reusing a blob handle is a handy ** performance improvement, but the blob handle should always be closed ** before control is returned to the user (to prevent a lock being held ** on the database file for longer than necessary). Thus, any virtual table ** method (xFilter etc.) that may directly or indirectly call this function ** must call sqlite3Fts3SegmentsClose() before returning. */ int sqlite3Fts3ReadBlock( Fts3Table *p, /* FTS3 table handle */ sqlite3_int64 iBlockid, /* Access the row with blockid=$iBlockid */ char **paBlob, /* OUT: Blob data in malloc'd buffer */ int *pnBlob /* OUT: Size of blob data */ ){ int rc; /* Return code */ /* pnBlob must be non-NULL. paBlob may be NULL or non-NULL. */ assert( pnBlob); if( p->pSegments ){ rc = sqlite3_blob_reopen(p->pSegments, iBlockid); }else{ if( 0==p->zSegmentsTbl ){ p->zSegmentsTbl = sqlite3_mprintf("%s_segments", p->zName); if( 0==p->zSegmentsTbl ) return SQLITE_NOMEM; } rc = sqlite3_blob_open( p->db, p->zDb, p->zSegmentsTbl, "block", iBlockid, 0, &p->pSegments ); } if( rc==SQLITE_OK ){ int nByte = sqlite3_blob_bytes(p->pSegments); if( paBlob ){ char *aByte = sqlite3_malloc(nByte + FTS3_NODE_PADDING); if( !aByte ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_blob_read(p->pSegments, aByte, nByte, 0); memset(&aByte[nByte], 0, FTS3_NODE_PADDING); if( rc!=SQLITE_OK ){ sqlite3_free(aByte); aByte = 0; } } *paBlob = aByte; } *pnBlob = nByte; } return rc; } /* ** Close the blob handle at p->pSegments, if it is open. See comments above ** the sqlite3Fts3ReadBlock() function for details. */ void sqlite3Fts3SegmentsClose(Fts3Table *p){ sqlite3_blob_close(p->pSegments); p->pSegments = 0; } /* ** Move the iterator passed as the first argument to the next term in the ** segment. If successful, SQLITE_OK is returned. If there is no next term, ** SQLITE_DONE. Otherwise, an SQLite error code. */ static int fts3SegReaderNext(Fts3Table *p, Fts3SegReader *pReader){ char *pNext; /* Cursor variable */ int nPrefix; /* Number of bytes in term prefix */ int nSuffix; /* Number of bytes in term suffix */ if( !pReader->aDoclist ){ pNext = pReader->aNode; }else{ pNext = &pReader->aDoclist[pReader->nDoclist]; } if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ int rc; /* Return code from Fts3ReadBlock() */ if( fts3SegReaderIsPending(pReader) ){ Fts3HashElem *pElem = *(pReader->ppNextElem); if( pElem==0 ){ pReader->aNode = 0; }else{ PendingList *pList = (PendingList *)fts3HashData(pElem); pReader->zTerm = (char *)fts3HashKey(pElem); pReader->nTerm = fts3HashKeysize(pElem); pReader->nNode = pReader->nDoclist = pList->nData + 1; pReader->aNode = pReader->aDoclist = pList->aData; pReader->ppNextElem++; assert( pReader->aNode ); } return SQLITE_OK; } if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); } pReader->aNode = 0; /* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf ** blocks have already been traversed. */ assert( pReader->iCurrentBlock<=pReader->iLeafEndBlock ); if( pReader->iCurrentBlock>=pReader->iLeafEndBlock ){ return SQLITE_OK; } rc = sqlite3Fts3ReadBlock( p, ++pReader->iCurrentBlock, &pReader->aNode, &pReader->nNode ); if( rc!=SQLITE_OK ) return rc; pNext = pReader->aNode; } /* Because of the FTS3_NODE_PADDING bytes of padding, the following is ** safe (no risk of overread) even if the node data is corrupted. */ pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); if( nPrefix<0 || nSuffix<=0 || &pNext[nSuffix]>&pReader->aNode[pReader->nNode] ){ return SQLITE_CORRUPT; } if( nPrefix+nSuffix>pReader->nTermAlloc ){ int nNew = (nPrefix+nSuffix)*2; char *zNew = sqlite3_realloc(pReader->zTerm, nNew); if( !zNew ){ return SQLITE_NOMEM; } pReader->zTerm = zNew; pReader->nTermAlloc = nNew; } memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); pReader->nTerm = nPrefix+nSuffix; pNext += nSuffix; pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist); pReader->aDoclist = pNext; pReader->pOffsetList = 0; /* Check that the doclist does not appear to extend past the end of the ** b-tree node. And that the final byte of the doclist is 0x00. If either ** of these statements is untrue, then the data structure is corrupt. */ if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode] || pReader->aDoclist[pReader->nDoclist-1] ){ return SQLITE_CORRUPT; } return SQLITE_OK; } /* ** Set the SegReader to point to the first docid in the doclist associated ** with the current term. */ |
︙ | ︙ | |||
879 880 881 882 883 884 885 886 887 888 889 890 891 | pReader->pOffsetList = 0; }else{ sqlite3_int64 iDelta; pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); pReader->iDocid += iDelta; } } /* ** Free all allocations associated with the iterator passed as the ** second argument. */ void sqlite3Fts3SegReaderFree(Fts3Table *p, Fts3SegReader *pReader){ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | < < < < < < < | | < > > | > | < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < | 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 | pReader->pOffsetList = 0; }else{ sqlite3_int64 iDelta; pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); pReader->iDocid += iDelta; } } /* ** This function is called to estimate the amount of data that will be ** loaded from the disk If SegReaderIterate() is called on this seg-reader, ** in units of average document size. ** ** This can be used as follows: If the caller has a small doclist that ** contains references to N documents, and is considering merging it with ** a large doclist (size X "average documents"), it may opt not to load ** the large doclist if X>N. */ int sqlite3Fts3SegReaderCost( Fts3Cursor *pCsr, /* FTS3 cursor handle */ Fts3SegReader *pReader, /* Segment-reader handle */ int *pnCost /* IN/OUT: Number of bytes read */ ){ Fts3Table *p = (Fts3Table*)pCsr->base.pVtab; int rc = SQLITE_OK; /* Return code */ int nCost = 0; /* Cost in bytes to return */ int pgsz = p->nPgsz; /* Database page size */ /* If this seg-reader is reading the pending-terms table, or if all data ** for the segment is stored on the root page of the b-tree, then the cost ** is zero. In this case all required data is already in main memory. */ if( p->bHasStat && !fts3SegReaderIsPending(pReader) && !fts3SegReaderIsRootOnly(pReader) ){ int nBlob = 0; sqlite3_int64 iBlock; if( pCsr->nRowAvg==0 ){ /* The average document size, which is required to calculate the cost ** of each doclist, has not yet been determined. Read the required ** data from the %_stat table to calculate it. ** ** Entry 0 of the %_stat table is a blob containing (nCol+1) FTS3 ** varints, where nCol is the number of columns in the FTS3 table. ** The first varint is the number of documents currently stored in ** the table. The following nCol varints contain the total amount of ** data stored in all rows of each column of the table, from left ** to right. */ sqlite3_stmt *pStmt; rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0); if( rc ) return rc; if( sqlite3_step(pStmt)==SQLITE_ROW ){ sqlite3_int64 nDoc = 0; sqlite3_int64 nByte = 0; const char *a = sqlite3_column_blob(pStmt, 0); if( a ){ const char *pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; a += sqlite3Fts3GetVarint(a, &nDoc); while( a<pEnd ){ a += sqlite3Fts3GetVarint(a, &nByte); } } pCsr->nRowAvg = (((nByte / nDoc) + pgsz - 1) / pgsz); } rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK || pCsr->nRowAvg==0 ) return rc; } /* Assume that a blob flows over onto overflow pages if it is larger ** than (pgsz-35) bytes in size (the file-format documentation ** confirms this). */ for(iBlock=pReader->iStartBlock; iBlock<=pReader->iLeafEndBlock; iBlock++){ rc = sqlite3Fts3ReadBlock(p, iBlock, 0, &nBlob); if( rc!=SQLITE_OK ) break; if( (nBlob+35)>pgsz ){ int nOvfl = (nBlob + 34)/pgsz; nCost += ((nOvfl + pCsr->nRowAvg - 1)/pCsr->nRowAvg); } } } *pnCost += nCost; return rc; } /* ** Free all allocations associated with the iterator passed as the ** second argument. */ void sqlite3Fts3SegReaderFree(Fts3Table *p, Fts3SegReader *pReader){ if( pReader && !fts3SegReaderIsPending(pReader) ){ sqlite3_free(pReader->zTerm); if( !fts3SegReaderIsRootOnly(pReader) ){ sqlite3_free(pReader->aNode); } } sqlite3_free(pReader); } /* ** Allocate a new SegReader object. */ int sqlite3Fts3SegReaderNew( Fts3Table *p, /* Virtual table handle */ int iAge, /* Segment "age". */ sqlite3_int64 iStartLeaf, /* First leaf to traverse */ sqlite3_int64 iEndLeaf, /* Final leaf to traverse */ sqlite3_int64 iEndBlock, /* Final block of segment */ const char *zRoot, /* Buffer containing root node */ int nRoot, /* Size of buffer containing root node */ Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ ){ int rc = SQLITE_OK; /* Return code */ Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ assert( iStartLeaf<=iEndLeaf ); if( iStartLeaf==0 ){ nExtra = nRoot + FTS3_NODE_PADDING; } pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); if( !pReader ){ return SQLITE_NOMEM; } memset(pReader, 0, sizeof(Fts3SegReader)); pReader->iIdx = iAge; pReader->iStartBlock = iStartLeaf; pReader->iLeafEndBlock = iEndLeaf; pReader->iEndBlock = iEndBlock; if( nExtra ){ /* The entire segment is stored in the root node. */ pReader->aNode = (char *)&pReader[1]; pReader->nNode = nRoot; memcpy(pReader->aNode, zRoot, nRoot); memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); }else{ pReader->iCurrentBlock = iStartLeaf-1; } if( rc==SQLITE_OK ){ *ppReader = pReader; }else{ sqlite3Fts3SegReaderFree(p, pReader); } return rc; } |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 | if( !pReader ){ rc = SQLITE_NOMEM; }else{ memset(pReader, 0, nByte); pReader->iIdx = 0x7FFFFFFF; pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); | < | 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 | if( !pReader ){ rc = SQLITE_NOMEM; }else{ memset(pReader, 0, nByte); pReader->iIdx = 0x7FFFFFFF; pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); } } if( isPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; |
︙ | ︙ | |||
1321 1322 1323 1324 1325 1326 1327 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( | | | 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( Fts3Table *p, /* Virtual table handle */ SegmentNode **ppTree, /* IN/OUT: SegmentNode handle */ int isCopyTerm, /* True if zTerm/nTerm is transient */ const char *zTerm, /* Pointer to buffer containing term */ int nTerm /* Size of term in bytes */ ){ SegmentNode *pTree = *ppTree; int rc; |
︙ | ︙ | |||
1951 1952 1953 1954 1955 1956 1957 | /* If the Fts3SegFilter defines a specific term (or term prefix) to search ** for, then advance each segment iterator until it points to a term of ** equal or greater value than the specified term. This prevents many ** unnecessary merge/sort operations for the case where single segment ** b-tree leaf nodes contain more than one term. */ | | < | < > | | < > | 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 | /* If the Fts3SegFilter defines a specific term (or term prefix) to search ** for, then advance each segment iterator until it points to a term of ** equal or greater value than the specified term. This prevents many ** unnecessary merge/sort operations for the case where single segment ** b-tree leaf nodes contain more than one term. */ for(i=0; i<nSegment; i++){ int nTerm = pFilter->nTerm; const char *zTerm = pFilter->zTerm; Fts3SegReader *pSeg = apSegment[i]; do { rc = fts3SegReaderNext(p, pSeg); if( rc!=SQLITE_OK ) goto finished; }while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ); } fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp); while( apSegment[0]->aNode ){ int nTerm = apSegment[0]->nTerm; char *zTerm = apSegment[0]->zTerm; int nMerge = 1; |
︙ | ︙ | |||
2068 2069 2070 2071 2072 2073 2074 | ** term (if such a term exists in the index) has already been made. */ if( pFilter->zTerm && !isPrefix ){ goto finished; } for(i=0; i<nMerge; i++){ | | | 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 | ** term (if such a term exists in the index) has already been made. */ if( pFilter->zTerm && !isPrefix ){ goto finished; } for(i=0; i<nMerge; i++){ rc = fts3SegReaderNext(p, apSegment[i]); if( rc!=SQLITE_OK ) goto finished; } fts3SegReaderSort(apSegment, nSegment, nMerge, fts3SegReaderCmp); } finished: sqlite3_free(aBuffer); |
︙ | ︙ | |||
2094 2095 2096 2097 2098 2099 2100 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ | | | 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ int iNewLevel = 0; /* Level to create new segment at */ sqlite3_stmt *pStmt = 0; SegmentWriter *pWriter = 0; int nSegment = 0; /* Number of segments being merged */ Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ Fts3SegFilter filter; /* Segment term filter condition */ |
︙ | ︙ | |||
2383 2384 2385 2386 2387 2388 2389 | sqlite3_bind_int64(pStmt, 1, p->iPrevDocid); sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, sqlite3_free); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); } /* | | > > | > > > > | > > > > | | | | | > > | | | | | | | 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 | sqlite3_bind_int64(pStmt, 1, p->iPrevDocid); sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, sqlite3_free); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); } /* ** Record 0 of the %_stat table contains a blob consisting of N varints, ** where N is the number of user defined columns in the fts3 table plus ** two. If nCol is the number of user defined columns, then values of the ** varints are set as follows: ** ** Varint 0: Total number of rows in the table. ** ** Varint 1..nCol: For each column, the total number of tokens stored in ** the column for all rows of the table. ** ** Varint 1+nCol: The total size, in bytes, of all text values in all ** columns of all rows of the table. ** */ static void fts3UpdateDocTotals( int *pRC, /* The result code */ Fts3Table *p, /* Table being updated */ u32 *aSzIns, /* Size increases */ u32 *aSzDel, /* Size decreases */ int nChng /* Change in the number of documents */ ){ char *pBlob; /* Storage for BLOB written into %_stat */ int nBlob; /* Size of BLOB written into %_stat */ u32 *a; /* Array of integers that becomes the BLOB */ sqlite3_stmt *pStmt; /* Statement for reading and writing */ int i; /* Loop counter */ int rc; /* Result code from subfunctions */ const int nStat = p->nColumn+2; if( *pRC ) return; a = sqlite3_malloc( (sizeof(u32)+10)*nStat ); if( a==0 ){ *pRC = SQLITE_NOMEM; return; } pBlob = (char*)&a[nStat]; rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0); if( rc ){ sqlite3_free(a); *pRC = rc; return; } if( sqlite3_step(pStmt)==SQLITE_ROW ){ fts3DecodeIntArray(nStat, a, sqlite3_column_blob(pStmt, 0), sqlite3_column_bytes(pStmt, 0)); }else{ memset(a, 0, sizeof(u32)*(nStat) ); } sqlite3_reset(pStmt); if( nChng<0 && a[0]<(u32)(-nChng) ){ a[0] = 0; }else{ a[0] += nChng; } for(i=0; i<p->nColumn+1; i++){ u32 x = a[i+1]; if( x+aSzIns[i] < aSzDel[i] ){ x = 0; }else{ x = x + aSzIns[i] - aSzDel[i]; } a[i+1] = x; } fts3EncodeIntArray(nStat, a, pBlob, &nBlob); rc = fts3SqlStmt(p, SQL_REPLACE_DOCTOTAL, &pStmt, 0); if( rc ){ sqlite3_free(a); *pRC = rc; return; } sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC); |
︙ | ︙ | |||
2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 | }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } return rc; } /* ** This function does the work for the xUpdate method of FTS3 virtual ** tables. */ int sqlite3Fts3UpdateMethod( sqlite3_vtab *pVtab, /* FTS3 vtab object */ int nArg, /* Size of argument array */ sqlite3_value **apVal, /* Array of arguments */ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ int isRemove = 0; /* True for an UPDATE or DELETE */ sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ u32 *aSzIns; /* Sizes of inserted documents */ u32 *aSzDel; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ /* Allocate space to hold the change in document sizes */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | < > < > | > | 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 | }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } sqlite3Fts3SegmentsClose(p); return rc; } /* ** Return the deferred doclist associated with deferred token pDeferred. ** This function assumes that sqlite3Fts3CacheDeferredDoclists() has already ** been called to allocate and populate the doclist. */ char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *pDeferred, int *pnByte){ if( pDeferred->pList ){ *pnByte = pDeferred->pList->nData; return pDeferred->pList->aData; } *pnByte = 0; return 0; } /* ** Helper fucntion for FreeDeferredDoclists(). This function removes all ** references to deferred doclists from within the tree of Fts3Expr ** structures headed by */ static void fts3DeferredDoclistClear(Fts3Expr *pExpr){ if( pExpr ){ fts3DeferredDoclistClear(pExpr->pLeft); fts3DeferredDoclistClear(pExpr->pRight); if( pExpr->isLoaded ){ sqlite3_free(pExpr->aDoclist); pExpr->isLoaded = 0; pExpr->aDoclist = 0; pExpr->nDoclist = 0; pExpr->pCurrent = 0; pExpr->iCurrent = 0; } } } /* ** Delete all cached deferred doclists. Deferred doclists are cached ** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function. */ void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *pCsr){ Fts3DeferredToken *pDef; for(pDef=pCsr->pDeferred; pDef; pDef=pDef->pNext){ sqlite3_free(pDef->pList); pDef->pList = 0; } if( pCsr->pDeferred ){ fts3DeferredDoclistClear(pCsr->pExpr); } } /* ** Free all entries in the pCsr->pDeffered list. Entries are added to ** this list using sqlite3Fts3DeferToken(). */ void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *pCsr){ Fts3DeferredToken *pDef; Fts3DeferredToken *pNext; for(pDef=pCsr->pDeferred; pDef; pDef=pNext){ pNext = pDef->pNext; sqlite3_free(pDef->pList); sqlite3_free(pDef); } pCsr->pDeferred = 0; } /* ** Generate deferred-doclists for all tokens in the pCsr->pDeferred list ** based on the row that pCsr currently points to. ** ** A deferred-doclist is like any other doclist with position information ** included, except that it only contains entries for a single row of the ** table, not for all rows. */ int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){ int rc = SQLITE_OK; /* Return code */ if( pCsr->pDeferred ){ int i; /* Used to iterate through table columns */ sqlite3_int64 iDocid; /* Docid of the row pCsr points to */ Fts3DeferredToken *pDef; /* Used to iterate through deferred tokens */ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; sqlite3_tokenizer *pT = p->pTokenizer; sqlite3_tokenizer_module const *pModule = pT->pModule; assert( pCsr->isRequireSeek==0 ); iDocid = sqlite3_column_int64(pCsr->pStmt, 0); for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){ const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1); sqlite3_tokenizer_cursor *pTC = 0; rc = pModule->xOpen(pT, zText, -1, &pTC); while( rc==SQLITE_OK ){ char const *zToken; /* Buffer containing token */ int nToken; /* Number of bytes in token */ int iDum1, iDum2; /* Dummy variables */ int iPos; /* Position of token in zText */ pTC->pTokenizer = pT; rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos); for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ Fts3PhraseToken *pPT = pDef->pToken; if( (pDef->iCol>=p->nColumn || pDef->iCol==i) && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken)) && (0==memcmp(zToken, pPT->z, pPT->n)) ){ fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc); } } } if( pTC ) pModule->xClose(pTC); if( rc==SQLITE_DONE ) rc = SQLITE_OK; } for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){ if( pDef->pList ){ rc = fts3PendingListAppendVarint(&pDef->pList, 0); } } } return rc; } /* ** Add an entry for token pToken to the pCsr->pDeferred list. */ int sqlite3Fts3DeferToken( Fts3Cursor *pCsr, /* Fts3 table cursor */ Fts3PhraseToken *pToken, /* Token to defer */ int iCol /* Column that token must appear in (or -1) */ ){ Fts3DeferredToken *pDeferred; pDeferred = sqlite3_malloc(sizeof(*pDeferred)); if( !pDeferred ){ return SQLITE_NOMEM; } memset(pDeferred, 0, sizeof(*pDeferred)); pDeferred->pToken = pToken; pDeferred->pNext = pCsr->pDeferred; pDeferred->iCol = iCol; pCsr->pDeferred = pDeferred; assert( pToken->pDeferred==0 ); pToken->pDeferred = pDeferred; return SQLITE_OK; } /* ** This function does the work for the xUpdate method of FTS3 virtual ** tables. */ int sqlite3Fts3UpdateMethod( sqlite3_vtab *pVtab, /* FTS3 vtab object */ int nArg, /* Size of argument array */ sqlite3_value **apVal, /* Array of arguments */ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ int isRemove = 0; /* True for an UPDATE or DELETE */ sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ u32 *aSzIns; /* Sizes of inserted documents */ u32 *aSzDel; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ assert( p->pSegments==0 ); /* Allocate space to hold the change in document sizes */ aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 ); if( aSzIns==0 ) return SQLITE_NOMEM; aSzDel = &aSzIns[p->nColumn+1]; memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2); /* If this is a DELETE or UPDATE operation, remove the old record. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ int isEmpty = 0; rc = fts3IsEmpty(p, apVal, &isEmpty); if( rc==SQLITE_OK ){ if( isEmpty ){ /* Deleting this row means the whole table is empty. In this case ** delete the contents of all three tables and throw away any ** data in the pendingTerms hash table. */ rc = fts3DeleteAll(p); }else{ isRemove = 1; iRemove = sqlite3_value_int64(apVal[0]); rc = fts3PendingTermsDocid(p, iRemove); fts3DeleteTerms(&rc, p, apVal, aSzDel); fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal); } nChng--; } } }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){ sqlite3_free(aSzIns); return fts3SpecialInsert(p, apVal[p->nColumn+2]); } /* If this is an INSERT or UPDATE operation, insert the new record. */ if( nArg>1 && rc==SQLITE_OK ){ rc = fts3InsertData(p, apVal, pRowid); if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ rc = fts3PendingTermsDocid(p, *pRowid); } if( rc==SQLITE_OK ){ rc = fts3InsertTerms(p, apVal, aSzIns); } if( p->bHasDocsize ){ fts3InsertDocsize(&rc, p, aSzIns); } nChng++; } if( p->bHasStat ){ fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); } sqlite3_free(aSzIns); sqlite3Fts3SegmentsClose(p); return rc; } /* ** Flush any data in the pending-terms hash table to disk. If successful, ** merge all segments in the database (including the new segment, if ** there was any data to flush) into a single segment. |
︙ | ︙ | |||
2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 | sqlite3Fts3PendingTermsClear(p); } }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); } } return rc; } #endif | > | 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 | sqlite3Fts3PendingTermsClear(p); } }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); } } sqlite3Fts3SegmentsClose(p); return rc; } #endif |
Added ext/fts3/fts3speed.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 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 | #-------------------------------------------------------------------------- # This script contains several sub-programs used to test FTS3/FTS4 # performance. It does not run the queries directly, but generates SQL # scripts that can be run using the shell tool. # # The following cases are tested: # # 1. Inserting documents into an FTS3 table. # 2. Optimizing an FTS3 table (i.e. "INSERT INTO t1 VALUES('optimize')"). # 3. Deleting documents from an FTS3 table. # 4. Querying FTS3 tables. # # Number of tokens in vocabulary. And number of tokens in each document. # set VOCAB_SIZE 2000 set DOC_SIZE 100 set NUM_INSERTS 100000 set NUM_SELECTS 1000 # Force everything in this script to be deterministic. # expr {srand(0)} proc usage {} { puts stderr "Usage: $::argv0 <rows> <selects>" exit -1 } proc sql {sql} { puts $::fd $sql } # Return a list of $nWord randomly generated tokens each between 2 and 10 # characters in length. # proc build_vocab {nWord} { set ret [list] set chars [list 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] for {set i 0} {$i<$nWord} {incr i} { set len [expr {int((rand()*9.0)+2)}] set term "" for {set j 0} {$j<$len} {incr j} { append term [lindex $chars [expr {int(rand()*[llength $chars])}]] } lappend ret $term } set ret } proc select_term {} { set n [llength $::vocab] set t [expr int(rand()*$n*3)] if {$t>=2*$n} { set t [expr {($t-2*$n)/100}] } if {$t>=$n} { set t [expr {($t-$n)/10}] } lindex $::vocab $t } proc select_doc {nTerm} { set ret [list] for {set i 0} {$i<$nTerm} {incr i} { lappend ret [select_term] } set ret } proc test_1 {nInsert} { sql "PRAGMA synchronous = OFF;" sql "DROP TABLE IF EXISTS t1;" sql "CREATE VIRTUAL TABLE t1 USING fts4;" for {set i 0} {$i < $nInsert} {incr i} { set doc [select_doc $::DOC_SIZE] sql "INSERT INTO t1 VALUES('$doc');" } } proc test_2 {} { sql "INSERT INTO t1(t1) VALUES('optimize');" } proc test_3 {nSelect} { for {set i 0} {$i < $nSelect} {incr i} { sql "SELECT count(*) FROM t1 WHERE t1 MATCH '[select_term]';" } } proc test_4 {nSelect} { for {set i 0} {$i < $nSelect} {incr i} { sql "SELECT count(*) FROM t1 WHERE t1 MATCH '[select_term] [select_term]';" } } if {[llength $argv]!=0} usage set ::vocab [build_vocab $::VOCAB_SIZE] set ::fd [open fts3speed_insert.sql w] test_1 $NUM_INSERTS close $::fd set ::fd [open fts3speed_select.sql w] test_3 $NUM_SELECTS close $::fd set ::fd [open fts3speed_select2.sql w] test_4 $NUM_SELECTS close $::fd set ::fd [open fts3speed_optimize.sql w] test_2 close $::fd puts "Success. Created files:" puts " fts3speed_insert.sql" puts " fts3speed_select.sql" puts " fts3speed_select2.sql" puts " fts3speed_optimize.sql" |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) /* ** This file contains an implementation of a couple of different variants ** of the r-tree algorithm. See the README file for further details. The ** same data-structure is used for all, but the algorithms for insert and | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. */ /* ** Database Format of R-Tree Tables ** -------------------------------- ** ** The data structure for a single virtual r-tree table is stored in three ** native SQLite tables declared as follows. In each case, the '%' character ** in the table name is replaced with the user-supplied name of the r-tree ** table. ** ** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB) ** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER) ** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER) ** ** The data for each node of the r-tree structure is stored in the %_node ** table. For each node that is not the root node of the r-tree, there is ** an entry in the %_parent table associating the node with its parent. ** And for each row of data in the table, there is an entry in the %_rowid ** table that maps from the entries rowid to the id of the node that it ** is stored on. ** ** The root node of an r-tree always exists, even if the r-tree table is ** empty. The nodeno of the root node is always 1. All other nodes in the ** table must be the same size as the root node. The content of each node ** is formatted as follows: ** ** 1. If the node is the root node (node 1), then the first 2 bytes ** of the node contain the tree depth as a big-endian integer. ** For non-root nodes, the first 2 bytes are left unused. ** ** 2. The next 2 bytes contain the number of entries currently ** stored in the node. ** ** 3. The remainder of the node contains the node entries. Each entry ** consists of a single 8-byte integer followed by an even number ** of 4-byte coordinates. For leaf nodes the integer is the rowid ** of a record. For internal nodes it is the node number of a ** child page. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) /* ** This file contains an implementation of a couple of different variants ** of the r-tree algorithm. See the README file for further details. The ** same data-structure is used for all, but the algorithms for insert and |
︙ | ︙ | |||
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 | #define PickSeeds LinearPickSeeds #define AssignCells splitNodeGuttman #endif #if VARIANT_RSTARTREE_SPLIT #define AssignCells splitNodeStartree #endif #ifndef SQLITE_CORE #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #else #include "sqlite3.h" #endif #include <string.h> #include <assert.h> #include <stdint.h> #ifndef SQLITE_AMALGAMATION typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned int u32; #endif typedef struct Rtree Rtree; typedef struct RtreeCursor RtreeCursor; typedef struct RtreeNode RtreeNode; typedef struct RtreeCell RtreeCell; typedef struct RtreeConstraint RtreeConstraint; typedef union RtreeCoord RtreeCoord; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is | > > > > > > | 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 | #define PickSeeds LinearPickSeeds #define AssignCells splitNodeGuttman #endif #if VARIANT_RSTARTREE_SPLIT #define AssignCells splitNodeStartree #endif #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #ifndef SQLITE_CORE #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #else #include "sqlite3.h" #endif #include <string.h> #include <assert.h> #include <stdint.h> #ifndef SQLITE_AMALGAMATION #include "sqlite3rtree.h" typedef sqlite3_int64 i64; typedef unsigned char u8; typedef unsigned int u32; #endif typedef struct Rtree Rtree; typedef struct RtreeCursor RtreeCursor; typedef struct RtreeNode RtreeNode; typedef struct RtreeCell RtreeCell; typedef struct RtreeConstraint RtreeConstraint; typedef struct RtreeMatchArg RtreeMatchArg; typedef struct RtreeGeomCallback RtreeGeomCallback; typedef union RtreeCoord RtreeCoord; /* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */ #define RTREE_MAX_DIMENSIONS 5 /* Size of hash table Rtree.aHash. This hash table is not expected to ** ever contain very many entries, so a fixed number of buckets is |
︙ | ︙ | |||
141 142 143 144 145 146 147 148 149 150 151 152 153 154 | ** If an R*-tree "Reinsert" operation is required, the same number of ** cells are removed from the overfull node and reinserted into the tree. */ #define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) #define RTREE_REINSERT(p) RTREE_MINCELLS(p) #define RTREE_MAXCELLS 51 /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; RtreeNode *pNode; /* Node cursor is currently pointing at */ int iCell; /* Index of current cell in pNode */ | > > > > > > > > > | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | ** If an R*-tree "Reinsert" operation is required, the same number of ** cells are removed from the overfull node and reinserted into the tree. */ #define RTREE_MINCELLS(p) ((((p)->iNodeSize-4)/(p)->nBytesPerCell)/3) #define RTREE_REINSERT(p) RTREE_MINCELLS(p) #define RTREE_MAXCELLS 51 /* ** The smallest possible node-size is (512-64)==448 bytes. And the largest ** supported cell size is 48 bytes (8 byte rowid + ten 4 byte coordinates). ** Therefore all non-root nodes must contain at least 3 entries. Since ** 2^40 is greater than 2^64, an r-tree structure always has a depth of ** 40 or less. */ #define RTREE_MAX_DEPTH 40 /* ** An rtree cursor object. */ struct RtreeCursor { sqlite3_vtab_cursor base; RtreeNode *pNode; /* Node cursor is currently pointing at */ int iCell; /* Index of current cell in pNode */ |
︙ | ︙ | |||
173 174 175 176 177 178 179 | ((double)coord.i) \ ) /* ** A search constraint. */ struct RtreeConstraint { | | | | > > | | | | | > < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ((double)coord.i) \ ) /* ** A search constraint. */ struct RtreeConstraint { int iCoord; /* Index of constrained coordinate */ int op; /* Constraining operation */ double rValue; /* Constraint value. */ int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */ }; /* Possible values for RtreeConstraint.op */ #define RTREE_EQ 0x41 #define RTREE_LE 0x42 #define RTREE_LT 0x43 #define RTREE_GE 0x44 #define RTREE_GT 0x45 #define RTREE_MATCH 0x46 /* ** An rtree structure node. */ struct RtreeNode { RtreeNode *pParent; /* Parent node */ i64 iNode; int nRef; int isDirty; u8 *zData; RtreeNode *pNext; /* Next node in this hash chain */ }; #define NCELL(pNode) readInt16(&(pNode)->zData[2]) /* ** Structure to store a deserialized rtree record. */ struct RtreeCell { i64 iRowid; RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; }; /* ** Value for the first field of every RtreeMatchArg object. The MATCH ** operator tests that the first field of a blob operand matches this ** value to avoid operating on invalid blobs (which could cause a segfault). */ #define RTREE_GEOMETRY_MAGIC 0x891245AB /* ** An instance of this structure must be supplied as a blob argument to ** the right-hand-side of an SQL MATCH operator used to constrain an ** r-tree query. */ struct RtreeMatchArg { u32 magic; /* Always RTREE_GEOMETRY_MAGIC */ int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); void *pContext; int nParam; double aParam[1]; }; /* ** When a geometry callback is created (see sqlite3_rtree_geometry_callback), ** a single instance of the following structure is allocated. It is used ** as the context for the user-function created by by s_r_g_c(). The object ** is eventually deleted by the destructor mechanism provided by ** sqlite3_create_function_v2() (which is called by s_r_g_c() to create ** the geometry callback function). */ struct RtreeGeomCallback { int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *); void *pContext; }; #ifndef MAX # define MAX(x,y) ((x) < (y) ? (y) : (x)) #endif #ifndef MIN # define MIN(x,y) ((x) > (y) ? (y) : (x)) #endif |
︙ | ︙ | |||
303 304 305 306 307 308 309 | } } /* ** Clear the content of node p (set all bytes to 0x00). */ static void nodeZero(Rtree *pRtree, RtreeNode *p){ | < | | < < < | | | | | < | 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 | } } /* ** Clear the content of node p (set all bytes to 0x00). */ static void nodeZero(Rtree *pRtree, RtreeNode *p){ memset(&p->zData[2], 0, pRtree->iNodeSize-2); p->isDirty = 1; } /* ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ static int nodeHash(i64 iNode){ return ( (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^ (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0) ) % HASHSIZE; } /* ** Search the node hash table for node iNode. If found, return a pointer ** to it. Otherwise, return 0. */ static RtreeNode *nodeHashLookup(Rtree *pRtree, i64 iNode){ RtreeNode *p; for(p=pRtree->aHash[nodeHash(iNode)]; p && p->iNode!=iNode; p=p->pNext); return p; } /* ** Add node pNode to the node hash table. */ static void nodeHashInsert(Rtree *pRtree, RtreeNode *pNode){ int iHash; assert( pNode->pNext==0 ); iHash = nodeHash(pNode->iNode); pNode->pNext = pRtree->aHash[iHash]; pRtree->aHash[iHash] = pNode; } /* ** Remove node pNode from the node hash table. */ static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ RtreeNode **pp; |
︙ | ︙ | |||
363 364 365 366 367 368 369 | /* ** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0), ** indicating that node has not yet been assigned a node number. It is ** assigned a node number when nodeWrite() is called to write the ** node contents out to the database. */ | | | > > > > > > | | < | | | | | | | | > > | < > > | | | > > > > > > > > > > > > | > > > > > | > > > | > > > > > > > | < < < < < < < < < < < < < | 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 | /* ** Allocate and return new r-tree node. Initially, (RtreeNode.iNode==0), ** indicating that node has not yet been assigned a node number. It is ** assigned a node number when nodeWrite() is called to write the ** node contents out to the database. */ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ RtreeNode *pNode; pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize); if( pNode ){ memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); pNode->zData = (u8 *)&pNode[1]; pNode->nRef = 1; pNode->pParent = pParent; pNode->isDirty = 1; nodeReference(pParent); } return pNode; } /* ** Obtain a reference to an r-tree node. */ static int nodeAcquire( Rtree *pRtree, /* R-tree structure */ i64 iNode, /* Node number to load */ RtreeNode *pParent, /* Either the parent node or NULL */ RtreeNode **ppNode /* OUT: Acquired node */ ){ int rc; int rc2 = SQLITE_OK; RtreeNode *pNode; /* Check if the requested node is already in the hash table. If so, ** increase its reference count and return it. */ if( (pNode = nodeHashLookup(pRtree, iNode)) ){ assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); if( pParent && !pNode->pParent ){ nodeReference(pParent); pNode->pParent = pParent; } pNode->nRef++; *ppNode = pNode; return SQLITE_OK; } sqlite3_bind_int64(pRtree->pReadNode, 1, iNode); rc = sqlite3_step(pRtree->pReadNode); if( rc==SQLITE_ROW ){ const u8 *zBlob = sqlite3_column_blob(pRtree->pReadNode, 0); if( pRtree->iNodeSize==sqlite3_column_bytes(pRtree->pReadNode, 0) ){ pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ rc2 = SQLITE_NOMEM; }else{ pNode->pParent = pParent; pNode->zData = (u8 *)&pNode[1]; pNode->nRef = 1; pNode->iNode = iNode; pNode->isDirty = 0; pNode->pNext = 0; memcpy(pNode->zData, zBlob, pRtree->iNodeSize); nodeReference(pParent); } } } rc = sqlite3_reset(pRtree->pReadNode); if( rc==SQLITE_OK ) rc = rc2; /* If the root node was just loaded, set pRtree->iDepth to the height ** of the r-tree structure. A height of zero means all data is stored on ** the root node. A height of one means the children of the root node ** are the leaves, and so on. If the depth as specified on the root node ** is greater than RTREE_MAX_DEPTH, the r-tree structure must be corrupt. */ if( pNode && iNode==1 ){ pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT; } } /* If no error has occurred so far, check if the "number of entries" ** field on the node is too large. If so, set the return code to ** SQLITE_CORRUPT. */ if( pNode && rc==SQLITE_OK ){ if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ rc = SQLITE_CORRUPT; } } if( rc==SQLITE_OK ){ if( pNode!=0 ){ nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT; } *ppNode = pNode; }else{ sqlite3_free(pNode); *ppNode = 0; } return rc; } /* ** Overwrite cell iCell of node pNode with the contents of pCell. |
︙ | ︙ | |||
492 493 494 495 496 497 498 | ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); | | < | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | ){ int nCell; /* Current number of cells in pNode */ int nMaxCell; /* Maximum number of cells for pNode */ nMaxCell = (pRtree->iNodeSize-4)/pRtree->nBytesPerCell; nCell = NCELL(pNode); assert( nCell<=nMaxCell ); if( nCell<nMaxCell ){ nodeOverwriteCell(pRtree, pNode, pCell, nCell); writeInt16(&pNode->zData[2], nCell+1); pNode->isDirty = 1; } return (nCell==nMaxCell); |
︙ | ︙ | |||
713 714 715 716 717 718 719 720 721 722 723 724 725 726 | rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int rc; RtreeCursor *pCsr = (RtreeCursor *)cur; | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | > > > | | | > > > | > > > > > > > > > > > > > > > | | | > > > > > | > | > > > > > > | > | > > | > > | | > | | 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 | rc = SQLITE_OK; } *ppCursor = (sqlite3_vtab_cursor *)pCsr; return rc; } /* ** Free the RtreeCursor.aConstraint[] array and its contents. */ static void freeCursorConstraints(RtreeCursor *pCsr){ if( pCsr->aConstraint ){ int i; /* Used to iterate through constraint array */ for(i=0; i<pCsr->nConstraint; i++){ sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom; if( pGeom ){ if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser); sqlite3_free(pGeom); } } sqlite3_free(pCsr->aConstraint); pCsr->aConstraint = 0; } } /* ** Rtree virtual table module xClose method. */ static int rtreeClose(sqlite3_vtab_cursor *cur){ Rtree *pRtree = (Rtree *)(cur->pVtab); int rc; RtreeCursor *pCsr = (RtreeCursor *)cur; freeCursorConstraints(pCsr); rc = nodeRelease(pRtree, pCsr->pNode); sqlite3_free(pCsr); return rc; } /* ** Rtree virtual table module xEof method. ** ** Return non-zero if the cursor does not currently point to a valid ** record (i.e if the scan has finished), or zero otherwise. */ static int rtreeEof(sqlite3_vtab_cursor *cur){ RtreeCursor *pCsr = (RtreeCursor *)cur; return (pCsr->pNode==0); } /* ** The r-tree constraint passed as the second argument to this function is ** guaranteed to be a MATCH constraint. */ static int testRtreeGeom( Rtree *pRtree, /* R-Tree object */ RtreeConstraint *pConstraint, /* MATCH constraint to test */ RtreeCell *pCell, /* Cell to test */ int *pbRes /* OUT: Test result */ ){ int i; double aCoord[RTREE_MAX_DIMENSIONS*2]; int nCoord = pRtree->nDim*2; assert( pConstraint->op==RTREE_MATCH ); assert( pConstraint->pGeom ); for(i=0; i<nCoord; i++){ aCoord[i] = DCOORD(pCell->aCoord[i]); } return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes); } /* ** Cursor pCursor currently points to a cell in a non-leaf page. ** Set *pbEof to true if the sub-tree headed by the cell is filtered ** (excluded) by the constraints in the pCursor->aConstraint[] ** array, or false otherwise. ** ** Return SQLITE_OK if successful or an SQLite error code if an error ** occurs within a geometry callback. */ static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ RtreeCell cell; int ii; int bRes = 0; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]); double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]); assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH ); switch( p->op ){ case RTREE_LE: case RTREE_LT: bRes = p->rValue<cell_min; break; case RTREE_GE: case RTREE_GT: bRes = p->rValue>cell_max; break; case RTREE_EQ: bRes = (p->rValue>cell_max || p->rValue<cell_min); break; default: { int rc; assert( p->op==RTREE_MATCH ); rc = testRtreeGeom(pRtree, p, &cell, &bRes); if( rc!=SQLITE_OK ){ return rc; } bRes = !bRes; break; } } } *pbEof = bRes; return SQLITE_OK; } /* ** Test if the cell that cursor pCursor currently points to ** would be filtered (excluded) by the constraints in the ** pCursor->aConstraint[] array. If so, set *pbEof to true before ** returning. If the cell is not filtered (excluded) by the constraints, ** set pbEof to zero. ** ** Return SQLITE_OK if successful or an SQLite error code if an error ** occurs within a geometry callback. ** ** This function assumes that the cell is part of a leaf node. */ static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){ RtreeCell cell; int ii; *pbEof = 0; nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell); for(ii=0; ii<pCursor->nConstraint; ii++){ RtreeConstraint *p = &pCursor->aConstraint[ii]; double coord = DCOORD(cell.aCoord[p->iCoord]); int res; assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH ); switch( p->op ){ case RTREE_LE: res = (coord<=p->rValue); break; case RTREE_LT: res = (coord<p->rValue); break; case RTREE_GE: res = (coord>=p->rValue); break; case RTREE_GT: res = (coord>p->rValue); break; case RTREE_EQ: res = (coord==p->rValue); break; default: { int rc; assert( p->op==RTREE_MATCH ); rc = testRtreeGeom(pRtree, p, &cell, &res); if( rc!=SQLITE_OK ){ return rc; } break; } } if( !res ){ *pbEof = 1; return SQLITE_OK; } } return SQLITE_OK; } /* ** Cursor pCursor currently points at a node that heads a sub-tree of ** height iHeight (if iHeight==0, then the node is a leaf). Descend ** to point to the left-most cell of the sub-tree that matches the ** configured constraints. |
︙ | ︙ | |||
827 828 829 830 831 832 833 | RtreeNode *pSavedNode = pCursor->pNode; int iSavedCell = pCursor->iCell; assert( iHeight>=0 ); if( iHeight==0 ){ | | | | | | 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | RtreeNode *pSavedNode = pCursor->pNode; int iSavedCell = pCursor->iCell; assert( iHeight>=0 ); if( iHeight==0 ){ rc = testRtreeEntry(pRtree, pCursor, &isEof); }else{ rc = testRtreeCell(pRtree, pCursor, &isEof); } if( rc!=SQLITE_OK || isEof || iHeight==0 ){ *pEof = isEof; return rc; } iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell); rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild); if( rc!=SQLITE_OK ){ return rc; } |
︙ | ︙ | |||
869 870 871 872 873 874 875 | return SQLITE_OK; } /* ** One of the cells in node pNode is guaranteed to have a 64-bit ** integer value equal to iRowid. Return the index of this cell. */ | | > > > > > > > | | > | > | | | > | > > > > > > | < < | > > > | 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 | return SQLITE_OK; } /* ** One of the cells in node pNode is guaranteed to have a 64-bit ** integer value equal to iRowid. Return the index of this cell. */ static int nodeRowidIndex( Rtree *pRtree, RtreeNode *pNode, i64 iRowid, int *piIndex ){ int ii; int nCell = NCELL(pNode); for(ii=0; ii<nCell; ii++){ if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){ *piIndex = ii; return SQLITE_OK; } } return SQLITE_CORRUPT; } /* ** Return the index of the cell containing a pointer to node pNode ** in its parent. If pNode is the root node, return -1. */ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){ RtreeNode *pParent = pNode->pParent; if( pParent ){ return nodeRowidIndex(pRtree, pParent, pNode->iNode, piIndex); } *piIndex = -1; return SQLITE_OK; } /* ** Rtree virtual table module xNext method. */ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){ Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab); RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; int rc = SQLITE_OK; /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is ** already at EOF. It is against the rules to call the xNext() method of ** a cursor that has already reached EOF. */ assert( pCsr->pNode ); if( pCsr->iStrategy==1 ){ /* This "scan" is a direct lookup by rowid. There is no next entry. */ nodeRelease(pRtree, pCsr->pNode); pCsr->pNode = 0; }else{ /* Move to the next entry that matches the configured constraints. */ int iHeight = 0; while( pCsr->pNode ){ RtreeNode *pNode = pCsr->pNode; int nCell = NCELL(pNode); for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){ int isEof; rc = descendToCell(pRtree, pCsr, iHeight, &isEof); if( rc!=SQLITE_OK || !isEof ){ return rc; } } pCsr->pNode = pNode->pParent; rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell); if( rc!=SQLITE_OK ){ return rc; } nodeReference(pCsr->pNode); nodeRelease(pRtree, pNode); iHeight++; } } return rc; |
︙ | ︙ | |||
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 | sqlite3_reset(pRtree->pReadRowid); }else{ rc = sqlite3_reset(pRtree->pReadRowid); } return rc; } /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; rtreeReference(pRtree); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | > | > > > > > > > > > > > | > | 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 | sqlite3_reset(pRtree->pReadRowid); }else{ rc = sqlite3_reset(pRtree->pReadRowid); } return rc; } /* ** This function is called to configure the RtreeConstraint object passed ** as the second argument for a MATCH constraint. The value passed as the ** first argument to this function is the right-hand operand to the MATCH ** operator. */ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){ RtreeMatchArg *p; sqlite3_rtree_geometry *pGeom; int nBlob; /* Check that value is actually a blob. */ if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR; /* Check that the blob is roughly the right size. */ nBlob = sqlite3_value_bytes(pValue); if( nBlob<sizeof(RtreeMatchArg) || ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0 ){ return SQLITE_ERROR; } pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc( sizeof(sqlite3_rtree_geometry) + nBlob ); if( !pGeom ) return SQLITE_NOMEM; memset(pGeom, 0, sizeof(sqlite3_rtree_geometry)); p = (RtreeMatchArg *)&pGeom[1]; memcpy(p, sqlite3_value_blob(pValue), nBlob); if( p->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(double)) ){ sqlite3_free(pGeom); return SQLITE_ERROR; } pGeom->pContext = p->pContext; pGeom->nParam = p->nParam; pGeom->aParam = p->aParam; pCons->xGeom = p->xGeom; pCons->pGeom = pGeom; return SQLITE_OK; } /* ** Rtree virtual table module xFilter method. */ static int rtreeFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ Rtree *pRtree = (Rtree *)pVtabCursor->pVtab; RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor; RtreeNode *pRoot = 0; int ii; int rc = SQLITE_OK; rtreeReference(pRtree); freeCursorConstraints(pCsr); pCsr->iStrategy = idxNum; if( idxNum==1 ){ /* Special case - lookup by rowid. */ RtreeNode *pLeaf; /* Leaf on which the required cell resides */ i64 iRowid = sqlite3_value_int64(argv[0]); rc = findLeafNode(pRtree, iRowid, &pLeaf); pCsr->pNode = pLeaf; if( pLeaf ){ assert( rc==SQLITE_OK ); rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell); } }else{ /* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array ** with the configured constraints. */ if( argc>0 ){ pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; }else{ memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc); assert( (idxStr==0 && argc==0) || strlen(idxStr)==argc*2 ); for(ii=0; ii<argc; ii++){ RtreeConstraint *p = &pCsr->aConstraint[ii]; p->op = idxStr[ii*2]; p->iCoord = idxStr[ii*2+1]-'a'; if( p->op==RTREE_MATCH ){ /* A MATCH operator. The right-hand-side must be a blob that ** can be cast into an RtreeMatchArg object. One created using ** an sqlite3_rtree_geometry_callback() SQL user function. */ rc = deserializeGeometry(argv[ii], p); if( rc!=SQLITE_OK ){ break; } }else{ p->rValue = sqlite3_value_double(argv[ii]); } } } } if( rc==SQLITE_OK ){ pCsr->pNode = 0; rc = nodeAcquire(pRtree, 1, 0, &pRoot); |
︙ | ︙ | |||
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 | ** Operator Byte Value ** ---------------------- ** = 0x41 ('A') ** <= 0x42 ('B') ** < 0x43 ('C') ** >= 0x44 ('D') ** > 0x45 ('E') ** ---------------------- ** ** The second of each pair of bytes identifies the coordinate column ** to which the constraint applies. The leftmost coordinate column ** is 'a', the second from the left 'b' etc. */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ | > | 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 | ** Operator Byte Value ** ---------------------- ** = 0x41 ('A') ** <= 0x42 ('B') ** < 0x43 ('C') ** >= 0x44 ('D') ** > 0x45 ('E') ** MATCH 0x46 ('F') ** ---------------------- ** ** The second of each pair of bytes identifies the coordinate column ** to which the constraint applies. The leftmost coordinate column ** is 'a', the second from the left 'b' etc. */ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
︙ | ︙ | |||
1129 1130 1131 1132 1133 1134 1135 | ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). */ pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } | | > > > > > > | > | | | | | | | | | < < | | | | | | | | | | | < | 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 | ** considered almost as quick as a direct rowid lookup (for which ** sqlite uses an internal cost of 0.0). */ pIdxInfo->estimatedCost = 10.0; return SQLITE_OK; } if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){ int j, opmsk; static const unsigned char compatible[] = { 0, 0, 1, 1, 2, 2 }; u8 op = 0; switch( p->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; default: assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); op = RTREE_MATCH; break; } assert( op!=0 ); /* Make sure this particular constraint has not been used before. ** If it has been used before, ignore it. ** ** A <= or < can be used if there is a prior >= or >. ** A >= or > can be used if there is a prior < or <=. ** A <= or < is disqualified if there is a prior <=, <, or ==. ** A >= or > is disqualified if there is a prior >=, >, or ==. ** A == is disqualifed if there is any prior constraint. */ assert( compatible[RTREE_EQ & 7]==0 ); assert( compatible[RTREE_LT & 7]==1 ); assert( compatible[RTREE_LE & 7]==1 ); assert( compatible[RTREE_GT & 7]==2 ); assert( compatible[RTREE_GE & 7]==2 ); cCol = p->iColumn - 1 + 'a'; opmsk = compatible[op & 7]; for(j=0; j<iIdx; j+=2){ if( zIdxStr[j+1]==cCol && (compatible[zIdxStr[j] & 7] & opmsk)!=0 ){ op = 0; break; } } if( op ){ assert( iIdx<sizeof(zIdxStr)-1 ); zIdxStr[iIdx++] = op; zIdxStr[iIdx++] = cCol; pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); |
︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 | RtreeCell *aCell, int nCell, int iExclude ){ int ii; float overlap = 0.0; for(ii=0; ii<nCell; ii++){ | > | > > > > | 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 | RtreeCell *aCell, int nCell, int iExclude ){ int ii; float overlap = 0.0; for(ii=0; ii<nCell; ii++){ #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii!=iExclude ) #else assert( iExclude==-1 ); #endif { int jj; float o = 1.0; for(jj=0; jj<(pRtree->nDim*2); jj+=2){ double x1; double x2; x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj])); |
︙ | ︙ | |||
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | #endif /* Select the child node which will be enlarged the least if pCell ** is inserted into it. Resolve ties by choosing the entry with ** the smallest area. */ for(iCell=0; iCell<nCell; iCell++){ float growth; float area; float overlap = 0.0; nodeGetCell(pRtree, pNode, iCell, &cell); growth = cellGrowth(pRtree, &cell, pCell); area = cellArea(pRtree, &cell); #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii==(pRtree->iDepth-1) ){ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); } | > > < > > > > > > > > | 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 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 | #endif /* Select the child node which will be enlarged the least if pCell ** is inserted into it. Resolve ties by choosing the entry with ** the smallest area. */ for(iCell=0; iCell<nCell; iCell++){ int bBest = 0; float growth; float area; float overlap = 0.0; nodeGetCell(pRtree, pNode, iCell, &cell); growth = cellGrowth(pRtree, &cell, pCell); area = cellArea(pRtree, &cell); #if VARIANT_RSTARTREE_CHOOSESUBTREE if( ii==(pRtree->iDepth-1) ){ overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell); } if( (iCell==0) || (overlap<fMinOverlap) || (overlap==fMinOverlap && growth<fMinGrowth) || (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea) ){ bBest = 1; } #else if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){ bBest = 1; } #endif if( bBest ){ fMinOverlap = overlap; fMinGrowth = growth; fMinArea = area; iBest = cell.iRowid; } } |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 | } /* ** A cell with the same content as pCell has just been inserted into ** the node pNode. This function updates the bounding box cells in ** all ancestor elements. */ | | > > | | > > > | 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 | } /* ** A cell with the same content as pCell has just been inserted into ** the node pNode. This function updates the bounding box cells in ** all ancestor elements. */ static int AdjustTree( Rtree *pRtree, /* Rtree table */ RtreeNode *pNode, /* Adjust ancestry of this node. */ RtreeCell *pCell /* This cell was just inserted */ ){ RtreeNode *p = pNode; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; if( nodeParentIndex(pRtree, p, &iCell) ){ return SQLITE_CORRUPT; } nodeGetCell(pRtree, pParent, iCell, &cell); if( !cellContains(pRtree, &cell, pCell) ){ cellUnion(pRtree, &cell, pCell); nodeOverwriteCell(pRtree, pParent, &cell, iCell); } p = pParent; } return SQLITE_OK; } /* ** Write mapping (iRowid->iNode) to the <rtree>_rowid table. */ static int rowidWrite(Rtree *pRtree, sqlite3_int64 iRowid, sqlite3_int64 iNode){ sqlite3_bind_int64(pRtree->pWriteRowid, 1, iRowid); |
︙ | ︙ | |||
1947 1948 1949 1950 1951 1952 1953 | nodeGetCell(pRtree, pNode, i, &aCell[i]); } nodeZero(pRtree, pNode); memcpy(&aCell[nCell], pCell, sizeof(RtreeCell)); nCell++; if( pNode->iNode==1 ){ | | | | | > > > > | > | > | | > > > > | 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 | nodeGetCell(pRtree, pNode, i, &aCell[i]); } nodeZero(pRtree, pNode); memcpy(&aCell[nCell], pCell, sizeof(RtreeCell)); nCell++; if( pNode->iNode==1 ){ pRight = nodeNew(pRtree, pNode); pLeft = nodeNew(pRtree, pNode); pRtree->iDepth++; pNode->isDirty = 1; writeInt16(pNode->zData, pRtree->iDepth); }else{ pLeft = pNode; pRight = nodeNew(pRtree, pLeft->pParent); nodeReference(pLeft); } if( !pLeft || !pRight ){ rc = SQLITE_NOMEM; goto splitnode_out; } memset(pLeft->zData, 0, pRtree->iNodeSize); memset(pRight->zData, 0, pRtree->iNodeSize); rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox); if( rc!=SQLITE_OK ){ goto splitnode_out; } /* Ensure both child nodes have node numbers assigned to them by calling ** nodeWrite(). Node pRight always needs a node number, as it was created ** by nodeNew() above. But node pLeft sometimes already has a node number. ** In this case avoid the all to nodeWrite(). */ if( SQLITE_OK!=(rc = nodeWrite(pRtree, pRight)) || (0==pLeft->iNode && SQLITE_OK!=(rc = nodeWrite(pRtree, pLeft))) ){ goto splitnode_out; } rightbbox.iRowid = pRight->iNode; leftbbox.iRowid = pLeft->iNode; if( pNode->iNode==1 ){ rc = rtreeInsertCell(pRtree, pLeft->pParent, &leftbbox, iHeight+1); if( rc!=SQLITE_OK ){ goto splitnode_out; } }else{ RtreeNode *pParent = pLeft->pParent; int iCell; rc = nodeParentIndex(pRtree, pLeft, &iCell); if( rc==SQLITE_OK ){ nodeOverwriteCell(pRtree, pParent, &leftbbox, iCell); rc = AdjustTree(pRtree, pParent, &leftbbox); } if( rc!=SQLITE_OK ){ goto splitnode_out; } } if( (rc = rtreeInsertCell(pRtree, pRight->pParent, &rightbbox, iHeight+1)) ){ goto splitnode_out; } for(i=0; i<NCELL(pRight); i++){ i64 iRowid = nodeGetRowid(pRtree, pRight, i); |
︙ | ︙ | |||
2034 2035 2036 2037 2038 2039 2040 2041 2042 | splitnode_out: nodeRelease(pRtree, pRight); nodeRelease(pRtree, pLeft); sqlite3_free(aCell); return rc; } static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ int rc = SQLITE_OK; | > > > > > > > > > > > > | > | | > > > > > > > > > | > > | < < | > | | > | < > | > | | | > | | > > > | 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 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 | splitnode_out: nodeRelease(pRtree, pRight); nodeRelease(pRtree, pLeft); sqlite3_free(aCell); return rc; } /* ** If node pLeaf is not the root of the r-tree and its pParent pointer is ** still NULL, load all ancestor nodes of pLeaf into memory and populate ** the pLeaf->pParent chain all the way up to the root node. ** ** This operation is required when a row is deleted (or updated - an update ** is implemented as a delete followed by an insert). SQLite provides the ** rowid of the row to delete, which can be used to find the leaf on which ** the entry resides (argument pLeaf). Once the leaf is located, this ** function is called to determine its ancestry. */ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ int rc = SQLITE_OK; RtreeNode *pChild = pLeaf; while( rc==SQLITE_OK && pChild->iNode!=1 && pChild->pParent==0 ){ int rc2 = SQLITE_OK; /* sqlite3_reset() return code */ sqlite3_bind_int64(pRtree->pReadParent, 1, pChild->iNode); rc = sqlite3_step(pRtree->pReadParent); if( rc==SQLITE_ROW ){ RtreeNode *pTest; /* Used to test for reference loops */ i64 iNode; /* Node number of parent node */ /* Before setting pChild->pParent, test that we are not creating a ** loop of references (as we would if, say, pChild==pParent). We don't ** want to do this as it leads to a memory leak when trying to delete ** the referenced counted node structures. */ iNode = sqlite3_column_int64(pRtree->pReadParent, 0); for(pTest=pLeaf; pTest && pTest->iNode!=iNode; pTest=pTest->pParent); if( !pTest ){ rc2 = nodeAcquire(pRtree, iNode, 0, &pChild->pParent); } } rc = sqlite3_reset(pRtree->pReadParent); if( rc==SQLITE_OK ) rc = rc2; if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT; pChild = pChild->pParent; } return rc; } static int deleteCell(Rtree *, RtreeNode *, int, int); static int removeNode(Rtree *pRtree, RtreeNode *pNode, int iHeight){ int rc; int rc2; RtreeNode *pParent; int iCell; assert( pNode->nRef==1 ); /* Remove the entry in the parent cell. */ rc = nodeParentIndex(pRtree, pNode, &iCell); if( rc==SQLITE_OK ){ pParent = pNode->pParent; pNode->pParent = 0; rc = deleteCell(pRtree, pParent, iCell, iHeight+1); } rc2 = nodeRelease(pRtree, pParent); if( rc==SQLITE_OK ){ rc = rc2; } if( rc!=SQLITE_OK ){ return rc; } /* Remove the xxx_node entry. */ sqlite3_bind_int64(pRtree->pDeleteNode, 1, pNode->iNode); sqlite3_step(pRtree->pDeleteNode); if( SQLITE_OK!=(rc = sqlite3_reset(pRtree->pDeleteNode)) ){ |
︙ | ︙ | |||
2097 2098 2099 2100 2101 2102 2103 | pNode->pNext = pRtree->pDeleted; pNode->nRef++; pRtree->pDeleted = pNode; return SQLITE_OK; } | | > | > | | | > > > < | > | | < | | 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 | pNode->pNext = pRtree->pDeleted; pNode->nRef++; pRtree->pDeleted = pNode; return SQLITE_OK; } static int fixBoundingBox(Rtree *pRtree, RtreeNode *pNode){ RtreeNode *pParent = pNode->pParent; int rc = SQLITE_OK; if( pParent ){ int ii; int nCell = NCELL(pNode); RtreeCell box; /* Bounding box for pNode */ nodeGetCell(pRtree, pNode, 0, &box); for(ii=1; ii<nCell; ii++){ RtreeCell cell; nodeGetCell(pRtree, pNode, ii, &cell); cellUnion(pRtree, &box, &cell); } box.iRowid = pNode->iNode; rc = nodeParentIndex(pRtree, pNode, &ii); if( rc==SQLITE_OK ){ nodeOverwriteCell(pRtree, pParent, &box, ii); rc = fixBoundingBox(pRtree, pParent); } } return rc; } /* ** Delete the cell at index iCell of node pNode. After removing the ** cell, adjust the r-tree data structure if required. */ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){ RtreeNode *pParent; int rc; if( SQLITE_OK!=(rc = fixLeafParent(pRtree, pNode)) ){ return rc; } /* Remove the cell from the node. This call just moves bytes around ** the in-memory node image, so it cannot fail. */ nodeDeleteCell(pRtree, pNode, iCell); /* If the node is not the tree root and now has less than the minimum ** number of cells, remove it from the tree. Otherwise, update the ** cell in the parent node so that it tightly contains the updated ** node. */ pParent = pNode->pParent; assert( pParent || pNode->iNode==1 ); if( pParent ){ if( NCELL(pNode)<RTREE_MINCELLS(pRtree) ){ rc = removeNode(pRtree, pNode, iHeight); }else{ rc = fixBoundingBox(pRtree, pNode); } } return rc; } static int Reinsert( |
︙ | ︙ | |||
2227 2228 2229 2230 2231 2232 2233 | rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, p->iRowid, pNode->iNode); } } } if( rc==SQLITE_OK ){ | | | 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 | rc = rowidWrite(pRtree, p->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, p->iRowid, pNode->iNode); } } } if( rc==SQLITE_OK ){ rc = fixBoundingBox(pRtree, pNode); } for(; rc==SQLITE_OK && ii<nCell; ii++){ /* Find a node to store this cell in. pNode->iNode currently contains ** the height of the sub-tree headed by the cell. */ RtreeNode *pInsert; RtreeCell *p = &aCell[aOrder[ii]]; |
︙ | ︙ | |||
2281 2282 2283 2284 2285 2286 2287 | pRtree->iReinsertHeight = iHeight; rc = Reinsert(pRtree, pNode, pCell, iHeight); } #else rc = SplitNode(pRtree, pNode, pCell, iHeight); #endif }else{ | | > | | | | > | 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 | pRtree->iReinsertHeight = iHeight; rc = Reinsert(pRtree, pNode, pCell, iHeight); } #else rc = SplitNode(pRtree, pNode, pCell, iHeight); #endif }else{ rc = AdjustTree(pRtree, pNode, pCell); if( rc==SQLITE_OK ){ if( iHeight==0 ){ rc = rowidWrite(pRtree, pCell->iRowid, pNode->iNode); }else{ rc = parentWrite(pRtree, pCell->iRowid, pNode->iNode); } } } return rc; } static int reinsertNodeContent(Rtree *pRtree, RtreeNode *pNode){ int ii; |
︙ | ︙ | |||
2330 2331 2332 2333 2334 2335 2336 | sqlite3_bind_null(pRtree->pWriteRowid, 2); sqlite3_step(pRtree->pWriteRowid); rc = sqlite3_reset(pRtree->pWriteRowid); *piRowid = sqlite3_last_insert_rowid(pRtree->db); return rc; } | < < < < < < < < < < < | 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 | sqlite3_bind_null(pRtree->pWriteRowid, 2); sqlite3_step(pRtree->pWriteRowid); rc = sqlite3_reset(pRtree->pWriteRowid); *piRowid = sqlite3_last_insert_rowid(pRtree->db); return rc; } /* ** The xUpdate method for rtree module virtual tables. */ static int rtreeUpdate( sqlite3_vtab *pVtab, int nData, sqlite3_value **azData, sqlite_int64 *pRowid ){ Rtree *pRtree = (Rtree *)pVtab; int rc = SQLITE_OK; rtreeReference(pRtree); assert(nData>=1); /* If azData[0] is not an SQL NULL value, it is the rowid of a ** record to delete from the r-tree table. The following block does ** just that. */ if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){ i64 iDelete; /* The rowid to delete */ |
︙ | ︙ | |||
2381 2382 2383 2384 2385 2386 2387 | iDelete = sqlite3_value_int64(azData[0]); rc = findLeafNode(pRtree, iDelete, &pLeaf); } /* Delete the cell in question from the leaf node. */ if( rc==SQLITE_OK ){ int rc2; | | > | > | 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 | iDelete = sqlite3_value_int64(azData[0]); rc = findLeafNode(pRtree, iDelete, &pLeaf); } /* Delete the cell in question from the leaf node. */ if( rc==SQLITE_OK ){ int rc2; rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); if( rc==SQLITE_OK ){ rc = deleteCell(pRtree, pLeaf, iCell, 0); } rc2 = nodeRelease(pRtree, pLeaf); if( rc==SQLITE_OK ){ rc = rc2; } } /* Delete the corresponding entry in the <rtree>_rowid table. */ |
︙ | ︙ | |||
2404 2405 2406 2407 2408 2409 2410 | ** it, schedule the contents of the child for reinsertion and ** reduce the tree height by one. ** ** This is equivalent to copying the contents of the child into ** the root node (the operation that Gutman's paper says to perform ** in this scenario). */ | | | | | | | | | > > | | | | < | 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 | ** it, schedule the contents of the child for reinsertion and ** reduce the tree height by one. ** ** This is equivalent to copying the contents of the child into ** the root node (the operation that Gutman's paper says to perform ** in this scenario). */ if( rc==SQLITE_OK && pRtree->iDepth>0 && NCELL(pRoot)==1 ){ int rc2; RtreeNode *pChild; i64 iChild = nodeGetRowid(pRtree, pRoot, 0); rc = nodeAcquire(pRtree, iChild, pRoot, &pChild); if( rc==SQLITE_OK ){ rc = removeNode(pRtree, pChild, pRtree->iDepth-1); } rc2 = nodeRelease(pRtree, pChild); if( rc==SQLITE_OK ) rc = rc2; if( rc==SQLITE_OK ){ pRtree->iDepth--; writeInt16(pRoot->zData, pRtree->iDepth); pRoot->isDirty = 1; } } /* Re-insert the contents of any underfull nodes removed from the tree. */ for(pLeaf=pRtree->pDeleted; pLeaf; pLeaf=pRtree->pDeleted){ if( rc==SQLITE_OK ){ rc = reinsertNodeContent(pRtree, pLeaf); |
︙ | ︙ | |||
2706 2707 2708 2709 2710 2711 2712 | char **pzErr, /* OUT: Error message, if any */ int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ | | | 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 | char **pzErr, /* OUT: Error message, if any */ int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; int nDb; /* Length of string argv[1] */ int nName; /* Length of string argv[2] */ int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32); const char *aErrMsg[] = { 0, /* 0 */ "Wrong number of columns for an rtree table", /* 1 */ "Too few columns for an rtree table", /* 2 */ "Too many columns for an rtree table" /* 3 */ }; |
︙ | ︙ | |||
2852 2853 2854 2855 2856 2857 2858 | /* ** Register the r-tree module with database handle db. This creates the ** virtual table module "rtree" and the debugging/analysis scalar ** function "rtreenode". */ int sqlite3RtreeInit(sqlite3 *db){ | > | < < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 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 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 | /* ** Register the r-tree module with database handle db. This creates the ** virtual table module "rtree" and the debugging/analysis scalar ** function "rtreenode". */ int sqlite3RtreeInit(sqlite3 *db){ const int utf8 = SQLITE_UTF8; int rc; rc = sqlite3_create_function(db, "rtreenode", 2, utf8, 0, rtreenode, 0, 0); if( rc==SQLITE_OK ){ int utf8 = SQLITE_UTF8; rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0); } if( rc==SQLITE_OK ){ void *c = (void *)RTREE_COORD_REAL32; rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0); } if( rc==SQLITE_OK ){ void *c = (void *)RTREE_COORD_INT32; rc = sqlite3_create_module_v2(db, "rtree_i32", &rtreeModule, c, 0); } return rc; } /* ** A version of sqlite3_free() that can be used as a callback. This is used ** in two places - as the destructor for the blob value returned by the ** invocation of a geometry function, and as the destructor for the geometry ** functions themselves. */ static void doSqlite3Free(void *p){ sqlite3_free(p); } /* ** Each call to sqlite3_rtree_geometry_callback() creates an ordinary SQLite ** scalar user function. This C function is the callback used for all such ** registered SQL functions. ** ** The scalar user functions return a blob that is interpreted by r-tree ** table MATCH operators. */ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); RtreeMatchArg *pBlob; int nBlob; nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(double); pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ int i; pBlob->magic = RTREE_GEOMETRY_MAGIC; pBlob->xGeom = pGeomCtx->xGeom; pBlob->pContext = pGeomCtx->pContext; pBlob->nParam = nArg; for(i=0; i<nArg; i++){ pBlob->aParam[i] = sqlite3_value_double(aArg[i]); } sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free); } } /* ** Register a new geometry function for use with the r-tree MATCH operator. */ int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *), void *pContext ){ RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */ /* Allocate and populate the context object. */ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback)); if( !pGeomCtx ) return SQLITE_NOMEM; pGeomCtx->xGeom = xGeom; pGeomCtx->pContext = pContext; /* Create the new user-function. Register a destructor function to delete ** the context object when it is no longer required. */ return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY, (void *)pGeomCtx, geomCallback, 0, 0, doSqlite3Free ); } #if !SQLITE_CORE int sqlite3_extension_init( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ SQLITE_EXTENSION_INIT2(pApi) return sqlite3RtreeInit(db); } #endif #endif |
Changes to ext/rtree/rtree1.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # if {![info exists testdir]} { | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl # Test plan: # # rtree-1.*: Creating/destroying r-tree tables. |
︙ | ︙ |
Changes to ext/rtree/rtree2.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # if {![info exists testdir]} { | | | | 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 | # #*********************************************************************** # # The focus of this file is testing the r-tree extension. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl ifcapable !rtree { finish_test return } set ::NROW 1000 set ::NDEL 10 set ::NSELECT 100 if {[info exists G(isquick)] && $G(isquick)} { set ::NROW 100 set ::NSELECT 10 } foreach module {rtree_i32 rtree} { for {set nDim 1} {$nDim <= 5} {incr nDim} { |
︙ | ︙ |
Changes to ext/rtree/rtree3.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #*********************************************************************** # # The focus of this file is testing that the r-tree correctly handles # out-of-memory conditions. # if {![info exists testdir]} { | | | | > | < < > | < > > > > > > > > > > > > > > > > > > | | > | > | | | | | | | | | > > | > > | | > > > > > | | | < | > > | | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #*********************************************************************** # # The focus of this file is testing that the r-tree correctly handles # out-of-memory conditions. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl source $testdir/malloc_common.tcl ifcapable !rtree { finish_test return } # Test summary: # # rtree3-1: Test OOM in simple CREATE TABLE, INSERT, DELETE and SELECT # commands on an almost empty table. # # rtree3-2: Test OOM in a DROP TABLE command. # # rtree3-3a: Test OOM during a transaction to insert 100 pseudo-random rows. # # rtree3-3b: Test OOM during a transaction deleting all entries in the # database constructed in [rtree3-3a] in pseudo-random order. # # rtree3-4a: OOM during "SELECT count(*) FROM ..." on a big table. # # rtree3-4b: OOM while deleting rows from a big table. # # rtree3-5: Test OOM while inserting rows into a big table. # # rtree3-6: Test OOM while deleting all rows of a table, one at a time. # # rtree3-7: OOM during an ALTER TABLE RENAME TABLE command. # # rtree3-8: Test OOM while registering the r-tree module with sqlite. # do_faultsim_test rtree3-1 -faults oom* -prep { faultsim_delete_and_reopen } -body { execsql { BEGIN TRANSACTION; CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); INSERT INTO rt VALUES(NULL, 3, 5, 7, 9); INSERT INTO rt VALUES(NULL, 13, 15, 17, 19); DELETE FROM rt WHERE ii = 1; SELECT * FROM rt; SELECT ii FROM rt WHERE ii = 2; COMMIT; } } do_test rtree3-2.prep { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); INSERT INTO rt VALUES(NULL, 3, 5, 7, 9); } faultsim_save_and_close } {} do_faultsim_test rtree3-2 -faults oom* -prep { faultsim_restore_and_reopen } -body { execsql { DROP TABLE rt } } do_malloc_test rtree3-3.prep { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); INSERT INTO rt VALUES(NULL, 3, 5, 7, 9); } faultsim_save_and_close } {} do_faultsim_test rtree3-3a -faults oom* -prep { faultsim_restore_and_reopen } -body { db eval BEGIN for {set ii 0} {$ii < 100} {incr ii} { set f [expr rand()] db eval {INSERT INTO rt VALUES(NULL, $f*10.0, $f*10.0, $f*15.0, $f*15.0)} } db eval COMMIT } faultsim_save_and_close do_faultsim_test rtree3-3b -faults oom* -prep { faultsim_restore_and_reopen } -body { db eval BEGIN for {set ii 0} {$ii < 100} {incr ii} { set f [expr rand()] db eval { DELETE FROM rt WHERE x1<($f*10.0) AND x1>($f*10.5) } } db eval COMMIT } do_test rtree3-4.prep { faultsim_delete_and_reopen execsql { BEGIN; PRAGMA page_size = 512; CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); } for {set i 0} {$i < 1500} {incr i} { execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) } } execsql { COMMIT } faultsim_save_and_close } {} do_faultsim_test rtree3-4a -faults oom-* -prep { faultsim_restore_and_reopen } -body { db eval { SELECT count(*) FROM rt } } -test { faultsim_test_result {0 1500} } do_faultsim_test rtree3-4b -faults oom-transient -prep { faultsim_restore_and_reopen } -body { db eval { DELETE FROM rt WHERE ii BETWEEN 1 AND 100 } } -test { faultsim_test_result {0 {}} } do_test rtree3-5.prep { faultsim_delete_and_reopen execsql { BEGIN; PRAGMA page_size = 512; CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); } for {set i 0} {$i < 100} {incr i} { execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) } } execsql { COMMIT } faultsim_save_and_close } {} do_faultsim_test rtree3-5 -faults oom-* -prep { faultsim_restore_and_reopen } -body { for {set i 100} {$i < 110} {incr i} { execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) } } } -test { faultsim_test_result {0 {}} } do_test rtree3-6.prep { faultsim_delete_and_reopen execsql { BEGIN; PRAGMA page_size = 512; CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2); } for {set i 0} {$i < 50} {incr i} { execsql { INSERT INTO rt VALUES($i, $i, $i+1, $i, $i+1) } } execsql { COMMIT } faultsim_save_and_close } {} do_faultsim_test rtree3-6 -faults oom-* -prep { faultsim_restore_and_reopen } -body { execsql BEGIN for {set i 0} {$i < 50} {incr i} { execsql { DELETE FROM rt WHERE ii=$i } } execsql COMMIT } -test { faultsim_test_result {0 {}} } do_test rtree3-7.prep { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2) } faultsim_save_and_close } {} do_faultsim_test rtree3-7 -faults oom-* -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE rt RENAME TO rt2 } } -test { faultsim_test_result {0 {}} } do_faultsim_test rtree3-8 -faults oom-* -prep { catch { db close } } -body { sqlite3 db test.db } do_faultsim_test rtree3-9 -faults oom-* -prep { sqlite3 db :memory: } -body { set rc [register_cube_geom db] if {$rc != "SQLITE_OK"} { error $rc } } -test { faultsim_test_result {0 {}} {1 SQLITE_NOMEM} } do_test rtree3-10.prep { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, z1, z2); INSERT INTO rt VALUES(1, 10, 10, 10, 11, 11, 11); INSERT INTO rt VALUES(2, 5, 6, 6, 7, 7, 8); } faultsim_save_and_close } {} do_faultsim_test rtree3-10 -faults oom-* -prep { faultsim_restore_and_reopen register_cube_geom db execsql { SELECT * FROM rt } } -body { execsql { SELECT ii FROM rt WHERE ii MATCH cube(4.5, 5.5, 6.5, 1, 1, 1) } } -test { faultsim_test_result {0 2} } finish_test |
Changes to ext/rtree/rtree4.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # Randomized test cases for the rtree extension. # if {![info exists testdir]} { | | | | 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 | # #*********************************************************************** # # Randomized test cases for the rtree extension. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree { finish_test return } set ::NROW 2500 if {[info exists G(isquick)] && $G(isquick)} { set ::NROW 250 } # Return a floating point number between -X and X. # proc rand {X} { return [expr {int((rand()-0.5)*1024.0*$X)/512.0}] |
︙ | ︙ | |||
89 90 91 92 93 94 95 | # contained-within queries after each insert to verify that all # is well. # unset -nocomplain where for {set i 1} {$i<$::NROW} {incr i} { # Do a random insert # | | | | | | | | | | | 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 | # contained-within queries after each insert to verify that all # is well. # unset -nocomplain where for {set i 1} {$i<$::NROW} {incr i} { # Do a random insert # do_test rtree4-$nDim.2.$i.1 { set vlist {} for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 50]}] lappend vlist $mn $mx } db eval "INSERT INTO rx VALUES(NULL, [join $vlist ,])" db eval "INSERT INTO bx VALUES(NULL, [join $vlist ,])" } {} # Do a contained-in query on all dimensions # set where {} for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mn$j>=$mn mx$j<=$mx } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.2 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do an overlaps query on all dimensions # set where {} for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mx$j>=$mn mn$j<=$mx } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.3 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do a contained-in query with surplus contraints at the beginning. # This should force a full-table scan on the rtree. # set where {} for {set j 0} {$j<$nDim} {incr j} { lappend where mn$j>-10000 mx$j<10000 } for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mn$j>=$mn mx$j<=$mx } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.3 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do an overlaps query with surplus contraints at the beginning. # This should force a full-table scan on the rtree. # set where {} for {set j 0} {$j<$nDim} {incr j} { lappend where mn$j>=-10000 mx$j<=10000 } for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mx$j>$mn mn$j<$mx } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.4 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do a contained-in query with surplus contraints at the end # set where {} for {set j 0} {$j<$nDim} {incr j} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mn$j>=$mn mx$j<$mx } for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} { lappend where mn$j>=-10000 mx$j<10000 } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.5 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do an overlaps query with surplus contraints at the end # set where {} for {set j [expr {$nDim-1}]} {$j>=0} {incr j -1} { set mn [rand 10000] set mx [expr {$mn+[randincr 500]}] lappend where mx$j>$mn mn$j<=$mx } for {set j 0} {$j<$nDim} {incr j} { lappend where mx$j>-10000 mn$j<=10000 } set where "WHERE [join $where { AND }]" do_test rtree4-$nDim.2.$i.6 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do a contained-in query with surplus contraints where the # constraints appear in a random order. # set where {} for {set j 0} {$j<$nDim} {incr j} { set mn1 [rand 10000] set mn2 [expr {$mn1+[randincr 100]}] set mx1 [expr {$mn2+[randincr 400]}] set mx2 [expr {$mx1+[randincr 100]}] lappend where mn$j>=$mn1 mn$j>$mn2 mx$j<$mx1 mx$j<=$mx2 } set where "WHERE [join [scramble $where] { AND }]" do_test rtree4-$nDim.2.$i.7 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] # Do an overlaps query with surplus contraints where the # constraints appear in a random order. # set where {} for {set j 0} {$j<$nDim} {incr j} { set mn1 [rand 10000] set mn2 [expr {$mn1+[randincr 100]}] set mx1 [expr {$mn2+[randincr 400]}] set mx2 [expr {$mx1+[randincr 100]}] lappend where mx$j>=$mn1 mx$j>$mn2 mn$j<$mx1 mn$j<=$mx2 } set where "WHERE [join [scramble $where] { AND }]" do_test rtree4-$nDim.2.$i.8 { list $where [db eval "SELECT id FROM rx $where ORDER BY id"] } [list $where [db eval "SELECT id FROM bx $where ORDER BY id"]] } } finish_test |
Changes to ext/rtree/rtree5.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #*********************************************************************** # # The focus of this file is testing the r-tree extension when it is # configured to store values as 32 bit integers. # if {![info exists testdir]} { | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #*********************************************************************** # # The focus of this file is testing the r-tree extension when it is # configured to store values as 32 bit integers. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree { finish_test return } |
︙ | ︙ |
Changes to ext/rtree/rtree6.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # # if {![info exists testdir]} { | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 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 ifcapable !rtree { finish_test return } |
︙ | ︙ | |||
67 68 69 70 71 72 73 | rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10} } {Ca} do_test rtree6-1.5 { rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10} } {Ca} | | | | | | < | > | | | | | < | > | | | | | < | > | | | | | < | > | | | | | < > | 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 | rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10} } {Ca} do_test rtree6-1.5 { rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10} } {Ca} do_eqp_test rtree6.2.1 { SELECT * FROM t1,t2 WHERE k=+ii AND x1<10 } { 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_eqp_test rtree6.2.2 { SELECT * FROM t1,t2 WHERE k=ii AND x1<10 } { 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_eqp_test rtree6.2.3 { SELECT * FROM t1,t2 WHERE k=ii } { 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_eqp_test rtree6.2.4 { SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10 } { 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb (~0 rows)} 0 1 1 {SCAN TABLE t2 (~100000 rows)} } do_eqp_test rtree6.2.5 { SELECT * FROM t1,t2 WHERE k=ii AND x1<v } { 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)} 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } finish_test |
Changes to ext/rtree/rtree7.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # # Test that nothing goes wrong if an rtree table is created, then the # database page-size is modified. At one point (3.6.22), this was causing # malfunctions. # if {![info exists testdir]} { | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # Test that nothing goes wrong if an rtree table is created, then the # database page-size is modified. At one point (3.6.22), this was causing # malfunctions. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree||!vacuum { finish_test return } |
︙ | ︙ |
Added ext/rtree/rtree8.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 | # 2010 February 16 # # 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 ifcapable !rtree { finish_test ; return } #------------------------------------------------------------------------- # The following block of tests - rtree8-1.* - feature reading and writing # an r-tree table while there exist open cursors on it. # proc populate_t1 {n} { execsql { DELETE FROM t1 } for {set i 1} {$i <= $n} {incr i} { execsql { INSERT INTO t1 VALUES($i, $i, $i+2) } } } # A DELETE while a cursor is reading the table. # do_test rtree8-1.1.1 { execsql { PRAGMA page_size = 512 } execsql { CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2) } populate_t1 5 } {} do_test rtree8-1.1.2 { set res [list] db eval { SELECT * FROM t1 } { lappend res $x1 $x2 if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } } } set res } {1 3 2 4 3 5} do_test rtree8-1.1.3 { execsql { SELECT * FROM t1 } } {1 1 3 2 2 4 3 3 5} # Many SELECTs on the same small table. # proc nested_select {n} { set ::max $n db eval { SELECT * FROM t1 } { if {$id == $n} { nested_select [expr $n+1] } } return $::max } do_test rtree8-1.2.1 { populate_t1 50 } {} do_test rtree8-1.2.2 { nested_select 1 } {51} # This test runs many SELECT queries simultaneously against a large # table, causing a collision in the hash-table used to store r-tree # nodes internally. # populate_t1 1500 do_execsql_test rtree8-1.3.1 { SELECT max(nodeno) FROM t1_node } {164} do_test rtree8-1.3.2 { set rowids [execsql {SELECT min(rowid) FROM t1_rowid GROUP BY nodeno}] set stmt_list [list] foreach row $rowids { set stmt [sqlite3_prepare db "SELECT * FROM t1 WHERE id = $row" -1 tail] sqlite3_step $stmt lappend res_list [sqlite3_column_int $stmt 0] lappend stmt_list $stmt } } {} do_test rtree8-1.3.3 { set res_list } $rowids do_execsql_test rtree8-1.3.4 { SELECT count(*) FROM t1 } {1500} do_test rtree8-1.3.5 { foreach stmt $stmt_list { sqlite3_finalize $stmt } } {} #------------------------------------------------------------------------- # The following block of tests - rtree8-2.* - test a couple of database # corruption cases. In this case things are not corrupted at the b-tree # level, but the contents of the various tables used internally by an # r-tree table are inconsistent. # populate_t1 50 do_execsql_test rtree8-2.1.1 { SELECT max(nodeno) FROM t1_node } {5} do_execsql_test rtree8-2.1.2 { DELETE FROM t1_node } {} for {set i 1} {$i <= 50} {incr i} { do_catchsql_test rtree8-2.1.3.$i { SELECT * FROM t1 WHERE id = $i } {1 {database disk image is malformed}} } do_catchsql_test rtree8-2.1.4 { SELECT * FROM t1 } {1 {database disk image is malformed}} do_catchsql_test rtree8-2.1.5 { DELETE FROM t1 } {1 {database disk image is malformed}} do_execsql_test rtree8-2.1.6 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2); } {} populate_t1 50 do_execsql_test rtree8-2.2.1 { DELETE FROM t1_parent } {} do_catchsql_test rtree8-2.2.2 { DELETE FROM t1 WHERE id=25 } {1 {database disk image is malformed}} do_execsql_test rtree8-2.2.3 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING rtree_i32(id, x1, x2); } {} #------------------------------------------------------------------------- # Test that trying to use the MATCH operator with the r-tree module does # not confuse it. # populate_t1 10 do_catchsql_test rtree8-3.1 { SELECT * FROM t1 WHERE x1 MATCH '1234' } {1 {SQL logic error or missing database}} #------------------------------------------------------------------------- # Test a couple of invalid arguments to rtreedepth(). # do_catchsql_test rtree8-4.1 { SELECT rtreedepth('hello world') } {1 {Invalid argument to rtreedepth()}} do_catchsql_test rtree8-4.2 { SELECT rtreedepth(X'00') } {1 {Invalid argument to rtreedepth()}} #------------------------------------------------------------------------- # Delete half of a lopsided tree. # do_execsql_test rtree8-5.1 { CREATE VIRTUAL TABLE t2 USING rtree_i32(id, x1, x2) } {} do_test rtree8-5.2 { execsql BEGIN for {set i 0} {$i < 100} {incr i} { execsql { INSERT INTO t2 VALUES($i, 100, 101) } } for {set i 100} {$i < 200} {incr i} { execsql { INSERT INTO t2 VALUES($i, 1000, 1001) } } execsql COMMIT } {} do_test rtree8-5.3 { execsql BEGIN for {set i 0} {$i < 200} {incr i} { execsql { DELETE FROM t2 WHERE id = $i } } execsql COMMIT } {} finish_test |
Added ext/rtree/rtree9.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 | # 2010 August 28 # # 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 r-tree module. Specifically, it tests # that custom r-tree queries (geometry callbacks) work. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree { finish_test ; return } register_cube_geom db do_execsql_test rtree9-1.1 { CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, y1, y2, z1, z2); INSERT INTO rt VALUES(1, 1, 2, 1, 2, 1, 2); } {} do_execsql_test rtree9-1.2 { SELECT * FROM rt WHERE id MATCH cube(0, 0, 0, 2, 2, 2); } {1 1.0 2.0 1.0 2.0 1.0 2.0} do_execsql_test rtree9-1.3 { SELECT * FROM rt WHERE id MATCH cube(3, 3, 3, 2, 2, 2); } {} do_execsql_test rtree9-1.4 { DELETE FROM rt; } {} for {set i 0} {$i < 1000} {incr i} { set x [expr $i%10] set y [expr ($i/10)%10] set z [expr ($i/100)%10] execsql { INSERT INTO rt VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) } } do_execsql_test rtree9-2.1 { SELECT id FROM rt WHERE id MATCH cube(2.5, 2.5, 2.5, 1, 1, 1) ORDER BY id; } {222 223 232 233 322 323 332 333} do_execsql_test rtree9-2.2 { SELECT id FROM rt WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id; } {555 556 565 566 655 656 665 666} do_execsql_test rtree9-3.1 { CREATE VIRTUAL TABLE rt32 USING rtree_i32(id, x1, x2, y1, y2, z1, z2); } {} for {set i 0} {$i < 1000} {incr i} { set x [expr $i%10] set y [expr ($i/10)%10] set z [expr ($i/100)%10] execsql { INSERT INTO rt32 VALUES($i, $x, $x+1, $y, $y+1, $z, $z+1) } } do_execsql_test rtree9-3.2 { SELECT id FROM rt32 WHERE id MATCH cube(3, 3, 3, 1, 1, 1) ORDER BY id; } {222 223 224 232 233 234 242 243 244 322 323 324 332 333 334 342 343 344 422 423 424 432 433 434 442 443 444} do_execsql_test rtree9-3.3 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 5.5, 1, 1, 1) ORDER BY id; } {555 556 565 566 655 656 665 666} do_catchsql_test rtree9-4.1 { SELECT id FROM rt32 WHERE id MATCH cube(5.5, 5.5, 1, 1, 1) ORDER BY id; } {1 {SQL logic error or missing database}} for {set x 2} {$x<200} {incr x 2} { do_catchsql_test rtree9-4.2.[expr $x/2] { SELECT id FROM rt WHERE id MATCH randomblob($x) } {1 {SQL logic error or missing database}} } do_catchsql_test rtree9-4.3 { SELECT id FROM rt WHERE id MATCH CAST( (cube(5.5, 5.5, 5.5, 1, 1, 1) || X'1234567812345678') AS blob ) } {1 {SQL logic error or missing database}} #------------------------------------------------------------------------- # Test the example 2d "circle" geometry callback. # register_circle_geom db breakpoint do_execsql_test rtree9-5.1 { CREATE VIRTUAL TABLE rt2 USING rtree(id, xmin, xmax, ymin, ymax); INSERT INTO rt2 VALUES(1, 1, 2, 1, 2); INSERT INTO rt2 VALUES(2, 1, 2, -2, -1); INSERT INTO rt2 VALUES(3, -2, -1, -2, -1); INSERT INTO rt2 VALUES(4, -2, -1, 1, 2); INSERT INTO rt2 VALUES(5, 2, 3, 2, 3); INSERT INTO rt2 VALUES(6, 2, 3, -3, -2); INSERT INTO rt2 VALUES(7, -3, -2, -3, -2); INSERT INTO rt2 VALUES(8, -3, -2, 2, 3); INSERT INTO rt2 VALUES(9, 1.8, 3, 1.8, 3); INSERT INTO rt2 VALUES(10, 1.8, 3, -3, -1.8); INSERT INTO rt2 VALUES(11, -3, -1.8, -3, -1.8); INSERT INTO rt2 VALUES(12, -3, -1.8, 1.8, 3); INSERT INTO rt2 VALUES(13, -15, 15, 1.8, 2.2); INSERT INTO rt2 VALUES(14, -15, 15, -2.2, -1.8); INSERT INTO rt2 VALUES(15, 1.8, 2.2, -15, 15); INSERT INTO rt2 VALUES(16, -2.2, -1.8, -15, 15); INSERT INTO rt2 VALUES(17, -100, 100, -100, 100); } {} do_execsql_test rtree9-5.2 { SELECT id FROM rt2 WHERE id MATCH circle(0.0, 0.0, 2.0); } {1 2 3 4 13 14 15 16 17} do_execsql_test rtree9-5.3 { UPDATE rt2 SET xmin=xmin+5, ymin=ymin+5, xmax=xmax+5, ymax=ymax+5; SELECT id FROM rt2 WHERE id MATCH circle(5.0, 5.0, 2.0); } {1 2 3 4 13 14 15 16 17} finish_test |
Added ext/rtree/rtreeA.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 | # 2010 September 22 # # 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 r-tree module. Specifically, it tests # that corrupt or inconsistent databases do not cause crashes in the r-tree # module. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree { finish_test ; return } proc create_t1 {} { db close forcedelete test.db sqlite3 db test.db execsql { PRAGMA page_size = 1024; CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2); } } proc populate_t1 {} { execsql BEGIN for {set i 0} {$i < 500} {incr i} { set x2 [expr $i+5] set y2 [expr $i+5] execsql { INSERT INTO t1 VALUES($i, $i, $x2, $i, $y2) } } execsql COMMIT } proc truncate_node {nodeno nTrunc} { set blob [db one {SELECT data FROM t1_node WHERE nodeno=$nodeno}] if {$nTrunc<0} {set nTrunc "end-$nTrunc"} set blob [string range $blob 0 $nTrunc] db eval { UPDATE t1_node SET data = $blob WHERE nodeno=$nodeno } } proc set_tree_depth {tbl {newvalue ""}} { set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=1"] if {$newvalue == ""} { binary scan $blob Su oldvalue return $oldvalue } set blob [binary format Sua* $newvalue [string range $blob 2 end]] db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=1" return [set_tree_depth $tbl] } proc set_entry_count {tbl nodeno {newvalue ""}} { set blob [db one "SELECT data FROM ${tbl}_node WHERE nodeno=$nodeno"] if {$newvalue == ""} { binary scan [string range $blob 2 end] Su oldvalue return $oldvalue } set blob [binary format a*Sua* \ [string range $blob 0 1] $newvalue [string range $blob 4 end] ] db eval "UPDATE ${tbl}_node SET data = \$blob WHERE nodeno=$nodeno" return [set_entry_count $tbl $nodeno] } proc do_corruption_tests {prefix args} { set testarray [lindex $args end] set errormsg {database disk image is malformed} foreach {z value} [lrange $args 0 end-1] { set n [string length $z] if {$n>=2 && [string equal -length $n $z "-error"]} { set errormsg $value } } foreach {tn sql} $testarray { do_catchsql_test $prefix.$tn $sql [list 1 $errormsg] } } #------------------------------------------------------------------------- # Test the libraries response if the %_node table is completely empty # (i.e. the root node is missing), or has been removed from the database # entirely. # create_t1 populate_t1 do_execsql_test rtreeA-1.0 { DELETE FROM t1_node; } {} do_corruption_tests rtreeA-1.1 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } do_execsql_test rtreeA-1.2.0 { DROP TABLE t1_node } {} do_corruption_tests rtreeA-1.2 -error "SQL logic error or missing database" { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } #------------------------------------------------------------------------- # Test the libraries response if some of the entries in the %_node table # are the wrong size. # create_t1 populate_t1 do_test rtreeA-2.1.0 { set nodes [db eval {select nodeno FROM t1_node}] foreach {a b c} $nodes { truncate_node $c 200 } } {} do_corruption_tests rtreeA-2.1 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } create_t1 populate_t1 do_test rtreeA-2.2.0 { truncate_node 1 200 } {} do_corruption_tests rtreeA-2.2 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } #------------------------------------------------------------------------- # Set the "depth" of the tree stored on the root node incorrectly. Test # that this does not cause any problems. # create_t1 populate_t1 do_test rtreeA-3.1.0.1 { set_tree_depth t1 } {1} do_test rtreeA-3.1.0.2 { set_tree_depth t1 3 } {3} do_corruption_tests rtreeA-3.1 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } do_test rtreeA-3.2.0 { set_tree_depth t1 1000 } {1000} do_corruption_tests rtreeA-3.2 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } create_t1 populate_t1 do_test rtreeA-3.3.0 { execsql { DELETE FROM t1 WHERE rowid = 0 } set_tree_depth t1 65535 } {65535} do_corruption_tests rtreeA-3.3 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" } #------------------------------------------------------------------------- # Set the "number of entries" field on some nodes incorrectly. # create_t1 populate_t1 do_test rtreeA-4.1.0 { set_entry_count t1 1 4000 } {4000} do_corruption_tests rtreeA-4.1 { 1 "SELECT * FROM t1" 2 "SELECT * FROM t1 WHERE rowid=5" 3 "INSERT INTO t1 VALUES(1000, 1, 2, 3, 4)" 4 "SELECT * FROM t1 WHERE x1<10 AND x2>12" } #------------------------------------------------------------------------- # Remove entries from the %_parent table and check that this does not # cause a crash. # create_t1 populate_t1 do_execsql_test rtreeA-5.1.0 { DELETE FROM t1_parent } {} do_corruption_tests rtreeA-5.1 { 1 "DELETE FROM t1 WHERE rowid = 5" 2 "DELETE FROM t1" } #------------------------------------------------------------------------- # Add some bad entries to the %_parent table. # create_t1 populate_t1 do_execsql_test rtreeA-6.1.0 { UPDATE t1_parent set parentnode = parentnode+1 } {} do_corruption_tests rtreeA-6.1 { 1 "DELETE FROM t1 WHERE rowid = 5" 2 "UPDATE t1 SET x1=x1+1, x2=x2+1" } finish_test |
Added ext/rtree/sqlite3rtree.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 2010 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. ** ************************************************************************* */ #ifndef _SQLITE3RTREE_H_ #define _SQLITE3RTREE_H_ #include <sqlite3.h> #ifdef __cplusplus extern "C" { #endif typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; /* ** Register a geometry callback named zGeom that can be used as part of an ** R-Tree geometry query as follows: ** ** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zGeom(... params ...) */ int sqlite3_rtree_geometry_callback( sqlite3 *db, const char *zGeom, int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes), void *pContext ); /* ** A pointer to a structure of the following type is passed as the first ** argument to callbacks registered using rtree_geometry_callback(). */ struct sqlite3_rtree_geometry { void *pContext; /* Copy of pContext passed to s_r_g_c() */ int nParam; /* Size of array aParam[] */ double *aParam; /* Parameters passed to SQL geom function */ void *pUser; /* Callback implementation user data */ void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ }; #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* ifndef _SQLITE3RTREE_H_ */ |
Changes to ext/rtree/tkt3363.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # The focus of this file is testing that ticket #3363 is fixed. # if {![info exists testdir]} { | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # # The focus of this file is testing that ticket #3363 is fixed. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source [file join [file dirname [info script]] rtree_util.tcl] source $testdir/tester.tcl ifcapable !rtree { finish_test return |
︙ | ︙ |
install-sh became executable.
︙ | ︙ |
Changes to main.mk.
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c | > > > > | 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 | $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_hexio.c \ $(TOP)/src/test_init.c \ $(TOP)/src/test_intarray.c \ $(TOP)/src/test_journal.c \ $(TOP)/src/test_malloc.c \ $(TOP)/src/test_multiplex.c \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ $(TOP)/src/test_pcache.c \ $(TOP)/src/test_quota.c \ $(TOP)/src/test_rtree.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_superlock.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c |
︙ | ︙ | |||
367 368 369 370 371 372 373 | rm tsrc/sqlite.h.in tsrc/parse.y tclsh $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new mv vdbe.new tsrc/vdbe.c touch target_source sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl tclsh $(TOP)/tool/mksqlite3c.tcl | > | > | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | rm tsrc/sqlite.h.in tsrc/parse.y tclsh $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new mv vdbe.new tsrc/vdbe.c touch target_source sqlite3.c: target_source $(TOP)/tool/mksqlite3c.tcl tclsh $(TOP)/tool/mksqlite3c.tcl echo '#ifndef USE_SYSTEM_SQLITE' >tclsqlite3.c cat sqlite3.c >>tclsqlite3.c echo '#endif /* USE_SYSTEM_SQLITE */' >>tclsqlite3.c cat $(TOP)/src/tclsqlite.c >>tclsqlite3.c fts2amal.c: target_source $(TOP)/ext/fts2/mkfts2amal.tcl tclsh $(TOP)/ext/fts2/mkfts2amal.tcl fts3amal.c: target_source $(TOP)/ext/fts3/mkfts3amal.tcl tclsh $(TOP)/ext/fts3/mkfts3amal.tcl |
︙ | ︙ | |||
520 521 522 523 524 525 526 527 528 529 530 531 532 533 | ./testfixture$(EXE) $(TOP)/test/all.test soaktest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test -soak=1 test: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \ $(TOP)/tool/spaceanal.tcl sed \ -e '/^#/d' \ -e 's,\\,\\\\,g' \ -e 's,",\\",g' \ | > > > > > > > > > > > | 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 | ./testfixture$(EXE) $(TOP)/test/all.test soaktest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test -soak=1 test: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/veryquick.test # The next two rules are used to support the "threadtest" target. Building # threadtest runs a few thread-safety tests that are implemented in C. This # target is invoked by the releasetest.tcl script. # threadtest3$(EXE): sqlite3.c $(TOP)/test/threadtest3.c $(TCCX) -O2 sqlite3.c $(TOP)/test/threadtest3.c \ -o threadtest3$(EXE) $(THREADLIB) threadtest: threadtest3$(EXE) ./threadtest3$(EXE) sqlite3_analyzer$(EXE): $(TOP)/src/tclsqlite.c sqlite3.c $(TESTSRC) \ $(TOP)/tool/spaceanal.tcl sed \ -e '/^#/d' \ -e 's,\\,\\\\,g' \ -e 's,",\\",g' \ |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
309 310 311 312 313 314 315 316 317 318 319 320 321 322 | sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ zWhere = whereOrName(db, zWhere, pTrig->zName); } } } return zWhere; } /* ** Generate code to drop and reload the internal representation of table ** pTab from the database, including triggers and temporary triggers. ** Argument zName is the name of the table in the database schema at | > > > > > | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ zWhere = whereOrName(db, zWhere, pTrig->zName); } } } if( zWhere ){ char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere); sqlite3DbFree(pParse->db, zWhere); zWhere = zNew; } return zWhere; } /* ** Generate code to drop and reload the internal representation of table ** pTab from the database, including triggers and temporary triggers. ** Argument zName is the name of the table in the database schema at |
︙ | ︙ |
Changes to src/analyze.c.
︙ | ︙ | |||
109 110 111 112 113 114 115 | sqlite3 *db = pParse->db; /* Database handle */ Index *pIdx; /* An index to being analyzed */ int iIdxCur; /* Cursor open on index being analyzed */ Vdbe *v; /* The virtual machine being built up */ int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ | | > | > > > > > > > | > | < < < | 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 | sqlite3 *db = pParse->db; /* Database handle */ Index *pIdx; /* An index to being analyzed */ int iIdxCur; /* Cursor open on index being analyzed */ Vdbe *v; /* The virtual machine being built up */ int i; /* Loop counter */ int topOfLoop; /* The top of the loop */ int endOfLoop; /* The end of the loop */ int addr = 0; /* The address of an instruction */ int jZeroRows = 0; /* Jump from here if number of rows is zero */ int iDb; /* Index of database containing pTab */ int regTabname = iMem++; /* Register containing table name */ int regIdxname = iMem++; /* Register containing index name */ int regSampleno = iMem++; /* Register containing next sample number */ int regCol = iMem++; /* Content of a column analyzed table */ int regRec = iMem++; /* Register holding completed record */ int regTemp = iMem++; /* Temporary use register */ int regRowid = iMem++; /* Rowid for the inserted record */ #ifdef SQLITE_ENABLE_STAT2 int regTemp2 = iMem++; /* Temporary use register */ int regSamplerecno = iMem++; /* Index of next sample to record */ int regRecno = iMem++; /* Current sample index */ int regLast = iMem++; /* Index of last sample to record */ int regFirst = iMem++; /* Index of first sample to record */ #endif v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; } if( pTab->tnum==0 ){ /* Do not gather statistics on views or virtual tables */ return; } if( memcmp(pTab->zName, "sqlite_", 7)==0 ){ /* Do not gather statistics on system tables */ return; } assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ return; } #endif /* Establish a read-lock on the table at the shared-cache level. */ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); iIdxCur = pParse->nTab++; sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int nCol = pIdx->nColumn; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); if( iMem+1+(nCol*2)>pParse->nMem ){ pParse->nMem = iMem+1+(nCol*2); } /* Open a cursor to the index to be analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); /* Populate the register containing the index name. */ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); #ifdef SQLITE_ENABLE_STAT2 /* If this iteration of the loop is generating code to analyze the ** first index in the pTab->pIndex list, then register regLast has ** not been populated. In this case populate it now. */ |
︙ | ︙ | |||
298 299 300 301 302 303 304 | ** ** I = (K+D-1)/D ** ** If K==0 then no entry is made into the sqlite_stat1 table. ** If K>0 then it is always the case the D>0 so division by zero ** is never possible. */ | < > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | ** ** I = (K+D-1)/D ** ** If K==0 then no entry is made into the sqlite_stat1 table. ** If K>0 then it is always the case the D>0 so division by zero ** is never possible. */ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); if( jZeroRows==0 ){ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem); } for(i=0; i<nCol; i++){ sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno); sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp); sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1); sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp); sqlite3VdbeAddOp1(v, OP_ToInt, regTemp); sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno); } sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); } /* If the table has no indices, create a single sqlite_stat1 entry ** containing NULL as the index name and the row count as the content. */ if( pTab->pIndex==0 ){ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb); VdbeComment((v, "%s", pTab->zName)); sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno); sqlite3VdbeAddOp1(v, OP_Close, iIdxCur); }else{ assert( jZeroRows>0 ); addr = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, jZeroRows); } sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname); sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0); sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); if( pParse->nMem<regRec ) pParse->nMem = regRec; if( jZeroRows ){ sqlite3VdbeJumpHere(v, addr); } } /* ** Generate code that will cause the most recent index analysis to ** be loaded into internal hash tables where is can be used. */ static void loadAnalysis(Parse *pParse, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp1(v, OP_LoadAnalysis, iDb); } } |
︙ | ︙ | |||
449 450 451 452 453 454 455 | const char *zDatabase; }; /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** | | > | > > > > | | | | | > > > > > > | | > > | 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 | const char *zDatabase; }; /* ** This callback is invoked once for each index when reading the ** sqlite_stat1 table. ** ** argv[0] = name of the table ** argv[1] = name of the index (might be NULL) ** argv[2] = results of analysis - on integer for each column ** ** Entries for which argv[1]==NULL simply record the number of rows in ** the table. */ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ analysisInfo *pInfo = (analysisInfo*)pData; Index *pIndex; Table *pTable; int i, c, n; unsigned int v; const char *z; assert( argc==3 ); UNUSED_PARAMETER2(NotUsed, argc); if( argv==0 || argv[0]==0 || argv[2]==0 ){ return 0; } pTable = sqlite3FindTable(pInfo->db, argv[0], pInfo->zDatabase); if( pTable==0 ){ return 0; } if( argv[1] ){ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase); }else{ pIndex = 0; } n = pIndex ? pIndex->nColumn : 0; z = argv[2]; for(i=0; *z && i<=n; i++){ v = 0; while( (c=z[0])>='0' && c<='9' ){ v = v*10 + c - '0'; z++; } if( i==0 ) pTable->nRowEst = v; if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; } return 0; } /* |
︙ | ︙ | |||
551 552 553 554 555 556 557 | sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, | | | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ return SQLITE_ERROR; } /* Load new statistics out of the sqlite_stat1 table */ zSql = sqlite3MPrintf(db, "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); sqlite3DbFree(db, zSql); } |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
120 121 122 123 124 125 126 | aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ | | | < | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | aNew = &db->aDb[db->nDb]; memset(aNew, 0, sizeof(*aNew)); /* Open the database file. If the btree is successfully opened, use ** it to obtain the database schema. At this point the schema may ** or may not be initialised. */ rc = sqlite3BtreeOpen(zFile, db, &aNew->pBt, 0, db->openFlags | SQLITE_OPEN_MAIN_DB); db->nDb++; if( rc==SQLITE_CONSTRAINT ){ rc = SQLITE_ERROR; zErrDyn = sqlite3MPrintf(db, "database is already attached"); }else if( rc==SQLITE_OK ){ Pager *pPager; aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt); |
︙ | ︙ | |||
363 364 365 366 367 368 369 | 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ detachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_detach", /* zName */ | > | > | | 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 | 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ detachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_detach", /* zName */ 0, /* pHash */ 0 /* pDestructor */ }; codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname); } /* ** Called by the parser to compile an ATTACH statement. ** ** ATTACH p AS pDbname KEY pKey */ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){ static const FuncDef attach_func = { 3, /* nArg */ SQLITE_UTF8, /* iPrefEnc */ 0, /* flags */ 0, /* pUserData */ 0, /* pNext */ attachFunc, /* xFunc */ 0, /* xStep */ 0, /* xFinalize */ "sqlite_attach", /* zName */ 0, /* pHash */ 0 /* pDestructor */ }; codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey); } #endif /* SQLITE_OMIT_ATTACH */ /* ** Initialize a DbFixer structure. This routine must be called prior |
︙ | ︙ |
Changes to src/backup.c.
︙ | ︙ | |||
146 147 148 149 150 151 152 | if( pSrcDb==pDestDb ){ sqlite3Error( pDestDb, SQLITE_ERROR, "source and destination must be distinct" ); p = 0; }else { | | > > > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | if( pSrcDb==pDestDb ){ sqlite3Error( pDestDb, SQLITE_ERROR, "source and destination must be distinct" ); p = 0; }else { /* Allocate space for a new sqlite3_backup object... ** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup)); if( !p ){ sqlite3Error(pDestDb, SQLITE_NOMEM, 0); } } /* If the allocation succeeded, populate the new object. */ |
︙ | ︙ | |||
529 530 531 532 533 534 535 536 537 538 539 540 541 542 | /* Exit the mutexes and free the backup context structure. */ if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); if( p->pDestDb ){ sqlite3_free(p); } sqlite3_mutex_leave(mutex); return rc; } /* | > > > | 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | /* Exit the mutexes and free the backup context structure. */ if( p->pDestDb ){ sqlite3_mutex_leave(p->pDestDb->mutex); } sqlite3BtreeLeave(p->pSrc); if( p->pDestDb ){ /* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a ** call to sqlite3_backup_init() and is destroyed by a call to ** sqlite3_backup_finish(). */ sqlite3_free(p); } sqlite3_mutex_leave(mutex); return rc; } /* |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
1668 1669 1670 1671 1672 1673 1674 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); } /* ** Open a database file. ** ** zFilename is the name of the database file. If zFilename is NULL | | > > | > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 | return sqlite3InvokeBusyHandler(&pBt->db->busyHandler); } /* ** Open a database file. ** ** zFilename is the name of the database file. If zFilename is NULL ** then an ephemeral database is created. The ephemeral database might ** be exclusively in memory, or it might use a disk-based memory cache. ** Either way, the ephemeral database will be automatically deleted ** when sqlite3BtreeClose() is called. ** ** If zFilename is ":memory:" then an in-memory database is created ** that is automatically destroyed when it is closed. ** ** The "flags" parameter is a bitmask that might contain bits ** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK ** bit is also set if the SQLITE_NoReadlock flags is set in db->flags. ** These flags are passed through into sqlite3PagerOpen() and must ** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK. ** ** If the database is already opened in the same database connection ** and we are in shared cache mode, then the open will fail with an ** SQLITE_CONSTRAINT error. We cannot allow two or more BtShared ** objects in the same database connection since doing so will lead ** to problems with locking. */ int sqlite3BtreeOpen( const char *zFilename, /* Name of the file containing the BTree database */ sqlite3 *db, /* Associated database handle */ Btree **ppBtree, /* Pointer to new Btree object written here */ int flags, /* Options */ int vfsFlags /* Flags passed through to sqlite3_vfs.xOpen() */ ){ sqlite3_vfs *pVfs; /* The VFS to use for this btree */ BtShared *pBt = 0; /* Shared part of btree structure */ Btree *p; /* Handle to return */ sqlite3_mutex *mutexOpen = 0; /* Prevents a race condition. Ticket #3537 */ int rc = SQLITE_OK; /* Result code from this function */ u8 nReserve; /* Byte of unused space on each page */ unsigned char zDbHeader[100]; /* Database header content */ /* True if opening an ephemeral, temporary database */ const int isTempDb = zFilename==0 || zFilename[0]==0; /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if ** either of the shared-data or autovacuum features are compiled ** into the library. */ #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) #ifdef SQLITE_OMIT_MEMORYDB const int isMemdb = 0; #else const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0) || (isTempDb && sqlite3TempInMemory(db)); #endif #endif assert( db!=0 ); assert( sqlite3_mutex_held(db->mutex) ); assert( (flags&0xff)==flags ); /* flags fit in 8 bits */ /* Only a BTREE_SINGLE database can be BTREE_UNORDERED */ assert( (flags & BTREE_UNORDERED)==0 || (flags & BTREE_SINGLE)!=0 ); /* A BTREE_SINGLE database is always a temporary and/or ephemeral */ assert( (flags & BTREE_SINGLE)==0 || isTempDb ); if( db->flags & SQLITE_NoReadlock ){ flags |= BTREE_NO_READLOCK; } if( isMemdb ){ flags |= BTREE_MEMORY; } if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){ vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB; } pVfs = db->pVfs; p = sqlite3MallocZero(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; } p->inTrans = TRANS_NONE; p->db = db; #ifndef SQLITE_OMIT_SHARED_CACHE p->lock.pBtree = p; p->lock.iTable = 1; #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* ** If this Btree is a candidate for shared cache, try to find an ** existing BtShared object that we can share with */ if( isMemdb==0 && isTempDb==0 ){ if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); |
︙ | ︙ | |||
1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 | EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); | > | 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 | EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } if( rc!=SQLITE_OK ){ goto btree_open_out; } pBt->openFlags = (u8)flags; pBt->db = db; sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); |
︙ | ︙ | |||
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 | if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ sqlite3PagerClose(pBt->pPager); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; } if( mutexOpen ){ assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } return rc; } | > > > > > > > > | 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 | if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ sqlite3PagerClose(pBt->pPager); } sqlite3_free(pBt); sqlite3_free(p); *ppBtree = 0; }else{ /* If the B-Tree was successfully opened, set the pager-cache size to the ** default value. Except, when opening on an existing shared pager-cache, ** do not change the pager-cache size. */ if( sqlite3BtreeSchema(p, 0, 0)==0 ){ sqlite3PagerSetCachesize(p->pBt->pPager, SQLITE_DEFAULT_CACHE_SIZE); } } if( mutexOpen ){ assert( sqlite3_mutex_held(mutexOpen) ); sqlite3_mutex_leave(mutexOpen); } return rc; } |
︙ | ︙ | |||
2063 2064 2065 2066 2067 2068 2069 | ** how well the database resists damage due to OS crashes and power ** failures. Level 1 is the same as asynchronous (no syncs() occur and ** there is a high probability of damage) Level 2 is the default. There ** is a very low but non-zero probability of damage. Level 3 reduces the ** probability of damage to near zero but with a write performance reduction. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS | | > > > > > > | | 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 | ** how well the database resists damage due to OS crashes and power ** failures. Level 1 is the same as asynchronous (no syncs() occur and ** there is a high probability of damage) Level 2 is the default. There ** is a very low but non-zero probability of damage. Level 3 reduces the ** probability of damage to near zero but with a write performance reduction. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS int sqlite3BtreeSetSafetyLevel( Btree *p, /* The btree to set the safety level on */ int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */ int fullSync, /* PRAGMA fullfsync. */ int ckptFullSync /* PRAGMA checkpoint_fullfync */ ){ BtShared *pBt = p->pBt; assert( sqlite3_mutex_held(p->db->mutex) ); assert( level>=1 && level<=3 ); sqlite3BtreeEnter(p); sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync); sqlite3BtreeLeave(p); return SQLITE_OK; } #endif /* ** Return TRUE if the given btree is set to safety level 1. In other |
︙ | ︙ | |||
3125 3126 3127 3128 3129 3130 3131 | ** the rollback journal (which causes the transaction to commit) and ** drop locks. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqlite3BtreeCommitPhaseTwo(Btree *p){ | < > > | 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 | ** the rollback journal (which causes the transaction to commit) and ** drop locks. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqlite3BtreeCommitPhaseTwo(Btree *p){ if( p->inTrans==TRANS_NONE ) return SQLITE_OK; sqlite3BtreeEnter(p); btreeIntegrity(p); /* If the handle has a write-transaction open, commit the shared-btrees ** transaction and set the shared state to TRANS_READ. */ if( p->inTrans==TRANS_WRITE ){ int rc; BtShared *pBt = p->pBt; assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->nTransaction>0 ); rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } |
︙ | ︙ | |||
6856 6857 6858 6859 6860 6861 6862 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ | | > | 6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 | ** The type of type is determined by the flags parameter. Only the ** following values of flags are currently in use. Other values for ** flags might not work: ** ** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys ** BTREE_ZERODATA Used for SQL indices */ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){ BtShared *pBt = p->pBt; MemPage *pRoot; Pgno pgnoRoot; int rc; int ptfFlags; /* Page-type flage for the root page of new table */ assert( sqlite3BtreeHoldsMutex(p) ); assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); #ifdef SQLITE_OMIT_AUTOVACUUM rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); |
︙ | ︙ | |||
6979 6980 6981 6982 6983 6984 6985 | }else{ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; } #endif assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); | > > > > > | > | 7025 7026 7027 7028 7029 7030 7031 7032 7033 7034 7035 7036 7037 7038 7039 7040 7041 7042 7043 7044 7045 7046 | }else{ rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0); if( rc ) return rc; } #endif assert( sqlite3PagerIswriteable(pRoot->pDbPage) ); if( createTabFlags & BTREE_INTKEY ){ ptfFlags = PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF; }else{ ptfFlags = PTF_ZERODATA | PTF_LEAF; } zeroPage(pRoot, ptfFlags); sqlite3PagerUnref(pRoot->pDbPage); assert( (pBt->openFlags & BTREE_SINGLE)==0 || pgnoRoot==2 ); *piTable = (int)pgnoRoot; return SQLITE_OK; } int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ int rc; sqlite3BtreeEnter(p); rc = btreeCreateTable(p, piTable, flags); |
︙ | ︙ | |||
8046 8047 8048 8049 8050 8051 8052 | ** (stored in BtCursor.aOverflow[]) is allocated and used by function ** accessPayload() (the worker function for sqlite3BtreeData() and ** sqlite3BtreePutData()). */ void sqlite3BtreeCacheOverflow(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); | < | | 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 8111 8112 | ** (stored in BtCursor.aOverflow[]) is allocated and used by function ** accessPayload() (the worker function for sqlite3BtreeData() and ** sqlite3BtreePutData()). */ void sqlite3BtreeCacheOverflow(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); invalidateOverflowCache(pCur); pCur->isIncrblobHandle = 1; } #endif /* ** Set both the "read version" (single byte at byte offset 18) and ** "write version" (single byte at byte offset 19) fields in the database |
︙ | ︙ |
Changes to src/btree.h.
︙ | ︙ | |||
63 64 65 66 67 68 69 | /* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ | | | | | < | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | /* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ #define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ #define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ #define BTREE_MEMORY 4 /* This is an in-memory DB */ #define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */ #define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetCacheSize(Btree*,int); int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); int sqlite3BtreeSyncDisabled(Btree*); int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); int sqlite3BtreeGetPageSize(Btree*); int sqlite3BtreeMaxPageCount(Btree*,int); u32 sqlite3BtreeLastPage(Btree*); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); |
︙ | ︙ | |||
104 105 106 107 108 109 110 | const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); int sqlite3BtreeCopyFile(Btree *, Btree *); int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR | | > > > > > > > | < | 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 | const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); int sqlite3BtreeCopyFile(Btree *, Btree *); int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR ** of the flags shown below. ** ** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. ** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data ** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With ** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored ** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL ** indices.) */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ #define BTREE_BLOBKEY 2 /* Table has keys only - no data */ int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int, int*); void sqlite3BtreeTripAllCursors(Btree*, int); void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); |
︙ | ︙ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
405 406 407 408 409 410 411 412 413 414 415 416 417 418 | sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ | > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 | sqlite3 *db; /* Database connection currently using this Btree */ BtCursor *pCursor; /* A list of all open cursors */ MemPage *pPage1; /* First page of the database */ u8 readOnly; /* True if the underlying file is readonly */ u8 pageSizeFixed; /* True if the page size can no longer be changed */ u8 secureDelete; /* True if secure_delete is enabled */ u8 initiallyEmpty; /* Database is empty at start of transaction */ u8 openFlags; /* Flags to sqlite3BtreeOpen() */ #ifndef SQLITE_OMIT_AUTOVACUUM u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */ #endif u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */ u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */ u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */ |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
722 723 724 725 726 727 728 | ** The call below sets the pName pointer to point at the token (pName1 or ** pName2) that stores the unqualified table name. The variable iDb is ** set to the index of the database that the table or view is to be ** created in. */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) return; | | | > | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 | ** The call below sets the pName pointer to point at the token (pName1 or ** pName2) that stores the unqualified table name. The variable iDb is ** set to the index of the database that the table or view is to be ** created in. */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName); if( iDb<0 ) return; if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){ /* If creating a temp table, the name may not be qualified. Unless ** the database name is "temp" anyway. */ sqlite3ErrorMsg(pParse, "temporary table name must be unqualified"); return; } if( !OMIT_TEMPDB && isTemp ) iDb = 1; pParse->sNameToken = *pName; zName = sqlite3NameFromToken(db, pName); |
︙ | ︙ | |||
771 772 773 774 775 776 777 778 779 780 | ** index or table name in the same database. Issue an error message if ** it does. The exception is if the statement being parsed was passed ** to an sqlite3_declare_vtab() call. In that case only the column names ** and types will be used, so there is no need to test for namespace ** collisions. */ if( !IN_DECLARE_VTAB ){ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto begin_table_error; } | > | | > | 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 | ** index or table name in the same database. Issue an error message if ** it does. The exception is if the statement being parsed was passed ** to an sqlite3_declare_vtab() call. In that case only the column names ** and types will be used, so there is no need to test for namespace ** collisions. */ if( !IN_DECLARE_VTAB ){ char *zDb = db->aDb[iDb].zName; if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto begin_table_error; } pTable = sqlite3FindTable(db, zName, zDb); if( pTable ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); } goto begin_table_error; } if( sqlite3FindIndex(db, zName, zDb)!=0 ){ sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); goto begin_table_error; } } pTable = sqlite3DbMallocZero(db, sizeof(Table)); if( pTable==0 ){ db->mallocFailed = 1; pParse->rc = SQLITE_NOMEM; pParse->nErr++; goto begin_table_error; } pTable->zName = zName; pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nRef = 1; pTable->nRowEst = 1000000; assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; /* If this is the magic sqlite_sequence table used by autoincrement, ** then record a pointer to this table in the main database structure ** so that INSERT can find the table easily. */ |
︙ | ︙ | |||
1644 1645 1646 1647 1648 1649 1650 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); sqlite3SelectDelete(db, pSelect); return; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; | | < < | 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 | if( pParse->nVar>0 ){ sqlite3ErrorMsg(pParse, "parameters are not allowed in views"); sqlite3SelectDelete(db, pSelect); return; } sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr); p = pParse->pNewTable; if( p==0 || pParse->nErr ){ sqlite3SelectDelete(db, pSelect); return; } sqlite3TwoPartName(pParse, pName1, pName2, &pName); iDb = sqlite3SchemaToIndex(db, p->pSchema); if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName) && sqlite3FixSelect(&sFix, pSelect) ){ sqlite3SelectDelete(db, pSelect); return; |
︙ | ︙ | |||
2767 2768 2769 2770 2771 2772 2773 | /* Fill the index with data and reparse the schema. Code an OP_Expire ** to invalidate all pre-compiled statements. */ if( pTblName ){ sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, | | > | 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 | /* Fill the index with data and reparse the schema. Code an OP_Expire ** to invalidate all pre-compiled statements. */ if( pTblName ){ sqlite3RefillIndex(pParse, pIndex, iMem); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName), P4_DYNAMIC); sqlite3VdbeAddOp1(v, OP_Expire, 0); } } /* When adding an index to the list of indices for a table, make ** sure all indices labeled OE_Replace come after all those labeled ** OE_Ignore. This is necessary for the correct constraint check |
︙ | ︙ | |||
2828 2829 2830 2831 2832 2833 2834 2835 | ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here ** are based on typical values found in actual indices. */ void sqlite3DefaultRowEst(Index *pIdx){ unsigned *a = pIdx->aiRowEst; int i; assert( a!=0 ); | > > | > | < < < | | | 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 | ** Apart from that, we have little to go on besides intuition as to ** how aiRowEst[] should be initialized. The numbers generated here ** are based on typical values found in actual indices. */ void sqlite3DefaultRowEst(Index *pIdx){ unsigned *a = pIdx->aiRowEst; int i; unsigned n; assert( a!=0 ); a[0] = pIdx->pTable->nRowEst; if( a[0]<10 ) a[0] = 10; n = 10; for(i=1; i<=pIdx->nColumn; i++){ a[i] = n; if( n>5 ) n--; } if( pIdx->onError!=OE_None ){ a[pIdx->nColumn] = 1; } } /* |
︙ | ︙ | |||
2895 2896 2897 2898 2899 2900 2901 | #endif /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, | | | 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 | #endif /* Generate code to remove the index and from the master table */ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3BeginWriteOperation(pParse, 1, iDb); sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE name=%Q AND type='index'", db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName ); if( sqlite3FindTable(db, "sqlite_stat1", db->aDb[iDb].zName) ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q", db->aDb[iDb].zName, pIndex->zName |
︙ | ︙ | |||
3387 3388 3389 3390 3391 3392 3393 | static const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; | | | 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 | static const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB; rc = sqlite3BtreeOpen(0, db, &pBt, 0, flags); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "unable to open a temporary database " "file for storing temporary tables"); pParse->rc = rc; return 1; } db->aDb[1].pBt = pBt; |
︙ | ︙ |
Changes to src/callback.c.
︙ | ︙ | |||
354 355 356 357 358 359 360 | /* If no match is found, search the built-in functions. ** ** If the SQLITE_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to | | | 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | /* If no match is found, search the built-in functions. ** ** If the SQLITE_PreferBuiltin flag is set, then search the built-in ** functions even if a prior app-defined function was found. And give ** priority to built-in functions. ** ** Except, if createFlag is true, that means that we are trying to ** install a new function. Whatever FuncDef structure is returned it will ** have fields overwritten with new information appropriate for the ** new function. But the FuncDefs for built-in functions are read-only. ** So we must not search for built-ins when creating a new function. */ if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); bestScore = 0; |
︙ | ︙ |
Changes to src/date.c.
︙ | ︙ | |||
129 130 131 132 133 134 135 | cnt++; }while( nextC ); end_getDigits: va_end(ap); return cnt; } | < < < < < < | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | cnt++; }while( nextC ); end_getDigits: va_end(ap); return cnt; } /* ** Parse a timezone extension on the end of a date-time. ** The extension is of the form: ** ** (+/-)HH:MM ** ** Or the "zulu" notation: |
︙ | ︙ | |||
336 337 338 339 340 341 342 | ** as there is a year and date. */ static int parseDateOrTime( sqlite3_context *context, const char *zDate, DateTime *p ){ | | | < < | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | ** as there is a year and date. */ static int parseDateOrTime( sqlite3_context *context, const char *zDate, DateTime *p ){ double r; if( parseYyyyMmDd(zDate,p)==0 ){ return 0; }else if( parseHhMmSs(zDate, p)==0 ){ return 0; }else if( sqlite3StrICmp(zDate,"now")==0){ setDateTimeToCurrent(context, p); return 0; }else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){ p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5); p->validJD = 1; return 0; } return 1; } |
︙ | ︙ | |||
567 568 569 570 571 572 573 | /* ** weekday N ** ** Move the date to the same time on the next occurrence of ** weekday N where 0==Sunday, 1==Monday, and so forth. If the ** date is already on the appropriate weekday, this is a no-op. */ | | > | | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | /* ** weekday N ** ** Move the date to the same time on the next occurrence of ** weekday N where 0==Sunday, 1==Monday, and so forth. If the ** date is already on the appropriate weekday, this is a no-op. */ if( strncmp(z, "weekday ", 8)==0 && sqlite3AtoF(&z[8], &r, sqlite3Strlen30(&z[8]), SQLITE_UTF8) && (n=(int)r)==r && n>=0 && r<7 ){ sqlite3_int64 Z; computeYMD_HMS(p); p->validTZ = 0; p->validJD = 0; computeJD(p); Z = ((p->iJD + 129600000)/86400000) % 7; if( Z>n ) Z -= 7; |
︙ | ︙ | |||
623 624 625 626 627 628 629 | case '4': case '5': case '6': case '7': case '8': case '9': { double rRounder; | > > | | > | 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 | case '4': case '5': case '6': case '7': case '8': case '9': { double rRounder; for(n=1; z[n] && z[n]!=':' && !sqlite3Isspace(z[n]); n++){} if( !sqlite3AtoF(z, &r, n, SQLITE_UTF8) ){ rc = 1; break; } if( z[n]==':' ){ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the ** specified number of hours, minutes, seconds, and fractional seconds ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be ** omitted. */ const char *z2 = z; |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
480 481 482 483 484 485 486 487 488 489 490 491 492 493 | int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ Expr *pRight, /* Right operand */ const Token *pToken /* Argument token */ ){ Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); return p; } /* ** Join two expressions using an AND operator. If either expression is ** NULL, then just return the other expression. */ | > > > | 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 | int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ Expr *pRight, /* Right operand */ const Token *pToken /* Argument token */ ){ Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); if( p ) { sqlite3ExprCheckHeight(pParse, p->nHeight); } return p; } /* ** Join two expressions using an AND operator. If either expression is ** NULL, then just return the other expression. */ |
︙ | ︙ | |||
551 552 553 554 555 556 557 | /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ i64 i; | | | 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ i64 i; int bOk = 0==sqlite3Atoi64(&z[1], &i, sqlite3Strlen30(&z[1]), SQLITE_UTF8); pExpr->iColumn = (ynVar)i; testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ); if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){ sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d", |
︙ | ︙ | |||
1531 1532 1533 1534 1535 1536 1537 | pX->iTable = iTab; } return eType; } #endif /* | | | | 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 | pX->iTable = iTab; } return eType; } #endif /* ** Generate code for scalar subqueries used as a subquery expression, EXISTS, ** or IN operators. Examples: ** ** (SELECT a FROM b) -- subquery ** EXISTS (SELECT a FROM b) -- EXISTS subquery ** x IN (4,5,11) -- IN operator with list on right-hand side ** x IN (SELECT a FROM b) -- IN operator with subquery on the right ** ** The pExpr parameter describes the expression that contains the IN |
︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 | */ if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } switch( pExpr->op ){ case TK_IN: { | > > > > > > > > > > | | | | | 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 | */ if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ char *zMsg = sqlite3MPrintf( pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr?"":"CORRELATED ", pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId ); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } #endif switch( pExpr->op ){ case TK_IN: { char affinity; /* Affinity of the LHS of the IN */ KeyInfo keyInfo; /* Keyinfo for the generated table */ int addr; /* Address of OP_OpenEphemeral instruction */ Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ if( rMayHaveNull ){ sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull); } affinity = sqlite3ExprAffinity(pLeft); |
︙ | ︙ | |||
1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 | ** SELECT... statement are columns, then numeric affinity is used ** if either column has NUMERIC or INTEGER affinity. If neither ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary | > | 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 | ** SELECT... statement are columns, then numeric affinity is used ** if either column has NUMERIC or INTEGER affinity. If neither ** 'x' nor the SELECT... statement are columns, then numeric affinity ** is used. */ pExpr->iTable = pParse->nTab++; addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid); if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED); memset(&keyInfo, 0, sizeof(keyInfo)); keyInfo.nField = 1; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* Case 1: expr IN (SELECT ...) ** ** Generate code to write the results of the select into the temporary |
︙ | ︙ | |||
1913 1914 1915 1916 1917 1918 1919 | ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; | | | < < > > > | < < | | 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 | ** z[n] character is guaranteed to be something that does not look ** like the continuation of the number. */ static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){ if( ALWAYS(z!=0) ){ double value; char *zV; sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ if( negateFlag ) value = -value; zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); } } #endif /* ** Generate an instruction that will put the integer describe by ** text z[0..n-1] into register iMem. ** ** Expr.u.zToken is always UTF8 and zero-terminated. */ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; if( negFlag ) i = -i; sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ int c; i64 value; const char *z = pExpr->u.zToken; assert( z!=0 ); c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; if( negFlag ){ value = -value; } zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ #ifdef SQLITE_OMIT_FLOATING_POINT sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z); #else codeReal(v, z, negFlag, iMem); |
︙ | ︙ | |||
2229 2230 2231 2232 2233 2234 2235 | int r = p->iReg; if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 | int r = p->iReg; if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ } return 0; } #endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ /* ** Generate code into the current Vdbe to evaluate the given ** expression. Attempt to store the results in register "target". ** Return the register where results are stored. ** ** With this routine, there is no guarantee that results will ** be stored in target. The result might be stored in some other |
︙ | ︙ | |||
2404 2405 2406 2407 2408 2409 2410 | break; } case TK_REGISTER: { inReg = pExpr->iTable; break; } case TK_AS: { | | | 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 | break; } case TK_REGISTER: { inReg = pExpr->iTable; break; } case TK_AS: { inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); break; } #ifndef SQLITE_OMIT_CAST case TK_CAST: { /* Expressions of the form: CAST(pLeft AS token) */ int aff, to_op; inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target); |
︙ | ︙ | |||
2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 | testcase( pX->op==TK_REGISTER ); cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); testcase( regFree1==0 ); cacheX.op = TK_REGISTER; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; } for(i=0; i<nExpr; i=i+2){ sqlite3ExprCachePush(pParse); if( pX ){ assert( pTest!=0 ); opCompare.pRight = aListelem[i].pExpr; }else{ | > > > > > | 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 | testcase( pX->op==TK_REGISTER ); cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, ®Free1); testcase( regFree1==0 ); cacheX.op = TK_REGISTER; opCompare.op = TK_EQ; opCompare.pLeft = &cacheX; pTest = &opCompare; /* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001: ** The value in regFree1 might get SCopy-ed into the file result. ** So make sure that the regFree1 register is not reused for other ** purposes and possibly overwritten. */ regFree1 = 0; } for(i=0; i<nExpr; i=i+2){ sqlite3ExprCachePush(pParse); if( pX ){ assert( pTest!=0 ); opCompare.pRight = aListelem[i].pExpr; }else{ |
︙ | ︙ | |||
2929 2930 2931 2932 2933 2934 2935 | ** results in register target. The results are guaranteed to appear ** in register target. */ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); | > > > | | | | > | 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 | ** results in register target. The results are guaranteed to appear ** in register target. */ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){ int inReg; assert( target>0 && target<=pParse->nMem ); if( pExpr && pExpr->op==TK_REGISTER ){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target); }else{ inReg = sqlite3ExprCodeTarget(pParse, pExpr, target); assert( pParse->pVdbe || pParse->db->mallocFailed ); if( inReg!=target && pParse->pVdbe ){ sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target); } } return target; } /* ** Generate code that evalutes the given expression and puts the result ** in register target. |
︙ | ︙ | |||
3105 3106 3107 3108 3109 3110 3111 3112 3113 | int target, /* Where to write results */ int doHardCopy /* Make a hard copy of every element */ ){ struct ExprList_item *pItem; int i, n; assert( pList!=0 ); assert( target>0 ); n = pList->nExpr; for(pItem=pList->a, i=0; i<n; i++, pItem++){ | > | < | | | < < | < < < | 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 | int target, /* Where to write results */ int doHardCopy /* Make a hard copy of every element */ ){ struct ExprList_item *pItem; int i, n; assert( pList!=0 ); assert( target>0 ); assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */ n = pList->nExpr; for(pItem=pList->a, i=0; i<n; i++, pItem++){ Expr *pExpr = pItem->pExpr; int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i); if( inReg!=target+i ){ sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy, inReg, target+i); } } return n; } /* ** Generate code for a BETWEEN operator. |
︙ | ︙ |
Changes to src/fkey.c.
︙ | ︙ | |||
376 377 378 379 380 381 382 | int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); for(i=0; i<nCol; i++){ | | | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); for(i=0; i<nCol; i++){ sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i); } /* If the parent table is the same as the child table, and we are about ** to increment the constraint-counter (i.e. this is an INSERT operation), ** then check if the row being inserted matches itself. If so, do not ** increment the constraint-counter. */ if( pTab==pFKey->pFrom && nIncr==1 ){ |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
284 285 286 287 288 289 290 | r = -(double)((sqlite_int64)((-r)+0.5)); }else{ zBuf = sqlite3_mprintf("%.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; } | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | r = -(double)((sqlite_int64)((-r)+0.5)); }else{ zBuf = sqlite3_mprintf("%.*f",n,r); if( zBuf==0 ){ sqlite3_result_error_nomem(context); return; } sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8); sqlite3_free(zBuf); } sqlite3_result_double(context, r); } #endif /* |
︙ | ︙ | |||
1449 1450 1451 1452 1453 1454 1455 | void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ struct compareInfo *pInfo; if( caseSensitive ){ pInfo = (struct compareInfo*)&likeInfoAlt; }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } | | | | | 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 | void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive){ struct compareInfo *pInfo; if( caseSensitive ){ pInfo = (struct compareInfo*)&likeInfoAlt; }else{ pInfo = (struct compareInfo*)&likeInfoNorm; } sqlite3CreateFunc(db, "like", 2, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); sqlite3CreateFunc(db, "like", 3, SQLITE_ANY, pInfo, likeFunc, 0, 0, 0); sqlite3CreateFunc(db, "glob", 2, SQLITE_ANY, (struct compareInfo*)&globInfo, likeFunc, 0, 0, 0); setLikeOptFlag(db, "glob", SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE); setLikeOptFlag(db, "like", caseSensitive ? (SQLITE_FUNC_LIKE | SQLITE_FUNC_CASE) : SQLITE_FUNC_LIKE); } /* ** pExpr points to an expression which implements a function. If |
︙ | ︙ | |||
1536 1537 1538 1539 1540 1541 1542 | FUNCTION(round, 2, 0, 0, roundFunc ), #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), /* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ | | | | 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 | FUNCTION(round, 2, 0, 0, roundFunc ), #endif FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), FUNCTION(coalesce, 0, 0, 0, 0 ), /* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ {-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0}, FUNCTION(hex, 1, 0, 0, hexFunc ), /* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */ {2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0}, FUNCTION(random, 0, 0, 0, randomFunc ), FUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ), |
︙ | ︙ | |||
1566 1567 1568 1569 1570 1571 1572 | FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ | | | 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 | FUNCTION(load_extension, 1, 0, 0, loadExt ), FUNCTION(load_extension, 2, 0, 0, loadExt ), #endif AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ), AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ), AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ), /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */ {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0}, AGGREGATE(count, 1, 0, 0, countStep, countFinalize ), AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize), AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize), LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), #ifdef SQLITE_CASE_SENSITIVE_LIKE LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE), |
︙ | ︙ |
Changes to src/loadext.c.
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 | ** Added for 3.6.0 */ sqlite3_extended_result_codes, sqlite3_limit, sqlite3_next_stmt, sqlite3_sql, sqlite3_status, }; /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a ** default entry point name (sqlite3_extension_init) is used. Use ** of the default name is recommended. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** Added for 3.6.0 */ sqlite3_extended_result_codes, sqlite3_limit, sqlite3_next_stmt, sqlite3_sql, sqlite3_status, /* ** Added for 3.7.4 */ sqlite3_backup_finish, sqlite3_backup_init, sqlite3_backup_pagecount, sqlite3_backup_remaining, sqlite3_backup_step, #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS sqlite3_compileoption_get, sqlite3_compileoption_used, #else 0, 0, #endif sqlite3_create_function_v2, sqlite3_db_config, sqlite3_db_mutex, sqlite3_db_status, sqlite3_extended_errcode, sqlite3_log, sqlite3_soft_heap_limit64, sqlite3_sourceid, sqlite3_stmt_status, sqlite3_strnicmp, #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY sqlite3_unlock_notify, #else 0, #endif #ifndef SQLITE_OMIT_WAL sqlite3_wal_autocheckpoint, sqlite3_wal_checkpoint, sqlite3_wal_hook, #else 0, 0, 0, #endif }; /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a ** default entry point name (sqlite3_extension_init) is used. Use ** of the default name is recommended. |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
25 26 27 28 29 30 31 | #ifdef SQLITE_ENABLE_RTREE # include "rtree.h" #endif #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" #endif | < > | > < > > > > > > > > > > > > > > > > > > | 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 | #ifdef SQLITE_ENABLE_RTREE # include "rtree.h" #endif #ifdef SQLITE_ENABLE_ICU # include "sqliteicu.h" #endif #ifndef SQLITE_AMALGAMATION /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant ** contains the text of SQLITE_VERSION macro. */ const char sqlite3_version[] = SQLITE_VERSION; #endif /* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns ** a pointer to the to the sqlite3_version[] string constant. */ const char *sqlite3_libversion(void){ return sqlite3_version; } /* IMPLEMENTATION-OF: R-63124-39300 The sqlite3_sourceid() function returns a ** pointer to a string constant whose value is the same as the ** SQLITE_SOURCE_ID C preprocessor macro. */ const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } /* IMPLEMENTATION-OF: R-35210-63508 The sqlite3_libversion_number() function ** returns an integer equal to SQLITE_VERSION_NUMBER. */ int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } /* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns ** zero if and only if SQLite was compiled mutexing code omitted due to ** the SQLITE_THREADSAFE compile-time option being set to 0. */ int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } #if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE) /* ** If the following function pointer is not NULL and if ** SQLITE_ENABLE_IOTRACE is enabled, then messages describing ** I/O active are written using this function. These messages |
︙ | ︙ | |||
154 155 156 157 158 159 160 161 162 163 164 165 166 167 | } /* Do the rest of the initialization under the recursive mutex so ** that we will be able to handle recursive calls into ** sqlite3_initialize(). The recursive calls normally come through ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other ** recursive calls might also be possible. */ sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); | > > > > > > > | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | } /* Do the rest of the initialization under the recursive mutex so ** that we will be able to handle recursive calls into ** sqlite3_initialize(). The recursive calls normally come through ** sqlite3_os_init() when it invokes sqlite3_vfs_register(), but other ** recursive calls might also be possible. ** ** IMPLEMENTATION-OF: R-00140-37445 SQLite automatically serializes calls ** to the xInit method, so the xInit method need not be threadsafe. ** ** The following mutex is what serializes access to the appdef pcache xInit ** methods. The sqlite3_pcache_methods.xInit() all is embedded in the ** call to sqlite3PcacheInitialize(). */ sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex); if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions); sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); |
︙ | ︙ | |||
434 435 436 437 438 439 440 | */ if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ | | | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 | */ if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0; if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ sqlite3BeginBenignMalloc(); pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); }else{ sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pFree = 0; db->lookaside.sz = (u16)sz; if( pStart ){ int i; |
︙ | ︙ | |||
482 483 484 485 486 487 488 | */ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { | | | | | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | */ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } default: { rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ break; } } va_end(ap); return rc; } |
︙ | ︙ | |||
593 594 595 596 597 598 599 600 601 602 603 604 | db->pSavepoint = pTmp->pNext; sqlite3DbFree(db, pTmp); } db->nSavepoint = 0; db->nStatement = 0; db->isTransactionSavepoint = 0; } /* ** Close an existing SQLite database */ int sqlite3_close(sqlite3 *db){ | > > > > > > > > > > > > > > > > > | | 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 | db->pSavepoint = pTmp->pNext; sqlite3DbFree(db, pTmp); } db->nSavepoint = 0; db->nStatement = 0; db->isTransactionSavepoint = 0; } /* ** Invoke the destructor function associated with FuncDef p, if any. Except, ** if this is not the last copy of the function, do not invoke it. Multiple ** copies of a single function are created when create_function() is called ** with SQLITE_ANY as the encoding. */ static void functionDestroy(sqlite3 *db, FuncDef *p){ FuncDestructor *pDestructor = p->pDestructor; if( pDestructor ){ pDestructor->nRef--; if( pDestructor->nRef==0 ){ pDestructor->xDestroy(pDestructor->pUserData); sqlite3DbFree(db, pDestructor); } } } /* ** Close an existing SQLite database */ int sqlite3_close(sqlite3 *db){ HashElem *i; /* Hash table iterator */ int j; if( !db ){ return SQLITE_OK; } if( !sqlite3SafetyCheckSickOrOk(db) ){ return SQLITE_MISUSE_BKPT; |
︙ | ︙ | |||
666 667 668 669 670 671 672 673 674 675 676 677 678 679 | assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); for(j=0; j<ArraySize(db->aFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ pHash = p->pHash; while( p ){ pNext = p->pNext; sqlite3DbFree(db, p); p = pNext; } } } for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ | > | 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 | assert( db->nDb<=2 ); assert( db->aDb==db->aDbStatic ); for(j=0; j<ArraySize(db->aFunc.a); j++){ FuncDef *pNext, *pHash, *p; for(p=db->aFunc.a[j]; p; p=pHash){ pHash = p->pHash; while( p ){ functionDestroy(db, p); pNext = p->pNext; sqlite3DbFree(db, p); p = pNext; } } } for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){ |
︙ | ︙ | |||
944 945 946 947 948 949 950 | sqlite3 *db, const char *zFunctionName, int nArg, int enc, void *pUserData, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), | | > | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 | sqlite3 *db, const char *zFunctionName, int nArg, int enc, void *pUserData, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*), FuncDestructor *pDestructor ){ FuncDef *p; int nName; assert( sqlite3_mutex_held(db->mutex) ); if( zFunctionName==0 || (xFunc && (xFinal || xStep)) || |
︙ | ︙ | |||
972 973 974 975 976 977 978 | ** to the hash table. */ if( enc==SQLITE_UTF16 ){ enc = SQLITE_UTF16NATIVE; }else if( enc==SQLITE_ANY ){ int rc; rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, | | | | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 | ** to the hash table. */ if( enc==SQLITE_UTF16 ){ enc = SQLITE_UTF16NATIVE; }else if( enc==SQLITE_ANY ){ int rc; rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8, pUserData, xFunc, xStep, xFinal, pDestructor); if( rc==SQLITE_OK ){ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE, pUserData, xFunc, xStep, xFinal, pDestructor); } if( rc!=SQLITE_OK ){ return rc; } enc = SQLITE_UTF16BE; } #else |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | } p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); assert(p || db->mallocFailed); if( !p ){ return SQLITE_NOMEM; } p->flags = 0; p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE_OK; } /* ** Create new user functions. */ int sqlite3_create_function( sqlite3 *db, | > > > > > > > > > | > > > > > > > > > > > > > > | > | > > > > > > > > > > | > > > > > > > | 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 | } p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1); assert(p || db->mallocFailed); if( !p ){ return SQLITE_NOMEM; } /* If an older version of the function with a configured destructor is ** being replaced invoke the destructor function here. */ functionDestroy(db, p); if( pDestructor ){ pDestructor->nRef++; } p->pDestructor = pDestructor; p->flags = 0; p->xFunc = xFunc; p->xStep = xStep; p->xFinalize = xFinal; p->pUserData = pUserData; p->nArg = (u16)nArg; return SQLITE_OK; } /* ** Create new user functions. */ int sqlite3_create_function( sqlite3 *db, const char *zFunc, int nArg, int enc, void *p, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*) ){ return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, 0); } int sqlite3_create_function_v2( sqlite3 *db, const char *zFunc, int nArg, int enc, void *p, void (*xFunc)(sqlite3_context*,int,sqlite3_value **), void (*xStep)(sqlite3_context*,int,sqlite3_value **), void (*xFinal)(sqlite3_context*), void (*xDestroy)(void *) ){ int rc = SQLITE_ERROR; FuncDestructor *pArg = 0; sqlite3_mutex_enter(db->mutex); if( xDestroy ){ pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor)); if( !pArg ){ xDestroy(p); goto out; } pArg->xDestroy = xDestroy; pArg->pUserData = p; } rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg); if( pArg && pArg->nRef==0 ){ assert( rc!=SQLITE_OK ); xDestroy(p); sqlite3DbFree(db, pArg); } out: rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #ifndef SQLITE_OMIT_UTF16 int sqlite3_create_function16( |
︙ | ︙ | |||
1054 1055 1056 1057 1058 1059 1060 | void (*xFinal)(sqlite3_context*) ){ int rc; char *zFunc8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); | | | 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 | void (*xFinal)(sqlite3_context*) ){ int rc; char *zFunc8; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE); rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0); sqlite3DbFree(db, zFunc8); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #endif |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | int nArg ){ int nName = sqlite3Strlen30(zName); int rc; sqlite3_mutex_enter(db->mutex); if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, | | | 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | int nArg ){ int nName = sqlite3Strlen30(zName); int rc; sqlite3_mutex_enter(db->mutex); if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){ sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8, 0, sqlite3InvalidFunction, 0, 0, 0); } rc = sqlite3ApiExit(db, SQLITE_OK); sqlite3_mutex_leave(db->mutex); return rc; } #ifndef SQLITE_OMIT_TRACE |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | ** ** The callback registered by this function replaces any existing callback ** registered using sqlite3_wal_hook(). Likewise, registering a callback ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ | | > > > | 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 | ** ** The callback registered by this function replaces any existing callback ** registered using sqlite3_wal_hook(). Likewise, registering a callback ** using sqlite3_wal_hook() disables the automatic checkpoint mechanism ** configured by this function. */ int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){ #ifdef SQLITE_OMIT_WAL UNUSED_PARAMETER(db); UNUSED_PARAMETER(nFrame); #else if( nFrame>0 ){ sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame)); }else{ sqlite3_wal_hook(db, 0, 0); } #endif return SQLITE_OK; |
︙ | ︙ | |||
1353 1354 1355 1356 1357 1358 1359 | return 1; #endif #if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 return 0; #endif } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 | return 1; #endif #if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3 return 0; #endif } /* ** Return UTF-8 encoded English language explanation of the most recent ** error. */ const char *sqlite3_errmsg(sqlite3 *db){ const char *z; if( !db ){ |
︙ | ︙ | |||
1571 1572 1573 1574 1575 1576 1577 | p->xCmp = 0; } } } } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); | | | | | | | < | 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 | p->xCmp = 0; } } } } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); if( pColl==0 ) return SQLITE_NOMEM; pColl->xCmp = xCompare; pColl->pUser = pCtx; pColl->xDel = xDel; pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); pColl->type = collType; sqlite3Error(db, SQLITE_OK, 0); return SQLITE_OK; } /* ** This array defines hard upper bounds on limit values. The |
︙ | ︙ | |||
1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 | ** ** A new lower limit does not shrink existing constructs. ** It merely prevents new constructs that exceed the limit ** from forming. */ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ return -1; } oldLimit = db->aLimit[limitId]; | > > > > > > > > > > > > > > > > > > > > > > | | | | 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 | ** ** A new lower limit does not shrink existing constructs. ** It merely prevents new constructs that exceed the limit ** from forming. */ int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ int oldLimit; /* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME ** there is a hard upper bound set at compile-time by a C preprocessor ** macro called SQLITE_MAX_NAME. (The "_LIMIT_" in the name is changed to ** "_MAX_".) */ assert( aHardLimit[SQLITE_LIMIT_LENGTH]==SQLITE_MAX_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_SQL_LENGTH]==SQLITE_MAX_SQL_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_COLUMN]==SQLITE_MAX_COLUMN ); assert( aHardLimit[SQLITE_LIMIT_EXPR_DEPTH]==SQLITE_MAX_EXPR_DEPTH ); assert( aHardLimit[SQLITE_LIMIT_COMPOUND_SELECT]==SQLITE_MAX_COMPOUND_SELECT); assert( aHardLimit[SQLITE_LIMIT_VDBE_OP]==SQLITE_MAX_VDBE_OP ); assert( aHardLimit[SQLITE_LIMIT_FUNCTION_ARG]==SQLITE_MAX_FUNCTION_ARG ); assert( aHardLimit[SQLITE_LIMIT_ATTACHED]==SQLITE_MAX_ATTACHED ); assert( aHardLimit[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]== SQLITE_MAX_LIKE_PATTERN_LENGTH ); assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER); assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH ); assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) ); if( limitId<0 || limitId>=SQLITE_N_LIMIT ){ return -1; } oldLimit = db->aLimit[limitId]; if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ } db->aLimit[limitId] = newLimit; } return oldLimit; /* IMP: R-53341-35419 */ } #if defined(SQLITE_ENABLE_AUTO_PROFILE) static void profile_sql(void *aux, const char *sql, u64 ns) { #pragma unused(aux) fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000); } #endif |
︙ | ︙ | |||
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 | int isThreadsafe; *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; | > > > > > > > > > > > > > > > > > > | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 | int isThreadsafe; *ppDb = 0; #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ) return rc; #endif /* Only allow sensible combinations of bits in the flags argument. ** Throw an error if any non-sense combination is used. If we ** do not block illegal combinations here, it could trigger ** assert() statements in deeper layers. Sensible combinations ** are: ** ** 1: SQLITE_OPEN_READONLY ** 2: SQLITE_OPEN_READWRITE ** 6: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE */ assert( SQLITE_OPEN_READONLY == 0x01 ); assert( SQLITE_OPEN_READWRITE == 0x02 ); assert( SQLITE_OPEN_CREATE == 0x04 ); testcase( (1<<(flags&7))==0x02 ); /* READONLY */ testcase( (1<<(flags&7))==0x04 ); /* READWRITE */ testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */ if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE; if( sqlite3GlobalConfig.bCoreMutex==0 ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_NOMUTEX ){ isThreadsafe = 0; }else if( flags & SQLITE_OPEN_FULLMUTEX ){ isThreadsafe = 1; |
︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | | | > | 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_WAL ); /* Allocate the sqlite data structure */ db = sqlite3MallocZero( sizeof(sqlite3) ); if( db==0 ) goto opendb_out; if( isThreadsafe ){ db->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); |
︙ | ︙ | |||
1794 1795 1796 1797 1798 1799 1800 | /* Also add a UTF-8 case-insensitive collation sequence. */ createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; | | | < | 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 | /* Also add a UTF-8 case-insensitive collation sequence. */ createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; rc = sqlite3BtreeOpen(zFilename, db, &db->aDb[0].pBt, 0, flags | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite3Error(db, rc, 0); goto opendb_out; } |
︙ | ︙ | |||
2304 2305 2306 2307 2308 2309 2310 | Pager *pPager; sqlite3_file *fd; sqlite3BtreeEnter(pBtree); pPager = sqlite3BtreePager(pBtree); assert( pPager!=0 ); fd = sqlite3PagerFile(pPager); assert( fd!=0 ); | > > > | | 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 | Pager *pPager; sqlite3_file *fd; sqlite3BtreeEnter(pBtree); pPager = sqlite3BtreePager(pBtree); assert( pPager!=0 ); fd = sqlite3PagerFile(pPager); assert( fd!=0 ); if( op==SQLITE_FCNTL_FILE_POINTER ){ *(sqlite3_file**)pArg = fd; rc = SQLITE_OK; }else if( fd->pMethods ){ rc = sqlite3OsFileControl(fd, op, pArg); } sqlite3BtreeLeave(pBtree); } } sqlite3_mutex_leave(db->mutex); return rc; |
︙ | ︙ | |||
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 | ** ** Return the size of a pcache header in bytes. */ case SQLITE_TESTCTRL_PGHDRSZ: { rc = sizeof(PgHdr); break; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } | > > > > > > > > > > > > > > > > | 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 | ** ** Return the size of a pcache header in bytes. */ case SQLITE_TESTCTRL_PGHDRSZ: { rc = sizeof(PgHdr); break; } /* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree); ** ** Pass pFree into sqlite3ScratchFree(). ** If sz>0 then allocate a scratch buffer into pNew. */ case SQLITE_TESTCTRL_SCRATCHMALLOC: { void *pFree, **ppNew; int sz; sz = va_arg(ap, int); ppNew = va_arg(ap, void**); pFree = va_arg(ap, void*); if( sz ) *ppNew = sqlite3ScratchMalloc(sz); sqlite3ScratchFree(pFree); break; } } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ return rc; } |
Changes to src/malloc.c.
︙ | ︙ | |||
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 | ************************************************************************* ** ** Memory allocation functions used throughout sqlite. */ #include "sqliteInt.h" #include <stdarg.h> /* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap ** limit. */ static void softHeapLimitEnforcer( void *NotUsed, sqlite3_int64 NotUsed2, int allocSize ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_release_memory(allocSize); } /* ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < < < > > > > | | | < | < < < < < < < < < < < < | < < < < | < < < < < < < | < | < < < < < < < < < | < < < < < < < < < < | | > > | | | | | > > > > > > > > < < < < | | < | < < < < < | | > > > > > > > > > | 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 | ************************************************************************* ** ** Memory allocation functions used throughout sqlite. */ #include "sqliteInt.h" #include <stdarg.h> /* ** Attempt to release up to n bytes of non-essential memory currently ** held by SQLite. An example of non-essential memory is memory used to ** cache database pages that are not currently in use. */ int sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT return sqlite3PcacheReleaseMemory(n); #else /* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine ** is a no-op returning zero if SQLite is not compiled with ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ UNUSED_PARAMETER(n); return 0; #endif } /* ** An instance of the following object records the location of ** each unused scratch buffer. */ typedef struct ScratchFreeslot { struct ScratchFreeslot *pNext; /* Next unused scratch buffer */ } ScratchFreeslot; /* ** State information local to the memory allocation subsystem. */ static SQLITE_WSD struct Mem0Global { sqlite3_mutex *mutex; /* Mutex to serialize access */ /* ** The alarm callback and its arguments. The mem0.mutex lock will ** be held while the callback is running. Recursive calls into ** the memory subsystem are allowed, but no new callbacks will be ** issued. */ sqlite3_int64 alarmThreshold; void (*alarmCallback)(void*, sqlite3_int64,int); void *alarmArg; /* ** Pointers to the end of sqlite3GlobalConfig.pScratch memory ** (so that a range test can be used to determine if an allocation ** being freed came from pScratch) and a pointer to the list of ** unused scratch allocations. */ void *pScratchEnd; ScratchFreeslot *pScratchFree; u32 nScratchFree; /* ** True if heap is nearly "full" where "full" is defined by the ** sqlite3_soft_heap_limit() setting. */ int nearlyFull; } mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; #define mem0 GLOBAL(struct Mem0Global, mem0) /* ** This routine runs when the memory allocator sees that the ** total memory allocation is about to exceed the soft heap ** limit. */ static void softHeapLimitEnforcer( void *NotUsed, sqlite3_int64 NotUsed2, int allocSize ){ UNUSED_PARAMETER2(NotUsed, NotUsed2); sqlite3_release_memory(allocSize); } /* ** Change the alarm callback */ static int sqlite3MemoryAlarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold ){ int nUsed; sqlite3_mutex_enter(mem0.mutex); mem0.alarmCallback = xCallback; mem0.alarmArg = pArg; mem0.alarmThreshold = iThreshold; nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed); sqlite3_mutex_leave(mem0.mutex); return SQLITE_OK; } #ifndef SQLITE_OMIT_DEPRECATED /* ** Deprecated external interface. Internal/core SQLite code ** should call sqlite3MemoryAlarm. */ int sqlite3_memory_alarm( void(*xCallback)(void *pArg, sqlite3_int64 used,int N), void *pArg, sqlite3_int64 iThreshold ){ return sqlite3MemoryAlarm(xCallback, pArg, iThreshold); } #endif /* ** Set the soft heap-size limit for the library. Passing a zero or ** negative value indicates no limit. */ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){ sqlite3_int64 priorLimit; sqlite3_int64 excess; #ifndef SQLITE_OMIT_AUTOINIT sqlite3_initialize(); #endif sqlite3_mutex_enter(mem0.mutex); priorLimit = mem0.alarmThreshold; sqlite3_mutex_leave(mem0.mutex); if( n<0 ) return priorLimit; if( n>0 ){ sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n); }else{ sqlite3MemoryAlarm(0, 0, 0); } excess = sqlite3_memory_used() - n; if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); return priorLimit; } void sqlite3_soft_heap_limit(int n){ if( n<0 ) n = 0; sqlite3_soft_heap_limit64(n); } /* ** Initialize the memory allocation subsystem. */ int sqlite3MallocInit(void){ if( sqlite3GlobalConfig.m.xMalloc==0 ){ sqlite3MemSetDefault(); } memset(&mem0, 0, sizeof(mem0)); if( sqlite3GlobalConfig.bCoreMutex ){ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); } if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 && sqlite3GlobalConfig.nScratch>0 ){ int i, n, sz; ScratchFreeslot *pSlot; sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch); sqlite3GlobalConfig.szScratch = sz; pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch; n = sqlite3GlobalConfig.nScratch; mem0.pScratchFree = pSlot; mem0.nScratchFree = n; for(i=0; i<n-1; i++){ pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot); pSlot = pSlot->pNext; } pSlot->pNext = 0; mem0.pScratchEnd = (void*)&pSlot[1]; }else{ mem0.pScratchEnd = 0; sqlite3GlobalConfig.pScratch = 0; sqlite3GlobalConfig.szScratch = 0; sqlite3GlobalConfig.nScratch = 0; } if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 || sqlite3GlobalConfig.nPage<1 ){ sqlite3GlobalConfig.pPage = 0; sqlite3GlobalConfig.szPage = 0; sqlite3GlobalConfig.nPage = 0; } return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); } /* ** Return true if the heap is currently under memory pressure - in other ** words if the amount of heap used is close to the limit set by ** sqlite3_soft_heap_limit(). */ int sqlite3HeapNearlyFull(void){ return mem0.nearlyFull; } /* ** Deinitialize the memory allocation subsystem. */ void sqlite3MallocEnd(void){ if( sqlite3GlobalConfig.m.xShutdown ){ sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData); |
︙ | ︙ | |||
174 175 176 177 178 179 180 | int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */ return res; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | int n, mx; sqlite3_int64 res; sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag); res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */ return res; } /* ** Trigger the alarm */ static void sqlite3MallocAlarm(int nByte){ void (*xCallback)(void*,sqlite3_int64,int); sqlite3_int64 nowUsed; void *pArg; |
︙ | ︙ | |||
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 | void *p; assert( sqlite3_mutex_held(mem0.mutex) ); nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed+nFull >= mem0.alarmThreshold ){ sqlite3MallocAlarm(nFull); } } p = sqlite3GlobalConfig.m.xMalloc(nFull); if( p==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nFull); p = sqlite3GlobalConfig.m.xMalloc(nFull); } if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; } /* ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ void *sqlite3Malloc(int n){ void *p; | > > > > > > | > > | 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 | void *p; assert( sqlite3_mutex_held(mem0.mutex) ); nFull = sqlite3GlobalConfig.m.xRoundup(n); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n); if( mem0.alarmCallback!=0 ){ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); if( nUsed+nFull >= mem0.alarmThreshold ){ mem0.nearlyFull = 1; sqlite3MallocAlarm(nFull); }else{ mem0.nearlyFull = 0; } } p = sqlite3GlobalConfig.m.xMalloc(nFull); #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT if( p==0 && mem0.alarmCallback ){ sqlite3MallocAlarm(nFull); p = sqlite3GlobalConfig.m.xMalloc(nFull); } #endif if( p ){ nFull = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1); } *pp = p; return nFull; } /* ** Allocate memory. This routine is like sqlite3_malloc() except that it ** assumes the memory subsystem has already been initialized. */ void *sqlite3Malloc(int n){ void *p; if( n<=0 /* IMP: R-65312-04917 */ || n>=0x7fffff00 ){ /* A memory allocation of a number of bytes which is near the maximum ** signed integer value might cause an integer overflow inside of the ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving ** 255 bytes of overhead. SQLite itself will never use anything near ** this amount. The only way to reach the limit is with sqlite3_malloc() */ p = 0; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); mallocWithAlarm(n, &p); sqlite3_mutex_leave(mem0.mutex); }else{ p = sqlite3GlobalConfig.m.xMalloc(n); } assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */ return p; } /* ** This version of the memory allocation is for use by the application. ** First make sure the memory subsystem is initialized, then do the ** allocation. |
︙ | ︙ | |||
311 312 313 314 315 316 317 | ** structures that would not normally fit on the stack of an ** embedded processor. */ void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); | < < < < < < < < < < < | | | | < < | < | | | < < | < < < < < < < < | < | | | | | > | | | > > > > > > > > > | > | | > > > > > > > > > | > > > > > > | > > > > > < < < < < < < < < < < < < < < < < < < < | 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 | ** structures that would not normally fit on the stack of an ** embedded processor. */ void *sqlite3ScratchMalloc(int n){ void *p; assert( n>0 ); sqlite3_mutex_enter(mem0.mutex); if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){ p = mem0.pScratchFree; mem0.pScratchFree = mem0.pScratchFree->pNext; mem0.nScratchFree--; sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1); sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); sqlite3_mutex_leave(mem0.mutex); }else{ if( sqlite3GlobalConfig.bMemstat ){ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n); n = mallocWithAlarm(n, &p); if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3_mutex_leave(mem0.mutex); p = sqlite3GlobalConfig.m.xMalloc(n); } sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); } assert( sqlite3_mutex_notheld(mem0.mutex) ); #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocations per thread ** are outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut<=1 ); if( p ) scratchAllocOut++; #endif return p; } void sqlite3ScratchFree(void *p){ if( p ){ #if SQLITE_THREADSAFE==0 && !defined(NDEBUG) /* Verify that no more than two scratch allocation per thread ** is outstanding at one time. (This is only checked in the ** single-threaded case since checking in the multi-threaded case ** would be much more complicated.) */ assert( scratchAllocOut>=1 && scratchAllocOut<=2 ); scratchAllocOut--; #endif if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){ /* Release memory from the SQLITE_CONFIG_SCRATCH allocation */ ScratchFreeslot *pSlot; pSlot = (ScratchFreeslot*)p; sqlite3_mutex_enter(mem0.mutex); pSlot->pNext = mem0.pScratchFree; mem0.pScratchFree = pSlot; mem0.nScratchFree++; assert( mem0.nScratchFree<=sqlite3GlobalConfig.nScratch ); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1); sqlite3_mutex_leave(mem0.mutex); }else{ /* Release memory back to the heap */ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); if( sqlite3GlobalConfig.bMemstat ){ int iSize = sqlite3MallocSize(p); sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); sqlite3GlobalConfig.m.xFree(p); sqlite3_mutex_leave(mem0.mutex); }else{ sqlite3GlobalConfig.m.xFree(p); } } } } /* ** TRUE if p is a lookaside memory allocation from db */ |
︙ | ︙ | |||
438 439 440 441 442 443 444 | } } /* ** Free memory previously obtained from sqlite3Malloc(). */ void sqlite3_free(void *p){ | | | 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | } } /* ** Free memory previously obtained from sqlite3Malloc(). */ void sqlite3_free(void *p){ if( p==0 ) return; /* IMP: R-49053-54554 */ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) ); assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p)); sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1); sqlite3GlobalConfig.m.xFree(p); |
︙ | ︙ | |||
485 486 487 488 489 490 491 | /* ** Change the size of an existing memory allocation */ void *sqlite3Realloc(void *pOld, int nBytes){ int nOld, nNew; void *pNew; if( pOld==0 ){ | | | > > > | 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 | /* ** Change the size of an existing memory allocation */ void *sqlite3Realloc(void *pOld, int nBytes){ int nOld, nNew; void *pNew; if( pOld==0 ){ return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */ } if( nBytes<=0 ){ sqlite3_free(pOld); /* IMP: R-31593-10574 */ return 0; } if( nBytes>=0x7fffff00 ){ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ return 0; } nOld = sqlite3MallocSize(pOld); /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second ** argument to xRealloc is always a value returned by a prior call to ** xRoundup. */ nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); if( nOld==nNew ){ pNew = pOld; }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= |
︙ | ︙ | |||
521 522 523 524 525 526 527 528 529 530 531 532 533 534 | nNew = sqlite3MallocSize(pNew); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } return pNew; } /* ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ | > | 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 | nNew = sqlite3MallocSize(pNew); sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */ return pNew; } /* ** The public interface to sqlite3Realloc. Make sure that the memory ** subsystem is initialized prior to invoking sqliteRealloc. */ |
︙ | ︙ |
Changes to src/mem1.c.
︙ | ︙ | |||
107 108 109 110 111 112 113 | ** redirected to xMalloc. Similarly, we know that nByte>0 becauses ** cases where nByte<=0 will have been intercepted by higher-level ** routines and redirected to xFree. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); | | | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | ** redirected to xMalloc. Similarly, we know that nByte>0 becauses ** cases where nByte<=0 will have been intercepted by higher-level ** routines and redirected to xFree. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ sqlite3_int64 *p = (sqlite3_int64*)pPrior; assert( pPrior!=0 && nByte>0 ); assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */ p--; p = SQLITE_REALLOC(p, nByte+8 ); if( p ){ p[0] = nByte; p++; }else{ testcase( sqlite3GlobalConfig.xLog!=0 ); |
︙ | ︙ |
Changes to src/mem2.c.
︙ | ︙ | |||
340 341 342 343 344 345 346 347 348 349 350 351 352 353 | ** much more likely to break and we are much more liking to find ** the error. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ struct MemBlockHdr *pOldHdr; void *pNew; assert( mem.disallow==0 ); pOldHdr = sqlite3MemsysGetHeader(pPrior); pNew = sqlite3MemMalloc(nByte); if( pNew ){ memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); } | > | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | ** much more likely to break and we are much more liking to find ** the error. */ static void *sqlite3MemRealloc(void *pPrior, int nByte){ struct MemBlockHdr *pOldHdr; void *pNew; assert( mem.disallow==0 ); assert( (nByte & 7)==0 ); /* EV: R-46199-30249 */ pOldHdr = sqlite3MemsysGetHeader(pPrior); pNew = sqlite3MemMalloc(nByte); if( pNew ){ memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); } |
︙ | ︙ |
Changes to src/mem5.c.
︙ | ︙ | |||
391 392 393 394 395 396 397 | ** (an allocation larger than 0x40000000) was requested and this ** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; assert( pPrior!=0 ); | | | 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | ** (an allocation larger than 0x40000000) was requested and this ** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; assert( pPrior!=0 ); assert( (nBytes&(nBytes-1))==0 ); /* EV: R-46199-30249 */ assert( nBytes>=0 ); if( nBytes==0 ){ return 0; } nOld = memsys5Size(pPrior); if( nBytes<=nOld ){ return pPrior; |
︙ | ︙ |
Changes to src/memjournal.c.
︙ | ︙ | |||
248 249 250 251 252 253 254 | ** an in-memory journal */ int sqlite3IsMemJournal(sqlite3_file *pJfd){ return pJfd->pMethods==&MemJournalMethods; } /* | | < | 248 249 250 251 252 253 254 255 256 257 258 259 | ** an in-memory journal */ int sqlite3IsMemJournal(sqlite3_file *pJfd){ return pJfd->pMethods==&MemJournalMethods; } /* ** Return the number of bytes required to store a MemJournal file descriptor. */ int sqlite3MemJournalSize(void){ return sizeof(MemJournal); } |
Changes to src/mutex.h.
︙ | ︙ | |||
59 60 61 62 63 64 65 | ** If this is a no-op implementation, implement everything as macros. */ #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) #define sqlite3_mutex_enter(X) #define sqlite3_mutex_try(X) SQLITE_OK #define sqlite3_mutex_leave(X) | | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 | ** If this is a no-op implementation, implement everything as macros. */ #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) #define sqlite3_mutex_enter(X) #define sqlite3_mutex_try(X) SQLITE_OK #define sqlite3_mutex_leave(X) #define sqlite3_mutex_held(X) ((void)(X),1) #define sqlite3_mutex_notheld(X) ((void)(X),1) #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() #endif /* defined(SQLITE_MUTEX_OMIT) */ |
Changes to src/os.c.
︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 188 189 190 191 192 | return pVfs->xRandomness(pVfs, nByte, zBufOut); } int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ int rc; if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); *pTimeOut = (sqlite3_int64)(r*86400000.0); } | > > > > > > | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | return pVfs->xRandomness(pVfs, nByte, zBufOut); } int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){ return pVfs->xSleep(pVfs, nMicro); } int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ int rc; /* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64() ** method to get the current date and time if that method is available ** (if iVersion is 2 or greater and the function pointer is not NULL) and ** will fall back to xCurrentTime() if xCurrentTimeInt64() is ** unavailable. */ if( pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64 ){ rc = pVfs->xCurrentTimeInt64(pVfs, pTimeOut); }else{ double r; rc = pVfs->xCurrentTime(pVfs, &r); *pTimeOut = (sqlite3_int64)(r*86400000.0); } |
︙ | ︙ |
Changes to src/os_unix.c.
︙ | ︙ | |||
3806 3807 3808 3809 3810 3811 3812 | if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, | | | 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 | if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion ); if( pMem==MAP_FAILED ){ rc = SQLITE_IOERR; goto shmpage_out; } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; |
︙ | ︙ | |||
4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 | assert( pNew->pInode==NULL ); /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ UNUSED_PARAMETER(isDelete); OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->fileFlags = 0; | > > > > > > > > > > > < | 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 | assert( pNew->pInode==NULL ); /* Parameter isDelete is only used on vxworks. Express this explicitly ** here to prevent compiler warnings about unused parameters. */ UNUSED_PARAMETER(isDelete); /* Usually the path zFilename should not be a relative pathname. The ** exception is when opening the proxy "conch" file in builds that ** include the special Apple locking styles. */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE assert( zFilename==0 || zFilename[0]=='/' || pVfs->pAppData==(void*)&autolockIoFinder ); #else assert( zFilename==0 || zFilename[0]=='/' ); #endif OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->fileFlags = 0; pNew->zPath = zFilename; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; rc = SQLITE_NOMEM; |
︙ | ︙ | |||
4668 4669 4670 4671 4672 4673 4674 | ){ int rc = SQLITE_OK; /* Return Code */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ struct stat sStat; /* Output of stat() on database file */ | > > > > > > > > > > > > | > > > | 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 | ){ int rc = SQLITE_OK; /* Return Code */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ struct stat sStat; /* Output of stat() on database file */ /* zPath is a path to a WAL or journal file. The following block derives ** the path to the associated database file from zPath. This block handles ** the following naming conventions: ** ** "<path to db>-journal" ** "<path to db>-wal" ** "<path to db>-journal-NNNN" ** "<path to db>-wal-NNNN" ** ** where NNNN is a 4 digit decimal number. The NNNN naming schemes are ** used by the test_multiplex.c module. */ nDb = sqlite3Strlen30(zPath) - 1; while( nDb>0 && zPath[nDb]!='l' ) nDb--; nDb -= ((flags & SQLITE_OPEN_WAL) ? 3 : 7); memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; if( 0==stat(zDb, &sStat) ){ *pMode = sStat.st_mode & 0777; }else{ rc = SQLITE_IOERR_FSTAT; } }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; |
︙ | ︙ | |||
5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 | ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ *pError = err; } return SQLITE_IOERR; } #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ if( sqlite3_hostid_num != 0){ pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); } #endif | > > > | 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 | ** bytes of writable memory. */ static int proxyGetHostID(unsigned char *pHostID, int *pError){ struct timespec timeout = {1, 0}; /* 1 sec timeout */ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); memset(pHostID, 0, PROXY_HOSTIDLEN); #if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\ && __MAC_OS_X_VERSION_MIN_REQUIRED<1050 if( gethostuuid(pHostID, &timeout) ){ int err = errno; if( pError ){ *pError = err; } return SQLITE_IOERR; } #endif #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ if( sqlite3_hostid_num != 0){ pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); } #endif |
︙ | ︙ |
Changes to src/os_win.c.
︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } #ifndef SQLITE_OMIT_WAL /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function winShmMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() | > > > > > > > > | 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 | static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; } #ifndef SQLITE_OMIT_WAL /* ** Windows will only let you create file view mappings ** on allocation size granularity boundaries. ** During sqlite3_os_init() we do a GetSystemInfo() ** to get the granularity size. */ SYSTEM_INFO winSysInfo; /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the winLockInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function winShmMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() |
︙ | ︙ | |||
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 | ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode **pp; winShmNode *p; assert( winShmMutexHeld() ); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ | > | > > > | > > > | 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | ** ** This is not a VFS shared-memory method; it is a utility function called ** by VFS shared-memory methods. */ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ winShmNode **pp; winShmNode *p; BOOL bRc; assert( winShmMutexHeld() ); pp = &winShmNodeList; while( (p = *pp)!=0 ){ if( p->nRef==0 ){ int i; if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ bRc = UnmapViewOfFile(p->aRegion[i].pMap); OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", (int)GetCurrentProcessId(), i, bRc ? "ok" : "failed")); bRc = CloseHandle(p->aRegion[i].hMap); OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", (int)GetCurrentProcessId(), i, bRc ? "ok" : "failed")); } if( p->hFile.h != INVALID_HANDLE_VALUE ){ SimulateIOErrorBenign(1); winClose((sqlite3_file *)&p->hFile); SimulateIOErrorBenign(0); } if( deleteFlag ){ |
︙ | ︙ | |||
1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 | winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ | > | | 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 | winShmNodeList = pShmNode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, /* Name of the file (UTF-8) */ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ 0); if( SQLITE_OK!=rc ){ rc = SQLITE_CANTOPEN_BKPT; goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. |
︙ | ︙ | |||
1772 1773 1774 1775 1776 1777 1778 1779 1780 | while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); if( hMap ){ pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, | > > > > > | > > > > > | | 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 | while( pShmNode->nRegion<=iRegion ){ HANDLE hMap; /* file-mapping handle */ void *pMap = 0; /* Mapped memory region */ hMap = CreateFileMapping(pShmNode->hFile.h, NULL, PAGE_READWRITE, 0, nByte, NULL ); OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, nByte, hMap ? "ok" : "failed")); if( hMap ){ int iOffset = pShmNode->nRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, iOffset - iOffsetShift, szRegion + iOffsetShift ); OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, pMap ? "ok" : "failed")); } if( !pMap ){ pShmNode->lastErrno = GetLastError(); rc = SQLITE_IOERR; if( hMap ) CloseHandle(hMap); goto shmpage_out; } pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; pShmNode->nRegion++; } } shmpage_out: if( pShmNode->nRegion>iRegion ){ int iOffset = iRegion*szRegion; int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; char *p = (char *)pShmNode->aRegion[iRegion].pMap; *pp = (void *)&p[iOffsetShift]; }else{ *pp = 0; } sqlite3_mutex_leave(pShmNode->mutex); return rc; } |
︙ | ︙ | |||
2020 2021 2022 2023 2024 2025 2026 | DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; #if SQLITE_OS_WINCE int isTemp = 0; #endif winFile *pFile = (winFile*)id; | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > < | | > | > > | 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 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 | DWORD dwShareMode; DWORD dwCreationDisposition; DWORD dwFlagsAndAttributes = 0; #if SQLITE_OS_WINCE int isTemp = 0; #endif winFile *pFile = (winFile*)id; void *zConverted; /* Filename in OS encoding */ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0xFFFFFF00; /* Type of file to open */ #endif int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); #ifndef NDEBUG int isReadonly = (flags & SQLITE_OPEN_READONLY); #endif int isReadWrite = (flags & SQLITE_OPEN_READWRITE); #ifndef NDEBUG int isOpenJournal = (isCreate && ( eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); #endif /* Check the following statements are true: ** ** (a) Exactly one of the READWRITE and READONLY flags must be set, and ** (b) if CREATE is set, then READWRITE must also be set, and ** (c) if EXCLUSIVE is set, then CREATE must also be set. ** (d) if DELETEONCLOSE is set, then CREATE must also be set. */ assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); assert(isCreate==0 || isReadWrite); assert(isExclusive==0 || isCreate); assert(isDelete==0 || isCreate); /* The main DB, main journal, WAL file and master journal are never ** automatically deleted. Nor are they ever temporary files. */ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL ); assert( id!=0 ); UNUSED_PARAMETER(pVfs); pFile->h = INVALID_HANDLE_VALUE; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ assert(isDelete && !isOpenJournal); rc = getTempname(MAX_PATH+1, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zUtf8Name = zTmpname; } /* Convert the filename to the system encoding. */ zConverted = convertUtf8Filename(zUtf8Name); if( zConverted==0 ){ return SQLITE_NOMEM; } if( isReadWrite ){ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; }else{ dwDesiredAccess = GENERIC_READ; } /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is ** created. SQLite doesn't use it to indicate "exclusive access" ** as it is usually understood. */ if( isExclusive ){ /* Creates a new file, only if it does not already exist. */ /* If the file exists, it fails. */ dwCreationDisposition = CREATE_NEW; }else if( isCreate ){ /* Open existing file, or create if it doesn't exist */ dwCreationDisposition = OPEN_ALWAYS; }else{ /* Opens a file, only if it exists. */ dwCreationDisposition = OPEN_EXISTING; } dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if( isDelete ){ #if SQLITE_OS_WINCE dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; isTemp = 1; #else dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE; #endif }else{ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; } /* Reports from the internet are that performance is always ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ #if SQLITE_OS_WINCE dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; #endif if( isNT() ){ h = CreateFileW((WCHAR*)zConverted, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, |
︙ | ︙ | |||
2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); free(zConverted); | > > | | > | > > < | > | | 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 | NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL ); #endif } OSTRACE(("OPEN %d %s 0x%lx %s\n", h, zName, dwDesiredAccess, h==INVALID_HANDLE_VALUE ? "failed" : "ok")); if( h==INVALID_HANDLE_VALUE ){ pFile->lastErrno = GetLastError(); free(zConverted); if( isReadWrite ){ return winOpen(pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); }else{ return SQLITE_CANTOPEN_BKPT; } } if( pOutFlags ){ if( isReadWrite ){ *pOutFlags = SQLITE_OPEN_READWRITE; }else{ *pOutFlags = SQLITE_OPEN_READONLY; } } memset(pFile, 0, sizeof(*pFile)); pFile->pMethod = &winIoMethod; pFile->h = h; pFile->lastErrno = NO_ERROR; pFile->pVfs = pVfs; pFile->pShm = 0; pFile->zPath = zName; pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); #if SQLITE_OS_WINCE if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB && !winceCreateLock(zName, pFile) ){ CloseHandle(h); free(zConverted); return SQLITE_CANTOPEN_BKPT; } if( isTemp ){ pFile->zDeleteOnClose = zConverted; }else #endif { free(zConverted); } OpenCounter(+1); return rc; } /* ** Delete the named file. ** ** Note that windows does not allow a file to be deleted if some other ** process has it open. Sometimes a virus scanner or indexing program |
︙ | ︙ | |||
2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 | winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_WIN */ | > > > > > > > | 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 | winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; #ifndef SQLITE_OMIT_WAL /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); GetSystemInfo(&winSysInfo); assert(winSysInfo.dwAllocationGranularity > 0); #endif sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } int sqlite3_os_end(void){ return SQLITE_OK; } #endif /* SQLITE_OS_WIN */ |
Changes to src/pager.c.
︙ | ︙ | |||
611 612 613 614 615 616 617 | sqlite3_vfs *pVfs; /* OS functions to use for IO */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 useJournal; /* Use a rollback journal on this file */ u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ | > | | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 | sqlite3_vfs *pVfs; /* OS functions to use for IO */ u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */ u8 useJournal; /* Use a rollback journal on this file */ u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */ u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ /************************************************************************** ** The following block contains those class members that change during ** routine opertion. Class members not in this block are either fixed |
︙ | ︙ | |||
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); break; } return 1; } /* ** Return a pointer to a human readable string in a static buffer ** containing the state of the Pager object passed as an argument. This ** is intended to be used within debuggers. For example, as an alternative ** to "print *pPager" in gdb: ** ** (gdb) printf "%s", print_pager_state(pPager) | > > | 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 | assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); break; } return 1; } #endif /* ifndef NDEBUG */ #ifdef SQLITE_DEBUG /* ** Return a pointer to a human readable string in a static buffer ** containing the state of the Pager object passed as an argument. This ** is intended to be used within debuggers. For example, as an alternative ** to "print *pPager" in gdb: ** ** (gdb) printf "%s", print_pager_state(pPager) |
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; | | | 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode || pPager->eLock==eLock ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; |
︙ | ︙ | |||
1293 1294 1295 1296 1297 1298 1299 | if( doTruncate || iLimit==0 ){ rc = sqlite3OsTruncate(pPager->jfd, 0); }else{ static const char zeroHdr[28] = {0}; rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); } if( rc==SQLITE_OK && !pPager->noSync ){ | | | 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 | if( doTruncate || iLimit==0 ){ rc = sqlite3OsTruncate(pPager->jfd, 0); }else{ static const char zeroHdr[28] = {0}; rc = sqlite3OsWrite(pPager->jfd, zeroHdr, sizeof(zeroHdr), 0); } if( rc==SQLITE_OK && !pPager->noSync ){ rc = sqlite3OsSync(pPager->jfd, SQLITE_SYNC_DATAONLY|pPager->syncFlags); } /* At this point the transaction is committed but the write lock ** is still held on the file. If there is a size limit configured for ** the persistent journal and the journal file currently consumes more ** space than that limit allows for, truncate it now. There is no need ** to sync the file following this operation. |
︙ | ︙ | |||
2745 2746 2747 2748 2749 2750 2751 | zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && !pPager->noSync && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ | | | 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 | zMaster = pPager->pTmpSpace; rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && !pPager->noSync && (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN) ){ rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); } if( rc==SQLITE_OK ){ rc = pager_end_transaction(pPager, zMaster[0]!='\0'); testcase( rc!=SQLITE_OK ); } if( rc==SQLITE_OK && zMaster[0] && res ){ /* If there was a master journal and this routine will return success, |
︙ | ︙ | |||
2919 2920 2921 2922 2923 2924 2925 | ** changed. */ static int pagerWalFrames( Pager *pPager, /* Pager object */ PgHdr *pList, /* List of frames to log */ Pgno nTruncate, /* Database size after this commit */ int isCommit, /* True if this is a commit */ | | | | 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 | ** changed. */ static int pagerWalFrames( Pager *pPager, /* Pager object */ PgHdr *pList, /* List of frames to log */ Pgno nTruncate, /* Database size after this commit */ int isCommit, /* True if this is a commit */ int syncFlags /* Flags to pass to OsSync() (or 0) */ ){ int rc; /* Return code */ assert( pPager->pWal ); rc = sqlite3WalFrames(pPager->pWal, pPager->pageSize, pList, nTruncate, isCommit, syncFlags ); if( rc==SQLITE_OK && pPager->pBackup ){ PgHdr *p; for(p=pList; p; p=p->pDirty){ sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData); } } |
︙ | ︙ | |||
2967 2968 2969 2970 2971 2972 2973 | ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager->pWal); rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); | | > | 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 | ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager->pWal); rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed); if( rc!=SQLITE_OK || changed ){ pager_reset(pPager); } return rc; } #endif /* ** This function is called as part of the transition from PAGER_OPEN ** to PAGER_READER state to determine the size of the database file ** in pages (assuming the page size currently stored in Pager.pageSize). ** ** If no error occurs, SQLITE_OK is returned and the size of the database |
︙ | ︙ | |||
3029 3030 3031 3032 3033 3034 3035 | pPager->mxPgno = (Pgno)nPage; } *pnPage = nPage; return SQLITE_OK; } | | | 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 | pPager->mxPgno = (Pgno)nPage; } *pnPage = nPage; return SQLITE_OK; } #ifndef SQLITE_OMIT_WAL /* ** Check if the *-wal file that corresponds to the database opened by pPager ** exists if the database is not empy, or verify that the *-wal file does ** not exist (by deleting it) if the database file is empty. ** ** If the database is not empty and the *-wal file exists, open the pager ** in WAL mode. If the database is empty or if no *-wal file exists and |
︙ | ︙ | |||
3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 | ** FULL The journal is synced twice before writes begin on the ** database (with some additional information - the nRec field ** of the journal header - being written in between the two ** syncs). If we assume that writing a ** single disk sector is atomic, then this mode provides ** assurance that the journal will not be corrupted to the ** point of causing damage to the database during rollback. ** ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS | > > > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > > > > > > | 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 | ** FULL The journal is synced twice before writes begin on the ** database (with some additional information - the nRec field ** of the journal header - being written in between the two ** syncs). If we assume that writing a ** single disk sector is atomic, then this mode provides ** assurance that the journal will not be corrupted to the ** point of causing damage to the database during rollback. ** ** The above is for a rollback-journal mode. For WAL mode, OFF continues ** to mean that no syncs ever occur. NORMAL means that the WAL is synced ** prior to the start of checkpoint and that the database file is synced ** at the conclusion of the checkpoint if the entire content of the WAL ** was written back into the database. But no sync operations occur for ** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL ** file is synced following each commit operation, in addition to the ** syncs associated with NORMAL. ** ** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The ** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync ** using fcntl(F_FULLFSYNC). SQLITE_SYNC_NORMAL means to do an ** ordinary fsync() call. There is no difference between SQLITE_SYNC_FULL ** and SQLITE_SYNC_NORMAL on platforms other than MacOSX. But the ** synchronous=FULL versus synchronous=NORMAL setting determines when ** the xSync primitive is called and is relevant to all platforms. ** ** Numeric values associated with these states are OFF==1, NORMAL=2, ** and FULL=3. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS void sqlite3PagerSetSafetyLevel( Pager *pPager, /* The pager to set safety level for */ int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */ int bFullFsync, /* PRAGMA fullfsync */ int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */ ){ assert( level>=1 && level<=3 ); pPager->noSync = (level==1 || pPager->tempFile) ?1:0; pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0; if( pPager->noSync ){ pPager->syncFlags = 0; pPager->ckptSyncFlags = 0; }else if( bFullFsync ){ pPager->syncFlags = SQLITE_SYNC_FULL; pPager->ckptSyncFlags = SQLITE_SYNC_FULL; }else if( bCkptFullFsync ){ pPager->syncFlags = SQLITE_SYNC_NORMAL; pPager->ckptSyncFlags = SQLITE_SYNC_FULL; }else{ pPager->syncFlags = SQLITE_SYNC_NORMAL; pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL; } } #endif /* ** The following global variable is incremented whenever the library ** attempts to open a temporary file. This information is used for ** testing and analysis only. |
︙ | ︙ | |||
3647 3648 3649 3650 3651 3652 3653 | u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL | | < < < | 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 | u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp); pPager->pWal = 0; #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. |
︙ | ︙ | |||
3816 3817 3818 3819 3820 3821 3822 | ** for garbage data to be appended to the file, the nRec field ** is populated with 0xFFFFFFFF when the journal header is written ** and never needs to be updated. */ if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) | | | | | 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 | ** for garbage data to be appended to the file, the nRec field ** is populated with 0xFFFFFFFF when the journal header is written ** and never needs to be updated. */ if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); if( rc!=SQLITE_OK ) return rc; } IOTRACE(("JHDR %p %lld\n", pPager, pPager->journalHdr)); rc = sqlite3OsWrite( pPager->jfd, zHeader, sizeof(zHeader), pPager->journalHdr ); if( rc!=SQLITE_OK ) return rc; } if( 0==(iDc&SQLITE_IOCAP_SEQUENTIAL) ){ PAGERTRACE(("SYNC journal of %d\n", PAGERID(pPager))); IOTRACE(("JSYNC %p\n", pPager)) rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags| (pPager->syncFlags==SQLITE_SYNC_FULL?SQLITE_SYNC_DATAONLY:0) ); if( rc!=SQLITE_OK ) return rc; } pPager->journalHdr = pPager->journalOff; if( newHdr && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){ pPager->nRec = 0; |
︙ | ︙ | |||
4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 | journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); }else{ journalFileSize = ROUND8(sqlite3MemJournalSize()); } /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; /* Compute and store the full pathname in an allocated buffer pointed ** to by zPathname, length nPathname. Or, if this is a temporary file, ** leave both nPathname and zPathname set to 0. */ if( zFilename && zFilename[0] ){ nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ return SQLITE_NOMEM; } | > > > > > > > < < < < < < < | | < < | 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 | journalFileSize = ROUND8(sqlite3JournalSize(pVfs)); }else{ journalFileSize = ROUND8(sqlite3MemJournalSize()); } /* Set the output variable to NULL in case an error occurs. */ *ppPager = 0; #ifndef SQLITE_OMIT_MEMORYDB if( flags & PAGER_MEMORY ){ memDb = 1; zFilename = 0; } #endif /* Compute and store the full pathname in an allocated buffer pointed ** to by zPathname, length nPathname. Or, if this is a temporary file, ** leave both nPathname and zPathname set to 0. */ if( zFilename && zFilename[0] ){ nPathname = pVfs->mxPathname+1; zPathname = sqlite3Malloc(nPathname*2); if( zPathname==0 ){ return SQLITE_NOMEM; } zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */ rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname); nPathname = sqlite3Strlen30(zPathname); if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){ /* This branch is taken when the journal path required by ** the database being opened will be more than pVfs->mxPathname ** bytes in length. This means the database cannot be opened, ** as it will not be possible to open the journal file or even ** check for a hot-journal before reading. |
︙ | ︙ | |||
4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 | pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); | > < < < < | | | < | > | 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 | pPager->sjfd = (sqlite3_file*)(pPtr += ROUND8(pVfs->szOsFile)); pPager->jfd = (sqlite3_file*)(pPtr += journalFileSize); pPager->zFilename = (char*)(pPtr += journalFileSize); assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) ); /* Fill in the Pager.zFilename and Pager.zJournal buffers, if required. */ if( zPathname ){ assert( nPathname>0 ); pPager->zJournal = (char*)(pPtr += nPathname + 1); memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); #ifndef SQLITE_OMIT_WAL pPager->zWal = &pPager->zJournal[nPathname+8+1]; memcpy(pPager->zWal, zPathname, nPathname); memcpy(&pPager->zWal[nPathname], "-wal", 4); #endif sqlite3_free(zPathname); } pPager->pVfs = pVfs; pPager->vfsFlags = vfsFlags; /* Open the pager file. */ if( zFilename && zFilename[0] ){ int fout = 0; /* VFS flags returned by xOpen() */ rc = sqlite3OsOpen(pVfs, pPager->zFilename, pPager->fd, vfsFlags, &fout); assert( !memDb ); readOnly = (fout&SQLITE_OPEN_READONLY); /* If the file was successfully opened for read/write access, ** choose a default page size in case we have to create the ** database file. The default page size is the maximum of: ** ** + SQLITE_DEFAULT_PAGE_SIZE, |
︙ | ︙ | |||
4424 4425 4426 4427 4428 4429 4430 | pPager->exclusiveMode = (u8)tempFile; pPager->changeCountDone = pPager->tempFile; pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; | | > | 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 | pPager->exclusiveMode = (u8)tempFile; pPager->changeCountDone = pPager->tempFile; pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; assert( useJournal || pPager->tempFile ); pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL; pPager->ckptSyncFlags = pPager->syncFlags; /* pPager->pFirst = 0; */ /* pPager->pFirstSynced = 0; */ /* pPager->pLast = 0; */ pPager->nExtra = (u16)nExtra; pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; assert( isOpen(pPager->fd) || tempFile ); setSectorSize(pPager); |
︙ | ︙ | |||
4524 4525 4526 4527 4528 4529 4530 | */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); | | | 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 | */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK); } sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, |
︙ | ︙ | |||
4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 | } } /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); assert( pPager->pWal==0 || rc==SQLITE_OK ); } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } | > > | 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 | } } /* If there is a WAL file in the file-system, open this database in WAL ** mode. Otherwise, the following function call is a no-op. */ rc = pagerOpenWalIfPresent(pPager); #ifndef SQLITE_OMIT_WAL assert( pPager->pWal==0 || rc==SQLITE_OK ); #endif } if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); } |
︙ | ︙ | |||
5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 | if( NEVER(pPager->errCode) ) return pPager->errCode; /* Higher-level routines never call this function if database is not ** writable. But check anyway, just for robustness. */ if( NEVER(pPager->readOnly) ) return SQLITE_PERM; CHECK_PAGE(pPg); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ assert( !pagerUseWal(pPager) ); | > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < | 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 | if( NEVER(pPager->errCode) ) return pPager->errCode; /* Higher-level routines never call this function if database is not ** writable. But check anyway, just for robustness. */ if( NEVER(pPager->readOnly) ) return SQLITE_PERM; CHECK_PAGE(pPg); /* The journal file needs to be opened. Higher level routines have already ** obtained the necessary locks to begin the write-transaction, but the ** rollback journal might not yet be open. Open it now if this is the case. ** ** This is done before calling sqlite3PcacheMakeDirty() on the page. ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then ** an error might occur and the pager would end up in WRITER_LOCKED state ** with pages marked as dirty in the cache. */ if( pPager->eState==PAGER_WRITER_LOCKED ){ rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } assert( pPager->eState>=PAGER_WRITER_CACHEMOD ); assert( assert_pager_state(pPager) ); /* Mark the page as dirty. If the page has already been written ** to the journal then we can return right away. */ sqlite3PcacheMakeDirty(pPg); if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){ assert( !pagerUseWal(pPager) ); }else{ /* The transaction journal now exists and we have a RESERVED or an ** EXCLUSIVE lock on the main database file. Write the current page to ** the transaction journal if it is not there already. */ if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){ assert( pagerUseWal(pPager)==0 ); |
︙ | ︙ | |||
5545 5546 5547 5548 5549 5550 5551 | /* Release the page reference. */ sqlite3PagerUnref(pPgHdr); } return rc; } /* | | | | | 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 | /* Release the page reference. */ sqlite3PagerUnref(pPgHdr); } return rc; } /* ** Sync the database file to disk. This is a no-op for in-memory databases ** or pages with the Pager.noSync flag set. ** ** If successful, or if called on a pager for which it is a no-op, this ** function returns SQLITE_OK. Otherwise, an IO error code is returned. */ int sqlite3PagerSync(Pager *pPager){ int rc; /* Return code */ assert( !MEMDB ); if( pPager->noSync ){ rc = SQLITE_OK; }else{ rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); } return rc; } /* ** This function may only be called while a write-transaction is active in ** rollback. If the connection is in WAL mode, this call is a no-op. |
︙ | ︙ | |||
5646 5647 5648 5649 5650 5651 5652 | */ sqlite3BackupRestart(pPager->pBackup); }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); if( pList ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, | | | 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 | */ sqlite3BackupRestart(pPager->pBackup); }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); if( pList ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, (pPager->fullSync ? pPager->syncFlags : 0) ); } if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } }else{ /* The following block updates the change-counter. Exactly how it |
︙ | ︙ | |||
5777 5778 5779 5780 5781 5782 5783 | assert( pPager->eState==PAGER_WRITER_DBMOD ); rc = pager_truncate(pPager, nNew); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } /* Finally, sync the database file. */ if( !pPager->noSync && !noSync ){ | | | 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 | assert( pPager->eState==PAGER_WRITER_DBMOD ); rc = pager_truncate(pPager, nNew); if( rc!=SQLITE_OK ) goto commit_phase_one_exit; } /* Finally, sync the database file. */ if( !pPager->noSync && !noSync ){ rc = sqlite3OsSync(pPager->fd, pPager->syncFlags); } IOTRACE(("DBSYNC %p\n", pPager)) } } commit_phase_one_exit: if( rc==SQLITE_OK && !pagerUseWal(pPager) ){ |
︙ | ︙ | |||
6348 6349 6350 6351 6352 6353 6354 | */ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); | > | | 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 | */ int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); assert( pPager->exclusiveMode || 0==sqlite3WalHeapMemory(pPager->pWal) ); if( eMode>=0 && !pPager->tempFile && !sqlite3WalHeapMemory(pPager->pWal) ){ pPager->exclusiveMode = (u8)eMode; } return (int)pPager->exclusiveMode; } /* ** Set the journal-mode for this pager. Parameter eMode must be one of: |
︙ | ︙ | |||
6517 6518 6519 6520 6521 6522 6523 | /* ** This function is called when the user invokes "PRAGMA checkpoint". */ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; | | < | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 6635 | /* ** This function is called when the user invokes "PRAGMA checkpoint". */ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, zBuf); } return rc; } int sqlite3PagerWalCallback(Pager *pPager){ return sqlite3WalCallback(pPager->pWal); } /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap); } /* ** Attempt to take an exclusive lock on the database file. If a PENDING lock ** is obtained instead, immediately release it. */ static int pagerExclusiveLock(Pager *pPager){ int rc; /* Return code */ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK ); rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ /* If the attempt to grab the pending lock failed, release the ** exclusive lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } return rc; } /* ** Call sqlite3WalOpen() to open the WAL handle. If the pager is in ** exclusive-locking mode when this function is called, take an EXCLUSIVE ** lock on the database file and use heap-memory to store the wal-index ** in. Otherwise, use the normal shared-memory. */ static int pagerOpenWal(Pager *pPager){ int rc = SQLITE_OK; assert( pPager->pWal==0 && pPager->tempFile==0 ); assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock); /* If the pager is already in exclusive-mode, the WAL module will use ** heap-memory for the wal-index instead of the VFS shared-memory ** implementation. Take the exclusive lock now, before opening the WAL ** file, to make sure this is safe. */ if( pPager->exclusiveMode ){ rc = pagerExclusiveLock(pPager); } /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), return an error code. */ if( rc==SQLITE_OK ){ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, pPager->exclusiveMode, &pPager->pWal ); } return rc; } /* ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file |
︙ | ︙ | |||
6571 6572 6573 6574 6575 6576 6577 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); | < < < < | | 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); rc = pagerOpenWal(pPager); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; pPager->eState = PAGER_OPEN; } }else{ *pbOpen = 1; } |
︙ | ︙ | |||
6614 6615 6616 6617 6618 6619 6620 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ | < | | | < | < < < < < | 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713 6714 6715 6716 6717 6718 6719 6720 6721 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ rc = pagerOpenWal(pPager); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerExclusiveLock(pPager); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, (u8*)pPager->pTmpSpace); pPager->pWal = 0; } } return rc; } #ifdef SQLITE_HAS_CODEC /* |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 | > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ #define PAGER_MEMORY 0x0004 /* In-memory database */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 |
︙ | ︙ | |||
98 99 100 101 102 103 104 | int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); | | | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); int sqlite3PagerSetPagesize(Pager*, u32*, int); int sqlite3PagerMaxPageCount(Pager*, int); void sqlite3PagerSetCachesize(Pager*, int); void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); int sqlite3PagerLockingMode(Pager *, int); int sqlite3PagerSetJournalMode(Pager *, int); int sqlite3PagerGetJournalMode(Pager*); int sqlite3PagerOkToChangeJournalMode(Pager*); i64 sqlite3PagerJournalSizeLimit(Pager *, i64); sqlite3_backup **sqlite3PagerBackupPtr(Pager*); |
︙ | ︙ |
Changes to src/pcache.c.
︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe. */ int sqlite3PcacheInitialize(void){ if( sqlite3GlobalConfig.pcache.xInit==0 ){ sqlite3PCacheSetDefault(); } return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); } void sqlite3PcacheShutdown(void){ if( sqlite3GlobalConfig.pcache.xShutdown ){ sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); } } /* ** Return the size in bytes of a PCache object. */ | > > > > | 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /*************************************************** General Interfaces ****** ** ** Initialize and shutdown the page cache subsystem. Neither of these ** functions are threadsafe. */ int sqlite3PcacheInitialize(void){ if( sqlite3GlobalConfig.pcache.xInit==0 ){ /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache. */ sqlite3PCacheSetDefault(); } return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); } void sqlite3PcacheShutdown(void){ if( sqlite3GlobalConfig.pcache.xShutdown ){ /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); } } /* ** Return the size in bytes of a PCache object. */ |
︙ | ︙ |
Changes to src/pcache1.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "sqliteInt.h" typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; | > > > > > | | | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include "sqliteInt.h" typedef struct PCache1 PCache1; typedef struct PgHdr1 PgHdr1; typedef struct PgFreeslot PgFreeslot; /* Each page cache is an instance of the following object. Every ** open database file (including each in-memory database and each ** temporary or transient database) has a single page cache which ** is an instance of this object. ** ** Pointers to structures of this type are cast and returned as ** opaque sqlite3_pcache* handles. */ struct PCache1 { /* Cache configuration parameters. Page size (szPage) and the purgeable ** flag (bPurgeable) are set when the cache is created. nMax may be ** modified at any time by a call to the pcache1CacheSize() method. ** The global mutex must be held when accessing nMax. */ |
︙ | ︙ | |||
80 81 82 83 84 85 86 87 88 89 90 91 92 93 | int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ int isInit; /* True if initialized */ } pcache1_g; /* ** All code in this file should access the global structure above via the | > > > | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | int nMaxPage; /* Sum of nMaxPage for purgeable caches */ int nMinPage; /* Sum of nMinPage for purgeable caches */ int nCurrentPage; /* Number of purgeable pages allocated */ PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. */ int szSlot; /* Size of each free slot */ int nSlot; /* The number of pcache slots */ int nFreeSlot; /* Number of unused pcache slots */ int nReserve; /* Try to keep nFreeSlot above this */ void *pStart, *pEnd; /* Bounds of pagecache malloc range */ PgFreeslot *pFree; /* Free page blocks */ int isInit; /* True if initialized */ } pcache1_g; /* ** All code in this file should access the global structure above via the |
︙ | ︙ | |||
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | ** enough to contain 'n' buffers of 'sz' bytes each. */ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.pStart = pBuf; pcache1.pFree = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; pcache1.pFree = p; pBuf = (void*)&((char*)pBuf)[sz]; | > > | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | ** enough to contain 'n' buffers of 'sz' bytes each. */ void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ if( pcache1.isInit ){ PgFreeslot *p; sz = ROUNDDOWN8(sz); pcache1.szSlot = sz; pcache1.nSlot = pcache1.nFreeSlot = n; pcache1.nReserve = n>90 ? 10 : (n/10 + 1); pcache1.pStart = pBuf; pcache1.pFree = 0; while( n-- ){ p = (PgFreeslot*)pBuf; p->pNext = pcache1.pFree; pcache1.pFree = p; pBuf = (void*)&((char*)pBuf)[sz]; |
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | void *p; assert( sqlite3_mutex_held(pcache1.mutex) ); sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot && pcache1.pFree ){ assert( pcache1.isInit ); p = (PgHdr1 *)pcache1.pFree; pcache1.pFree = pcache1.pFree->pNext; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); }else{ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the ** global pcache mutex and unlock the pager-cache object pCache. This is ** so that if the attempt to allocate a new buffer causes the the ** configured soft-heap-limit to be breached, it will be possible to | > > | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | void *p; assert( sqlite3_mutex_held(pcache1.mutex) ); sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte); if( nByte<=pcache1.szSlot && pcache1.pFree ){ assert( pcache1.isInit ); p = (PgHdr1 *)pcache1.pFree; pcache1.pFree = pcache1.pFree->pNext; pcache1.nFreeSlot--; assert( pcache1.nFreeSlot>=0 ); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1); }else{ /* Allocate a new buffer using sqlite3Malloc. Before doing so, exit the ** global pcache mutex and unlock the pager-cache object pCache. This is ** so that if the attempt to allocate a new buffer causes the the ** configured soft-heap-limit to be breached, it will be possible to |
︙ | ︙ | |||
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* | > > | | 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 | if( p==0 ) return; if( p>=pcache1.pStart && p<pcache1.pEnd ){ PgFreeslot *pSlot; sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1); pSlot = (PgFreeslot*)p; pSlot->pNext = pcache1.pFree; pcache1.pFree = pSlot; pcache1.nFreeSlot++; assert( pcache1.nFreeSlot<=pcache1.nSlot ); }else{ int iSize; assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); sqlite3MemdebugSetType(p, MEMTYPE_HEAP); iSize = sqlite3MallocSize(p); sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize); sqlite3_free(p); } } #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* ** Return the size of a pcache allocation */ static int pcache1MemSize(void *p){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( p>=pcache1.pStart && p<pcache1.pEnd ){ return pcache1.szSlot; }else{ int iSize; |
︙ | ︙ | |||
271 272 273 274 275 276 277 278 279 280 281 282 283 284 | */ void sqlite3PageFree(void *p){ pcache1EnterMutex(); pcache1Free(p); pcache1LeaveMutex(); } /******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** | > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | */ void sqlite3PageFree(void *p){ pcache1EnterMutex(); pcache1Free(p); pcache1LeaveMutex(); } /* ** Return true if it desirable to avoid allocating a new page cache ** entry. ** ** If memory was allocated specifically to the page cache using ** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then ** it is desirable to avoid allocating a new page cache entry because ** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient ** for all page cache needs and we should not need to spill the ** allocation onto the heap. ** ** Or, the heap is used for all page cache memory put the heap is ** under memory pressure, then again it is desirable to avoid ** allocating a new page cache entry in order to avoid stressing ** the heap even further. */ static int pcache1UnderMemoryPressure(PCache1 *pCache){ assert( sqlite3_mutex_held(pcache1.mutex) ); if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){ return pcache1.nFreeSlot<pcache1.nReserve; }else{ return sqlite3HeapNearlyFull(); } } /******************************************************************************/ /******** General Implementation Functions ************************************/ /* ** This function is used to resize the hash table used by the cache passed ** as the first argument. ** |
︙ | ︙ | |||
512 513 514 515 516 517 518 | ** ** 1. Regardless of the value of createFlag, the cache is searched for a ** copy of the requested page. If one is found, it is returned. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** | | | > > | > > > | 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 | ** ** 1. Regardless of the value of createFlag, the cache is searched for a ** copy of the requested page. If one is found, it is returned. ** ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** ** 3. If createFlag is 1, and the page is not already in the cache, then ** return NULL (do not allocate a new page) if any of the following ** conditions are true: ** ** (a) the number of pages pinned by the cache is greater than ** PCache1.nMax, or ** ** (b) the number of pages pinned by the cache is greater than ** the sum of nMax for all purgeable caches, less the sum of ** nMin for all other purgeable caches, or ** ** 4. If none of the first three conditions apply and the cache is marked ** as purgeable, and if one of the following is true: ** ** (a) The number of pages allocated for the cache is already ** PCache1.nMax, or ** ** (b) The number of pages allocated for all purgeable caches is ** already equal to or greater than the sum of nMax for all ** purgeable caches, ** ** (c) The system is under memory pressure and wants to avoid ** unnecessary pages cache entry allocations ** ** then attempt to recycle a page from the LRU list. If it is the right ** size, return the recycled buffer. Otherwise, free the buffer and ** proceed to step 5. ** ** 5. Otherwise, allocate and return a new page buffer. */ |
︙ | ︙ | |||
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 | } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) )){ goto fetch_out; } if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if( pCache->bPurgeable && pcache1.pLruTail && ( | > | > > | 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 | } /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) || pcache1UnderMemoryPressure(pCache) )){ goto fetch_out; } if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){ goto fetch_out; } /* Step 4. Try to recycle a page buffer if appropriate. */ if( pCache->bPurgeable && pcache1.pLruTail && ( (pCache->nPage+1>=pCache->nMax) || pcache1.nCurrentPage>=pcache1.nMaxPage || pcache1UnderMemoryPressure(pCache) )){ pPage = pcache1.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); if( pPage->pCache->szPage!=pCache->szPage ){ pcache1FreePage(pPage); pPage = 0; |
︙ | ︙ | |||
715 716 717 718 719 720 721 722 723 724 725 726 727 728 | /* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; pcache1EnterMutex(); pcache1TruncateUnsafe(pCache, 0); pcache1.nMaxPage -= pCache->nMax; pcache1.nMinPage -= pCache->nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); sqlite3_free(pCache->apHash); | > | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 | /* ** Implementation of the sqlite3_pcache.xDestroy method. ** ** Destroy a cache allocated using pcache1Create(). */ static void pcache1Destroy(sqlite3_pcache *p){ PCache1 *pCache = (PCache1 *)p; assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); pcache1EnterMutex(); pcache1TruncateUnsafe(pCache, 0); pcache1.nMaxPage -= pCache->nMax; pcache1.nMinPage -= pCache->nMin; pcache1EnforceMaxPage(); pcache1LeaveMutex(); sqlite3_free(pCache->apHash); |
︙ | ︙ | |||
762 763 764 765 766 767 768 | ** of bytes of memory released. */ int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; if( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(); | | | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 | ** of bytes of memory released. */ int sqlite3PcacheReleaseMemory(int nReq){ int nFree = 0; if( pcache1.pStart==0 ){ PgHdr1 *p; pcache1EnterMutex(); while( (nReq<0 || nFree<nReq) && ((p=pcache1.pLruTail)!=0) ){ nFree += pcache1MemSize(PGHDR1_TO_PAGE(p)); pcache1PinPage(p); pcache1RemoveFromHash(p); pcache1FreePage(p); } pcache1LeaveMutex(); } |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 | } aPragma[] = { { "full_column_names", SQLITE_FullColNames }, { "short_column_names", SQLITE_ShortColNames }, { "count_changes", SQLITE_CountRows }, { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifndef SQLITE_OMIT_AUTOMATIC_INDEX { "automatic_index", SQLITE_AutoIndex }, #endif #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, | > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | } aPragma[] = { { "full_column_names", SQLITE_FullColNames }, { "short_column_names", SQLITE_ShortColNames }, { "count_changes", SQLITE_CountRows }, { "empty_result_callbacks", SQLITE_NullCallback }, { "legacy_file_format", SQLITE_LegacyFileFmt }, { "fullfsync", SQLITE_FullFSync }, { "checkpoint_fullfsync", SQLITE_CkptFullFSync }, { "reverse_unordered_selects", SQLITE_ReverseOrder }, #ifndef SQLITE_OMIT_AUTOMATIC_INDEX { "automatic_index", SQLITE_AutoIndex }, #endif #ifdef SQLITE_DEBUG { "sql_trace", SQLITE_SqlTrace }, { "vdbe_listing", SQLITE_VdbeListing }, |
︙ | ︙ | |||
584 585 586 587 588 589 590 | ** ** Get or set the size limit on rollback journal files. */ if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ | | | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | ** ** Get or set the size limit on rollback journal files. */ if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){ Pager *pPager = sqlite3BtreePager(pDb->pBt); i64 iLimit = -2; if( zRight ){ sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8); if( iLimit<-1 ) iLimit = -1; } iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit); returnSingleInt(pParse, "journal_size_limit", &iLimit); }else #endif /* SQLITE_OMIT_PAGER_PRAGMAS */ |
︙ | ︙ | |||
1505 1506 1507 1508 1509 1510 1511 | /* ** Reset the safety level, in case the fullfsync flag or synchronous ** setting changed. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS if( db->autoCommit ){ sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level, | | > | 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 | /* ** Reset the safety level, in case the fullfsync flag or synchronous ** setting changed. */ #ifndef SQLITE_OMIT_PAGER_PRAGMAS if( db->autoCommit ){ sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level, (db->flags&SQLITE_FullFSync)!=0, (db->flags&SQLITE_CkptFullFSync)!=0); } #endif pragma_out: sqlite3DbFree(db, zLeft); sqlite3DbFree(db, zRight); } #endif /* SQLITE_OMIT_PRAGMA */ |
Changes to src/prepare.c.
︙ | ︙ | |||
627 628 629 630 631 632 633 | } rc = pParse->rc; #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", | | | | | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | } rc = pParse->rc; #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", "selectid", "order", "from", "detail" }; int iFirst, mx; if( pParse->explain==2 ){ sqlite3VdbeSetNumCols(pParse->pVdbe, 4); iFirst = 8; mx = 12; }else{ sqlite3VdbeSetNumCols(pParse->pVdbe, 8); iFirst = 0; mx = 8; } for(i=iFirst; i<mx; i++){ sqlite3VdbeSetColName(pParse->pVdbe, i-iFirst, COLNAME_NAME, |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
438 439 440 441 442 443 444 | addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); sqlite3VdbeJumpHere(v, addr2); | < | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1); addr2 = sqlite3VdbeAddOp0(v, OP_Goto); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor); sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor); sqlite3VdbeJumpHere(v, addr2); } } /* ** Add code to implement the OFFSET */ static void codeOffset( |
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 | r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); } /* ** Generate an error message when a SELECT is used within a subexpression ** (example: "a IN (SELECT * FROM table)") but it has more than 1 result | > | | > > | 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 | r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); } #ifndef SQLITE_OMIT_SUBQUERY /* ** Generate an error message when a SELECT is used within a subexpression ** (example: "a IN (SELECT * FROM table)") but it has more than 1 result ** column. We do this in a subroutine because the error used to occur ** in multiple places. (The error only occurs in one place now, but we ** retain the subroutine to minimize code disruption.) */ static int checkForMultiColumnSelectError( Parse *pParse, /* Parse context. */ SelectDest *pDest, /* Destination of SELECT results */ int nExpr /* Number of result columns returned by SELECT */ ){ int eDest = pDest->eDest; if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){ sqlite3ErrorMsg(pParse, "only a single result allowed for " "a SELECT that is part of an expression"); return 1; }else{ return 0; } } #endif /* ** This routine generates the code for the inside of the inner loop ** of a SELECT. ** ** If srcTab and nColumn are both zero, then the pEList expressions ** are evaluated in order to get the data for this row. If nColumn>0 |
︙ | ︙ | |||
586 587 588 589 590 591 592 | assert( pEList->nExpr==nColumn ); codeDistinct(pParse, distinct, iContinue, nColumn, regResult); if( pOrderBy==0 ){ codeOffset(v, p, iContinue); } } | < < < < | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 | assert( pEList->nExpr==nColumn ); codeDistinct(pParse, distinct, iContinue, nColumn, regResult); if( pOrderBy==0 ){ codeOffset(v, p, iContinue); } } switch( eDest ){ /* In this mode, write each query result to the key of the temporary ** table iParm. */ #ifndef SQLITE_OMIT_COMPOUND_SELECT case SRT_Union: { int r1; |
︙ | ︙ | |||
718 719 720 721 722 723 724 | default: { assert( eDest==SRT_Discard ); break; } #endif } | | > > | < < | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 | default: { assert( eDest==SRT_Discard ); break; } #endif } /* Jump to the end of the loop if the LIMIT is reached. Except, if ** there is a sorter, in which case the sorter has already limited ** the output for us. */ if( pOrderBy==0 && p->iLimit ){ sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); } } /* ** Given an expression list, generate a KeyInfo structure that records ** the collating sequence for each expression in that expression list. |
︙ | ︙ | |||
769 770 771 772 773 774 775 776 777 778 779 780 781 782 | pInfo->aColl[i] = pColl; pInfo->aSortOrder[i] = pItem->sortOrder; } } return pInfo; } /* ** If the inner loop was generated using a non-null pOrderBy argument, ** then the results were placed in a sorter. After the loop is terminated ** we need to run the sorter and output the results. The following ** routine generates the code needed to do that. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | pInfo->aColl[i] = pColl; pInfo->aSortOrder[i] = pItem->sortOrder; } } return pInfo; } #ifndef SQLITE_OMIT_COMPOUND_SELECT /* ** Name of the connection operator, used for error messages. */ static const char *selectOpName(int id){ char *z; switch( id ){ case TK_ALL: z = "UNION ALL"; break; case TK_INTERSECT: z = "INTERSECT"; break; case TK_EXCEPT: z = "EXCEPT"; break; default: z = "UNION"; break; } return z; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ #ifndef SQLITE_OMIT_EXPLAIN /* ** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function ** is a no-op. Otherwise, it adds a single row of output to the EQP result, ** where the caption is of the form: ** ** "USE TEMP B-TREE FOR xxx" ** ** where xxx is one of "DISTINCT", "ORDER BY" or "GROUP BY". Exactly which ** is determined by the zUsage argument. */ static void explainTempTable(Parse *pParse, const char *zUsage){ if( pParse->explain==2 ){ Vdbe *v = pParse->pVdbe; char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } /* ** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function ** is a no-op. Otherwise, it adds a single row of output to the EQP result, ** where the caption is of one of the two forms: ** ** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)" ** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)" ** ** where iSub1 and iSub2 are the integers passed as the corresponding ** function parameters, and op is the text representation of the parameter ** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT, ** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is ** false, or the second form if it is true. */ static void explainComposite( Parse *pParse, /* Parse context */ int op, /* One of TK_UNION, TK_EXCEPT etc. */ int iSub1, /* Subquery id 1 */ int iSub2, /* Subquery id 2 */ int bUseTmp /* True if a temp table was used */ ){ assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL ); if( pParse->explain==2 ){ Vdbe *v = pParse->pVdbe; char *zMsg = sqlite3MPrintf( pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2, bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op) ); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } /* ** Assign expression b to lvalue a. A second, no-op, version of this macro ** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code ** in sqlite3Select() to assign values to structure member variables that ** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the ** code with #ifndef directives. */ # define explainSetInteger(a, b) a = b #else /* No-op versions of the explainXXX() functions and macros. */ # define explainTempTable(y,z) # define explainComposite(v,w,x,y,z) # define explainSetInteger(y,z) #endif /* ** If the inner loop was generated using a non-null pOrderBy argument, ** then the results were placed in a sorter. After the loop is terminated ** we need to run the sorter and output the results. The following ** routine generates the code needed to do that. */ |
︙ | ︙ | |||
857 858 859 860 861 862 863 | } break; } } sqlite3ReleaseTempReg(pParse, regRow); sqlite3ReleaseTempReg(pParse, regRowid); | < < < < | 937 938 939 940 941 942 943 944 945 946 947 948 949 950 | } break; } } sqlite3ReleaseTempReg(pParse, regRow); sqlite3ReleaseTempReg(pParse, regRowid); /* The bottom of the loop */ sqlite3VdbeResolveLabel(v, addrContinue); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); sqlite3VdbeResolveLabel(v, addrBreak); if( eDest==SRT_Output || eDest==SRT_Coroutine ){ sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0); |
︙ | ︙ | |||
1120 1121 1122 1123 1124 1125 1126 | sqlite3VdbeSetColName(v, i, COLNAME_NAME, sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); } | < < < < < < < < < < < < < < < < | 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 | sqlite3VdbeSetColName(v, i, COLNAME_NAME, sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); } /* ** Given a an expression list (which is really the list of expressions ** that form the result set of a SELECT statement) compute appropriate ** column names for a table that would hold the expression list. ** ** All column names will be unique. ** |
︙ | ︙ | |||
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 | return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bEnabled==0 ); pTab->nRef = 1; pTab->zName = 0; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ sqlite3DeleteTable(db, pTab); return 0; } | > | 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 | return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bEnabled==0 ); pTab->nRef = 1; pTab->zName = 0; pTab->nRowEst = 1000000; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ sqlite3DeleteTable(db, pTab); return 0; } |
︙ | ︙ | |||
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 | v = sqlite3GetVdbe(pParse); if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeComment((v, "LIMIT counter")); sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); } | > > | 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 | v = sqlite3GetVdbe(pParse); if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); }else{ if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n; } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeComment((v, "LIMIT counter")); sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); } |
︙ | ︙ | |||
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 | ){ int rc = SQLITE_OK; /* Success code from a subroutine */ Select *pPrior; /* Another SELECT immediately to our left */ Vdbe *v; /* Generate code to this VDBE */ SelectDest dest; /* Alternative data destination */ Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ db = pParse->db; pPrior = p->pPrior; | > > > > | 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 | ){ int rc = SQLITE_OK; /* Success code from a subroutine */ Select *pPrior; /* Another SELECT immediately to our left */ Vdbe *v; /* Generate code to this VDBE */ SelectDest dest; /* Alternative data destination */ Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ #ifndef SQLITE_OMIT_EXPLAIN int iSub1; /* EQP id of left-hand query */ int iSub2; /* EQP id of right-hand query */ #endif /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ db = pParse->db; pPrior = p->pPrior; |
︙ | ︙ | |||
1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 | assert( v!=0 ); /* The VDBE already created by calling function */ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); | > | 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 | assert( v!=0 ); /* The VDBE already created by calling function */ /* Create the destination temporary table if necessary */ if( dest.eDest==SRT_EphemTab ){ assert( p->pEList ); sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); dest.eDest = SRT_Table; } /* Make sure all SELECTs in the statement have the same number of elements ** in their result sets. */ assert( p->pEList && pPrior->pEList ); |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 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 | } /* Generate code for the left and right SELECT statements. */ switch( p->op ){ case TK_ALL: { int addr = 0; assert( !pPrior->pLimit ); pPrior->pLimit = p->pLimit; pPrior->pOffset = p->pOffset; rc = sqlite3Select(pParse, pPrior, &dest); p->pLimit = 0; p->pOffset = 0; if( rc ){ goto multi_select_end; } p->pPrior = 0; p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeComment((v, "Jump ahead if LIMIT reached")); } rc = sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; if( addr ){ sqlite3VdbeJumpHere(v, addr); } break; } case TK_EXCEPT: case TK_UNION: { | > > > > > > > > > > | 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 1627 1628 1629 1630 1631 1632 1633 1634 1635 | } /* Generate code for the left and right SELECT statements. */ switch( p->op ){ case TK_ALL: { int addr = 0; int nLimit; assert( !pPrior->pLimit ); pPrior->pLimit = p->pLimit; pPrior->pOffset = p->pOffset; explainSetInteger(iSub1, pParse->iNextSelectId); rc = sqlite3Select(pParse, pPrior, &dest); p->pLimit = 0; p->pOffset = 0; if( rc ){ goto multi_select_end; } p->pPrior = 0; p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeComment((v, "Jump ahead if LIMIT reached")); } explainSetInteger(iSub2, pParse->iNextSelectId); rc = sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; p->nSelectRow += pPrior->nSelectRow; if( pPrior->pLimit && sqlite3ExprIsInteger(pPrior->pLimit, &nLimit) && p->nSelectRow > (double)nLimit ){ p->nSelectRow = (double)nLimit; } if( addr ){ sqlite3VdbeJumpHere(v, addr); } break; } case TK_EXCEPT: case TK_UNION: { |
︙ | ︙ | |||
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 1627 | assert( p->pEList ); } /* Code the SELECT statements to our left */ assert( !pPrior->pOrderBy ); sqlite3SelectDestInit(&uniondest, priorOp, unionTab); rc = sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; } /* Code the current SELECT statement */ if( p->op==TK_EXCEPT ){ op = SRT_Except; }else{ assert( p->op==TK_UNION ); op = SRT_Union; } p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; uniondest.eDest = op; rc = sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); /* Query flattening in sqlite3Select() might refill p->pOrderBy. ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ sqlite3ExprListDelete(db, p->pOrderBy); pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; p->iLimit = 0; p->iOffset = 0; /* Convert the data in the temporary table into whatever form | > > > | 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 | assert( p->pEList ); } /* Code the SELECT statements to our left */ assert( !pPrior->pOrderBy ); sqlite3SelectDestInit(&uniondest, priorOp, unionTab); explainSetInteger(iSub1, pParse->iNextSelectId); rc = sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; } /* Code the current SELECT statement */ if( p->op==TK_EXCEPT ){ op = SRT_Except; }else{ assert( p->op==TK_UNION ); op = SRT_Union; } p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; uniondest.eDest = op; explainSetInteger(iSub2, pParse->iNextSelectId); rc = sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); /* Query flattening in sqlite3Select() might refill p->pOrderBy. ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ sqlite3ExprListDelete(db, p->pOrderBy); pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; p->iLimit = 0; p->iOffset = 0; /* Convert the data in the temporary table into whatever form |
︙ | ︙ | |||
1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 | p->addrOpenEphm[0] = addr; p->pRightmost->selFlags |= SF_UsesEphemeral; assert( p->pEList ); /* Code the SELECTs to our left into temporary table "tab1". */ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); rc = sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; } /* Code the current SELECT into temporary table "tab2" */ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); assert( p->addrOpenEphm[1] == -1 ); p->addrOpenEphm[1] = addr; p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; intersectdest.iParm = tab2; rc = sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary ** tables. */ | > > > | 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 | p->addrOpenEphm[0] = addr; p->pRightmost->selFlags |= SF_UsesEphemeral; assert( p->pEList ); /* Code the SELECTs to our left into temporary table "tab1". */ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); explainSetInteger(iSub1, pParse->iNextSelectId); rc = sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; } /* Code the current SELECT into temporary table "tab2" */ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); assert( p->addrOpenEphm[1] == -1 ); p->addrOpenEphm[1] = addr; p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; intersectdest.iParm = tab2; explainSetInteger(iSub2, pParse->iNextSelectId); rc = sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary ** tables. */ |
︙ | ︙ | |||
1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 | sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; } } /* Compute collating sequences used by ** temporary tables needed to implement the compound select. ** Attach the KeyInfo structure to all temporary tables. ** ** This section is run by the right-most SELECT statement only. ** SELECT statements to the left always skip this part. The right-most | > > | 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 | sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; } } explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL); /* Compute collating sequences used by ** temporary tables needed to implement the compound select. ** Attach the KeyInfo structure to all temporary tables. ** ** This section is run by the right-most SELECT statement only. ** SELECT statements to the left always skip this part. The right-most |
︙ | ︙ | |||
2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 | int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ int *aPermute; /* Mapping from ORDER BY terms to result set columns */ assert( p->pOrderBy!=0 ); assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ labelEnd = sqlite3VdbeMakeLabel(v); | > > > > | 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 | int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ int *aPermute; /* Mapping from ORDER BY terms to result set columns */ #ifndef SQLITE_OMIT_EXPLAIN int iSub1; /* EQP id of left-hand query */ int iSub2; /* EQP id of right-hand query */ #endif assert( p->pOrderBy!=0 ); assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ labelEnd = sqlite3VdbeMakeLabel(v); |
︙ | ︙ | |||
2177 2178 2179 2180 2181 2182 2183 | } } } /* Separate the left and the right query from one another */ p->pPrior = 0; | < | 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 | } } } /* Separate the left and the right query from one another */ p->pPrior = 0; sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER"); if( pPrior->pPrior==0 ){ sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER"); } /* Compute the limit registers */ computeLimitRegisters(pParse, p, labelEnd); |
︙ | ︙ | |||
2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 | /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. */ VdbeNoopComment((v, "Begin coroutine for left SELECT")); pPrior->iLimit = regLimitA; sqlite3Select(pParse, pPrior, &destA); sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); VdbeNoopComment((v, "End coroutine for left SELECT")); /* Generate a coroutine to evaluate the SELECT statement on ** the right - the "B" select */ addrSelectB = sqlite3VdbeCurrentAddr(v); VdbeNoopComment((v, "Begin coroutine for right SELECT")); savedLimit = p->iLimit; savedOffset = p->iOffset; p->iLimit = regLimitB; p->iOffset = 0; sqlite3Select(pParse, p, &destB); p->iLimit = savedLimit; p->iOffset = savedOffset; sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); VdbeNoopComment((v, "End coroutine for right SELECT")); | > > | 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 | /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. */ VdbeNoopComment((v, "Begin coroutine for left SELECT")); pPrior->iLimit = regLimitA; explainSetInteger(iSub1, pParse->iNextSelectId); sqlite3Select(pParse, pPrior, &destA); sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); VdbeNoopComment((v, "End coroutine for left SELECT")); /* Generate a coroutine to evaluate the SELECT statement on ** the right - the "B" select */ addrSelectB = sqlite3VdbeCurrentAddr(v); VdbeNoopComment((v, "Begin coroutine for right SELECT")); savedLimit = p->iLimit; savedOffset = p->iOffset; p->iLimit = regLimitB; p->iOffset = 0; explainSetInteger(iSub2, pParse->iNextSelectId); sqlite3Select(pParse, p, &destB); p->iLimit = savedLimit; p->iOffset = savedOffset; sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); VdbeNoopComment((v, "End coroutine for right SELECT")); |
︙ | ︙ | |||
2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); }else{ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); } /* Generate a subroutine to run when the results from select B ** are exhausted and only data in select A remains. */ if( op==TK_INTERSECT ){ addrEofB = addrEofA; }else{ VdbeNoopComment((v, "eof-B subroutine")); addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB); } | > > | 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); }else{ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); p->nSelectRow += pPrior->nSelectRow; } /* Generate a subroutine to run when the results from select B ** are exhausted and only data in select A remains. */ if( op==TK_INTERSECT ){ addrEofB = addrEofA; if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow; }else{ VdbeNoopComment((v, "eof-B subroutine")); addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB); } |
︙ | ︙ | |||
2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 | if( p->pPrior ){ sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ return SQLITE_OK; } #endif #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Forward Declarations */ static void substExprList(sqlite3*, ExprList*, int, ExprList*); | > | 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 | if( p->pPrior ){ sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ explainComposite(pParse, p->op, iSub1, iSub2, 0); return SQLITE_OK; } #endif #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Forward Declarations */ static void substExprList(sqlite3*, ExprList*, int, ExprList*); |
︙ | ︙ | |||
3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 | pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); pTab->iPKey = -1; pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase); | > | 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 | pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); pTab->iPKey = -1; pTab->nRowEst = 1000000; pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase); |
︙ | ︙ | |||
3461 3462 3463 3464 3465 3466 3467 | int addrNext = 0; int regAgg; ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); | | | 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 | int addrNext = 0; int regAgg; ExprList *pList = pF->pExpr->x.pList; assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) ); if( pList ){ nArg = pList->nExpr; regAgg = sqlite3GetTempRange(pParse, nArg); sqlite3ExprCodeExprList(pParse, pList, regAgg, 1); }else{ nArg = 0; regAgg = 0; } if( pF->iDistinct>=0 ){ addrNext = sqlite3VdbeMakeLabel(v); assert( nArg==1 ); |
︙ | ︙ | |||
3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 | int isDistinct; /* True if the DISTINCT keyword is present */ int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ db = pParse->db; if( p==0 || db->mallocFailed || pParse->nErr ){ return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; memset(&sAggInfo, 0, sizeof(sAggInfo)); | > > > > > | 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 | int isDistinct; /* True if the DISTINCT keyword is present */ int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ #ifndef SQLITE_OMIT_EXPLAIN int iRestoreSelectId = pParse->iSelectId; pParse->iSelectId = pParse->iNextSelectId++; #endif db = pParse->db; if( p==0 || db->mallocFailed || pParse->nErr ){ return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; memset(&sAggInfo, 0, sizeof(sAggInfo)); |
︙ | ︙ | |||
3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 | isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto select_end; /* Generate code for all sub-queries in the FROM clause */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; SelectDest dest; | > > > > > > > > > | 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 | isAgg = (p->selFlags & SF_Aggregate)!=0; assert( pEList!=0 ); /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto select_end; /* If writing to memory or generating a set ** only a single column may be output. */ #ifndef SQLITE_OMIT_SUBQUERY if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){ goto select_end; } #endif /* Generate code for all sub-queries in the FROM clause */ #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) for(i=0; !p->pPrior && i<pTabList->nSrc; i++){ struct SrcList_item *pItem = &pTabList->a[i]; SelectDest dest; |
︙ | ︙ | |||
3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 | isAgg = 1; p->selFlags |= SF_Aggregate; } i = -1; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); assert( pItem->isPopulated==0 ); sqlite3Select(pParse, pSub, &dest); pItem->isPopulated = 1; } if( /*pParse->nErr ||*/ db->mallocFailed ){ goto select_end; } pParse->nHeight -= sqlite3SelectExprHeight(p); pTabList = p->pSrc; if( !IgnorableOrderby(pDest) ){ | > > | 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 | isAgg = 1; p->selFlags |= SF_Aggregate; } i = -1; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); assert( pItem->isPopulated==0 ); explainSetInteger(pItem->iSelectId, pParse->iNextSelectId); sqlite3Select(pParse, pSub, &dest); pItem->isPopulated = 1; pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow; } if( /*pParse->nErr ||*/ db->mallocFailed ){ goto select_end; } pParse->nHeight -= sqlite3SelectExprHeight(p); pTabList = p->pSrc; if( !IgnorableOrderby(pDest) ){ |
︙ | ︙ | |||
3687 3688 3689 3690 3691 3692 3693 | pLoop->pRightmost = p; pLoop->pNext = pRight; pRight = pLoop; } mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; if( mxSelect && cnt>mxSelect ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); | | | < < | < < < < < < > < | 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 | pLoop->pRightmost = p; pLoop->pNext = pRight; pRight = pLoop; } mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; if( mxSelect && cnt>mxSelect ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); goto select_end; } } rc = multiSelect(pParse, p, pDest); explainSetInteger(pParse->iSelectId, iRestoreSelectId); return rc; } #endif /* If possible, rewrite the query to use GROUP BY instead of DISTINCT. ** GROUP BY might use an index, DISTINCT never does. */ assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; p->selFlags &= ~SF_Distinct; } /* If there is both a GROUP BY and an ORDER BY clause and they are ** identical, then disable the ORDER BY clause since the GROUP BY ** will cause elements to come out in the correct order. This is ** an optimization - the correct answer should result regardless. ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER |
︙ | ︙ | |||
3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iEnd); /* Open a virtual index to use for the distinct set. */ | > | > > | 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 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 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); p->nSelectRow = (double)LARGEST_INT64; computeLimitRegisters(pParse, p, iEnd); /* Open a virtual index to use for the distinct set. */ if( p->selFlags & SF_Distinct ){ KeyInfo *pKeyInfo; assert( isAgg || pGroupBy ); distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); }else{ distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if( !isAgg && pGroupBy==0 ){ /* This case is for non-aggregate queries ** Begin the database scan */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0); if( pWInfo==0 ) goto select_end; if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut; /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral ** into an OP_Noop. */ if( addrSortIndex>=0 && pOrderBy==0 ){ sqlite3VdbeChangeToNoop(v, addrSortIndex, 1); |
︙ | ︙ | |||
3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 | for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ pItem->iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in | > > > | 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 | for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ pItem->iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100; }else{ p->nSelectRow = (double)1; } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in |
︙ | ︙ | |||
3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 | ** in sorted order */ int regBase; int regRecord; int nCol; int nGroupBy; groupBySort = 1; nGroupBy = pGroupBy->nExpr; nCol = nGroupBy + 1; j = nGroupBy+1; for(i=0; i<sAggInfo.nColumn; i++){ if( sAggInfo.aCol[i].iSorterColumn>=j ){ nCol++; | > > > | 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 | ** in sorted order */ int regBase; int regRecord; int nCol; int nGroupBy; explainTempTable(pParse, isDistinct && !(p->selFlags&SF_Distinct)?"DISTINCT":"GROUP BY"); groupBySort = 1; nGroupBy = pGroupBy->nExpr; nCol = nGroupBy + 1; j = nGroupBy+1; for(i=0; i<sAggInfo.nColumn; i++){ if( sAggInfo.aCol[i].iSorterColumn>=j ){ nCol++; |
︙ | ︙ | |||
4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 | selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); } /* endif aggregate query */ /* If there is an ORDER BY clause, then we need to sort the results ** and send them to the callback one by one. */ if( pOrderBy ){ generateSortTail(pParse, p, v, pEList->nExpr, pDest); } /* Jump here to skip this query */ sqlite3VdbeResolveLabel(v, iEnd); /* The SELECT was successfully coded. Set the return code to 0 ** to indicate no errors. */ rc = 0; /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: /* Identify column names if results of the SELECT are to be output. */ if( rc==SQLITE_OK && pDest->eDest==SRT_Output ){ generateColumnNames(pParse, pTabList, pEList); } | > > > > > > | 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 | selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1, pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); } /* endif aggregate query */ if( distinct>=0 ){ explainTempTable(pParse, "DISTINCT"); } /* If there is an ORDER BY clause, then we need to sort the results ** and send them to the callback one by one. */ if( pOrderBy ){ explainTempTable(pParse, "ORDER BY"); generateSortTail(pParse, p, v, pEList->nExpr, pDest); } /* Jump here to skip this query */ sqlite3VdbeResolveLabel(v, iEnd); /* The SELECT was successfully coded. Set the return code to 0 ** to indicate no errors. */ rc = 0; /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: explainSetInteger(pParse->iSelectId, iRestoreSelectId); /* Identify column names if results of the SELECT are to be output. */ if( rc==SQLITE_OK && pDest->eDest==SRT_Output ){ generateColumnNames(pParse, pTabList, pEList); } |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
533 534 535 536 537 538 539 540 541 542 543 544 545 546 | ** ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the ** sync operation only needs to flush data to mass storage. Inode ** information need not be flushed. If the lower four bits of the flag ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. ** If the lower four bits equal SQLITE_SYNC_FULL, that means ** to use Mac OS X style fullsync instead of fsync(). */ #define SQLITE_SYNC_NORMAL 0x00002 #define SQLITE_SYNC_FULL 0x00003 #define SQLITE_SYNC_DATAONLY 0x00010 /* ** CAPI3REF: OS Interface Open File Handle | > > > > > > > > > > > > | 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 | ** ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the ** sync operation only needs to flush data to mass storage. Inode ** information need not be flushed. If the lower four bits of the flag ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. ** If the lower four bits equal SQLITE_SYNC_FULL, that means ** to use Mac OS X style fullsync instead of fsync(). ** ** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags ** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL ** settings. The [synchronous pragma] determines when calls to the ** xSync VFS method occur and applies uniformly across all platforms. ** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how ** energetic or rigorous or forceful the sync operations are and ** only make a difference on Mac OSX for the default SQLite code. ** (Third-party VFS implementations might also make the distinction ** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the ** operating systems natively supported by SQLite, only Mac OSX ** cares about the difference.) */ #define SQLITE_SYNC_NORMAL 0x00002 #define SQLITE_SYNC_FULL 0x00003 #define SQLITE_SYNC_DATAONLY 0x00010 /* ** CAPI3REF: OS Interface Open File Handle |
︙ | ︙ | |||
701 702 703 704 705 706 707 708 709 710 711 712 713 714 | */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only | > > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 | */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 #define SQLITE_FCNTL_SIZE_HINT 5 #define SQLITE_FCNTL_CHUNK_SIZE 6 #define SQLITE_FCNTL_FILE_POINTER 7 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only |
︙ | ︙ | |||
748 749 750 751 752 753 754 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** | | > > > > | | | | > | | | > | | | | | | | 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 | ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** ** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname() with an optional suffix added. ** ^If a suffix is added to the zFilename parameter, it will ** consist of a single "-" character followed by no more than ** 10 alphanumeric and/or "-" characters. ** ^SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. ** If the zFilename parameter to xOpen is a NULL pointer then xOpen ** must invent its own temporary name for the file. ^Whenever the ** xFilename parameter is NULL it will also be the case that the ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. ** ** The flags argument to xOpen() includes all bits set in ** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] ** or [sqlite3_open16()] is used, then flags includes at least ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. ** If xOpen() opens a file read-only then it sets *pOutFlags to ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** ** ^(SQLite will also add one of the following flags to the xOpen() ** call, depending on the object being opened: ** ** <ul> ** <li> [SQLITE_OPEN_MAIN_DB] ** <li> [SQLITE_OPEN_MAIN_JOURNAL] ** <li> [SQLITE_OPEN_TEMP_DB] ** <li> [SQLITE_OPEN_TEMP_JOURNAL] ** <li> [SQLITE_OPEN_TRANSIENT_DB] ** <li> [SQLITE_OPEN_SUBJOURNAL] ** <li> [SQLITE_OPEN_MASTER_JOURNAL] ** <li> [SQLITE_OPEN_WAL] ** </ul>)^ ** ** The file I/O implementation can use the object type flags to ** change the way it deals with files. For example, an application ** that does not care about crash recovery or rollback might make ** the open of a journal file a no-op. Writes to this journal would ** also be no-ops, and any attempt to read the journal would return ** SQLITE_IOERR. Or the implementation might recognize that a database ** file will be doing page-aligned sector reads and writes in a random ** order and set up its I/O subsystem accordingly. ** ** SQLite might also add one of the following flags to the xOpen method: ** ** <ul> ** <li> [SQLITE_OPEN_DELETEONCLOSE] ** <li> [SQLITE_OPEN_EXCLUSIVE] ** </ul> ** ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be ** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] ** will be set for TEMP databases and their journals, transient ** databases, and subjournals. ** ** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the ** SQLITE_OPEN_CREATE, is used to indicate that file should always ** be created, and that it is an error if it already exists. ** It is <i>not</i> used to indicate the file should be opened ** for exclusive access. ** ** ^At least szOsFile bytes of memory are allocated by SQLite ** to hold the [sqlite3_file] structure passed as the third ** argument to xOpen. The xOpen method does not have to ** allocate the structure; it should just fill it in. Note that ** the xOpen method must set the sqlite3_file.pMethods to either ** a valid [sqlite3_io_methods] object or to NULL. xOpen must do ** this even if the open fails. SQLite expects that the sqlite3_file.pMethods ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** to test whether a file is at least readable. The file can be a ** directory. ** ** ^SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** ** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() ** interfaces are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at ** least the number of microseconds given. ^The xCurrentTime() ** method returns a Julian Day Number for the current date and time as ** a floating point value. ** ^The xCurrentTimeInt64() method returns, as an integer, the Julian ** Day Number multipled by 86400000 (the number of milliseconds in ** a 24-hour day). ** ^SQLite will use the xCurrentTimeInt64() method to get the current ** date and time if that method is available (if iVersion is 2 or ** greater and the function pointer is not NULL) and will fall back ** to xCurrentTime() if xCurrentTimeInt64() is unavailable. */ |
︙ | ︙ | |||
1239 1240 1241 1242 1243 1244 1245 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] | | | < | | | | | < | | 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 | ** <dd> ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: ** <ul> ** <li> [sqlite3_memory_used()] ** <li> [sqlite3_memory_highwater()] ** <li> [sqlite3_soft_heap_limit64()] ** <li> [sqlite3_status()] ** </ul>)^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. ** </dd> ** ** <dt>SQLITE_CONFIG_SCRATCH</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte ** aligned memory buffer from which the scrach allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. ** ^SQLite will use no more than two scratch buffers per thread. So ** N should be set to twice the expected maximum number of threads. ** ^SQLite will never require a scratch buffer that is more than 6 ** times the database page size. ^If SQLite needs needs additional ** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.</dd> ** ** <dt>SQLITE_CONFIG_PAGECACHE</dt> ** <dd> ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on ** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. ** ^SQLite will use the memory provided by the first argument to satisfy its ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. ** The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.</dd> ** ** <dt>SQLITE_CONFIG_HEAP</dt> ** <dd> ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. |
︙ | ︙ | |||
1411 1412 1413 1414 1415 1416 1417 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally | | > > > | > > > | 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 | ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally ** rounded down to the next smaller multiple of 8. ^(The lookaside memory ** configuration for a database connection can only be changed when that ** connection is not currently using lookaside memory, or in other words ** when the "current value" returned by ** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^</dd> ** ** </dl> */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ /* |
︙ | ︙ | |||
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These | > > > | 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** This is a legacy interface that is preserved for backwards compatibility. ** Use of this interface is not recommended. ** ** Definition: A <b>result table</b> is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These |
︙ | ︙ | |||
1737 1738 1739 1740 1741 1742 1743 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** | | | 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 | ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** ** ^(As an example of the result table format, suppose a query result ** is as follows: ** ** <blockquote><pre> ** Name | Age ** ----------------------- ** Alice | 43 ** Bob | 28 |
︙ | ︙ | |||
1761 1762 1763 1764 1765 1766 1767 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; | | | | | | 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 | ** azResult[1] = "Age"; ** azResult[2] = "Alice"; ** azResult[3] = "43"; ** azResult[4] = "Bob"; ** azResult[5] = "28"; ** azResult[6] = "Cindy"; ** azResult[7] = "21"; ** </pre></blockquote>)^ ** ** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 ** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** ** After the application has finished with the result from sqlite3_get_table(), ** it must pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** ** The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()]. */ int sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ int *pnRow, /* Number of result rows written here */ int *pnColumn, /* Number of result columns written here */ |
︙ | ︙ | |||
1933 1934 1935 1936 1937 1938 1939 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() | | > > | 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 | ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() ** is always aligned to at least an 8 byte boundary, or to a ** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time ** option is used. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability ** is no longer provided. Only built-in memory allocators can be used. ** ** The Windows OS interface layer calls |
︙ | ︙ | |||
2191 2192 2193 2194 2195 2196 2197 | void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** | | | | | > > > > > > > > > > > | | 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 | void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback ** function X to be invoked periodically during long running calls to ** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for ** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** ** ^The parameter P is passed through as the only parameter to the ** callback function X. ^The parameter N is the number of ** [virtual machine instructions] that are evaluated between successive ** invocations of the callback X. ** ** ^Only a single progress handler may be defined at one time per ** [database connection]; setting a new progress handler cancels the ** old one. ^Setting parameter X to NULL disables the progress handler. ** ^The progress handler is also disabled by setting N to a value less ** than 1. ** ** ^If the progress callback returns non-zero, the operation is ** interrupted. This feature can be used to implement a ** "Cancel" button on a GUI progress dialog box. ** ** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); |
︙ | ︙ | |||
2260 2261 2262 2263 2264 2265 2266 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], | | | 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 | ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().</dd>)^ ** </dl> ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], ** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_PRIVATECACHE] flags, ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread ** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was |
︙ | ︙ | |||
2385 2386 2387 2388 2389 2390 2391 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the | | | | | > > > > > | 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 | ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the ** new limit for that construct.)^ ** ** ^If the new limit is a negative number, the limit is unchanged. ** ^(For each limit category SQLITE_LIMIT_<i>NAME</i> there is a ** [limits | hard upper bound] ** set at compile-time by a C preprocessor macro called ** [limits | SQLITE_MAX_<i>NAME</i>]. ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ ** ^Attempts to increase a limit above its hard upper bound are ** silently truncated to the hard upper bound. ** ** ^Regardless of whether or not the limit was changed, the ** [sqlite3_limit()] interface returns the prior value of the limit. ** ^Hence, to find the current value of a limit without changing it, ** simply invoke this interface with the third parameter set to -1. ** ** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and ** separate databases controlled by JavaScript applications downloaded ** off the Internet. The internal databases can be given the ** large, default limits. Databases managed by external sources can |
︙ | ︙ | |||
2424 2425 2426 2427 2428 2429 2430 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> | | | > > | < | 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 | ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** ** <dl> ** ^(<dt>SQLITE_LIMIT_LENGTH</dt> ** <dd>The maximum size of any string or BLOB or table row, in bytes.<dd>)^ ** ** ^(<dt>SQLITE_LIMIT_SQL_LENGTH</dt> ** <dd>The maximum length of an SQL statement, in bytes.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COLUMN</dt> ** <dd>The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_EXPR_DEPTH</dt> ** <dd>The maximum depth of the parse tree on any expression.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_COMPOUND_SELECT</dt> ** <dd>The maximum number of terms in a compound SELECT statement.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VDBE_OP</dt> ** <dd>The maximum number of instructions in a virtual machine program ** used to implement an SQL statement. This limit is not currently ** enforced, though that might be added in some future release of ** SQLite.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_FUNCTION_ARG</dt> ** <dd>The maximum number of arguments on a function.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_ATTACHED</dt> ** <dd>The maximum number of [ATTACH | attached databases].)^</dd> ** ** ^(<dt>SQLITE_LIMIT_LIKE_PATTERN_LENGTH</dt> ** <dd>The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.</dd>)^ ** ** ^(<dt>SQLITE_LIMIT_VARIABLE_NUMBER</dt> ** <dd>The maximum index number of any [parameter] in an SQL statement.)^ ** ** ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt> ** <dd>The maximum depth of recursion for triggers.</dd>)^ ** </dl> */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 |
︙ | ︙ | |||
2528 2529 2530 2531 2532 2533 2534 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL | | < < < < < | | | | | > > > > > | 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 | ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** ** <ol> ** <li> ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL ** statement and try to run it again. ** </li> ** ** <li> ** ^When an error occurs, [sqlite3_step()] will return one of the detailed ** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code ** and the application would have to make a second call to [sqlite3_reset()] ** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. ** </li> ** ** <li> ** ^If the specific value bound to [parameter | host parameter] in the ** WHERE clause might influence the choice of query plan for a statement, ** then the statement will be automatically recompiled, as if there had been ** a schema change, on the first [sqlite3_step()] call following any change ** to the [sqlite3_bind_text | bindings] of that [parameter]. ** ^The specific value of WHERE-clause [parameter] might influence the ** choice of query plan if the parameter is the left-hand side of a [LIKE] ** or [GLOB] operator or if the parameter is compared to an indexed column ** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled. ** the ** </li> ** </ol> */ int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ |
︙ | ︙ | |||
2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing ** for the values it stores. ^Values stored in sqlite3_value objects | > > > > > > > > > > > > > > | 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If An SQL Statement Writes The Database ** ** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if ** the [prepared statement] X is [SELECT] statement and false (zero) if ** X is an [INSERT], [UPDATE], [DELETE], CREATE, DROP, [ANALYZE], ** [ALTER], or [REINDEX] statement. ** If X is a NULL pointer or any other kind of statement, including but ** not limited to [ATTACH], [DETACH], [COMMIT], [ROLLBACK], [RELEASE], ** [SAVEPOINT], [PRAGMA], or [VACUUM] the result of sqlite3_stmt_readonly(X) is ** undefined. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing ** for the values it stores. ^Values stored in sqlite3_value objects |
︙ | ︙ | |||
2617 2618 2619 2620 2621 2622 2623 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications | | | 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 | ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications ** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with |
︙ | ︙ | |||
2691 2692 2693 2694 2695 2696 2697 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or | | > > > | 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called ** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), ** sqlite3_bind_text(), or sqlite3_bind_text16() fails. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
︙ | ︙ | |||
2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). */ int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column | > > | 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 | /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). ** ** See also: [sqlite3_data_count()] */ int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column |
︙ | ︙ | |||
3002 3003 3004 3005 3006 3007 3008 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** | | | > > > > > > | 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 | ** by sqlite3_step(). The use of the "v2" interface is recommended. */ int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** ** ^The sqlite3_data_count(P) interface returns the number of columns in the ** current row of the result set of [prepared statement] P. ** ^If prepared statement P does not have results ready to return ** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of ** interfaces) then sqlite3_data_count(P) returns 0. ** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. ** ** See also: [sqlite3_column_count()] */ int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** |
︙ | ︙ | |||
3083 3084 3085 3086 3087 3088 3089 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. | > > > > > > > > > > > > | | > | < < < < < | 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 | ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes() returns zero. ** ** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts ** the string to UTF-16 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes16() uses ** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns ** the number of bytes in that string. ** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. ** ** ^The values returned by [sqlite3_column_bytes()] and ** [sqlite3_column_bytes16()] do not include the zero terminators at the end ** of the string. ^For clarity: the values returned by ** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), ** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], |
︙ | ︙ | |||
3139 3140 3141 3142 3143 3144 3145 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** | | | | | | | | 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 | ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** ** Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. ** Type conversions and pointer invalidations might occur ** in the following cases: ** ** <ul> ** <li> The initial content is a BLOB and sqlite3_column_text() or ** sqlite3_column_text16() is called. A zero-terminator might ** need to be added to the string.</li> ** <li> The initial content is UTF-8 text and sqlite3_column_bytes16() or ** sqlite3_column_text16() is called. The content must be converted ** to UTF-16.</li> ** <li> The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.</li> ** </ul> ** ** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer references will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** ** The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** ** <ul> ** <li>sqlite3_column_text() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_blob() followed by sqlite3_column_bytes()</li> ** <li>sqlite3_column_text16() followed by sqlite3_column_bytes16()</li> ** </ul> ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result ** into the desired format, then invoke sqlite3_column_bytes() or ** sqlite3_column_bytes16() to find the size of the result. Do not mix calls ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() |
︙ | ︙ | |||
3208 3209 3210 3211 3212 3213 3214 | int sqlite3_column_type(sqlite3_stmt*, int iCol); sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. | > | | > | | | > > > | > | < < < > > > > > > | 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 | int sqlite3_column_type(sqlite3_stmt*, int iCol); sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the most recent evaluation of the statement encountered no errors or ** or if the statement is never been evaluated, then sqlite3_finalize() returns ** SQLITE_OK. ^If the most recent evaluation of statement S failed, then ** sqlite3_finalize(S) returns the appropriate [error code] or ** [extended error code]. ** ** ^The sqlite3_finalize(S) routine can be called at any point during ** the life cycle of [prepared statement] S: ** before statement S is ever evaluated, after ** one or more calls to [sqlite3_reset()], or after any call ** to [sqlite3_step()] regardless of whether or not the statement has ** completed execution. ** ** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. ** ** The application must finalize every [prepared statement] in order to avoid ** resource leaks. It is a grievous error for the application to try to use ** a prepared statement after it has been finalized. Any use of a prepared ** statement after it has been finalized can result in undefined and ** undesirable behavior such as segfaults and heap corruption. */ int sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] |
︙ | ︙ | |||
3254 3255 3256 3257 3258 3259 3260 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** | | | > | < | > | | | > | | | | | | | | | > > > > > > > > > > > < < < < < | 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 | /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** these routines are the text encoding expected for ** the the second parameter (the name of the function being created) ** and the presence or absence of a destructor callback for ** the application data pointer. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** ** ^The second parameter is the name of the SQL function to be created or ** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 ** representation, exclusive of the zero-terminator. ^Note that the name ** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. ** ^Any attempt to create a function with a longer name ** will result in [SQLITE_MISUSE] being returned. ** ** ^The third parameter (nArg) ** is the number of arguments that the SQL function or ** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Every SQL function implementation must be able to work ** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be ** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** ^The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL poiners for all three function ** callbacks. ** ** ^(If the tenth parameter to sqlite3_create_function_v2() is not NULL, ** then it is destructor for the application data pointer. ** The destructor is invoked when the function is deleted, either by being ** overloaded or when the database connection closes.)^ ** ^The destructor is also invoked if the call to ** sqlite3_create_function_v2() fails. ** ^When the destructor callback of the tenth parameter is invoked, it ** is passed a single argument which is a copy of the application data ** pointer which was the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with ** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. ** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** ** ^Built-in functions may be overloaded by new application-defined functions. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ int sqlite3_create_function( |
︙ | ︙ | |||
3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. | > > > > > > > > > > > | 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 | const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); int sqlite3_create_function_v2( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*), void(*xDestroy)(void*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. |
︙ | ︙ | |||
3694 3695 3696 3697 3698 3699 3700 | void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_value(sqlite3_context*, sqlite3_value*); void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** | | | | | | > | > > | < < > | < | < > > > > | > > > > > | > > > | < | < | < < | | > | | > > > > > > > > > > > > > > > > > | | < < | | | > > > > > > > > > | | | | 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 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 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 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 | void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); void sqlite3_result_value(sqlite3_context*, sqlite3_value*); void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** ** ^These functions add, remove, or modify a [collation] associated ** with the [database connection] specified as the first argument. ** ** ^The name of the collation is a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() ** and a UTF-16 string in native byte order for sqlite3_create_collation16(). ** ^Collation names that compare equal according to [sqlite3_strnicmp()] are ** considered to be the same name. ** ** ^(The third argument (eTextRep) must be one of the constants: ** <ul> ** <li> [SQLITE_UTF8], ** <li> [SQLITE_UTF16LE], ** <li> [SQLITE_UTF16BE], ** <li> [SQLITE_UTF16], or ** <li> [SQLITE_UTF16_ALIGNED]. ** </ul>)^ ** ^The eTextRep argument determines the encoding of strings passed ** to the collating function callback, xCallback. ** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep ** force strings to be UTF16 with native byte order. ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin ** on an even byte address. ** ** ^The fourth argument, pArg, is a application data pointer that is passed ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. ** ^Multiple collating functions can be registered using the same name but ** with different eTextRep parameters and SQLite will use whichever ** function requires the least amount of data transformation. ** ^If the xCallback argument is NULL then the collating function is ** deleted. ^When all collating functions having the same name are deleted, ** that collation is no longer usable. ** ** ^The collating function callback is invoked with a copy of the pArg ** application data pointer and with two strings in the encoding specified ** by the eTextRep argument. The collating function must return an ** integer that is negative, zero, or positive ** if the first string is less than, equal to, or greater than the second, ** respectively. A collating function must alway return the same answer ** given the same inputs. If two or more collating functions are registered ** to the same collation name (using different eTextRep values) then all ** must give an equivalent answer when invoked with equivalent strings. ** The collating function must obey the following properties for all ** strings A, B, and C: ** ** <ol> ** <li> If A==B then B==A. ** <li> If A==B and B==C then A==C. ** <li> If A<B THEN B>A. ** <li> If A<B and B<C then A<C. ** </ol> ** ** If a collating function fails any of the above constraints and that ** collating function is registered and used, then the behavior of SQLite ** is undefined. ** ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** ** ^The xDestroy callback is <u>not</u> called if the ** sqlite3_create_collation_v2() function fails. Applications that invoke ** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should ** check the return code and dispose of the application data pointer ** themselves rather than expecting SQLite to deal with it for them. ** This is different from every other SQLite interface. The inconsistency ** is unfortunate but cannot be changed without breaking backwards ** compatibility. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); /* ** CAPI3REF: Collation Needed Callbacks ** ** ^To avoid having to register all collation sequences before a database |
︙ | ︙ | |||
3845 3846 3847 3848 3849 3850 3851 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** | | | | | > > > | 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 | const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** ** The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** ** If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to ** the nearest second. The number of milliseconds of sleep actually ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. If the xSleep() method ** of the default VFS is not implemented correctly, or not implemented at ** all, then the behavior of sqlite3_sleep() may deviate from the description ** in the previous paragraphs. */ int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is |
︙ | ︙ | |||
4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. */ int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** | > > | | > > > > | | < > | < | > > > > > | < > > > | > > > > > > > > > > > > | | > > | > > > > > > | > > > | | < < | < < > | > | 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 | ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. ** ^The sqlite3_release_memory() routine is a no-op returning zero ** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. */ int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the ** soft limit on the amount of heap memory that may be allocated by SQLite. ** ^SQLite strives to keep heap memory utilization below the soft heap ** limit by reducing the number of pages held in the page cache ** as heap memory usages approaches the limit. ** ^The soft heap limit is "soft" because even though SQLite strives to stay ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of ** the soft heap limit prior to the call. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. ** ** ^(The soft heap limit is not enforced in the current implementation ** if one or more of following conditions are true: ** ** <ul> ** <li> The soft heap limit is set to zero. ** <li> Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. ** <li> An alternative page cache implementation is specifed using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). ** <li> The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. ** </ul>)^ ** ** Beginning with SQLite version 3.7.3, the soft heap limit is enforced ** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] ** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], ** the soft heap limit is enforced on every memory allocation. Without ** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced ** when memory is allocated by the page cache. Testing suggests that because ** the page cache is the predominate memory user in SQLite, most ** applications will achieve adequate soft heap limit enforcement without ** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. ** ** The circumstances under which SQLite will enforce the soft heap limit may ** changes in future releases of SQLite. */ sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); /* ** CAPI3REF: Deprecated Soft Heap Limit Interface ** DEPRECATED ** ** This is a deprecated version of the [sqlite3_soft_heap_limit64()] ** interface. This routine is provided for historical compatibility ** only. All new applications should use the ** [sqlite3_soft_heap_limit64()] interface rather than this one. */ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** ** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. |
︙ | ︙ | |||
4233 4234 4235 4236 4237 4238 4239 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* | | | < | > > > > > | > > > | > | > > > | < > > > | | > | < | > > > | < | < < | 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 | ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load Statically Linked Extensions ** ** ^This interface causes the xEntryPoint() function to be invoked for ** each new [database connection] that is created. The idea here is that ** xEntryPoint() is the entry point for a statically linked SQLite extension ** that is to be automatically loaded into all new database connections. ** ** ^(Even though the function prototype shows that xEntryPoint() takes ** no arguments and returns void, SQLite invokes xEntryPoint() with three ** arguments and expects and integer result as if the signature of the ** entry point where as follows: ** ** <blockquote><pre> ** int xEntryPoint( ** sqlite3 *db, ** const char **pzErrMsg, ** const struct sqlite3_api_routines *pThunk ** ); ** </pre></blockquote>)^ ** ** If the xEntryPoint routine encounters an error, it should make *pzErrMsg ** point to an appropriate error message (obtained from [sqlite3_mprintf()]) ** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg ** is NULL before calling the xEntryPoint(). ^SQLite will invoke ** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any ** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], ** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. ** ** ^Calling sqlite3_auto_extension(X) with an entry point X that is already ** on the list of automatic extensions is a harmless no-op. ^No entry point ** will be called more than once for each database connection that is opened. ** ** See also: [sqlite3_reset_auto_extension()]. */ int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** ** ^This interface disables all automatic extensions previously ** registered using [sqlite3_auto_extension()]. */ void sqlite3_reset_auto_extension(void); /* ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. |
︙ | ︙ | |||
4440 4441 4442 4443 4444 4445 4446 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite | | > > | 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The destructor will also ** be invoked if the call to sqlite3_create_module_v2() fails. ** ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ |
︙ | ︙ | |||
4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Close A BLOB Handle ** ** ^Closes an open [BLOB handle]. ** ** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the | > > > > > > > > > > > > > > > > > > > > > > > | 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Move a BLOB Handle to a New Row ** ** ^This function is used to move an existing blob handle so that it points ** to a different row of the same database table. ^The new row is identified ** by the rowid value passed as the second argument. Only the row can be ** changed. ^The database, table and column on which the blob handle is open ** remain the same. Moving an existing blob handle to a new row can be ** faster than closing the existing handle and opening a new one. ** ** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - ** it must exist and there must be either a blob or text value stored in ** the nominated column.)^ ^If the new row is not present in the table, or if ** it does not contain a blob or text value, or if another error occurs, an ** SQLite error code is returned and the blob handle is considered aborted. ** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or ** [sqlite3_blob_reopen()] on an aborted blob handle immediately return ** SQLITE_ABORT. ** ** ^This function sets the database handle error code and message. */ SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); /* ** CAPI3REF: Close A BLOB Handle ** ** ^Closes an open [BLOB handle]. ** ** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the |
︙ | ︙ | |||
4899 4900 4901 4902 4903 4904 4905 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. | | | 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 | ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. ** ^The xMutexInit routine is called by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** ** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially ** those obtained by the xMutexInit method. ^The xMutexEnd() |
︙ | ︙ | |||
5030 5031 5032 5033 5034 5035 5036 | /* ** CAPI3REF: Low-Level Control Of Database Files ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The | | > > > > > > | 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 | /* ** CAPI3REF: Low-Level Control Of Database Files ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The ** name of the database is "main" for the main database or "temp" for the ** TEMP database, or the name that appears after the AS keyword for ** databases that are added using the [ATTACH] SQL command. ** ^A NULL pointer can be used in place of "main" to refer to the ** main database file. ** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** ** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes ** a pointer to the underlying [sqlite3_file] object to be written into ** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER ** case is a short-circuit path which does not actually invoke the ** underlying sqlite3_io_methods.xFileControl method. ** ** ^If the second parameter (zDbName) does not match the name of any ** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] ** or [sqlite3_errmsg()]. The underlying xFileControl method might ** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying |
︙ | ︙ | |||
5096 5097 5098 5099 5100 5101 5102 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 | > | | | 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 | #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 #define SQLITE_TESTCTRL_LAST 18 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information ** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters ** nothing is written into *pHighwater and the resetFlag is ignored.)^ ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** ** ^The sqlite3_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter |
︙ | ︙ | |||
5165 5166 5167 5168 5169 5170 5171 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache | | | 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 | ** <dd>This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of page cache ** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_PAGECACHE_SIZE</dt> |
︙ | ︙ | |||
5188 5189 5190 5191 5192 5193 5194 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory | | | 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 | ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.</dd>)^ ** ** ^(<dt>SQLITE_STATUS_SCRATCH_OVERFLOW</dt> ** <dd>This parameter returns the number of bytes of scratch memory ** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values ** returned include overflows because the requested allocation was too ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. ** </dd>)^ ** |
︙ | ︙ | |||
5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 | ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections | > > > | 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 | ** [SQLITE_DBSTATUS_LOOKASIDE_USED | SQLITE_DBSTATUS_*] macros is likely ** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections |
︙ | ︙ | |||
5363 5364 5365 5366 5367 5368 5369 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an | | > | | | | > > > > | > | > > > | | > | | | | | | > > | | | | | > | | | | | | | | | > | | < < | | < | > | | | | | | | | | 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 | /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure.)^ ** In many applications, most of the heap memory allocated by ** SQLite is used for the page cache. ** By implementing a ** custom page cache using this API, an application can better control ** the amount of memory consumed by SQLite, the way in which ** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** ** The alternative page cache mechanism is an ** extreme measure that is only needed by the most demanding applications. ** The built-in page cache is recommended for most uses. ** ** ^(The contents of the sqlite3_pcache_methods structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** ^(The xInit() method is called once for each effective ** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() ** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ ** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. ** ^(If the xInit() method is NULL, then the ** built-in default page cache is used instead of the application defined ** page cache.)^ ** ** ^The xShutdown() method is called by [sqlite3_shutdown()]. ** It can be used to clean up ** any outstanding resources before process shutdown, if required. ** ^The xShutdown() method may be NULL. ** ** ^SQLite automatically serializes calls to the xInit method, ** so the xInit method need not be threadsafe. ^The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. All other methods must be threadsafe ** in multithreaded applications. ** ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** ** ^SQLite invokes the xCreate() method to construct a new cache instance. ** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an ** increment (here called "R") of about 100 or 200. SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. ** ^R is constant for a particular build of SQLite. ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, calls to xUnpin() on a cache with bPurgeable set to ** false will always have the "discard" flag set to true. ** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** ** The xPagecount() method must return the number of pages currently ** stored in the cache, both pinned and unpinned. ** ** The xFetch() method locates a page in the cache and returns a pointer to ** the page, or a NULL pointer. ** A "page", in this context, means a buffer of szPage bytes aligned at an ** 8-byte boundary. The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. If the requested page is not already in the cache, then the ** behavior of the cache implementation should use the value of the createFlag ** parameter to help it determined what action to take: ** ** <table border=1 width=85% align=center> ** <tr><th> createFlag <th> Behaviour when page is not already in cache ** <tr><td> 0 <td> Do not allocate a new page. Return NULL. ** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so. ** Otherwise return NULL. ** <tr><td> 2 <td> Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. ** </table> ** ** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite ** will only use a createFlag of 2 after a prior call with a createFlag of 1 ** failed.)^ In between the to xFetch() calls, SQLite may ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. ** ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. If the third parameter, discard, is non-zero, ** then the page must be evicted from the cache. ** ^If the discard parameter is ** zero, then the page may be discarded or retained at the discretion of ** page cache implementation. ^The page cache implementation ** may choose to evict unpinned pages at any time. ** ** The cache must not perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch(). ** ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument. If the cache ** previously contains an entry associated with newKey, it must be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** ** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal ** to the value of the iLimit parameter passed to xTruncate(). If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods |
︙ | ︙ |
Changes to src/sqlite3ext.h.
︙ | ︙ | |||
187 188 189 190 191 192 193 194 195 196 197 198 199 200 | void (*randomness)(int,void*); sqlite3 *(*context_db_handle)(sqlite3_context*); int (*extended_result_codes)(sqlite3*,int); int (*limit)(sqlite3*,int,int); sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); const char *(*sql)(sqlite3_stmt*); int (*status)(int,int*,int*,int); }; /* ** The following macros redefine the API routines so that they are ** redirected throught the global sqlite3_api structure. ** ** This header file is also used by the loadext.c source file | > > > > > > > > > > > > > > > > > > > > > | 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 | void (*randomness)(int,void*); sqlite3 *(*context_db_handle)(sqlite3_context*); int (*extended_result_codes)(sqlite3*,int); int (*limit)(sqlite3*,int,int); sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); const char *(*sql)(sqlite3_stmt*); int (*status)(int,int*,int*,int); int (*backup_finish)(sqlite3_backup*); sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*); int (*backup_pagecount)(sqlite3_backup*); int (*backup_remaining)(sqlite3_backup*); int (*backup_step)(sqlite3_backup*,int); const char *(*compileoption_get)(int); int (*compileoption_used)(const char*); int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*)); int (*db_config)(sqlite3*,int,...); sqlite3_mutex *(*db_mutex)(sqlite3*); int (*db_status)(sqlite3*,int,int*,int*,int); int (*extended_errcode)(sqlite3*); void (*log)(int,const char*,...); sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64); const char *(*sourceid)(void); int (*stmt_status)(sqlite3_stmt*,int,int); int (*strnicmp)(const char*,const char*,int); int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*); int (*wal_autocheckpoint)(sqlite3*,int); int (*wal_checkpoint)(sqlite3*,const char*); void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*); }; /* ** The following macros redefine the API routines so that they are ** redirected throught the global sqlite3_api structure. ** ** This header file is also used by the loadext.c source file |
︙ | ︙ | |||
366 367 368 369 370 371 372 373 374 375 376 377 378 | #define sqlite3_randomness sqlite3_api->randomness #define sqlite3_context_db_handle sqlite3_api->context_db_handle #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes #define sqlite3_limit sqlite3_api->limit #define sqlite3_next_stmt sqlite3_api->next_stmt #define sqlite3_sql sqlite3_api->sql #define sqlite3_status sqlite3_api->status #endif /* SQLITE_CORE */ #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; #define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; #endif /* _SQLITE3EXT_H_ */ | > > > > > > > > > > > > > > > > > > > > > | 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 | #define sqlite3_randomness sqlite3_api->randomness #define sqlite3_context_db_handle sqlite3_api->context_db_handle #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes #define sqlite3_limit sqlite3_api->limit #define sqlite3_next_stmt sqlite3_api->next_stmt #define sqlite3_sql sqlite3_api->sql #define sqlite3_status sqlite3_api->status #define sqlite3_backup_finish sqlite3_api->backup_finish #define sqlite3_backup_init sqlite3_api->backup_init #define sqlite3_backup_pagecount sqlite3_api->backup_pagecount #define sqlite3_backup_remaining sqlite3_api->backup_remaining #define sqlite3_backup_step sqlite3_api->backup_step #define sqlite3_compileoption_get sqlite3_api->compileoption_get #define sqlite3_compileoption_used sqlite3_api->compileoption_used #define sqlite3_create_function_v2 sqlite3_api->create_function_v2 #define sqlite3_db_config sqlite3_api->db_config #define sqlite3_db_mutex sqlite3_api->db_mutex #define sqlite3_db_status sqlite3_api->db_status #define sqlite3_extended_errcode sqlite3_api->extended_errcode #define sqlite3_log sqlite3_api->log #define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64 #define sqlite3_sourceid sqlite3_api->sourceid #define sqlite3_stmt_status sqlite3_api->stmt_status #define sqlite3_strnicmp sqlite3_api->strnicmp #define sqlite3_unlock_notify sqlite3_api->unlock_notify #define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint #define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint #define sqlite3_wal_hook sqlite3_api->wal_hook #endif /* SQLITE_CORE */ #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; #define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; #endif /* _SQLITE3EXT_H_ */ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
110 111 112 113 114 115 116 | # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* | | > > > > > > | | | 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 | # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #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 ** level of threadsafety. 2 means the libary is multithreaded - multiple ** threads can use SQLite as long as no two threads try to use the same ** database connection at the same time. ** ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy. */ #if !defined(SQLITE_THREADSAFE) #if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE #else # define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ #endif #endif /* ** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. ** It determines whether or not the features related to ** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can |
︙ | ︙ | |||
596 597 598 599 600 601 602 603 604 605 606 607 608 609 | typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; | > | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 | typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; |
︙ | ︙ | |||
903 904 905 906 907 908 909 | #define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ #define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when ** accessing read-only databases */ #define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ #define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ | | > | 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | #define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ #define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when ** accessing read-only databases */ #define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ #define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ #define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */ #define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ #define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */ #define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */ #define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */ /* ** Bits of the sqlite3.flags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. ** These must be the low-order bits of the flags field. */ #define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ |
︙ | ︙ | |||
952 953 954 955 956 957 958 959 960 961 962 963 964 965 | void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ }; /* ** Possible values for FuncDef.flags */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ | > > > > > > > > > > > > > > > > > > > > > | 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 | void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ FuncDestructor *pDestructor; /* Reference counted destructor function */ }; /* ** This structure encapsulates a user-function destructor callback (as ** configured using create_function_v2()) and a reference counter. When ** create_function_v2() is called to create a function with a destructor, ** a single object of this type is allocated. FuncDestructor.nRef is set to ** the number of FuncDef objects created (either 1 or 3, depending on whether ** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor ** member of each of the new FuncDef objects is set to point to the allocated ** FuncDestructor. ** ** Thereafter, when one of the FuncDef objects is deleted, the reference ** count on this object is decremented. When it reaches 0, the destructor ** is invoked and the FuncDestructor structure freed. */ struct FuncDestructor { int nRef; void (*xDestroy)(void *); void *pUserData; }; /* ** Possible values for FuncDef.flags */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ |
︙ | ︙ | |||
992 993 994 995 996 997 998 | ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ | | | | | | 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 | ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ pArg, 0, xFunc, 0, 0, #zName, 0, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} /* ** All current savepoints are stored in a linked list starting at ** sqlite3.pSavepoint. The first element in the list is the most recently ** opened savepoint. Savepoints are added to the list by the vdbe ** OP_Savepoint instruction. */ |
︙ | ︙ | |||
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | struct Table { char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK | > | 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 | struct Table { char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK |
︙ | ︙ | |||
1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 | u8 notIndexed; /* True if there is a NOT INDEXED clause */ 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 */ char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */ Index *pIndex; /* Index structure corresponding to zIndex, if any */ } 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 */ | > > > | 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 | u8 notIndexed; /* True if there is a NOT INDEXED clause */ 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 */ char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */ Index *pIndex; /* Index structure corresponding to zIndex, if any */ #ifndef SQLITE_OMIT_EXPLAIN int iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */ #endif } 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 */ |
︙ | ︙ | |||
1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 | ** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx ** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the ** case that more than one of these conditions is true. */ struct WherePlan { u32 wsFlags; /* WHERE_* flags that describe the strategy */ u32 nEq; /* Number of == constraints */ union { Index *pIdx; /* Index when WHERE_INDEXED is true */ struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ } u; }; | > | 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 | ** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx ** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the ** case that more than one of these conditions is true. */ struct WherePlan { u32 wsFlags; /* WHERE_* flags that describe the strategy */ u32 nEq; /* Number of == constraints */ double nRow; /* Estimated number of rows (for EQP) */ union { Index *pIdx; /* Index when WHERE_INDEXED is true */ struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ } u; }; |
︙ | ︙ | |||
1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 | SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may | > | 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 | SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ double nRowOut; /* Estimated number of output rows */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may |
︙ | ︙ | |||
1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 | Select *pPrior; /* Prior select in a compound select statement */ Select *pNext; /* Next select to the left in a compound */ Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ }; /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ #define SF_Distinct 0x0001 /* Output should be DISTINCT */ | > | 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 | Select *pPrior; /* Prior select in a compound select statement */ Select *pNext; /* Next select to the left in a compound */ Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ double nSelectRow; /* Estimated number of result rows */ }; /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ #define SF_Distinct 0x0001 /* Output should be DISTINCT */ |
︙ | ︙ | |||
2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 | u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ }; #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else #define IN_DECLARE_VTAB (pParse->declareVtab) #endif | > > > > > | 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 | u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ #ifndef SQLITE_OMIT_EXPLAIN int iSelectId; int iNextSelectId; #endif }; #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else #define IN_DECLARE_VTAB (pParse->declareVtab) #endif |
︙ | ︙ | |||
2474 2475 2476 2477 2478 2479 2480 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ int sqlite3StrICmp(const char *, const char *); | < | 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 | # define sqlite3Tolower(x) tolower((unsigned char)(x)) #endif /* ** Internal function prototypes */ int sqlite3StrICmp(const char *, const char *); int sqlite3Strlen30(const char*); #define sqlite3StrNICmp sqlite3_strnicmp int sqlite3MallocInit(void); void sqlite3MallocEnd(void); void *sqlite3Malloc(int); void *sqlite3MallocZero(int); |
︙ | ︙ | |||
2498 2499 2500 2501 2502 2503 2504 | int sqlite3DbMallocSize(sqlite3*, void*); void *sqlite3ScratchMalloc(int); void sqlite3ScratchFree(void*); void *sqlite3PageMalloc(int); void sqlite3PageFree(void*); void sqlite3MemSetDefault(void); void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); | | | 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 | int sqlite3DbMallocSize(sqlite3*, void*); void *sqlite3ScratchMalloc(int); void sqlite3ScratchFree(void*); void *sqlite3PageMalloc(int); void sqlite3PageFree(void*); void sqlite3MemSetDefault(void); void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); int sqlite3HeapNearlyFull(void); /* ** On systems with ample stack space and that support alloca(), make ** use of alloca() to obtain space for large automatic objects. By default, ** obtain space from malloc(). ** ** The alloca() routine never returns NULL. This will cause code paths |
︙ | ︙ | |||
2669 2670 2671 2672 2673 2674 2675 | void sqlite3ExprCodeCopy(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); void sqlite3ExprCachePop(Parse*, int); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); | < | 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 | void sqlite3ExprCodeCopy(Parse*, int, int, int); void sqlite3ExprCacheStore(Parse*, int, int, int); void sqlite3ExprCachePush(Parse*); void sqlite3ExprCachePop(Parse*, int); void sqlite3ExprCacheRemove(Parse*, int, int); void sqlite3ExprCacheClear(Parse*); void sqlite3ExprCacheAffinityChange(Parse*, int, int); int sqlite3ExprCode(Parse*, Expr*, int); int sqlite3ExprCodeTemp(Parse*, Expr*, int*); int sqlite3ExprCodeTarget(Parse*, Expr*, int); int sqlite3ExprCodeAndCache(Parse*, Expr*, int); void sqlite3ExprCodeConstants(Parse*, Expr*); int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int); void sqlite3ExprIfTrue(Parse*, Expr*, int, int); |
︙ | ︙ | |||
2789 2790 2791 2792 2793 2794 2795 | # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); void sqlite3Detach(Parse*, Expr*); | < < | < | 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 | # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK # define sqlite3AuthContextPush(a,b,c) # define sqlite3AuthContextPop(a) ((void)(a)) #endif void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); void sqlite3Detach(Parse*, Expr*); int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); int sqlite3FixSrcList(DbFixer*, SrcList*); int sqlite3FixSelect(DbFixer*, Select*); int sqlite3FixExpr(DbFixer*, Expr*); int sqlite3FixExprList(DbFixer*, ExprList*); int sqlite3FixTriggerStep(DbFixer*, TriggerStep*); int sqlite3AtoF(const char *z, double*, int, u8); int sqlite3GetInt32(const char *, int*); int sqlite3Utf16ByteLen(const void *pData, int nChar); int sqlite3Utf8CharLen(const char *pData, int nByte); int sqlite3Utf8Read(const u8*, const u8**); /* ** Routines to read and write variable-length integers. These used to ** be defined locally, but now we use the varint routines in the util.c |
︙ | ︙ | |||
2845 2846 2847 2848 2849 2850 2851 | const char *sqlite3IndexAffinityStr(Vdbe *, Index *); void sqlite3TableAffinityStr(Vdbe *, Table *); char sqlite3CompareAffinity(Expr *pExpr, char aff2); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); char sqlite3ExprAffinity(Expr *pExpr); | | | 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 | const char *sqlite3IndexAffinityStr(Vdbe *, Index *); void sqlite3TableAffinityStr(Vdbe *, Table *); char sqlite3CompareAffinity(Expr *pExpr, char aff2); int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); char sqlite3ExprAffinity(Expr *pExpr); int sqlite3Atoi64(const char*, i64*, int, u8); void sqlite3Error(sqlite3*, int, const char*,...); void *sqlite3HexToBlob(sqlite3*, const char *z, int n); int sqlite3TwoPartName(Parse *, Token *, Token *, Token **); const char *sqlite3ErrStr(int); int sqlite3ReadSchema(Parse *pParse); CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int); CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); |
︙ | ︙ | |||
2916 2917 2918 2919 2920 2921 2922 | void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3SchemaFree(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), | | > > | 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 | void sqlite3MinimumFileFormat(Parse*, int, int); void sqlite3SchemaFree(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*), FuncDestructor *pDestructor ); int sqlite3ApiExit(sqlite3 *db, int); int sqlite3OpenTempDatabase(Parse *); void sqlite3StrAccumInit(StrAccum*, char*, int, int); void sqlite3StrAccumAppend(StrAccum*,const char*,int); char *sqlite3StrAccumFinish(StrAccum*); void sqlite3StrAccumReset(StrAccum*); |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
666 667 668 669 670 671 672 673 674 675 676 677 678 679 | Tcl_IncrRefCount(pCmd); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj( ( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid)); Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); } static void tclCollateNeeded( void *pCtx, sqlite3 *db, int enc, const char *zName | > | 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 | Tcl_IncrRefCount(pCmd); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj( ( (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE"), -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zDb, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewStringObj(zTbl, -1)); Tcl_ListObjAppendElement(0, pCmd, Tcl_NewWideIntObj(rowid)); Tcl_EvalObjEx(pDb->interp, pCmd, TCL_EVAL_DIRECT); Tcl_DecrRefCount(pCmd); } static void tclCollateNeeded( void *pCtx, sqlite3 *db, int enc, const char *zName |
︙ | ︙ | |||
3010 3011 3012 3013 3014 3015 3016 3017 | ** Initialize this module. ** ** This Tcl module contains only a single new Tcl command named "sqlite". ** (Hence there is no namespace. There is no point in using a namespace ** if the extension only supplies one new name!) The "sqlite" command is ** used to open a new SQLite database. See the DbMain() routine above ** for additional information. */ | > > | > > > > > > > | | | | | | | | | 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 3045 3046 3047 3048 3049 | ** Initialize this module. ** ** This Tcl module contains only a single new Tcl command named "sqlite". ** (Hence there is no namespace. There is no point in using a namespace ** if the extension only supplies one new name!) The "sqlite" command is ** used to open a new SQLite database. See the DbMain() routine above ** for additional information. ** ** The EXTERN macros are required by TCL in order to work on windows. */ EXTERN int Sqlite3_Init(Tcl_Interp *interp){ Tcl_InitStubs(interp, "8.4", 0); Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0); Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION); #ifndef SQLITE_3_SUFFIX_ONLY /* The "sqlite" alias is undocumented. It is here only to support ** legacy scripts. All new scripts should use only the "sqlite3" ** command. */ Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0); #endif return TCL_OK; } EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } EXTERN int Sqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } EXTERN int Tclsqlite3_SafeInit(Tcl_Interp *interp){ return TCL_OK; } EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Sqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK; } EXTERN int Tclsqlite3_SafeUnload(Tcl_Interp *interp, int flags){ return TCL_OK;} #ifndef SQLITE_3_SUFFIX_ONLY int Sqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } int Tclsqlite_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); } int Sqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } int Tclsqlite_SafeInit(Tcl_Interp *interp){ return TCL_OK; } |
︙ | ︙ | |||
3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 | extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); extern int Sqlitetestbackup_Init(Tcl_Interp*); extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int SqlitetestStat_Init(Tcl_Interp*); Sqliteconfig_Init(interp); Sqlitetest1_Init(interp); Sqlitetest2_Init(interp); Sqlitetest3_Init(interp); Sqlitetest4_Init(interp); Sqlitetest5_Init(interp); | > > > > | 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 | extern int SqlitetestThread_Init(Tcl_Interp*); extern int SqlitetestOnefile_Init(); extern int SqlitetestOsinst_Init(Tcl_Interp*); extern int Sqlitetestbackup_Init(Tcl_Interp*); extern int Sqlitetestintarray_Init(Tcl_Interp*); extern int Sqlitetestvfs_Init(Tcl_Interp *); extern int SqlitetestStat_Init(Tcl_Interp*); extern int Sqlitetestrtree_Init(Tcl_Interp*); extern int Sqlitequota_Init(Tcl_Interp*); extern int Sqlitemultiplex_Init(Tcl_Interp*); extern int SqliteSuperlock_Init(Tcl_Interp*); Sqliteconfig_Init(interp); Sqlitetest1_Init(interp); Sqlitetest2_Init(interp); Sqlitetest3_Init(interp); Sqlitetest4_Init(interp); Sqlitetest5_Init(interp); |
︙ | ︙ | |||
3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 | SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); #endif } | > > > > | 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 | SqlitetestThread_Init(interp); SqlitetestOnefile_Init(interp); SqlitetestOsinst_Init(interp); Sqlitetestbackup_Init(interp); Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); #endif } |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
1231 1232 1233 1234 1235 1236 1237 | char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT INT\"", 0); return TCL_ERROR; } for(i=2; i<5; i++){ | | | 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 | char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT INT\"", 0); return TCL_ERROR; } for(i=2; i<5; i++){ if( sqlite3Atoi64(argv[i], &a[i-2], 1000000, SQLITE_UTF8) ){ Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0); return TCL_ERROR; } } z = sqlite3_mprintf(argv[1], a[0], a[1], a[2]); Tcl_AppendResult(interp, z, 0); sqlite3_free(z); |
︙ | ︙ | |||
1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 | Tcl_SetObjResult(interp, pRet); return TCL_OK; } #endif #ifndef SQLITE_OMIT_INCRBLOB /* ** sqlite3_blob_read CHANNEL OFFSET N ** ** This command is used to test the sqlite3_blob_read() in ways that ** the Tcl channel interface does not. The first argument should ** be the name of a valid channel created by the [incrblob] method | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1627 1628 1629 1630 1631 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 | Tcl_SetObjResult(interp, pRet); return TCL_OK; } #endif #ifndef SQLITE_OMIT_INCRBLOB static int blobHandleFromObj( Tcl_Interp *interp, Tcl_Obj *pObj, sqlite3_blob **ppBlob ){ char *z; int n; z = Tcl_GetStringFromObj(pObj, &n); if( n==0 ){ *ppBlob = 0; }else{ int notUsed; Tcl_Channel channel; ClientData instanceData; channel = Tcl_GetChannel(interp, z, ¬Used); if( !channel ) return TCL_ERROR; Tcl_Flush(channel); Tcl_Seek(channel, 0, SEEK_SET); instanceData = Tcl_GetChannelInstanceData(channel); *ppBlob = *((sqlite3_blob **)instanceData); } return TCL_OK; } /* ** sqlite3_blob_bytes CHANNEL */ static int test_blob_bytes( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_blob *pBlob; int nByte; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL"); return TCL_ERROR; } if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; nByte = sqlite3_blob_bytes(pBlob); Tcl_SetObjResult(interp, Tcl_NewIntObj(nByte)); return TCL_OK; } /* ** sqlite3_blob_close CHANNEL */ static int test_blob_close( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_blob *pBlob; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL"); return TCL_ERROR; } if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; sqlite3_blob_close(pBlob); return TCL_OK; } /* ** sqlite3_blob_read CHANNEL OFFSET N ** ** This command is used to test the sqlite3_blob_read() in ways that ** the Tcl channel interface does not. The first argument should ** be the name of a valid channel created by the [incrblob] method |
︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 | */ static int test_blob_read( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ | < < < | < | | < < < | | > | 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 | */ static int test_blob_read( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_blob *pBlob; int nByte; int iOffset; unsigned char *zBuf = 0; int rc; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET N"); return TCL_ERROR; } if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) || TCL_OK!=Tcl_GetIntFromObj(interp, objv[3], &nByte) ){ return TCL_ERROR; } if( nByte>0 ){ zBuf = (unsigned char *)Tcl_Alloc(nByte); } rc = sqlite3_blob_read(pBlob, zBuf, nByte, iOffset); if( rc==SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(zBuf, nByte)); }else{ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); } Tcl_Free((char *)zBuf); |
︙ | ︙ | |||
1665 1666 1667 1668 1669 1670 1671 | */ static int test_blob_write( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ | < < < | | < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 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 | */ static int test_blob_write( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_blob *pBlob; int iOffset; int rc; unsigned char *zBuf; int nBuf; if( objc!=4 && objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL OFFSET DATA ?NDATA?"); return TCL_ERROR; } if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iOffset) ){ return TCL_ERROR; } zBuf = Tcl_GetByteArrayFromObj(objv[3], &nBuf); if( objc==5 && Tcl_GetIntFromObj(interp, objv[4], &nBuf) ){ return TCL_ERROR; } rc = sqlite3_blob_write(pBlob, zBuf, nBuf, iOffset); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); } return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); } static int test_blob_reopen( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ Tcl_WideInt iRowid; sqlite3_blob *pBlob; int rc; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHANNEL ROWID"); return TCL_ERROR; } if( blobHandleFromObj(interp, objv[1], &pBlob) ) return TCL_ERROR; if( Tcl_GetWideIntFromObj(interp, objv[2], &iRowid) ) return TCL_ERROR; rc = sqlite3_blob_reopen(pBlob, iRowid); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE); } return (rc==SQLITE_OK ? TCL_OK : TCL_ERROR); } #endif /* ** Usage: sqlite3_create_collation_v2 DB-HANDLE NAME CMP-PROC DEL-PROC ** ** This Tcl proc is used for testing the experimental ** sqlite3_create_collation_v2() interface. |
︙ | ︙ | |||
1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 | return TCL_ERROR; } rc = sqlite3_create_collation_v2(db, Tcl_GetString(objv[2]), SQLITE_UTF8, (void *)p, testCreateCollationCmp, testCreateCollationDel ); return TCL_OK; } /* ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC? */ static int test_load_extension( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 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 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 | return TCL_ERROR; } rc = sqlite3_create_collation_v2(db, Tcl_GetString(objv[2]), SQLITE_UTF8, (void *)p, testCreateCollationCmp, testCreateCollationDel ); return TCL_OK; } /* ** USAGE: sqlite3_create_function_v2 DB NAME NARG ENC ?SWITCHES? ** ** Available switches are: ** ** -func SCRIPT ** -step SCRIPT ** -final SCRIPT ** -destroy SCRIPT */ typedef struct CreateFunctionV2 CreateFunctionV2; struct CreateFunctionV2 { Tcl_Interp *interp; Tcl_Obj *pFunc; /* Script for function invocation */ Tcl_Obj *pStep; /* Script for agg. step invocation */ Tcl_Obj *pFinal; /* Script for agg. finalization invocation */ Tcl_Obj *pDestroy; /* Destructor script */ }; static void cf2Func(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ } static void cf2Step(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ } static void cf2Final(sqlite3_context *ctx){ } static void cf2Destroy(void *pUser){ CreateFunctionV2 *p = (CreateFunctionV2 *)pUser; if( p->interp && p->pDestroy ){ int rc = Tcl_EvalObjEx(p->interp, p->pDestroy, 0); if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp); } if( p->pFunc ) Tcl_DecrRefCount(p->pFunc); if( p->pStep ) Tcl_DecrRefCount(p->pStep); if( p->pFinal ) Tcl_DecrRefCount(p->pFinal); if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy); sqlite3_free(p); } static int test_create_function_v2( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The invoking TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3 *db; const char *zFunc; int nArg; int enc; CreateFunctionV2 *p; int i; int rc; struct EncTable { const char *zEnc; int enc; } aEnc[] = { {"utf8", SQLITE_UTF8 }, {"utf16", SQLITE_UTF16 }, {"utf16le", SQLITE_UTF16LE }, {"utf16be", SQLITE_UTF16BE }, {"any", SQLITE_ANY }, {"0", 0 } }; if( objc<5 || (objc%2)==0 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG ENC SWITCHES..."); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zFunc = Tcl_GetString(objv[2]); if( Tcl_GetIntFromObj(interp, objv[3], &nArg) ) return TCL_ERROR; if( Tcl_GetIndexFromObjStruct(interp, objv[4], aEnc, sizeof(aEnc[0]), "encoding", 0, &enc) ){ return TCL_ERROR; } enc = aEnc[enc].enc; p = sqlite3_malloc(sizeof(CreateFunctionV2)); assert( p ); memset(p, 0, sizeof(CreateFunctionV2)); p->interp = interp; for(i=5; i<objc; i+=2){ int iSwitch; const char *azSwitch[] = {"-func", "-step", "-final", "-destroy", 0}; if( Tcl_GetIndexFromObj(interp, objv[i], azSwitch, "switch", 0, &iSwitch) ){ sqlite3_free(p); return TCL_ERROR; } switch( iSwitch ){ case 0: p->pFunc = objv[i+1]; break; case 1: p->pStep = objv[i+1]; break; case 2: p->pFinal = objv[i+1]; break; case 3: p->pDestroy = objv[i+1]; break; } } if( p->pFunc ) p->pFunc = Tcl_DuplicateObj(p->pFunc); if( p->pStep ) p->pStep = Tcl_DuplicateObj(p->pStep); if( p->pFinal ) p->pFinal = Tcl_DuplicateObj(p->pFinal); if( p->pDestroy ) p->pDestroy = Tcl_DuplicateObj(p->pDestroy); if( p->pFunc ) Tcl_IncrRefCount(p->pFunc); if( p->pStep ) Tcl_IncrRefCount(p->pStep); if( p->pFinal ) Tcl_IncrRefCount(p->pFinal); if( p->pDestroy ) Tcl_IncrRefCount(p->pDestroy); rc = sqlite3_create_function_v2(db, zFunc, nArg, enc, (void *)p, (p->pFunc ? cf2Func : 0), (p->pStep ? cf2Step : 0), (p->pFinal ? cf2Final : 0), cf2Destroy ); if( rc!=SQLITE_OK ){ Tcl_ResetResult(interp); Tcl_AppendResult(interp, sqlite3TestErrorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite3_load_extension DB-HANDLE FILE ?PROC? */ static int test_load_extension( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ |
︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 | if( pStmt ){ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } /* ** Usage: sqlite3_reset STMT ** ** Reset a statement handle. */ static int test_reset( | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 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 | if( pStmt ){ if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } /* ** Usage: sqlite3_stmt_readonly STMT ** ** Return true if STMT is a NULL pointer or a pointer to a statement ** that is guaranteed to leave the database unmodified. */ static int test_stmt_readonly( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_stmt *pStmt; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = sqlite3_stmt_readonly(pStmt); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc)); return TCL_OK; } /* ** Usage: sqlite3_reset STMT ** ** Reset a statement handle. */ static int test_reset( |
︙ | ︙ | |||
2287 2288 2289 2290 2291 2292 2293 | static Tcl_Interp* pTestCollateInterp; static int test_collate_func( void *pCtx, int nA, const void *zA, int nB, const void *zB ){ Tcl_Interp *i = pTestCollateInterp; | | | 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 | static Tcl_Interp* pTestCollateInterp; static int test_collate_func( void *pCtx, int nA, const void *zA, int nB, const void *zB ){ Tcl_Interp *i = pTestCollateInterp; int encin = SQLITE_PTR_TO_INT(pCtx); int res; int n; sqlite3_value *pVal; Tcl_Obj *pX; pX = Tcl_NewStringObj("test_collate", -1); |
︙ | ︙ | |||
2416 2417 2418 2419 2420 2421 2422 | int i; char *z; for(z = (char*)pName, i=0; *z || z[1]; z++){ if( *z ) zNeededCollation[i++] = *z; } zNeededCollation[i] = 0; sqlite3_create_collation( | | | 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 | int i; char *z; for(z = (char*)pName, i=0; *z || z[1]; z++){ if( *z ) zNeededCollation[i++] = *z; } zNeededCollation[i] = 0; sqlite3_create_collation( db, "test_collate", ENC(db), SQLITE_INT_TO_PTR(enc), test_collate_func); } /* ** Usage: add_test_collate_needed DB */ static int test_collate_needed( void * clientData, |
︙ | ︙ | |||
2465 2466 2467 2468 2469 2470 2471 | static int alignmentCollFunc( void *NotUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2 ){ int rc, n; n = nKey1<nKey2 ? nKey1 : nKey2; | | | | 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 | static int alignmentCollFunc( void *NotUsed, int nKey1, const void *pKey1, int nKey2, const void *pKey2 ){ int rc, n; n = nKey1<nKey2 ? nKey1 : nKey2; if( nKey1>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey1))) ) unaligned_string_counter++; if( nKey2>0 && 1==(1&(SQLITE_PTR_TO_INT(pKey2))) ) unaligned_string_counter++; rc = memcmp(pKey1, pKey2, n); if( rc==0 ){ rc = nKey1 - nKey2; } return rc; } static int add_alignment_test_collations( |
︙ | ︙ | |||
4280 4281 4282 4283 4284 4285 4286 | */ static int test_soft_heap_limit( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ | < | > < < | < < > | | 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 | */ static int test_soft_heap_limit( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_int64 amt; sqlite3_int64 N = -1; if( objc!=1 && objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "?N?"); return TCL_ERROR; } if( objc==2 ){ if( Tcl_GetWideIntFromObj(interp, objv[1], &N) ) return TCL_ERROR; } amt = sqlite3_soft_heap_limit64(N); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(amt)); return TCL_OK; } /* ** Usage: sqlite3_thread_cleanup ** ** Call the sqlite3_thread_cleanup API. |
︙ | ︙ | |||
5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 | Tcl_AppendResult(interp, "command has no objProc: ", Tcl_GetString(objv[1]), (char*)0); return TCL_ERROR; } return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1); } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 | Tcl_AppendResult(interp, "command has no objProc: ", Tcl_GetString(objv[1]), (char*)0); return TCL_ERROR; } return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1); } #ifndef SQLITE_OMIT_EXPLAIN /* ** WARNING: The following function, printExplainQueryPlan() is an exact ** copy of example code from eqp.in (eqp.html). If this code is modified, ** then the documentation copy needs to be modified as well. */ /* ** Argument pStmt is a prepared SQL statement. This function compiles ** an EXPLAIN QUERY PLAN command to report on the prepared statement, ** and prints the report to stdout using printf(). */ int printExplainQueryPlan(sqlite3_stmt *pStmt){ const char *zSql; /* Input SQL */ char *zExplain; /* SQL with EXPLAIN QUERY PLAN prepended */ sqlite3_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */ int rc; /* Return code from sqlite3_prepare_v2() */ zSql = sqlite3_sql(pStmt); if( zSql==0 ) return SQLITE_ERROR; zExplain = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zSql); if( zExplain==0 ) return SQLITE_NOMEM; rc = sqlite3_prepare_v2(sqlite3_db_handle(pStmt), zExplain, -1, &pExplain, 0); sqlite3_free(zExplain); if( rc!=SQLITE_OK ) return rc; while( SQLITE_ROW==sqlite3_step(pExplain) ){ int iSelectid = sqlite3_column_int(pExplain, 0); int iOrder = sqlite3_column_int(pExplain, 1); int iFrom = sqlite3_column_int(pExplain, 2); const char *zDetail = (const char *)sqlite3_column_text(pExplain, 3); printf("%d %d %d %s\n", iSelectid, iOrder, iFrom, zDetail); } return sqlite3_finalize(pExplain); } static int test_print_eqp( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite3_stmt *pStmt; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = printExplainQueryPlan(pStmt); Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } #endif /* SQLITE_OMIT_EXPLAIN */ /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; |
︙ | ︙ | |||
5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 | { "sqlite3_reset", test_reset ,0 }, { "sqlite3_expired", test_expired ,0 }, { "sqlite3_transfer_bindings", test_transfer_bind ,0 }, { "sqlite3_changes", test_changes ,0 }, { "sqlite3_step", test_step ,0 }, { "sqlite3_sql", test_sql ,0 }, { "sqlite3_next_stmt", test_next_stmt ,0 }, { "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, { "sqlite3_thread_cleanup", test_thread_cleanup, 0}, { "sqlite3_pager_refcounts", test_pager_refcounts, 0}, { "sqlite3_load_extension", test_load_extension, 0}, | > | 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 | { "sqlite3_reset", test_reset ,0 }, { "sqlite3_expired", test_expired ,0 }, { "sqlite3_transfer_bindings", test_transfer_bind ,0 }, { "sqlite3_changes", test_changes ,0 }, { "sqlite3_step", test_step ,0 }, { "sqlite3_sql", test_sql ,0 }, { "sqlite3_next_stmt", test_next_stmt ,0 }, { "sqlite3_stmt_readonly", test_stmt_readonly ,0 }, { "sqlite3_release_memory", test_release_memory, 0}, { "sqlite3_soft_heap_limit", test_soft_heap_limit, 0}, { "sqlite3_thread_cleanup", test_thread_cleanup, 0}, { "sqlite3_pager_refcounts", test_pager_refcounts, 0}, { "sqlite3_load_extension", test_load_extension, 0}, |
︙ | ︙ | |||
5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 | { "vfs_unregister_all", vfs_unregister_all, 0 }, { "vfs_reregister_all", vfs_reregister_all, 0 }, { "file_control_test", file_control_test, 0 }, { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "file_control_chunksize_test", file_control_chunksize_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, { "path_is_local", path_is_local, 0 }, { "path_is_dos", path_is_dos, 0 }, /* Functions from os.h */ #ifndef SQLITE_OMIT_UTF16 { "add_test_collate", test_collate, 0 }, { "add_test_collate_needed", test_collate_needed, 0 }, { "add_test_function", test_function, 0 }, #endif { "sqlite3_test_errstr", test_errstr, 0 }, { "tcl_variable_type", tcl_variable_type, 0 }, #ifndef SQLITE_OMIT_SHARED_CACHE { "sqlite3_enable_shared_cache", test_enable_shared, 0 }, { "sqlite3_shared_cache_report", sqlite3BtreeSharedCacheReport, 0}, #endif { "sqlite3_libversion_number", test_libversion_number, 0 }, #ifdef SQLITE_ENABLE_COLUMN_METADATA { "sqlite3_table_column_metadata", test_table_column_metadata, 0 }, #endif #ifndef SQLITE_OMIT_INCRBLOB | > | | > > > | | > | 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 | { "vfs_unregister_all", vfs_unregister_all, 0 }, { "vfs_reregister_all", vfs_reregister_all, 0 }, { "file_control_test", file_control_test, 0 }, { "file_control_lasterrno_test", file_control_lasterrno_test, 0 }, { "file_control_lockproxy_test", file_control_lockproxy_test, 0 }, { "file_control_chunksize_test", file_control_chunksize_test, 0 }, { "sqlite3_vfs_list", vfs_list, 0 }, { "sqlite3_create_function_v2", test_create_function_v2, 0 }, { "path_is_local", path_is_local, 0 }, { "path_is_dos", path_is_dos, 0 }, /* Functions from os.h */ #ifndef SQLITE_OMIT_UTF16 { "add_test_collate", test_collate, 0 }, { "add_test_collate_needed", test_collate_needed, 0 }, { "add_test_function", test_function, 0 }, #endif { "sqlite3_test_errstr", test_errstr, 0 }, { "tcl_variable_type", tcl_variable_type, 0 }, #ifndef SQLITE_OMIT_SHARED_CACHE { "sqlite3_enable_shared_cache", test_enable_shared, 0 }, { "sqlite3_shared_cache_report", sqlite3BtreeSharedCacheReport, 0}, #endif { "sqlite3_libversion_number", test_libversion_number, 0 }, #ifdef SQLITE_ENABLE_COLUMN_METADATA { "sqlite3_table_column_metadata", test_table_column_metadata, 0 }, #endif #ifndef SQLITE_OMIT_INCRBLOB { "sqlite3_blob_read", test_blob_read, 0 }, { "sqlite3_blob_write", test_blob_write, 0 }, { "sqlite3_blob_reopen", test_blob_reopen, 0 }, { "sqlite3_blob_bytes", test_blob_bytes, 0 }, { "sqlite3_blob_close", test_blob_close, 0 }, #endif { "pcache_stats", test_pcache_stats, 0 }, #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY { "sqlite3_unlock_notify", test_unlock_notify, 0 }, #endif { "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, { "print_explain_query_plan", test_print_eqp, 0 }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; |
︙ | ︙ |
Changes to src/test3.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | ** A bogus sqlite3 connection structure for use in the btree ** tests. */ static sqlite3 sDb; static int nRefSqlite3 = 0; /* | | | | < | | 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 | ** A bogus sqlite3 connection structure for use in the btree ** tests. */ static sqlite3 sDb; static int nRefSqlite3 = 0; /* ** Usage: btree_open FILENAME NCACHE ** ** Open a new database */ static int btree_open( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ const char **argv /* Text of each argument */ ){ Btree *pBt; int rc, nCache; char zBuf[100]; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME NCACHE FLAGS\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &nCache) ) return TCL_ERROR; nRefSqlite3++; if( nRefSqlite3==1 ){ sDb.pVfs = sqlite3_vfs_find(0); sDb.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); sqlite3_mutex_enter(sDb.mutex); } rc = sqlite3BtreeOpen(argv[1], &sDb, &pBt, 0, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_DB); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; } sqlite3BtreeSetCacheSize(pBt, nCache); sqlite3_snprintf(sizeof(zBuf), zBuf,"%p", pBt); |
︙ | ︙ |
Changes to src/test_config.c.
︙ | ︙ | |||
518 519 520 521 522 523 524 525 526 527 528 529 530 531 | #endif #ifdef SQLITE_SECURE_DELETE Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY); #endif #ifdef YYTRACKMAXSTACKDEPTH Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY); #endif | > > > > > > | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | #endif #ifdef SQLITE_SECURE_DELETE Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_MULTIPLEX_EXT_OVWR Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "0", TCL_GLOBAL_ONLY); #endif #ifdef YYTRACKMAXSTACKDEPTH Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ | |||
540 541 542 543 544 545 546 | Tcl_SetVar2(interp, "os_options", "arch", "arm", TCL_GLOBAL_ONLY); # else # error Unrecognized architecture for exec_options # endif #else Tcl_SetVar2(interp, "os_options", "arch", "unknown", TCL_GLOBAL_ONLY); #endif | | | 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | Tcl_SetVar2(interp, "os_options", "arch", "arm", TCL_GLOBAL_ONLY); # else # error Unrecognized architecture for exec_options # endif #else Tcl_SetVar2(interp, "os_options", "arch", "unknown", TCL_GLOBAL_ONLY); #endif #define LINKVAR(x) { \ static const int cv_ ## x = SQLITE_ ## x; \ Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \ TCL_LINK_INT | TCL_LINK_READ_ONLY); } LINKVAR( MAX_LENGTH ); LINKVAR( MAX_COLUMN ); |
︙ | ︙ |
Added src/test_multiplex.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 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 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 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 | /* ** 2010 October 28 ** ** 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 a VFS "shim" - a layer that sits in between the ** pager and the real VFS. ** ** This particular shim enforces a multiplex system on DB files. ** This shim shards/partitions a single DB file into smaller ** "chunks" such that the total DB file size may exceed the maximum ** file size of the underlying file system. ** */ #include "sqlite3.h" #include <string.h> #include <assert.h> #include "sqliteInt.h" /************************ Shim Definitions ******************************/ /* This is the limit on the chunk size. It may be changed by calling ** the sqlite3_multiplex_set() interface. */ #define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000 /* Default limit on number of chunks. Care should be taken ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT ** format specifier. It may be changed by calling ** the sqlite3_multiplex_set() interface. */ #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the ** last SQLITE_MULTIPLEX_EXT_SZ characters of the ** filename will be overwritten, otherwise, the ** multiplex extension is simply appended to the filename. ** Ex. (undefined) test.db -> test.db01 ** (defined) test.db -> test.01 ** Chunk 0 does not have a modified extension. */ #define SQLITE_MULTIPLEX_EXT_FMT "%02d" #define SQLITE_MULTIPLEX_EXT_SZ 2 /************************ Object Definitions ******************************/ /* Forward declaration of all object types */ typedef struct multiplexGroup multiplexGroup; typedef struct multiplexConn multiplexConn; /* ** A "multiplex group" is a collection of files that collectively ** makeup a single SQLite DB file. This allows the size of the DB ** to exceed the limits imposed by the file system. ** ** There is an instance of the following object for each defined multiplex ** group. */ struct multiplexGroup { sqlite3_file **pReal; /* Handles to each chunk */ char *bOpen; /* 0 if chunk not opened */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ }; /* ** An instance of the following object represents each open connection ** to a file that is multiplex'ed. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying ** VFS is appended to this structure. */ struct multiplexConn { sqlite3_file base; /* Base class - must be first */ multiplexGroup *pGroup; /* The underlying group of files */ }; /************************* Global Variables **********************************/ /* ** All global variables used by this file are containing within the following ** gMultiplex structure. */ static struct { /* The pOrigVfs is the real, original underlying VFS implementation. ** Most operations pass-through to the real VFS. This value is read-only ** during operation. It is only modified at start-time and thus does not ** require a mutex. */ sqlite3_vfs *pOrigVfs; /* The sThisVfs is the VFS structure used by this shim. It is initialized ** at start-time and thus does not require a mutex */ sqlite3_vfs sThisVfs; /* The sIoMethods defines the methods used by sqlite3_file objects ** associated with this shim. It is initialized at start-time and does ** not require a mutex. ** ** When the underlying VFS is called to open a file, it might return ** either a version 1 or a version 2 sqlite3_file object. This shim ** has to create a wrapper sqlite3_file of the same version. Hence ** there are two I/O method structures, one for version 1 and the other ** for version 2. */ sqlite3_io_methods sIoMethodsV1; sqlite3_io_methods sIoMethodsV2; /* True when this shim has been initialized. */ int isInitialized; /* For run-time access any of the other global data structures in this ** shim, the following mutex must be held. */ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; /* Chunk params. */ int nChunkSize; int nMaxChunks; /* Storage for temp file names. Allocated during ** initialization to the max pathname of the underlying VFS. */ char *zName; } gMultiplex; /************************* Utility Routines *********************************/ /* ** Acquire and release the mutex used to serialize access to the ** list of multiplexGroups. */ static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } /* Translate an sqlite3_file* that is really a multiplexGroup* into ** the sqlite3_file* for the underlying original VFS. */ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ multiplexGroup *pGroup = pConn->pGroup; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ if( iChunk<gMultiplex.nMaxChunks ){ sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ if( !pGroup->bOpen[iChunk] ){ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); if( iChunk ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk); #endif } *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags); if( *rc==SQLITE_OK ){ pGroup->bOpen[iChunk] = -1; return pSubOpen; } return NULL; } *rc = SQLITE_OK; return pSubOpen; } *rc = SQLITE_FULL; return NULL; } /************************* VFS Method Wrappers *****************************/ /* ** This is the xOpen method used for the "multiplex" VFS. ** ** Most of the work is done by the underlying original VFS. This method ** simply links the new file into the appropriate multiplex group if it is a ** file that needs to be tracked. */ static int multiplexOpen( sqlite3_vfs *pVfs, /* The multiplex VFS */ const char *zName, /* Name of file to be opened */ sqlite3_file *pConn, /* Fill in this file descriptor */ int flags, /* Flags to control the opening */ int *pOutFlags /* Flags showing results of opening */ ){ int rc; /* Result code */ multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int nName = sqlite3Strlen30(zName); int i; int sz; UNUSED_PARAMETER(pVfs); /* We need to create a group structure and manage ** access to this group of files. */ multiplexEnter(); pMultiplexOpen = (multiplexConn*)pConn; /* allocate space for group */ sz = sizeof(multiplexGroup) /* multiplexGroup */ + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */ + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */ + gMultiplex.nMaxChunks /* bOpen[] */ + nName + 1; /* zName */ #ifndef SQLITE_MULTIPLEX_EXT_OVWR sz += SQLITE_MULTIPLEX_EXT_SZ; assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); #else assert(nName >= SQLITE_MULTIPLEX_EXT_SZ); assert(nName < pOrigVfs->mxPathname); #endif pGroup = sqlite3_malloc( sz ); if( pGroup==0 ){ rc=SQLITE_NOMEM; }else{ /* assign pointers to extra space allocated */ char *p = (char *)&pGroup[1]; pMultiplexOpen->pGroup = pGroup; memset(pGroup, 0, sz); pGroup->pReal = (sqlite3_file **)p; p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks); for(i=0; i<gMultiplex.nMaxChunks; i++){ pGroup->pReal[i] = (sqlite3_file *)p; p += pOrigVfs->szOsFile; } pGroup->bOpen = p; p += gMultiplex.nMaxChunks; pGroup->zName = p; /* save off base filename, name length, and original open flags */ memcpy(pGroup->zName, zName, nName+1); pGroup->nName = nName; pGroup->flags = flags; pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); if( pSubOpen ){ if( pSubOpen->pMethods->iVersion==1 ){ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; }else{ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; } /* place this group at the head of our list */ pGroup->pNext = gMultiplex.pGroups; if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; gMultiplex.pGroups = pGroup; }else{ sqlite3_free(pGroup); } } multiplexLeave(); return rc; } /* ** This is the xDelete method used for the "multiplex" VFS. ** It attempts to delete the filename specified, as well ** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension. */ static int multiplexDelete( sqlite3_vfs *pVfs, /* The multiplex VFS */ const char *zName, /* Name of file to delete */ int syncDir ){ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int rc = SQLITE_OK; int nName = sqlite3Strlen30(zName); int i; UNUSED_PARAMETER(pVfs); multiplexEnter(); memcpy(gMultiplex.zName, zName, nName+1); for(i=0; i<gMultiplex.nMaxChunks; i++){ int rc2; int exists = 0; if( i ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif } rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); if( rc2==SQLITE_OK && exists){ /* if it exists, delete it */ rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); if( rc2!=SQLITE_OK ) rc = rc2; }else{ /* stop at first "gap" */ break; } } multiplexLeave(); return rc; } static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){ return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d); } static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){ return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d); } static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){ return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b); } static void multiplexDlError(sqlite3_vfs *a, int b, char *c){ gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c); } static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){ return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c); } static void multiplexDlClose(sqlite3_vfs *a, void *b){ gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b); } static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){ return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c); } static int multiplexSleep(sqlite3_vfs *a, int b){ return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b); } static int multiplexCurrentTime(sqlite3_vfs *a, double *b){ return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b); } static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){ return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c); } static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){ return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b); } /************************ I/O Method Wrappers *******************************/ /* xClose requests get passed through to the original VFS. ** We loop over all open chunk handles and close them. ** The group structure for this file is unlinked from ** our list of groups and freed. */ static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); /* close any open handles */ for(i=0; i<gMultiplex.nMaxChunks; i++){ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; int rc2 = pSubOpen->pMethods->xClose(pSubOpen); if( rc2!=SQLITE_OK ) rc = rc2; pGroup->bOpen[i] = 0; } } /* remove from linked list */ if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; if( pGroup->pPrev ){ pGroup->pPrev->pNext = pGroup->pNext; }else{ gMultiplex.pGroups = pGroup->pNext; } sqlite3_free(pGroup); multiplexLeave(); return rc; } /* Pass xRead requests thru to the original VFS after ** determining the correct chunk to operate on. ** Break up reads across chunk boundaries. */ static int multiplexRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; int rc = SQLITE_OK; multiplexEnter(); while( iAmt > 0 ){ int i = (int)(iOfst/gMultiplex.nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); if( rc!=SQLITE_OK ) break; pBuf = (char *)pBuf + iAmt; iOfst += iAmt; iAmt = extra; }else{ rc = SQLITE_IOERR_READ; break; } } multiplexLeave(); return rc; } /* Pass xWrite requests thru to the original VFS after ** determining the correct chunk to operate on. ** Break up writes across chunk boundaries. */ static int multiplexWrite( sqlite3_file *pConn, const void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; int rc = SQLITE_OK; multiplexEnter(); while( iAmt > 0 ){ int i = (int)(iOfst/gMultiplex.nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); if( rc!=SQLITE_OK ) break; pBuf = (char *)pBuf + iAmt; iOfst += iAmt; iAmt = extra; }else{ rc = SQLITE_IOERR_WRITE; break; } } multiplexLeave(); return rc; } /* Pass xTruncate requests thru to the original VFS after ** determining the correct chunk to operate on. Delete any ** chunks above the truncate mark. */ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int rc2; int i; sqlite3_file *pSubOpen; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ multiplexEnter(); memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); /* delete the chunks above the truncate limit */ for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){ /* close any open chunks before deleting them */ if( pGroup->bOpen[i] ){ pSubOpen = pGroup->pReal[i]; rc2 = pSubOpen->pMethods->xClose(pSubOpen); if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; pGroup->bOpen[i] = 0; } #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; } pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL); if( pSubOpen ){ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize); if( rc2!=SQLITE_OK ) rc = rc2; }else{ rc = SQLITE_IOERR_TRUNCATE; } multiplexLeave(); return rc; } /* Pass xSync requests through to the original VFS without change */ static int multiplexSync(sqlite3_file *pConn, int flags){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); for(i=0; i<gMultiplex.nMaxChunks; i++){ /* if we don't have it open, we don't need to sync it */ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); if( rc2!=SQLITE_OK ) rc = rc2; } } multiplexLeave(); return rc; } /* Pass xFileSize requests through to the original VFS. ** Aggregate the size of all the chunks before returning. */ static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int rc2; int i; multiplexEnter(); *pSize = 0; for(i=0; i<gMultiplex.nMaxChunks; i++){ sqlite3_file *pSubOpen = NULL; /* if not opened already, check to see if the chunk exists */ if( pGroup->bOpen[i] ){ pSubOpen = pGroup->pReal[i]; }else{ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int exists = 0; memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); if( i ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif } rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); if( rc2==SQLITE_OK && exists){ /* if it exists, open it */ pSubOpen = multiplexSubOpen(p, i, &rc, NULL); }else{ /* stop at first "gap" */ break; } } if( pSubOpen ){ sqlite3_int64 sz; rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( rc2!=SQLITE_OK ){ rc = rc2; }else{ *pSize += sz; assert(sz<=gMultiplex.nChunkSize); } }else{ break; } } multiplexLeave(); return rc; } /* Pass xLock requests through to the original VFS unchanged. */ static int multiplexLock(sqlite3_file *pConn, int lock){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xLock(pSubOpen, lock); } return SQLITE_BUSY; } /* Pass xUnlock requests through to the original VFS unchanged. */ static int multiplexUnlock(sqlite3_file *pConn, int lock){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xUnlock(pSubOpen, lock); } return SQLITE_IOERR_UNLOCK; } /* Pass xCheckReservedLock requests through to the original VFS unchanged. */ static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } return SQLITE_IOERR_CHECKRESERVEDLOCK; } /* Pass xFileControl requests through to the original VFS unchanged. */ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen; if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK; pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); } return SQLITE_ERROR; } /* Pass xSectorSize requests through to the original VFS unchanged. */ static int multiplexSectorSize(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xSectorSize(pSubOpen); } return SQLITE_DEFAULT_SECTOR_SIZE; } /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. */ static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); } return 0; } /* Pass xShmMap requests through to the original VFS unchanged. */ static int multiplexShmMap( sqlite3_file *pConn, /* Handle open on database file */ int iRegion, /* Region to retrieve */ int szRegion, /* Size of regions */ int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); } return SQLITE_IOERR; } /* Pass xShmLock requests through to the original VFS unchanged. */ static int multiplexShmLock( sqlite3_file *pConn, /* Database file holding the shared memory */ int ofst, /* First lock to acquire or release */ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); } return SQLITE_BUSY; } /* Pass xShmBarrier requests through to the original VFS unchanged. */ static void multiplexShmBarrier(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ pSubOpen->pMethods->xShmBarrier(pSubOpen); } } /* Pass xShmUnmap requests through to the original VFS unchanged. */ static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ multiplexConn *p = (multiplexConn*)pConn; int rc; sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } /************************** Public Interfaces *****************************/ /* ** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName ** as the VFS that does the actual work. Use the default if ** zOrigVfsName==NULL. ** ** The multiplex VFS shim is named "multiplex". It will become the default ** VFS if makeDefault is non-zero. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once ** during start-up. */ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ sqlite3_vfs *pOrigVfs; if( gMultiplex.isInitialized ) return SQLITE_MISUSE; pOrigVfs = sqlite3_vfs_find(zOrigVfsName); if( pOrigVfs==0 ) return SQLITE_ERROR; assert( pOrigVfs!=&gMultiplex.sThisVfs ); gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( !gMultiplex.pMutex ){ return SQLITE_NOMEM; } gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); if( !gMultiplex.zName ){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; gMultiplex.pGroups = NULL; gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); gMultiplex.sThisVfs.zName = "multiplex"; gMultiplex.sThisVfs.xOpen = multiplexOpen; gMultiplex.sThisVfs.xDelete = multiplexDelete; gMultiplex.sThisVfs.xAccess = multiplexAccess; gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; gMultiplex.sThisVfs.xDlError = multiplexDlError; gMultiplex.sThisVfs.xDlSym = multiplexDlSym; gMultiplex.sThisVfs.xDlClose = multiplexDlClose; gMultiplex.sThisVfs.xRandomness = multiplexRandomness; gMultiplex.sThisVfs.xSleep = multiplexSleep; gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime; gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError; gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64; gMultiplex.sIoMethodsV1.iVersion = 1; gMultiplex.sIoMethodsV1.xClose = multiplexClose; gMultiplex.sIoMethodsV1.xRead = multiplexRead; gMultiplex.sIoMethodsV1.xWrite = multiplexWrite; gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate; gMultiplex.sIoMethodsV1.xSync = multiplexSync; gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize; gMultiplex.sIoMethodsV1.xLock = multiplexLock; gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock; gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock; gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl; gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize; gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics; gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; gMultiplex.sIoMethodsV2.iVersion = 2; gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); return SQLITE_OK; } /* ** Shutdown the multiplex system. ** ** All SQLite database connections must be closed before calling this ** routine. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ int sqlite3_multiplex_shutdown(void){ if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; if( gMultiplex.pGroups ) return SQLITE_MISUSE; gMultiplex.isInitialized = 0; sqlite3_free(gMultiplex.zName); sqlite3_mutex_free(gMultiplex.pMutex); sqlite3_vfs_unregister(&gMultiplex.sThisVfs); memset(&gMultiplex, 0, sizeof(gMultiplex)); return SQLITE_OK; } /* ** Adjust chunking params. VFS should be initialized first. ** No files should be open. Re-intializing will reset these ** to the default. */ int sqlite3_multiplex_set( int nChunkSize, /* Max chunk size */ int nMaxChunks /* Max number of chunks */ ){ if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; if( gMultiplex.pGroups ) return SQLITE_MISUSE; if( nChunkSize<32 ) return SQLITE_MISUSE; if( nMaxChunks<1 ) return SQLITE_MISUSE; if( nMaxChunks>99 ) return SQLITE_MISUSE; multiplexEnter(); gMultiplex.nChunkSize = nChunkSize; gMultiplex.nMaxChunks = nMaxChunks; multiplexLeave(); return SQLITE_OK; } /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include <tcl.h> extern const char *sqlite3TestErrorName(int); /* ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT */ static int test_multiplex_initialize( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zName; /* Name of new multiplex VFS */ int makeDefault; /* True to make the new VFS the default */ int rc; /* Value returned by multiplex_initialize() */ UNUSED_PARAMETER(clientData); /* Process arguments */ if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); return TCL_ERROR; } zName = Tcl_GetString(objv[1]); if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; if( zName[0]=='\0' ) zName = 0; /* Call sqlite3_multiplex_initialize() */ rc = sqlite3_multiplex_initialize(zName, makeDefault); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_multiplex_shutdown */ static int test_multiplex_shutdown( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; /* Value returned by multiplex_shutdown() */ UNUSED_PARAMETER(clientData); if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS */ static int test_multiplex_set( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int nChunkSize; /* Max chunk size */ int nMaxChunks; /* Max number of chunks */ int rc; /* Value returned by sqlite3_multiplex_set() */ UNUSED_PARAMETER(clientData); /* Process arguments */ if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS"); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR; /* Invoke sqlite3_multiplex_set() */ rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_multiplex_dump */ static int test_multiplex_dump( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_Obj *pResult; Tcl_Obj *pGroupTerm; multiplexGroup *pGroup; int i; int nChunks = 0; UNUSED_PARAMETER(clientData); UNUSED_PARAMETER(objc); UNUSED_PARAMETER(objv); pResult = Tcl_NewObj(); multiplexEnter(); for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){ pGroupTerm = Tcl_NewObj(); pGroup->zName[pGroup->nName] = '\0'; Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewStringObj(pGroup->zName, -1)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nName)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->flags)); /* count number of chunks with open handles */ for(i=0; i<gMultiplex.nMaxChunks; i++){ if( pGroup->bOpen[i] ) nChunks++; } Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(nChunks)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(gMultiplex.nChunkSize)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(gMultiplex.nMaxChunks)); Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } multiplexLeave(); Tcl_SetObjResult(interp, pResult); return TCL_OK; } /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside ** of this module. */ int Sqlitemultiplex_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3_multiplex_initialize", test_multiplex_initialize }, { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, { "sqlite3_multiplex_set", test_multiplex_set }, { "sqlite3_multiplex_dump", test_multiplex_dump }, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } return TCL_OK; } #endif |
Added src/test_quota.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 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 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 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 | /* ** 2010 September 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. ** ************************************************************************* ** ** This file contains a VFS "shim" - a layer that sits in between the ** pager and the real VFS. ** ** This particular shim enforces a quota system on files. One or more ** database files are in a "quota group" that is defined by a GLOB ** pattern. A quota is set for the combined size of all files in the ** the group. A quota of zero means "no limit". If the total size ** of all files in the quota group is greater than the limit, then ** write requests that attempt to enlarge a file fail with SQLITE_FULL. ** ** However, before returning SQLITE_FULL, the write requests invoke ** a callback function that is configurable for each quota group. ** This callback has the opportunity to enlarge the quota. If the ** callback does enlarge the quota such that the total size of all ** files within the group is less than the new quota, then the write ** continues as if nothing had happened. */ #include "sqlite3.h" #include <string.h> #include <assert.h> /************************ Object Definitions ******************************/ /* Forward declaration of all object types */ typedef struct quotaGroup quotaGroup; typedef struct quotaConn quotaConn; typedef struct quotaFile quotaFile; /* ** A "quota group" is a collection of files whose collective size we want ** to limit. Each quota group is defined by a GLOB pattern. ** ** There is an instance of the following object for each defined quota ** group. This object records the GLOB pattern that defines which files ** belong to the quota group. The object also remembers the size limit ** for the group (the quota) and the callback to be invoked when the ** sum of the sizes of the files within the group goes over the limit. ** ** A quota group must be established (using sqlite3_quota_set(...)) ** prior to opening any of the database connections that access files ** within the quota group. */ struct quotaGroup { const char *zPattern; /* Filename pattern to be quotaed */ sqlite3_int64 iLimit; /* Upper bound on total file size */ sqlite3_int64 iSize; /* Current size of all files */ void (*xCallback)( /* Callback invoked when going over quota */ const char *zFilename, /* Name of file whose size increases */ sqlite3_int64 *piLimit, /* IN/OUT: The current limit */ sqlite3_int64 iSize, /* Total size of all files in the group */ void *pArg /* Client data */ ); void *pArg; /* Third argument to the xCallback() */ void (*xDestroy)(void*); /* Optional destructor for pArg */ quotaGroup *pNext, **ppPrev; /* Doubly linked list of all quota objects */ quotaFile *pFiles; /* Files within this group */ }; /* ** An instance of this structure represents a single file that is part ** of a quota group. A single file can be opened multiple times. In ** order keep multiple openings of the same file from causing the size ** of the file to count against the quota multiple times, each file ** has a unique instance of this object and multiple open connections ** to the same file each point to a single instance of this object. */ struct quotaFile { char *zFilename; /* Name of this file */ quotaGroup *pGroup; /* Quota group to which this file belongs */ sqlite3_int64 iSize; /* Current size of this file */ int nRef; /* Number of times this file is open */ quotaFile *pNext, **ppPrev; /* Linked list of files in the same group */ }; /* ** An instance of the following object represents each open connection ** to a file that participates in quota tracking. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying ** VFS is appended to this structure. */ struct quotaConn { sqlite3_file base; /* Base class - must be first */ quotaFile *pFile; /* The underlying file */ /* The underlying VFS sqlite3_file is appended to this object */ }; /************************* Global Variables **********************************/ /* ** All global variables used by this file are containing within the following ** gQuota structure. */ static struct { /* The pOrigVfs is the real, original underlying VFS implementation. ** Most operations pass-through to the real VFS. This value is read-only ** during operation. It is only modified at start-time and thus does not ** require a mutex. */ sqlite3_vfs *pOrigVfs; /* The sThisVfs is the VFS structure used by this shim. It is initialized ** at start-time and thus does not require a mutex */ sqlite3_vfs sThisVfs; /* The sIoMethods defines the methods used by sqlite3_file objects ** associated with this shim. It is initialized at start-time and does ** not require a mutex. ** ** When the underlying VFS is called to open a file, it might return ** either a version 1 or a version 2 sqlite3_file object. This shim ** has to create a wrapper sqlite3_file of the same version. Hence ** there are two I/O method structures, one for version 1 and the other ** for version 2. */ sqlite3_io_methods sIoMethodsV1; sqlite3_io_methods sIoMethodsV2; /* True when this shim as been initialized. */ int isInitialized; /* For run-time access any of the other global data structures in this ** shim, the following mutex must be held. */ sqlite3_mutex *pMutex; /* List of quotaGroup objects. */ quotaGroup *pGroup; } gQuota; /************************* Utility Routines *********************************/ /* ** Acquire and release the mutex used to serialize access to the ** list of quotaGroups. */ static void quotaEnter(void){ sqlite3_mutex_enter(gQuota.pMutex); } static void quotaLeave(void){ sqlite3_mutex_leave(gQuota.pMutex); } /* If the reference count and threshold for a quotaGroup are both ** zero, then destroy the quotaGroup. */ static void quotaGroupDeref(quotaGroup *pGroup){ if( pGroup->pFiles==0 && pGroup->iLimit==0 ){ *pGroup->ppPrev = pGroup->pNext; if( pGroup->pNext ) pGroup->pNext->ppPrev = pGroup->ppPrev; if( pGroup->xDestroy ) pGroup->xDestroy(pGroup->pArg); sqlite3_free(pGroup); } } /* ** Return TRUE if string z matches glob pattern zGlob. ** ** Globbing rules: ** ** '*' Matches any sequence of zero or more characters. ** ** '?' Matches exactly one character. ** ** [...] Matches one character from the enclosed list of ** characters. ** ** [^...] Matches one character not in the enclosed list. ** */ static int quotaStrglob(const char *zGlob, const char *z){ int c, c2; int invert; int seen; while( (c = (*(zGlob++)))!=0 ){ if( c=='*' ){ while( (c=(*(zGlob++))) == '*' || c=='?' ){ if( c=='?' && (*(z++))==0 ) return 0; } if( c==0 ){ return 1; }else if( c=='[' ){ while( *z && quotaStrglob(zGlob-1,z)==0 ){ z++; } return (*z)!=0; } while( (c2 = (*(z++)))!=0 ){ while( c2!=c ){ c2 = *(z++); if( c2==0 ) return 0; } if( quotaStrglob(zGlob,z) ) return 1; } return 0; }else if( c=='?' ){ if( (*(z++))==0 ) return 0; }else if( c=='[' ){ int prior_c = 0; seen = 0; invert = 0; c = *(z++); if( c==0 ) return 0; c2 = *(zGlob++); if( c2=='^' ){ invert = 1; c2 = *(zGlob++); } if( c2==']' ){ if( c==']' ) seen = 1; c2 = *(zGlob++); } while( c2 && c2!=']' ){ if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ c2 = *(zGlob++); if( c>=prior_c && c<=c2 ) seen = 1; prior_c = 0; }else{ if( c==c2 ){ seen = 1; } prior_c = c2; } c2 = *(zGlob++); } if( c2==0 || (seen ^ invert)==0 ) return 0; }else{ if( c!=(*(z++)) ) return 0; } } return *z==0; } /* Find a quotaGroup given the filename. ** ** Return a pointer to the quotaGroup object. Return NULL if not found. */ static quotaGroup *quotaGroupFind(const char *zFilename){ quotaGroup *p; for(p=gQuota.pGroup; p && quotaStrglob(p->zPattern, zFilename)==0; p=p->pNext){} return p; } /* Translate an sqlite3_file* that is really a quotaConn* into ** the sqlite3_file* for the underlying original VFS. */ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){ quotaConn *p = (quotaConn*)pConn; return (sqlite3_file*)&p[1]; } /************************* VFS Method Wrappers *****************************/ /* ** This is the xOpen method used for the "quota" VFS. ** ** Most of the work is done by the underlying original VFS. This method ** simply links the new file into the appropriate quota group if it is a ** file that needs to be tracked. */ static int quotaOpen( sqlite3_vfs *pVfs, /* The quota VFS */ const char *zName, /* Name of file to be opened */ sqlite3_file *pConn, /* Fill in this file descriptor */ int flags, /* Flags to control the opening */ int *pOutFlags /* Flags showing results of opening */ ){ int rc; /* Result code */ quotaConn *pQuotaOpen; /* The new quota file descriptor */ quotaFile *pFile; /* Corresponding quotaFile obj */ quotaGroup *pGroup; /* The group file belongs to */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gQuota.pOrigVfs; /* Real VFS */ /* If the file is not a main database file or a WAL, then use the ** normal xOpen method. */ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL))==0 ){ return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags); } /* If the name of the file does not match any quota group, then ** use the normal xOpen method. */ quotaEnter(); pGroup = quotaGroupFind(zName); if( pGroup==0 ){ rc = pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags); }else{ /* If we get to this point, it means the file needs to be quota tracked. */ pQuotaOpen = (quotaConn*)pConn; pSubOpen = quotaSubOpen(pConn); rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags); if( rc==SQLITE_OK ){ for(pFile=pGroup->pFiles; pFile && strcmp(pFile->zFilename, zName); pFile=pFile->pNext){} if( pFile==0 ){ int nName = strlen(zName); pFile = sqlite3_malloc( sizeof(*pFile) + nName + 1 ); if( pFile==0 ){ quotaLeave(); pSubOpen->pMethods->xClose(pSubOpen); return SQLITE_NOMEM; } memset(pFile, 0, sizeof(*pFile)); pFile->zFilename = (char*)&pFile[1]; memcpy(pFile->zFilename, zName, nName+1); pFile->pNext = pGroup->pFiles; if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext; pFile->ppPrev = &pGroup->pFiles; pGroup->pFiles = pFile; pFile->pGroup = pGroup; } pFile->nRef++; pQuotaOpen->pFile = pFile; if( pSubOpen->pMethods->iVersion==1 ){ pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV1; }else{ pQuotaOpen->base.pMethods = &gQuota.sIoMethodsV2; } } } quotaLeave(); return rc; } /************************ I/O Method Wrappers *******************************/ /* xClose requests get passed through to the original VFS. But we ** also have to unlink the quotaConn from the quotaFile and quotaGroup. ** The quotaFile and/or quotaGroup are freed if they are no longer in use. */ static int quotaClose(sqlite3_file *pConn){ quotaConn *p = (quotaConn*)pConn; quotaFile *pFile = p->pFile; sqlite3_file *pSubOpen = quotaSubOpen(pConn); int rc; rc = pSubOpen->pMethods->xClose(pSubOpen); quotaEnter(); pFile->nRef--; if( pFile->nRef==0 ){ quotaGroup *pGroup = pFile->pGroup; pGroup->iSize -= pFile->iSize; if( pFile->pNext ) pFile->pNext->ppPrev = pFile->ppPrev; *pFile->ppPrev = pFile->pNext; quotaGroupDeref(pGroup); sqlite3_free(pFile); } quotaLeave(); return rc; } /* Pass xRead requests directory thru to the original VFS without ** further processing. */ static int quotaRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); } /* Check xWrite requests to see if they expand the file. If they do, ** the perform a quota check before passing them through to the ** original VFS. */ static int quotaWrite( sqlite3_file *pConn, const void *pBuf, int iAmt, sqlite3_int64 iOfst ){ quotaConn *p = (quotaConn*)pConn; sqlite3_file *pSubOpen = quotaSubOpen(pConn); sqlite3_int64 iEnd = iOfst+iAmt; quotaGroup *pGroup; quotaFile *pFile = p->pFile; sqlite3_int64 szNew; if( pFile->iSize<iEnd ){ pGroup = pFile->pGroup; quotaEnter(); szNew = pGroup->iSize - pFile->iSize + iEnd; if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ if( pGroup->xCallback ){ pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew, pGroup->pArg); } if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){ quotaLeave(); return SQLITE_FULL; } } pGroup->iSize = szNew; pFile->iSize = iEnd; quotaLeave(); } return pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); } /* Pass xTruncate requests thru to the original VFS. If the ** success, update the file size. */ static int quotaTruncate(sqlite3_file *pConn, sqlite3_int64 size){ quotaConn *p = (quotaConn*)pConn; sqlite3_file *pSubOpen = quotaSubOpen(pConn); int rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); quotaFile *pFile = p->pFile; quotaGroup *pGroup; if( rc==SQLITE_OK ){ quotaEnter(); pGroup = pFile->pGroup; pGroup->iSize -= pFile->iSize; pFile->iSize = size; pGroup->iSize += size; quotaLeave(); } return rc; } /* Pass xSync requests through to the original VFS without change */ static int quotaSync(sqlite3_file *pConn, int flags){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xSync(pSubOpen, flags); } /* Pass xFileSize requests through to the original VFS but then ** update the quotaGroup with the new size before returning. */ static int quotaFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ quotaConn *p = (quotaConn*)pConn; sqlite3_file *pSubOpen = quotaSubOpen(pConn); quotaFile *pFile = p->pFile; quotaGroup *pGroup; sqlite3_int64 sz; int rc; rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( rc==SQLITE_OK ){ quotaEnter(); pGroup = pFile->pGroup; pGroup->iSize -= pFile->iSize; pFile->iSize = sz; pGroup->iSize += sz; quotaLeave(); *pSize = sz; } return rc; } /* Pass xLock requests through to the original VFS unchanged. */ static int quotaLock(sqlite3_file *pConn, int lock){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xLock(pSubOpen, lock); } /* Pass xUnlock requests through to the original VFS unchanged. */ static int quotaUnlock(sqlite3_file *pConn, int lock){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xUnlock(pSubOpen, lock); } /* Pass xCheckReservedLock requests through to the original VFS unchanged. */ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } /* Pass xFileControl requests through to the original VFS unchanged. */ static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); } /* Pass xSectorSize requests through to the original VFS unchanged. */ static int quotaSectorSize(sqlite3_file *pConn){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xSectorSize(pSubOpen); } /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. */ static int quotaDeviceCharacteristics(sqlite3_file *pConn){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); } /* Pass xShmMap requests through to the original VFS unchanged. */ static int quotaShmMap( sqlite3_file *pConn, /* Handle open on database file */ int iRegion, /* Region to retrieve */ int szRegion, /* Size of regions */ int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); } /* Pass xShmLock requests through to the original VFS unchanged. */ static int quotaShmLock( sqlite3_file *pConn, /* Database file holding the shared memory */ int ofst, /* First lock to acquire or release */ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); } /* Pass xShmBarrier requests through to the original VFS unchanged. */ static void quotaShmBarrier(sqlite3_file *pConn){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); pSubOpen->pMethods->xShmBarrier(pSubOpen); } /* Pass xShmUnmap requests through to the original VFS unchanged. */ static int quotaShmUnmap(sqlite3_file *pConn, int deleteFlag){ sqlite3_file *pSubOpen = quotaSubOpen(pConn); return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } /************************** Public Interfaces *****************************/ /* ** Initialize the quota VFS shim. Use the VFS named zOrigVfsName ** as the VFS that does the actual work. Use the default if ** zOrigVfsName==NULL. ** ** The quota VFS shim is named "quota". It will become the default ** VFS if makeDefault is non-zero. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once ** during start-up. */ int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault){ sqlite3_vfs *pOrigVfs; if( gQuota.isInitialized ) return SQLITE_MISUSE; pOrigVfs = sqlite3_vfs_find(zOrigVfsName); if( pOrigVfs==0 ) return SQLITE_ERROR; assert( pOrigVfs!=&gQuota.sThisVfs ); gQuota.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( !gQuota.pMutex ){ return SQLITE_NOMEM; } gQuota.isInitialized = 1; gQuota.pOrigVfs = pOrigVfs; gQuota.sThisVfs = *pOrigVfs; gQuota.sThisVfs.xOpen = quotaOpen; gQuota.sThisVfs.szOsFile += sizeof(quotaConn); gQuota.sThisVfs.zName = "quota"; gQuota.sIoMethodsV1.iVersion = 1; gQuota.sIoMethodsV1.xClose = quotaClose; gQuota.sIoMethodsV1.xRead = quotaRead; gQuota.sIoMethodsV1.xWrite = quotaWrite; gQuota.sIoMethodsV1.xTruncate = quotaTruncate; gQuota.sIoMethodsV1.xSync = quotaSync; gQuota.sIoMethodsV1.xFileSize = quotaFileSize; gQuota.sIoMethodsV1.xLock = quotaLock; gQuota.sIoMethodsV1.xUnlock = quotaUnlock; gQuota.sIoMethodsV1.xCheckReservedLock = quotaCheckReservedLock; gQuota.sIoMethodsV1.xFileControl = quotaFileControl; gQuota.sIoMethodsV1.xSectorSize = quotaSectorSize; gQuota.sIoMethodsV1.xDeviceCharacteristics = quotaDeviceCharacteristics; gQuota.sIoMethodsV2 = gQuota.sIoMethodsV1; gQuota.sIoMethodsV2.iVersion = 2; gQuota.sIoMethodsV2.xShmMap = quotaShmMap; gQuota.sIoMethodsV2.xShmLock = quotaShmLock; gQuota.sIoMethodsV2.xShmBarrier = quotaShmBarrier; gQuota.sIoMethodsV2.xShmUnmap = quotaShmUnmap; sqlite3_vfs_register(&gQuota.sThisVfs, makeDefault); return SQLITE_OK; } /* ** Shutdown the quota system. ** ** All SQLite database connections must be closed before calling this ** routine. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly one while ** shutting down in order to free all remaining quota groups. */ int sqlite3_quota_shutdown(void){ quotaGroup *pGroup; if( gQuota.isInitialized==0 ) return SQLITE_MISUSE; for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ if( pGroup->pFiles ) return SQLITE_MISUSE; } while( gQuota.pGroup ){ pGroup = gQuota.pGroup; gQuota.pGroup = pGroup->pNext; pGroup->iLimit = 0; quotaGroupDeref(pGroup); } gQuota.isInitialized = 0; sqlite3_mutex_free(gQuota.pMutex); sqlite3_vfs_unregister(&gQuota.sThisVfs); memset(&gQuota, 0, sizeof(gQuota)); return SQLITE_OK; } /* ** Create or destroy a quota group. ** ** The quota group is defined by the zPattern. When calling this routine ** with a zPattern for a quota group that already exists, this routine ** merely updates the iLimit, xCallback, and pArg values for that quota ** group. If zPattern is new, then a new quota group is created. ** ** If the iLimit for a quota group is set to zero, then the quota group ** is disabled and will be deleted when the last database connection using ** the quota group is closed. ** ** Calling this routine on a zPattern that does not exist and with a ** zero iLimit is a no-op. ** ** A quota group must exist with a non-zero iLimit prior to opening ** database connections if those connections are to participate in the ** quota group. Creating a quota group does not affect database connections ** that are already open. */ int sqlite3_quota_set( const char *zPattern, /* The filename pattern */ sqlite3_int64 iLimit, /* New quota to set for this quota group */ void (*xCallback)( /* Callback invoked when going over quota */ const char *zFilename, /* Name of file whose size increases */ sqlite3_int64 *piLimit, /* IN/OUT: The current limit */ sqlite3_int64 iSize, /* Total size of all files in the group */ void *pArg /* Client data */ ), void *pArg, /* client data passed thru to callback */ void (*xDestroy)(void*) /* Optional destructor for pArg */ ){ quotaGroup *pGroup; quotaEnter(); pGroup = gQuota.pGroup; while( pGroup && strcmp(pGroup->zPattern, zPattern)!=0 ){ pGroup = pGroup->pNext; } if( pGroup==0 ){ int nPattern = strlen(zPattern); if( iLimit<=0 ){ quotaLeave(); return SQLITE_OK; } pGroup = sqlite3_malloc( sizeof(*pGroup) + nPattern + 1 ); if( pGroup==0 ){ quotaLeave(); return SQLITE_NOMEM; } memset(pGroup, 0, sizeof(*pGroup)); pGroup->zPattern = (char*)&pGroup[1]; memcpy((char *)pGroup->zPattern, zPattern, nPattern+1); if( gQuota.pGroup ) gQuota.pGroup->ppPrev = &pGroup->pNext; pGroup->pNext = gQuota.pGroup; pGroup->ppPrev = &gQuota.pGroup; gQuota.pGroup = pGroup; } pGroup->iLimit = iLimit; pGroup->xCallback = xCallback; if( pGroup->xDestroy && pGroup->pArg!=pArg ){ pGroup->xDestroy(pGroup->pArg); } pGroup->pArg = pArg; pGroup->xDestroy = xDestroy; quotaGroupDeref(pGroup); quotaLeave(); return SQLITE_OK; } /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include <tcl.h> /* ** Argument passed to a TCL quota-over-limit callback. */ typedef struct TclQuotaCallback TclQuotaCallback; struct TclQuotaCallback { Tcl_Interp *interp; /* Interpreter in which to run the script */ Tcl_Obj *pScript; /* Script to be run */ }; extern const char *sqlite3TestErrorName(int); /* ** This is the callback from a quota-over-limit. */ static void tclQuotaCallback( const char *zFilename, /* Name of file whose size increases */ sqlite3_int64 *piLimit, /* IN/OUT: The current limit */ sqlite3_int64 iSize, /* Total size of all files in the group */ void *pArg /* Client data */ ){ TclQuotaCallback *p; /* Callback script object */ Tcl_Obj *pEval; /* Script to evaluate */ Tcl_Obj *pVarname; /* Name of variable to pass as 2nd arg */ unsigned int rnd; /* Random part of pVarname */ int rc; /* Tcl error code */ p = (TclQuotaCallback *)pArg; if( p==0 ) return; pVarname = Tcl_NewStringObj("::piLimit_", -1); Tcl_IncrRefCount(pVarname); sqlite3_randomness(sizeof(rnd), (void *)&rnd); Tcl_AppendObjToObj(pVarname, Tcl_NewIntObj((int)(rnd&0x7FFFFFFF))); Tcl_ObjSetVar2(p->interp, pVarname, 0, Tcl_NewWideIntObj(*piLimit), 0); pEval = Tcl_DuplicateObj(p->pScript); Tcl_IncrRefCount(pEval); Tcl_ListObjAppendElement(0, pEval, Tcl_NewStringObj(zFilename, -1)); Tcl_ListObjAppendElement(0, pEval, pVarname); Tcl_ListObjAppendElement(0, pEval, Tcl_NewWideIntObj(iSize)); rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); if( rc==TCL_OK ){ Tcl_Obj *pLimit = Tcl_ObjGetVar2(p->interp, pVarname, 0, 0); rc = Tcl_GetWideIntFromObj(p->interp, pLimit, piLimit); Tcl_UnsetVar(p->interp, Tcl_GetString(pVarname), 0); } Tcl_DecrRefCount(pEval); Tcl_DecrRefCount(pVarname); if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp); } /* ** Destructor for a TCL quota-over-limit callback. */ static void tclCallbackDestructor(void *pObj){ TclQuotaCallback *p = (TclQuotaCallback*)pObj; if( p ){ Tcl_DecrRefCount(p->pScript); sqlite3_free((char *)p); } } /* ** tclcmd: sqlite3_quota_initialize NAME MAKEDEFAULT */ static int test_quota_initialize( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zName; /* Name of new quota VFS */ int makeDefault; /* True to make the new VFS the default */ int rc; /* Value returned by quota_initialize() */ /* Process arguments */ if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); return TCL_ERROR; } zName = Tcl_GetString(objv[1]); if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; if( zName[0]=='\0' ) zName = 0; /* Call sqlite3_quota_initialize() */ rc = sqlite3_quota_initialize(zName, makeDefault); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_quota_shutdown */ static int test_quota_shutdown( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; /* Value returned by quota_shutdown() */ if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } /* Call sqlite3_quota_shutdown() */ rc = sqlite3_quota_shutdown(); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_quota_set PATTERN LIMIT SCRIPT */ static int test_quota_set( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zPattern; /* File pattern to configure */ sqlite3_int64 iLimit; /* Initial quota in bytes */ Tcl_Obj *pScript; /* Tcl script to invoke to increase quota */ int rc; /* Value returned by quota_set() */ TclQuotaCallback *p; /* Callback object */ int nScript; /* Length of callback script */ void (*xDestroy)(void*); /* Optional destructor for pArg */ void (*xCallback)(const char *, sqlite3_int64 *, sqlite3_int64, void *); /* Process arguments */ if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "PATTERN LIMIT SCRIPT"); return TCL_ERROR; } zPattern = Tcl_GetString(objv[1]); if( Tcl_GetWideIntFromObj(interp, objv[2], &iLimit) ) return TCL_ERROR; pScript = objv[3]; Tcl_GetStringFromObj(pScript, &nScript); if( nScript>0 ){ /* Allocate a TclQuotaCallback object */ p = (TclQuotaCallback *)sqlite3_malloc(sizeof(TclQuotaCallback)); if( !p ){ Tcl_SetResult(interp, (char *)"SQLITE_NOMEM", TCL_STATIC); return TCL_OK; } memset(p, 0, sizeof(TclQuotaCallback)); p->interp = interp; Tcl_IncrRefCount(pScript); p->pScript = pScript; xDestroy = tclCallbackDestructor; xCallback = tclQuotaCallback; }else{ p = 0; xDestroy = 0; xCallback = 0; } /* Invoke sqlite3_quota_set() */ rc = sqlite3_quota_set(zPattern, iLimit, xCallback, (void*)p, xDestroy); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_quota_dump */ static int test_quota_dump( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_Obj *pResult; Tcl_Obj *pGroupTerm; Tcl_Obj *pFileTerm; quotaGroup *pGroup; quotaFile *pFile; pResult = Tcl_NewObj(); quotaEnter(); for(pGroup=gQuota.pGroup; pGroup; pGroup=pGroup->pNext){ pGroupTerm = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewStringObj(pGroup->zPattern, -1)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewWideIntObj(pGroup->iLimit)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewWideIntObj(pGroup->iSize)); for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){ pFileTerm = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, pFileTerm, Tcl_NewStringObj(pFile->zFilename, -1)); Tcl_ListObjAppendElement(interp, pFileTerm, Tcl_NewWideIntObj(pFile->iSize)); Tcl_ListObjAppendElement(interp, pFileTerm, Tcl_NewWideIntObj(pFile->nRef)); Tcl_ListObjAppendElement(interp, pGroupTerm, pFileTerm); } Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } quotaLeave(); Tcl_SetObjResult(interp, pResult); return TCL_OK; } /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside ** of this module. */ int Sqlitequota_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3_quota_initialize", test_quota_initialize }, { "sqlite3_quota_shutdown", test_quota_shutdown }, { "sqlite3_quota_set", test_quota_set }, { "sqlite3_quota_dump", test_quota_dump }, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } return TCL_OK; } #endif |
Added src/test_rtree.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 | /* ** 2010 August 28 ** ** 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. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** is not included in the SQLite library. */ #include <sqlite3.h> /* Solely for the UNUSED_PARAMETER() macro. */ #include "sqliteInt.h" /* ** Type used to cache parameter information for the "circle" r-tree geometry ** callback. */ typedef struct Circle Circle; struct Circle { struct Box { double xmin; double xmax; double ymin; double ymax; } aBox[2]; double centerx; double centery; double radius; }; /* ** Destructor function for Circle objects allocated by circle_geom(). */ static void circle_del(void *p){ sqlite3_free(p); } /* ** Implementation of "circle" r-tree geometry callback. */ static int circle_geom( sqlite3_rtree_geometry *p, int nCoord, double *aCoord, int *pRes ){ int i; /* Iterator variable */ Circle *pCircle; /* Structure defining circular region */ double xmin, xmax; /* X dimensions of box being tested */ double ymin, ymax; /* X dimensions of box being tested */ if( p->pUser==0 ){ /* If pUser is still 0, then the parameter values have not been tested ** for correctness or stored into a Circle structure yet. Do this now. */ /* This geometry callback is for use with a 2-dimensional r-tree table. ** Return an error if the table does not have exactly 2 dimensions. */ if( nCoord!=4 ) return SQLITE_ERROR; /* Test that the correct number of parameters (3) have been supplied, ** and that the parameters are in range (that the radius of the circle ** radius is greater than zero). */ if( p->nParam!=3 || p->aParam[2]<0.0 ) return SQLITE_ERROR; /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM ** if the allocation fails. */ pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle))); if( !pCircle ) return SQLITE_NOMEM; p->xDelUser = circle_del; /* Record the center and radius of the circular region. One way that ** tested bounding boxes that intersect the circular region are detected ** is by testing if each corner of the bounding box lies within radius ** units of the center of the circle. */ pCircle->centerx = p->aParam[0]; pCircle->centery = p->aParam[1]; pCircle->radius = p->aParam[2]; /* Define two bounding box regions. The first, aBox[0], extends to ** infinity in the X dimension. It covers the same range of the Y dimension ** as the circular region. The second, aBox[1], extends to infinity in ** the Y dimension and is constrained to the range of the circle in the ** X dimension. ** ** Then imagine each box is split in half along its short axis by a line ** that intersects the center of the circular region. A bounding box ** being tested can be said to intersect the circular region if it contains ** points from each half of either of the two infinite bounding boxes. */ pCircle->aBox[0].xmin = pCircle->centerx; pCircle->aBox[0].xmax = pCircle->centerx; pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius; pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius; pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius; pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius; pCircle->aBox[1].ymin = pCircle->centery; pCircle->aBox[1].ymax = pCircle->centery; } pCircle = (Circle *)p->pUser; xmin = aCoord[0]; xmax = aCoord[1]; ymin = aCoord[2]; ymax = aCoord[3]; /* Check if any of the 4 corners of the bounding-box being tested lie ** inside the circular region. If they do, then the bounding-box does ** intersect the region of interest. Set the output variable to true and ** return SQLITE_OK in this case. */ for(i=0; i<4; i++){ double x = (i&0x01) ? xmax : xmin; double y = (i&0x02) ? ymax : ymin; double d2; d2 = (x-pCircle->centerx)*(x-pCircle->centerx); d2 += (y-pCircle->centery)*(y-pCircle->centery); if( d2<(pCircle->radius*pCircle->radius) ){ *pRes = 1; return SQLITE_OK; } } /* Check if the bounding box covers any other part of the circular region. ** See comments above for a description of how this test works. If it does ** cover part of the circular region, set the output variable to true ** and return SQLITE_OK. */ for(i=0; i<2; i++){ if( xmin<=pCircle->aBox[i].xmin && xmax>=pCircle->aBox[i].xmax && ymin<=pCircle->aBox[i].ymin && ymax>=pCircle->aBox[i].ymax ){ *pRes = 1; return SQLITE_OK; } } /* The specified bounding box does not intersect the circular region. Set ** the output variable to zero and return SQLITE_OK. */ *pRes = 0; return SQLITE_OK; } /* END of implementation of "circle" geometry callback. ************************************************************************** *************************************************************************/ #include <assert.h> #include "tcl.h" typedef struct Cube Cube; struct Cube { double x; double y; double z; double width; double height; double depth; }; static void cube_context_free(void *p){ sqlite3_free(p); } /* ** The context pointer registered along with the 'cube' callback is ** always ((void *)&gHere). This is just to facilitate testing, it is not ** actually used for anything. */ static int gHere = 42; /* ** Implementation of a simple r-tree geom callback to test for intersection ** of r-tree rows with a "cube" shape. Cubes are defined by six scalar ** coordinates as follows: ** ** cube(x, y, z, width, height, depth) ** ** The width, height and depth parameters must all be greater than zero. */ static int cube_geom( sqlite3_rtree_geometry *p, int nCoord, double *aCoord, int *piRes ){ Cube *pCube = (Cube *)p->pUser; assert( p->pContext==(void *)&gHere ); if( pCube==0 ){ if( p->nParam!=6 || nCoord!=6 || p->aParam[3]<=0.0 || p->aParam[4]<=0.0 || p->aParam[5]<=0.0 ){ return SQLITE_ERROR; } pCube = (Cube *)sqlite3_malloc(sizeof(Cube)); if( !pCube ){ return SQLITE_NOMEM; } pCube->x = p->aParam[0]; pCube->y = p->aParam[1]; pCube->z = p->aParam[2]; pCube->width = p->aParam[3]; pCube->height = p->aParam[4]; pCube->depth = p->aParam[5]; p->pUser = (void *)pCube; p->xDelUser = cube_context_free; } assert( nCoord==6 ); *piRes = 0; if( aCoord[0]<=(pCube->x+pCube->width) && aCoord[1]>=pCube->x && aCoord[2]<=(pCube->y+pCube->height) && aCoord[3]>=pCube->y && aCoord[4]<=(pCube->z+pCube->depth) && aCoord[5]>=pCube->z ){ *piRes = 1; } return SQLITE_OK; } static int register_cube_geom( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifndef SQLITE_ENABLE_RTREE UNUSED_PARAMETER(clientData); UNUSED_PARAMETER(interp); UNUSED_PARAMETER(objc); UNUSED_PARAMETER(objv); #else extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); extern const char *sqlite3TestErrorName(int); sqlite3 *db; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_rtree_geometry_callback(db, "cube", cube_geom, (void *)&gHere); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); #endif return TCL_OK; } static int register_circle_geom( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifndef SQLITE_ENABLE_RTREE UNUSED_PARAMETER(clientData); UNUSED_PARAMETER(interp); UNUSED_PARAMETER(objc); UNUSED_PARAMETER(objv); #else extern int getDbPointer(Tcl_Interp*, const char*, sqlite3**); extern const char *sqlite3TestErrorName(int); sqlite3 *db; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); #endif return TCL_OK; } int Sqlitetestrtree_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "register_cube_geom", register_cube_geom, 0, 0); Tcl_CreateObjCommand(interp, "register_circle_geom",register_circle_geom,0,0); return TCL_OK; } |
Added src/test_superlock.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 | /* ** 2010 November 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. ** ************************************************************************* ** Example code for obtaining an exclusive lock on an SQLite database ** file. This method is complicated, but works for both WAL and rollback ** mode database files. The interface to the example code in this file ** consists of the following two functions: ** ** sqlite3demo_superlock() ** sqlite3demo_superunlock() */ #include <sqlite3.h> #include <string.h> /* memset(), strlen() */ #include <assert.h> /* assert() */ /* ** A structure to collect a busy-handler callback and argument and a count ** of the number of times it has been invoked. */ struct SuperlockBusy { int (*xBusy)(void*,int); /* Pointer to busy-handler function */ void *pBusyArg; /* First arg to pass to xBusy */ int nBusy; /* Number of times xBusy has been invoked */ }; typedef struct SuperlockBusy SuperlockBusy; /* ** The pCtx pointer passed to this function is actually a pointer to a ** SuperlockBusy structure. Invoke the busy-handler function encapsulated ** by the structure and return the result. */ static int superlockBusyHandler(void *pCtx, int UNUSED){ SuperlockBusy *pBusy = (SuperlockBusy *)pCtx; if( pBusy->xBusy==0 ) return 0; return pBusy->xBusy(pBusy->pBusyArg, pBusy->nBusy++); } /* ** This function is used to determine if the main database file for ** connection db is open in WAL mode or not. If no error occurs and the ** database file is in WAL mode, set *pbWal to true and return SQLITE_OK. ** If it is not in WAL mode, set *pbWal to false. ** ** If an error occurs, return an SQLite error code. The value of *pbWal ** is undefined in this case. */ static int superlockIsWal(sqlite3 *db, int *pbWal){ int rc; /* Return Code */ sqlite3_stmt *pStmt; /* Compiled PRAGMA journal_mode statement */ rc = sqlite3_prepare(db, "PRAGMA main.journal_mode", -1, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; *pbWal = 0; if( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zMode = (const char *)sqlite3_column_text(pStmt, 0); if( zMode && strlen(zMode)==3 && sqlite3_strnicmp("wal", zMode, 3)==0 ){ *pbWal = 1; } } return sqlite3_finalize(pStmt); } /* ** Obtain an exclusive shm-lock on nByte bytes starting at offset idx ** of the file fd. If the lock cannot be obtained immediately, invoke ** the busy-handler until either it is obtained or the busy-handler ** callback returns 0. */ static int superlockShmLock( sqlite3_file *fd, /* Database file handle */ int idx, /* Offset of shm-lock to obtain */ int nByte, /* Number of consective bytes to lock */ SuperlockBusy *pBusy /* Busy-handler wrapper object */ ){ int rc; int (*xShmLock)(sqlite3_file*, int, int, int) = fd->pMethods->xShmLock; do { rc = xShmLock(fd, idx, nByte, SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE); }while( rc==SQLITE_BUSY && superlockBusyHandler((void *)pBusy, 0) ); return rc; } /* ** Obtain the extra locks on the database file required for WAL databases. ** Invoke the supplied busy-handler as required. */ static int superlockWalLock( sqlite3 *db, /* Database handle open on WAL database */ SuperlockBusy *pBusy /* Busy handler wrapper object */ ){ int rc; /* Return code */ sqlite3_file *fd = 0; /* Main database file handle */ void volatile *p = 0; /* Pointer to first page of shared memory */ int nBusy = 0; /* Number of calls already made to xBusy */ /* Obtain a pointer to the sqlite3_file object open on the main db file. */ rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd); if( rc!=SQLITE_OK ) return rc; /* Obtain the "recovery" lock. Normally, this lock is only obtained by ** clients running database recovery. */ rc = superlockShmLock(fd, 2, 1, pBusy); if( rc!=SQLITE_OK ) return rc; /* Zero the start of the first shared-memory page. This means that any ** clients that open read or write transactions from this point on will ** have to run recovery before proceeding. Since they need the "recovery" ** lock that this process is holding to do that, no new read or write ** transactions may now be opened. Nor can a checkpoint be run, for the ** same reason. */ rc = fd->pMethods->xShmMap(fd, 0, 32*1024, 1, &p); if( rc!=SQLITE_OK ) return rc; memset((void *)p, 0, 32); /* Obtain exclusive locks on all the "read-lock" slots. Once these locks ** are held, it is guaranteed that there are no active reader, writer or ** checkpointer clients. */ rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy); return rc; } /* ** Obtain a superlock on the database file identified by zPath, using the ** locking primitives provided by VFS zVfs. If successful, SQLITE_OK is ** returned and output variable *ppLock is populated with an opaque handle ** that may be used with sqlite3demo_superunlock() to release the lock. ** ** If an error occurs, *ppLock is set to 0 and an SQLite error code ** (e.g. SQLITE_BUSY) is returned. ** ** If a required lock cannot be obtained immediately and the xBusy parameter ** to this function is not NULL, then xBusy is invoked in the same way ** as a busy-handler registered with SQLite (using sqlite3_busy_handler()) ** until either the lock can be obtained or the busy-handler function returns ** 0 (indicating "give up"). */ int sqlite3demo_superlock( const char *zPath, /* Path to database file to lock */ const char *zVfs, /* VFS to use to access database file */ int (*xBusy)(void*,int), /* Busy handler callback */ void *pBusyArg, /* Context arg for busy handler */ void **ppLock /* OUT: Context to pass to superunlock() */ ){ sqlite3 *db = 0; /* Database handle open on zPath */ SuperlockBusy busy = {0, 0, 0}; /* Busy handler wrapper object */ int rc; /* Return code */ /* Open a database handle on the file to superlock. */ rc = sqlite3_open_v2( zPath, &db, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs ); /* Install a busy-handler and execute a BEGIN EXCLUSIVE. If this is not ** a WAL database, this is all we need to do. ** ** A wrapper function is used to invoke the busy-handler instead of ** registering the busy-handler function supplied by the user directly ** with SQLite. This is because the same busy-handler function may be ** invoked directly later on when attempting to obtain the extra locks ** required in WAL mode. By using the wrapper, we are able to guarantee ** that the "nBusy" integer parameter passed to the users busy-handler ** represents the total number of busy-handler invocations made within ** this call to sqlite3demo_superlock(), including any made during the ** "BEGIN EXCLUSIVE". */ if( rc==SQLITE_OK ){ busy.xBusy = xBusy; busy.pBusyArg = pBusyArg; sqlite3_busy_handler(db, superlockBusyHandler, (void *)&busy); rc = sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0); } /* If the BEGIN EXCLUSIVE was executed successfully and this is a WAL ** database, call superlockWalLock() to obtain the extra locks required ** to prevent readers, writers and/or checkpointers from accessing the ** db while this process is holding the superlock. ** ** Before attempting any WAL locks, commit the transaction started above ** to drop the WAL read and write locks currently held. Otherwise, the ** new WAL locks may conflict with the old. */ if( rc==SQLITE_OK ){ int bWal; /* True for a WAL database, false otherwise */ if( SQLITE_OK==(rc = superlockIsWal(db, &bWal)) && bWal ){ rc = sqlite3_exec(db, "COMMIT", 0, 0, 0); if( rc==SQLITE_OK ){ rc = superlockWalLock(db, &busy); } } } if( rc!=SQLITE_OK ){ sqlite3_close(db); *ppLock = 0; }else{ *ppLock = (void *)db; } return rc; } /* ** Release a superlock held on a database file. The argument passed to ** this function must have been obtained from a successful call to ** sqlite3demo_superlock(). */ void sqlite3demo_superunlock(void *pLock){ sqlite3_close((sqlite3 *)pLock); } /* ** End of example code. Everything below here is the test harness. ************************************************************************** ************************************************************************** *************************************************************************/ #ifdef SQLITE_TEST #include <tcl.h> struct InterpAndScript { Tcl_Interp *interp; Tcl_Obj *pScript; }; typedef struct InterpAndScript InterpAndScript; static void superunlock_del(ClientData cd){ sqlite3demo_superunlock((void *)cd); } static int superunlock_cmd( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); return TCL_OK; } static int superlock_busy(void *pCtx, int nBusy){ InterpAndScript *p = (InterpAndScript *)pCtx; Tcl_Obj *pEval; /* Script to evaluate */ int iVal = 0; /* Value to return */ pEval = Tcl_DuplicateObj(p->pScript); Tcl_IncrRefCount(pEval); Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewIntObj(nBusy)); Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iVal); Tcl_DecrRefCount(pEval); return iVal; } /* ** Tclcmd: sqlite3demo_superlock CMDNAME PATH VFS BUSY-HANDLER-SCRIPT */ static int superlock_cmd( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ void *pLock; /* Lock context */ char *zPath; char *zVfs = 0; InterpAndScript busy = {0, 0}; int (*xBusy)(void*,int) = 0; /* Busy handler callback */ int rc; /* Return code from sqlite3demo_superlock() */ if( objc<3 || objc>5 ){ Tcl_WrongNumArgs( interp, 1, objv, "CMDNAME PATH ?VFS? ?BUSY-HANDLER-SCRIPT?"); return TCL_ERROR; } zPath = Tcl_GetString(objv[2]); if( objc>3 ){ zVfs = Tcl_GetString(objv[3]); if( strlen(zVfs)==0 ) zVfs = 0; } if( objc>4 ){ busy.interp = interp; busy.pScript = objv[4]; xBusy = superlock_busy; } rc = sqlite3demo_superlock(zPath, zVfs, xBusy, &busy, &pLock); assert( rc==SQLITE_OK || pLock==0 ); assert( rc!=SQLITE_OK || pLock!=0 ); if( rc!=SQLITE_OK ){ Tcl_ResetResult(interp); Tcl_AppendResult(interp, sqlite3ErrStr(rc), 0); return TCL_ERROR; } Tcl_CreateObjCommand( interp, Tcl_GetString(objv[1]), superunlock_cmd, pLock, superunlock_del ); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } int SqliteSuperlock_Init(Tcl_Interp *interp){ Tcl_CreateObjCommand(interp, "sqlite3demo_superlock", superlock_cmd, 0, 0); return TCL_OK; } #endif |
Changes to src/test_vfs.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | ** ** Available options are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ** ** Available options are: ** ** -noshm BOOLEAN (True to omit shm methods. Default false) ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) ** -iversion INTEGER (Value for sqlite3_vfs.iVersion) */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
634 635 636 637 638 639 640 641 642 643 644 645 646 647 | /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ | > | 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | /* Create the ephemeral table into which the update results will ** be stored. */ assert( v ); ephemTab = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0)); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); /* fill the ephemeral table */ sqlite3SelectDestInit(&dest, SRT_Table, ephemTab); sqlite3Select(pParse, pSelect, &dest); /* Generate code to scan the ephemeral table and call VUpdate. */ |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | /* Convenient short-hand */ #define UpperToLower sqlite3UpperToLower /* ** Some systems have stricmp(). Others have strcasecmp(). Because ** there is no consistency, we will define our own. */ int sqlite3StrICmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* | > > > > > > < | > | > > | | < < | | > > | < < | < < < < < | < < | < > | < < < < < < < < < < < < < | < < < < < < < < < < < | > | | | | | | > > > > > | > > | | > | | | > | > | | | | > | > > | | | | > > > > > > > | 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 | /* Convenient short-hand */ #define UpperToLower sqlite3UpperToLower /* ** Some systems have stricmp(). Others have strcasecmp(). Because ** there is no consistency, we will define our own. ** ** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows ** applications and extensions to compare the contents of two buffers ** containing UTF-8 strings in a case-independent fashion, using the same ** definition of case independence that SQLite uses internally when ** comparing identifiers. */ int sqlite3StrICmp(const char *zLeft, const char *zRight){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; } /* ** The string z[] is an text representation of a real number. ** Convert this string to a double and write it into *pResult. ** ** The string z[] is length bytes in length (bytes, not characters) and ** uses the encoding enc. The string is not necessarily zero-terminated. ** ** Return TRUE if the result is a valid real number (or integer) and FALSE ** if the string is empty or contains extraneous text. Valid numbers ** are in one of these formats: ** ** [+-]digits[E[+-]digits] ** [+-]digits.[digits][E[+-]digits] ** [+-].digits[E[+-]digits] ** ** Leading and trailing whitespace is ignored for the purpose of determining ** validity. ** ** If some prefix of the input string is a valid number, this routine ** returns FALSE but it still converts the prefix and writes the result ** into *pResult. */ int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ #ifndef SQLITE_OMIT_FLOATING_POINT int incr = (enc==SQLITE_UTF8?1:2); const char *zEnd = z + length; /* sign * significand * (10 ^ (esign * exponent)) */ int sign = 1; /* sign of significand */ i64 s = 0; /* significand */ int d = 0; /* adjust exponent for shifting decimal point */ int esign = 1; /* sign of exponent */ int e = 0; /* exponent */ int eValid = 1; /* True exponent is either not used or is well-formed */ double result; int nDigits = 0; *pResult = 0.0; /* Default return value, in case of an error */ if( enc==SQLITE_UTF16BE ) z++; /* skip leading spaces */ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; if( z>=zEnd ) return 0; /* get sign of significand */ if( *z=='-' ){ sign = -1; z+=incr; }else if( *z=='+' ){ z+=incr; } /* skip leading zeroes */ while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++; /* copy max significant digits to significand */ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ s = s*10 + (*z - '0'); z+=incr, nDigits++; } /* skip non-significant significand digits ** (increase exponent by d to shift decimal left) */ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++; if( z>=zEnd ) goto do_atof_calc; /* if decimal point is present */ if( *z=='.' ){ z+=incr; /* copy digits from after decimal to significand ** (decrease exponent by d to shift decimal right) */ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ s = s*10 + (*z - '0'); z+=incr, nDigits++, d--; } /* skip non-significant digits */ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++; } if( z>=zEnd ) goto do_atof_calc; /* if exponent is present */ if( *z=='e' || *z=='E' ){ z+=incr; eValid = 0; if( z>=zEnd ) goto do_atof_calc; /* get sign of exponent */ if( *z=='-' ){ esign = -1; z+=incr; }else if( *z=='+' ){ z+=incr; } /* copy digits to exponent */ while( z<zEnd && sqlite3Isdigit(*z) ){ e = e*10 + (*z - '0'); z+=incr; eValid = 1; } } /* skip trailing spaces */ if( nDigits && eValid ){ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; } do_atof_calc: /* adjust exponent by d, and update sign */ e = (e*esign) + d; if( e<0 ) { esign = -1; e *= -1; } else { esign = 1; |
︙ | ︙ | |||
401 402 403 404 405 406 407 | result = (double)s; } } /* store the result */ *pResult = result; | | | | > | | > | > | > > > | | | > > > > | | < < > > | | > | | > > > | > | < | < < > | | | | | | | | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | 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 | result = (double)s; } } /* store the result */ *pResult = result; /* return true if number and no extra non-whitespace chracters after */ return z>=zEnd && nDigits>0 && eValid; #else return !sqlite3Atoi64(z, pResult, length, enc); #endif /* SQLITE_OMIT_FLOATING_POINT */ } /* ** Compare the 19-character string zNum against the text representation ** value 2^63: 9223372036854775808. Return negative, zero, or positive ** if zNum is less than, equal to, or greater than the string. ** Note that zNum must contain exactly 19 characters. ** ** Unlike memcmp() this routine is guaranteed to return the difference ** in the values of the last digit if the only difference is in the ** last digit. So, for example, ** ** compare2pow63("9223372036854775800", 1) ** ** will return -8. */ static int compare2pow63(const char *zNum, int incr){ int c = 0; int i; /* 012345678901234567 */ const char *pow63 = "922337203685477580"; for(i=0; c==0 && i<18; i++){ c = (zNum[i*incr]-pow63[i])*10; } if( c==0 ){ c = zNum[18*incr] - '8'; testcase( c==(-1) ); testcase( c==0 ); testcase( c==(+1) ); } return c; } /* ** Convert zNum to a 64-bit signed integer and write ** the value of the integer into *pNum. ** If zNum is exactly 9223372036854665808, return 2. ** This is a special case as the context will determine ** if it is too big (used as a negative). ** If zNum is not an integer or is an integer that ** is too large to be expressed with 64 bits, ** then return 1. Otherwise return 0. ** ** length is the number of bytes in the string (bytes, not characters). ** The string is not necessarily zero-terminated. The encoding is ** given by enc. */ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ int incr = (enc==SQLITE_UTF8?1:2); i64 v = 0; int neg = 0; /* assume positive */ int i; int c = 0; const char *zStart; const char *zEnd = zNum + length; if( enc==SQLITE_UTF16BE ) zNum++; while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; if( zNum>=zEnd ) goto do_atoi_calc; if( *zNum=='-' ){ neg = 1; zNum+=incr; }else if( *zNum=='+' ){ zNum+=incr; } do_atoi_calc: zStart = zNum; while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ v = v*10 + c - '0'; } *pNum = neg ? -v : v; testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){ /* zNum is empty or contains non-numeric text or is longer ** than 19 digits (thus guaranteeing that it is too large) */ return 1; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ return 0; }else{ /* 19-digit numbers must be no larger than 9223372036854775807 if positive ** or 9223372036854775808 if negative. Note that 9223372036854665808 ** is 2^63. Return 1 if to large */ c=compare2pow63(zNum, incr); if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ return c<neg ? 0 : 1; } } /* ** If zNum represents an integer that will fit in 32-bits, then set ** *pValue to that integer and return true. Otherwise return false. ** |
︙ | ︙ |
Changes to src/vacuum.c.
︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 115 116 117 118 | int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; | > > > > | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | int isMemDb; /* True if vacuuming a :memory: database */ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; } if( db->activeVdbeCnt>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); return SQLITE_ERROR; } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. */ #include "sqliteInt.h" #include "vdbeInt.h" /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test ** procedures use this information to make sure that indices are ** working correctly. This variable has no function other than to ** help verify the correct operation of the library. */ | > > > > > > > > > > > | 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 | ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. */ #include "sqliteInt.h" #include "vdbeInt.h" /* ** Invoke this macro on memory cells just prior to changing the ** value of the cell. This macro verifies that shallow copies are ** not misused. */ #ifdef SQLITE_DEBUG # define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M) #else # define memAboutToChange(P,M) #endif /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_SeekXX, OP_Next, or OP_Prev opcodes. The test ** procedures use this information to make sure that indices are ** working correctly. This variable has no function other than to ** help verify the correct operation of the library. */ |
︙ | ︙ | |||
234 235 236 237 238 239 240 | ** Try to convert a value into a numeric representation if we can ** do so without loss of information. In other words, if the string ** looks like a number, convert it into a number. If it does not ** look like a number, leave it alone. */ static void applyNumericAffinity(Mem *pRec){ if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ | > | < | < | < < < < < < < | | | | | | < < < < < < | 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 | ** Try to convert a value into a numeric representation if we can ** do so without loss of information. In other words, if the string ** looks like a number, convert it into a number. If it does not ** look like a number, leave it alone. */ static void applyNumericAffinity(Mem *pRec){ if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){ double rValue; i64 iValue; u8 enc = pRec->enc; if( (pRec->flags&MEM_Str)==0 ) return; if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return; if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){ pRec->u.i = iValue; pRec->flags |= MEM_Int; }else{ pRec->r = rValue; pRec->flags |= MEM_Real; } } } /* ** Processing is determine by the affinity parameter: ** |
︙ | ︙ | |||
310 311 312 313 314 315 316 | } /* ** Try to convert the type of a function argument or a result column ** into a numeric representation. Use either INTEGER or REAL whichever ** is appropriate. But only do the conversion if it is possible without ** loss of information and return the revised type of the argument. | < < > | | > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | } /* ** Try to convert the type of a function argument or a result column ** into a numeric representation. Use either INTEGER or REAL whichever ** is appropriate. But only do the conversion if it is possible without ** loss of information and return the revised type of the argument. */ int sqlite3_value_numeric_type(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; if( pMem->type==SQLITE_TEXT ){ applyNumericAffinity(pMem); sqlite3VdbeMemStoreType(pMem); } return pMem->type; } /* ** Exported version of applyAffinity(). This one works on sqlite3_value*, ** not the internal Mem* type. */ |
︙ | ︙ | |||
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 | ** value or convert mem[p2] to a different type. */ assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); } #endif switch( pOp->opcode ){ /***************************************************************************** ** What follows is a massive switch statement where each case implements a | > > > > > > | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 | ** value or convert mem[p2] to a different type. */ assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Int; } /* Sanity checking on other operands */ #ifdef SQLITE_DEBUG if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); assert( memIsValid(&aMem[pOp->p1]) ); REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); } if( (pOp->opflags & OPFLG_IN2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); assert( memIsValid(&aMem[pOp->p2]) ); REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_IN3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); assert( memIsValid(&aMem[pOp->p3]) ); REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); } if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); memAboutToChange(p, &aMem[pOp->p2]); } if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); memAboutToChange(p, &aMem[pOp->p3]); } #endif switch( pOp->opcode ){ /***************************************************************************** ** What follows is a massive switch statement where each case implements a |
︙ | ︙ | |||
752 753 754 755 756 757 758 759 760 761 762 763 764 765 | ** ** Write the current address onto register P1 ** and then jump to address P2. */ case OP_Gosub: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; } | > | 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 | ** ** Write the current address onto register P1 ** and then jump to address P2. */ case OP_Gosub: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); memAboutToChange(p, pIn1); pIn1->flags = MEM_Int; pIn1->u.i = pc; REGISTER_TRACE(pOp->p1, pIn1); pc = pOp->p2 - 1; break; } |
︙ | ︙ | |||
957 958 959 960 961 962 963 | break; } /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this | | < < < < | 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | break; } /* Opcode: Blob P1 P2 * P4 ** ** P4 points to a blob of data P1 bytes long. Store this ** blob in register P2. */ case OP_Blob: { /* out2-prerelease */ assert( pOp->p1 <= SQLITE_MAX_LENGTH ); sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0); pOut->enc = encoding; UPDATE_MAX_BLOBSIZE(pOut); break; |
︙ | ︙ | |||
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 | assert( p1+n<=p2 || p2+n<=p1 ); pIn1 = &aMem[p1]; pOut = &aMem[p2]; while( n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); pIn1->zMalloc = zMalloc; REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; | > > | 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 | assert( p1+n<=p2 || p2+n<=p1 ); pIn1 = &aMem[p1]; pOut = &aMem[p2]; while( n-- ){ assert( pOut<=&aMem[p->nMem] ); assert( pIn1<=&aMem[p->nMem] ); assert( memIsValid(pIn1) ); memAboutToChange(p, pOut); zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); pIn1->zMalloc = zMalloc; REGISTER_TRACE(p2++, pOut); pIn1++; pOut++; |
︙ | ︙ | |||
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 | ** copy. */ case OP_SCopy: { /* in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * ** ** The registers P1 through P1+P2-1 contain a single row of | > > > | 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | ** copy. */ case OP_SCopy: { /* in1, out2 */ pIn1 = &aMem[pOp->p1]; pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); #ifdef SQLITE_DEBUG if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1; #endif REGISTER_TRACE(pOp->p2, pOut); break; } /* Opcode: ResultRow P1 P2 * * * ** ** The registers P1 through P1+P2-1 contain a single row of |
︙ | ︙ | |||
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 | /* Make sure the results of the current row are \000 terminated ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ pMem = p->pResultSet = &aMem[pOp->p1]; for(i=0; i<pOp->p2; i++){ sqlite3VdbeMemNulTerminate(&pMem[i]); sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW | > > > > | 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 | /* Make sure the results of the current row are \000 terminated ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ pMem = p->pResultSet = &aMem[pOp->p1]; for(i=0; i<pOp->p2; i++){ assert( memIsValid(&pMem[i]) ); Deephemeralize(&pMem[i]); assert( (pMem[i].flags & MEM_Ephem)==0 || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 ); sqlite3VdbeMemNulTerminate(&pMem[i]); sqlite3VdbeMemStoreType(&pMem[i]); REGISTER_TRACE(pOp->p1+i, &pMem[i]); } if( db->mallocFailed ) goto no_mem; /* Return SQLITE_ROW |
︙ | ︙ | |||
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 | sqlite3_context ctx; sqlite3_value **apVal; int n; n = pOp->p5; apVal = p->apArg; assert( apVal || n==0 ); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; i<n; i++, pArg++){ apVal[i] = pArg; sqlite3VdbeMemStoreType(pArg); REGISTER_TRACE(pOp->p2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ ctx.pFunc = pOp->p4.pFunc; ctx.pVdbeFunc = 0; }else{ ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; ctx.pFunc = ctx.pVdbeFunc->pFunc; } | > > > > > < < | | 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 | sqlite3_context ctx; sqlite3_value **apVal; int n; n = pOp->p5; apVal = p->apArg; assert( apVal || n==0 ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) ); assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n ); pArg = &aMem[pOp->p2]; for(i=0; i<n; i++, pArg++){ assert( memIsValid(pArg) ); apVal[i] = pArg; Deephemeralize(pArg); sqlite3VdbeMemStoreType(pArg); REGISTER_TRACE(pOp->p2+i, pArg); } assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC ); if( pOp->p4type==P4_FUNCDEF ){ ctx.pFunc = pOp->p4.pFunc; ctx.pVdbeFunc = 0; }else{ ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc; ctx.pFunc = ctx.pVdbeFunc->pFunc; } ctx.s.flags = MEM_Null; ctx.s.db = db; ctx.s.xDel = 0; ctx.s.zMalloc = 0; /* The output cell may already have a buffer allocated. Move ** the pointer to ctx.s so in case the user-function can use ** the already allocated buffer instead of allocating a new one. */ sqlite3VdbeMemMove(&ctx.s, pOut); MemSetTypeFlag(&ctx.s, MEM_Null); ctx.isError = 0; if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } (*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */ if( db->mallocFailed ){ /* Even though a malloc() has failed, the implementation of the ** user function may have called an sqlite3_result_XXX() function ** to return a value. The following call releases any resources ** associated with such a value. */ sqlite3VdbeMemRelease(&ctx.s); |
︙ | ︙ | |||
1435 1436 1437 1438 1439 1440 1441 | ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * ** ** Shift the integer value in register P2 to the left by the | | | 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 | ** Take the bit-wise OR of the values in register P1 and P2 and ** store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftLeft P1 P2 P3 * * ** ** Shift the integer value in register P2 to the left by the ** number of bits specified by the integer in register P1. ** Store the result in register P3. ** If either input is NULL, the result is NULL. */ /* Opcode: ShiftRight P1 P2 P3 * * ** ** Shift the integer value in register P2 to the right by the ** number of bits specified by the integer in register P1. |
︙ | ︙ | |||
1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 | ** Add the constant P2 to the value in register P1. ** The result is always an integer. ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; } /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ pc = pOp->p2 - 1; | > > | 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 | ** Add the constant P2 to the value in register P1. ** The result is always an integer. ** ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; } /* Opcode: MustBeInt P1 P2 * * * ** ** Force the value in register P1 to be an integer. If the value ** in P1 is not an integer and cannot be converted into an integer ** without data loss, then jump immediately to P2, or if P2==0 ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ pc = pOp->p2 - 1; |
︙ | ︙ | |||
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 | ** equivalent of printf(). Blob values are unchanged and ** are afterwards simply interpreted as text. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); rc = ExpandBlob(pIn1); assert( pIn1->flags & MEM_Str || db->mallocFailed ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero); | > | 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 | ** equivalent of printf(). Blob values are unchanged and ** are afterwards simply interpreted as text. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); rc = ExpandBlob(pIn1); assert( pIn1->flags & MEM_Str || db->mallocFailed ); pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero); |
︙ | ︙ | |||
1588 1589 1590 1591 1592 1593 1594 | ** equivalent of atoi() or atof() and store 0 if no such conversion ** is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ pIn1 = &aMem[pOp->p1]; | < | < | | 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 | ** equivalent of atoi() or atof() and store 0 if no such conversion ** is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ pIn1 = &aMem[pOp->p1]; sqlite3VdbeMemNumerify(pIn1); break; } #endif /* SQLITE_OMIT_CAST */ /* Opcode: ToInt P1 * * * * ** ** Force the value in register P1 to be an integer. If ** The value is currently a real number, drop its fractional part. ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToInt: { /* same as TK_TO_INT, in1 */ |
︙ | ︙ | |||
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 | ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } break; } #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then ** jump to address P2. ** ** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or ** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL | > | | 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 | ** If the value is text or blob, try to convert it to an integer using the ** equivalent of atoi() and store 0.0 if no such conversion is possible. ** ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ pIn1 = &aMem[pOp->p1]; memAboutToChange(p, pIn1); if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } break; } #endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */ /* Opcode: Lt P1 P2 P3 P4 P5 ** ** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then ** jump to address P2. ** ** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or ** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL ** bit is clear then fall through if either operand is NULL. ** ** The SQLITE_AFF_MASK portion of P5 must be an affinity character - ** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made ** to coerce both inputs according to this affinity before the ** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric ** affinity is used. Note that the affinity conversions are stored ** back into the input registers P1 and P3. So this opcode can cause |
︙ | ︙ | |||
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 | case OP_Le: res = res<=0; break; case OP_Gt: res = res>0; break; default: res = res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); }else if( res ){ pc = pOp->p2-1; } | > | 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 | case OP_Le: res = res<=0; break; case OP_Gt: res = res>0; break; default: res = res>=0; break; } if( pOp->p5 & SQLITE_STOREP2 ){ pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res; REGISTER_TRACE(pOp->p2, pOut); }else if( res ){ pc = pOp->p2-1; } |
︙ | ︙ | |||
1797 1798 1799 1800 1801 1802 1803 | assert( pOp->p4.ai ); aPermute = pOp->p4.ai; break; } /* Opcode: Compare P1 P2 P3 P4 * ** | | | | 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 | assert( pOp->p4.ai ); aPermute = pOp->p4.ai; break; } /* Opcode: Compare P1 P2 P3 P4 * ** ** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this ** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of ** the comparison for use by the next OP_Jump instruct. ** ** P4 is a KeyInfo structure that defines collating sequences and sort ** orders for the comparison. The permutation applies to registers ** only. The KeyInfo elements are used sequentially. ** ** The comparison is a sort comparison, so NULLs compare equal, |
︙ | ︙ | |||
1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 | }else{ assert( p1>0 && p1+n<=p->nMem+1 ); assert( p2>0 && p2+n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; i<n; i++){ idx = aPermute ? aPermute[i] : i; REGISTER_TRACE(p1+idx, &aMem[p1+idx]); REGISTER_TRACE(p2+idx, &aMem[p2+idx]); assert( i<pKeyInfo->nField ); pColl = pKeyInfo->aColl[i]; bRev = pKeyInfo->aSortOrder[i]; iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); if( iCompare ){ | > > | 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 | }else{ assert( p1>0 && p1+n<=p->nMem+1 ); assert( p2>0 && p2+n<=p->nMem+1 ); } #endif /* SQLITE_DEBUG */ for(i=0; i<n; i++){ idx = aPermute ? aPermute[i] : i; assert( memIsValid(&aMem[p1+idx]) ); assert( memIsValid(&aMem[p2+idx]) ); REGISTER_TRACE(p1+idx, &aMem[p1+idx]); REGISTER_TRACE(p2+idx, &aMem[p2+idx]); assert( i<pKeyInfo->nField ); pColl = pKeyInfo->aColl[i]; bRev = pKeyInfo->aSortOrder[i]; iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl); if( iCompare ){ |
︙ | ︙ | |||
2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 | p1 = pOp->p1; p2 = pOp->p2; pC = 0; memset(&sMem, 0, sizeof(sMem)); assert( p1<p->nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; MemSetTypeFlag(pDest, MEM_Null); zRec = 0; /* This block sets the variable payloadSize to be the total number of ** bytes in the record. ** ** zRec is set to be the complete text of the record if it is available. | > | 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 | p1 = pOp->p1; p2 = pOp->p2; pC = 0; memset(&sMem, 0, sizeof(sMem)); assert( p1<p->nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); MemSetTypeFlag(pDest, MEM_Null); zRec = 0; /* This block sets the variable payloadSize to be the total number of ** bytes in the record. ** ** zRec is set to be the complete text of the record if it is available. |
︙ | ︙ | |||
2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 | assert( sqlite3BtreeCursorIsValid(pCrsr) ); rc = sqlite3BtreeDataSize(pCrsr, &payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( pC->pseudoTableReg>0 ){ pReg = &aMem[pC->pseudoTableReg]; assert( pReg->flags & MEM_Blob ); payloadSize = pReg->n; zRec = pReg->z; pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( payloadSize==0 || zRec!=0 ); }else{ /* Consider the row to be NULL */ payloadSize = 0; | > | 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 | assert( sqlite3BtreeCursorIsValid(pCrsr) ); rc = sqlite3BtreeDataSize(pCrsr, &payloadSize); assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ } }else if( pC->pseudoTableReg>0 ){ pReg = &aMem[pC->pseudoTableReg]; assert( pReg->flags & MEM_Blob ); assert( memIsValid(pReg) ); payloadSize = pReg->n; zRec = pReg->z; pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( payloadSize==0 || zRec!=0 ); }else{ /* Consider the row to be NULL */ payloadSize = 0; |
︙ | ︙ | |||
2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 | zAffinity = pOp->p4.z; assert( zAffinity!=0 ); assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); pIn1++; } break; } /* Opcode: MakeRecord P1 P2 P3 P4 * ** | > | | < | < < | 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 | zAffinity = pOp->p4.z; assert( zAffinity!=0 ); assert( zAffinity[pOp->p2]==0 ); pIn1 = &aMem[pOp->p1]; while( (cAff = *(zAffinity++))!=0 ){ assert( pIn1 <= &p->aMem[p->nMem] ); assert( memIsValid(pIn1) ); ExpandBlob(pIn1); applyAffinity(pIn1, cAff, encoding); pIn1++; } break; } /* Opcode: MakeRecord P1 P2 P3 P4 * ** ** Convert P2 registers beginning with P1 into the [record format] ** use as a data record in a database table or as a key ** in an index. The OP_Column opcode can decode the record later. ** ** P4 may be a string that is P2 characters long. The nth character of the ** string indicates the column affinity that should be used for the nth ** field of the index key. ** ** The mapping from character to affinity is given by the SQLITE_AFF_ ** macros defined in sqliteInt.h. |
︙ | ︙ | |||
2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 | nField = pOp->p1; zAffinity = pOp->p4.z; assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; file_format = p->minWriteFileFormat; /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(pRec=pData0; pRec<=pLast; pRec++){ if( zAffinity ){ applyAffinity(pRec, zAffinity[pRec-pData0], encoding); } if( pRec->flags&MEM_Zero && pRec->n>0 ){ sqlite3VdbeMemExpandBlob(pRec); } serial_type = sqlite3VdbeSerialType(pRec, file_format); | > > > > > > | 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 | nField = pOp->p1; zAffinity = pOp->p4.z; assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 ); pData0 = &aMem[nField]; nField = pOp->p2; pLast = &pData0[nField-1]; file_format = p->minWriteFileFormat; /* Identify the output register */ assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 ); pOut = &aMem[pOp->p3]; memAboutToChange(p, pOut); /* Loop through the elements that will make up the record to figure ** out how much space is required for the new record. */ for(pRec=pData0; pRec<=pLast; pRec++){ assert( memIsValid(pRec) ); if( zAffinity ){ applyAffinity(pRec, zAffinity[pRec-pData0], encoding); } if( pRec->flags&MEM_Zero && pRec->n>0 ){ sqlite3VdbeMemExpandBlob(pRec); } serial_type = sqlite3VdbeSerialType(pRec, file_format); |
︙ | ︙ | |||
2439 2440 2441 2442 2443 2444 2445 | } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ | < < | 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 | } /* Make sure the output register has a buffer large enough to store ** the new record. The output register (pOp->p3) is not allowed to ** be one of the input registers (because the following call to ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ if( sqlite3VdbeMemGrow(pOut, (int)nByte, 0) ){ goto no_mem; } zNewRecord = (u8 *)pOut->z; /* Write the record */ i = putVarint32(zNewRecord, nHdr); |
︙ | ︙ | |||
2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 | if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=pSavepoint ){ pTmp = db->pSavepoint; | > | 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 | if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); db->flags = (db->flags | SQLITE_InternChanges); } } /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ while( db->pSavepoint!=pSavepoint ){ pTmp = db->pSavepoint; |
︙ | ︙ | |||
2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 | }else{ wrFlag = 0; } if( pOp->p5 ){ assert( p2>0 ); assert( p2<=p->nMem ); pIn2 = &aMem[p2]; sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; /* The p2 value always comes from a prior OP_CreateTable opcode and ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ if( NEVER(p2<2) ) { | > > | 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 | }else{ wrFlag = 0; } if( pOp->p5 ){ assert( p2>0 ); assert( p2<=p->nMem ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); p2 = (int)pIn2->u.i; /* The p2 value always comes from a prior OP_CreateTable opcode and ** that opcode will always set the p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ if( NEVER(p2<2) ) { |
︙ | ︙ | |||
3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 | }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } assert( pOp->p1>=0 ); pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; /* Since it performs no memory allocation or IO, the only values that ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. ** SQLITE_EMPTY is only returned when attempting to open the table ** rooted at page 1 of a zero-byte database. */ | > | 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 | }else if( pOp->p4type==P4_INT32 ){ nField = pOp->p4.i; } assert( pOp->p1>=0 ); pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; pCur->isOrdered = 1; rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor); pCur->pKeyInfo = pKeyInfo; /* Since it performs no memory allocation or IO, the only values that ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. ** SQLITE_EMPTY is only returned when attempting to open the table ** rooted at page 1 of a zero-byte database. */ |
︙ | ︙ | |||
3059 3060 3061 3062 3063 3064 3065 | ** different name to distinguish its use. Tables created using ** by this opcode will be used for automatically created transient ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; | | | | | | | > | 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 | ** different name to distinguish its use. Tables created using ** by this opcode will be used for automatically created transient ** indices in joins. */ case OP_OpenAutoindex: case OP_OpenEphemeral: { VdbeCursor *pCx; static const int vfsFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; rc = sqlite3BtreeOpen(0, db, &pCx->pBt, BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before ** opening it. If a transient table is required, just use the ** automatically created table with root-page 1 (an BLOB_INTKEY table). */ if( pOp->p4.pKeyInfo ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, (KeyInfo*)pOp->p4.z, pCx->pCursor); pCx->pKeyInfo = pOp->p4.pKeyInfo; pCx->pKeyInfo->enc = ENC(p->db); } pCx->isTable = 0; }else{ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, pCx->pCursor); pCx->isTable = 1; } } pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); pCx->isIndex = !pCx->isTable; break; } /* Opcode: OpenPseudo P1 P2 P3 * * ** ** Open a new cursor that points to a fake table that contains a single |
︙ | ︙ | |||
3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 | assert( pOp->p2!=0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); if( pC->pCursor!=0 ){ oc = pOp->opcode; pC->nullRow = 0; if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ | > | 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 | assert( pOp->p2!=0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pseudoTableReg==0 ); assert( OP_SeekLe == OP_SeekLt+1 ); assert( OP_SeekGe == OP_SeekLt+2 ); assert( OP_SeekGt == OP_SeekLt+3 ); assert( pC->isOrdered ); if( pC->pCursor!=0 ){ oc = pOp->opcode; pC->nullRow = 0; if( pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ |
︙ | ︙ | |||
3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 | r.flags = (u16)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekGe || r.flags==0 ); assert( oc!=OP_SeekLt || r.flags==0 ); r.aMem = &aMem[pOp->p3]; ExpandBlob(r.aMem); rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } pC->rowidIsValid = 0; } | > > > | 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 | r.flags = (u16)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt))); assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY ); assert( oc!=OP_SeekGe || r.flags==0 ); assert( oc!=OP_SeekLt || r.flags==0 ); r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } #endif ExpandBlob(r.aMem); rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } pC->rowidIsValid = 0; } |
︙ | ︙ | |||
3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 | if( ALWAYS(pC->pCursor!=0) ){ assert( pC->isTable==0 ); if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; r.aMem = pIn3; r.flags = UNPACKED_PREFIX_MATCH; pIdxKey = &r; }else{ assert( pIn3->flags & MEM_Blob ); | > > > | | 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 | if( ALWAYS(pC->pCursor!=0) ){ assert( pC->isTable==0 ); if( pOp->p4.i>0 ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; r.aMem = pIn3; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } #endif r.flags = UNPACKED_PREFIX_MATCH; pIdxKey = &r; }else{ assert( pIn3->flags & MEM_Blob ); assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, aTempRec, sizeof(aTempRec)); if( pIdxKey==0 ){ goto no_mem; } pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } |
︙ | ︙ | |||
3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 | if( pCrsr!=0 ){ /* Populate the index search key. */ r.pKeyInfo = pCx->pKeyInfo; r.nField = nField + 1; r.flags = UNPACKED_PREFIX_SEARCH; r.aMem = aMx; /* Extract the value of R from register P3. */ sqlite3VdbeMemIntegerify(pIn3); R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to | > > > | 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 | if( pCrsr!=0 ){ /* Populate the index search key. */ r.pKeyInfo = pCx->pKeyInfo; r.nField = nField + 1; r.flags = UNPACKED_PREFIX_SEARCH; r.aMem = aMx; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } #endif /* Extract the value of R from register P3. */ sqlite3VdbeMemIntegerify(pIn3); R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to |
︙ | ︙ | |||
3538 3539 3540 3541 3542 3543 3544 | break; } /* Opcode: NotExists P1 P2 P3 * * ** ** Use the content of register P3 as a integer key. If a record ** with that key does not exist in table of P1, then jump to P2. | | | 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 | break; } /* Opcode: NotExists P1 P2 P3 * * ** ** Use the content of register P3 as a integer key. If a record ** with that key does not exist in table of P1, then jump to P2. ** If the record does exist, then fall through. The cursor is left ** pointing to the record if it exists. ** ** The difference between this operation and NotFound is that this ** operation assumes the key is an integer and that P1 is a table whereas ** NotFound assumes key is a blob constructed from MakeRecord and ** P1 is an index. ** |
︙ | ︙ | |||
3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 | /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=pFrame->nMem ); pMem = &pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); pMem = &aMem[pOp->p3]; } REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } if( v<pMem->u.i+1 ){ v = pMem->u.i + 1; } pMem->u.i = v; } #endif sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0); } if( pC->useRandomRowid ){ | > > | | | < > > > < > > | | < > | | > > > > < | < > > | 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 | /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=pFrame->nMem ); pMem = &pFrame->aMem[pOp->p3]; }else{ /* Assert that P3 is a valid memory cell. */ assert( pOp->p3<=p->nMem ); pMem = &aMem[pOp->p3]; memAboutToChange(p, pMem); } assert( memIsValid(pMem) ); REGISTER_TRACE(pOp->p3, pMem); sqlite3VdbeMemIntegerify(pMem); assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){ rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } if( v<pMem->u.i+1 ){ v = pMem->u.i + 1; } pMem->u.i = v; } #endif sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0); } if( pC->useRandomRowid ){ /* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the ** largest possible integer (9223372036854775807) then the database ** engine starts picking positive candidate ROWIDs at random until ** it finds one that is not previously used. */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ /* on the first attempt, simply do one more than previous */ v = db->lastRowid; v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ v++; /* ensure non-zero */ cnt = 0; while( ((rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)v, 0, &res))==SQLITE_OK) && (res==0) && (++cnt<100)){ /* collision - try another random rowid */ sqlite3_randomness(sizeof(v), &v); if( cnt<5 ){ /* try "small" random rowids for the initial attempts */ v &= 0xffffff; }else{ v &= (MAX_ROWID>>1); /* ensure doesn't go negative */ } v++; /* ensure non-zero */ } if( rc==SQLITE_OK && res==0 ){ rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } assert( v>0 ); /* EV: R-40812-03570 */ } pC->rowidIsValid = 0; pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } pOut->u.i = v; break; |
︙ | ︙ | |||
3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 | int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pCursor!=0 ); assert( pC->pseudoTableReg==0 ); assert( pC->isTable ); REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); REGISTER_TRACE(pOp->p3, pKey); iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); iKey = pOp->p3; } | > > | 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 | int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ const char *zDb; /* database name - used by the update hook */ const char *zTbl; /* Table name - used by the opdate hook */ int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); assert( memIsValid(pData) ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->pCursor!=0 ); assert( pC->pseudoTableReg==0 ); assert( pC->isTable ); REGISTER_TRACE(pOp->p2, pData); if( pOp->opcode==OP_Insert ){ pKey = &aMem[pOp->p3]; assert( pKey->flags & MEM_Int ); assert( memIsValid(pKey) ); REGISTER_TRACE(pOp->p3, pKey); iKey = pKey->u.i; }else{ assert( pOp->opcode==OP_InsertInt ); iKey = pOp->p3; } |
︙ | ︙ | |||
3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 | case OP_RowData: { VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; pOut = &aMem[pOp->p2]; /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC->isTable || pOp->opcode==OP_RowKey ); assert( pC->isIndex || pOp->opcode==OP_RowData ); assert( pC!=0 ); | > | 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 | case OP_RowData: { VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; pOut = &aMem[pOp->p2]; memAboutToChange(p, pOut); /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC->isTable || pOp->opcode==OP_RowKey ); assert( pC->isIndex || pOp->opcode==OP_RowData ); assert( pC!=0 ); |
︙ | ︙ | |||
4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 | assert( pC!=0 ); pCrsr = pC->pCursor; if( ALWAYS(pCrsr!=0) ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.flags = 0; r.aMem = &aMem[pOp->p2]; rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); if( rc==SQLITE_OK && res==0 ){ rc = sqlite3BtreeDelete(pCrsr); } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; } | > > > | 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 | assert( pC!=0 ); pCrsr = pC->pCursor; if( ALWAYS(pCrsr!=0) ){ r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; r.flags = 0; r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } #endif rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res); if( rc==SQLITE_OK && res==0 ){ rc = sqlite3BtreeDelete(pCrsr); } assert( pC->deferredMoveto==0 ); pC->cacheStatus = CACHE_STALE; } |
︙ | ︙ | |||
4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 | VdbeCursor *pC; int res; UnpackedRecord r; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); if( ALWAYS(pC->pCursor!=0) ){ assert( pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ r.flags = UNPACKED_IGNORE_ROWID; } r.aMem = &aMem[pOp->p3]; rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res); if( pOp->opcode==OP_IdxLT ){ res = -res; }else{ assert( pOp->opcode==OP_IdxGE ); res++; } | > > > > | 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 | VdbeCursor *pC; int res; UnpackedRecord r; assert( pOp->p1>=0 && pOp->p1<p->nCursor ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( pC->isOrdered ); if( ALWAYS(pC->pCursor!=0) ){ assert( pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ r.flags = UNPACKED_IGNORE_ROWID; } r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); } #endif rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res); if( pOp->opcode==OP_IdxLT ){ res = -res; }else{ assert( pOp->opcode==OP_IdxGE ); res++; } |
︙ | ︙ | |||
4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 | assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ p->nChange += nChange; if( pOp->p3>0 ){ aMem[pOp->p3].u.i += nChange; } } break; } /* Opcode: CreateTable P1 P2 * * * | > > | 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 | assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable( db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0) ); if( pOp->p3 ){ p->nChange += nChange; if( pOp->p3>0 ){ assert( memIsValid(&aMem[pOp->p3]) ); memAboutToChange(p, &aMem[pOp->p3]); aMem[pOp->p3].u.i += nChange; } } break; } /* Opcode: CreateTable P1 P2 * * * |
︙ | ︙ | |||
4518 4519 4520 4521 4522 4523 4524 | pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ /* flags = BTREE_INTKEY; */ | | | | 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 | pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ /* flags = BTREE_INTKEY; */ flags = BTREE_INTKEY; }else{ flags = BTREE_BLOBKEY; } rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags); pOut->u.i = pgno; break; } /* Opcode: ParseSchema P1 P2 * P4 * |
︙ | ︙ | |||
4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 | Mem *pEnd; /* Last memory cell in new array */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ pProgram = pOp->p4.pProgram; pRt = &aMem[pOp->p3]; assert( pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). ** | > | 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 | Mem *pEnd; /* Last memory cell in new array */ VdbeFrame *pFrame; /* New vdbe frame to execute in */ SubProgram *pProgram; /* Sub-program to execute */ void *t; /* Token identifying trigger */ pProgram = pOp->p4.pProgram; pRt = &aMem[pOp->p3]; assert( memIsValid(pRt) ); assert( pProgram->nOp>0 ); /* If the p5 flag is clear, then recursive invocation of triggers is ** disabled for backwards compatibility (p5 is set if this sub-program ** is really a trigger, not a foreign key action, and the flag set ** and cleared by the "PRAGMA recursive_triggers" command is clear). ** |
︙ | ︙ | |||
5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 | VdbeFrame *pFrame; if( p->pFrame ){ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); pIn1 = &pFrame->aMem[pOp->p1]; }else{ pIn1 = &aMem[pOp->p1]; } sqlite3VdbeMemIntegerify(pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); if( pIn1->u.i<pIn2->u.i){ pIn1->u.i = pIn2->u.i; } break; | > | 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 | VdbeFrame *pFrame; if( p->pFrame ){ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); pIn1 = &pFrame->aMem[pOp->p1]; }else{ pIn1 = &aMem[pOp->p1]; } assert( memIsValid(pIn1) ); sqlite3VdbeMemIntegerify(pIn1); pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); if( pIn1->u.i<pIn2->u.i){ pIn1->u.i = pIn2->u.i; } break; |
︙ | ︙ | |||
5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 | n = pOp->p5; assert( n>=0 ); pRec = &aMem[pOp->p2]; apVal = p->apArg; assert( apVal || n==0 ); for(i=0; i<n; i++, pRec++){ apVal[i] = pRec; sqlite3VdbeMemStoreType(pRec); } ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); ctx.pMem = pMem = &aMem[pOp->p3]; pMem->n++; ctx.s.flags = MEM_Null; ctx.s.z = 0; ctx.s.zMalloc = 0; ctx.s.xDel = 0; ctx.s.db = db; ctx.isError = 0; ctx.pColl = 0; if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } | > > | | 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 | n = pOp->p5; assert( n>=0 ); pRec = &aMem[pOp->p2]; apVal = p->apArg; assert( apVal || n==0 ); for(i=0; i<n; i++, pRec++){ assert( memIsValid(pRec) ); apVal[i] = pRec; memAboutToChange(p, pRec); sqlite3VdbeMemStoreType(pRec); } ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); ctx.pMem = pMem = &aMem[pOp->p3]; pMem->n++; ctx.s.flags = MEM_Null; ctx.s.z = 0; ctx.s.zMalloc = 0; ctx.s.xDel = 0; ctx.s.db = db; ctx.isError = 0; ctx.pColl = 0; if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); ctx.pColl = pOp[-1].p4.pColl; } (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ if( ctx.isError ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); rc = ctx.isError; } sqlite3VdbeMemRelease(&ctx.s); break; } |
︙ | ︙ | |||
5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 | int res; int i; Mem **apArg; pQuery = &aMem[pOp->p3]; pArgc = &pQuery[1]; pCur = p->apCsr[pOp->p1]; REGISTER_TRACE(pOp->p3, pQuery); assert( pCur->pVtabCursor ); pVtabCursor = pCur->pVtabCursor; pVtab = pVtabCursor->pVtab; pModule = pVtab->pModule; /* Grab the index number and argc parameters */ | > | 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 | int res; int i; Mem **apArg; pQuery = &aMem[pOp->p3]; pArgc = &pQuery[1]; pCur = p->apCsr[pOp->p1]; assert( memIsValid(pQuery) ); REGISTER_TRACE(pOp->p3, pQuery); assert( pCur->pVtabCursor ); pVtabCursor = pCur->pVtabCursor; pVtab = pVtabCursor->pVtab; pModule = pVtab->pModule; /* Grab the index number and argc parameters */ |
︙ | ︙ | |||
5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 | Mem *pDest; sqlite3_context sContext; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; if( pCur->nullRow ){ sqlite3VdbeMemSetNull(pDest); break; } pVtab = pCur->pVtabCursor->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); | > | 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 | Mem *pDest; sqlite3_context sContext; VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); if( pCur->nullRow ){ sqlite3VdbeMemSetNull(pDest); break; } pVtab = pCur->pVtabCursor->pVtab; pModule = pVtab->pModule; assert( pModule->xColumn ); |
︙ | ︙ | |||
5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 | case OP_VRename: { sqlite3_vtab *pVtab; Mem *pName; pVtab = pOp->p4.pVtab->pVtab; pName = &aMem[pOp->p1]; assert( pVtab->pModule->xRename ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); rc = pVtab->pModule->xRename(pVtab, pName->z); importVtabErrMsg(p, pVtab); break; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 P4 * | > > | 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 | case OP_VRename: { sqlite3_vtab *pVtab; Mem *pName; pVtab = pOp->p4.pVtab->pVtab; pName = &aMem[pOp->p1]; assert( pVtab->pModule->xRename ); assert( memIsValid(pName) ); REGISTER_TRACE(pOp->p1, pName); assert( pName->flags & MEM_Str ); rc = pVtab->pModule->xRename(pVtab, pName->z); importVtabErrMsg(p, pVtab); p->expired = 0; break; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* Opcode: VUpdate P1 P2 P3 P4 * |
︙ | ︙ | |||
5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 | pModule = (sqlite3_module *)pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ apArg = p->apArg; pX = &aMem[pOp->p3]; for(i=0; i<nArg; i++){ sqlite3VdbeMemStoreType(pX); apArg[i] = pX; pX++; } rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); importVtabErrMsg(p, pVtab); if( rc==SQLITE_OK && pOp->p1 ){ | > > | 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 | pModule = (sqlite3_module *)pVtab->pModule; nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); if( ALWAYS(pModule->xUpdate) ){ apArg = p->apArg; pX = &aMem[pOp->p3]; for(i=0; i<nArg; i++){ assert( memIsValid(pX) ); memAboutToChange(p, pX); sqlite3VdbeMemStoreType(pX); apArg[i] = pX; pX++; } rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid); importVtabErrMsg(p, pVtab); if( rc==SQLITE_OK && pOp->p1 ){ |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 62 63 64 65 66 | Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ | > | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | Bool rowidIsValid; /* True if lastRowid is valid */ Bool atFirst; /* True if pointing to first entry */ Bool useRandomRowid; /* Generate new record numbers semi-randomly */ Bool nullRow; /* True if pointing to a row with no data */ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */ Bool isTable; /* True if a table requiring integer keys */ Bool isIndex; /* True if an index containing keys only - no data */ Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */ Btree *pBt; /* Separate file holding temporary table */ int pseudoTableReg; /* Register holding pseudotable content. */ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */ int nField; /* Number of fields in the header */ i64 seqCount; /* Sequence counter */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ |
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 156 157 158 159 160 | double r; /* Real value */ sqlite3 *db; /* The associated database connection */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ }; /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** | > > > > | 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | double r; /* Real value */ sqlite3 *db; /* The associated database connection */ char *z; /* String or BLOB value */ int n; /* Number of characters in string value, excluding '\0' */ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */ u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */ u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */ #ifdef SQLITE_DEBUG Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */ void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */ #endif void (*xDel)(void *); /* If not null, call this function to delete Mem.z */ char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */ }; /* One or more of the following flags are set to indicate the validOK ** representations of the value stored in the Mem struct. ** |
︙ | ︙ | |||
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ | > < < > > > > > > > > | 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 | #define MEM_Null 0x0001 /* Value is NULL */ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_RowSet 0x0020 /* Value is a RowSet object */ #define MEM_Frame 0x0040 /* Value is a VdbeFrame object */ #define MEM_Invalid 0x0080 /* Value is undefined */ #define MEM_TypeMask 0x00ff /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the ** string is \000 or \u0000 terminated */ #define MEM_Term 0x0200 /* String rep is nul terminated */ #define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */ #define MEM_Static 0x0800 /* Mem.z points to a static string */ #define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */ #define MEM_Agg 0x2000 /* Mem.z points to an agg function context */ #define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */ #ifdef SQLITE_OMIT_INCRBLOB #undef MEM_Zero #define MEM_Zero 0x0000 #endif /* ** Clear any existing type flags from a Mem and replace them with f */ #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. */ #ifdef SQLITE_DEBUG #define memIsValid(M) ((M)->flags & MEM_Invalid)==0 #endif /* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains ** additional information about auxiliary information bound to arguments ** of the function. This is used to implement the sqlite3_get_auxdata() ** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data ** that can be associated with a constant argument to a function. This ** allows functions such as "regexp" to compile their constant regular |
︙ | ︙ | |||
386 387 388 389 390 391 392 393 394 395 396 397 398 399 | int sqlite3VdbeMemFinalize(Mem*, FuncDef*); const char *sqlite3OpcodeName(int); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeCloseStatement(Vdbe *, int); void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif | > > > > | 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 | int sqlite3VdbeMemFinalize(Mem*, FuncDef*); const char *sqlite3OpcodeName(int); int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeCloseStatement(Vdbe *, int); void sqlite3VdbeFrameDelete(VdbeFrame*); int sqlite3VdbeFrameRestore(VdbeFrame *); void sqlite3VdbeMemStoreType(Mem *pMem); #ifdef SQLITE_DEBUG void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 72 73 74 75 76 77 78 | ** ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ int sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; #ifdef SQLITE_ENABLE_SQLRR SRRecFinalize(pStmt); #endif sqlite3 *db = v->db; | > > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | ** ** This routine sets the error code and string returned by ** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16(). */ int sqlite3_finalize(sqlite3_stmt *pStmt){ int rc; if( pStmt==0 ){ /* IMPLEMENTATION-OF: R-57228-12904 Invoking sqlite3_finalize() on a NULL ** pointer is a harmless no-op. */ rc = SQLITE_OK; }else{ Vdbe *v = (Vdbe*)pStmt; #ifdef SQLITE_ENABLE_SQLRR SRRecFinalize(pStmt); #endif sqlite3 *db = v->db; |
︙ | ︙ | |||
154 155 156 157 158 159 160 | */ const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); p->flags &= ~MEM_Str; p->flags |= MEM_Blob; | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | */ const void *sqlite3_value_blob(sqlite3_value *pVal){ Mem *p = (Mem*)pVal; if( p->flags & (MEM_Blob|MEM_Str) ){ sqlite3VdbeMemExpandBlob(p); p->flags &= ~MEM_Str; p->flags |= MEM_Blob; return p->n ? p->z : 0; }else{ return sqlite3_value_text(pVal); } } int sqlite3_value_bytes(sqlite3_value *pVal){ return sqlite3ValueBytes(pVal, SQLITE_UTF8); } |
︙ | ︙ | |||
508 509 510 511 512 513 514 515 516 517 518 519 520 521 | assert( p && p->pFunc ); return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->s.db; } /* | > > > > > > | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | assert( p && p->pFunc ); return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. ** ** IMPLEMENTATION-OF: R-46798-50301 The sqlite3_context_db_handle() interface ** returns a copy of the pointer to the database connection (the 1st ** parameter) of the sqlite3_create_function() and ** sqlite3_create_function16() routines that originally registered the ** application defined function. */ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ assert( p && p->pFunc ); return p->s.db; } /* |
︙ | ︙ | |||
717 718 719 720 721 722 723 | ** sqlite3_column_int() ** sqlite3_column_int64() ** sqlite3_column_text() ** sqlite3_column_text16() ** sqlite3_column_real() ** sqlite3_column_bytes() ** sqlite3_column_bytes16() | < | | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | ** sqlite3_column_int() ** sqlite3_column_int64() ** sqlite3_column_text() ** sqlite3_column_text16() ** sqlite3_column_real() ** sqlite3_column_bytes() ** sqlite3_column_bytes16() ** sqiite3_column_blob() */ static void columnMallocFailure(sqlite3_stmt *pStmt) { /* If malloc() failed during an encoding conversion within an ** sqlite3_column_XXX API, then set the return code of the statement to ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR ** and _finalize() will return NOMEM. |
︙ | ︙ | |||
986 987 988 989 990 991 992 993 994 995 996 997 998 999 | pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. */ if( p->isPrepareV2 && ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) ){ p->expired = 1; } return SQLITE_OK; | > > > > > > | 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 | pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. ** ** IMPLEMENTATION-OF: R-48440-37595 If the specific value bound to host ** parameter in the WHERE clause might influence the choice of query plan ** for a statement, then the statement will be automatically recompiled, ** as if there had been a schema change, on the first sqlite3_step() call ** following any change to the bindings of that parameter. */ if( p->isPrepareV2 && ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) ){ p->expired = 1; } return SQLITE_OK; |
︙ | ︙ | |||
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 | if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } sqlite3Error(p->db, rc, 0); rc = sqlite3ApiExit(p->db, rc); } sqlite3_mutex_leave(p->db->mutex); } return rc; } /* ** Bind a blob value to an SQL statement variable. | > > | 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 | if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } sqlite3Error(p->db, rc, 0); rc = sqlite3ApiExit(p->db, rc); } sqlite3_mutex_leave(p->db->mutex); }else if( xDel!=SQLITE_STATIC && xDel!=SQLITE_TRANSIENT ){ xDel((void*)zData); } return rc; } /* ** Bind a blob value to an SQL statement variable. |
︙ | ︙ | |||
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 | ** in the argument belongs. This is the same database handle that was ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } /* ** Return a pointer to the next prepared statement after pStmt associated ** with database connection pDb. If pStmt is NULL, return the first ** prepared statement for the database connection. Return NULL if there ** are no more. */ | > > > > > > > > | 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 | ** in the argument belongs. This is the same database handle that was ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } /* ** Return true if the prepared statement is guaranteed to not modify the ** database. */ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->readOnly : 1; } /* ** Return a pointer to the next prepared statement after pStmt associated ** with database connection pDb. If pStmt is NULL, return the first ** prepared statement for the database connection. Return NULL if there ** are no more. */ |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
1178 1179 1180 1181 1182 1183 1184 | pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ pMem->type = SQLITE_INTEGER; pMem++; | < | | | | < | 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 | pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ pMem->type = SQLITE_INTEGER; pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p3; /* P3 */ pMem->type = SQLITE_INTEGER; pMem++; if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); |
︙ | ︙ | |||
1228 1229 1230 1231 1232 1233 1234 | #endif { pMem->flags = MEM_Null; /* Comment */ pMem->type = SQLITE_NULL; } } | | | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 | #endif { pMem->flags = MEM_Null; /* Comment */ pMem->type = SQLITE_NULL; } } p->nResColumn = 8 - 4*(p->explain-1); p->rc = SQLITE_OK; rc = SQLITE_ROW; } return rc; } #endif /* SQLITE_OMIT_EXPLAIN */ |
︙ | ︙ | |||
1751 1752 1753 1754 1755 1756 1757 | ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); | | > | 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 | ** still have 'null' as the master journal pointer, so they will roll ** back independently if a failure occurs. */ for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); if( zFile==0 ){ continue; /* Ignore TEMP and :memory: databases */ } assert( zFile[0]!=0 ); if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ needSync = 1; } rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset); offset += sqlite3Strlen30(zFile)+1; if( rc!=SQLITE_OK ){ sqlite3OsCloseFree(pMaster); |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ }; /* ** Open a blob handle. */ int sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ int iCol; /* Table column this handle is open on */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ }; /* ** This function is used by both blob_open() and blob_reopen(). It seeks ** the b-tree cursor associated with blob handle p to point to row iRow. ** If successful, SQLITE_OK is returned and subsequent calls to ** sqlite3_blob_read() or sqlite3_blob_write() access the specified row. ** ** If an error occurs, or if the specified row does not exist or does not ** contain a value of type TEXT or BLOB in the column nominated when the ** blob handle was opened, then an error code is returned and *pzErr may ** be set to point to a buffer containing an error message. It is the ** responsibility of the caller to free the error message buffer using ** sqlite3DbFree(). ** ** If an error does occur, then the b-tree cursor is closed. All subsequent ** calls to sqlite3_blob_read(), blob_write() or blob_reopen() will ** immediately return SQLITE_ABORT. */ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){ int rc; /* Error code */ char *zErr = 0; /* Error message */ Vdbe *v = (Vdbe *)p->pStmt; /* Set the value of the SQL statements only variable to integer iRow. ** This is done directly instead of using sqlite3_bind_int64() to avoid ** triggering asserts related to mutexes. */ assert( v->aVar[0].flags&MEM_Int ); v->aVar[0].u.i = iRow; rc = sqlite3_step(p->pStmt); if( rc==SQLITE_ROW ){ u32 type = v->apCsr[0]->aType[p->iCol]; if( type<12 ){ zErr = sqlite3MPrintf(p->db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" ); rc = SQLITE_ERROR; sqlite3_finalize(p->pStmt); p->pStmt = 0; }else{ p->iOffset = v->apCsr[0]->aOffset[p->iCol]; p->nByte = sqlite3VdbeSerialTypeLen(type); p->pCsr = v->apCsr[0]->pCursor; sqlite3BtreeEnterCursor(p->pCsr); sqlite3BtreeCacheOverflow(p->pCsr); sqlite3BtreeLeaveCursor(p->pCsr); } } if( rc==SQLITE_ROW ){ rc = SQLITE_OK; }else if( p->pStmt ){ rc = sqlite3_finalize(p->pStmt); p->pStmt = 0; if( rc==SQLITE_OK ){ zErr = sqlite3MPrintf(p->db, "no such rowid: %lld", iRow); rc = SQLITE_ERROR; }else{ zErr = sqlite3MPrintf(p->db, "%s", sqlite3_errmsg(p->db)); } } assert( rc!=SQLITE_OK || zErr==0 ); assert( rc!=SQLITE_ROW && rc!=SQLITE_DONE ); *pzErr = zErr; return rc; } /* ** Open a blob handle. */ int sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ |
︙ | ︙ | |||
67 68 69 70 71 72 73 | {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ /* One of the following two instructions is replaced by an OP_Noop. */ {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ | | > | | < | > > > > > > < < | | > > | 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 | {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ /* One of the following two instructions is replaced by an OP_Noop. */ {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */ {OP_Column, 0, 0, 1}, /* 7 */ {OP_ResultRow, 1, 0, 0}, /* 8 */ {OP_Goto, 0, 5, 0}, /* 9 */ {OP_Close, 0, 0, 0}, /* 10 */ {OP_Halt, 0, 0, 0}, /* 11 */ }; int rc = SQLITE_OK; char *zErr = 0; Table *pTab; Parse *pParse = 0; Incrblob *pBlob = 0; flags = !!flags; /* flags = (flags ? 1 : 0); */ *ppBlob = 0; sqlite3_mutex_enter(db->mutex); pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); if( !pBlob ) goto blob_open_out; pParse = sqlite3StackAllocRaw(db, sizeof(*pParse)); if( !pParse ) goto blob_open_out; do { memset(pParse, 0, sizeof(Parse)); pParse->db = db; sqlite3DbFree(db, zErr); zErr = 0; sqlite3BtreeEnterAll(db); pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); if( pTab && IsVirtual(pTab) ){ pTab = 0; sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); } |
︙ | ︙ | |||
115 116 117 118 119 120 121 | } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* Now search pTab for the exact column. */ | | | 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* Now search pTab for the exact column. */ for(iCol=0; iCol<pTab->nCol; iCol++) { if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ break; } } if( iCol==pTab->nCol ){ sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); |
︙ | ︙ | |||
169 170 171 172 173 174 175 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } | | > | > > | | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db); assert( pBlob->pStmt || db->mallocFailed ); if( pBlob->pStmt ){ Vdbe *v = (Vdbe *)pBlob->pStmt; int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); |
︙ | ︙ | |||
216 217 218 219 220 221 222 223 224 225 226 | sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } | > > > < | < < < < | < < < | < < < < < < < < < < < < < < | < < | < < < < < < < < < < < < < | > | < < < < < < < | | 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 | sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } pBlob->flags = flags; pBlob->iCol = iCol; pBlob->db = db; sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } sqlite3_bind_int64(pBlob->pStmt, 1, iRow); rc = blobSeekToRow(pBlob, iRow, &zErr); } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA ); blob_open_out: if( rc==SQLITE_OK && db->mallocFailed==0 ){ *ppBlob = (sqlite3_blob *)pBlob; }else{ if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt); sqlite3DbFree(db, pBlob); } sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } |
︙ | ︙ | |||
327 328 329 330 331 332 333 | sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, 0); | | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, 0); }else if( v==0 ){ /* If there is no statement handle, then the blob-handle has ** already been invalidated. Return SQLITE_ABORT in this case. */ rc = SQLITE_ABORT; }else{ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is ** returned, clean-up the statement handle. |
︙ | ︙ | |||
377 378 379 380 381 382 383 384 385 | ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob ** so no mutex is required for access. */ int sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return p ? p->nByte : 0; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** The Incrblob.nByte field is fixed for the lifetime of the Incrblob ** so no mutex is required for access. */ int sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return p ? p->nByte : 0; } /* ** Move an existing blob handle to point to a different row of the same ** database table. ** ** If an error occurs, or if the specified row does not exist or does not ** contain a blob or text value, then an error code is returned and the ** database handle error code and message set. If this happens, then all ** subsequent calls to sqlite3_blob_xxx() functions (except blob_close()) ** immediately return SQLITE_ABORT. */ int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){ int rc; Incrblob *p = (Incrblob *)pBlob; sqlite3 *db; if( p==0 ) return SQLITE_MISUSE_BKPT; db = p->db; sqlite3_mutex_enter(db->mutex); if( p->pStmt==0 ){ /* If there is no statement handle, then the blob-handle has ** already been invalidated. Return SQLITE_ABORT in this case. */ rc = SQLITE_ABORT; }else{ char *zErr; rc = blobSeekToRow(p, iRow, &zErr); if( rc!=SQLITE_OK ){ sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); } assert( rc!=SQLITE_SCHEMA ); } rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
128 129 130 131 132 133 134 135 136 137 138 139 140 141 | if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; } return SQLITE_OK; } /* ** If the given Mem* has a zero-filled tail, turn it into an ordinary | > > > | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){ return SQLITE_NOMEM; } pMem->z[pMem->n] = 0; pMem->z[pMem->n+1] = 0; pMem->flags |= MEM_Term; #ifdef SQLITE_DEBUG pMem->pScopyFrom = 0; #endif } return SQLITE_OK; } /* ** If the given Mem* has a zero-filled tail, turn it into an ordinary |
︙ | ︙ | |||
248 249 250 251 252 253 254 | assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); ctx.s.flags = MEM_Null; ctx.s.db = pMem->db; ctx.pMem = pMem; ctx.pFunc = pFunc; | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); memset(&ctx, 0, sizeof(ctx)); ctx.s.flags = MEM_Null; ctx.s.db = pMem->db; ctx.pMem = pMem; ctx.pFunc = pFunc; pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */ assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel ); sqlite3DbFree(pMem->db, pMem->zMalloc); memcpy(pMem, &ctx.s, sizeof(ctx.s)); rc = ctx.isError; } return rc; } |
︙ | ︙ | |||
361 362 363 364 365 366 367 | flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ i64 value; | < < < < < | > | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | flags = pMem->flags; if( flags & MEM_Int ){ return pMem->u.i; }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ i64 value; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); return value; }else{ return 0; } } /* |
︙ | ︙ | |||
390 391 392 393 394 395 396 | if( pMem->flags & MEM_Real ){ return pMem->r; }else if( pMem->flags & MEM_Int ){ return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ double val = (double)0; | < < < < < < < | | 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 | if( pMem->flags & MEM_Real ){ return pMem->r; }else if( pMem->flags & MEM_Int ){ return (double)pMem->u.i; }else if( pMem->flags & (MEM_Str|MEM_Blob) ){ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ double val = (double)0; sqlite3AtoF(pMem->z, &val, pMem->n, pMem->enc); return val; }else{ /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */ return (double)0; } } |
︙ | ︙ | |||
470 471 472 473 474 475 476 | ** Invalidate any prior representations. ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite3VdbeMemNumerify(Mem *pMem){ | < | | | < < < < | | | | | | | > > > | 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 | ** Invalidate any prior representations. ** ** Every effort is made to force the conversion, even if the input ** is a string that does not look completely like a number. Convert ** as much of the string as we can and ignore the rest. */ int sqlite3VdbeMemNumerify(Mem *pMem){ if( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))==0 ){ assert( (pMem->flags & (MEM_Blob|MEM_Str))!=0 ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){ MemSetTypeFlag(pMem, MEM_Int); }else{ pMem->r = sqlite3VdbeRealValue(pMem); MemSetTypeFlag(pMem, MEM_Real); sqlite3VdbeIntegerAffinity(pMem); } } assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 ); pMem->flags &= ~(MEM_Str|MEM_Blob); return SQLITE_OK; } /* ** Delete any previous value and set the value stored in *pMem to NULL. */ void sqlite3VdbeMemSetNull(Mem *pMem){ |
︙ | ︙ | |||
589 590 591 592 593 594 595 596 597 598 599 600 601 602 | n += p->u.nZero; } return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } return 0; } /* ** Size of struct Mem not including the Mem.zMalloc member. */ #define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) /* ** Make an shallow copy of pFrom into pTo. Prior contents of | > > > > > > > > > > > > > > > > > > > > > > | 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 612 613 614 | n += p->u.nZero; } return n>p->db->aLimit[SQLITE_LIMIT_LENGTH]; } return 0; } #ifdef SQLITE_DEBUG /* ** This routine prepares a memory cell for modication by breaking ** its link to a shallow copy and by marking any current shallow ** copies of this cell as invalid. ** ** This is used for testing and debugging only - to make sure shallow ** copies are not misused. */ void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){ int i; Mem *pX; for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){ if( pX->pScopyFrom==pMem ){ pX->flags |= MEM_Invalid; pX->pScopyFrom = 0; } } pMem->pScopyFrom = 0; } #endif /* SQLITE_DEBUG */ /* ** Size of struct Mem not including the Mem.zMalloc member. */ #define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc)) /* ** Make an shallow copy of pFrom into pTo. Prior contents of |
︙ | ︙ | |||
957 958 959 960 961 962 963 | sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ return 0; } } | | | 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 | sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED); if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){ assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 ); if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){ return 0; } } sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */ }else{ assert( (pVal->flags&MEM_Blob)==0 ); sqlite3VdbeMemStringify(pVal, enc); assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) ); } assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0 || pVal->db->mallocFailed ); |
︙ | ︙ | |||
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 | u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2. ** The ifdef here is to enable us to achieve 100% branch test coverage even ** when SQLITE_ENABLE_STAT2 is omitted. */ #ifdef SQLITE_ENABLE_STAT2 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ | > > > > > > > > > > > > > | | > > > > | 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 | u8 enc, /* Encoding to use */ u8 affinity, /* Affinity to use */ sqlite3_value **ppVal /* Write the new value here */ ){ int op; char *zVal = 0; sqlite3_value *pVal = 0; int negInt = 1; const char *zNeg = ""; if( !pExpr ){ *ppVal = 0; return SQLITE_OK; } op = pExpr->op; /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2. ** The ifdef here is to enable us to achieve 100% branch test coverage even ** when SQLITE_ENABLE_STAT2 is omitted. */ #ifdef SQLITE_ENABLE_STAT2 if( op==TK_REGISTER ) op = pExpr->op2; #else if( NEVER(op==TK_REGISTER) ) op = pExpr->op2; #endif /* Handle negative integers in a single step. This is needed in the ** case when the value is -9223372036854775808. */ if( op==TK_UMINUS && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){ pExpr = pExpr->pLeft; op = pExpr->op; negInt = -1; zNeg = "-"; } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; if( ExprHasProperty(pExpr, EP_IntValue) ){ sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt); }else{ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); }else{ sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8); } if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str; if( enc!=SQLITE_UTF8 ){ sqlite3VdbeChangeEncoding(pVal, enc); } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ sqlite3VdbeMemNumerify(pVal); pVal->u.i = -1 * pVal->u.i; /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ pVal->r = (double)-1 * pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } } #ifndef SQLITE_OMIT_BLOB_LITERAL else if( op==TK_BLOB ){ int nVal; assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); assert( pExpr->u.zToken[1]=='\'' ); |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
367 368 369 370 371 372 373 | pParse->regRowid ); sqlite3DbFree(db, zStmt); v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); | | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | pParse->regRowid ); sqlite3DbFree(db, zStmt); v = sqlite3GetVdbe(pParse); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Expire, 0, 0); zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC); sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0, pTab->zName, sqlite3Strlen30(pTab->zName) + 1); } /* If we are rereading the sqlite_master table create the in-memory ** record of the table. The xConnect() method is not called until |
︙ | ︙ | |||
668 669 670 671 672 673 674 | pTab->aCol = pParse->pNewTable->aCol; pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ | | | 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 | pTab->aCol = pParse->pNewTable->aCol; pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; } pParse->declareVtab = 0; if( pParse->pVdbe ){ sqlite3VdbeFinalize(pParse->pVdbe); |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
424 425 426 427 428 429 430 431 432 433 434 435 436 437 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* | > > > > > > > | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Candidate values for Wal.exclusiveMode. */ #define WAL_NORMAL_MODE 0 #define WAL_EXCLUSIVE_MODE 1 #define WAL_HEAPMEMORY_MODE 2 /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* |
︙ | ︙ | |||
510 511 512 513 514 515 516 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ | > > > > | | | > | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM; }else{ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); } } *ppPage = pWal->apWiData[iPage]; assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } |
︙ | ︙ | |||
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 | aData += 2; }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); | > > > > > > | | 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 | aData += 2; }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } static void walShmBarrier(Wal *pWal){ if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){ sqlite3OsShmBarrier(pWal->pDbFd); } } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); walShmBarrier(pWal); memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr)); } /* ** This function encodes a single frame header and writes it to a buffer ** supplied by the caller. A frame-header is made up of a series of ** 4-byte big-endian integers, as follows: |
︙ | ︙ | |||
1181 1182 1183 1184 1185 1186 1187 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ | > > > > > > > | > | 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){ int i; for(i=0; i<pWal->nWiData; i++){ sqlite3_free((void *)pWal->apWiData[i]); pWal->apWiData[i] = 0; } }else{ sqlite3OsShmUnmap(pWal->pDbFd, isDelete); } } /* ** Open a connection to the WAL file zWalName. The database file must ** already be opened on connection pDbFd. The buffer that zWalName points ** to must remain valid for the lifetime of the returned Wal* handle. ** |
︙ | ︙ | |||
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); | > | 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ int bNoShm, /* True to run in heap-memory mode */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); |
︙ | ︙ | |||
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } | > | 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } |
︙ | ︙ | |||
1669 1670 1671 1672 1673 1674 1675 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ | > | > | 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); if( rc==SQLITE_OK ){ isDelete = 1; } } walIndexClose(pWal, isDelete); |
︙ | ︙ | |||
1725 1726 1727 1728 1729 1730 1731 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); | | | 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); walShmBarrier(pWal); memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ return 1; /* Dirty read */ } if( h1.isInit==0 ){ return 1; /* Malformed header - probably all zeros */ |
︙ | ︙ | |||
1926 1927 1928 1929 1930 1931 1932 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); | | | 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); walShmBarrier(pWal); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ /* It is not safe to allow the reader to continue here if frames ** may have been appended to the log before READ_LOCK(0) was obtained. ** When holding READ_LOCK(0), the reader ignores the entire log file, ** which implies that the database file contains a trustworthy ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from |
︙ | ︙ | |||
2020 2021 2022 2023 2024 2025 2026 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ | | | 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ walShmBarrier(pWal); if( pInfo->aReadMark[mxI]!=mxReadMark || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); |
︙ | ︙ | |||
2362 2363 2364 2365 2366 2367 2368 | ** to the current log file, it is possible to overwrite the start of the ** existing log file with the new frames (i.e. "reset" the log). If so, ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left ** unchanged. ** ** SQLITE_OK is returned if no error is encountered (regardless of whether ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned | | | 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 | ** to the current log file, it is possible to overwrite the start of the ** existing log file with the new frames (i.e. "reset" the log). If so, ** it sets pWal->hdr.mxFrame to 0. Otherwise, pWal->hdr.mxFrame is left ** unchanged. ** ** SQLITE_OK is returned if no error is encountered (regardless of whether ** or not pWal->hdr.mxFrame is modified). An SQLite error code is returned ** if an error occurs. */ static int walRestartLog(Wal *pWal){ int rc = SQLITE_OK; int cnt; if( pWal->readLock==0 ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); |
︙ | ︙ | |||
2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); sqlite3_randomness(4, &aSalt[1]); walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; assert( pInfo->aReadMark[0]==0 ); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; | > > | 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 | sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); sqlite3_randomness(4, &aSalt[1]); walIndexWriteHdr(pWal); pInfo->nBackfill = 0; for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED; assert( pInfo->aReadMark[0]==0 ); walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); }else if( rc!=SQLITE_BUSY ){ return rc; } } walUnlockShared(pWal, WAL_READ_LOCK(0)); pWal->readLock = -1; cnt = 0; do{ int notUsed; |
︙ | ︙ | |||
2474 2475 2476 2477 2478 2479 2480 | rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } | | | 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 | rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0); WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok")); if( rc!=SQLITE_OK ){ return rc; } } assert( (int)pWal->szPage==szPage ); /* Write the log file. */ for(p=pList; p; p=p->pDirty){ u32 nDbsize; /* Db-size field for frame header */ i64 iOffset; /* Write offset in log file */ void *pData; |
︙ | ︙ | |||
2661 2662 2663 2664 2665 2666 2667 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do | | > | 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do ** not actually change anything. The pager uses this to see if it ** should acquire the database exclusive lock prior to invoking ** the op==1 case. */ int sqlite3WalExclusiveMode(Wal *pWal, int op){ int rc; assert( pWal->writeLock==0 ); assert( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE || op==-1 ); /* pWal->readLock is usually set, but might be -1 if there was a ** prior error while attempting to acquire are read-lock. This cannot ** happen if the connection is actually in exclusive mode (as no xShmLock ** locks are taken in this case). Nor should the pager attempt to ** upgrade to exclusive-mode following such an error. */ |
︙ | ︙ | |||
2700 2701 2702 2703 2704 2705 2706 2707 2708 | pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } #endif /* #ifndef SQLITE_OMIT_WAL */ | > > > > > > > > > | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 | pWal->exclusiveMode = 1; rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } /* ** Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } #endif /* #ifndef SQLITE_OMIT_WAL */ |
Changes to src/wal.h.
︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ | > | | 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 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 # define sqlite3WalHeapMemory(z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, int, Wal**); int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and ** preserves the current state even if the other threads or processes ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the |
︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 | */ int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ int sqlite3WalExclusiveMode(Wal *pWal, int op); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ | > > > > > > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | */ int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ int sqlite3WalExclusiveMode(Wal *pWal, int op); /* Return true if the argument is non-NULL and the WAL module is using ** heap-memory for the wal-index. Otherwise, if the argument is NULL or the ** WAL module is using shared-memory, return false. */ int sqlite3WalHeapMemory(Wal *pWal); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ |
Changes to src/where.c.
︙ | ︙ | |||
188 189 190 191 192 193 194 | /* ** A WhereCost object records a lookup strategy and the estimated ** cost of pursuing that strategy. */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ | < | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | /* ** A WhereCost object records a lookup strategy and the estimated ** cost of pursuing that strategy. */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ Bitmask used; /* Bitmask of cursors used by this plan */ }; /* ** Bitmasks for the operators that indices are able to exploit. An ** OR-ed combination of these values can be used when searching for ** terms in the where clause. |
︙ | ︙ | |||
231 232 233 234 235 236 237 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ | | > | 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */ #define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */ #define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */ #define WHERE_REVERSE 0x02000000 /* Scan in reverse order */ #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ |
︙ | ︙ | |||
665 666 667 668 669 670 671 | pRight = pList->a[0].pExpr; op = pRight->op; if( op==TK_REGISTER ){ op = pRight->op2; } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; | > | | | | 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 | pRight = pList->a[0].pExpr; op = pRight->op; if( op==TK_REGISTER ){ op = pRight->op2; } if( op==TK_VARIABLE ){ Vdbe *pReprepare = pParse->pReprepare; int iCol = pRight->iColumn; pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE); if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ z = (char *)sqlite3_value_text(pVal); } sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */ assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); }else if( op==TK_STRING ){ z = pRight->u.zToken; } if( z ){ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; } if( cnt!=0 && 255!=(u8)z[cnt-1] ){ Expr *pPrefix; *pisComplete = c==wc[0] && z[cnt+1]==0; pPrefix = sqlite3Expr(db, TK_STRING, z); if( pPrefix ) pPrefix->u.zToken[cnt] = 0; *ppPrefix = pPrefix; if( op==TK_VARIABLE ){ Vdbe *v = pParse->pVdbe; sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */ if( *pisComplete && pRight->u.zToken[1] ){ /* If the rhs of the LIKE expression is a variable, and the current ** value of the variable means there is no need to invoke the LIKE ** function, then no OP_Variable will be added to the program. ** This causes problems for the sqlite3_bind_parameter_name() ** API. To workaround them, add a dummy OP_Variable here. */ |
︙ | ︙ | |||
1551 1552 1553 1554 1555 1556 1557 | #define TRACE_IDX_OUTPUTS(A) #endif /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( | | > | > | > | | 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 | #define TRACE_IDX_OUTPUTS(A) #endif /* ** Required because bestIndex() is called by bestOrClauseIndex() */ static void bestIndex( Parse*, WhereClause*, struct SrcList_item*, Bitmask, Bitmask, ExprList*, WhereCost*); /* ** This routine attempts to find an scanning strategy that can be used ** to optimize an 'OR' expression that is part of a WHERE clause. ** ** The table associated with FROM clause term pSrc may be either a ** regular B-Tree table or a virtual table. */ static void bestOrClauseIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses ** are used */ if( pSrc->notIndexed || pSrc->pIndex!=0 ){ return; } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( pTerm->eOperator==WO_OR && ((pTerm->prereqAll & ~maskSrc) & notReady)==0 |
︙ | ︙ | |||
1600 1601 1602 1603 1604 1605 1606 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ WhereCost sTermCost; WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", (pOrTerm - pOrWC->a), (pTerm - pWC->a) )); if( pOrTerm->eOperator==WO_AND ){ WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; | | | | < > | 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 | for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ WhereCost sTermCost; WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", (pOrTerm - pOrWC->a), (pTerm - pWC->a) )); if( pOrTerm->eOperator==WO_AND ){ WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc; bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost); }else if( pOrTerm->leftCursor==iCur ){ WhereClause tempWC; tempWC.pParse = pWC->pParse; tempWC.pMaskSet = pWC->pMaskSet; tempWC.op = TK_AND; tempWC.a = pOrTerm; tempWC.nTerm = 1; bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } rTotal += sTermCost.rCost; nRow += sTermCost.plan.nRow; used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account ** for the cost of the sort. */ if( pOrderBy!=0 ){ WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", rTotal, rTotal+nRow*estLog(nRow))); rTotal += nRow*estLog(nRow); } /* If the cost of scanning using this OR term for optimization is ** less than the current cost stored in pCost, replace the contents ** of pCost. */ WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); if( rTotal<pCost->rCost ){ pCost->rCost = rTotal; pCost->used = used; pCost->plan.nRow = nRow; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } } } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ } |
︙ | ︙ | |||
1701 1702 1703 1704 1705 1706 1707 | if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; | | | | 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 | if( pSrc->notIndexed ){ /* The NOT INDEXED clause appears in the SQL. */ return; } assert( pParse->nQueryLoop >= (double)1 ); pTable = pSrc->pTab; nTableRow = pTable->nRowEst; logN = estLog(nTableRow); costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1); if( costTempIdx>=pCost->rCost ){ /* The cost of creating the transient table would be greater than ** doing the full table scan */ return; } /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->plan.nRow = logN + 1; pCost->plan.wsFlags = WHERE_TEMP_INDEX; pCost->used = pTerm->prereqRight; break; } } } #else |
︙ | ︙ | |||
2055 2056 2057 2058 2059 2060 2061 | ** routine takes care of freeing the sqlite3_index_info structure after ** everybody has finished with it. */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 | ** routine takes care of freeing the sqlite3_index_info structure after ** everybody has finished with it. */ static void bestVirtualIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for index */ Bitmask notValid, /* Cursors not valid for any purpose */ ExprList *pOrderBy, /* The order by clause */ WhereCost *pCost, /* Lowest cost query plan */ sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */ ){ Table *pTab = pSrc->pTab; sqlite3_index_info *pIdxInfo; struct sqlite3_index_constraint *pIdxCons; |
︙ | ︙ | |||
2185 2186 2187 2188 2189 2190 2191 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ | | | 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 | } pCost->plan.nEq = 0; pIdxInfo->nOrderBy = nOrderBy; /* Try to find a more efficient access pattern by using multiple indexes ** to optimize an OR expression within the WHERE clause. */ bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Argument pIdx is a pointer to an index structure that has an array of ** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column ** stored in Index.aSample. The domain of values stored in said column |
︙ | ︙ | |||
2311 2312 2313 2314 2315 2316 2317 | sqlite3_value **pp ){ /* The evalConstExpr() function will have already converted any TK_VARIABLE ** expression involved in an comparison into a TK_REGISTER. */ assert( pExpr->op!=TK_VARIABLE ); if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ int iVar = pExpr->iColumn; | | | 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 | sqlite3_value **pp ){ /* The evalConstExpr() function will have already converted any TK_VARIABLE ** expression involved in an comparison into a TK_REGISTER. */ assert( pExpr->op!=TK_VARIABLE ); if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ int iVar = pExpr->iColumn; sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */ *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); return SQLITE_OK; } return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); } #endif |
︙ | ︙ | |||
2466 2467 2468 2469 2470 2471 2472 | ** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ | | > | 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 | ** selected plan may still take advantage of the tables built-in rowid ** index. */ static void bestBtreeIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ Index *pIdx; /* Copy of pProbe, or zero for IPK index */ int eqTermMask; /* Current mask of valid equality operators */ |
︙ | ︙ | |||
2508 2509 2510 2511 2512 2513 2514 | /* There is no INDEXED BY clause. Create a fake Index object to ** represent the primary key */ Index *pFirst; /* Any other index on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; | < > > < < < < < < < < < < | 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 | /* There is no INDEXED BY clause. Create a fake Index object to ** represent the primary key */ Index *pFirst; /* Any other index on the table */ memset(&sPk, 0, sizeof(Index)); sPk.nColumn = 1; sPk.aiColumn = &aiColumnPk; sPk.aiRowEst = aiRowEstPk; sPk.onError = OE_Replace; sPk.pTable = pSrc->pTab; aiRowEstPk[0] = pSrc->pTab->nRowEst; aiRowEstPk[1] = 1; pFirst = pSrc->pTab->pIndex; if( pSrc->notIndexed==0 ){ sPk.pNext = pFirst; } pProbe = &sPk; wsFlagMask = ~( WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE ); eqTermMask = WO_EQ|WO_IN; pIdx = 0; } |
︙ | ︙ | |||
2737 2738 2739 2740 2741 2742 2743 | /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** | | < < | < > | > > | | | | | 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 | /* If there are additional constraints on this table that cannot ** be used with the current index, but which might lower the number ** of output rows, adjust the nRow value accordingly. This only ** matters if the current index is the least costly, so do not bother ** with this step if we already know this index will not be chosen. ** Also, never reduce the output row count below 2 using this step. ** ** It is critical that the notValid mask be used here instead of ** the notReady mask. When computing an "optimal" index, the notReady ** mask will only have one bit set - the bit for the current table. ** The notValid mask, on the other hand, always has all bits set for ** tables that are not in outer loops. If notReady is used here instead ** of notValid, then a optimal index that depends on inner joins loops ** might be selected even when there exists an optimal index that has ** no such dependency. */ if( nRow>2 && cost<=pCost->rCost ){ int k; /* Loop counter */ int nSkipEq = nEq; /* Number of == constraints to skip */ int nSkipRange = nBound; /* Number of < constraints to skip */ Bitmask thisTab; /* Bitmap for pSrc */ thisTab = getMask(pWC->pMaskSet, iCur); for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){ if( pTerm->wtFlags & TERM_VIRTUAL ) continue; if( (pTerm->prereqAll & notValid)!=thisTab ) continue; if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){ if( nSkipEq ){ /* Ignore the first nEq equality matches since the index ** has already accounted for these */ nSkipEq--; }else{ /* Assume each additional equality match reduces the result |
︙ | ︙ | |||
2797 2798 2799 2800 2801 2802 2803 | notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) | | < > | 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 | notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) && (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->plan.nRow)) ){ pCost->rCost = cost; pCost->used = used; pCost->plan.nRow = nRow; pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; pCost->plan.u.pIdx = pIdx; } /* If there was an INDEXED BY clause, then only that one index is ** considered. */ |
︙ | ︙ | |||
2837 2838 2839 2840 2841 2842 2843 | ); WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); | | | > | | | 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 | ); WHERETRACE(("best index is: %s\n", ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" : pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") )); bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost); pCost->plan.wsFlags |= eqTermMask; } /* ** Find the query plan for accessing table pSrc->pTab. Write the ** best query plan and its cost into the WhereCost object supplied ** as the last parameter. This function may calculate the cost of ** both real and virtual table scans. */ static void bestIndex( Parse *pParse, /* The parsing context */ WhereClause *pWC, /* The WHERE clause */ struct SrcList_item *pSrc, /* The FROM clause term to search */ Bitmask notReady, /* Mask of cursors not available for indexing */ Bitmask notValid, /* Cursors not available for any purpose */ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pSrc->pTab) ){ sqlite3_index_info *p = 0; bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p); if( p->needToFreeIdxStr ){ sqlite3_free(p->idxStr); } sqlite3DbFree(pParse->db, p); }else #endif { bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost); } } /* ** Disable a term in the WHERE clause. Except, do not disable the term ** if it controls a LEFT OUTER JOIN and it did not originate in the ON ** or USING clause of that join. |
︙ | ︙ | |||
3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 | } } } } *pzAff = zAff; return regBase; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 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 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 | } } } } *pzAff = zAff; return regBase; } #ifndef SQLITE_OMIT_EXPLAIN /* ** This routine is a helper for explainIndexRange() below ** ** pStr holds the text of an expression that we are building up one term ** at a time. This routine adds a new term to the end of the expression. ** Terms are separated by AND so add the "AND" text for second and subsequent ** terms only. */ static void explainAppendTerm( StrAccum *pStr, /* The text expression being built */ int iTerm, /* Index of this term. First is zero */ const char *zColumn, /* Name of the column */ const char *zOp /* Name of the operator */ ){ if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5); sqlite3StrAccumAppend(pStr, zColumn, -1); sqlite3StrAccumAppend(pStr, zOp, 1); sqlite3StrAccumAppend(pStr, "?", 1); } /* ** Argument pLevel describes a strategy for scanning table pTab. This ** function returns a pointer to a string buffer containing a description ** of the subset of table rows scanned by the strategy in the form of an ** SQL expression. Or, if all rows are scanned, NULL is returned. ** ** For example, if the query: ** ** SELECT * FROM t1 WHERE a=1 AND b>2; ** ** is run and there is an index on (a, b), then this function returns a ** string similar to: ** ** "a=? AND b>?" ** ** The returned pointer points to memory obtained from sqlite3DbMalloc(). ** It is the responsibility of the caller to free the buffer when it is ** no longer required. */ static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){ WherePlan *pPlan = &pLevel->plan; Index *pIndex = pPlan->u.pIdx; int nEq = pPlan->nEq; int i, j; Column *aCol = pTab->aCol; int *aiColumn = pIndex->aiColumn; StrAccum txt; if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){ return 0; } sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH); txt.db = db; sqlite3StrAccumAppend(&txt, " (", 2); for(i=0; i<nEq; i++){ explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "="); } j = i; if( pPlan->wsFlags&WHERE_BTM_LIMIT ){ explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">"); } if( pPlan->wsFlags&WHERE_TOP_LIMIT ){ explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<"); } sqlite3StrAccumAppend(&txt, ")", 1); return sqlite3StrAccumFinish(&txt); } /* ** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN ** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single ** record is added to the output to describe the table scan strategy in ** pLevel. */ static void explainOneScan( Parse *pParse, /* Parse context */ SrcList *pTabList, /* Table list this loop refers to */ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ int iLevel, /* Value for "level" column of output */ int iFrom, /* Value for "from" column of output */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ if( pParse->explain==2 ){ u32 flags = pLevel->plan.wsFlags; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ char *zMsg; /* Text to add to EQP output */ sqlite3_int64 nRow; /* Expected number of rows visited by scan */ int iId = pParse->iSelectId; /* Select id (left-most output column) */ int isSearch; /* True for a SEARCH. False for SCAN. */ if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return; isSearch = (pLevel->plan.nEq>0) || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX)); zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN"); if( pItem->pSelect ){ zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId); }else{ zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName); } if( pItem->zAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } if( (flags & WHERE_INDEXED)!=0 ){ char *zWhere = explainIndexRange(db, pLevel, pItem->pTab); zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg, ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""), ((flags & WHERE_IDX_ONLY)?"COVERING ":""), ((flags & WHERE_TEMP_INDEX)?"":" "), ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName), zWhere ); sqlite3DbFree(db, zWhere); }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg); if( flags&WHERE_ROWID_EQ ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg); }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg); }else if( flags&WHERE_BTM_LIMIT ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg); }else if( flags&WHERE_TOP_LIMIT ){ zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg); } } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (flags & WHERE_VIRTUALTABLE)!=0 ){ sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, pVtabIdx->idxNum, pVtabIdx->idxStr); } #endif if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){ testcase( wctrlFlags & WHERE_ORDERBY_MIN ); nRow = 1; }else{ nRow = (sqlite3_int64)pLevel->plan.nRow; } zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow); sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC); } } #else # define explainOneScan(u,v,w,x,y,z) #endif /* SQLITE_OMIT_EXPLAIN */ /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ |
︙ | ︙ | |||
3536 3537 3538 3539 3540 3541 3542 | /* If there are inequality constraints, check that the value ** of the table column that the inequality contrains is not NULL. ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); | | | 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 | /* If there are inequality constraints, check that the value ** of the table column that the inequality contrains is not NULL. ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); } sqlite3ReleaseTempReg(pParse, r1); /* Seek the table cursor, if required */ disableTerm(pLevel, pRangeStart); |
︙ | ︙ | |||
3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, regRowid); sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, sqlite3VdbeCurrentAddr(v)+2, r, iSet); | > > > | 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ explainOneScan( pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 ); if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, regRowid); sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, sqlite3VdbeCurrentAddr(v)+2, r, iSet); |
︙ | ︙ | |||
4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; /* Loop through the remaining entries in the FROM clause to find the ** next nested loop. The loop tests all FROM clause entries ** either once or twice. ** ** The first test is always performed if there are two or more entries ** remaining and never performed if there is only one FROM clause entry ** to choose from. The first test looks for an "optimal" scan. In ** this context an optimal scan is one that uses the same strategy ** for the given FROM clause entry as would be selected if the entry ** were used as the innermost nested loop. In other words, a table ** is chosen such that the cost of running that table cannot be reduced ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. ** ** The second loop iteration is only performed if no optimal scan | > > > > > > > | | | | > > | > | > | 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; WHERETRACE(("*** Begin search for loop %d ***\n", i)); /* Loop through the remaining entries in the FROM clause to find the ** next nested loop. The loop tests all FROM clause entries ** either once or twice. ** ** The first test is always performed if there are two or more entries ** remaining and never performed if there is only one FROM clause entry ** to choose from. The first test looks for an "optimal" scan. In ** this context an optimal scan is one that uses the same strategy ** for the given FROM clause entry as would be selected if the entry ** were used as the innermost nested loop. In other words, a table ** is chosen such that the cost of running that table cannot be reduced ** by waiting for other tables to run first. This "optimal" test works ** by first assuming that the FROM clause is on the inner loop and finding ** its query plan, then checking to see if that query plan uses any ** other FROM clause terms that are notReady. If no notReady terms are ** used then the "optimal" query plan works. ** ** Note that the WhereCost.nRow parameter for an optimal scan might ** not be as small as it would be if the table really were the innermost ** join. The nRow value can be reduced by WHERE clause constraints ** that do not use indices. But this nRow reduction only happens if the ** table really is the innermost join. ** ** The second loop iteration is only performed if no optimal scan ** strategies were found by the first iteration. This second iteration ** is used to search for the lowest cost scan overall. ** ** Previous versions of SQLite performed only the second iteration - ** the next outermost loop was always that with the lowest overall ** cost. However, this meant that SQLite could select the wrong plan ** for scripts such as the following: ** ** CREATE TABLE t1(a, b); ** CREATE TABLE t2(c, d); ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; ** ** The best strategy is to iterate through table t1 first. However it ** is not possible to determine this with a simple greedy algorithm. ** Since the cost of a linear scan through table t2 is the same ** as the cost of a linear scan through table t1, a simple greedy ** algorithm may choose to use t2 for the outer loop, which is a much ** costlier approach. */ nUnconstrained = 0; notIndexed = 0; for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){ Bitmask mask; /* Mask of tables not yet ready */ for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){ int doNotReorder; /* True if this table should not be reordered */ WhereCost sCost; /* Cost information from best[Virtual]Index() */ ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; if( j!=iFrom && doNotReorder ) break; m = getMask(pMaskSet, pTabItem->iCursor); if( (m & notReady)==0 ){ if( j==iFrom ) iFrom++; continue; } mask = (isOptimal ? m : notReady); pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); if( pTabItem->pIndex==0 ) nUnconstrained++; WHERETRACE(("=== trying table %d with isOptimal=%d ===\n", j, isOptimal)); assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else #endif { bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that ** index if it uses any index at all */ assert( pTabItem->pIndex==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 |
︙ | ︙ | |||
4171 4172 4173 4174 4175 4176 4177 | */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */ | | > > | | | > | > | > | 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 | */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */ || (sCost.rCost<=bestPlan.rCost && sCost.plan.nRow<bestPlan.plan.nRow)) ){ WHERETRACE(("=== table %d is best so far" " with cost=%g and nRow=%g\n", j, sCost.rCost, sCost.plan.nRow)); bestPlan = sCost; bestJ = j; } if( doNotReorder ) break; } } assert( bestJ>=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d" " with cost=%g and nRow=%g\n", bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow)); if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ *ppOrderBy = 0; } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ pLevel->iIdxCur = pParse->nTab++; }else{ pLevel->iIdxCur = -1; } notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); pLevel->iFrom = (u8)bestJ; if( bestPlan.plan.nRow>=(double)1 ){ pParse->nQueryLoop *= bestPlan.plan.nRow; } /* Check that if the table scanned by this loop iteration had an ** INDEXED BY clause attached to it, that the named index is being ** used for the scan. If not, then query compilation has failed. ** Return an error. */ pIdx = pTabList->a[bestJ].pIndex; |
︙ | ︙ | |||
4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 | } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 | } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; pWInfo->nRowOut = (double)1; for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; pLevel->iTabCur = pTabItem->iCursor; pWInfo->nRowOut *= pLevel->plan.nRow; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); |
︙ | ︙ | |||
4340 4341 4342 4343 4344 4345 4346 4347 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; i<nTabList; i++){ notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); | > > | | 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; i<nTabList; i++){ pLevel = &pWInfo->a[i]; explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags); notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); pWInfo->iContinue = pLevel->addrCont; } #ifdef SQLITE_TEST /* For testing and debugging use only */ /* Record in the query plan information about the current table ** and the index used to access it (if any). If the table itself ** is not used, its name is just '{}'. If no index is used ** the index is listed as "{}". If the primary key is used the |
︙ | ︙ |
Changes to test/alter2.test.
︙ | ︙ | |||
23 24 25 26 27 28 29 | ifcapable {!pragma} return # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts. See proc [set_file_format]. # do_not_use_codec | < < < < < | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | ifcapable {!pragma} return # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts. See proc [set_file_format]. # do_not_use_codec # The file format change affects the way row-records stored in tables (but # not indices) are interpreted. Before version 3.1.3, a row-record for a # table with N columns was guaranteed to contain exactly N fields. As # of version 3.1.3, the record may contain up to N fields. In this case # the M fields that are present are the values for the left-most M # columns. The (N-M) rightmost columns contain NULL. # |
︙ | ︙ |
Changes to test/alter3.test.
︙ | ︙ | |||
24 25 26 27 28 29 30 | ifcapable !altertable { finish_test return } # Determine if there is a codec available on this test. # | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | ifcapable !altertable { finish_test return } # Determine if there is a codec available on this test. # if {[catch {sqlite3 -has-codec} r] || $r} { set has_codec 1 } else { set has_codec 0 } # Test Organisation: |
︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 | # proc get_file_format {{fname test.db}} { return [hexio_get_int [hexio_read $fname 44 4]] } do_test alter3-1.1 { execsql { CREATE TABLE abc(a, b, c); SELECT sql FROM sqlite_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter3-1.2 { execsql {ALTER TABLE abc ADD d INTEGER;} execsql { | > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | # proc get_file_format {{fname test.db}} { return [hexio_get_int [hexio_read $fname 44 4]] } do_test alter3-1.1 { execsql { PRAGMA legacy_file_format=ON; CREATE TABLE abc(a, b, c); SELECT sql FROM sqlite_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter3-1.2 { execsql {ALTER TABLE abc ADD d INTEGER;} execsql { |
︙ | ︙ | |||
194 195 196 197 198 199 200 201 202 203 204 205 206 207 | } do_test alter3-4.1 { db close file delete -force test.db set ::DB [sqlite3 db test.db] execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 100); INSERT INTO t1 VALUES(2, 300); SELECT * FROM t1; } } {1 100 2 300} do_test alter3-4.1 { | > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | } do_test alter3-4.1 { db close file delete -force test.db set ::DB [sqlite3 db test.db] execsql { PRAGMA legacy_file_format=ON; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 100); INSERT INTO t1 VALUES(2, 300); SELECT * FROM t1; } } {1 100 2 300} do_test alter3-4.1 { |
︙ | ︙ |
Changes to test/alter4.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 | # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } | < < < < < < < < < < < < < < | 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 | # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } # Test Organisation: # ------------------ # # alter4-1.*: Test that ALTER TABLE correctly modifies the CREATE TABLE sql. # alter4-2.*: Test error messages. # alter4-3.*: Test adding columns with default value NULL. # alter4-4.*: Test adding columns with default values other than NULL. # alter4-5.*: Test adding columns to tables in ATTACHed databases. # alter4-6.*: Test that temp triggers are not accidentally dropped. # alter4-7.*: Test that VACUUM resets the file-format. # do_test alter4-1.1 { execsql { CREATE TEMP TABLE abc(a, b, c); SELECT sql FROM sqlite_temp_master; } } {{CREATE TABLE abc(a, b, c)}} do_test alter4-1.2 { |
︙ | ︙ | |||
178 179 180 181 182 183 184 | } {} do_test alter4-3.2 { execsql { ALTER TABLE t1 ADD c; SELECT * FROM t1; } } {1 100 {} 2 300 {}} | < < < < < | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | } {} do_test alter4-3.2 { execsql { ALTER TABLE t1 ADD c; SELECT * FROM t1; } } {1 100 {} 2 300 {}} ifcapable schema_version { do_test alter4-3.4 { execsql { PRAGMA schema_version; } } {10} } |
︙ | ︙ | |||
213 214 215 216 217 218 219 | } {} do_test alter4-4.2 { execsql { ALTER TABLE t1 ADD c DEFAULT 'hello world'; SELECT * FROM t1; } } {1 100 {hello world} 2 300 {hello world}} | < < < < < | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | } {} do_test alter4-4.2 { execsql { ALTER TABLE t1 ADD c DEFAULT 'hello world'; SELECT * FROM t1; } } {1 100 {hello world} 2 300 {hello world}} ifcapable schema_version { do_test alter4-4.4 { execsql { PRAGMA schema_version; } } {20} } |
︙ | ︙ | |||
263 264 265 266 267 268 269 | ifcapable schema_version { do_test alter4-5.4 { execsql { PRAGMA aux.schema_version; } } {31} } | < < < < < | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | ifcapable schema_version { do_test alter4-5.4 { execsql { PRAGMA aux.schema_version; } } {31} } do_test alter4-5.6 { execsql { ALTER TABLE aux.t1 ADD COLUMN d DEFAULT 1000; SELECT sql FROM aux.sqlite_master; } } {{CREATE TABLE t1(a,b, c VARCHAR(128), d DEFAULT 1000)}} do_test alter4-5.7 { |
︙ | ︙ | |||
329 330 331 332 333 334 335 | ALTER TABLE t1 ADD COLUMN c DEFAULT 'c'; INSERT INTO t1(a, b) VALUES(3, 4); SELECT * FROM log; } } {b 1 2 a 1 2 b 3 4 a 3 4} } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | ALTER TABLE t1 ADD COLUMN c DEFAULT 'c'; INSERT INTO t1(a, b) VALUES(3, 4); SELECT * FROM log; } } {b 1 2 a 1 2 b 3 4 a 3 4} } # Ticket #1183 - Make sure adding columns to large tables does not cause # memory corruption (as was the case before this bug was fixed). do_test alter4-8.1 { execsql { CREATE TEMP TABLE t4(c1); } } {} |
︙ | ︙ |
Changes to test/analyze.test.
︙ | ︙ | |||
69 70 71 72 73 74 75 | do_test analyze-1.6.3 { catchsql { CREATE INDEX main.stat1idx ON SQLite_stat1(idx); } } {1 {table sqlite_stat1 may not be indexed}} do_test analyze-1.7 { execsql { | | | | | | | | | 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 | do_test analyze-1.6.3 { catchsql { CREATE INDEX main.stat1idx ON SQLite_stat1(idx); } } {1 {table sqlite_stat1 may not be indexed}} do_test analyze-1.7 { execsql { SELECT * FROM sqlite_stat1 WHERE idx NOT NULL } } {} do_test analyze-1.8 { catchsql { ANALYZE main } } {0 {}} do_test analyze-1.9 { execsql { SELECT * FROM sqlite_stat1 WHERE idx NOT NULL } } {} do_test analyze-1.10 { catchsql { CREATE TABLE t1(a,b); ANALYZE main.t1; } } {0 {}} do_test analyze-1.11 { execsql { SELECT * FROM sqlite_stat1 } } {t1 {} 0} do_test analyze-1.12 { catchsql { ANALYZE t1; } } {0 {}} do_test analyze-1.13 { execsql { SELECT * FROM sqlite_stat1 } } {t1 {} 0} # Create some indices that can be analyzed. But do not yet add # data. Without data in the tables, no analysis is done. # do_test analyze-2.1 { execsql { CREATE INDEX t1i1 ON t1(a); ANALYZE main.t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } } {t1 {} 0} do_test analyze-2.2 { execsql { CREATE INDEX t1i2 ON t1(b); ANALYZE t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } } {t1 {} 0} do_test analyze-2.3 { execsql { CREATE INDEX t1i3 ON t1(a,b); ANALYZE main; SELECT * FROM sqlite_stat1 ORDER BY idx; } } {t1 {} 0} # Start adding data to the table. Verify that the analysis # is done correctly. # do_test analyze-3.1 { execsql { INSERT INTO t1 VALUES(1,2); |
︙ | ︙ |
Changes to test/analyze2.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec #-------------------------------------------------------------------- | > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } set testprefix analyze2 # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec #-------------------------------------------------------------------- |
︙ | ︙ | |||
115 116 117 118 119 120 121 | } for {set i 0} {$i < 1000} {incr i} { execsql { INSERT INTO t1 VALUES($i, $i) } } execsql COMMIT execsql ANALYZE } {} | | | > | > | | > | > | | > | > | | > | > | | > | > | | > | > | | > | > | | > | > | | > | > | | > | > | 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 | } for {set i 0} {$i < 1000} {incr i} { execsql { INSERT INTO t1 VALUES($i, $i) } } execsql COMMIT execsql ANALYZE } {} do_eqp_test 2.2 { SELECT * FROM t1 WHERE x>500 AND y>700 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)} } do_eqp_test 2.3 { SELECT * FROM t1 WHERE x>700 AND y>500 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)} } do_eqp_test 2.3 { SELECT * FROM t1 WHERE y>700 AND x>500 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~100 rows)} } do_eqp_test 2.4 { SELECT * FROM t1 WHERE y>500 AND x>700 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>?) (~100 rows)} } do_eqp_test 2.5 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 200 AND y BETWEEN 400 AND 700 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)} } do_eqp_test 2.6 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 400 AND 700 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~75 rows)} } do_eqp_test 2.7 { SELECT * FROM t1 WHERE x BETWEEN -400 AND -300 AND y BETWEEN 100 AND 300 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)} } do_eqp_test 2.8 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN -400 AND -300 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~25 rows)} } do_eqp_test 2.9 { SELECT * FROM t1 WHERE x BETWEEN 500 AND 100 AND y BETWEEN 100 AND 300 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~25 rows)} } do_eqp_test 2.10 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 300 AND y BETWEEN 500 AND 100 } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~25 rows)} } do_test analyze2-3.1 { set alphabet [list a b c d e f g h i j] execsql BEGIN for {set i 0} {$i < 1000} {incr i} { set str [lindex $alphabet [expr ($i/100)%10]] append str [lindex $alphabet [expr ($i/ 10)%10]] |
︙ | ︙ | |||
173 174 175 176 177 178 179 | SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't1_y' GROUP BY tbl,idx } } {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}} | | | > | > | | > | > | | > | > | | > | > | | > | > | 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 | SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't1_y' GROUP BY tbl,idx } } {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}} do_eqp_test 3.3 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b' } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~50 rows)} } do_eqp_test 3.4 { SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h' } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x>? AND x<?) (~50 rows)} } do_eqp_test 3.5 { SELECT * FROM t1 WHERE x<'a' AND y>'h' } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)} } do_eqp_test 3.6 { SELECT * FROM t1 WHERE x<444 AND y>'h' } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)} } do_eqp_test 3.7 { SELECT * FROM t1 WHERE x<221 AND y>'g' } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1_x (x<?) (~66 rows)} } do_test analyze2-4.1 { execsql { CREATE TABLE t3(a COLLATE nocase, b) } execsql { CREATE INDEX t3a ON t3(a) } execsql { CREATE INDEX t3b ON t3(b) } set alphabet [list A b C d E f G h I j] execsql BEGIN |
︙ | ︙ | |||
221 222 223 224 225 226 227 | SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3b' GROUP BY tbl,idx } } {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}} | | | > | > | | > | > | 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3b' GROUP BY tbl,idx } } {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}} do_eqp_test 4.4 { SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C' } { 0 0 0 {SEARCH TABLE t3 USING INDEX t3b (b>? AND b<?) (~11 rows)} } do_eqp_test 4.5 { SELECT * FROM t3 WHERE a > 'A' AND a < 'c' AND b > 'A' AND b < 'c' } { 0 0 0 {SEARCH TABLE t3 USING INDEX t3a (a>? AND a<?) (~22 rows)} } ifcapable utf16 { proc test_collate {enc lhs rhs} { # puts $enc return [string compare $lhs $rhs] } do_test analyze2-5.1 { |
︙ | ︙ | |||
256 257 258 259 260 261 262 | execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE tbl = 't4' GROUP BY tbl,idx } } {t4 t4x {afa bej cej dej eej fej gej hej iej jej}} | | | | | | > | > > | | > | > > | 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 | execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE tbl = 't4' GROUP BY tbl,idx } } {t4 t4x {afa bej cej dej eej fej gej hej iej jej}} do_eqp_test 5.3 { SELECT * FROM t4 WHERE x>'ccc' } {0 0 0 {SEARCH TABLE t4 USING COVERING INDEX t4x (x>?) (~800 rows)}} do_eqp_test 5.4 { SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ccc' AND t42.x>'ggg' } { 0 0 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~300 rows)} 0 1 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~800 rows)} } do_eqp_test 5.5 { SELECT * FROM t4 AS t41, t4 AS t42 WHERE t41.x>'ddd' AND t42.x>'ccc' } { 0 0 0 {SEARCH TABLE t4 AS t41 USING COVERING INDEX t4x (x>?) (~700 rows)} 0 1 1 {SEARCH TABLE t4 AS t42 USING COVERING INDEX t4x (x>?) (~800 rows)} } } #-------------------------------------------------------------------- # These tests, analyze2-6.*, verify that the library behaves correctly # when one of the sqlite_stat1 and sqlite_stat2 tables is missing. # # If the sqlite_stat1 table is not present, then the sqlite_stat2 |
︙ | ︙ | |||
302 303 304 305 306 307 308 | } {} do_test analyze2-6.1.1 { eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } | | | | | | | | | | | | | | 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 458 459 460 461 462 463 464 465 466 467 | } {} do_test analyze2-6.1.1 { eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.2 { db cache flush execsql ANALYZE eqp {SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.4 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.5 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a=? AND b=?) (~9 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.1.6 { execsql { PRAGMA writable_schema = 1; INSERT INTO sqlite_master SELECT * FROM master; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a = 1 AND t6.a = 1 AND t6.b = 1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a=?) (~1 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.1 { execsql { DELETE FROM sqlite_stat1; DELETE FROM sqlite_stat2; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.2 { db cache flush execsql ANALYZE eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.3 { sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.4 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat1'; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.5 { execsql { PRAGMA writable_schema = 1; DELETE FROM sqlite_master WHERE tbl_name = 'sqlite_stat2'; } sqlite3 db test.db eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~110000 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-6.2.6 { execsql { PRAGMA writable_schema = 1; INSERT INTO sqlite_master SELECT * FROM master; } sqlite3 db test.db execsql ANALYZE eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} #-------------------------------------------------------------------- # These tests, analyze2-7.*, test that the sqlite_stat2 functionality # works in shared-cache mode. Note that these tests reuse the database # created for the analyze2-6.* tests. # ifcapable shared_cache { |
︙ | ︙ | |||
455 456 457 458 459 460 461 | } {20} do_test analyze2-7.5 { eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 | | | | | | | | 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 | } {20} do_test analyze2-7.5 { eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.6 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.7 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.8 { execsql { DELETE FROM sqlite_stat2 } db2 execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.9 { execsql { SELECT * FROM sqlite_master } db2 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db2 } {0 0 1 {SEARCH TABLE t6 USING COVERING INDEX t6i (a>?) (~2 rows)} 0 1 0 {SEARCH TABLE t5 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze2-7.10 { incr_schema_cookie test.db execsql { SELECT * FROM sqlite_master } db1 eqp { SELECT * FROM t5,t6 WHERE t5.rowid=t6.rowid AND t5.a>1 AND t5.a<15 AND t6.a>1 } db1 } {0 0 0 {SEARCH TABLE t5 USING COVERING INDEX t5i (a>? AND a<?) (~2 rows)} 0 1 1 {SEARCH TABLE t6 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} db1 close db2 close sqlite3_enable_shared_cache $::enable_shared_cache } finish_test |
Changes to test/analyze3.test.
︙ | ︙ | |||
91 92 93 94 95 96 97 | } execsql { COMMIT; ANALYZE; } } {} | | | | | | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | } execsql { COMMIT; ANALYZE; } } {} do_eqp_test analyze3-1.1.2 { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~100 rows)}} do_eqp_test analyze3-1.1.3 { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 } {0 0 0 {SCAN TABLE t1 (~111 rows)}} do_test analyze3-1.1.4 { sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.1.5 { set l [string range "200" 0 end] set u [string range "300" 0 end] |
︙ | ︙ | |||
140 141 142 143 144 145 146 | CREATE TABLE t2(x TEXT, y); INSERT INTO t2 SELECT * FROM t1; CREATE INDEX i2 ON t2(x); COMMIT; ANALYZE; } } {} | | | | | | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | CREATE TABLE t2(x TEXT, y); INSERT INTO t2 SELECT * FROM t1; CREATE INDEX i2 ON t2(x); COMMIT; ANALYZE; } } {} do_eqp_test analyze3-1.2.2 { SELECT sum(y) FROM t2 WHERE x>1 AND x<2 } {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~200 rows)}} do_eqp_test analyze3-1.2.3 { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 } {0 0 0 {SCAN TABLE t2 (~111 rows)}} do_test analyze3-1.2.4 { sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 } } {161 0 4760} do_test analyze3-1.2.5 { set l [string range "12" 0 end] set u [string range "20" 0 end] sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u} |
︙ | ︙ | |||
187 188 189 190 191 192 193 | CREATE TABLE t3(y TEXT, x INTEGER); INSERT INTO t3 SELECT y, x FROM t1; CREATE INDEX i3 ON t3(x); COMMIT; ANALYZE; } } {} | | | | | | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | CREATE TABLE t3(y TEXT, x INTEGER); INSERT INTO t3 SELECT y, x FROM t1; CREATE INDEX i3 ON t3(x); COMMIT; ANALYZE; } } {} do_eqp_test analyze3-1.3.2 { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~100 rows)}} do_eqp_test analyze3-1.3.3 { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 } {0 0 0 {SCAN TABLE t3 (~111 rows)}} do_test analyze3-1.3.4 { sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 } } {199 0 14850} do_test analyze3-1.3.5 { set l [string range "200" 0 end] set u [string range "300" 0 end] |
︙ | ︙ | |||
242 243 244 245 246 247 248 | append t [lindex {a b c d e f g h i j} [expr $i/100]] append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]] append t [lindex {a b c d e f g h i j} [expr ($i%10)]] execsql { INSERT INTO t1 VALUES($i, $t) } } execsql COMMIT } {} | | | | | | | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | append t [lindex {a b c d e f g h i j} [expr $i/100]] append t [lindex {a b c d e f g h i j} [expr ($i/10)%10]] append t [lindex {a b c d e f g h i j} [expr ($i%10)]] execsql { INSERT INTO t1 VALUES($i, $t) } } execsql COMMIT } {} do_eqp_test analyze3-2.2 { SELECT count(a) FROM t1 WHERE b LIKE 'a%' } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~55000 rows)}} do_eqp_test analyze3-2.3 { SELECT count(a) FROM t1 WHERE b LIKE '%a' } {0 0 0 {SCAN TABLE t1 (~500000 rows)}} do_test analyze3-2.4 { sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' } } {101 0 100} do_test analyze3-2.5 { sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE '%a' } } {999 999 100} |
︙ | ︙ |
Changes to test/attachmalloc.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 | finish_test return } source $testdir/malloc_common.tcl do_malloc_test attachmalloc-1 -tclprep { | | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | finish_test return } source $testdir/malloc_common.tcl do_malloc_test attachmalloc-1 -tclprep { catch { db close } for {set i 2} {$i<=4} {incr i} { catch { db$i close } file delete -force test$i.db file delete -force test$i.db-journal } } -tclbody { if {[catch {sqlite3 db test.db}]} { error "out of memory" } |
︙ | ︙ | |||
57 58 59 60 61 62 63 | db2 close } -sqlbody { CREATE TABLE t1(d, e, f); ATTACH 'test2.db' AS db1; } set enable_shared_cache [sqlite3_enable_shared_cache 1] | | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | db2 close } -sqlbody { CREATE TABLE t1(d, e, f); ATTACH 'test2.db' AS db1; } set enable_shared_cache [sqlite3_enable_shared_cache 1] sqlite3 dbaux test3.db dbaux eval {SELECT * FROM sqlite_master} do_malloc_test attachmalloc-3 -sqlbody { SELECT * FROM sqlite_master; ATTACH 'test3.db' AS three; } -cleanup { db eval { DETACH three } } dbaux close sqlite3_enable_shared_cache $enable_shared_cache finish_test |
Changes to test/auth.test.
︙ | ︙ | |||
1972 1973 1974 1975 1976 1977 1978 | CREATE TABLE t4(a,b,c); CREATE INDEX t4i1 ON t4(a); CREATE INDEX t4i2 ON t4(b,a,c); INSERT INTO t4 VALUES(1,2,3); ANALYZE; } set ::authargs | | | | | 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 | CREATE TABLE t4(a,b,c); CREATE INDEX t4i1 ON t4(a); CREATE INDEX t4i2 ON t4(b,a,c); INSERT INTO t4 VALUES(1,2,3); ANALYZE; } set ::authargs } {t4 {} main {} t2 {} main {}} do_test auth-1.295 { execsql { SELECT count(*) FROM sqlite_stat1; } } 3 proc auth {code args} { if {$code=="SQLITE_ANALYZE"} { set ::authargs [concat $::authargs $args] return SQLITE_DENY } return SQLITE_OK } do_test auth-1.296 { set ::authargs {} catchsql { ANALYZE; } } {1 {not authorized}} do_test auth-1.297 { execsql { SELECT count(*) FROM sqlite_stat1; } } 3 } ;# ifcapable analyze # Authorization for ALTER TABLE ADD COLUMN. # These tests are omitted if the library # was built without ALTER TABLE support. ifcapable {altertable} { |
︙ | ︙ |
Changes to test/autoindex1.test.
︙ | ︙ | |||
136 137 138 139 140 141 142 | } } {4087} # Ticket [8011086c85c6c404014c947fcf3eb9f42b184a0d] from 2010-07-08 # Make sure automatic indices are not created for the RHS of an IN expression # that is not a correlated subquery. # | | < | | | | | > > > > | < | < | | | > > > > | < | < | | | | > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | } } {4087} # Ticket [8011086c85c6c404014c947fcf3eb9f42b184a0d] from 2010-07-08 # Make sure automatic indices are not created for the RHS of an IN expression # that is not a correlated subquery. # do_execsql_test autoindex1-500 { CREATE TABLE t501(a INTEGER PRIMARY KEY, b); CREATE TABLE t502(x INTEGER PRIMARY KEY, y); EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=?); } { 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)} 0 0 0 {EXECUTE LIST SUBQUERY 1} 1 0 0 {SCAN TABLE t502 (~100000 rows)} } do_execsql_test autoindex1-501 { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } { 0 0 0 {SCAN TABLE t501 (~500000 rows)} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} 1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?) (~7 rows)} } do_execsql_test autoindex1-502 { EXPLAIN QUERY PLAN SELECT b FROM t501 WHERE t501.a=123 AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b); } { 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} 1 0 0 {SCAN TABLE t502 (~100000 rows)} } # The following code checks a performance regression reported on the # mailing list on 2010-10-19. The problem is that the nRowEst field # of ephermeral tables was not being initialized correctly and so no # automatic index was being created for the emphemeral table when it was # used as part of a join. # do_execsql_test autoindex1-600 { CREATE TABLE flock_owner( owner_rec_id INTEGER CONSTRAINT flock_owner_key PRIMARY KEY, flock_no VARCHAR(6) NOT NULL REFERENCES flock (flock_no), owner_person_id INTEGER NOT NULL REFERENCES person (person_id), owner_change_date TEXT, last_changed TEXT NOT NULL, CONSTRAINT fo_owner_date UNIQUE (flock_no, owner_change_date) ); CREATE TABLE sheep ( Sheep_No char(7) NOT NULL, Date_of_Birth char(8), Sort_DoB text, Flock_Book_Vol char(2), Breeder_No char(6), Breeder_Person integer, Originating_Flock char(6), Registering_Flock char(6), Tag_Prefix char(9), Tag_No char(15), Sort_Tag_No integer, Breeders_Temp_Tag char(15), Sex char(1), Sheep_Name char(32), Sire_No char(7), Dam_No char(7), Register_Code char(1), Colour char(48), Colour_Code char(2), Pattern_Code char(8), Horns char(1), Litter_Size char(1), Coeff_of_Inbreeding real, Date_of_Registration text, Date_Last_Changed text, UNIQUE(Sheep_No)); CREATE INDEX fo_flock_no_index ON flock_owner (flock_no); CREATE INDEX fo_owner_change_date_index ON flock_owner (owner_change_date); CREATE INDEX fo_owner_person_id_index ON flock_owner (owner_person_id); CREATE INDEX sheep_org_flock_index ON sheep (originating_flock); CREATE INDEX sheep_reg_flock_index ON sheep (registering_flock); EXPLAIN QUERY PLAN SELECT x.sheep_no, x.registering_flock, x.date_of_registration FROM sheep x LEFT JOIN (SELECT s.sheep_no, prev.flock_no, prev.owner_person_id, s.date_of_registration, prev.owner_change_date FROM sheep s JOIN flock_owner prev ON s.registering_flock = prev.flock_no AND (prev.owner_change_date <= s.date_of_registration || ' 00:00:00') WHERE NOT EXISTS (SELECT 'x' FROM flock_owner later WHERE prev.flock_no = later.flock_no AND later.owner_change_date > prev.owner_change_date AND later.owner_change_date <= s.date_of_registration||' 00:00:00') ) y ON x.sheep_no = y.sheep_no WHERE y.sheep_no IS NULL ORDER BY x.registering_flock; } { 1 0 0 {SCAN TABLE sheep AS s (~1000000 rows)} 1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?) (~2 rows)} 1 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} 2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?) (~1 rows)} 0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index (~1000000 rows)} 0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?) (~8 rows)} } finish_test |
Changes to test/backcompat.test.
︙ | ︙ | |||
35 36 37 38 39 40 41 | # set binaries [list] set pattern "[file tail [info nameofexec]]?*" if {$tcl_platform(platform)=="windows"} { set pattern [string map {\.exe {}} $pattern] } foreach file [glob -nocomplain $pattern] { | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | # set binaries [list] set pattern "[file tail [info nameofexec]]?*" if {$tcl_platform(platform)=="windows"} { set pattern [string map {\.exe {}} $pattern] } foreach file [glob -nocomplain $pattern] { if {[file executable $file] && [file isfile $file]} {lappend binaries $file} } if {[llength $binaries]==0} { puts "WARNING: No historical binaries to test against." puts "WARNING: No backwards-compatibility tests have been run." finish_test return } |
︙ | ︙ |
Changes to test/backup.test.
︙ | ︙ | |||
173 174 175 176 177 178 179 | eval $zOpenScript # Set to true if copying to an in-memory destination. Copying to an # in-memory destination is only possible if the initial destination # page size is the same as the source page size (in this case 1024 bytes). # set isMemDest [expr { | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | eval $zOpenScript # Set to true if copying to an in-memory destination. Copying to an # in-memory destination is only possible if the initial destination # page size is the same as the source page size (in this case 1024 bytes). # set isMemDest [expr { $zDestFile eq ":memory:" || $file_dest eq "temp" && $TEMP_STORE>=2 }] if { $isMemDest==0 || $pgsz_dest == 1024 } { if 0 { puts -nonewline "Test $iTest: src=$zSrcFile dest=$zDestFile" puts -nonewline " (as $db_dest.$file_dest)" puts -nonewline " rows_dest=$rows_dest pgsz_dest=$pgsz_dest" |
︙ | ︙ |
Changes to test/capi3d.test.
1 2 3 4 5 6 7 8 9 10 11 12 | # 2008 June 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 implements regression tests for SQLite library. # | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # 2008 June 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 implements regression tests for SQLite library. # # This file is devoted to testing the sqlite3_next_stmt and # sqlite3_stmt_readonly interfaces. # # $Id: capi3d.test,v 1.2 2008/07/14 15:11:20 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl |
︙ | ︙ | |||
84 85 86 87 88 89 90 | do_test capi3-1.2.$i.2 { foreach p [scramble $::stmtlist] { sqlite3_finalize $p } sqlite3_next_stmt db 0 } {} } | | > | > > > > > > > > > > > > > > > > > > > > > | 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 | do_test capi3-1.2.$i.2 { foreach p [scramble $::stmtlist] { sqlite3_finalize $p } sqlite3_next_stmt db 0 } {} } # Tests for the is-read-only interface. # proc test_is_readonly {testname sql truth} { do_test $testname [format { set DB [sqlite3_connection_pointer db] set STMT [sqlite3_prepare $DB {%s} -1 TAIL] set rc [sqlite3_stmt_readonly $STMT] sqlite3_finalize $STMT set rc } $sql] $truth } test_is_readonly capi3d-2.1 {SELECT * FROM sqlite_master} 1 test_is_readonly capi3d-2.2 {CREATE TABLE t1(x)} 0 db eval {CREATE TABLE t1(x)} test_is_readonly capi3d-2.3 {INSERT INTO t1 VALUES(5)} 0 test_is_readonly capi3d-2.4 {UPDATE t1 SET x=x+1 WHERE x<0} 0 test_is_readonly capi3d-2.5 {SELECT * FROM t1} 1 do_test capi3-2.99 { sqlite3_stmt_readonly 0 } 1 finish_test |
Added test/capi3e.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 | # 2010 Novemeber 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 implements regression tests for SQLite library. The # focus of this script testing the callback-free C/C++ API. # # $Id: capi3e.test,v 1.70 2009/01/09 02:49:32 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Make sure the system encoding is utf-8. Otherwise, if the system encoding # is other than utf-8, [file isfile $x] may not refer to the same file # as [sqlite3 db $x]. encoding system utf-8 # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. proc utf16 {str {nt 1}} { set r [encoding convertto unicode $str] if {$nt} { append r "\x00\x00" } return $r } # Return the UTF-8 representation of the supplied UTF-16 string $str. proc utf8 {str} { # If $str ends in two 0x00 0x00 bytes, knock these off before # converting to UTF-8 using TCL. binary scan $str \c* vals if {[lindex $vals end]==0 && [lindex $vals end-1]==0} { set str [binary format \c* [lrange $vals 0 end-2]] } set r [encoding convertfrom unicode $str] return $r } # These tests complement those in capi2.test. They are organized # as follows: # # capi3e-1.*: Test sqlite3_open with various UTF8 filenames # capi3e-2.*: Test sqlite3_open16 with various UTF8 filenames # capi3e-3.*: Test ATTACH with various UTF8 filenames db close # here's the list of file names we're testing set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db} set i 0 foreach name $names { incr i do_test capi3e-1.1.$i { set db2 [sqlite3_open $name {}] sqlite3_errcode $db2 } {SQLITE_OK} do_test capi3e-1.2.$i { sqlite3_close $db2 } {SQLITE_OK} do_test capi3e-1.3.$i { file isfile $name } {1} } set i 0 foreach name $names { incr i do_test capi3e-2.1.$i { set db2 [sqlite3_open16 [utf16 $name] {}] sqlite3_errcode $db2 } {SQLITE_OK} do_test capi3e-2.2.$i { sqlite3_close $db2 } {SQLITE_OK} do_test capi3e-2.3.$i { file isfile $name } {1} } ifcapable attach { do_test capi3e-3.1 { sqlite3 db2 base.db } {} set i 0 foreach name $names { incr i do_test capi3e-3.2.$i { db2 eval "ATTACH DATABASE '$name' AS db$i;" } {} do_test capi3e-3.3.$i { db2 eval "DETACH DATABASE db$i;" } {} } do_test capi3e-3.4 { db2 close } {} } # clean up forcedelete base.db foreach name $names { forcedelete $name } finish_test |
Changes to test/conflict.test.
︙ | ︙ | |||
303 304 305 306 307 308 309 | 12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1 16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0 } { if {$t0} {set t1 {column a is not unique}} | | | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | 12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0 13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0 0 14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0 0 15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 0 1 16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0 0 } { if {$t0} {set t1 {column a is not unique}} if {[info exists TEMP_STORE] && $TEMP_STORE==3} { set t3 0 } else { set t3 [expr {$t3+$t4}] } do_test conflict-6.$i { db close sqlite3 db test.db |
︙ | ︙ |
Changes to test/date.test.
︙ | ︙ | |||
80 81 82 83 84 85 86 | set sql [format {strftime('%%H:%%M:%%f',1237962480.%03d,'unixepoch')} $i] set res [format {06:28:00.%03d} $i] datetest 2.2c-$i $sql $res } datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26 datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27 datetest 2.4a {date('2003-10-22','weekday 1')} 2003-10-27 | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | set sql [format {strftime('%%H:%%M:%%f',1237962480.%03d,'unixepoch')} $i] set res [format {06:28:00.%03d} $i] datetest 2.2c-$i $sql $res } datetest 2.3 {date('2003-10-22','weekday 0')} 2003-10-26 datetest 2.4 {date('2003-10-22','weekday 1')} 2003-10-27 datetest 2.4a {date('2003-10-22','weekday 1')} 2003-10-27 datetest 2.4b {date('2003-10-22','weekday 1x')} NULL datetest 2.4c {date('2003-10-22','weekday -1')} NULL datetest 2.4d {date('2003-10-22','weakday 1x')} NULL datetest 2.4e {date('2003-10-22','weekday ')} NULL datetest 2.5 {date('2003-10-22','weekday 2')} 2003-10-28 datetest 2.6 {date('2003-10-22','weekday 3')} 2003-10-22 datetest 2.7 {date('2003-10-22','weekday 4')} 2003-10-23 datetest 2.8 {date('2003-10-22','weekday 5')} 2003-10-24 |
︙ | ︙ |
Changes to test/dbstatus.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #*********************************************************************** # # Tests for the sqlite3_db_status() function # set testdir [file dirname $argv0] source $testdir/tester.tcl # Make sure sqlite3_db_config() and sqlite3_db_status are working. # unset -nocomplain PAGESZ unset -nocomplain BASESZ do_test dbstatus-1.1 { | > > > > > > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #*********************************************************************** # # Tests for the sqlite3_db_status() function # set testdir [file dirname $argv0] source $testdir/tester.tcl # Memory statistics must be enabled for this test. db close sqlite3_shutdown sqlite3_config_memstatus 1 sqlite3_initialize sqlite3 db test.db # Make sure sqlite3_db_config() and sqlite3_db_status are working. # unset -nocomplain PAGESZ unset -nocomplain BASESZ do_test dbstatus-1.1 { |
︙ | ︙ |
Added test/e_createtable.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 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 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 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 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 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 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 1627 1628 1629 1630 1631 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 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 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 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 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 1922 1923 1924 1925 1926 1927 1928 1929 1930 | # 2010 September 25 # # 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 to verify that the "testable statements" in # the lang_createtable.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl set ::testprefix e_createtable # Test organization: # # e_createtable-0.*: Test that the syntax diagrams are correct. # # e_createtable-1.*: Test statements related to table and database names, # the TEMP and TEMPORARY keywords, and the IF NOT EXISTS clause. # # e_createtable-2.*: Test "CREATE TABLE AS" statements. # proc do_createtable_tests {nm args} { uplevel do_select_tests [list e_createtable-$nm] $args } #------------------------------------------------------------------------- # This command returns a serialized tcl array mapping from the name of # each attached database to a list of tables in that database. For example, # if the database schema is created with: # # CREATE TABLE t1(x); # CREATE TEMP TABLE t2(x); # CREATE TEMP TABLE t3(x); # # Then this command returns "main t1 temp {t2 t3}". # proc table_list {} { set res [list] db eval { pragma database_list } a { set dbname $a(name) set master $a(name).sqlite_master if {$dbname == "temp"} { set master sqlite_temp_master } lappend res $dbname [ db eval "SELECT DISTINCT tbl_name FROM $master ORDER BY tbl_name" ] } set res } # EVIDENCE-OF: R-25262-01881 -- syntax diagram type-name # do_createtable_tests 0.1.1 -repair { drop_all_tables } { 1 "CREATE TABLE t1(c1 one)" {} 2 "CREATE TABLE t1(c1 one two)" {} 3 "CREATE TABLE t1(c1 one two three)" {} 4 "CREATE TABLE t1(c1 one two three four)" {} 5 "CREATE TABLE t1(c1 one two three four(14))" {} 6 "CREATE TABLE t1(c1 one two three four(14, 22))" {} 7 "CREATE TABLE t1(c1 var(+14, -22.3))" {} 8 "CREATE TABLE t1(c1 var(1.0e10))" {} } do_createtable_tests 0.1.2 -error { near "%s": syntax error } { 1 "CREATE TABLE t1(c1 one(number))" {number} } # EVIDENCE-OF: R-18762-12428 -- syntax diagram column-constraint # # Note: Not shown in the syntax diagram is the "NULL" constraint. This # is the opposite of "NOT NULL" - it implies that the column may # take a NULL value. This is the default anyway, so this type of # constraint is rarely used. # do_createtable_tests 0.2.1 -repair { drop_all_tables execsql { CREATE TABLE t2(x PRIMARY KEY) } } { 1.1 "CREATE TABLE t1(c1 text PRIMARY KEY)" {} 1.2 "CREATE TABLE t1(c1 text PRIMARY KEY ASC)" {} 1.3 "CREATE TABLE t1(c1 text PRIMARY KEY DESC)" {} 1.4 "CREATE TABLE t1(c1 text CONSTRAINT cons PRIMARY KEY DESC)" {} 2.1 "CREATE TABLE t1(c1 text NOT NULL)" {} 2.2 "CREATE TABLE t1(c1 text CONSTRAINT nm NOT NULL)" {} 2.3 "CREATE TABLE t1(c1 text NULL)" {} 2.4 "CREATE TABLE t1(c1 text CONSTRAINT nm NULL)" {} 3.1 "CREATE TABLE t1(c1 text UNIQUE)" {} 3.2 "CREATE TABLE t1(c1 text CONSTRAINT un UNIQUE)" {} 4.1 "CREATE TABLE t1(c1 text CHECK(c1!=0))" {} 4.2 "CREATE TABLE t1(c1 text CONSTRAINT chk CHECK(c1!=0))" {} 5.1 "CREATE TABLE t1(c1 text DEFAULT 1)" {} 5.2 "CREATE TABLE t1(c1 text DEFAULT -1)" {} 5.3 "CREATE TABLE t1(c1 text DEFAULT +1)" {} 5.4 "CREATE TABLE t1(c1 text DEFAULT -45.8e22)" {} 5.5 "CREATE TABLE t1(c1 text DEFAULT (1+1))" {} 5.6 "CREATE TABLE t1(c1 text CONSTRAINT \"1 2\" DEFAULT (1+1))" {} 6.1 "CREATE TABLE t1(c1 text COLLATE nocase)" {} 6.2 "CREATE TABLE t1(c1 text CONSTRAINT 'a x' COLLATE nocase)" {} 7.1 "CREATE TABLE t1(c1 REFERENCES t2)" {} 7.2 "CREATE TABLE t1(c1 CONSTRAINT abc REFERENCES t2)" {} 8.1 { CREATE TABLE t1(c1 PRIMARY KEY NOT NULL UNIQUE CHECK(c1 IS 'ten') DEFAULT 123 REFERENCES t1 ); } {} 8.2 { CREATE TABLE t1(c1 REFERENCES t1 DEFAULT 123 CHECK(c1 IS 'ten') UNIQUE NOT NULL PRIMARY KEY ); } {} } # EVIDENCE-OF: R-17905-31923 -- syntax diagram table-constraint # do_createtable_tests 0.3.1 -repair { drop_all_tables execsql { CREATE TABLE t2(x PRIMARY KEY) } } { 1.1 "CREATE TABLE t1(c1, c2, PRIMARY KEY(c1))" {} 1.2 "CREATE TABLE t1(c1, c2, PRIMARY KEY(c1, c2))" {} 1.3 "CREATE TABLE t1(c1, c2, PRIMARY KEY(c1, c2) ON CONFLICT IGNORE)" {} 2.1 "CREATE TABLE t1(c1, c2, UNIQUE(c1))" {} 2.2 "CREATE TABLE t1(c1, c2, UNIQUE(c1, c2))" {} 2.3 "CREATE TABLE t1(c1, c2, UNIQUE(c1, c2) ON CONFLICT IGNORE)" {} 3.1 "CREATE TABLE t1(c1, c2, CHECK(c1 IS NOT c2))" {} 4.1 "CREATE TABLE t1(c1, c2, FOREIGN KEY(c1) REFERENCES t2)" {} } # EVIDENCE-OF: R-18765-31171 -- syntax diagram column-def # do_createtable_tests 0.4.1 -repair { drop_all_tables } { 1 {CREATE TABLE t1( col1, col2 TEXT, col3 INTEGER UNIQUE, col4 VARCHAR(10, 10) PRIMARY KEY, "name with spaces" REFERENCES t1 ); } {} } # EVIDENCE-OF: R-59573-11075 -- syntax diagram create-table-stmt # do_createtable_tests 0.5.1 -repair { drop_all_tables execsql { CREATE TABLE t2(a, b, c) } } { 1 "CREATE TABLE t1(a, b, c)" {} 2 "CREATE TEMP TABLE t1(a, b, c)" {} 3 "CREATE TEMPORARY TABLE t1(a, b, c)" {} 4 "CREATE TABLE IF NOT EXISTS t1(a, b, c)" {} 5 "CREATE TEMP TABLE IF NOT EXISTS t1(a, b, c)" {} 6 "CREATE TEMPORARY TABLE IF NOT EXISTS t1(a, b, c)" {} 7 "CREATE TABLE main.t1(a, b, c)" {} 8 "CREATE TEMP TABLE temp.t1(a, b, c)" {} 9 "CREATE TEMPORARY TABLE temp.t1(a, b, c)" {} 10 "CREATE TABLE IF NOT EXISTS main.t1(a, b, c)" {} 11 "CREATE TEMP TABLE IF NOT EXISTS temp.t1(a, b, c)" {} 12 "CREATE TEMPORARY TABLE IF NOT EXISTS temp.t1(a, b, c)" {} 13 "CREATE TABLE t1 AS SELECT * FROM t2" {} 14 "CREATE TEMP TABLE t1 AS SELECT c, b, a FROM t2" {} 15 "CREATE TABLE t1 AS SELECT count(*), max(b), min(a) FROM t2" {} } # EVIDENCE-OF: R-32138-02228 -- syntax diagram foreign-key-clause # # 1: Explicit parent-key columns. # 2: Implicit child-key columns. # # 1: MATCH FULL # 2: MATCH PARTIAL # 3: MATCH SIMPLE # 4: MATCH STICK # 5: # # 1: ON DELETE SET NULL # 2: ON DELETE SET DEFAULT # 3: ON DELETE CASCADE # 4: ON DELETE RESTRICT # 5: ON DELETE NO ACTION # 6: # # 1: ON UPDATE SET NULL # 2: ON UPDATE SET DEFAULT # 3: ON UPDATE CASCADE # 4: ON UPDATE RESTRICT # 5: ON UPDATE NO ACTION # 6: # # 1: NOT DEFERRABLE INITIALLY DEFERRED # 2: NOT DEFERRABLE INITIALLY IMMEDIATE # 3: NOT DEFERRABLE # 4: DEFERRABLE INITIALLY DEFERRED # 5: DEFERRABLE INITIALLY IMMEDIATE # 6: DEFERRABLE # 7: # do_createtable_tests 0.6.1 -repair { drop_all_tables execsql { CREATE TABLE t2(x PRIMARY KEY, y) } execsql { CREATE TABLE t3(i, j, UNIQUE(i, j) ) } } { 11146 { CREATE TABLE t1(a REFERENCES t2(x) MATCH FULL ON DELETE SET NULL ON UPDATE RESTRICT DEFERRABLE )} {} 11412 { CREATE TABLE t1(a REFERENCES t2(x) ON DELETE RESTRICT ON UPDATE SET NULL MATCH FULL NOT DEFERRABLE INITIALLY IMMEDIATE )} {} 12135 { CREATE TABLE t1(a REFERENCES t2(x) MATCH PARTIAL ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY IMMEDIATE )} {} 12427 { CREATE TABLE t1(a REFERENCES t2(x) MATCH PARTIAL ON DELETE RESTRICT ON UPDATE SET DEFAULT )} {} 12446 { CREATE TABLE t1(a REFERENCES t2(x) MATCH PARTIAL ON DELETE RESTRICT ON UPDATE RESTRICT DEFERRABLE )} {} 12522 { CREATE TABLE t1(a REFERENCES t2(x) MATCH PARTIAL ON DELETE NO ACTION ON UPDATE SET DEFAULT NOT DEFERRABLE INITIALLY IMMEDIATE )} {} 13133 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE SET NULL ON UPDATE CASCADE NOT DEFERRABLE )} {} 13216 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE SET DEFAULT ON UPDATE SET NULL DEFERRABLE )} {} 13263 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE SET DEFAULT NOT DEFERRABLE )} {} 13421 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE RESTRICT ON UPDATE SET DEFAULT NOT DEFERRABLE INITIALLY DEFERRED )} {} 13432 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE RESTRICT ON UPDATE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE )} {} 13523 { CREATE TABLE t1(a REFERENCES t2(x) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE SET DEFAULT NOT DEFERRABLE )} {} 14336 { CREATE TABLE t1(a REFERENCES t2(x) MATCH STICK ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE )} {} 14611 { CREATE TABLE t1(a REFERENCES t2(x) MATCH STICK ON UPDATE SET NULL NOT DEFERRABLE INITIALLY DEFERRED )} {} 15155 { CREATE TABLE t1(a REFERENCES t2(x) ON DELETE SET NULL ON UPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE )} {} 15453 { CREATE TABLE t1(a REFERENCES t2(x) ON DELETE RESTRICT ON UPDATE NO ACTION NOT DEFERRABLE )} {} 15661 { CREATE TABLE t1(a REFERENCES t2(x) NOT DEFERRABLE INITIALLY DEFERRED )} {} 21115 { CREATE TABLE t1(a REFERENCES t2 MATCH FULL ON DELETE SET NULL ON UPDATE SET NULL DEFERRABLE INITIALLY IMMEDIATE )} {} 21123 { CREATE TABLE t1(a REFERENCES t2 MATCH FULL ON DELETE SET NULL ON UPDATE SET DEFAULT NOT DEFERRABLE )} {} 21217 { CREATE TABLE t1(a REFERENCES t2 MATCH FULL ON DELETE SET DEFAULT ON UPDATE SET NULL )} {} 21362 { CREATE TABLE t1(a REFERENCES t2 MATCH FULL ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE )} {} 22143 { CREATE TABLE t1(a REFERENCES t2 MATCH PARTIAL ON DELETE SET NULL ON UPDATE RESTRICT NOT DEFERRABLE )} {} 22156 { CREATE TABLE t1(a REFERENCES t2 MATCH PARTIAL ON DELETE SET NULL ON UPDATE NO ACTION DEFERRABLE )} {} 22327 { CREATE TABLE t1(a REFERENCES t2 MATCH PARTIAL ON DELETE CASCADE ON UPDATE SET DEFAULT )} {} 22663 { CREATE TABLE t1(a REFERENCES t2 MATCH PARTIAL NOT DEFERRABLE )} {} 23236 { CREATE TABLE t1(a REFERENCES t2 MATCH SIMPLE ON DELETE SET DEFAULT ON UPDATE CASCADE DEFERRABLE )} {} 24155 { CREATE TABLE t1(a REFERENCES t2 MATCH STICK ON DELETE SET NULL ON UPDATE NO ACTION DEFERRABLE INITIALLY IMMEDIATE )} {} 24522 { CREATE TABLE t1(a REFERENCES t2 MATCH STICK ON DELETE NO ACTION ON UPDATE SET DEFAULT NOT DEFERRABLE INITIALLY IMMEDIATE )} {} 24625 { CREATE TABLE t1(a REFERENCES t2 MATCH STICK ON UPDATE SET DEFAULT DEFERRABLE INITIALLY IMMEDIATE )} {} 25454 { CREATE TABLE t1(a REFERENCES t2 ON DELETE RESTRICT ON UPDATE NO ACTION DEFERRABLE INITIALLY DEFERRED )} {} } #------------------------------------------------------------------------- # Test cases e_createtable-1.* - test statements related to table and # database names, the TEMP and TEMPORARY keywords, and the IF NOT EXISTS # clause. # drop_all_tables forcedelete test.db2 test.db3 do_execsql_test e_createtable-1.0 { ATTACH 'test.db2' AS auxa; ATTACH 'test.db3' AS auxb; } {} # EVIDENCE-OF: R-17899-04554 Table names that begin with "sqlite_" are # reserved for internal use. It is an error to attempt to create a table # with a name that starts with "sqlite_". # do_createtable_tests 1.1.1 -error { object name reserved for internal use: %s } { 1 "CREATE TABLE sqlite_abc(a, b, c)" sqlite_abc 2 "CREATE TABLE temp.sqlite_helloworld(x)" sqlite_helloworld 3 {CREATE TABLE auxa."sqlite__"(x, y)} sqlite__ 4 {CREATE TABLE auxb."sqlite_"(z)} sqlite_ 5 {CREATE TABLE "SQLITE_TBL"(z)} SQLITE_TBL } do_createtable_tests 1.1.2 { 1 "CREATE TABLE sqlit_abc(a, b, c)" {} 2 "CREATE TABLE temp.sqlitehelloworld(x)" {} 3 {CREATE TABLE auxa."sqlite"(x, y)} {} 4 {CREATE TABLE auxb."sqlite-"(z)} {} 5 {CREATE TABLE "SQLITE-TBL"(z)} {} } # EVIDENCE-OF: R-10195-31023 If a <database-name> is specified, it # must be either "main", "temp", or the name of an attached database. # # EVIDENCE-OF: R-39822-07822 In this case the new table is created in # the named database. # # Test cases 1.2.* test the first of the two requirements above. The # second is verified by cases 1.3.*. # do_createtable_tests 1.2.1 -error { unknown database %s } { 1 "CREATE TABLE george.t1(a, b)" george 2 "CREATE TABLE _.t1(a, b)" _ } do_createtable_tests 1.2.2 { 1 "CREATE TABLE main.abc(a, b, c)" {} 2 "CREATE TABLE temp.helloworld(x)" {} 3 {CREATE TABLE auxa."t 1"(x, y)} {} 4 {CREATE TABLE auxb.xyz(z)} {} } drop_all_tables do_createtable_tests 1.3 -tclquery { unset -nocomplain X array set X [table_list] list $X(main) $X(temp) $X(auxa) $X(auxb) } { 1 "CREATE TABLE main.abc(a, b, c)" {abc {} {} {}} 2 "CREATE TABLE main.t1(a, b, c)" {{abc t1} {} {} {}} 3 "CREATE TABLE temp.tmp(a, b, c)" {{abc t1} tmp {} {}} 4 "CREATE TABLE auxb.tbl(x, y)" {{abc t1} tmp {} tbl} 5 "CREATE TABLE auxb.t1(k, v)" {{abc t1} tmp {} {t1 tbl}} 6 "CREATE TABLE auxa.next(c, d)" {{abc t1} tmp next {t1 tbl}} } # EVIDENCE-OF: R-18895-27365 If the "TEMP" or "TEMPORARY" keyword occurs # between the "CREATE" and "TABLE" then the new table is created in the # temp database. # drop_all_tables do_createtable_tests 1.4 -tclquery { unset -nocomplain X array set X [table_list] list $X(main) $X(temp) $X(auxa) $X(auxb) } { 1 "CREATE TEMP TABLE t1(a, b)" {{} t1 {} {}} 2 "CREATE TEMPORARY TABLE t2(a, b)" {{} {t1 t2} {} {}} } # EVIDENCE-OF: R-49439-47561 It is an error to specify both a # <database-name> and the TEMP or TEMPORARY keyword, unless the # <database-name> is "temp". # drop_all_tables do_createtable_tests 1.5.1 -error { temporary table name must be unqualified } { 1 "CREATE TEMP TABLE main.t1(a, b)" {} 2 "CREATE TEMPORARY TABLE auxa.t2(a, b)" {} 3 "CREATE TEMP TABLE auxb.t3(a, b)" {} 4 "CREATE TEMPORARY TABLE main.xxx(x)" {} } drop_all_tables do_createtable_tests 1.5.2 -tclquery { unset -nocomplain X array set X [table_list] list $X(main) $X(temp) $X(auxa) $X(auxb) } { 1 "CREATE TEMP TABLE temp.t1(a, b)" {{} t1 {} {}} 2 "CREATE TEMPORARY TABLE temp.t2(a, b)" {{} {t1 t2} {} {}} 3 "CREATE TEMP TABLE TEMP.t3(a, b)" {{} {t1 t2 t3} {} {}} 4 "CREATE TEMPORARY TABLE TEMP.xxx(x)" {{} {t1 t2 t3 xxx} {} {}} } # EVIDENCE-OF: R-00917-09393 If no database name is specified and the # TEMP keyword is not present then the table is created in the main # database. # drop_all_tables do_createtable_tests 1.6 -tclquery { unset -nocomplain X array set X [table_list] list $X(main) $X(temp) $X(auxa) $X(auxb) } { 1 "CREATE TABLE t1(a, b)" {t1 {} {} {}} 2 "CREATE TABLE t2(a, b)" {{t1 t2} {} {} {}} 3 "CREATE TABLE t3(a, b)" {{t1 t2 t3} {} {} {}} 4 "CREATE TABLE xxx(x)" {{t1 t2 t3 xxx} {} {} {}} } drop_all_tables do_execsql_test e_createtable-1.7.0 { CREATE TABLE t1(x, y); CREATE INDEX i1 ON t1(x); CREATE VIEW v1 AS SELECT * FROM t1; CREATE TABLE auxa.tbl1(x, y); CREATE INDEX auxa.idx1 ON tbl1(x); CREATE VIEW auxa.view1 AS SELECT * FROM tbl1; } {} # EVIDENCE-OF: R-01232-54838 It is usually an error to attempt to create # a new table in a database that already contains a table, index or view # of the same name. # # Test cases 1.7.1.* verify that creating a table in a database with a # table/index/view of the same name does fail. 1.7.2.* tests that creating # a table with the same name as a table/index/view in a different database # is Ok. # do_createtable_tests 1.7.1 -error { %s } { 1 "CREATE TABLE t1(a, b)" {{table t1 already exists}} 2 "CREATE TABLE i1(a, b)" {{there is already an index named i1}} 3 "CREATE TABLE v1(a, b)" {{table v1 already exists}} 4 "CREATE TABLE auxa.tbl1(a, b)" {{table tbl1 already exists}} 5 "CREATE TABLE auxa.idx1(a, b)" {{there is already an index named idx1}} 6 "CREATE TABLE auxa.view1(a, b)" {{table view1 already exists}} } do_createtable_tests 1.7.2 { 1 "CREATE TABLE auxa.t1(a, b)" {} 2 "CREATE TABLE auxa.i1(a, b)" {} 3 "CREATE TABLE auxa.v1(a, b)" {} 4 "CREATE TABLE tbl1(a, b)" {} 5 "CREATE TABLE idx1(a, b)" {} 6 "CREATE TABLE view1(a, b)" {} } # EVIDENCE-OF: R-33917-24086 However, if the "IF NOT EXISTS" clause is # specified as part of the CREATE TABLE statement and a table or view of # the same name already exists, the CREATE TABLE command simply has no # effect (and no error message is returned). # drop_all_tables do_execsql_test e_createtable-1.8.0 { CREATE TABLE t1(x, y); CREATE INDEX i1 ON t1(x); CREATE VIEW v1 AS SELECT * FROM t1; CREATE TABLE auxa.tbl1(x, y); CREATE INDEX auxa.idx1 ON tbl1(x); CREATE VIEW auxa.view1 AS SELECT * FROM tbl1; } {} do_createtable_tests 1.8 { 1 "CREATE TABLE IF NOT EXISTS t1(a, b)" {} 2 "CREATE TABLE IF NOT EXISTS auxa.tbl1(a, b)" {} 3 "CREATE TABLE IF NOT EXISTS v1(a, b)" {} 4 "CREATE TABLE IF NOT EXISTS auxa.view1(a, b)" {} } # EVIDENCE-OF: R-16465-40078 An error is still returned if the table # cannot be created because of an existing index, even if the "IF NOT # EXISTS" clause is specified. # do_createtable_tests 1.9 -error { %s } { 1 "CREATE TABLE IF NOT EXISTS i1(a, b)" {{there is already an index named i1}} 2 "CREATE TABLE IF NOT EXISTS auxa.idx1(a, b)" {{there is already an index named idx1}} } # EVIDENCE-OF: R-05513-33819 It is not an error to create a table that # has the same name as an existing trigger. # drop_all_tables do_execsql_test e_createtable-1.10.0 { CREATE TABLE t1(x, y); CREATE TABLE auxb.t2(x, y); CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END; CREATE TRIGGER auxb.tr2 AFTER INSERT ON t2 BEGIN SELECT 1; END; } {} do_createtable_tests 1.10 { 1 "CREATE TABLE tr1(a, b)" {} 2 "CREATE TABLE tr2(a, b)" {} 3 "CREATE TABLE auxb.tr1(a, b)" {} 4 "CREATE TABLE auxb.tr2(a, b)" {} } # EVIDENCE-OF: R-22283-14179 Tables are removed using the DROP TABLE # statement. # drop_all_tables do_execsql_test e_createtable-1.11.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); CREATE TABLE auxa.t3(a, b); CREATE TABLE auxa.t4(a, b); } {} do_execsql_test e_createtable-1.11.1.1 { SELECT * FROM t1; SELECT * FROM t2; SELECT * FROM t3; SELECT * FROM t4; } {} do_execsql_test e_createtable-1.11.1.2 { DROP TABLE t1 } {} do_catchsql_test e_createtable-1.11.1.3 { SELECT * FROM t1 } {1 {no such table: t1}} do_execsql_test e_createtable-1.11.1.4 { DROP TABLE t3 } {} do_catchsql_test e_createtable-1.11.1.5 { SELECT * FROM t3 } {1 {no such table: t3}} do_execsql_test e_createtable-1.11.2.1 { SELECT name FROM sqlite_master; SELECT name FROM auxa.sqlite_master; } {t2 t4} do_execsql_test e_createtable-1.11.2.2 { DROP TABLE t2 } {} do_execsql_test e_createtable-1.11.2.3 { DROP TABLE t4 } {} do_execsql_test e_createtable-1.11.2.4 { SELECT name FROM sqlite_master; SELECT name FROM auxa.sqlite_master; } {} #------------------------------------------------------------------------- # Test cases e_createtable-2.* - test statements related to the CREATE # TABLE AS ... SELECT statement. # # Three Tcl commands: # # select_column_names SQL # The argument must be a SELECT statement. Return a list of the names # of the columns of the result-set that would be returned by executing # the SELECT. # # table_column_names TBL # The argument must be a table name. Return a list of column names, from # left to right, for the table. # # table_column_decltypes TBL # The argument must be a table name. Return a list of column declared # types, from left to right, for the table. # proc sci {select cmd} { set res [list] set STMT [sqlite3_prepare_v2 db $select -1 dummy] for {set i 0} {$i < [sqlite3_column_count $STMT]} {incr i} { lappend res [$cmd $STMT $i] } sqlite3_finalize $STMT set res } proc tci {tbl cmd} { sci "SELECT * FROM $tbl" $cmd } proc select_column_names {sql} { sci $sql sqlite3_column_name } proc table_column_names {tbl} { tci $tbl sqlite3_column_name } proc table_column_decltypes {tbl} { tci $tbl sqlite3_column_decltype } # Create a database schema. This schema is used by tests 2.1.* through 2.3.*. # drop_all_tables do_execsql_test e_createtable-2.0 { CREATE TABLE t1(a, b, c); CREATE TABLE t2(d, e, f); CREATE TABLE t3(g BIGINT, h VARCHAR(10)); CREATE TABLE t4(i BLOB, j ANYOLDATA); CREATE TABLE t5(k FLOAT, l INTEGER); CREATE TABLE t6(m DEFAULT 10, n DEFAULT 5, PRIMARY KEY(m, n)); CREATE TABLE t7(x INTEGER PRIMARY KEY); CREATE TABLE t8(o COLLATE nocase DEFAULT 'abc'); CREATE TABLE t9(p NOT NULL, q DOUBLE CHECK (q!=0), r STRING UNIQUE); } {} # EVIDENCE-OF: R-64828-59568 The table has the same number of columns as # the rows returned by the SELECT statement. The name of each column is # the same as the name of the corresponding column in the result set of # the SELECT statement. # do_createtable_tests 2.1 -tclquery { table_column_names x1 } -repair { catchsql { DROP TABLE x1 } } { 1 "CREATE TABLE x1 AS SELECT * FROM t1" {a b c} 2 "CREATE TABLE x1 AS SELECT c, b, a FROM t1" {c b a} 3 "CREATE TABLE x1 AS SELECT * FROM t1, t2" {a b c d e f} 4 "CREATE TABLE x1 AS SELECT count(*) FROM t1" {count(*)} 5 "CREATE TABLE x1 AS SELECT count(a) AS a, max(b) FROM t1" {a max(b)} } # EVIDENCE-OF: R-37111-22855 The declared type of each column is # determined by the expression affinity of the corresponding expression # in the result set of the SELECT statement, as follows: Expression # Affinity Column Declared Type TEXT "TEXT" NUMERIC "NUM" INTEGER "INT" # REAL "REAL" NONE "" (empty string) # do_createtable_tests 2.2 -tclquery { table_column_decltypes x1 } -repair { catchsql { DROP TABLE x1 } } { 1 "CREATE TABLE x1 AS SELECT a FROM t1" {""} 2 "CREATE TABLE x1 AS SELECT * FROM t3" {INT TEXT} 3 "CREATE TABLE x1 AS SELECT * FROM t4" {"" NUM} 4 "CREATE TABLE x1 AS SELECT * FROM t5" {REAL INT} } # EVIDENCE-OF: R-16667-09772 A table created using CREATE TABLE AS has # no PRIMARY KEY and no constraints of any kind. The default value of # each column is NULL. The default collation sequence for each column of # the new table is BINARY. # # The following tests create tables based on SELECT statements that read # from tables that have primary keys, constraints and explicit default # collation sequences. None of this is transfered to the definition of # the new table as stored in the sqlite_master table. # # Tests 2.3.2.* show that the default value of each column is NULL. # do_createtable_tests 2.3.1 -query { SELECT sql FROM sqlite_master ORDER BY rowid DESC LIMIT 1 } { 1 "CREATE TABLE x1 AS SELECT * FROM t6" {{CREATE TABLE x1(m,n)}} 2 "CREATE TABLE x2 AS SELECT * FROM t7" {{CREATE TABLE x2(x INT)}} 3 "CREATE TABLE x3 AS SELECT * FROM t8" {{CREATE TABLE x3(o)}} 4 "CREATE TABLE x4 AS SELECT * FROM t9" {{CREATE TABLE x4(p,q REAL,r NUM)}} } do_execsql_test e_createtable-2.3.2.1 { INSERT INTO x1 DEFAULT VALUES; INSERT INTO x2 DEFAULT VALUES; INSERT INTO x3 DEFAULT VALUES; INSERT INTO x4 DEFAULT VALUES; } {} db nullvalue null do_execsql_test e_createtable-2.3.2.2 { SELECT * FROM x1 } {null null} do_execsql_test e_createtable-2.3.2.3 { SELECT * FROM x2 } {null} do_execsql_test e_createtable-2.3.2.4 { SELECT * FROM x3 } {null} do_execsql_test e_createtable-2.3.2.5 { SELECT * FROM x4 } {null null null} db nullvalue {} drop_all_tables do_execsql_test e_createtable-2.4.0 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('i', 'one'); INSERT INTO t1 VALUES('ii', 'two'); INSERT INTO t1 VALUES('iii', 'three'); } {} # EVIDENCE-OF: R-24153-28352 Tables created using CREATE TABLE AS are # initially populated with the rows of data returned by the SELECT # statement. # # EVIDENCE-OF: R-08224-30249 Rows are assigned contiguously ascending # rowid values, starting with 1, in the order that they are returned by # the SELECT statement. # # Each test case below is specified as the name of a table to create # using "CREATE TABLE ... AS SELECT ..." and a SELECT statement to use in # creating it. The table is created. # # Test cases 2.4.*.1 check that after it has been created, the data in the # table is the same as the data returned by the SELECT statement executed as # a standalone command, verifying the first testable statement above. # # Test cases 2.4.*.2 check that the rowids were allocated contiguously # as required by the second testable statement above. That the rowids # from the contiguous block were allocated to rows in the order rows are # returned by the SELECT statement is verified by 2.4.*.1. # # EVIDENCE-OF: R-32365-09043 A "CREATE TABLE ... AS SELECT" statement # creates and populates a database table based on the results of a # SELECT statement. # # The above is also considered to be tested by the following. It is # clear that tables are being created and populated by the command in # question. # foreach {tn tbl select} { 1 x1 "SELECT * FROM t1" 2 x2 "SELECT * FROM t1 ORDER BY x DESC" 3 x3 "SELECT * FROM t1 ORDER BY x ASC" } { # Create the table using a "CREATE TABLE ... AS SELECT ..." command. execsql [subst {CREATE TABLE $tbl AS $select}] # Check that the rows inserted into the table, sorted in ascending rowid # order, match those returned by executing the SELECT statement as a # standalone command. do_execsql_test e_createtable-2.4.$tn.1 [subst { SELECT * FROM $tbl ORDER BY rowid; }] [execsql $select] # Check that the rowids in the new table are a contiguous block starting # with rowid 1. Note that this will fail if SELECT statement $select # returns 0 rows (as max(rowid) will be NULL). do_execsql_test e_createtable-2.4.$tn.2 [subst { SELECT min(rowid), count(rowid)==max(rowid) FROM $tbl }] {1 1} } #-------------------------------------------------------------------------- # Test cases for column defintions in CREATE TABLE statements that do not # use a SELECT statement. Not including data constraints. In other words, # tests for the specification of: # # * declared types, # * default values, and # * default collation sequences. # # EVIDENCE-OF: R-27219-49057 Unlike most SQL databases, SQLite does not # restrict the type of data that may be inserted into a column based on # the columns declared type. # # Test this by creating a few tables with varied declared types, then # inserting various different types of values into them. # drop_all_tables do_execsql_test e_createtable-3.1.0 { CREATE TABLE t1(x VARCHAR(10), y INTEGER, z DOUBLE); CREATE TABLE t2(a DATETIME, b STRING, c REAL); CREATE TABLE t3(o, t); } {} # value type -> declared column type # ---------------------------------- # integer -> VARCHAR(10) # string -> INTEGER # blob -> DOUBLE # do_execsql_test e_createtable-3.1.1 { INSERT INTO t1 VALUES(14, 'quite a lengthy string', X'555655'); SELECT * FROM t1; } {14 {quite a lengthy string} UVU} # string -> DATETIME # integer -> STRING # time -> REAL # do_execsql_test e_createtable-3.1.2 { INSERT INTO t2 VALUES('not a datetime', 13, '12:41:59'); SELECT * FROM t2; } {{not a datetime} 13 12:41:59} # EVIDENCE-OF: R-10565-09557 The declared type of a column is used to # determine the affinity of the column only. # # Affinities are tested in more detail elsewhere (see document # datatype3.html). Here, just test that affinity transformations # consistent with the expected affinity of each column (based on # the declared type) appear to take place. # # Affinities of t1 (test cases 3.2.1.*): TEXT, INTEGER, REAL # Affinities of t2 (test cases 3.2.2.*): NUMERIC, NUMERIC, REAL # Affinities of t3 (test cases 3.2.3.*): NONE, NONE # do_execsql_test e_createtable-3.2.0 { DELETE FROM t1; DELETE FROM t2; } {} do_createtable_tests 3.2.1 -query { SELECT quote(x), quote(y), quote(z) FROM t1 ORDER BY rowid DESC LIMIT 1; } { 1 "INSERT INTO t1 VALUES(15, '22.0', '14')" {'15' 22 14.0} 2 "INSERT INTO t1 VALUES(22.0, 22.0, 22.0)" {'22.0' 22 22.0} } do_createtable_tests 3.2.2 -query { SELECT quote(a), quote(b), quote(c) FROM t2 ORDER BY rowid DESC LIMIT 1; } { 1 "INSERT INTO t2 VALUES(15, '22.0', '14')" {15 22 14.0} 2 "INSERT INTO t2 VALUES(22.0, 22.0, 22.0)" {22 22 22.0} } do_createtable_tests 3.2.3 -query { SELECT quote(o), quote(t) FROM t3 ORDER BY rowid DESC LIMIT 1; } { 1 "INSERT INTO t3 VALUES('15', '22.0')" {'15' '22.0'} 2 "INSERT INTO t3 VALUES(15, 22.0)" {15 22.0} } # EVIDENCE-OF: R-42316-09582 If there is no explicit DEFAULT clause # attached to a column definition, then the default value of the column # is NULL. # # None of the columns in table t1 have an explicit DEFAULT clause. # So testing that the default value of all columns in table t1 is # NULL serves to verify the above. # do_createtable_tests 3.2.3 -query { SELECT quote(x), quote(y), quote(z) FROM t1 } -repair { execsql { DELETE FROM t1 } } { 1 "INSERT INTO t1(x, y) VALUES('abc', 'xyz')" {'abc' 'xyz' NULL} 2 "INSERT INTO t1(x, z) VALUES('abc', 'xyz')" {'abc' NULL 'xyz'} 3 "INSERT INTO t1 DEFAULT VALUES" {NULL NULL NULL} } # EVIDENCE-OF: R-62940-43005 An explicit DEFAULT clause may specify that # the default value is NULL, a string constant, a blob constant, a # signed-number, or any constant expression enclosed in parentheses. An # explicit default value may also be one of the special case-independent # keywords CURRENT_TIME, CURRENT_DATE or CURRENT_TIMESTAMP. # do_execsql_test e_createtable-3.3.1 { CREATE TABLE t4( a DEFAULT NULL, b DEFAULT 'string constant', c DEFAULT X'424C4F42', d DEFAULT 1, e DEFAULT -1, f DEFAULT 3.14, g DEFAULT -3.14, h DEFAULT ( substr('abcd', 0, 2) || 'cd' ), i DEFAULT CURRENT_TIME, j DEFAULT CURRENT_DATE, k DEFAULT CURRENT_TIMESTAMP ); } {} # EVIDENCE-OF: R-10288-43169 For the purposes of the DEFAULT clause, an # expression is considered constant provided that it does not contain # any sub-queries or string constants enclosed in double quotes. # do_createtable_tests 3.4.1 -error { default value of column [x] is not constant } { 1 {CREATE TABLE t5(x DEFAULT ( (SELECT 1) ))} {} 2 {CREATE TABLE t5(x DEFAULT ( "abc" ))} {} 3 {CREATE TABLE t5(x DEFAULT ( 1 IN (SELECT 1) ))} {} 4 {CREATE TABLE t5(x DEFAULT ( EXISTS (SELECT 1) ))} {} } do_createtable_tests 3.4.2 -repair { catchsql { DROP TABLE t5 } } { 1 {CREATE TABLE t5(x DEFAULT ( 'abc' ))} {} 2 {CREATE TABLE t5(x DEFAULT ( 1 IN (1, 2, 3) ))} {} } # EVIDENCE-OF: R-18814-23501 Each time a row is inserted into the table # by an INSERT statement that does not provide explicit values for all # table columns the values stored in the new row are determined by their # default values # # Verify this with some assert statements for which all, some and no # columns lack explicit values. # set sqlite_current_time 1000000000 do_createtable_tests 3.5 -query { SELECT quote(a), quote(b), quote(c), quote(d), quote(e), quote(f), quote(g), quote(h), quote(i), quote(j), quote(k) FROM t4 ORDER BY rowid DESC LIMIT 1; } { 1 "INSERT INTO t4 DEFAULT VALUES" { NULL {'string constant'} X'424C4F42' 1 -1 3.14 -3.14 'acd' '01:46:40' '2001-09-09' {'2001-09-09 01:46:40'} } 2 "INSERT INTO t4(a, b, c) VALUES(1, 2, 3)" { 1 2 3 1 -1 3.14 -3.14 'acd' '01:46:40' '2001-09-09' {'2001-09-09 01:46:40'} } 3 "INSERT INTO t4(k, j, i) VALUES(1, 2, 3)" { NULL {'string constant'} X'424C4F42' 1 -1 3.14 -3.14 'acd' 3 2 1 } 4 "INSERT INTO t4(a,b,c,d,e,f,g,h,i,j,k) VALUES(1,2,3,4,5,6,7,8,9,10,11)" { 1 2 3 4 5 6 7 8 9 10 11 } } # EVIDENCE-OF: R-12572-62501 If the default value of the column is a # constant NULL, text, blob or signed-number value, then that value is # used directly in the new row. # do_execsql_test e_createtable-3.6.1 { CREATE TABLE t5( a DEFAULT NULL, b DEFAULT 'text value', c DEFAULT X'424C4F42', d DEFAULT -45678.6, e DEFAULT 394507 ); } {} do_execsql_test e_createtable-3.6.2 { INSERT INTO t5 DEFAULT VALUES; SELECT quote(a), quote(b), quote(c), quote(d), quote(e) FROM t5; } {NULL {'text value'} X'424C4F42' -45678.6 394507} # EVIDENCE-OF: R-60616-50251 If the default value of a column is an # expression in parentheses, then the expression is evaluated once for # each row inserted and the results used in the new row. # # Test case 3.6.4 demonstrates that the expression is evaluated # separately for each row if the INSERT is an "INSERT INTO ... SELECT ..." # command. # set ::nextint 0 proc nextint {} { incr ::nextint } db func nextint nextint do_execsql_test e_createtable-3.7.1 { CREATE TABLE t6(a DEFAULT ( nextint() ), b DEFAULT ( nextint() )); } {} do_execsql_test e_createtable-3.7.2 { INSERT INTO t6 DEFAULT VALUES; SELECT quote(a), quote(b) FROM t6; } {1 2} do_execsql_test e_createtable-3.7.3 { INSERT INTO t6(a) VALUES('X'); SELECT quote(a), quote(b) FROM t6; } {1 2 'X' 3} do_execsql_test e_createtable-3.7.4 { INSERT INTO t6(a) SELECT a FROM t6; SELECT quote(a), quote(b) FROM t6; } {1 2 'X' 3 1 4 'X' 5} # EVIDENCE-OF: R-18683-56219 If the default value of a column is # CURRENT_TIME, CURRENT_DATE or CURRENT_DATETIME, then the value used in # the new row is a text representation of the current UTC date and/or # time. # # This is difficult to test literally without knowing what time the # user will run the tests. Instead, we test that the three cases # above set the value to the current date and/or time according to # the xCurrentTime() method of the VFS. Which is usually the same # as UTC. In this case, however, we instrument it to always return # a time equivalent to "2001-09-09 01:46:40 UTC". # set sqlite_current_time 1000000000 do_execsql_test e_createtable-3.8.1 { CREATE TABLE t7( a DEFAULT CURRENT_TIME, b DEFAULT CURRENT_DATE, c DEFAULT CURRENT_TIMESTAMP ); } {} do_execsql_test e_createtable-3.8.2 { INSERT INTO t7 DEFAULT VALUES; SELECT quote(a), quote(b), quote(c) FROM t7; } {'01:46:40' '2001-09-09' {'2001-09-09 01:46:40'}} # EVIDENCE-OF: R-62327-53843 For CURRENT_TIME, the format of the value # is "HH:MM:SS". # # EVIDENCE-OF: R-03775-43471 For CURRENT_DATE, "YYYY-MM-DD". # # EVIDENCE-OF: R-07677-44926 The format for CURRENT_TIMESTAMP is # "YYYY-MM-DD HH:MM:SS". # # The three above are demonstrated by tests 1, 2 and 3 below. # Respectively. # do_createtable_tests 3.8.3 -query { SELECT a, b, c FROM t7 ORDER BY rowid DESC LIMIT 1; } { 1 "INSERT INTO t7(b, c) VALUES('x', 'y')" {01:46:40 x y} 2 "INSERT INTO t7(c, a) VALUES('x', 'y')" {y 2001-09-09 x} 3 "INSERT INTO t7(a, b) VALUES('x', 'y')" {x y {2001-09-09 01:46:40}} } # EVIDENCE-OF: R-55061-47754 The COLLATE clause specifies the name of a # collating sequence to use as the default collation sequence for the # column. # # EVIDENCE-OF: R-40275-54363 If no COLLATE clause is specified, the # default collation sequence is BINARY. # do_execsql_test e_createtable-3-9.1 { CREATE TABLE t8(a COLLATE nocase, b COLLATE rtrim, c COLLATE binary, d); INSERT INTO t8 VALUES('abc', 'abc', 'abc', 'abc'); INSERT INTO t8 VALUES('abc ', 'abc ', 'abc ', 'abc '); INSERT INTO t8 VALUES('ABC ', 'ABC ', 'ABC ', 'ABC '); INSERT INTO t8 VALUES('ABC', 'ABC', 'ABC', 'ABC'); } {} do_createtable_tests 3.9 { 2 "SELECT a FROM t8 ORDER BY a, rowid" {abc ABC {abc } {ABC }} 3 "SELECT b FROM t8 ORDER BY b, rowid" {{ABC } ABC abc {abc }} 4 "SELECT c FROM t8 ORDER BY c, rowid" {ABC {ABC } abc {abc }} 5 "SELECT d FROM t8 ORDER BY d, rowid" {ABC {ABC } abc {abc }} } # EVIDENCE-OF: R-25473-20557 The number of columns in a table is limited # by the SQLITE_MAX_COLUMN compile-time parameter. # proc columns {n} { set res [list] for {set i 0} {$i < $n} {incr i} { lappend res "c$i" } join $res ", " } do_execsql_test e_createtable-3.10.1 [subst { CREATE TABLE t9([columns $::SQLITE_MAX_COLUMN]); }] {} do_catchsql_test e_createtable-3.10.2 [subst { CREATE TABLE t10([columns [expr $::SQLITE_MAX_COLUMN+1]]); }] {1 {too many columns on t10}} # EVIDENCE-OF: R-27775-64721 Both of these limits can be lowered at # runtime using the sqlite3_limit() C/C++ interface. # # A 30,000 byte blob consumes 30,003 bytes of record space. A record # that contains 3 such blobs consumes (30,000*3)+1 bytes of space. Tests # 3.11.4 and 3.11.5, which verify that SQLITE_MAX_LENGTH may be lowered # at runtime, are based on this calculation. # sqlite3_limit db SQLITE_LIMIT_COLUMN 500 do_execsql_test e_createtable-3.11.1 [subst { CREATE TABLE t10([columns 500]); }] {} do_catchsql_test e_createtable-3.11.2 [subst { CREATE TABLE t11([columns 501]); }] {1 {too many columns on t11}} # Check that it is not possible to raise the column limit above its # default compile time value. # sqlite3_limit db SQLITE_LIMIT_COLUMN [expr $::SQLITE_MAX_COLUMN+2] do_catchsql_test e_createtable-3.11.3 [subst { CREATE TABLE t11([columns [expr $::SQLITE_MAX_COLUMN+1]]); }] {1 {too many columns on t11}} sqlite3_limit db SQLITE_LIMIT_LENGTH 90010 do_execsql_test e_createtable-3.11.4 { CREATE TABLE t12(a, b, c); INSERT INTO t12 VALUES(randomblob(30000),randomblob(30000),randomblob(30000)); } {} do_catchsql_test e_createtable-3.11.5 { INSERT INTO t12 VALUES(randomblob(30001),randomblob(30000),randomblob(30000)); } {1 {string or blob too big}} #------------------------------------------------------------------------- # Tests for statements regarding constraints (PRIMARY KEY, UNIQUE, NOT # NULL and CHECK constraints). # # EVIDENCE-OF: R-52382-54248 Each table in SQLite may have at most one # PRIMARY KEY. # # EVIDENCE-OF: R-18080-47271 If there is more than one PRIMARY KEY # clause in a single CREATE TABLE statement, it is an error. # # To test the two above, show that zero primary keys is Ok, one primary # key is Ok, and two or more primary keys is an error. # drop_all_tables do_createtable_tests 4.1.1 { 1 "CREATE TABLE t1(a, b, c)" {} 2 "CREATE TABLE t2(a PRIMARY KEY, b, c)" {} 3 "CREATE TABLE t3(a, b, c, PRIMARY KEY(a))" {} 4 "CREATE TABLE t4(a, b, c, PRIMARY KEY(c,b,a))" {} } do_createtable_tests 4.1.2 -error { table "t5" has more than one primary key } { 1 "CREATE TABLE t5(a PRIMARY KEY, b PRIMARY KEY, c)" {} 2 "CREATE TABLE t5(a, b PRIMARY KEY, c, PRIMARY KEY(a))" {} 3 "CREATE TABLE t5(a INTEGER PRIMARY KEY, b PRIMARY KEY, c)" {} 4 "CREATE TABLE t5(a INTEGER PRIMARY KEY, b, c, PRIMARY KEY(b, c))" {} 5 "CREATE TABLE t5(a PRIMARY KEY, b, c, PRIMARY KEY(a))" {} 6 "CREATE TABLE t5(a INTEGER PRIMARY KEY, b, c, PRIMARY KEY(a))" {} } proc table_pk {tbl} { set pk [list] db eval "pragma table_info($tbl)" a { if {$a(pk)} { lappend pk $a(name) } } set pk } # EVIDENCE-OF: R-41411-18837 If the keywords PRIMARY KEY are added to a # column definition, then the primary key for the table consists of that # single column. # # The above is tested by 4.2.1.* # # EVIDENCE-OF: R-31775-48204 Or, if a PRIMARY KEY clause is specified as # a table-constraint, then the primary key of the table consists of the # list of columns specified as part of the PRIMARY KEY clause. # # The above is tested by 4.2.2.* # do_createtable_tests 4.2 -repair { catchsql { DROP TABLE t5 } } -tclquery { table_pk t5 } { 1.1 "CREATE TABLE t5(a, b INTEGER PRIMARY KEY, c)" {b} 1.2 "CREATE TABLE t5(a PRIMARY KEY, b, c)" {a} 2.1 "CREATE TABLE t5(a, b, c, PRIMARY KEY(a))" {a} 2.2 "CREATE TABLE t5(a, b, c, PRIMARY KEY(c,b,a))" {a b c} 2.3 "CREATE TABLE t5(a, b INTEGER PRIMARY KEY, c)" {b} } # EVIDENCE-OF: R-33986-09410 Each row in a table with a primary key must # feature a unique combination of values in its primary key columns. # # EVIDENCE-OF: R-39102-06737 If an INSERT or UPDATE statement attempts # to modify the table content so that two or more rows feature identical # primary key values, it is a constraint violation. # drop_all_tables do_execsql_test 4.3.0 { CREATE TABLE t1(x PRIMARY KEY, y); INSERT INTO t1 VALUES(0, 'zero'); INSERT INTO t1 VALUES(45.5, 'one'); INSERT INTO t1 VALUES('brambles', 'two'); INSERT INTO t1 VALUES(X'ABCDEF', 'three'); CREATE TABLE t2(x, y, PRIMARY KEY(x, y)); INSERT INTO t2 VALUES(0, 'zero'); INSERT INTO t2 VALUES(45.5, 'one'); INSERT INTO t2 VALUES('brambles', 'two'); INSERT INTO t2 VALUES(X'ABCDEF', 'three'); } {} do_createtable_tests 4.3.1 -error { %s not unique } { 1 "INSERT INTO t1 VALUES(0, 0)" {"column x is"} 2 "INSERT INTO t1 VALUES(45.5, 'abc')" {"column x is"} 3 "INSERT INTO t1 VALUES(0.0, 'abc')" {"column x is"} 4 "INSERT INTO t1 VALUES('brambles', 'abc')" {"column x is"} 5 "INSERT INTO t1 VALUES(X'ABCDEF', 'abc')" {"column x is"} 6 "INSERT INTO t2 VALUES(0, 'zero')" {"columns x, y are"} 7 "INSERT INTO t2 VALUES(45.5, 'one')" {"columns x, y are"} 8 "INSERT INTO t2 VALUES(0.0, 'zero')" {"columns x, y are"} 9 "INSERT INTO t2 VALUES('brambles', 'two')" {"columns x, y are"} 10 "INSERT INTO t2 VALUES(X'ABCDEF', 'three')" {"columns x, y are"} } do_createtable_tests 4.3.2 { 1 "INSERT INTO t1 VALUES(-1, 0)" {} 2 "INSERT INTO t1 VALUES(45.2, 'abc')" {} 3 "INSERT INTO t1 VALUES(0.01, 'abc')" {} 4 "INSERT INTO t1 VALUES('bramble', 'abc')" {} 5 "INSERT INTO t1 VALUES(X'ABCDEE', 'abc')" {} 6 "INSERT INTO t2 VALUES(0, 0)" {} 7 "INSERT INTO t2 VALUES(45.5, 'abc')" {} 8 "INSERT INTO t2 VALUES(0.0, 'abc')" {} 9 "INSERT INTO t2 VALUES('brambles', 'abc')" {} 10 "INSERT INTO t2 VALUES(X'ABCDEF', 'abc')" {} } do_createtable_tests 4.3.3 -error { %s not unique } { 1 "UPDATE t1 SET x=0 WHERE y='two'" {"column x is"} 2 "UPDATE t1 SET x='brambles' WHERE y='three'" {"column x is"} 3 "UPDATE t1 SET x=45.5 WHERE y='zero'" {"column x is"} 4 "UPDATE t1 SET x=X'ABCDEF' WHERE y='one'" {"column x is"} 5 "UPDATE t1 SET x=0.0 WHERE y='three'" {"column x is"} 6 "UPDATE t2 SET x=0, y='zero' WHERE y='two'" {"columns x, y are"} 7 "UPDATE t2 SET x='brambles', y='two' WHERE y='three'" {"columns x, y are"} 8 "UPDATE t2 SET x=45.5, y='one' WHERE y='zero'" {"columns x, y are"} 9 "UPDATE t2 SET x=X'ABCDEF', y='three' WHERE y='one'" {"columns x, y are"} 10 "UPDATE t2 SET x=0.0, y='zero' WHERE y='three'" {"columns x, y are"} } # EVIDENCE-OF: R-52572-02078 For the purposes of determining the # uniqueness of primary key values, NULL values are considered distinct # from all other values, including other NULLs. # do_createtable_tests 4.4 { 1 "INSERT INTO t1 VALUES(NULL, 0)" {} 2 "INSERT INTO t1 VALUES(NULL, 0)" {} 3 "INSERT INTO t1 VALUES(NULL, 0)" {} 4 "INSERT INTO t2 VALUES(NULL, 'zero')" {} 5 "INSERT INTO t2 VALUES(NULL, 'one')" {} 6 "INSERT INTO t2 VALUES(NULL, 'two')" {} 7 "INSERT INTO t2 VALUES(NULL, 'three')" {} 8 "INSERT INTO t2 VALUES(0, NULL)" {} 9 "INSERT INTO t2 VALUES(45.5, NULL)" {} 10 "INSERT INTO t2 VALUES(0.0, NULL)" {} 11 "INSERT INTO t2 VALUES('brambles', NULL)" {} 12 "INSERT INTO t2 VALUES(X'ABCDEF', NULL)" {} 13 "INSERT INTO t2 VALUES(NULL, NULL)" {} 14 "INSERT INTO t2 VALUES(NULL, NULL)" {} } # EVIDENCE-OF: R-61866-38053 Unless the column is an INTEGER PRIMARY KEY # SQLite allows NULL values in a PRIMARY KEY column. # # If the column is an integer primary key, attempting to insert a NULL # into the column triggers the auto-increment behaviour. Attempting # to use UPDATE to set an ipk column to a NULL value is an error. # do_createtable_tests 4.5.1 { 1 "SELECT count(*) FROM t1 WHERE x IS NULL" 3 2 "SELECT count(*) FROM t2 WHERE x IS NULL" 6 3 "SELECT count(*) FROM t2 WHERE y IS NULL" 7 4 "SELECT count(*) FROM t2 WHERE x IS NULL AND y IS NULL" 2 } do_execsql_test 4.5.2 { CREATE TABLE t3(s, u INTEGER PRIMARY KEY, v); INSERT INTO t3 VALUES(1, NULL, 2); INSERT INTO t3 VALUES('x', NULL, 'y'); SELECT u FROM t3; } {1 2} do_catchsql_test 4.5.3 { INSERT INTO t3 VALUES(2, 5, 3); UPDATE t3 SET u = NULL WHERE s = 2; } {1 {datatype mismatch}} # EVIDENCE-OF: R-00227-21080 A UNIQUE constraint is similar to a PRIMARY # KEY constraint, except that a single table may have any number of # UNIQUE constraints. # drop_all_tables do_createtable_tests 4.6 { 1 "CREATE TABLE t1(a UNIQUE, b UNIQUE)" {} 2 "CREATE TABLE t2(a UNIQUE, b, c, UNIQUE(c, b))" {} 3 "CREATE TABLE t3(a, b, c, UNIQUE(a), UNIQUE(b), UNIQUE(c))" {} 4 "CREATE TABLE t4(a, b, c, UNIQUE(a, b, c))" {} } # EVIDENCE-OF: R-55240-58877 For each UNIQUE constraint on the table, # each row must feature a unique combination of values in the columns # identified by the UNIQUE constraint. # # EVIDENCE-OF: R-47733-51480 If an INSERT or UPDATE statement attempts # to modify the table content so that two or more rows feature identical # values in a set of columns that are subject to a UNIQUE constraint, it # is a constraint violation. # do_execsql_test 4.7.0 { INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(4.3, 5.5); INSERT INTO t1 VALUES('reveal', 'variableness'); INSERT INTO t1 VALUES(X'123456', X'654321'); INSERT INTO t4 VALUES('xyx', 1, 1); INSERT INTO t4 VALUES('xyx', 2, 1); INSERT INTO t4 VALUES('uvw', 1, 1); } do_createtable_tests 4.7.1 -error { %s not unique } { 1 "INSERT INTO t1 VALUES(1, 'one')" {{column a is}} 2 "INSERT INTO t1 VALUES(4.3, 'two')" {{column a is}} 3 "INSERT INTO t1 VALUES('reveal', 'three')" {{column a is}} 4 "INSERT INTO t1 VALUES(X'123456', 'four')" {{column a is}} 5 "UPDATE t1 SET a = 1 WHERE rowid=2" {{column a is}} 6 "UPDATE t1 SET a = 4.3 WHERE rowid=3" {{column a is}} 7 "UPDATE t1 SET a = 'reveal' WHERE rowid=4" {{column a is}} 8 "UPDATE t1 SET a = X'123456' WHERE rowid=1" {{column a is}} 9 "INSERT INTO t4 VALUES('xyx', 1, 1)" {{columns a, b, c are}} 10 "INSERT INTO t4 VALUES('xyx', 2, 1)" {{columns a, b, c are}} 11 "INSERT INTO t4 VALUES('uvw', 1, 1)" {{columns a, b, c are}} 12 "UPDATE t4 SET a='xyx' WHERE rowid=3" {{columns a, b, c are}} 13 "UPDATE t4 SET b=1 WHERE rowid=2" {{columns a, b, c are}} 14 "UPDATE t4 SET a=0, b=0, c=0" {{columns a, b, c are}} } # EVIDENCE-OF: R-21289-11559 As with PRIMARY KEY constraints, for the # purposes of UNIQUE constraints NULL values are considered distinct # from all other values (including other NULLs). # do_createtable_tests 4.8 { 1 "INSERT INTO t1 VALUES(NULL, NULL)" {} 2 "INSERT INTO t1 VALUES(NULL, NULL)" {} 3 "UPDATE t1 SET a = NULL" {} 4 "UPDATE t1 SET b = NULL" {} 5 "INSERT INTO t4 VALUES(NULL, NULL, NULL)" {} 6 "INSERT INTO t4 VALUES(NULL, NULL, NULL)" {} 7 "UPDATE t4 SET a = NULL" {} 8 "UPDATE t4 SET b = NULL" {} 9 "UPDATE t4 SET c = NULL" {} } # EVIDENCE-OF: R-26983-26377 INTEGER PRIMARY KEY columns aside, both # UNIQUE and PRIMARY KEY constraints are implemented by creating an # index in the database (in the same way as a "CREATE UNIQUE INDEX" # statement would). do_createtable_tests 4.9 -repair drop_all_tables -query { SELECT count(*) FROM sqlite_master WHERE type='index' } { 1 "CREATE TABLE t1(a TEXT PRIMARY KEY, b)" 1 2 "CREATE TABLE t1(a INTEGER PRIMARY KEY, b)" 0 3 "CREATE TABLE t1(a TEXT UNIQUE, b)" 1 4 "CREATE TABLE t1(a PRIMARY KEY, b TEXT UNIQUE)" 2 5 "CREATE TABLE t1(a PRIMARY KEY, b, c, UNIQUE(c, b))" 2 } # EVIDENCE-OF: R-02252-33116 Such an index is used like any other index # in the database to optimize queries. # do_execsql_test 4.10.0 { CREATE TABLE t1(a, b PRIMARY KEY); CREATE TABLE t2(a, b, c, UNIQUE(b, c)); } do_createtable_tests 4.10 { 1 "EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 5" {0 0 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (b=?) (~1 rows)}} 2 "EXPLAIN QUERY PLAN SELECT * FROM t2 ORDER BY b, c" {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1 (~1000000 rows)}} 3 "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b=10 AND c>10" {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?) (~3 rows)}} } # EVIDENCE-OF: R-45493-35653 A CHECK constraint may be attached to a # column definition or specified as a table constraint. In practice it # makes no difference. # # All the tests that deal with CHECK constraints below (4.11.* and # 4.12.*) are run once for a table with the check constraint attached # to a column definition, and once with a table where the check # condition is specified as a table constraint. # # EVIDENCE-OF: R-55435-14303 Each time a new row is inserted into the # table or an existing row is updated, the expression associated with # each CHECK constraint is evaluated and cast to a NUMERIC value in the # same way as a CAST expression. If the result is zero (integer value 0 # or real value 0.0), then a constraint violation has occurred. # drop_all_tables do_execsql_test 4.11 { CREATE TABLE x1(a TEXT, b INTEGER CHECK( b>0 )); CREATE TABLE t1(a TEXT, b INTEGER, CHECK( b>0 )); INSERT INTO x1 VALUES('x', 'xx'); INSERT INTO x1 VALUES('y', 'yy'); INSERT INTO t1 SELECT * FROM x1; CREATE TABLE x2(a CHECK( a||b ), b); CREATE TABLE t2(a, b, CHECK( a||b )); INSERT INTO x2 VALUES(1, 'xx'); INSERT INTO x2 VALUES(1, 'yy'); INSERT INTO t2 SELECT * FROM x2; } do_createtable_tests 4.11 -error {constraint failed} { 1a "INSERT INTO x1 VALUES('one', 0)" {} 1b "INSERT INTO t1 VALUES('one', -4.0)" {} 2a "INSERT INTO x2 VALUES('abc', 1)" {} 2b "INSERT INTO t2 VALUES('abc', 1)" {} 3a "INSERT INTO x2 VALUES(0, 'abc')" {} 3b "INSERT INTO t2 VALUES(0, 'abc')" {} 4a "UPDATE t1 SET b=-1 WHERE rowid=1" {} 4b "UPDATE x1 SET b=-1 WHERE rowid=1" {} 4a "UPDATE x2 SET a='' WHERE rowid=1" {} 4b "UPDATE t2 SET a='' WHERE rowid=1" {} } # EVIDENCE-OF: R-34109-39108 If the CHECK expression evaluates to NULL, # or any other non-zero value, it is not a constraint violation. # do_createtable_tests 4.12 { 1a "INSERT INTO x1 VALUES('one', NULL)" {} 1b "INSERT INTO t1 VALUES('one', NULL)" {} 2a "INSERT INTO x1 VALUES('one', 2)" {} 2b "INSERT INTO t1 VALUES('one', 2)" {} 3a "INSERT INTO x2 VALUES(1, 'abc')" {} 3b "INSERT INTO t2 VALUES(1, 'abc')" {} } # EVIDENCE-OF: R-02060-64547 A NOT NULL constraint may only be attached # to a column definition, not specified as a table constraint. # drop_all_tables do_createtable_tests 4.13.1 { 1 "CREATE TABLE t1(a NOT NULL, b)" {} 2 "CREATE TABLE t2(a PRIMARY KEY NOT NULL, b)" {} 3 "CREATE TABLE t3(a NOT NULL, b NOT NULL, c NOT NULL UNIQUE)" {} } do_createtable_tests 4.13.2 -error { near "NOT": syntax error } { 1 "CREATE TABLE t4(a, b, NOT NULL(a))" {} 2 "CREATE TABLE t4(a PRIMARY KEY, b, NOT NULL(a))" {} 3 "CREATE TABLE t4(a, b, c UNIQUE, NOT NULL(a, b, c))" {} } # EVIDENCE-OF: R-31795-57643 a NOT NULL constraint dictates that the # associated column may not contain a NULL value. Attempting to set the # column value to NULL when inserting a new row or updating an existing # one causes a constraint violation. # # These tests use the tables created by 4.13. # do_execsql_test 4.14.0 { INSERT INTO t1 VALUES('x', 'y'); INSERT INTO t1 VALUES('z', NULL); INSERT INTO t2 VALUES('x', 'y'); INSERT INTO t2 VALUES('z', NULL); INSERT INTO t3 VALUES('x', 'y', 'z'); INSERT INTO t3 VALUES(1, 2, 3); } do_createtable_tests 4.14 -error { %s may not be NULL } { 1 "INSERT INTO t1 VALUES(NULL, 'a')" {t1.a} 2 "INSERT INTO t2 VALUES(NULL, 'b')" {t2.a} 3 "INSERT INTO t3 VALUES('c', 'd', NULL)" {t3.c} 4 "INSERT INTO t3 VALUES('e', NULL, 'f')" {t3.b} 5 "INSERT INTO t3 VALUES(NULL, 'g', 'h')" {t3.a} } # EVIDENCE-OF: R-42511-39459 PRIMARY KEY, UNIQUE and NOT NULL # constraints may be explicitly assigned a default conflict resolution # algorithm by including a conflict-clause in their definitions. # # Conflict clauses: ABORT, ROLLBACK, IGNORE, FAIL, REPLACE # # Test cases 4.15.*, 4.16.* and 4.17.* focus on PRIMARY KEY, NOT NULL # and UNIQUE constraints, respectively. # drop_all_tables do_execsql_test 4.15.0 { CREATE TABLE t1_ab(a PRIMARY KEY ON CONFLICT ABORT, b); CREATE TABLE t1_ro(a PRIMARY KEY ON CONFLICT ROLLBACK, b); CREATE TABLE t1_ig(a PRIMARY KEY ON CONFLICT IGNORE, b); CREATE TABLE t1_fa(a PRIMARY KEY ON CONFLICT FAIL, b); CREATE TABLE t1_re(a PRIMARY KEY ON CONFLICT REPLACE, b); CREATE TABLE t1_xx(a PRIMARY KEY, b); INSERT INTO t1_ab VALUES(1, 'one'); INSERT INTO t1_ab VALUES(2, 'two'); INSERT INTO t1_ro SELECT * FROM t1_ab; INSERT INTO t1_ig SELECT * FROM t1_ab; INSERT INTO t1_fa SELECT * FROM t1_ab; INSERT INTO t1_re SELECT * FROM t1_ab; INSERT INTO t1_xx SELECT * FROM t1_ab; CREATE TABLE t2_ab(a, b NOT NULL ON CONFLICT ABORT); CREATE TABLE t2_ro(a, b NOT NULL ON CONFLICT ROLLBACK); CREATE TABLE t2_ig(a, b NOT NULL ON CONFLICT IGNORE); CREATE TABLE t2_fa(a, b NOT NULL ON CONFLICT FAIL); CREATE TABLE t2_re(a, b NOT NULL ON CONFLICT REPLACE); CREATE TABLE t2_xx(a, b NOT NULL); INSERT INTO t2_ab VALUES(1, 'one'); INSERT INTO t2_ab VALUES(2, 'two'); INSERT INTO t2_ro SELECT * FROM t2_ab; INSERT INTO t2_ig SELECT * FROM t2_ab; INSERT INTO t2_fa SELECT * FROM t2_ab; INSERT INTO t2_re SELECT * FROM t2_ab; INSERT INTO t2_xx SELECT * FROM t2_ab; CREATE TABLE t3_ab(a, b, UNIQUE(a, b) ON CONFLICT ABORT); CREATE TABLE t3_ro(a, b, UNIQUE(a, b) ON CONFLICT ROLLBACK); CREATE TABLE t3_ig(a, b, UNIQUE(a, b) ON CONFLICT IGNORE); CREATE TABLE t3_fa(a, b, UNIQUE(a, b) ON CONFLICT FAIL); CREATE TABLE t3_re(a, b, UNIQUE(a, b) ON CONFLICT REPLACE); CREATE TABLE t3_xx(a, b, UNIQUE(a, b)); INSERT INTO t3_ab VALUES(1, 'one'); INSERT INTO t3_ab VALUES(2, 'two'); INSERT INTO t3_ro SELECT * FROM t3_ab; INSERT INTO t3_ig SELECT * FROM t3_ab; INSERT INTO t3_fa SELECT * FROM t3_ab; INSERT INTO t3_re SELECT * FROM t3_ab; INSERT INTO t3_xx SELECT * FROM t3_ab; } foreach {tn tbl res ac data} { 1 t1_ab {1 {column a is not unique}} 0 {1 one 2 two 3 three} 2 t1_ro {1 {column a is not unique}} 1 {1 one 2 two} 3 t1_fa {1 {column a is not unique}} 0 {1 one 2 two 3 three 4 string} 4 t1_ig {0 {}} 0 {1 one 2 two 3 three 4 string 6 string} 5 t1_re {0 {}} 0 {1 one 2 two 4 string 3 string 6 string} 6 t1_xx {1 {column a is not unique}} 0 {1 one 2 two 3 three} } { catchsql COMMIT do_execsql_test 4.15.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')" do_catchsql_test 4.15.$tn.2 " INSERT INTO $tbl SELECT ((a%2)*a+3), 'string' FROM $tbl; " $res do_test e_createtable-4.15.$tn.3 { sqlite3_get_autocommit db } $ac do_execsql_test 4.15.$tn.4 "SELECT * FROM $tbl" $data } foreach {tn tbl res ac data} { 1 t2_ab {1 {t2_ab.b may not be NULL}} 0 {1 one 2 two 3 three} 2 t2_ro {1 {t2_ro.b may not be NULL}} 1 {1 one 2 two} 3 t2_fa {1 {t2_fa.b may not be NULL}} 0 {1 one 2 two 3 three 4 xx} 4 t2_ig {0 {}} 0 {1 one 2 two 3 three 4 xx 6 xx} 5 t2_re {1 {t2_re.b may not be NULL}} 0 {1 one 2 two 3 three} 6 t2_xx {1 {t2_xx.b may not be NULL}} 0 {1 one 2 two 3 three} } { catchsql COMMIT do_execsql_test 4.16.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')" do_catchsql_test 4.16.$tn.2 " INSERT INTO $tbl SELECT a+3, CASE a WHEN 2 THEN NULL ELSE 'xx' END FROM $tbl " $res do_test e_createtable-4.16.$tn.3 { sqlite3_get_autocommit db } $ac do_execsql_test 4.16.$tn.4 "SELECT * FROM $tbl" $data } foreach {tn tbl res ac data} { 1 t3_ab {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three} 2 t3_ro {1 {columns a, b are not unique}} 1 {1 one 2 two} 3 t3_fa {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three 4 three} 4 t3_ig {0 {}} 0 {1 one 2 two 3 three 4 three 6 three} 5 t3_re {0 {}} 0 {1 one 2 two 4 three 3 three 6 three} 6 t3_xx {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three} } { catchsql COMMIT do_execsql_test 4.17.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')" do_catchsql_test 4.17.$tn.2 " INSERT INTO $tbl SELECT ((a%2)*a+3), 'three' FROM $tbl " $res do_test e_createtable-4.17.$tn.3 { sqlite3_get_autocommit db } $ac do_execsql_test 4.17.$tn.4 "SELECT * FROM $tbl" $data } catchsql COMMIT # EVIDENCE-OF: R-12645-39772 Or, if a constraint definition does not # include a conflict-clause or it is a CHECK constraint, the default # conflict resolution algorithm is ABORT. # # The first half of the above is tested along with explicit ON # CONFLICT clauses above (specifically, the tests involving t1_xx, t2_xx # and t3_xx). The following just tests that the default conflict # handling for CHECK constraints is ABORT. # do_execsql_test 4.18.1 { CREATE TABLE t4(a, b CHECK (b!=10)); INSERT INTO t4 VALUES(1, 2); INSERT INTO t4 VALUES(3, 4); } do_execsql_test 4.18.2 { BEGIN; INSERT INTO t4 VALUES(5, 6) } do_catchsql_test 4.18.3 { INSERT INTO t4 SELECT a+4, b+4 FROM t4 } {1 {constraint failed}} do_test e_createtable-4.18.4 { sqlite3_get_autocommit db } 0 do_execsql_test 4.18.5 { SELECT * FROM t4 } {1 2 3 4 5 6} # EVIDENCE-OF: R-19114-56113 Different constraints within the same table # may have different default conflict resolution algorithms. # do_execsql_test 4.19.0 { CREATE TABLE t5(a NOT NULL ON CONFLICT IGNORE, b NOT NULL ON CONFLICT ABORT); } do_catchsql_test 4.19.1 { INSERT INTO t5 VALUES(NULL, 'not null') } {0 {}} do_execsql_test 4.19.2 { SELECT * FROM t5 } {} do_catchsql_test 4.19.3 { INSERT INTO t5 VALUES('not null', NULL) } \ {1 {t5.b may not be NULL}} do_execsql_test 4.19.4 { SELECT * FROM t5 } {} #------------------------------------------------------------------------ # Tests for INTEGER PRIMARY KEY and rowid related statements. # # EVIDENCE-OF: R-52584-04009 The rowid value can be accessed using one # of the special case-independent names "rowid", "oid", or "_rowid_" in # place of a column name. # drop_all_tables do_execsql_test 5.1.0 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES('one', 'first'); INSERT INTO t1 VALUES('two', 'second'); INSERT INTO t1 VALUES('three', 'third'); } do_createtable_tests 5.1 { 1 "SELECT rowid FROM t1" {1 2 3} 2 "SELECT oid FROM t1" {1 2 3} 3 "SELECT _rowid_ FROM t1" {1 2 3} 4 "SELECT ROWID FROM t1" {1 2 3} 5 "SELECT OID FROM t1" {1 2 3} 6 "SELECT _ROWID_ FROM t1" {1 2 3} 7 "SELECT RoWiD FROM t1" {1 2 3} 8 "SELECT OiD FROM t1" {1 2 3} 9 "SELECT _RoWiD_ FROM t1" {1 2 3} } # EVIDENCE-OF: R-26501-17306 If a table contains a user defined column # named "rowid", "oid" or "_rowid_", then that name always refers the # explicitly declared column and cannot be used to retrieve the integer # rowid value. # do_execsql_test 5.2.0 { CREATE TABLE t2(oid, b); CREATE TABLE t3(a, _rowid_); CREATE TABLE t4(a, b, rowid); INSERT INTO t2 VALUES('one', 'two'); INSERT INTO t2 VALUES('three', 'four'); INSERT INTO t3 VALUES('five', 'six'); INSERT INTO t3 VALUES('seven', 'eight'); INSERT INTO t4 VALUES('nine', 'ten', 'eleven'); INSERT INTO t4 VALUES('twelve', 'thirteen', 'fourteen'); } do_createtable_tests 5.2 { 1 "SELECT oid, rowid, _rowid_ FROM t2" {one 1 1 three 2 2} 2 "SELECT oid, rowid, _rowid_ FROM t3" {1 1 six 2 2 eight} 3 "SELECT oid, rowid, _rowid_ FROM t4" {1 eleven 1 2 fourteen 2} } # Argument $tbl is the name of a table in the database. Argument $col is # the name of one of the tables columns. Return 1 if $col is an alias for # the rowid, or 0 otherwise. # proc is_integer_primary_key {tbl col} { lindex [db eval [subst { DELETE FROM $tbl; INSERT INTO $tbl ($col) VALUES(0); SELECT (rowid==$col) FROM $tbl; DELETE FROM $tbl; }]] 0 } # EVIDENCE-OF: R-53738-31673 With one exception, if a table has a # primary key that consists of a single column, and the declared type of # that column is "INTEGER" in any mixture of upper and lower case, then # the column becomes an alias for the rowid. # # EVIDENCE-OF: R-45951-08347 if the declaration of a column with # declared type "INTEGER" includes an "PRIMARY KEY DESC" clause, it does # not become an alias for the rowid and is not classified as an integer # primary key. # do_createtable_tests 5.3 -tclquery { is_integer_primary_key t5 pk } -repair { catchsql { DROP TABLE t5 } } { 1 "CREATE TABLE t5(pk integer primary key)" 1 2 "CREATE TABLE t5(pk integer, primary key(pk))" 1 3 "CREATE TABLE t5(pk integer, v integer, primary key(pk))" 1 4 "CREATE TABLE t5(pk integer, v integer, primary key(pk, v))" 0 5 "CREATE TABLE t5(pk int, v integer, primary key(pk, v))" 0 6 "CREATE TABLE t5(pk int, v integer, primary key(pk))" 0 7 "CREATE TABLE t5(pk int primary key, v integer)" 0 8 "CREATE TABLE t5(pk inTEger primary key)" 1 9 "CREATE TABLE t5(pk inteGEr, primary key(pk))" 1 10 "CREATE TABLE t5(pk INTEGER, v integer, primary key(pk))" 1 } # EVIDENCE-OF: R-41444-49665 Other integer type names like "INT" or # "BIGINT" or "SHORT INTEGER" or "UNSIGNED INTEGER" causes the primary # key column to behave as an ordinary table column with integer affinity # and a unique index, not as an alias for the rowid. # do_execsql_test 5.4.1 { CREATE TABLE t6(pk INT primary key); CREATE TABLE t7(pk BIGINT primary key); CREATE TABLE t8(pk SHORT INTEGER primary key); CREATE TABLE t9(pk UNSIGNED INTEGER primary key); } do_test e_createtable-5.4.2.1 { is_integer_primary_key t6 pk } 0 do_test e_createtable-5.4.2.2 { is_integer_primary_key t7 pk } 0 do_test e_createtable-5.4.2.3 { is_integer_primary_key t8 pk } 0 do_test e_createtable-5.4.2.4 { is_integer_primary_key t9 pk } 0 do_execsql_test 5.4.3 { INSERT INTO t6 VALUES('2.0'); INSERT INTO t7 VALUES('2.0'); INSERT INTO t8 VALUES('2.0'); INSERT INTO t9 VALUES('2.0'); SELECT typeof(pk), pk FROM t6; SELECT typeof(pk), pk FROM t7; SELECT typeof(pk), pk FROM t8; SELECT typeof(pk), pk FROM t9; } {integer 2 integer 2 integer 2 integer 2} do_catchsql_test 5.4.4.1 { INSERT INTO t6 VALUES(2) } {1 {column pk is not unique}} do_catchsql_test 5.4.4.2 { INSERT INTO t7 VALUES(2) } {1 {column pk is not unique}} do_catchsql_test 5.4.4.3 { INSERT INTO t8 VALUES(2) } {1 {column pk is not unique}} do_catchsql_test 5.4.4.4 { INSERT INTO t9 VALUES(2) } {1 {column pk is not unique}} # EVIDENCE-OF: R-56094-57830 the following three table declarations all # cause the column "x" to be an alias for the rowid (an integer primary # key): CREATE TABLE t(x INTEGER PRIMARY KEY ASC, y, z); CREATE TABLE # t(x INTEGER, y, z, PRIMARY KEY(x ASC)); CREATE TABLE t(x INTEGER, y, # z, PRIMARY KEY(x DESC)); # # EVIDENCE-OF: R-20149-25884 the following declaration does not result # in "x" being an alias for the rowid: CREATE TABLE t(x INTEGER PRIMARY # KEY DESC, y, z); # do_createtable_tests 5 -tclquery { is_integer_primary_key t x } -repair { catchsql { DROP TABLE t } } { 5.1 "CREATE TABLE t(x INTEGER PRIMARY KEY ASC, y, z)" 1 5.2 "CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x ASC))" 1 5.3 "CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x DESC))" 1 6.1 "CREATE TABLE t(x INTEGER PRIMARY KEY DESC, y, z)" 0 } # EVIDENCE-OF: R-03733-29734 Rowid values may be modified using an # UPDATE statement in the same way as any other column value can, either # using one of the built-in aliases ("rowid", "oid" or "_rowid_") or by # using an alias created by an integer primary key. # do_execsql_test 5.7.0 { CREATE TABLE t10(a, b); INSERT INTO t10 VALUES('ten', 10); CREATE TABLE t11(a, b INTEGER PRIMARY KEY); INSERT INTO t11 VALUES('ten', 10); } do_createtable_tests 5.7.1 -query { SELECT rowid, _rowid_, oid FROM t10; } { 1 "UPDATE t10 SET rowid = 5" {5 5 5} 2 "UPDATE t10 SET _rowid_ = 6" {6 6 6} 3 "UPDATE t10 SET oid = 7" {7 7 7} } do_createtable_tests 5.7.2 -query { SELECT rowid, _rowid_, oid, b FROM t11; } { 1 "UPDATE t11 SET rowid = 5" {5 5 5 5} 2 "UPDATE t11 SET _rowid_ = 6" {6 6 6 6} 3 "UPDATE t11 SET oid = 7" {7 7 7 7} 4 "UPDATE t11 SET b = 8" {8 8 8 8} } # EVIDENCE-OF: R-58706-14229 Similarly, an INSERT statement may provide # a value to use as the rowid for each row inserted. # do_createtable_tests 5.8.1 -query { SELECT rowid, _rowid_, oid FROM t10; } -repair { execsql { DELETE FROM t10 } } { 1 "INSERT INTO t10(oid) VALUES(15)" {15 15 15} 2 "INSERT INTO t10(rowid) VALUES(16)" {16 16 16} 3 "INSERT INTO t10(_rowid_) VALUES(17)" {17 17 17} 4 "INSERT INTO t10(a, b, oid) VALUES(1,2,3)" {3 3 3} } do_createtable_tests 5.8.2 -query { SELECT rowid, _rowid_, oid, b FROM t11; } -repair { execsql { DELETE FROM t11 } } { 1 "INSERT INTO t11(oid) VALUES(15)" {15 15 15 15} 2 "INSERT INTO t11(rowid) VALUES(16)" {16 16 16 16} 3 "INSERT INTO t11(_rowid_) VALUES(17)" {17 17 17 17} 4 "INSERT INTO t11(a, b) VALUES(1,2)" {2 2 2 2} } # EVIDENCE-OF: R-32326-44592 Unlike normal SQLite columns, an integer # primary key or rowid column must contain integer values. Integer # primary key or rowid columns are not able to hold floating point # values, strings, BLOBs, or NULLs. # # This is considered by the tests for the following 3 statements, # which show that: # # 1. Attempts to UPDATE a rowid column to a non-integer value fail, # 2. Attempts to INSERT a real, string or blob value into a rowid # column fail, and # 3. Attempting to INSERT a NULL value into a rowid column causes the # system to automatically select an integer value to use. # # EVIDENCE-OF: R-64224-62578 If an UPDATE statement attempts to set an # integer primary key or rowid column to a NULL or blob value, or to a # string or real value that cannot be losslessly converted to an # integer, a "datatype mismatch" error occurs and the statement is # aborted. # drop_all_tables do_execsql_test 5.9.0 { CREATE TABLE t12(x INTEGER PRIMARY KEY, y); INSERT INTO t12 VALUES(5, 'five'); } do_createtable_tests 5.9.1 -query { SELECT typeof(x), x FROM t12 } { 1 "UPDATE t12 SET x = 4" {integer 4} 2 "UPDATE t12 SET x = 10.0" {integer 10} 3 "UPDATE t12 SET x = '12.0'" {integer 12} 4 "UPDATE t12 SET x = '-15.0'" {integer -15} } do_createtable_tests 5.9.2 -error { datatype mismatch } { 1 "UPDATE t12 SET x = 4.1" {} 2 "UPDATE t12 SET x = 'hello'" {} 3 "UPDATE t12 SET x = NULL" {} 4 "UPDATE t12 SET x = X'ABCD'" {} 5 "UPDATE t12 SET x = X'3900'" {} 6 "UPDATE t12 SET x = X'39'" {} } # EVIDENCE-OF: R-05734-13629 If an INSERT statement attempts to insert a # blob value, or a string or real value that cannot be losslessly # converted to an integer into an integer primary key or rowid column, a # "datatype mismatch" error occurs and the statement is aborted. # do_execsql_test 5.10.0 { DELETE FROM t12 } do_createtable_tests 5.10.1 -error { datatype mismatch } { 1 "INSERT INTO t12(x) VALUES(4.1)" {} 2 "INSERT INTO t12(x) VALUES('hello')" {} 3 "INSERT INTO t12(x) VALUES(X'ABCD')" {} 4 "INSERT INTO t12(x) VALUES(X'3900')" {} 5 "INSERT INTO t12(x) VALUES(X'39')" {} } do_createtable_tests 5.10.2 -query { SELECT typeof(x), x FROM t12 } -repair { execsql { DELETE FROM t12 } } { 1 "INSERT INTO t12(x) VALUES(4)" {integer 4} 2 "INSERT INTO t12(x) VALUES(10.0)" {integer 10} 3 "INSERT INTO t12(x) VALUES('12.0')" {integer 12} 4 "INSERT INTO t12(x) VALUES('4e3')" {integer 4000} 5 "INSERT INTO t12(x) VALUES('-14.0')" {integer -14} } # EVIDENCE-OF: R-07986-46024 If an INSERT statement attempts to insert a # NULL value into a rowid or integer primary key column, the system # chooses an integer value to use as the rowid automatically. # do_execsql_test 5.11.0 { DELETE FROM t12 } do_createtable_tests 5.11 -query { SELECT typeof(x), x FROM t12 WHERE y IS (SELECT max(y) FROM t12) } { 1 "INSERT INTO t12 DEFAULT VALUES" {integer 1} 2 "INSERT INTO t12(y) VALUES(5)" {integer 2} 3 "INSERT INTO t12(x,y) VALUES(NULL, 10)" {integer 3} 4 "INSERT INTO t12(x,y) SELECT NULL, 15 FROM t12" {integer 4 integer 5 integer 6} 5 "INSERT INTO t12(y) SELECT 20 FROM t12 LIMIT 3" {integer 7 integer 8 integer 9} } finish_test |
Added test/e_delete.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 458 459 460 461 462 463 464 465 466 467 468 469 470 | # 2010 September 21 # # 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 to verify that the "testable statements" in # the lang_delete.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl proc do_delete_tests {args} { uplevel do_select_tests $args } do_execsql_test e_delete-0.0 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a); } {} # EVIDENCE-OF: R-24177-52883 -- syntax diagram delete-stmt # # EVIDENCE-OF: R-12802-60464 -- syntax diagram qualified-table-name # do_delete_tests e_delete-0.1 { 1 "DELETE FROM t1" {} 2 "DELETE FROM t1 INDEXED BY i1" {} 3 "DELETE FROM t1 NOT INDEXED" {} 4 "DELETE FROM main.t1" {} 5 "DELETE FROM main.t1 INDEXED BY i1" {} 6 "DELETE FROM main.t1 NOT INDEXED" {} 7 "DELETE FROM t1 WHERE a>2" {} 8 "DELETE FROM t1 INDEXED BY i1 WHERE a>2" {} 9 "DELETE FROM t1 NOT INDEXED WHERE a>2" {} 10 "DELETE FROM main.t1 WHERE a>2" {} 11 "DELETE FROM main.t1 INDEXED BY i1 WHERE a>2" {} 12 "DELETE FROM main.t1 NOT INDEXED WHERE a>2" {} } # EVIDENCE-OF: R-20205-17349 If the WHERE clause is not present, all # records in the table are deleted. # drop_all_tables do_test e_delete-1.0 { db transaction { foreach t {t1 t2 t3 t4 t5 t6} { execsql [string map [list %T% $t] { CREATE TABLE %T%(x, y); INSERT INTO %T% VALUES(1, 'one'); INSERT INTO %T% VALUES(2, 'two'); INSERT INTO %T% VALUES(3, 'three'); INSERT INTO %T% VALUES(4, 'four'); INSERT INTO %T% VALUES(5, 'five'); }] } } } {} do_delete_tests e_delete-1.1 { 1 "DELETE FROM t1 ; SELECT * FROM t1" {} 2 "DELETE FROM main.t2 ; SELECT * FROM t2" {} } # EVIDENCE-OF: R-30203-16177 If a WHERE clause is supplied, then only # those rows for which the result of evaluating the WHERE clause as a # boolean expression is true are deleted. # do_delete_tests e_delete-1.2 { 1 "DELETE FROM t3 WHERE 1 ; SELECT x FROM t3" {} 2 "DELETE FROM main.t4 WHERE 0 ; SELECT x FROM t4" {1 2 3 4 5} 3 "DELETE FROM t4 WHERE 0.0 ; SELECT x FROM t4" {1 2 3 4 5} 4 "DELETE FROM t4 WHERE NULL ; SELECT x FROM t4" {1 2 3 4 5} 5 "DELETE FROM t4 WHERE y!='two'; SELECT x FROM t4" {2} 6 "DELETE FROM t4 WHERE y='two' ; SELECT x FROM t4" {} 7 "DELETE FROM t5 WHERE x=(SELECT max(x) FROM t5);SELECT x FROM t5" {1 2 3 4} 8 "DELETE FROM t5 WHERE (SELECT max(x) FROM t4) ;SELECT x FROM t5" {1 2 3 4} 9 "DELETE FROM t5 WHERE (SELECT max(x) FROM t6) ;SELECT x FROM t5" {} 10 "DELETE FROM t6 WHERE y>'seven' ; SELECT y FROM t6" {one four five} } #------------------------------------------------------------------------- # Tests for restrictions on DELETE statements that appear within trigger # programs. # forcedelete test.db2 forcedelete test.db3 do_execsql_test e_delete-2.0 { ATTACH 'test.db2' AS aux; ATTACH 'test.db3' AS aux2; CREATE TABLE temp.t7(a, b); INSERT INTO temp.t7 VALUES(1, 2); CREATE TABLE main.t7(a, b); INSERT INTO main.t7 VALUES(3, 4); CREATE TABLE aux.t7(a, b); INSERT INTO aux.t7 VALUES(5, 6); CREATE TABLE aux2.t7(a, b); INSERT INTO aux2.t7 VALUES(7, 8); CREATE TABLE main.t8(a, b); INSERT INTO main.t8 VALUES(1, 2); CREATE TABLE aux.t8(a, b); INSERT INTO aux.t8 VALUES(3, 4); CREATE TABLE aux2.t8(a, b); INSERT INTO aux2.t8 VALUES(5, 6); CREATE TABLE aux.t9(a, b); INSERT INTO aux.t9 VALUES(1, 2); CREATE TABLE aux2.t9(a, b); INSERT INTO aux2.t9 VALUES(3, 4); CREATE TABLE aux2.t10(a, b); INSERT INTO aux2.t10 VALUES(1, 2); } {} # EVIDENCE-OF: R-09681-58560 The table-name specified as part of a # DELETE statement within a trigger body must be unqualified. # # EVIDENCE-OF: R-36771-43788 In other words, the database-name. prefix # on the table name is not allowed within triggers. # do_delete_tests e_delete-2.1 -error { qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers } { 1 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN DELETE FROM main.t2; END; } {} 2 { CREATE TRIGGER tr1 BEFORE UPDATE ON t2 BEGIN DELETE FROM temp.t7 WHERE a=new.a; END; } {} 3 { CREATE TRIGGER tr1 AFTER UPDATE ON t8 BEGIN DELETE FROM aux2.t8 WHERE b!=a; END; } {} } # EVIDENCE-OF: R-28818-63526 If the table to which the trigger is # attached is not in the temp database, then DELETE statements within # the trigger body must operate on tables within the same database as # it. # # This is tested in two parts. First, check that if a table of the # specified name does not exist, an error is raised. Secondly, test # that if tables with the specified name exist in multiple databases, # the local database table is used. # do_delete_tests e_delete-2.2.1 -error { no such table: %s } { 1 { CREATE TRIGGER main.tr1 AFTER INSERT ON main.t7 BEGIN DELETE FROM t9; END; INSERT INTO main.t7 VALUES(1, 2); } {main.t9} 2 { CREATE TRIGGER aux.tr2 BEFORE UPDATE ON t9 BEGIN DELETE FROM t10; END; UPDATE t9 SET a=1; } {aux.t10} } do_execsql_test e_delete-2.2.X { DROP TRIGGER main.tr1; DROP TRIGGER aux.tr2; } {} do_delete_tests e_delete-2.2.2 { 1 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t8 BEGIN DELETE FROM t9; END; INSERT INTO aux.t8 VALUES(1, 2); SELECT count(*) FROM aux.t9 UNION ALL SELECT count(*) FROM aux2.t9; } {0 1} 2 { CREATE TRIGGER main.tr1 AFTER INSERT ON t8 BEGIN DELETE FROM t7; END; INSERT INTO main.t8 VALUES(1, 2); SELECT count(*) FROM temp.t7 UNION ALL SELECT count(*) FROM main.t7 UNION ALL SELECT count(*) FROM aux.t7 UNION ALL SELECT count(*) FROM aux2.t7; } {1 0 1 1} } # EVIDENCE-OF: R-31567-38587 If the table to which the trigger is # attached is in the TEMP database, then the unqualified name of the # table being deleted is resolved in the same way as it is for a # top-level statement (by searching first the TEMP database, then the # main database, then any other databases in the order they were # attached). # do_execsql_test e_delete-2.3.0 { DROP TRIGGER aux.tr1; DROP TRIGGER main.tr1; DELETE FROM main.t8 WHERE oid>1; DELETE FROM aux.t8 WHERE oid>1; INSERT INTO aux.t9 VALUES(1, 2); INSERT INTO main.t7 VALUES(3, 4); } {} do_execsql_test e_delete-2.3.1 { SELECT count(*) FROM temp.t7 UNION ALL SELECT count(*) FROM main.t7 UNION ALL SELECT count(*) FROM aux.t7 UNION ALL SELECT count(*) FROM aux2.t7; SELECT count(*) FROM main.t8 UNION ALL SELECT count(*) FROM aux.t8 UNION ALL SELECT count(*) FROM aux2.t8; SELECT count(*) FROM aux.t9 UNION ALL SELECT count(*) FROM aux2.t9; SELECT count(*) FROM aux2.t10; } {1 1 1 1 1 1 1 1 1 1} do_execsql_test e_delete-2.3.2 { CREATE TRIGGER temp.tr1 AFTER INSERT ON t7 BEGIN DELETE FROM t7; DELETE FROM t8; DELETE FROM t9; DELETE FROM t10; END; INSERT INTO temp.t7 VALUES('hello', 'world'); } {} do_execsql_test e_delete-2.3.3 { SELECT count(*) FROM temp.t7 UNION ALL SELECT count(*) FROM main.t7 UNION ALL SELECT count(*) FROM aux.t7 UNION ALL SELECT count(*) FROM aux2.t7; SELECT count(*) FROM main.t8 UNION ALL SELECT count(*) FROM aux.t8 UNION ALL SELECT count(*) FROM aux2.t8; SELECT count(*) FROM aux.t9 UNION ALL SELECT count(*) FROM aux2.t9; SELECT count(*) FROM aux2.t10; } {0 1 1 1 0 1 1 0 1 0} # EVIDENCE-OF: R-28691-49464 The INDEXED BY and NOT INDEXED clauses are # not allowed on DELETE statements within triggers. # do_execsql_test e_delete-2.4.0 { CREATE INDEX i8 ON t8(a, b); } {} do_delete_tests e_delete-2.4 -error { the %s %s clause is not allowed on UPDATE or DELETE statements within triggers } { 1 { CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN DELETE FROM t8 INDEXED BY i8 WHERE a=5; END; } {INDEXED BY} 2 { CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN DELETE FROM t8 NOT INDEXED WHERE a=5; END; } {NOT INDEXED} } ifcapable update_delete_limit { # EVIDENCE-OF: R-64942-06615 The LIMIT and ORDER BY clauses (described # below) are unsupported for DELETE statements within triggers. # do_delete_tests e_delete-2.5 -error { near "%s": syntax error } { 1 { CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN DELETE FROM t8 LIMIT 10; END; } {LIMIT} 2 { CREATE TRIGGER tr3 AFTER INSERT ON t8 BEGIN DELETE FROM t8 ORDER BY a LIMIT 5; END; } {ORDER} } # EVIDENCE-OF: R-40026-10531 If SQLite is compiled with the # SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option, then the syntax # of the DELETE statement is extended by the addition of optional ORDER # BY and LIMIT clauses: # # EVIDENCE-OF: R-45897-01670 -- syntax diagram delete-stmt-limited # do_delete_tests e_delete-3.1 { 1 "DELETE FROM t1 LIMIT 5" {} 2 "DELETE FROM t1 LIMIT 5-1 OFFSET 2+2" {} 3 "DELETE FROM t1 LIMIT 2+2, 16/4" {} 4 "DELETE FROM t1 ORDER BY x LIMIT 5" {} 5 "DELETE FROM t1 ORDER BY x LIMIT 5-1 OFFSET 2+2" {} 6 "DELETE FROM t1 ORDER BY x LIMIT 2+2, 16/4" {} 7 "DELETE FROM t1 WHERE x>2 LIMIT 5" {} 8 "DELETE FROM t1 WHERE x>2 LIMIT 5-1 OFFSET 2+2" {} 9 "DELETE FROM t1 WHERE x>2 LIMIT 2+2, 16/4" {} 10 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 5" {} 11 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 5-1 OFFSET 2+2" {} 12 "DELETE FROM t1 WHERE x>2 ORDER BY x LIMIT 2+2, 16/4" {} } drop_all_tables proc rebuild_t1 {} { catchsql { DROP TABLE t1 } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(4, 'four'); INSERT INTO t1 VALUES(5, 'five'); } } # EVIDENCE-OF: R-44062-08550 If a DELETE statement has a LIMIT clause, # the maximum number of rows that will be deleted is found by evaluating # the accompanying expression and casting it to an integer value. # rebuild_t1 do_delete_tests e_delete-3.2 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1 "DELETE FROM t1 LIMIT 3" {4 5} 2 "DELETE FROM t1 LIMIT 1+1" {3 4 5} 3 "DELETE FROM t1 LIMIT '4'" {5} 4 "DELETE FROM t1 LIMIT '1.0'" {2 3 4 5} } # EVIDENCE-OF: R-02661-56399 If the result of the evaluating the LIMIT # clause cannot be losslessly converted to an integer value, it is an # error. # do_delete_tests e_delete-3.3 -error { datatype mismatch } { 1 "DELETE FROM t1 LIMIT 'abc'" {} 2 "DELETE FROM t1 LIMIT NULL" {} 3 "DELETE FROM t1 LIMIT X'ABCD'" {} 4 "DELETE FROM t1 LIMIT 1.2" {} } # EVIDENCE-OF: R-00598-03741 A negative LIMIT value is interpreted as # "no limit". # do_delete_tests e_delete-3.4 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1 "DELETE FROM t1 LIMIT -1" {} 2 "DELETE FROM t1 LIMIT 2-4" {} 3 "DELETE FROM t1 LIMIT -4.0" {} 4 "DELETE FROM t1 LIMIT 5*-1" {} } # EVIDENCE-OF: R-26377-49195 If the DELETE statement also has an OFFSET # clause, then it is similarly evaluated and cast to an integer value. # Again, it is an error if the value cannot be losslessly converted to # an integer. # do_delete_tests e_delete-3.5 -error { datatype mismatch } { 1 "DELETE FROM t1 LIMIT 1 OFFSET 'abc'" {} 2 "DELETE FROM t1 LIMIT 1 OFFSET NULL" {} 3 "DELETE FROM t1 LIMIT 1 OFFSET X'ABCD'" {} 4 "DELETE FROM t1 LIMIT 1 OFFSET 1.2" {} 5 "DELETE FROM t1 LIMIT 'abc', 1" {} 6 "DELETE FROM t1 LIMIT NULL, 1" {} 7 "DELETE FROM t1 LIMIT X'ABCD', 1" {} 8 "DELETE FROM t1 LIMIT 1.2, 1" {} } # EVIDENCE-OF: R-64004-53814 If there is no OFFSET clause, or the # calculated integer value is negative, the effective OFFSET value is # zero. # do_delete_tests e_delete-3.6 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1a "DELETE FROM t1 LIMIT 3 OFFSET 0" {4 5} 1b "DELETE FROM t1 LIMIT 3" {4 5} 1c "DELETE FROM t1 LIMIT 3 OFFSET -1" {4 5} 2a "DELETE FROM t1 LIMIT 1+1 OFFSET 0" {3 4 5} 2b "DELETE FROM t1 LIMIT 1+1" {3 4 5} 2c "DELETE FROM t1 LIMIT 1+1 OFFSET 2-5" {3 4 5} 3a "DELETE FROM t1 LIMIT '4' OFFSET 0" {5} 3b "DELETE FROM t1 LIMIT '4'" {5} 3c "DELETE FROM t1 LIMIT '4' OFFSET -1.0" {5} 4a "DELETE FROM t1 LIMIT '1.0' OFFSET 0" {2 3 4 5} 4b "DELETE FROM t1 LIMIT '1.0'" {2 3 4 5} 4c "DELETE FROM t1 LIMIT '1.0' OFFSET -11" {2 3 4 5} } # EVIDENCE-OF: R-48141-52334 If the DELETE statement has an ORDER BY # clause, then all rows that would be deleted in the absence of the # LIMIT clause are sorted according to the ORDER BY. The first M rows, # where M is the value found by evaluating the OFFSET clause expression, # are skipped, and the following N, where N is the value of the LIMIT # expression, are deleted. # do_delete_tests e_delete-3.7 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1 "DELETE FROM t1 ORDER BY b LIMIT 2" {1 2 3} 2 "DELETE FROM t1 ORDER BY length(b), a LIMIT 3" {3 5} 3 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 0" {1 2 3 4} 4 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 1" {1 2 3 5} 5 "DELETE FROM t1 ORDER BY a DESC LIMIT 1 OFFSET 2" {1 2 4 5} } # EVIDENCE-OF: R-64535-08414 If there are less than N rows remaining # after taking the OFFSET clause into account, or if the LIMIT clause # evaluated to a negative value, then all remaining rows are deleted. # do_delete_tests e_delete-3.8 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1 "DELETE FROM t1 ORDER BY a ASC LIMIT 10" {} 2 "DELETE FROM t1 ORDER BY a ASC LIMIT -1" {} 3 "DELETE FROM t1 ORDER BY a ASC LIMIT 4 OFFSET 2" {1 2} } # EVIDENCE-OF: R-37284-06965 If the DELETE statement has no ORDER BY # clause, then all rows that would be deleted in the absence of the # LIMIT clause are assembled in an arbitrary order before applying the # LIMIT and OFFSET clauses to determine the subset that are actually # deleted. # # In practice, the "arbitrary order" is rowid order. # do_delete_tests e_delete-3.9 -repair rebuild_t1 -query { SELECT a FROM t1 } { 1 "DELETE FROM t1 LIMIT 2" {3 4 5} 2 "DELETE FROM t1 LIMIT 3" {4 5} 3 "DELETE FROM t1 LIMIT 1 OFFSET 0" {2 3 4 5} 4 "DELETE FROM t1 LIMIT 1 OFFSET 1" {1 3 4 5} 5 "DELETE FROM t1 LIMIT 1 OFFSET 2" {1 2 4 5} } # EVIDENCE-OF: R-26627-30313 The ORDER BY clause on an DELETE statement # is used only to determine which rows fall within the LIMIT. The order # in which rows are deleted is arbitrary and is not influenced by the # ORDER BY clause. # # In practice, rows are always deleted in rowid order. # do_delete_tests e_delete-3.10 -repair { rebuild_t1 catchsql { DROP TABLE t1log } execsql { CREATE TABLE t1log(x); CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN INSERT INTO t1log VALUES(old.a); END; } } -query { SELECT x FROM t1log } { 1 "DELETE FROM t1 ORDER BY a DESC LIMIT 2" {4 5} 2 "DELETE FROM t1 ORDER BY a DESC LIMIT -1" {1 2 3 4 5} 3 "DELETE FROM t1 ORDER BY a ASC LIMIT 2" {1 2} 4 "DELETE FROM t1 ORDER BY a ASC LIMIT -1" {1 2 3 4 5} } } finish_test |
Changes to test/e_expr.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # the lang_expr.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl # Set up three global variables: # # ::opname An array mapping from SQL operator to an easy to parse # name. The names are used as part of test case names. # # ::opprec An array mapping from SQL operator to a numeric # precedence value. Operators that group more tightly | > > > > > > > > > > > | 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 | # the lang_expr.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl proc do_expr_test {tn expr type value} { uplevel do_execsql_test $tn [list "SELECT typeof($expr), $expr"] [ list [list $type $value] ] } proc do_qexpr_test {tn expr value} { uplevel do_execsql_test $tn [list "SELECT quote($expr)"] [list $value] } # Set up three global variables: # # ::opname An array mapping from SQL operator to an easy to parse # name. The names are used as part of test case names. # # ::opprec An array mapping from SQL operator to a numeric # precedence value. Operators that group more tightly |
︙ | ︙ | |||
409 410 411 412 413 414 415 | # do_execsql_test e_expr-10.1.1 { SELECT typeof(5) } {integer} do_execsql_test e_expr-10.1.2 { SELECT typeof(5.1) } {real} do_execsql_test e_expr-10.1.3 { SELECT typeof('5.1') } {text} do_execsql_test e_expr-10.1.4 { SELECT typeof(X'ABCD') } {blob} do_execsql_test e_expr-10.1.5 { SELECT typeof(NULL) } {null} | | < | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | # do_execsql_test e_expr-10.1.1 { SELECT typeof(5) } {integer} do_execsql_test e_expr-10.1.2 { SELECT typeof(5.1) } {real} do_execsql_test e_expr-10.1.3 { SELECT typeof('5.1') } {text} do_execsql_test e_expr-10.1.4 { SELECT typeof(X'ABCD') } {blob} do_execsql_test e_expr-10.1.5 { SELECT typeof(NULL) } {null} # "Scientific notation is supported for point literal values." # do_execsql_test e_expr-10.2.1 { SELECT typeof(3.4e-02) } {real} do_execsql_test e_expr-10.2.2 { SELECT typeof(3e+5) } {real} do_execsql_test e_expr-10.2.3 { SELECT 3.4e-02 } {0.034} do_execsql_test e_expr-10.2.4 { SELECT 3e+4 } {30000.0} # EVIDENCE-OF: R-35229-17830 A string constant is formed by enclosing |
︙ | ︙ | |||
613 614 615 616 617 618 619 | [sqlite3_column_type $stmt 3] } {NULL NULL NULL NULL} do_test e_expr-11.7.1 { sqlite3_finalize $stmt } SQLITE_OK #------------------------------------------------------------------------- # "Test" the syntax diagrams in lang_expr.html. # | | | | 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 | [sqlite3_column_type $stmt 3] } {NULL NULL NULL NULL} do_test e_expr-11.7.1 { sqlite3_finalize $stmt } SQLITE_OK #------------------------------------------------------------------------- # "Test" the syntax diagrams in lang_expr.html. # # EVIDENCE-OF: R-62067-43884 -- syntax diagram signed-number # do_execsql_test e_expr-12.1.1 { SELECT 0, +0, -0 } {0 0 0} do_execsql_test e_expr-12.1.2 { SELECT 1, +1, -1 } {1 1 -1} do_execsql_test e_expr-12.1.3 { SELECT 2, +2, -2 } {2 2 -2} do_execsql_test e_expr-12.1.4 { SELECT 1.4, +1.4, -1.4 } {1.4 1.4 -1.4} do_execsql_test e_expr-12.1.5 { SELECT 1.5e+5, +1.5e+5, -1.5e+5 } {150000.0 150000.0 -150000.0} do_execsql_test e_expr-12.1.6 { SELECT 0.0001, +0.0001, -0.0001 } {0.0001 0.0001 -0.0001} # EVIDENCE-OF: R-21258-25489 -- syntax diagram literal-value # set sqlite_current_time 1 do_execsql_test e_expr-12.2.1 {SELECT 123} {123} do_execsql_test e_expr-12.2.2 {SELECT 123.4e05} {12340000.0} do_execsql_test e_expr-12.2.3 {SELECT 'abcde'} {abcde} do_execsql_test e_expr-12.2.4 {SELECT X'414243'} {ABC} do_execsql_test e_expr-12.2.5 {SELECT NULL} {{}} |
︙ | ︙ | |||
922 923 924 925 926 927 928 929 930 | # # EVIDENCE-OF: R-04532-11527 The LIKE operator is case sensitive by # default for unicode characters that are beyond the ASCII range. # # EVIDENCE-OF: R-44381-11669 the expression # 'a' LIKE 'A' is TRUE but # 'æ' LIKE 'Æ' is FALSE. # do_execsql_test e_expr-14.5.1 { SELECT 'A' LIKE 'a' } 1 | > > > > > | > | 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 | # # EVIDENCE-OF: R-04532-11527 The LIKE operator is case sensitive by # default for unicode characters that are beyond the ASCII range. # # EVIDENCE-OF: R-44381-11669 the expression # 'a' LIKE 'A' is TRUE but # 'æ' LIKE 'Æ' is FALSE. # # The restriction to ASCII characters does not apply if the ICU # library is compiled in. When ICU is enabled SQLite does not act # as it does "by default". # do_execsql_test e_expr-14.5.1 { SELECT 'A' LIKE 'a' } 1 ifcapable !icu { do_execsql_test e_expr-14.5.2 "SELECT '\u00c6' LIKE '\u00e6'" 0 } # EVIDENCE-OF: R-56683-13731 If the optional ESCAPE clause is present, # then the expression following the ESCAPE keyword must evaluate to a # string consisting of a single character. # do_catchsql_test e_expr-14.6.1 { SELECT 'A' LIKE 'a' ESCAPE '12' |
︙ | ︙ | |||
971 972 973 974 975 976 977 | # EVIDENCE-OF: R-51359-17496 The infix LIKE operator is implemented by # calling the application-defined SQL functions like(Y,X) or like(Y,X,Z). # proc likefunc {args} { eval lappend ::likeargs $args return 1 } | | > | 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 | # EVIDENCE-OF: R-51359-17496 The infix LIKE operator is implemented by # calling the application-defined SQL functions like(Y,X) or like(Y,X,Z). # proc likefunc {args} { eval lappend ::likeargs $args return 1 } db func like -argcount 2 likefunc db func like -argcount 3 likefunc set ::likeargs [list] do_execsql_test e_expr-15.1.1 { SELECT 'abc' LIKE 'def' } 1 do_test e_expr-15.1.2 { set likeargs } {def abc} set ::likeargs [list] do_execsql_test e_expr-15.1.3 { SELECT 'abc' LIKE 'def' ESCAPE 'X' } 1 do_test e_expr-15.1.4 { set likeargs } {def abc X} db close |
︙ | ︙ | |||
1042 1043 1044 1045 1046 1047 1048 | do_test e_expr-17.3.4 { set globargs } {Y X} sqlite3 db test.db # EVIDENCE-OF: R-41650-20872 No regexp() user function is defined by # default and so use of the REGEXP operator will normally result in an # error message. # | > > > | | | | | | > | 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 | do_test e_expr-17.3.4 { set globargs } {Y X} sqlite3 db test.db # EVIDENCE-OF: R-41650-20872 No regexp() user function is defined by # default and so use of the REGEXP operator will normally result in an # error message. # # There is a regexp function if ICU is enabled though. # ifcapable !icu { do_catchsql_test e_expr-18.1.1 { SELECT regexp('abc', 'def') } {1 {no such function: regexp}} do_catchsql_test e_expr-18.1.2 { SELECT 'abc' REGEXP 'def' } {1 {no such function: REGEXP}} } # EVIDENCE-OF: R-33693-50180 The REGEXP operator is a special syntax for # the regexp() user function. # # EVIDENCE-OF: R-57289-13578 If a application-defined SQL function named # "regexp" is added at run-time, that function will be called in order # to implement the REGEXP operator. |
︙ | ︙ | |||
1098 1099 1100 1101 1102 1103 1104 1105 1106 | do_execsql_test e_expr-19.2.1 { SELECT 'abc' MATCH 'def' } 1 do_test e_expr-19.2.2 { set matchargs } {def abc} set ::matchargs [list] do_execsql_test e_expr-19.2.3 { SELECT 'X' NOT MATCH 'Y' } 0 do_test e_expr-19.2.4 { set matchargs } {Y X} sqlite3 db test.db finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 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 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 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 1627 1628 1629 1630 1631 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 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 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 | do_execsql_test e_expr-19.2.1 { SELECT 'abc' MATCH 'def' } 1 do_test e_expr-19.2.2 { set matchargs } {def abc} set ::matchargs [list] do_execsql_test e_expr-19.2.3 { SELECT 'X' NOT MATCH 'Y' } 0 do_test e_expr-19.2.4 { set matchargs } {Y X} sqlite3 db test.db #------------------------------------------------------------------------- # Test cases for the testable statements related to the CASE expression. # # EVIDENCE-OF: R-15199-61389 There are two basic forms of the CASE # expression: those with a base expression and those without. # do_execsql_test e_expr-20.1 { SELECT CASE WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE 'else' END; } {true} do_execsql_test e_expr-20.2 { SELECT CASE 0 WHEN 1 THEN 'true' WHEN 0 THEN 'false' ELSE 'else' END; } {false} proc var {nm} { lappend ::varlist $nm return [set "::$nm"] } db func var var # EVIDENCE-OF: R-30638-59954 In a CASE without a base expression, each # WHEN expression is evaluated and the result treated as a boolean, # starting with the leftmost and continuing to the right. # foreach {a b c} {0 0 0} break set varlist [list] do_execsql_test e_expr-21.1.1 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' END } {{}} do_test e_expr-21.1.2 { set varlist } {a b c} set varlist [list] do_execsql_test e_expr-21.1.3 { SELECT CASE WHEN var('c') THEN 'C' WHEN var('b') THEN 'B' WHEN var('a') THEN 'A' ELSE 'no result' END } {{no result}} do_test e_expr-21.1.4 { set varlist } {c b a} # EVIDENCE-OF: R-39009-25596 The result of the CASE expression is the # evaluation of the THEN expression that corresponds to the first WHEN # expression that evaluates to true. # foreach {a b c} {0 1 0} break do_execsql_test e_expr-21.2.1 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' ELSE 'no result' END } {B} foreach {a b c} {0 1 1} break do_execsql_test e_expr-21.2.2 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' ELSE 'no result' END } {B} foreach {a b c} {0 0 1} break do_execsql_test e_expr-21.2.3 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' ELSE 'no result' END } {C} # EVIDENCE-OF: R-24227-04807 Or, if none of the WHEN expressions # evaluate to true, the result of evaluating the ELSE expression, if # any. # foreach {a b c} {0 0 0} break do_execsql_test e_expr-21.3.1 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' ELSE 'no result' END } {{no result}} # EVIDENCE-OF: R-14168-07579 If there is no ELSE expression and none of # the WHEN expressions are true, then the overall result is NULL. # db nullvalue null do_execsql_test e_expr-21.3.2 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' END } {null} db nullvalue {} # EVIDENCE-OF: R-13943-13592 A NULL result is considered untrue when # evaluating WHEN terms. # do_execsql_test e_expr-21.4.1 { SELECT CASE WHEN NULL THEN 'A' WHEN 1 THEN 'B' END } {B} do_execsql_test e_expr-21.4.2 { SELECT CASE WHEN 0 THEN 'A' WHEN NULL THEN 'B' ELSE 'C' END } {C} # EVIDENCE-OF: R-38620-19499 In a CASE with a base expression, the base # expression is evaluated just once and the result is compared against # the evaluation of each WHEN expression from left to right. # # Note: This test case tests the "evaluated just once" part of the above # statement. Tests associated with the next two statements test that the # comparisons take place. # foreach {a b c} [list [expr 3] [expr 4] [expr 5]] break set ::varlist [list] do_execsql_test e_expr-22.1.1 { SELECT CASE var('a') WHEN 1 THEN 'A' WHEN 2 THEN 'B' WHEN 3 THEN 'C' END } {C} do_test e_expr-22.1.2 { set ::varlist } {a} # EVIDENCE-OF: R-07667-49537 The result of the CASE expression is the # evaluation of the THEN expression that corresponds to the first WHEN # expression for which the comparison is true. # do_execsql_test e_expr-22.2.1 { SELECT CASE 23 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END } {B} do_execsql_test e_expr-22.2.2 { SELECT CASE 1 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END } {A} # EVIDENCE-OF: R-47543-32145 Or, if none of the WHEN expressions # evaluate to a value equal to the base expression, the result of # evaluating the ELSE expression, if any. # do_execsql_test e_expr-22.3.1 { SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' ELSE 'D' END } {D} # EVIDENCE-OF: R-54721-48557 If there is no ELSE expression and none of # the WHEN expressions produce a result equal to the base expression, # the overall result is NULL. # do_execsql_test e_expr-22.4.1 { SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END } {{}} db nullvalue null do_execsql_test e_expr-22.4.2 { SELECT CASE 24 WHEN 1 THEN 'A' WHEN 23 THEN 'B' WHEN 23 THEN 'C' END } {null} db nullvalue {} # EVIDENCE-OF: R-11479-62774 When comparing a base expression against a # WHEN expression, the same collating sequence, affinity, and # NULL-handling rules apply as if the base expression and WHEN # expression are respectively the left- and right-hand operands of an = # operator. # proc rev {str} { set ret "" set chars [split $str] for {set i [expr [llength $chars]-1]} {$i>=0} {incr i -1} { append ret [lindex $chars $i] } set ret } proc reverse {lhs rhs} { string compare [rev $lhs] [ref $rhs] } db collate reverse reverse do_execsql_test e_expr-23.1.1 { CREATE TABLE t1( a TEXT COLLATE NOCASE, b COLLATE REVERSE, c INTEGER, d BLOB ); INSERT INTO t1 VALUES('abc', 'cba', 55, 34.5); } {} do_execsql_test e_expr-23.1.2 { SELECT CASE a WHEN 'xyz' THEN 'A' WHEN 'AbC' THEN 'B' END FROM t1 } {B} do_execsql_test e_expr-23.1.3 { SELECT CASE 'AbC' WHEN 'abc' THEN 'A' WHEN a THEN 'B' END FROM t1 } {B} do_execsql_test e_expr-23.1.4 { SELECT CASE a WHEN b THEN 'A' ELSE 'B' END FROM t1 } {B} do_execsql_test e_expr-23.1.5 { SELECT CASE b WHEN a THEN 'A' ELSE 'B' END FROM t1 } {A} do_execsql_test e_expr-23.1.6 { SELECT CASE 55 WHEN '55' THEN 'A' ELSE 'B' END } {B} do_execsql_test e_expr-23.1.7 { SELECT CASE c WHEN '55' THEN 'A' ELSE 'B' END FROM t1 } {A} do_execsql_test e_expr-23.1.8 { SELECT CASE '34.5' WHEN d THEN 'A' ELSE 'B' END FROM t1 } {B} do_execsql_test e_expr-23.1.9 { SELECT CASE NULL WHEN NULL THEN 'A' ELSE 'B' END } {B} # EVIDENCE-OF: R-37304-39405 If the base expression is NULL then the # result of the CASE is always the result of evaluating the ELSE # expression if it exists, or NULL if it does not. # do_execsql_test e_expr-24.1.1 { SELECT CASE NULL WHEN 'abc' THEN 'A' WHEN 'def' THEN 'B' END; } {{}} do_execsql_test e_expr-24.1.2 { SELECT CASE NULL WHEN 'abc' THEN 'A' WHEN 'def' THEN 'B' ELSE 'C' END; } {C} # EVIDENCE-OF: R-56280-17369 Both forms of the CASE expression use lazy, # or short-circuit, evaluation. # set varlist [list] foreach {a b c} {0 1 0} break do_execsql_test e_expr-25.1.1 { SELECT CASE WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' END } {B} do_test e_expr-25.1.2 { set ::varlist } {a b} set varlist [list] do_execsql_test e_expr-25.1.3 { SELECT CASE '0' WHEN var('a') THEN 'A' WHEN var('b') THEN 'B' WHEN var('c') THEN 'C' END } {A} do_test e_expr-25.1.4 { set ::varlist } {a} # EVIDENCE-OF: R-34773-62253 The only difference between the following # two CASE expressions is that the x expression is evaluated exactly # once in the first example but might be evaluated multiple times in the # second: CASE x WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END CASE WHEN # x=w1 THEN r1 WHEN x=w2 THEN r2 ELSE r3 END # proc ceval {x} { incr ::evalcount return $x } db func ceval ceval set ::evalcount 0 do_execsql_test e_expr-26.1.1 { CREATE TABLE t2(x, w1, r1, w2, r2, r3); INSERT INTO t2 VALUES(1, 1, 'R1', 2, 'R2', 'R3'); INSERT INTO t2 VALUES(2, 1, 'R1', 2, 'R2', 'R3'); INSERT INTO t2 VALUES(3, 1, 'R1', 2, 'R2', 'R3'); } {} do_execsql_test e_expr-26.1.2 { SELECT CASE x WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END FROM t2 } {R1 R2 R3} do_execsql_test e_expr-26.1.3 { SELECT CASE WHEN x=w1 THEN r1 WHEN x=w2 THEN r2 ELSE r3 END FROM t2 } {R1 R2 R3} do_execsql_test e_expr-26.1.4 { SELECT CASE ceval(x) WHEN w1 THEN r1 WHEN w2 THEN r2 ELSE r3 END FROM t2 } {R1 R2 R3} do_test e_expr-26.1.5 { set ::evalcount } {3} set ::evalcount 0 do_execsql_test e_expr-26.1.6 { SELECT CASE WHEN ceval(x)=w1 THEN r1 WHEN ceval(x)=w2 THEN r2 ELSE r3 END FROM t2 } {R1 R2 R3} do_test e_expr-26.1.6 { set ::evalcount } {5} #------------------------------------------------------------------------- # Test statements related to CAST expressions. # # EVIDENCE-OF: R-65079-31758 Application of a CAST expression is # different to application of a column affinity, as with a CAST # expression the storage class conversion is forced even if it is lossy # and irrreversible. # do_execsql_test e_expr-27.1.1 { CREATE TABLE t3(a TEXT, b REAL, c INTEGER); INSERT INTO t3 VALUES(X'555655', '1.23abc', 4.5); SELECT typeof(a), a, typeof(b), b, typeof(c), c FROM t3; } {blob UVU text 1.23abc real 4.5} do_execsql_test e_expr-27.1.2 { SELECT typeof(CAST(X'555655' as TEXT)), CAST(X'555655' as TEXT), typeof(CAST('1.23abc' as REAL)), CAST('1.23abc' as REAL), typeof(CAST(4.5 as INTEGER)), CAST(4.5 as INTEGER) } {text UVU real 1.23 integer 4} # EVIDENCE-OF: R-27225-65050 If the value of <expr> is NULL, then # the result of the CAST expression is also NULL. # do_expr_test e_expr-27.2.1 { CAST(NULL AS integer) } null {} do_expr_test e_expr-27.2.2 { CAST(NULL AS text) } null {} do_expr_test e_expr-27.2.3 { CAST(NULL AS blob) } null {} do_expr_test e_expr-27.2.4 { CAST(NULL AS number) } null {} # EVIDENCE-OF: R-31076-23575 Casting a value to a <type-name> with # no affinity causes the value to be converted into a BLOB. # do_expr_test e_expr-27.3.1 { CAST('abc' AS blob) } blob abc do_expr_test e_expr-27.3.2 { CAST('def' AS shobblob_x) } blob def do_expr_test e_expr-27.3.3 { CAST('ghi' AS abbLOb10) } blob ghi # EVIDENCE-OF: R-22956-37754 Casting to a BLOB consists of first casting # the value to TEXT in the encoding of the database connection, then # interpreting the resulting byte sequence as a BLOB instead of as TEXT. # do_qexpr_test e_expr-27.4.1 { CAST('ghi' AS blob) } X'676869' do_qexpr_test e_expr-27.4.2 { CAST(456 AS blob) } X'343536' do_qexpr_test e_expr-27.4.3 { CAST(1.78 AS blob) } X'312E3738' rename db db2 sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16le' } do_qexpr_test e_expr-27.4.4 { CAST('ghi' AS blob) } X'670068006900' do_qexpr_test e_expr-27.4.5 { CAST(456 AS blob) } X'340035003600' do_qexpr_test e_expr-27.4.6 { CAST(1.78 AS blob) } X'31002E0037003800' db close sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16be' } do_qexpr_test e_expr-27.4.7 { CAST('ghi' AS blob) } X'006700680069' do_qexpr_test e_expr-27.4.8 { CAST(456 AS blob) } X'003400350036' do_qexpr_test e_expr-27.4.9 { CAST(1.78 AS blob) } X'0031002E00370038' db close rename db2 db # EVIDENCE-OF: R-04207-37981 To cast a BLOB value to TEXT, the sequence # of bytes that make up the BLOB is interpreted as text encoded using # the database encoding. # do_expr_test e_expr-28.1.1 { CAST (X'676869' AS text) } text ghi do_expr_test e_expr-28.1.2 { CAST (X'670068006900' AS text) } text g rename db db2 sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16le' } do_expr_test e_expr-28.1.3 { CAST (X'676869' AS text) == 'ghi' } integer 0 do_expr_test e_expr-28.1.4 { CAST (X'670068006900' AS text) } text ghi db close rename db2 db # EVIDENCE-OF: R-22235-47006 Casting an INTEGER or REAL value into TEXT # renders the value as if via sqlite3_snprintf() except that the # resulting TEXT uses the encoding of the database connection. # do_expr_test e_expr-28.2.1 { CAST (1 AS text) } text 1 do_expr_test e_expr-28.2.2 { CAST (45 AS text) } text 45 do_expr_test e_expr-28.2.3 { CAST (-45 AS text) } text -45 do_expr_test e_expr-28.2.4 { CAST (8.8 AS text) } text 8.8 do_expr_test e_expr-28.2.5 { CAST (2.3e+5 AS text) } text 230000.0 do_expr_test e_expr-28.2.6 { CAST (-2.3e-5 AS text) } text -2.3e-05 do_expr_test e_expr-28.2.7 { CAST (0.0 AS text) } text 0.0 do_expr_test e_expr-28.2.7 { CAST (0 AS text) } text 0 # EVIDENCE-OF: R-26346-36443 When casting a BLOB value to a REAL, the # value is first converted to TEXT. # do_expr_test e_expr-29.1.1 { CAST (X'312E3233' AS REAL) } real 1.23 do_expr_test e_expr-29.1.2 { CAST (X'3233302E30' AS REAL) } real 230.0 do_expr_test e_expr-29.1.3 { CAST (X'2D392E3837' AS REAL) } real -9.87 do_expr_test e_expr-29.1.4 { CAST (X'302E30303031' AS REAL) } real 0.0001 rename db db2 sqlite3 db :memory: db eval { PRAGMA encoding = 'utf-16le' } do_expr_test e_expr-29.1.5 { CAST (X'31002E0032003300' AS REAL) } real 1.23 do_expr_test e_expr-29.1.6 { CAST (X'3200330030002E003000' AS REAL) } real 230.0 do_expr_test e_expr-29.1.7 { CAST (X'2D0039002E0038003700' AS REAL) } real -9.87 do_expr_test e_expr-29.1.8 { CAST (X'30002E003000300030003100' AS REAL) } real 0.0001 db close rename db2 db # EVIDENCE-OF: R-54898-34554 When casting a TEXT value to REAL, the # longest possible prefix of the value that can be interpreted as a real # number is extracted from the TEXT value and the remainder ignored. # do_expr_test e_expr-29.2.1 { CAST('1.23abcd' AS REAL) } real 1.23 do_expr_test e_expr-29.2.2 { CAST('1.45.23abcd' AS REAL) } real 1.45 do_expr_test e_expr-29.2.3 { CAST('-2.12e-01ABC' AS REAL) } real -0.212 do_expr_test e_expr-29.2.4 { CAST('1 2 3 4' AS REAL) } real 1.0 # EVIDENCE-OF: R-11321-47427 Any leading spaces in the TEXT value are # ignored when converging from TEXT to REAL. # do_expr_test e_expr-29.3.1 { CAST(' 1.23abcd' AS REAL) } real 1.23 do_expr_test e_expr-29.3.2 { CAST(' 1.45.23abcd' AS REAL) } real 1.45 do_expr_test e_expr-29.3.3 { CAST(' -2.12e-01ABC' AS REAL) } real -0.212 do_expr_test e_expr-29.3.4 { CAST(' 1 2 3 4' AS REAL) } real 1.0 # EVIDENCE-OF: R-22662-28218 If there is no prefix that can be # interpreted as a real number, the result of the conversion is 0.0. # do_expr_test e_expr-29.4.1 { CAST('' AS REAL) } real 0.0 do_expr_test e_expr-29.4.2 { CAST('not a number' AS REAL) } real 0.0 do_expr_test e_expr-29.4.3 { CAST('XXI' AS REAL) } real 0.0 # EVIDENCE-OF: R-21829-14563 When casting a BLOB value to INTEGER, the # value is first converted to TEXT. # do_expr_test e_expr-30.1.1 { CAST(X'313233' AS INTEGER) } integer 123 do_expr_test e_expr-30.1.2 { CAST(X'2D363738' AS INTEGER) } integer -678 do_expr_test e_expr-30.1.3 { CAST(X'31303030303030' AS INTEGER) } integer 1000000 do_expr_test e_expr-30.1.4 { CAST(X'2D31313235383939393036383432363234' AS INTEGER) } integer -1125899906842624 rename db db2 sqlite3 db :memory: execsql { PRAGMA encoding = 'utf-16be' } do_expr_test e_expr-30.1.5 { CAST(X'003100320033' AS INTEGER) } integer 123 do_expr_test e_expr-30.1.6 { CAST(X'002D003600370038' AS INTEGER) } integer -678 do_expr_test e_expr-30.1.7 { CAST(X'0031003000300030003000300030' AS INTEGER) } integer 1000000 do_expr_test e_expr-30.1.8 { CAST(X'002D0031003100320035003800390039003900300036003800340032003600320034' AS INTEGER) } integer -1125899906842624 db close rename db2 db # EVIDENCE-OF: R-47612-45842 When casting a TEXT value to INTEGER, the # longest possible prefix of the value that can be interpreted as an # integer number is extracted from the TEXT value and the remainder # ignored. # do_expr_test e_expr-30.2.1 { CAST('123abcd' AS INT) } integer 123 do_expr_test e_expr-30.2.2 { CAST('14523abcd' AS INT) } integer 14523 do_expr_test e_expr-30.2.3 { CAST('-2.12e-01ABC' AS INT) } integer -2 do_expr_test e_expr-30.2.4 { CAST('1 2 3 4' AS INT) } integer 1 # EVIDENCE-OF: R-34400-33772 Any leading spaces in the TEXT value when # converting from TEXT to INTEGER are ignored. # do_expr_test e_expr-30.3.1 { CAST(' 123abcd' AS INT) } integer 123 do_expr_test e_expr-30.3.2 { CAST(' 14523abcd' AS INT) } integer 14523 do_expr_test e_expr-30.3.3 { CAST(' -2.12e-01ABC' AS INT) } integer -2 do_expr_test e_expr-30.3.4 { CAST(' 1 2 3 4' AS INT) } integer 1 # EVIDENCE-OF: R-43164-44276 If there is no prefix that can be # interpreted as an integer number, the result of the conversion is 0. # do_expr_test e_expr-30.4.1 { CAST('' AS INTEGER) } integer 0 do_expr_test e_expr-30.4.2 { CAST('not a number' AS INTEGER) } integer 0 do_expr_test e_expr-30.4.3 { CAST('XXI' AS INTEGER) } integer 0 # EVIDENCE-OF: R-00741-38776 A cast of a REAL value into an INTEGER will # truncate the fractional part of the REAL. # do_expr_test e_expr-31.1.1 { CAST(3.14159 AS INTEGER) } integer 3 do_expr_test e_expr-31.1.2 { CAST(1.99999 AS INTEGER) } integer 1 do_expr_test e_expr-31.1.3 { CAST(-1.99999 AS INTEGER) } integer -1 do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0 # EVIDENCE-OF: R-06126-36021 If an REAL is too large to be represented # as an INTEGER then the result of the cast is the largest negative # integer: -9223372036854775808. # do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.2 { CAST(-2e+50 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.3 { CAST(-9223372036854775809.0 AS INT) } integer -9223372036854775808 do_expr_test e_expr-31.2.4 { CAST(9223372036854775809.0 AS INT) } integer -9223372036854775808 # EVIDENCE-OF: R-09295-61337 Casting a TEXT or BLOB value into NUMERIC # first does a forced conversion into REAL but then further converts the # result into INTEGER if and only if the conversion from REAL to INTEGER # is lossless and reversible. # do_expr_test e_expr-32.1.1 { CAST('45' AS NUMERIC) } integer 45 do_expr_test e_expr-32.1.2 { CAST('45.0' AS NUMERIC) } integer 45 do_expr_test e_expr-32.1.3 { CAST('45.2' AS NUMERIC) } real 45.2 do_expr_test e_expr-32.1.4 { CAST('11abc' AS NUMERIC) } integer 11 do_expr_test e_expr-32.1.5 { CAST('11.1abc' AS NUMERIC) } real 11.1 # EVIDENCE-OF: R-30347-18702 Casting a REAL or INTEGER value to NUMERIC # is a no-op, even if a real value could be losslessly converted to an # integer. # do_expr_test e_expr-32.2.1 { CAST(13.0 AS NUMERIC) } real 13.0 do_expr_test e_expr-32.2.2 { CAST(13.5 AS NUMERIC) } real 13.5 do_expr_test e_expr-32.2.3 { CAST(-9223372036854775808 AS NUMERIC) } integer -9223372036854775808 do_expr_test e_expr-32.2.4 { CAST(9223372036854775807 AS NUMERIC) } integer 9223372036854775807 # EVIDENCE-OF: R-64550-29191 Note that the result from casting any # non-BLOB value into a BLOB and the result from casting any BLOB value # into a non-BLOB value may be different depending on whether the # database encoding is UTF-8, UTF-16be, or UTF-16le. # sqlite3 db1 :memory: ; db1 eval { PRAGMA encoding = 'utf-8' } sqlite3 db2 :memory: ; db2 eval { PRAGMA encoding = 'utf-16le' } sqlite3 db3 :memory: ; db3 eval { PRAGMA encoding = 'utf-16be' } foreach {tn castexpr differs} { 1 { CAST(123 AS BLOB) } 1 2 { CAST('' AS BLOB) } 0 3 { CAST('abcd' AS BLOB) } 1 4 { CAST(X'abcd' AS TEXT) } 1 5 { CAST(X'' AS TEXT) } 0 } { set r1 [db1 eval "SELECT typeof($castexpr), quote($castexpr)"] set r2 [db2 eval "SELECT typeof($castexpr), quote($castexpr)"] set r3 [db3 eval "SELECT typeof($castexpr), quote($castexpr)"] if {$differs} { set res [expr {$r1!=$r2 && $r2!=$r3}] } else { set res [expr {$r1==$r2 && $r2==$r3}] } do_test e_expr-33.1.$tn {set res} 1 } db1 close db2 close db3 close #------------------------------------------------------------------------- # Test statements related to the EXISTS and NOT EXISTS operators. # catch { db close } file delete -force test.db sqlite3 db test.db do_execsql_test e_expr-34.1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(NULL, 2); INSERT INTO t1 VALUES(1, NULL); INSERT INTO t1 VALUES(NULL, NULL); } {} # EVIDENCE-OF: R-25588-27181 The EXISTS operator always evaluates to one # of the integer values 0 and 1. # # This statement is not tested by itself. Instead, all e_expr-34.* tests # following this point explicitly test that specific invocations of EXISTS # return either integer 0 or integer 1. # # EVIDENCE-OF: R-58553-63740 If executing the SELECT statement specified # as the right-hand operand of the EXISTS operator would return one or # more rows, then the EXISTS operator evaluates to 1. # foreach {tn expr} { 1 { EXISTS ( SELECT a FROM t1 ) } 2 { EXISTS ( SELECT b FROM t1 ) } 3 { EXISTS ( SELECT 24 ) } 4 { EXISTS ( SELECT NULL ) } 5 { EXISTS ( SELECT a FROM t1 WHERE a IS NULL ) } } { do_expr_test e_expr-34.2.$tn $expr integer 1 } # EVIDENCE-OF: R-19673-40972 If executing the SELECT would return no # rows at all, then the EXISTS operator evaluates to 0. # foreach {tn expr} { 1 { EXISTS ( SELECT a FROM t1 WHERE 0) } 2 { EXISTS ( SELECT b FROM t1 WHERE a = 5) } 3 { EXISTS ( SELECT 24 WHERE 0) } 4 { EXISTS ( SELECT NULL WHERE 1=2) } } { do_expr_test e_expr-34.3.$tn $expr integer 0 } # EVIDENCE-OF: R-35109-49139 The number of columns in each row returned # by the SELECT statement (if any) and the specific values returned have # no effect on the results of the EXISTS operator. # foreach {tn expr res} { 1 { EXISTS ( SELECT * FROM t1 ) } 1 2 { EXISTS ( SELECT *, *, * FROM t1 ) } 1 3 { EXISTS ( SELECT 24, 25 ) } 1 4 { EXISTS ( SELECT NULL, NULL, NULL ) } 1 5 { EXISTS ( SELECT a,b,a||b FROM t1 WHERE a IS NULL ) } 1 6 { EXISTS ( SELECT a, a FROM t1 WHERE 0) } 0 7 { EXISTS ( SELECT b, b, a FROM t1 WHERE a = 5) } 0 8 { EXISTS ( SELECT 24, 46, 89 WHERE 0) } 0 9 { EXISTS ( SELECT NULL, NULL WHERE 1=2) } 0 } { do_expr_test e_expr-34.4.$tn $expr integer $res } # EVIDENCE-OF: R-10645-12439 In particular, rows containing NULL values # are not handled any differently from rows without NULL values. # foreach {tn e1 e2} { 1 { EXISTS (SELECT 'not null') } { EXISTS (SELECT NULL) } 2 { EXISTS (SELECT NULL FROM t1) } { EXISTS (SELECT 'bread' FROM t1) } } { set res [db one "SELECT $e1"] do_expr_test e_expr-34.5.${tn}a $e1 integer $res do_expr_test e_expr-34.5.${tn}b $e2 integer $res } #------------------------------------------------------------------------- # Test statements related to scalar sub-queries. # catch { db close } file delete -force test.db sqlite3 db test.db do_test e_expr-35.0 { execsql { CREATE TABLE t2(a, b); INSERT INTO t2 VALUES('one', 'two'); INSERT INTO t2 VALUES('three', NULL); INSERT INTO t2 VALUES(4, 5.0); } } {} # EVIDENCE-OF: R-00980-39256 A SELECT statement enclosed in parentheses # may appear as a scalar quantity. # # EVIDENCE-OF: R-56294-03966 All types of SELECT statement, including # aggregate and compound SELECT queries (queries with keywords like # UNION or EXCEPT) are allowed as scalar subqueries. # do_expr_test e_expr-35.1.1 { (SELECT 35) } integer 35 do_expr_test e_expr-35.1.2 { (SELECT NULL) } null {} do_expr_test e_expr-35.1.3 { (SELECT count(*) FROM t2) } integer 3 do_expr_test e_expr-35.1.4 { (SELECT 4 FROM t2) } integer 4 do_expr_test e_expr-35.1.5 { (SELECT b FROM t2 UNION SELECT a+1 FROM t2) } null {} do_expr_test e_expr-35.1.6 { (SELECT a FROM t2 UNION SELECT COALESCE(b, 55) FROM t2 ORDER BY 1) } integer 4 # EVIDENCE-OF: R-46899-53765 A SELECT used as a scalar quantity must # return a result set with a single column. # # The following block tests that errors are returned in a bunch of cases # where a subquery returns more than one column. # set M {only a single result allowed for a SELECT that is part of an expression} foreach {tn sql} { 1 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2) } 2 { SELECT (SELECT * FROM t2 UNION SELECT a+1, b+1 FROM t2 ORDER BY 1) } 3 { SELECT (SELECT 1, 2) } 4 { SELECT (SELECT NULL, NULL, NULL) } 5 { SELECT (SELECT * FROM t2) } 6 { SELECT (SELECT * FROM (SELECT 1, 2, 3)) } } { do_catchsql_test e_expr-35.2.$tn $sql [list 1 $M] } # EVIDENCE-OF: R-35764-28041 The result of the expression is the value # of the only column in the first row returned by the SELECT statement. # # EVIDENCE-OF: R-41898-06686 If the SELECT yields more than one result # row, all rows after the first are ignored. # do_execsql_test e_expr-36.3.1 { CREATE TABLE t4(x, y); INSERT INTO t4 VALUES(1, 'one'); INSERT INTO t4 VALUES(2, 'two'); INSERT INTO t4 VALUES(3, 'three'); } {} foreach {tn expr restype resval} { 2 { ( SELECT x FROM t4 ORDER BY x ) } integer 1 3 { ( SELECT x FROM t4 ORDER BY y ) } integer 1 4 { ( SELECT x FROM t4 ORDER BY x DESC ) } integer 3 5 { ( SELECT x FROM t4 ORDER BY y DESC ) } integer 2 6 { ( SELECT y FROM t4 ORDER BY y DESC ) } text two 7 { ( SELECT sum(x) FROM t4 ) } integer 6 8 { ( SELECT group_concat(y,'') FROM t4 ) } text onetwothree 9 { ( SELECT max(x) FROM t4 WHERE y LIKE '___') } integer 2 } { do_expr_test e_expr-36.3.$tn $expr $restype $resval } # EVIDENCE-OF: R-25492-41572 If the SELECT yields no rows, then the # value of the expression is NULL. # foreach {tn expr} { 1 { ( SELECT x FROM t4 WHERE x>3 ORDER BY x ) } 2 { ( SELECT x FROM t4 WHERE y<'one' ORDER BY y ) } } { do_expr_test e_expr-36.4.$tn $expr null {} } finish_test |
Changes to test/e_fkey.test.
︙ | ︙ | |||
964 965 966 967 968 969 970 | trackid INTEGER, trackname TEXT, trackartist INTEGER, FOREIGN KEY(trackartist) REFERENCES artist(artistid) ); } } {} | | < | | | > > > | < | < | | > > > | < | 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 | trackid INTEGER, trackname TEXT, trackartist INTEGER, FOREIGN KEY(trackartist) REFERENCES artist(artistid) ); } } {} do_execsql_test e_fkey-25.2 { PRAGMA foreign_keys = OFF; EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?; } { 0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SCAN TABLE track (~100000 rows)} } do_execsql_test e_fkey-25.3 { PRAGMA foreign_keys = ON; EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1; } { 0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SCAN TABLE track (~100000 rows)} } do_test e_fkey-25.4 { execsql { INSERT INTO artist VALUES(5, 'artist 5'); INSERT INTO artist VALUES(6, 'artist 6'); INSERT INTO artist VALUES(7, 'artist 7'); INSERT INTO track VALUES(1, 'track 1', 5); INSERT INTO track VALUES(2, 'track 2', 6); |
︙ | ︙ | |||
1089 1090 1091 1092 1093 1094 1095 | ); CREATE INDEX trackindex ON track(trackartist); } } {} do_test e_fkey-27.2 { eqp { INSERT INTO artist VALUES(?, ?) } } {} | | | | | | | < > | | | | | < > | 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 | ); CREATE INDEX trackindex ON track(trackartist); } } {} do_test e_fkey-27.2 { eqp { INSERT INTO artist VALUES(?, ?) } } {} do_execsql_test e_fkey-27.3 { EXPLAIN QUERY PLAN UPDATE artist SET artistid = ?, artistname = ? } { 0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} } do_execsql_test e_fkey-27.4 { EXPLAIN QUERY PLAN DELETE FROM artist } { 0 0 0 {SCAN TABLE artist (~1000000 rows)} 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)} } ########################################################################### ### SECTION 4.1: Composite Foreign Key Constraints ########################################################################### #------------------------------------------------------------------------- |
︙ | ︙ |
Added test/e_insert.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 | # 2010 September 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. # #*********************************************************************** # # The majority of this file implements tests to verify that the "testable # statements" in the lang_insert.html document are correct. # # Also, it contains tests to verify the statements in (the very short) # lang_replace.html. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Organization of tests: # # e_insert-0.*: Test the syntax diagram. # # e_insert-1.*: Test statements of the form "INSERT ... VALUES(...)". # # e_insert-2.*: Test statements of the form "INSERT ... SELECT ...". # # e_insert-3.*: Test statements of the form "INSERT ... DEFAULT VALUES". # # e_insert-4.*: Test statements regarding the conflict clause. # # e_insert-5.*: Test that the qualified table name and "DEFAULT VALUES" # syntaxes do not work in trigger bodies. # do_execsql_test e_insert-0.0 { CREATE TABLE a1(a, b); CREATE TABLE a2(a, b, c DEFAULT 'xyz'); CREATE TABLE a3(x DEFAULT 1.0, y DEFAULT 'string', z); CREATE TABLE a4(c UNIQUE, d); } {} proc do_insert_tests {args} { uplevel do_select_tests $args } # EVIDENCE-OF: R-41448-54465 -- syntax diagram insert-stmt # do_insert_tests e_insert-0 { 1 "INSERT INTO a1 DEFAULT VALUES" {} 2 "INSERT INTO main.a1 DEFAULT VALUES" {} 3 "INSERT OR ROLLBACK INTO main.a1 DEFAULT VALUES" {} 4 "INSERT OR ROLLBACK INTO a1 DEFAULT VALUES" {} 5 "INSERT OR ABORT INTO main.a1 DEFAULT VALUES" {} 6 "INSERT OR ABORT INTO a1 DEFAULT VALUES" {} 7 "INSERT OR REPLACE INTO main.a1 DEFAULT VALUES" {} 8 "INSERT OR REPLACE INTO a1 DEFAULT VALUES" {} 9 "INSERT OR FAIL INTO main.a1 DEFAULT VALUES" {} 10 "INSERT OR FAIL INTO a1 DEFAULT VALUES" {} 11 "INSERT OR FAIL INTO main.a1 DEFAULT VALUES" {} 12 "INSERT OR IGNORE INTO a1 DEFAULT VALUES" {} 13 "REPLACE INTO a1 DEFAULT VALUES" {} 14 "REPLACE INTO main.a1 DEFAULT VALUES" {} 15 "INSERT INTO a1 VALUES(1, 2)" {} 16 "INSERT INTO main.a1 VALUES(1, 2)" {} 17 "INSERT OR ROLLBACK INTO main.a1 VALUES(1, 2)" {} 18 "INSERT OR ROLLBACK INTO a1 VALUES(1, 2)" {} 19 "INSERT OR ABORT INTO main.a1 VALUES(1, 2)" {} 20 "INSERT OR ABORT INTO a1 VALUES(1, 2)" {} 21 "INSERT OR REPLACE INTO main.a1 VALUES(1, 2)" {} 22 "INSERT OR REPLACE INTO a1 VALUES(1, 2)" {} 23 "INSERT OR FAIL INTO main.a1 VALUES(1, 2)" {} 24 "INSERT OR FAIL INTO a1 VALUES(1, 2)" {} 25 "INSERT OR FAIL INTO main.a1 VALUES(1, 2)" {} 26 "INSERT OR IGNORE INTO a1 VALUES(1, 2)" {} 27 "REPLACE INTO a1 VALUES(1, 2)" {} 28 "REPLACE INTO main.a1 VALUES(1, 2)" {} 29 "INSERT INTO a1 (b, a) VALUES(1, 2)" {} 30 "INSERT INTO main.a1 (b, a) VALUES(1, 2)" {} 31 "INSERT OR ROLLBACK INTO main.a1 (b, a) VALUES(1, 2)" {} 32 "INSERT OR ROLLBACK INTO a1 (b, a) VALUES(1, 2)" {} 33 "INSERT OR ABORT INTO main.a1 (b, a) VALUES(1, 2)" {} 34 "INSERT OR ABORT INTO a1 (b, a) VALUES(1, 2)" {} 35 "INSERT OR REPLACE INTO main.a1 (b, a) VALUES(1, 2)" {} 36 "INSERT OR REPLACE INTO a1 (b, a) VALUES(1, 2)" {} 37 "INSERT OR FAIL INTO main.a1 (b, a) VALUES(1, 2)" {} 38 "INSERT OR FAIL INTO a1 (b, a) VALUES(1, 2)" {} 39 "INSERT OR FAIL INTO main.a1 (b, a) VALUES(1, 2)" {} 40 "INSERT OR IGNORE INTO a1 (b, a) VALUES(1, 2)" {} 41 "REPLACE INTO a1 (b, a) VALUES(1, 2)" {} 42 "REPLACE INTO main.a1 (b, a) VALUES(1, 2)" {} 43 "INSERT INTO a1 SELECT c, b FROM a2" {} 44 "INSERT INTO main.a1 SELECT c, b FROM a2" {} 45 "INSERT OR ROLLBACK INTO main.a1 SELECT c, b FROM a2" {} 46 "INSERT OR ROLLBACK INTO a1 SELECT c, b FROM a2" {} 47 "INSERT OR ABORT INTO main.a1 SELECT c, b FROM a2" {} 48 "INSERT OR ABORT INTO a1 SELECT c, b FROM a2" {} 49 "INSERT OR REPLACE INTO main.a1 SELECT c, b FROM a2" {} 50 "INSERT OR REPLACE INTO a1 SELECT c, b FROM a2" {} 51 "INSERT OR FAIL INTO main.a1 SELECT c, b FROM a2" {} 52 "INSERT OR FAIL INTO a1 SELECT c, b FROM a2" {} 53 "INSERT OR FAIL INTO main.a1 SELECT c, b FROM a2" {} 54 "INSERT OR IGNORE INTO a1 SELECT c, b FROM a2" {} 55 "REPLACE INTO a1 SELECT c, b FROM a2" {} 56 "REPLACE INTO main.a1 SELECT c, b FROM a2" {} 57 "INSERT INTO a1 (b, a) SELECT c, b FROM a2" {} 58 "INSERT INTO main.a1 (b, a) SELECT c, b FROM a2" {} 59 "INSERT OR ROLLBACK INTO main.a1 (b, a) SELECT c, b FROM a2" {} 60 "INSERT OR ROLLBACK INTO a1 (b, a) SELECT c, b FROM a2" {} 61 "INSERT OR ABORT INTO main.a1 (b, a) SELECT c, b FROM a2" {} 62 "INSERT OR ABORT INTO a1 (b, a) SELECT c, b FROM a2" {} 63 "INSERT OR REPLACE INTO main.a1 (b, a) SELECT c, b FROM a2" {} 64 "INSERT OR REPLACE INTO a1 (b, a) SELECT c, b FROM a2" {} 65 "INSERT OR FAIL INTO main.a1 (b, a) SELECT c, b FROM a2" {} 66 "INSERT OR FAIL INTO a1 (b, a) SELECT c, b FROM a2" {} 67 "INSERT OR FAIL INTO main.a1 (b, a) SELECT c, b FROM a2" {} 68 "INSERT OR IGNORE INTO a1 (b, a) SELECT c, b FROM a2" {} 69 "REPLACE INTO a1 (b, a) SELECT c, b FROM a2" {} 70 "REPLACE INTO main.a1 (b, a) SELECT c, b FROM a2" {} } delete_all_data # EVIDENCE-OF: R-20288-20462 The first form (with the "VALUES" keyword) # creates a single new row in an existing table. # do_insert_tests e_insert-1.1 { 0 "SELECT count(*) FROM a2" {0} 1a "INSERT INTO a2 VALUES(1, 2, 3)" {} 1b "SELECT count(*) FROM a2" {1} 2a "INSERT INTO a2(a, b) VALUES(1, 2)" {} 2b "SELECT count(*) FROM a2" {2} } # EVIDENCE-OF: R-36040-20870 If no column-list is specified then the # number of values must be the same as the number of columns in the # table. # # A test in the block above verifies that if the VALUES list has the # correct number of columns (for table a2, 3 columns) works. So these # tests just show that other values cause an error. # do_insert_tests e_insert-1.2 -error { table %s has %d columns but %d values were supplied } { 1 "INSERT INTO a2 VALUES(1)" {a2 3 1} 2 "INSERT INTO a2 VALUES(1,2)" {a2 3 2} 3 "INSERT INTO a2 VALUES(1,2,3,4)" {a2 3 4} 4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5} } # EVIDENCE-OF: R-52422-65517 In this case the result of evaluting the # left-most expression in the VALUES list is inserted into the left-most # column of the new row, and so on. # delete_all_data do_insert_tests e_insert-1.3 { 1a "INSERT INTO a2 VALUES(1, 2, 3)" {} 1b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {1 2 3} 2a "INSERT INTO a2 VALUES('abc', NULL, 3*3+1)" {} 2b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {abc {} 10} 3a "INSERT INTO a2 VALUES((SELECT count(*) FROM a2), 'x', 'y')" {} 3b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y} } # EVIDENCE-OF: R-62524-00361 If a column-list is specified, then the # number of values must match the number of specified columns. # do_insert_tests e_insert-1.4 -error { %d values for %d columns } { 1 "INSERT INTO a2(a, b, c) VALUES(1)" {1 3} 2 "INSERT INTO a2(a, b, c) VALUES(1,2)" {2 3} 3 "INSERT INTO a2(a, b, c) VALUES(1,2,3,4)" {4 3} 4 "INSERT INTO a2(a, b, c) VALUES(1,2,3,4,5)" {5 3} 5 "INSERT INTO a2(c, a) VALUES(1)" {1 2} 6 "INSERT INTO a2(c, a) VALUES(1,2,3)" {3 2} 7 "INSERT INTO a2(c, a) VALUES(1,2,3,4)" {4 2} 8 "INSERT INTO a2(c, a) VALUES(1,2,3,4,5)" {5 2} } # EVIDENCE-OF: R-07016-26442 Each of the named columns of the new row is # populated with the results of evaluating the corresponding VALUES # expression. # # EVIDENCE-OF: R-12183-43719 Table columns that do not appear in the # column list are populated with the default column value (specified as # part of the CREATE TABLE statement), or with NULL if no default value # is specified. # delete_all_data do_insert_tests e_insert-1.5 { 1a "INSERT INTO a2(b, c) VALUES('b', 'c')" {} 1b "SELECT * FROM a2" {{} b c} 2a "INSERT INTO a2(a, b) VALUES('a', 'b')" {} 2b "SELECT * FROM a2" {{} b c a b xyz} } # EVIDENCE-OF: R-52173-30215 A new entry is inserted into the table for # each row of data returned by executing the SELECT statement. # delete_all_data do_insert_tests e_insert-2.1 { 0 "SELECT count(*) FROM a1" {0} 1a "SELECT count(*) FROM (SELECT 1, 2)" {1} 1b "INSERT INTO a1 SELECT 1, 2" {} 1c "SELECT count(*) FROM a1" {1} 2a "SELECT count(*) FROM (SELECT b, a FROM a1)" {1} 2b "INSERT INTO a1 SELECT b, a FROM a1" {} 2c "SELECT count(*) FROM a1" {2} 3a "SELECT count(*) FROM (SELECT b, a FROM a1)" {2} 3b "INSERT INTO a1 SELECT b, a FROM a1" {} 3c "SELECT count(*) FROM a1" {4} 4a "SELECT count(*) FROM (SELECT b, a FROM a1)" {4} 4b "INSERT INTO a1 SELECT b, a FROM a1" {} 4c "SELECT count(*) FROM a1" {8} 4a "SELECT count(*) FROM (SELECT min(b), min(a) FROM a1)" {1} 4b "INSERT INTO a1 SELECT min(b), min(a) FROM a1" {} 4c "SELECT count(*) FROM a1" {9} } # EVIDENCE-OF: R-63614-47421 If a column-list is specified, the number # of columns in the result of the SELECT must be the same as the number # of items in the column-list. # do_insert_tests e_insert-2.2 -error { %d values for %d columns } { 1 "INSERT INTO a3(x, y) SELECT a, b, c FROM a2" {3 2} 2 "INSERT INTO a3(x, y) SELECT * FROM a2" {3 2} 3 "INSERT INTO a3(x, y) SELECT * FROM a2 CROSS JOIN a1" {5 2} 4 "INSERT INTO a3(x, y) SELECT * FROM a2 NATURAL JOIN a1" {3 2} 5 "INSERT INTO a3(x, y) SELECT a2.a FROM a2,a1" {1 2} 6 "INSERT INTO a3(z) SELECT a, b, c FROM a2" {3 1} 7 "INSERT INTO a3(z) SELECT * FROM a2" {3 1} 8 "INSERT INTO a3(z) SELECT * FROM a2 CROSS JOIN a1" {5 1} 9 "INSERT INTO a3(z) SELECT * FROM a2 NATURAL JOIN a1" {3 1} 10 "INSERT INTO a3(z) SELECT a1.* FROM a2,a1" {2 1} } # EVIDENCE-OF: R-58951-07798 Otherwise, if no column-list is specified, # the number of columns in the result of the SELECT must be the same as # the number of columns in the table. # do_insert_tests e_insert-2.3 -error { table %s has %d columns but %d values were supplied } { 1 "INSERT INTO a1 SELECT a, b, c FROM a2" {a1 2 3} 2 "INSERT INTO a1 SELECT * FROM a2" {a1 2 3} 3 "INSERT INTO a1 SELECT * FROM a2 CROSS JOIN a1" {a1 2 5} 4 "INSERT INTO a1 SELECT * FROM a2 NATURAL JOIN a1" {a1 2 3} 5 "INSERT INTO a1 SELECT a2.a FROM a2,a1" {a1 2 1} } # EVIDENCE-OF: R-31074-37730 Any SELECT statement, including compound # SELECTs and SELECT statements with ORDER BY and/or LIMIT clauses, may # be used in an INSERT statement of this form. # delete_all_data do_execsql_test e_insert-2.3.0 { INSERT INTO a1 VALUES('x', 'y'); } {} do_insert_tests e_insert-2.3 { 1 "INSERT INTO a1 SELECT a,b FROM a1 UNION SELECT b,a FROM a1 ORDER BY 1" {} 2 "INSERT INTO a1(b, a) SELECT * FROM a1 LIMIT 1" {} 3 "INSERT INTO a1 SELECT 'a'||a, 'b'||b FROM a1 LIMIT 2 OFFSET 1" {} 4 "INSERT INTO a1 SELECT * FROM a1 ORDER BY b, a" {} S "SELECT * FROM a1" { x y x y y x y x ax by ay bx ay bx ax by y x y x x y x y } } # EVIDENCE-OF: R-25149-22012 The INSERT ... DEFAULT VALUES statement # inserts a single new row into the named table. # delete_all_data do_insert_tests e_insert-3.1 { 1 "SELECT count(*) FROM a3" {0} 2a "INSERT INTO a3 DEFAULT VALUES" {} 2b "SELECT count(*) FROM a3" {1} } # EVIDENCE-OF: R-18927-01951 Each column of the new row is populated # with its default value, or with a NULL if no default value is # specified as part of the column definition in the CREATE TABLE # statement. # delete_all_data do_insert_tests e_insert-3.2 { 1.1 "INSERT INTO a3 DEFAULT VALUES" {} 1.2 "SELECT * FROM a3" {1.0 string {}} 2.1 "INSERT INTO a3 DEFAULT VALUES" {} 2.2 "SELECT * FROM a3" {1.0 string {} 1.0 string {}} 3.1 "INSERT INTO a2 DEFAULT VALUES" {} 3.2 "SELECT * FROM a2" {{} {} xyz} 4.1 "INSERT INTO a2 DEFAULT VALUES" {} 4.2 "SELECT * FROM a2" {{} {} xyz {} {} xyz} 5.1 "INSERT INTO a1 DEFAULT VALUES" {} 5.2 "SELECT * FROM a1" {{} {}} 6.1 "INSERT INTO a1 DEFAULT VALUES" {} 6.2 "SELECT * FROM a1" {{} {} {} {}} } # EVIDENCE-OF: R-46928-50290 The optional conflict-clause allows the # specification of an alternative constraint conflict resolution # algorithm to use during this one INSERT command. # # EVIDENCE-OF: R-23110-47146 the parser allows the use of the single # keyword REPLACE as an alias for "INSERT OR REPLACE". # # The two requirements above are tested by e_select-4.1.* and # e_select-4.2.*, respectively. # # EVIDENCE-OF: R-03421-22330 The REPLACE command is an alias for the # "INSERT OR REPLACE" variant of the INSERT command. # # This is a dup of R-23110-47146. Therefore it is also verified # by e_select-4.2.*. This requirement is the only one from # lang_replace.html. # do_execsql_test e_insert-4.1.0 { INSERT INTO a4 VALUES(1, 'a'); INSERT INTO a4 VALUES(2, 'a'); INSERT INTO a4 VALUES(3, 'a'); } {} foreach {tn sql error ac data } { 1.1 "INSERT INTO a4 VALUES(2,'b')" {column c is not unique} 1 {1 a 2 a 3 a} 1.2 "INSERT OR REPLACE INTO a4 VALUES(2, 'b')" {} 1 {1 a 3 a 2 b} 1.3 "INSERT OR IGNORE INTO a4 VALUES(3, 'c')" {} 1 {1 a 3 a 2 b} 1.4 "BEGIN" {} 0 {1 a 3 a 2 b} 1.5 "INSERT INTO a4 VALUES(1, 'd')" {column c is not unique} 0 {1 a 3 a 2 b} 1.6 "INSERT OR ABORT INTO a4 VALUES(1, 'd')" {column c is not unique} 0 {1 a 3 a 2 b} 1.7 "INSERT OR ROLLBACK INTO a4 VALUES(1, 'd')" {column c is not unique} 1 {1 a 3 a 2 b} 1.8 "INSERT INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'" {column c is not unique} 1 {1 a 3 a 2 b} 1.9 "INSERT OR FAIL INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'" {column c is not unique} 1 {1 a 3 a 2 b 4 e} 2.1 "INSERT INTO a4 VALUES(2,'f')" {column c is not unique} 1 {1 a 3 a 2 b 4 e} 2.2 "REPLACE INTO a4 VALUES(2, 'f')" {} 1 {1 a 3 a 4 e 2 f} } { do_catchsql_test e_insert-4.1.$tn.1 $sql [list [expr {$error!=""}] $error] do_execsql_test e_insert-4.1.$tn.2 {SELECT * FROM a4} [list {*}$data] do_test e_insert-4.1.$tn.3 {sqlite3_get_autocommit db} $ac } # EVIDENCE-OF: R-64196-02418 The optional "database-name." prefix on the # table-name is support for top-level INSERT statements only. # # EVIDENCE-OF: R-05731-00924 The table name must be unqualified for # INSERT statements that occur within CREATE TRIGGER statements. # set err {1 {qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers}} do_catchsql_test e_insert-5.1.1 { CREATE TRIGGER AFTER UPDATE ON a1 BEGIN INSERT INTO main.a4 VALUES(new.a, new.b); END; } $err do_catchsql_test e_insert-5.1.2 { CREATE TEMP TABLE IF NOT EXISTS tmptable(a, b); CREATE TRIGGER AFTER DELETE ON a3 BEGIN INSERT INTO temp.tmptable VALUES(1, 2); END; } $err # EVIDENCE-OF: R-15888-36326 Similarly, the "DEFAULT VALUES" form of the # INSERT statement is supported for top-level INSERT statements only and # not for INSERT statements within triggers. # do_catchsql_test e_insert-5.2.1 { CREATE TRIGGER AFTER UPDATE ON a1 BEGIN INSERT INTO a4 DEFAULT VALUES; END; } {1 {near "DEFAULT": syntax error}} delete_all_data finish_test |
Added test/e_reindex.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 | # 2010 September 24 # # 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 to verify that the "testable statements" in # the lang_reindex.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl proc do_reindex_tests {args} { uplevel do_select_tests $args } do_execsql_test e_reindex-0.0 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); CREATE INDEX i2 ON t1(b, a); } {} # EVIDENCE-OF: R-57021-15304 -- syntax diagram reindex-stmt # do_reindex_tests e_reindex-0.1 { 1 "REINDEX" {} 2 "REINDEX nocase" {} 3 "REINDEX binary" {} 4 "REINDEX t1" {} 5 "REINDEX main.t1" {} 4 "REINDEX i1" {} 5 "REINDEX main.i1" {} } # EVIDENCE-OF: R-52173-44778 The REINDEX command is used to delete and # recreate indices from scratch. # # Test this by corrupting some database indexes, running REINDEX, and # observing that the corruption is gone. # do_execsql_test e_reindex-1.1 { INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); PRAGMA writable_schema = 1; UPDATE sqlite_master SET sql = '-- ' || sql WHERE type = 'index'; } {} db close sqlite3 db test.db do_execsql_test e_reindex-1.2 { DELETE FROM t1 WHERE a = 3; INSERT INTO t1 VALUES(7, 8); INSERT INTO t1 VALUES(9, 10); PRAGMA writable_schema = 1; UPDATE sqlite_master SET sql = substr(sql, 4) WHERE type = 'index'; } {} db close sqlite3 db test.db do_execsql_test e_reindex-1.3 { PRAGMA integrity_check; } [list \ {rowid 4 missing from index i2} \ {rowid 4 missing from index i1} \ {rowid 5 missing from index i2} \ {rowid 5 missing from index i1} \ {wrong # of entries in index i2} \ {wrong # of entries in index i1} ] do_execsql_test e_reindex-1.4 { REINDEX; PRAGMA integrity_check; } {ok} #------------------------------------------------------------------------- # The remaining tests in this file focus on testing that the REINDEX # command reindexes the correct subset of the indexes in the database. # They all use the following dataset. # db close forcedelete test.db2 forcedelete test.db sqlite3 db test.db proc sort_by_length {lhs rhs} { set res [expr {[string length $lhs] - [string length $rhs]}] if {$res!=0} {return $res} return [string compare $lhs $rhs] } array set V {one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight 8} proc sort_by_value {lhs rhs} { global V set res [expr {$V($lhs) - $V(rhs)}] if {$res!=0} {return $res} return [string compare $lhs $rhs] } db collate collA sort_by_length db collate collB sort_by_value set BY(length) {one six two five four eight seven three} set BY(value) {one two three four five six seven eight} do_execsql_test e_reindex-2.0 { ATTACH 'test.db2' AS aux; CREATE TABLE t1(x); CREATE INDEX i1_a ON t1(x COLLATE collA); CREATE INDEX i1_b ON t1(x COLLATE collB); INSERT INTO t1 VALUES('one'); INSERT INTO t1 VALUES('two'); INSERT INTO t1 VALUES('three'); INSERT INTO t1 VALUES('four'); INSERT INTO t1 VALUES('five'); INSERT INTO t1 VALUES('six'); INSERT INTO t1 VALUES('seven'); INSERT INTO t1 VALUES('eight'); CREATE TABLE t2(x); CREATE INDEX i2_a ON t2(x COLLATE collA); CREATE INDEX i2_b ON t2(x COLLATE collB); INSERT INTO t2 SELECT x FROM t1; CREATE TABLE aux.t1(x); CREATE INDEX aux.i1_a ON t1(x COLLATE collA); CREATE INDEX aux.i1_b ON t1(x COLLATE collB); INSERT INTO aux.t1 SELECT x FROM main.t1; } {} proc test_index {tn tbl collation expected} { set sql "SELECT x FROM $tbl ORDER BY x COLLATE $collation" uplevel do_execsql_test e_reindex-2.$tn [list $sql] [list $::BY($expected)] } proc set_collations {a b} { db collate collA "sort_by_$a" db collate collB "sort_by_$b" } test_index 1.1 t1 collA length test_index 1.2 t1 collB value test_index 1.3 t2 collA length test_index 1.4 t2 collB value test_index 1.5 aux.t1 collA length test_index 1.6 aux.t1 collB value # EVIDENCE-OF: R-47362-07898 If the REINDEX keyword is not followed by a # collation-sequence or database object identifier, then all indices in # all attached databases are rebuilt. # set_collations value length do_execsql_test e_reindex-2.2.1 "REINDEX" {} test_index 2.2 t1 collA value test_index 2.3 t1 collB length test_index 2.4 t2 collA value test_index 2.5 t2 collB length test_index 2.6 aux.t1 collA value test_index 2.7 aux.t1 collB length # EVIDENCE-OF: R-45878-07697 If the REINDEX keyword is followed by a # collation-sequence name, then all indices in all attached databases # that use the named collation sequences are recreated. # set_collations length value do_execsql_test e_reindex-2.3.1 "REINDEX collA" {} test_index 3.2 t1 collA length test_index 3.3 t1 collB length test_index 3.4 t2 collA length test_index 3.5 t2 collB length test_index 3.6 aux.t1 collA length test_index 3.7 aux.t1 collB length do_execsql_test e_reindex-2.3.8 "REINDEX collB" {} test_index 3.9 t1 collA length test_index 3.10 t1 collB value test_index 3.11 t2 collA length test_index 3.12 t2 collB value test_index 3.13 aux.t1 collA length test_index 3.14 aux.t1 collB value # EVIDENCE-OF: R-49616-30196 Or, if the argument attached to the REINDEX # identifies a specific database table, then all indices attached to the # database table are rebuilt. # set_collations value length do_execsql_test e_reindex-2.4.1 "REINDEX t1" {} test_index 4.2 t1 collA value test_index 4.3 t1 collB length test_index 4.4 t2 collA length test_index 4.5 t2 collB value test_index 4.6 aux.t1 collA length test_index 4.7 aux.t1 collB value do_execsql_test e_reindex-2.4.8 "REINDEX aux.t1" {} test_index 4.9 t1 collA value test_index 4.10 t1 collB length test_index 4.11 t2 collA length test_index 4.12 t2 collB value test_index 4.13 aux.t1 collA value test_index 4.14 aux.t1 collB length do_execsql_test e_reindex-2.4.15 "REINDEX t2" {} test_index 4.16 t1 collA value test_index 4.17 t1 collB length test_index 4.18 t2 collA value test_index 4.19 t2 collB length test_index 4.20 aux.t1 collA value test_index 4.21 aux.t1 collB length # EVIDENCE-OF: R-58823-28748 If it identifies a specific database index, # then just that index is recreated. # set_collations length value do_execsql_test e_reindex-2.5.1 "REINDEX i1_a" {} test_index 5.2 t1 collA length test_index 5.3 t1 collB length test_index 5.4 t2 collA value test_index 5.5 t2 collB length test_index 5.6 aux.t1 collA value test_index 5.7 aux.t1 collB length do_execsql_test e_reindex-2.5.8 "REINDEX i2_b" {} test_index 5.9 t1 collA length test_index 5.10 t1 collB length test_index 5.11 t2 collA value test_index 5.12 t2 collB value test_index 5.13 aux.t1 collA value test_index 5.14 aux.t1 collB length do_execsql_test e_reindex-2.5.15 "REINDEX aux.i1_b" {} test_index 5.16 t1 collA length test_index 5.17 t1 collB length test_index 5.18 t2 collA value test_index 5.19 t2 collB value test_index 5.20 aux.t1 collA value test_index 5.21 aux.t1 collB value do_execsql_test e_reindex-2.5.22 "REINDEX i1_b" {} test_index 5.23 t1 collA length test_index 5.24 t1 collB value test_index 5.25 t2 collA value test_index 5.26 t2 collB value test_index 5.27 aux.t1 collA value test_index 5.28 aux.t1 collB value do_execsql_test e_reindex-2.5.29 "REINDEX i2_a" {} test_index 5.30 t1 collA length test_index 5.31 t1 collB value test_index 5.32 t2 collA length test_index 5.33 t2 collB value test_index 5.34 aux.t1 collA value test_index 5.35 aux.t1 collB value do_execsql_test e_reindex-2.5.36 "REINDEX aux.i1_a" {} test_index 5.37 t1 collA length test_index 5.38 t1 collB value test_index 5.39 t2 collA length test_index 5.40 t2 collB value test_index 5.41 aux.t1 collA length test_index 5.42 aux.t1 collB value # EVIDENCE-OF: R-15639-02023 If no database-name is specified and there # exists both a table or index and a collation sequence of the specified # name, SQLite interprets this as a request to rebuild the indices that # use the named collation sequence. # set_collations value length do_execsql_test e_reindex-2.6.0 { CREATE TABLE collA(x); CREATE INDEX icolla_a ON collA(x COLLATE collA); CREATE INDEX icolla_b ON collA(x COLLATE collB); INSERT INTO collA SELECT x FROM t1; } {} test_index 6.1 collA collA value test_index 6.2 collA collB length set_collations length value do_execsql_test e_reindex-2.6.3 "REINDEX collA" {} test_index 6.4 collA collA length test_index 6.5 collA collB length do_execsql_test e_reindex-2.6.3 "REINDEX main.collA" {} test_index 6.4 collA collA length test_index 6.5 collA collB value set_collations value length do_execsql_test e_reindex-2.6.6 "REINDEX main.collA" {} test_index 6.7 collA collA value test_index 6.8 collA collB length finish_test |
Added test/e_select.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 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 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 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 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 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 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 1627 1628 1629 1630 1631 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 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 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 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 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 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 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 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 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 | # 2010 July 16 # # 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 to verify that the "testable statements" in # the lang_select.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_execsql_test e_select-1.0 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('a', 'one'); INSERT INTO t1 VALUES('b', 'two'); INSERT INTO t1 VALUES('c', 'three'); CREATE TABLE t2(a, b); INSERT INTO t2 VALUES('a', 'I'); INSERT INTO t2 VALUES('b', 'II'); INSERT INTO t2 VALUES('c', 'III'); CREATE TABLE t3(a, c); INSERT INTO t3 VALUES('a', 1); INSERT INTO t3 VALUES('b', 2); CREATE TABLE t4(a, c); INSERT INTO t4 VALUES('a', NULL); INSERT INTO t4 VALUES('b', 2); } {} set t1_cross_t2 [list \ a one a I a one b II \ a one c III b two a I \ b two b II b two c III \ c three a I c three b II \ c three c III \ ] set t1_cross_t1 [list \ a one a one a one b two \ a one c three b two a one \ b two b two b two c three \ c three a one c three b two \ c three c three \ ] # This proc is a specialized version of [do_execsql_test]. # # The second argument to this proc must be a SELECT statement that # features a cross join of some time. Instead of the usual ",", # "CROSS JOIN" or "INNER JOIN" join-op, the string %JOIN% must be # substituted. # # This test runs the SELECT three times - once with: # # * s/%JOIN%/,/ # * s/%JOIN%/JOIN/ # * s/%JOIN%/INNER JOIN/ # * s/%JOIN%/CROSS JOIN/ # # and checks that each time the results of the SELECT are $res. # proc do_join_test {tn select res} { foreach {tn2 joinop} [list 1 , 2 "CROSS JOIN" 3 "INNER JOIN"] { set S [string map [list %JOIN% $joinop] $select] uplevel do_execsql_test $tn.$tn2 [list $S] [list $res] } } #------------------------------------------------------------------------- # The following tests check that all paths on the syntax diagrams on # the lang_select.html page may be taken. # # EVIDENCE-OF: R-18428-22111 -- syntax diagram join-constraint # do_join_test e_select-0.1.1 { SELECT count(*) FROM t1 %JOIN% t2 ON (t1.a=t2.a) } {3} do_join_test e_select-0.1.2 { SELECT count(*) FROM t1 %JOIN% t2 USING (a) } {3} do_join_test e_select-0.1.3 { SELECT count(*) FROM t1 %JOIN% t2 } {9} do_catchsql_test e_select-0.1.4 { SELECT count(*) FROM t1, t2 ON (t1.a=t2.a) USING (a) } {1 {cannot have both ON and USING clauses in the same join}} do_catchsql_test e_select-0.1.5 { SELECT count(*) FROM t1, t2 USING (a) ON (t1.a=t2.a) } {1 {near "ON": syntax error}} # EVIDENCE-OF: R-44854-11739 -- syntax diagram select-core # # 0: SELECT ... # 1: SELECT DISTINCT ... # 2: SELECT ALL ... # # 0: No FROM clause # 1: Has FROM clause # # 0: No WHERE clause # 1: Has WHERE clause # # 0: No GROUP BY clause # 1: Has GROUP BY clause # 2: Has GROUP BY and HAVING clauses # do_select_tests e_select-0.2 { 0000.1 "SELECT 1, 2, 3 " {1 2 3} 1000.1 "SELECT DISTINCT 1, 2, 3 " {1 2 3} 2000.1 "SELECT ALL 1, 2, 3 " {1 2 3} 0100.1 "SELECT a, b, a||b FROM t1 " { a one aone b two btwo c three cthree } 1100.1 "SELECT DISTINCT a, b, a||b FROM t1 " { a one aone b two btwo c three cthree } 1200.1 "SELECT ALL a, b, a||b FROM t1 " { a one aone b two btwo c three cthree } 0010.1 "SELECT 1, 2, 3 WHERE 1 " {1 2 3} 0010.2 "SELECT 1, 2, 3 WHERE 0 " {} 0010.3 "SELECT 1, 2, 3 WHERE NULL " {} 1010.1 "SELECT DISTINCT 1, 2, 3 WHERE 1 " {1 2 3} 2010.1 "SELECT ALL 1, 2, 3 WHERE 1 " {1 2 3} 0110.1 "SELECT a, b, a||b FROM t1 WHERE a!='x' " { a one aone b two btwo c three cthree } 0110.2 "SELECT a, b, a||b FROM t1 WHERE a=='x'" {} 1110.1 "SELECT DISTINCT a, b, a||b FROM t1 WHERE a!='x' " { a one aone b two btwo c three cthree } 2110.0 "SELECT ALL a, b, a||b FROM t1 WHERE a=='x'" {} 0001.1 "SELECT 1, 2, 3 GROUP BY 2" {1 2 3} 0002.1 "SELECT 1, 2, 3 GROUP BY 2 HAVING count(*)=1" {1 2 3} 0002.2 "SELECT 1, 2, 3 GROUP BY 2 HAVING count(*)>1" {} 1001.1 "SELECT DISTINCT 1, 2, 3 GROUP BY 2" {1 2 3} 1002.1 "SELECT DISTINCT 1, 2, 3 GROUP BY 2 HAVING count(*)=1" {1 2 3} 1002.2 "SELECT DISTINCT 1, 2, 3 GROUP BY 2 HAVING count(*)>1" {} 2001.1 "SELECT ALL 1, 2, 3 GROUP BY 2" {1 2 3} 2002.1 "SELECT ALL 1, 2, 3 GROUP BY 2 HAVING count(*)=1" {1 2 3} 2002.2 "SELECT ALL 1, 2, 3 GROUP BY 2 HAVING count(*)>1" {} 0101.1 "SELECT count(*), max(a) FROM t1 GROUP BY b" {1 a 1 c 1 b} 0102.1 "SELECT count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=1" { 1 a 1 c 1 b } 0102.2 "SELECT count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=2" { } 1101.1 "SELECT DISTINCT count(*), max(a) FROM t1 GROUP BY b" {1 a 1 c 1 b} 1102.1 "SELECT DISTINCT count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=1" { 1 a 1 c 1 b } 1102.2 "SELECT DISTINCT count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=2" { } 2101.1 "SELECT ALL count(*), max(a) FROM t1 GROUP BY b" {1 a 1 c 1 b} 2102.1 "SELECT ALL count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=1" { 1 a 1 c 1 b } 2102.2 "SELECT ALL count(*), max(a) FROM t1 GROUP BY b HAVING count(*)=2" { } 0011.1 "SELECT 1, 2, 3 WHERE 1 GROUP BY 2" {1 2 3} 0012.1 "SELECT 1, 2, 3 WHERE 0 GROUP BY 2 HAVING count(*)=1" {} 0012.2 "SELECT 1, 2, 3 WHERE 0 GROUP BY 2 HAVING count(*)>1" {} 1011.1 "SELECT DISTINCT 1, 2, 3 WHERE 0 GROUP BY 2" {} 1012.1 "SELECT DISTINCT 1, 2, 3 WHERE 1 GROUP BY 2 HAVING count(*)=1" {1 2 3} 1012.2 "SELECT DISTINCT 1, 2, 3 WHERE NULL GROUP BY 2 HAVING count(*)>1" {} 2011.1 "SELECT ALL 1, 2, 3 WHERE 1 GROUP BY 2" {1 2 3} 2012.1 "SELECT ALL 1, 2, 3 WHERE 0 GROUP BY 2 HAVING count(*)=1" {} 2012.2 "SELECT ALL 1, 2, 3 WHERE 'abc' GROUP BY 2 HAVING count(*)>1" {} 0111.1 "SELECT count(*), max(a) FROM t1 WHERE a='a' GROUP BY b" {1 a} 0112.1 "SELECT count(*), max(a) FROM t1 WHERE a='c' GROUP BY b HAVING count(*)=1" {1 c} 0112.2 "SELECT count(*), max(a) FROM t1 WHERE 0 GROUP BY b HAVING count(*)=2" { } 1111.1 "SELECT DISTINCT count(*), max(a) FROM t1 WHERE a<'c' GROUP BY b" {1 a 1 b} 1112.1 "SELECT DISTINCT count(*), max(a) FROM t1 WHERE a>'a' GROUP BY b HAVING count(*)=1" { 1 c 1 b } 1112.2 "SELECT DISTINCT count(*), max(a) FROM t1 WHERE 0 GROUP BY b HAVING count(*)=2" { } 2111.1 "SELECT ALL count(*), max(a) FROM t1 WHERE b>'one' GROUP BY b" {1 c 1 b} 2112.1 "SELECT ALL count(*), max(a) FROM t1 WHERE a!='b' GROUP BY b HAVING count(*)=1" { 1 a 1 c } 2112.2 "SELECT ALL count(*), max(a) FROM t1 WHERE 0 GROUP BY b HAVING count(*)=2" { } } # EVIDENCE-OF: R-23316-20169 -- syntax diagram result-column # do_select_tests e_select-0.3 { 1 "SELECT * FROM t1" {a one b two c three} 2 "SELECT t1.* FROM t1" {a one b two c three} 3 "SELECT 'x'||a||'x' FROM t1" {xax xbx xcx} 4 "SELECT 'x'||a||'x' alias FROM t1" {xax xbx xcx} 5 "SELECT 'x'||a||'x' AS alias FROM t1" {xax xbx xcx} } # EVIDENCE-OF: R-41233-21397 -- syntax diagram join-source # # EVIDENCE-OF: R-45040-11121 -- syntax diagram join-op # do_select_tests e_select-0.4 { 1 "SELECT t1.rowid FROM t1" {1 2 3} 2 "SELECT t1.rowid FROM t1,t2" {1 1 1 2 2 2 3 3 3} 3 "SELECT t1.rowid FROM t1,t2,t3" {1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3} 4 "SELECT t1.rowid FROM t1" {1 2 3} 5 "SELECT t1.rowid FROM t1 JOIN t2" {1 1 1 2 2 2 3 3 3} 6 "SELECT t1.rowid FROM t1 JOIN t2 JOIN t3" {1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3} 7 "SELECT t1.rowid FROM t1 NATURAL JOIN t3" {1 2} 8 "SELECT t1.rowid FROM t1 NATURAL LEFT OUTER JOIN t3" {1 2 3} 9 "SELECT t1.rowid FROM t1 NATURAL LEFT JOIN t3" {1 2 3} 10 "SELECT t1.rowid FROM t1 NATURAL INNER JOIN t3" {1 2} 11 "SELECT t1.rowid FROM t1 NATURAL CROSS JOIN t3" {1 2} 12 "SELECT t1.rowid FROM t1 JOIN t3" {1 1 2 2 3 3} 13 "SELECT t1.rowid FROM t1 LEFT OUTER JOIN t3" {1 1 2 2 3 3} 14 "SELECT t1.rowid FROM t1 LEFT JOIN t3" {1 1 2 2 3 3} 15 "SELECT t1.rowid FROM t1 INNER JOIN t3" {1 1 2 2 3 3} 16 "SELECT t1.rowid FROM t1 CROSS JOIN t3" {1 1 2 2 3 3} } # EVIDENCE-OF: R-56911-63533 -- syntax diagram compound-operator # do_select_tests e_select-0.5 { 1 "SELECT rowid FROM t1 UNION ALL SELECT rowid+2 FROM t4" {1 2 3 3 4} 2 "SELECT rowid FROM t1 UNION SELECT rowid+2 FROM t4" {1 2 3 4} 3 "SELECT rowid FROM t1 INTERSECT SELECT rowid+2 FROM t4" {3} 4 "SELECT rowid FROM t1 EXCEPT SELECT rowid+2 FROM t4" {1 2} } # EVIDENCE-OF: R-60388-27458 -- syntax diagram ordering-term # do_select_tests e_select-0.6 { 1 "SELECT b||a FROM t1 ORDER BY b||a" {onea threec twob} 2 "SELECT b||a FROM t1 ORDER BY (b||a) COLLATE nocase" {onea threec twob} 3 "SELECT b||a FROM t1 ORDER BY (b||a) ASC" {onea threec twob} 4 "SELECT b||a FROM t1 ORDER BY (b||a) DESC" {twob threec onea} } # EVIDENCE-OF: R-36494-33519 -- syntax diagram select-stmt # do_select_tests e_select-0.7 { 1 "SELECT * FROM t1" {a one b two c three} 2 "SELECT * FROM t1 ORDER BY b" {a one c three b two} 3 "SELECT * FROM t1 ORDER BY b, a" {a one c three b two} 4 "SELECT * FROM t1 LIMIT 10" {a one b two c three} 5 "SELECT * FROM t1 LIMIT 10 OFFSET 5" {} 6 "SELECT * FROM t1 LIMIT 10, 5" {} 7 "SELECT * FROM t1 ORDER BY a LIMIT 10" {a one b two c three} 8 "SELECT * FROM t1 ORDER BY b LIMIT 10 OFFSET 5" {} 9 "SELECT * FROM t1 ORDER BY a,b LIMIT 10, 5" {} 10 "SELECT * FROM t1 UNION SELECT b, a FROM t1" {a one b two c three one a three c two b} 11 "SELECT * FROM t1 UNION SELECT b, a FROM t1 ORDER BY b" {one a two b three c a one c three b two} 12 "SELECT * FROM t1 UNION SELECT b, a FROM t1 ORDER BY b, a" {one a two b three c a one c three b two} 13 "SELECT * FROM t1 UNION SELECT b, a FROM t1 LIMIT 10" {a one b two c three one a three c two b} 14 "SELECT * FROM t1 UNION SELECT b, a FROM t1 LIMIT 10 OFFSET 5" {two b} 15 "SELECT * FROM t1 UNION SELECT b, a FROM t1 LIMIT 10, 5" {} 16 "SELECT * FROM t1 UNION SELECT b, a FROM t1 ORDER BY a LIMIT 10" {a one b two c three one a three c two b} 17 "SELECT * FROM t1 UNION SELECT b, a FROM t1 ORDER BY b LIMIT 10 OFFSET 5" {b two} 18 "SELECT * FROM t1 UNION SELECT b, a FROM t1 ORDER BY a,b LIMIT 10, 5" {} } #------------------------------------------------------------------------- # The following tests focus on FROM clause (join) processing. # # EVIDENCE-OF: R-16074-54196 If the FROM clause is omitted from a simple # SELECT statement, then the input data is implicitly a single row zero # columns wide # do_select_tests e_select-1.1 { 1 "SELECT 'abc'" {abc} 2 "SELECT 'abc' WHERE NULL" {} 3 "SELECT NULL" {{}} 4 "SELECT count(*)" {1} 5 "SELECT count(*) WHERE 0" {0} 6 "SELECT count(*) WHERE 1" {1} } # EVIDENCE-OF: R-48114-33255 If there is only a single table in the # join-source following the FROM clause, then the input data used by the # SELECT statement is the contents of the named table. # # The results of the SELECT queries suggest that they are operating on the # contents of the table 'xx'. # do_execsql_test e_select-1.2.0 { CREATE TABLE xx(x, y); INSERT INTO xx VALUES('IiJlsIPepMuAhU', X'10B00B897A15BAA02E3F98DCE8F2'); INSERT INTO xx VALUES(NULL, -16.87); INSERT INTO xx VALUES(-17.89, 'linguistically'); } {} do_select_tests e_select-1.2 { 1 "SELECT quote(x), quote(y) FROM xx" { 'IiJlsIPepMuAhU' X'10B00B897A15BAA02E3F98DCE8F2' NULL -16.87 -17.89 'linguistically' } 2 "SELECT count(*), count(x), count(y) FROM xx" {3 2 3} 3 "SELECT sum(x), sum(y) FROM xx" {-17.89 -16.87} } # EVIDENCE-OF: R-23593-12456 If there is more than one table specified # as part of the join-source following the FROM keyword, then the # contents of each named table are joined into a single dataset for the # simple SELECT statement to operate on. # # There are more detailed tests for subsequent requirements that add # more detail to this idea. We just add a single test that shows that # data is coming from each of the three tables following the FROM clause # here to show that the statement, vague as it is, is not incorrect. # do_select_tests e_select-1.3 { 1 "SELECT * FROM t1, t2, t3" { a one a I a 1 a one a I b 2 a one b II a 1 a one b II b 2 a one c III a 1 a one c III b 2 b two a I a 1 b two a I b 2 b two b II a 1 b two b II b 2 b two c III a 1 b two c III b 2 c three a I a 1 c three a I b 2 c three b II a 1 c three b II b 2 c three c III a 1 c three c III b 2 } } # # The following block of tests - e_select-1.4.* - test that the description # of cartesian joins in the SELECT documentation is consistent with SQLite. # In doing so, we test the following three requirements as a side-effect: # # EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER # JOIN", "JOIN" or a comma (",") and there is no ON or USING clause, # then the result of the join is simply the cartesian product of the # left and right-hand datasets. # # The tests are built on this assertion. Really, they test that the output # of a CROSS JOIN, JOIN, INNER JOIN or "," join matches the expected result # of calculating the cartesian product of the left and right-hand datasets. # # EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER # JOIN", "JOIN" and "," join operators. # # EVIDENCE-OF: R-07544-24155 The "CROSS JOIN" join operator produces the # same data as the "INNER JOIN", "JOIN" and "," operators # # All tests are run 4 times, with the only difference in each run being # which of the 4 equivalent cartesian product join operators are used. # Since the output data is the same in all cases, we consider that this # qualifies as testing the two statements above. # do_execsql_test e_select-1.4.0 { CREATE TABLE x1(a, b); CREATE TABLE x2(c, d, e); CREATE TABLE x3(f, g, h, i); -- x1: 3 rows, 2 columns INSERT INTO x1 VALUES(24, 'converging'); INSERT INTO x1 VALUES(NULL, X'CB71'); INSERT INTO x1 VALUES('blonds', 'proprietary'); -- x2: 2 rows, 3 columns INSERT INTO x2 VALUES(-60.06, NULL, NULL); INSERT INTO x2 VALUES(-58, NULL, 1.21); -- x3: 5 rows, 4 columns INSERT INTO x3 VALUES(-39.24, NULL, 'encompass', -1); INSERT INTO x3 VALUES('presenting', 51, 'reformation', 'dignified'); INSERT INTO x3 VALUES('conducting', -87.24, 37.56, NULL); INSERT INTO x3 VALUES('coldest', -96, 'dramatists', 82.3); INSERT INTO x3 VALUES('alerting', NULL, -93.79, NULL); } {} # EVIDENCE-OF: R-59089-25828 The columns of the cartesian product # dataset are, in order, all the columns of the left-hand dataset # followed by all the columns of the right-hand dataset. # do_join_test e_select-1.4.1.1 { SELECT * FROM x1 %JOIN% x2 LIMIT 1 } [concat {24 converging} {-60.06 {} {}}] do_join_test e_select-1.4.1.2 { SELECT * FROM x2 %JOIN% x1 LIMIT 1 } [concat {-60.06 {} {}} {24 converging}] do_join_test e_select-1.4.1.3 { SELECT * FROM x3 %JOIN% x2 LIMIT 1 } [concat {-39.24 {} encompass -1} {-60.06 {} {}}] do_join_test e_select-1.4.1.4 { SELECT * FROM x2 %JOIN% x3 LIMIT 1 } [concat {-60.06 {} {}} {-39.24 {} encompass -1}] # EVIDENCE-OF: R-44414-54710 There is a row in the cartesian product # dataset formed by combining each unique combination of a row from the # left-hand and right-hand datasets. # do_join_test e_select-1.4.2.1 { SELECT * FROM x2 %JOIN% x3 } [list -60.06 {} {} -39.24 {} encompass -1 \ -60.06 {} {} presenting 51 reformation dignified \ -60.06 {} {} conducting -87.24 37.56 {} \ -60.06 {} {} coldest -96 dramatists 82.3 \ -60.06 {} {} alerting {} -93.79 {} \ -58 {} 1.21 -39.24 {} encompass -1 \ -58 {} 1.21 presenting 51 reformation dignified \ -58 {} 1.21 conducting -87.24 37.56 {} \ -58 {} 1.21 coldest -96 dramatists 82.3 \ -58 {} 1.21 alerting {} -93.79 {} \ ] # TODO: Come back and add a few more like the above. # EVIDENCE-OF: R-20659-43267 In other words, if the left-hand dataset # consists of Nlhs rows of Mlhs columns, and the right-hand dataset of # Nrhs rows of Mrhs columns, then the cartesian product is a dataset of # Nlhs.Nrhs rows, each containing Mlhs+Mrhs columns. # # x1, x2 (Nlhs=3, Nrhs=2) (Mlhs=2, Mrhs=3) do_join_test e_select-1.4.3.1 { SELECT count(*) FROM x1 %JOIN% x2 } [expr 3*2] do_test e_select-1.4.3.2 { expr {[llength [execsql {SELECT * FROM x1, x2}]] / 6} } [expr 2+3] # x2, x3 (Nlhs=2, Nrhs=5) (Mlhs=3, Mrhs=4) do_join_test e_select-1.4.3.3 { SELECT count(*) FROM x2 %JOIN% x3 } [expr 2*5] do_test e_select-1.4.3.4 { expr {[llength [execsql {SELECT * FROM x2 JOIN x3}]] / 10} } [expr 3+4] # x3, x1 (Nlhs=5, Nrhs=3) (Mlhs=4, Mrhs=2) do_join_test e_select-1.4.3.5 { SELECT count(*) FROM x3 %JOIN% x1 } [expr 5*3] do_test e_select-1.4.3.6 { expr {[llength [execsql {SELECT * FROM x3 CROSS JOIN x1}]] / 15} } [expr 4+2] # x3, x3 (Nlhs=5, Nrhs=5) (Mlhs=4, Mrhs=4) do_join_test e_select-1.4.3.7 { SELECT count(*) FROM x3 %JOIN% x3 } [expr 5*5] do_test e_select-1.4.3.8 { expr {[llength [execsql {SELECT * FROM x3 INNER JOIN x3 AS x4}]] / 25} } [expr 4+4] # Some extra cartesian product tests using tables t1 and t2. # do_execsql_test e_select-1.4.4.1 { SELECT * FROM t1, t2 } $t1_cross_t2 do_execsql_test e_select-1.4.4.2 { SELECT * FROM t1 AS x, t1 AS y} $t1_cross_t1 do_select_tests e_select-1.4.5 [list \ 1 { SELECT * FROM t1 CROSS JOIN t2 } $t1_cross_t2 \ 2 { SELECT * FROM t1 AS y CROSS JOIN t1 AS x } $t1_cross_t1 \ 3 { SELECT * FROM t1 INNER JOIN t2 } $t1_cross_t2 \ 4 { SELECT * FROM t1 AS y INNER JOIN t1 AS x } $t1_cross_t1 \ ] # EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then # the ON expression is evaluated for each row of the cartesian product # as a boolean expression. All rows for which the expression evaluates # to false are excluded from the dataset. # foreach {tn select res} [list \ 1 { SELECT * FROM t1 %JOIN% t2 ON (1) } $t1_cross_t2 \ 2 { SELECT * FROM t1 %JOIN% t2 ON (0) } [list] \ 3 { SELECT * FROM t1 %JOIN% t2 ON (NULL) } [list] \ 4 { SELECT * FROM t1 %JOIN% t2 ON ('abc') } [list] \ 5 { SELECT * FROM t1 %JOIN% t2 ON ('1ab') } $t1_cross_t2 \ 6 { SELECT * FROM t1 %JOIN% t2 ON (0.9) } $t1_cross_t2 \ 7 { SELECT * FROM t1 %JOIN% t2 ON ('0.9') } $t1_cross_t2 \ 8 { SELECT * FROM t1 %JOIN% t2 ON (0.0) } [list] \ \ 9 { SELECT t1.b, t2.b FROM t1 %JOIN% t2 ON (t1.a = t2.a) } \ {one I two II three III} \ 10 { SELECT t1.b, t2.b FROM t1 %JOIN% t2 ON (t1.a = 'a') } \ {one I one II one III} \ 11 { SELECT t1.b, t2.b FROM t1 %JOIN% t2 ON (CASE WHEN t1.a = 'a' THEN NULL ELSE 1 END) } \ {two I two II two III three I three II three III} \ ] { do_join_test e_select-1.3.$tn $select $res } # EVIDENCE-OF: R-63358-54862 If there is a USING clause specified as # part of the join-constraint, then each of the column names specified # must exist in the datasets to both the left and right of the join-op. # do_select_tests e_select-1.4 -error { cannot join using column %s - column not present in both tables } { 1 { SELECT * FROM t1, t3 USING (b) } "b" 2 { SELECT * FROM t3, t1 USING (c) } "c" 3 { SELECT * FROM t3, (SELECT a AS b, b AS c FROM t1) USING (a) } "a" } # EVIDENCE-OF: R-55987-04584 For each pair of namesake columns, the # expression "lhs.X = rhs.X" is evaluated for each row of the cartesian # product as a boolean expression. All rows for which one or more of the # expressions evaluates to false are excluded from the result set. # do_select_tests e_select-1.5 { 1 { SELECT * FROM t1, t3 USING (a) } {a one 1 b two 2} 2 { SELECT * FROM t3, t4 USING (a,c) } {b 2} } # EVIDENCE-OF: R-54046-48600 When comparing values as a result of a # USING clause, the normal rules for handling affinities, collation # sequences and NULL values in comparisons apply. # # EVIDENCE-OF: R-35466-18578 The column from the dataset on the # left-hand side of the join operator is considered to be on the # left-hand side of the comparison operator (=) for the purposes of # collation sequence and affinity precedence. # do_execsql_test e_select-1.6.0 { CREATE TABLE t5(a COLLATE nocase, b COLLATE binary); INSERT INTO t5 VALUES('AA', 'cc'); INSERT INTO t5 VALUES('BB', 'dd'); INSERT INTO t5 VALUES(NULL, NULL); CREATE TABLE t6(a COLLATE binary, b COLLATE nocase); INSERT INTO t6 VALUES('aa', 'cc'); INSERT INTO t6 VALUES('bb', 'DD'); INSERT INTO t6 VALUES(NULL, NULL); } {} foreach {tn select res} { 1 { SELECT * FROM t5 %JOIN% t6 USING (a) } {AA cc cc BB dd DD} 2 { SELECT * FROM t6 %JOIN% t5 USING (a) } {} 3 { SELECT * FROM (SELECT a COLLATE nocase, b FROM t6) %JOIN% t5 USING (a) } {aa cc cc bb DD dd} 4 { SELECT * FROM t5 %JOIN% t6 USING (a,b) } {AA cc} 5 { SELECT * FROM t6 %JOIN% t5 USING (a,b) } {} } { do_join_test e_select-1.6.$tn $select $res } # EVIDENCE-OF: R-57047-10461 For each pair of columns identified by a # USING clause, the column from the right-hand dataset is omitted from # the joined dataset. # # EVIDENCE-OF: R-56132-15700 This is the only difference between a USING # clause and its equivalent ON constraint. # foreach {tn select res} { 1a { SELECT * FROM t1 %JOIN% t2 USING (a) } {a one I b two II c three III} 1b { SELECT * FROM t1 %JOIN% t2 ON (t1.a=t2.a) } {a one a I b two b II c three c III} 2a { SELECT * FROM t3 %JOIN% t4 USING (a) } {a 1 {} b 2 2} 2b { SELECT * FROM t3 %JOIN% t4 ON (t3.a=t4.a) } {a 1 a {} b 2 b 2} 3a { SELECT * FROM t3 %JOIN% t4 USING (a,c) } {b 2} 3b { SELECT * FROM t3 %JOIN% t4 ON (t3.a=t4.a AND t3.c=t4.c) } {b 2 b 2} 4a { SELECT * FROM (SELECT a COLLATE nocase, b FROM t6) AS x %JOIN% t5 USING (a) } {aa cc cc bb DD dd} 4b { SELECT * FROM (SELECT a COLLATE nocase, b FROM t6) AS x %JOIN% t5 ON (x.a=t5.a) } {aa cc AA cc bb DD BB dd} } { do_join_test e_select-1.7.$tn $select $res } # EVIDENCE-OF: R-41434-12448 If the join-op is a "LEFT JOIN" or "LEFT # OUTER JOIN", then after the ON or USING filtering clauses have been # applied, an extra row is added to the output for each row in the # original left-hand input dataset that corresponds to no rows at all in # the composite dataset (if any). # do_execsql_test e_select-1.8.0 { CREATE TABLE t7(a, b, c); CREATE TABLE t8(a, d, e); INSERT INTO t7 VALUES('x', 'ex', 24); INSERT INTO t7 VALUES('y', 'why', 25); INSERT INTO t8 VALUES('x', 'abc', 24); INSERT INTO t8 VALUES('z', 'ghi', 26); } {} do_select_tests e_select-1.8 { 1a "SELECT count(*) FROM t7 JOIN t8 ON (t7.a=t8.a)" {1} 1b "SELECT count(*) FROM t7 LEFT JOIN t8 ON (t7.a=t8.a)" {2} 2a "SELECT count(*) FROM t7 JOIN t8 USING (a)" {1} 2b "SELECT count(*) FROM t7 LEFT JOIN t8 USING (a)" {2} } # EVIDENCE-OF: R-15607-52988 The added rows contain NULL values in the # columns that would normally contain values copied from the right-hand # input dataset. # do_select_tests e_select-1.9 { 1a "SELECT * FROM t7 JOIN t8 ON (t7.a=t8.a)" {x ex 24 x abc 24} 1b "SELECT * FROM t7 LEFT JOIN t8 ON (t7.a=t8.a)" {x ex 24 x abc 24 y why 25 {} {} {}} 2a "SELECT * FROM t7 JOIN t8 USING (a)" {x ex 24 abc 24} 2b "SELECT * FROM t7 LEFT JOIN t8 USING (a)" {x ex 24 abc 24 y why 25 {} {}} } # EVIDENCE-OF: R-01809-52134 If the NATURAL keyword is added to any of # the join-ops, then an implicit USING clause is added to the # join-constraints. The implicit USING clause contains each of the # column names that appear in both the left and right-hand input # datasets. # do_select_tests e_select-1-10 { 1a "SELECT * FROM t7 JOIN t8 USING (a)" {x ex 24 abc 24} 1b "SELECT * FROM t7 NATURAL JOIN t8" {x ex 24 abc 24} 2a "SELECT * FROM t8 JOIN t7 USING (a)" {x abc 24 ex 24} 2b "SELECT * FROM t8 NATURAL JOIN t7" {x abc 24 ex 24} 3a "SELECT * FROM t7 LEFT JOIN t8 USING (a)" {x ex 24 abc 24 y why 25 {} {}} 3b "SELECT * FROM t7 NATURAL LEFT JOIN t8" {x ex 24 abc 24 y why 25 {} {}} 4a "SELECT * FROM t8 LEFT JOIN t7 USING (a)" {x abc 24 ex 24 z ghi 26 {} {}} 4b "SELECT * FROM t8 NATURAL LEFT JOIN t7" {x abc 24 ex 24 z ghi 26 {} {}} 5a "SELECT * FROM t3 JOIN t4 USING (a,c)" {b 2} 5b "SELECT * FROM t3 NATURAL JOIN t4" {b 2} 6a "SELECT * FROM t3 LEFT JOIN t4 USING (a,c)" {a 1 b 2} 6b "SELECT * FROM t3 NATURAL LEFT JOIN t4" {a 1 b 2} } # EVIDENCE-OF: R-49566-01570 If the left and right-hand input datasets # feature no common column names, then the NATURAL keyword has no effect # on the results of the join. # do_execsql_test e_select-1.11.0 { CREATE TABLE t10(x, y); INSERT INTO t10 VALUES(1, 'true'); INSERT INTO t10 VALUES(0, 'false'); } {} do_select_tests e_select-1-11 { 1a "SELECT a, x FROM t1 CROSS JOIN t10" {a 1 a 0 b 1 b 0 c 1 c 0} 1b "SELECT a, x FROM t1 NATURAL CROSS JOIN t10" {a 1 a 0 b 1 b 0 c 1 c 0} } # EVIDENCE-OF: R-39625-59133 A USING or ON clause may not be added to a # join that specifies the NATURAL keyword. # foreach {tn sql} { 1 {SELECT * FROM t1 NATURAL LEFT JOIN t2 USING (a)} 2 {SELECT * FROM t1 NATURAL LEFT JOIN t2 ON (t1.a=t2.a)} 3 {SELECT * FROM t1 NATURAL LEFT JOIN t2 ON (45)} } { do_catchsql_test e_select-1.12.$tn " $sql " {1 {a NATURAL join may not have an ON or USING clause}} } #------------------------------------------------------------------------- # The next block of tests - e_select-3.* - concentrate on verifying # statements made regarding WHERE clause processing. # drop_all_tables do_execsql_test e_select-3.0 { CREATE TABLE x1(k, x, y, z); INSERT INTO x1 VALUES(1, 'relinquished', 'aphasia', 78.43); INSERT INTO x1 VALUES(2, X'A8E8D66F', X'07CF', -81); INSERT INTO x1 VALUES(3, -22, -27.57, NULL); INSERT INTO x1 VALUES(4, NULL, 'bygone', 'picky'); INSERT INTO x1 VALUES(5, NULL, 96.28, NULL); INSERT INTO x1 VALUES(6, 0, 1, 2); CREATE TABLE x2(k, x, y2); INSERT INTO x2 VALUES(1, 50, X'B82838'); INSERT INTO x2 VALUES(5, 84.79, 65.88); INSERT INTO x2 VALUES(3, -22, X'0E1BE452A393'); INSERT INTO x2 VALUES(7, 'mistrusted', 'standardized'); } {} # EVIDENCE-OF: R-06999-14330 If a WHERE clause is specified, the WHERE # expression is evaluated for each row in the input data as a boolean # expression. All rows for which the WHERE clause expression evaluates # to false are excluded from the dataset before continuing. # do_execsql_test e_select-3.1.1 { SELECT k FROM x1 WHERE x } {3} do_execsql_test e_select-3.1.2 { SELECT k FROM x1 WHERE y } {3 5 6} do_execsql_test e_select-3.1.3 { SELECT k FROM x1 WHERE z } {1 2 6} do_execsql_test e_select-3.1.4 { SELECT k FROM x1 WHERE '1'||z } {1 2 4 6} do_execsql_test e_select-3.1.5 { SELECT k FROM x1 WHERE x IS NULL } {4 5} do_execsql_test e_select-3.1.6 { SELECT k FROM x1 WHERE z - 78.43 } {2 4 6} do_execsql_test e_select-3.2.1a { SELECT k FROM x1 LEFT JOIN x2 USING(k) } {1 2 3 4 5 6} do_execsql_test e_select-3.2.1b { SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k } {1 3 5} do_execsql_test e_select-3.2.2 { SELECT k FROM x1 LEFT JOIN x2 USING(k) WHERE x2.k IS NULL } {2 4 6} do_execsql_test e_select-3.2.3 { SELECT k FROM x1 NATURAL JOIN x2 WHERE x2.k } {3} do_execsql_test e_select-3.2.4 { SELECT k FROM x1 NATURAL JOIN x2 WHERE x2.k-3 } {} #------------------------------------------------------------------------- # Tests below this point are focused on verifying the testable statements # related to caculating the result rows of a simple SELECT statement. # drop_all_tables do_execsql_test e_select-4.0 { CREATE TABLE z1(a, b, c); CREATE TABLE z2(d, e); CREATE TABLE z3(a, b); INSERT INTO z1 VALUES(51.65, -59.58, 'belfries'); INSERT INTO z1 VALUES(-5, NULL, 75); INSERT INTO z1 VALUES(-2.2, -23.18, 'suiters'); INSERT INTO z1 VALUES(NULL, 67, 'quartets'); INSERT INTO z1 VALUES(-1.04, -32.3, 'aspen'); INSERT INTO z1 VALUES(63, 'born', -26); INSERT INTO z2 VALUES(NULL, 21); INSERT INTO z2 VALUES(36, 6); INSERT INTO z3 VALUES('subsistence', 'gauze'); INSERT INTO z3 VALUES(49.17, -67); } {} # EVIDENCE-OF: R-36327-17224 If a result expression is the special # expression "*" then all columns in the input data are substituted for # that one expression. # # EVIDENCE-OF: R-43693-30522 If the expression is the alias of a table # or subquery in the FROM clause followed by ".*" then all columns from # the named table or subquery are substituted for the single expression. # do_select_tests e_select-4.1 { 1 "SELECT * FROM z1 LIMIT 1" {51.65 -59.58 belfries} 2 "SELECT * FROM z1,z2 LIMIT 1" {51.65 -59.58 belfries {} 21} 3 "SELECT z1.* FROM z1,z2 LIMIT 1" {51.65 -59.58 belfries} 4 "SELECT z2.* FROM z1,z2 LIMIT 1" {{} 21} 5 "SELECT z2.*, z1.* FROM z1,z2 LIMIT 1" {{} 21 51.65 -59.58 belfries} 6 "SELECT count(*), * FROM z1" {6 63 born -26} 7 "SELECT max(a), * FROM z1" {63 63 born -26} 8 "SELECT *, min(a) FROM z1" {63 born -26 -5} 9 "SELECT *,* FROM z1,z2 LIMIT 1" { 51.65 -59.58 belfries {} 21 51.65 -59.58 belfries {} 21 } 10 "SELECT z1.*,z1.* FROM z2,z1 LIMIT 1" { 51.65 -59.58 belfries 51.65 -59.58 belfries } } # EVIDENCE-OF: R-61869-22578 It is an error to use a "*" or "alias.*" # expression in any context other than than a result expression list. # # EVIDENCE-OF: R-44324-41166 It is also an error to use a "*" or # "alias.*" expression in a simple SELECT query that does not have a # FROM clause. # foreach {tn select err} { 1.1 "SELECT a, b, c FROM z1 WHERE *" {near "*": syntax error} 1.2 "SELECT a, b, c FROM z1 GROUP BY *" {near "*": syntax error} 1.3 "SELECT 1 + * FROM z1" {near "*": syntax error} 1.4 "SELECT * + 1 FROM z1" {near "+": syntax error} 2.1 "SELECT *" {no tables specified} 2.2 "SELECT * WHERE 1" {no tables specified} 2.3 "SELECT * WHERE 0" {no tables specified} 2.4 "SELECT count(*), *" {no tables specified} } { do_catchsql_test e_select-4.2.$tn $select [list 1 $err] } # EVIDENCE-OF: R-08669-22397 The number of columns in the rows returned # by a simple SELECT statement is equal to the number of expressions in # the result expression list after substitution of * and alias.* # expressions. # foreach {tn select nCol} { 1 "SELECT * FROM z1" 3 2 "SELECT * FROM z1 NATURAL JOIN z3" 3 3 "SELECT z1.* FROM z1 NATURAL JOIN z3" 3 4 "SELECT z3.* FROM z1 NATURAL JOIN z3" 2 5 "SELECT z1.*, z3.* FROM z1 NATURAL JOIN z3" 5 6 "SELECT 1, 2, z1.* FROM z1" 5 7 "SELECT a, *, b, c FROM z1" 6 } { set ::stmt [sqlite3_prepare_v2 db $select -1 DUMMY] do_test e_select-4.3.$tn { sqlite3_column_count $::stmt } $nCol sqlite3_finalize $::stmt } # In lang_select.html, a non-aggregate query is defined as any simple SELECT # that has no GROUP BY clause and no aggregate expressions in the result # expression list. Other queries are aggregate queries. Test cases # e_select-4.4.* through e_select-4.12.*, inclusive, which test the part of # simple SELECT that is different for aggregate and non-aggregate queries # verify (in a way) that these definitions are consistent: # # EVIDENCE-OF: R-20637-43463 A simple SELECT statement is an aggregate # query if it contains either a GROUP BY clause or one or more aggregate # functions in the result-set. # # EVIDENCE-OF: R-23155-55597 Otherwise, if a simple SELECT contains no # aggregate functions or a GROUP BY clause, it is a non-aggregate query. # # EVIDENCE-OF: R-44050-47362 If the SELECT statement is a non-aggregate # query, then each expression in the result expression list is evaluated # for each row in the dataset filtered by the WHERE clause. # do_select_tests e_select-4.4 { 1 "SELECT a, b FROM z1" {51.65 -59.58 -5 {} -2.2 -23.18 {} 67 -1.04 -32.3 63 born} 2 "SELECT a IS NULL, b+1, * FROM z1" { 0 -58.58 51.65 -59.58 belfries 0 {} -5 {} 75 0 -22.18 -2.2 -23.18 suiters 1 68 {} 67 quartets 0 -31.3 -1.04 -32.3 aspen 0 1 63 born -26 } 3 "SELECT 32*32, d||e FROM z2" {1024 {} 1024 366} } # Test cases e_select-4.5.* and e_select-4.6.* together show that: # # EVIDENCE-OF: R-51988-01124 The single row of result-set data created # by evaluating the aggregate and non-aggregate expressions in the # result-set forms the result of an aggregate query without a GROUP BY # clause. # # EVIDENCE-OF: R-57629-25253 If the SELECT statement is an aggregate # query without a GROUP BY clause, then each aggregate expression in the # result-set is evaluated once across the entire dataset. # do_select_tests e_select-4.5 { 1 "SELECT count(a), max(a), count(b), max(b) FROM z1" {5 63 5 born} 2 "SELECT count(*), max(1)" {1 1} 3 "SELECT sum(b+1) FROM z1 NATURAL LEFT JOIN z3" {-43.06} 4 "SELECT sum(b+2) FROM z1 NATURAL LEFT JOIN z3" {-38.06} 5 "SELECT sum(b IS NOT NULL) FROM z1 NATURAL LEFT JOIN z3" {5} } # EVIDENCE-OF: R-26684-40576 Each non-aggregate expression in the # result-set is evaluated once for an arbitrarily selected row of the # dataset. # # EVIDENCE-OF: R-27994-60376 The same arbitrarily selected row is used # for each non-aggregate expression. # # Note: The results of many of the queries in this block of tests are # technically undefined, as the documentation does not specify which row # SQLite will arbitrarily select to use for the evaluation of the # non-aggregate expressions. # drop_all_tables do_execsql_test e_select-4.6.0 { CREATE TABLE a1(one PRIMARY KEY, two); INSERT INTO a1 VALUES(1, 1); INSERT INTO a1 VALUES(2, 3); INSERT INTO a1 VALUES(3, 6); INSERT INTO a1 VALUES(4, 10); CREATE TABLE a2(one PRIMARY KEY, three); INSERT INTO a2 VALUES(1, 1); INSERT INTO a2 VALUES(3, 2); INSERT INTO a2 VALUES(6, 3); INSERT INTO a2 VALUES(10, 4); } {} do_select_tests e_select-4.6 { 1 "SELECT one, two, count(*) FROM a1" {4 10 4} 2 "SELECT one, two, count(*) FROM a1 WHERE one<3" {2 3 2} 3 "SELECT one, two, count(*) FROM a1 WHERE one>3" {4 10 1} 4 "SELECT *, count(*) FROM a1 JOIN a2" {4 10 10 4 16} 5 "SELECT *, sum(three) FROM a1 NATURAL JOIN a2" {3 6 2 3} 6 "SELECT *, sum(three) FROM a1 NATURAL JOIN a2" {3 6 2 3} 7 "SELECT group_concat(three, ''), a1.* FROM a1 NATURAL JOIN a2" {12 3 6} } # EVIDENCE-OF: R-04486-07266 Or, if the dataset contains zero rows, then # each non-aggregate expression is evaluated against a row consisting # entirely of NULL values. # do_select_tests e_select-4.7 { 1 "SELECT one, two, count(*) FROM a1 WHERE 0" {{} {} 0} 2 "SELECT sum(two), * FROM a1, a2 WHERE three>5" {{} {} {} {} {}} 3 "SELECT max(one) IS NULL, one IS NULL, two IS NULL FROM a1 WHERE two=7" { 1 1 1 } } # EVIDENCE-OF: R-64138-28774 An aggregate query without a GROUP BY # clause always returns exactly one row of data, even if there are zero # rows of input data. # foreach {tn select} { 8.1 "SELECT count(*) FROM a1" 8.2 "SELECT count(*) FROM a1 WHERE 0" 8.3 "SELECT count(*) FROM a1 WHERE 1" 8.4 "SELECT max(a1.one)+min(two), a1.one, two, * FROM a1, a2 WHERE 1" 8.5 "SELECT max(a1.one)+min(two), a1.one, two, * FROM a1, a2 WHERE 0" } { # Set $nRow to the number of rows returned by $select: set ::stmt [sqlite3_prepare_v2 db $select -1 DUMMY] set nRow 0 while {"SQLITE_ROW" == [sqlite3_step $::stmt]} { incr nRow } set rc [sqlite3_finalize $::stmt] # Test that $nRow==1 and that statement execution was successful # (rc==SQLITE_OK). do_test e_select-4.$tn [list list $rc $nRow] {SQLITE_OK 1} } drop_all_tables do_execsql_test e_select-4.9.0 { CREATE TABLE b1(one PRIMARY KEY, two); INSERT INTO b1 VALUES(1, 'o'); INSERT INTO b1 VALUES(4, 'f'); INSERT INTO b1 VALUES(3, 't'); INSERT INTO b1 VALUES(2, 't'); INSERT INTO b1 VALUES(5, 'f'); INSERT INTO b1 VALUES(7, 's'); INSERT INTO b1 VALUES(6, 's'); CREATE TABLE b2(x, y); INSERT INTO b2 VALUES(NULL, 0); INSERT INTO b2 VALUES(NULL, 1); INSERT INTO b2 VALUES('xyz', 2); INSERT INTO b2 VALUES('abc', 3); INSERT INTO b2 VALUES('xyz', 4); CREATE TABLE b3(a COLLATE nocase, b COLLATE binary); INSERT INTO b3 VALUES('abc', 'abc'); INSERT INTO b3 VALUES('aBC', 'aBC'); INSERT INTO b3 VALUES('Def', 'Def'); INSERT INTO b3 VALUES('dEF', 'dEF'); } {} # EVIDENCE-OF: R-57754-57109 If the SELECT statement is an aggregate # query with a GROUP BY clause, then each of the expressions specified # as part of the GROUP BY clause is evaluated for each row of the # dataset. Each row is then assigned to a "group" based on the results; # rows for which the results of evaluating the GROUP BY expressions are # the same are assigned to the same group. # # These tests also show that the following is not untrue: # # EVIDENCE-OF: R-25883-55063 The expressions in the GROUP BY clause do # not have to be expressions that appear in the result. # do_select_tests e_select-4.9 { 1 "SELECT group_concat(one), two FROM b1 GROUP BY two" { 4,5 f 1 o 7,6 s 3,2 t } 2 "SELECT group_concat(one), sum(one) FROM b1 GROUP BY (one>4)" { 1,4,3,2 10 5,7,6 18 } 3 "SELECT group_concat(one) FROM b1 GROUP BY (two>'o'), one%2" { 4 1,5 2,6 3,7 } 4 "SELECT group_concat(one) FROM b1 GROUP BY (one==2 OR two=='o')" { 4,3,5,7,6 1,2 } } # EVIDENCE-OF: R-14926-50129 For the purposes of grouping rows, NULL # values are considered equal. # do_select_tests e_select-4.10 { 1 "SELECT group_concat(y) FROM b2 GROUP BY x" {0,1 3 2,4} 2 "SELECT count(*) FROM b2 GROUP BY CASE WHEN y<4 THEN NULL ELSE 0 END" {4 1} } # EVIDENCE-OF: R-10470-30318 The usual rules for selecting a collation # sequence with which to compare text values apply when evaluating # expressions in a GROUP BY clause. # do_select_tests e_select-4.11 { 1 "SELECT count(*) FROM b3 GROUP BY b" {1 1 1 1} 2 "SELECT count(*) FROM b3 GROUP BY a" {2 2} 3 "SELECT count(*) FROM b3 GROUP BY +b" {1 1 1 1} 4 "SELECT count(*) FROM b3 GROUP BY +a" {2 2} 5 "SELECT count(*) FROM b3 GROUP BY b||''" {1 1 1 1} 6 "SELECT count(*) FROM b3 GROUP BY a||''" {1 1 1 1} } # EVIDENCE-OF: R-63573-50730 The expressions in a GROUP BY clause may # not be aggregate expressions. # foreach {tn select} { 12.1 "SELECT * FROM b3 GROUP BY count(*)" 12.2 "SELECT max(a) FROM b3 GROUP BY max(b)" 12.3 "SELECT group_concat(a) FROM b3 GROUP BY a, max(b)" } { set res {1 {aggregate functions are not allowed in the GROUP BY clause}} do_catchsql_test e_select-4.$tn $select $res } # EVIDENCE-OF: R-31537-00101 If a HAVING clause is specified, it is # evaluated once for each group of rows as a boolean expression. If the # result of evaluating the HAVING clause is false, the group is # discarded. # # This requirement is tested by all e_select-4.13.* tests. # # EVIDENCE-OF: R-04132-09474 If the HAVING clause is an aggregate # expression, it is evaluated across all rows in the group. # # Tested by e_select-4.13.1.* # # EVIDENCE-OF: R-28262-47447 If a HAVING clause is a non-aggregate # expression, it is evaluated with respect to an arbitrarily selected # row from the group. # # Tested by e_select-4.13.2.* # # Tests in this block also show that this is not untrue: # # EVIDENCE-OF: R-55403-13450 The HAVING expression may refer to values, # even aggregate functions, that are not in the result. # do_execsql_test e_select-4.13.0 { CREATE TABLE c1(up, down); INSERT INTO c1 VALUES('x', 1); INSERT INTO c1 VALUES('x', 2); INSERT INTO c1 VALUES('x', 4); INSERT INTO c1 VALUES('x', 8); INSERT INTO c1 VALUES('y', 16); INSERT INTO c1 VALUES('y', 32); CREATE TABLE c2(i, j); INSERT INTO c2 VALUES(1, 0); INSERT INTO c2 VALUES(2, 1); INSERT INTO c2 VALUES(3, 3); INSERT INTO c2 VALUES(4, 6); INSERT INTO c2 VALUES(5, 10); INSERT INTO c2 VALUES(6, 15); INSERT INTO c2 VALUES(7, 21); INSERT INTO c2 VALUES(8, 28); INSERT INTO c2 VALUES(9, 36); CREATE TABLE c3(i PRIMARY KEY, k TEXT); INSERT INTO c3 VALUES(1, 'hydrogen'); INSERT INTO c3 VALUES(2, 'helium'); INSERT INTO c3 VALUES(3, 'lithium'); INSERT INTO c3 VALUES(4, 'beryllium'); INSERT INTO c3 VALUES(5, 'boron'); INSERT INTO c3 VALUES(94, 'plutonium'); } {} do_select_tests e_select-4.13 { 1.1 "SELECT up FROM c1 GROUP BY up HAVING count(*)>3" {x} 1.2 "SELECT up FROM c1 GROUP BY up HAVING sum(down)>16" {y} 1.3 "SELECT up FROM c1 GROUP BY up HAVING sum(down)<16" {x} 1.4 "SELECT up||down FROM c1 GROUP BY (down<5) HAVING max(down)<10" {x4} 2.1 "SELECT up FROM c1 GROUP BY up HAVING down>10" {y} 2.2 "SELECT up FROM c1 GROUP BY up HAVING up='y'" {y} 2.3 "SELECT i, j FROM c2 GROUP BY i>4 HAVING i>6" {9 36} } # EVIDENCE-OF: R-23927-54081 Each expression in the result-set is then # evaluated once for each group of rows. # # EVIDENCE-OF: R-53735-47017 If the expression is an aggregate # expression, it is evaluated across all rows in the group. # do_select_tests e_select-4.15 { 1 "SELECT sum(down) FROM c1 GROUP BY up" {15 48} 2 "SELECT sum(j), max(j) FROM c2 GROUP BY (i%3)" {54 36 27 21 39 28} 3 "SELECT sum(j), max(j) FROM c2 GROUP BY (j%2)" {80 36 40 21} 4 "SELECT 1+sum(j), max(j)+1 FROM c2 GROUP BY (j%2)" {81 37 41 22} 5 "SELECT count(*), round(avg(i),2) FROM c1, c2 ON (i=down) GROUP BY j%2" {3 4.33 1 2.0} } # EVIDENCE-OF: R-62913-19830 Otherwise, it is evaluated against a single # arbitrarily chosen row from within the group. # # EVIDENCE-OF: R-53924-08809 If there is more than one non-aggregate # expression in the result-set, then all such expressions are evaluated # for the same row. # do_select_tests e_select-4.15 { 1 "SELECT i, j FROM c2 GROUP BY i%2" {8 28 9 36} 2 "SELECT i, j FROM c2 GROUP BY i%2 HAVING j<30" {8 28} 3 "SELECT i, j FROM c2 GROUP BY i%2 HAVING j>30" {9 36} 4 "SELECT i, j FROM c2 GROUP BY i%2 HAVING j>30" {9 36} 5 "SELECT count(*), i, k FROM c2 NATURAL JOIN c3 GROUP BY substr(k, 1, 1)" {2 5 boron 2 2 helium 1 3 lithium} } # EVIDENCE-OF: R-19334-12811 Each group of input dataset rows # contributes a single row to the set of result rows. # # EVIDENCE-OF: R-02223-49279 Subject to filtering associated with the # DISTINCT keyword, the number of rows returned by an aggregate query # with a GROUP BY clause is the same as the number of groups of rows # produced by applying the GROUP BY and HAVING clauses to the filtered # input dataset. # do_select_tests e_select.4.16 -count { 1 "SELECT i, j FROM c2 GROUP BY i%2" 2 2 "SELECT i, j FROM c2 GROUP BY i" 9 3 "SELECT i, j FROM c2 GROUP BY i HAVING i<5" 4 } #------------------------------------------------------------------------- # The following tests attempt to verify statements made regarding the ALL # and DISTINCT keywords. # drop_all_tables do_execsql_test e_select-5.1.0 { CREATE TABLE h1(a, b); INSERT INTO h1 VALUES(1, 'one'); INSERT INTO h1 VALUES(1, 'I'); INSERT INTO h1 VALUES(1, 'i'); INSERT INTO h1 VALUES(4, 'four'); INSERT INTO h1 VALUES(4, 'IV'); INSERT INTO h1 VALUES(4, 'iv'); CREATE TABLE h2(x COLLATE nocase); INSERT INTO h2 VALUES('One'); INSERT INTO h2 VALUES('Two'); INSERT INTO h2 VALUES('Three'); INSERT INTO h2 VALUES('Four'); INSERT INTO h2 VALUES('one'); INSERT INTO h2 VALUES('two'); INSERT INTO h2 VALUES('three'); INSERT INTO h2 VALUES('four'); CREATE TABLE h3(c, d); INSERT INTO h3 VALUES(1, NULL); INSERT INTO h3 VALUES(2, NULL); INSERT INTO h3 VALUES(3, NULL); INSERT INTO h3 VALUES(4, '2'); INSERT INTO h3 VALUES(5, NULL); INSERT INTO h3 VALUES(6, '2,3'); INSERT INTO h3 VALUES(7, NULL); INSERT INTO h3 VALUES(8, '2,4'); INSERT INTO h3 VALUES(9, '3'); } {} # EVIDENCE-OF: R-60770-10612 One of the ALL or DISTINCT keywords may # follow the SELECT keyword in a simple SELECT statement. # do_select_tests e_select-5.1 { 1 "SELECT ALL a FROM h1" {1 1 1 4 4 4} 2 "SELECT DISTINCT a FROM h1" {1 4} } # EVIDENCE-OF: R-08861-34280 If the simple SELECT is a SELECT ALL, then # the entire set of result rows are returned by the SELECT. # # EVIDENCE-OF: R-47911-02086 If neither ALL or DISTINCT are present, # then the behaviour is as if ALL were specified. # # EVIDENCE-OF: R-14442-41305 If the simple SELECT is a SELECT DISTINCT, # then duplicate rows are removed from the set of result rows before it # is returned. # # The three testable statements above are tested by e_select-5.2.*, # 5.3.* and 5.4.* respectively. # do_select_tests e_select-5 { 3.1 "SELECT ALL x FROM h2" {One Two Three Four one two three four} 3.2 "SELECT ALL x FROM h1, h2 ON (x=b)" {One one Four four} 3.1 "SELECT x FROM h2" {One Two Three Four one two three four} 3.2 "SELECT x FROM h1, h2 ON (x=b)" {One one Four four} 4.1 "SELECT DISTINCT x FROM h2" {four one three two} 4.2 "SELECT DISTINCT x FROM h1, h2 ON (x=b)" {four one} } # EVIDENCE-OF: R-02054-15343 For the purposes of detecting duplicate # rows, two NULL values are considered to be equal. # do_select_tests e_select-5.5 { 1 "SELECT DISTINCT d FROM h3" {{} 2 2,3 2,4 3} } # EVIDENCE-OF: R-58359-52112 The normal rules for selecting a collation # sequence to compare text values with apply. # do_select_tests e_select-5.6 { 1 "SELECT DISTINCT b FROM h1" {I IV four i iv one} 2 "SELECT DISTINCT b COLLATE nocase FROM h1" {four i iv one} 3 "SELECT DISTINCT x FROM h2" {four one three two} 4 "SELECT DISTINCT x COLLATE binary FROM h2" { Four One Three Two four one three two } } #------------------------------------------------------------------------- # The following tests - e_select-7.* - test that statements made to do # with compound SELECT statements are correct. # # EVIDENCE-OF: R-39368-64333 In a compound SELECT, all the constituent # SELECTs must return the same number of result columns. # # All the other tests in this section use compound SELECTs created # using component SELECTs that do return the same number of columns. # So the tests here just show that it is an error to attempt otherwise. # drop_all_tables do_execsql_test e_select-7.1.0 { CREATE TABLE j1(a, b, c); CREATE TABLE j2(e, f); CREATE TABLE j3(g); } {} do_select_tests e_select-7.1 -error { SELECTs to the left and right of %s do not have the same number of result columns } { 1 "SELECT a, b FROM j1 UNION ALL SELECT g FROM j3" {{UNION ALL}} 2 "SELECT * FROM j1 UNION ALL SELECT * FROM j3" {{UNION ALL}} 3 "SELECT a, b FROM j1 UNION ALL SELECT g FROM j3" {{UNION ALL}} 4 "SELECT a, b FROM j1 UNION ALL SELECT * FROM j3,j2" {{UNION ALL}} 5 "SELECT * FROM j3,j2 UNION ALL SELECT a, b FROM j1" {{UNION ALL}} 6 "SELECT a, b FROM j1 UNION SELECT g FROM j3" {UNION} 7 "SELECT * FROM j1 UNION SELECT * FROM j3" {UNION} 8 "SELECT a, b FROM j1 UNION SELECT g FROM j3" {UNION} 9 "SELECT a, b FROM j1 UNION SELECT * FROM j3,j2" {UNION} 10 "SELECT * FROM j3,j2 UNION SELECT a, b FROM j1" {UNION} 11 "SELECT a, b FROM j1 INTERSECT SELECT g FROM j3" {INTERSECT} 12 "SELECT * FROM j1 INTERSECT SELECT * FROM j3" {INTERSECT} 13 "SELECT a, b FROM j1 INTERSECT SELECT g FROM j3" {INTERSECT} 14 "SELECT a, b FROM j1 INTERSECT SELECT * FROM j3,j2" {INTERSECT} 15 "SELECT * FROM j3,j2 INTERSECT SELECT a, b FROM j1" {INTERSECT} 16 "SELECT a, b FROM j1 EXCEPT SELECT g FROM j3" {EXCEPT} 17 "SELECT * FROM j1 EXCEPT SELECT * FROM j3" {EXCEPT} 18 "SELECT a, b FROM j1 EXCEPT SELECT g FROM j3" {EXCEPT} 19 "SELECT a, b FROM j1 EXCEPT SELECT * FROM j3,j2" {EXCEPT} 20 "SELECT * FROM j3,j2 EXCEPT SELECT a, b FROM j1" {EXCEPT} } # EVIDENCE-OF: R-01450-11152 As the components of a compound SELECT must # be simple SELECT statements, they may not contain ORDER BY or LIMIT # clauses. # foreach {tn select op1 op2} { 1 "SELECT * FROM j1 ORDER BY a UNION ALL SELECT * FROM j2,j3" {ORDER BY} {UNION ALL} 2 "SELECT count(*) FROM j1 ORDER BY 1 UNION ALL SELECT max(e) FROM j2" {ORDER BY} {UNION ALL} 3 "SELECT count(*), * FROM j1 ORDER BY 1,2,3 UNION ALL SELECT *,* FROM j2" {ORDER BY} {UNION ALL} 4 "SELECT * FROM j1 LIMIT 10 UNION ALL SELECT * FROM j2,j3" LIMIT {UNION ALL} 5 "SELECT * FROM j1 LIMIT 10 OFFSET 5 UNION ALL SELECT * FROM j2,j3" LIMIT {UNION ALL} 6 "SELECT a FROM j1 LIMIT (SELECT e FROM j2) UNION ALL SELECT g FROM j2,j3" LIMIT {UNION ALL} 7 "SELECT * FROM j1 ORDER BY a UNION SELECT * FROM j2,j3" {ORDER BY} {UNION} 8 "SELECT count(*) FROM j1 ORDER BY 1 UNION SELECT max(e) FROM j2" {ORDER BY} {UNION} 9 "SELECT count(*), * FROM j1 ORDER BY 1,2,3 UNION SELECT *,* FROM j2" {ORDER BY} {UNION} 10 "SELECT * FROM j1 LIMIT 10 UNION SELECT * FROM j2,j3" LIMIT {UNION} 11 "SELECT * FROM j1 LIMIT 10 OFFSET 5 UNION SELECT * FROM j2,j3" LIMIT {UNION} 12 "SELECT a FROM j1 LIMIT (SELECT e FROM j2) UNION SELECT g FROM j2,j3" LIMIT {UNION} 13 "SELECT * FROM j1 ORDER BY a EXCEPT SELECT * FROM j2,j3" {ORDER BY} {EXCEPT} 14 "SELECT count(*) FROM j1 ORDER BY 1 EXCEPT SELECT max(e) FROM j2" {ORDER BY} {EXCEPT} 15 "SELECT count(*), * FROM j1 ORDER BY 1,2,3 EXCEPT SELECT *,* FROM j2" {ORDER BY} {EXCEPT} 16 "SELECT * FROM j1 LIMIT 10 EXCEPT SELECT * FROM j2,j3" LIMIT {EXCEPT} 17 "SELECT * FROM j1 LIMIT 10 OFFSET 5 EXCEPT SELECT * FROM j2,j3" LIMIT {EXCEPT} 18 "SELECT a FROM j1 LIMIT (SELECT e FROM j2) EXCEPT SELECT g FROM j2,j3" LIMIT {EXCEPT} 19 "SELECT * FROM j1 ORDER BY a INTERSECT SELECT * FROM j2,j3" {ORDER BY} {INTERSECT} 20 "SELECT count(*) FROM j1 ORDER BY 1 INTERSECT SELECT max(e) FROM j2" {ORDER BY} {INTERSECT} 21 "SELECT count(*), * FROM j1 ORDER BY 1,2,3 INTERSECT SELECT *,* FROM j2" {ORDER BY} {INTERSECT} 22 "SELECT * FROM j1 LIMIT 10 INTERSECT SELECT * FROM j2,j3" LIMIT {INTERSECT} 23 "SELECT * FROM j1 LIMIT 10 OFFSET 5 INTERSECT SELECT * FROM j2,j3" LIMIT {INTERSECT} 24 "SELECT a FROM j1 LIMIT (SELECT e FROM j2) INTERSECT SELECT g FROM j2,j3" LIMIT {INTERSECT} } { set err "$op1 clause should come after $op2 not before" do_catchsql_test e_select-7.2.$tn $select [list 1 $err] } # EVIDENCE-OF: R-22874-32655 ORDER BY and LIMIT clauses may only occur # at the end of the entire compound SELECT. # foreach {tn select} { 1 "SELECT * FROM j1 UNION ALL SELECT * FROM j2,j3 ORDER BY a" 2 "SELECT count(*) FROM j1 UNION ALL SELECT max(e) FROM j2 ORDER BY 1" 3 "SELECT count(*), * FROM j1 UNION ALL SELECT *,* FROM j2 ORDER BY 1,2,3" 4 "SELECT * FROM j1 UNION ALL SELECT * FROM j2,j3 LIMIT 10" 5 "SELECT * FROM j1 UNION ALL SELECT * FROM j2,j3 LIMIT 10 OFFSET 5" 6 "SELECT a FROM j1 UNION ALL SELECT g FROM j2,j3 LIMIT (SELECT 10)" 7 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 ORDER BY a" 8 "SELECT count(*) FROM j1 UNION SELECT max(e) FROM j2 ORDER BY 1" 9 "SELECT count(*), * FROM j1 UNION SELECT *,* FROM j2 ORDER BY 1,2,3" 10 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10" 11 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10 OFFSET 5" 12 "SELECT a FROM j1 UNION SELECT g FROM j2,j3 LIMIT (SELECT 10)" 13 "SELECT * FROM j1 EXCEPT SELECT * FROM j2,j3 ORDER BY a" 14 "SELECT count(*) FROM j1 EXCEPT SELECT max(e) FROM j2 ORDER BY 1" 15 "SELECT count(*), * FROM j1 EXCEPT SELECT *,* FROM j2 ORDER BY 1,2,3" 16 "SELECT * FROM j1 EXCEPT SELECT * FROM j2,j3 LIMIT 10" 17 "SELECT * FROM j1 EXCEPT SELECT * FROM j2,j3 LIMIT 10 OFFSET 5" 18 "SELECT a FROM j1 EXCEPT SELECT g FROM j2,j3 LIMIT (SELECT 10)" 19 "SELECT * FROM j1 INTERSECT SELECT * FROM j2,j3 ORDER BY a" 20 "SELECT count(*) FROM j1 INTERSECT SELECT max(e) FROM j2 ORDER BY 1" 21 "SELECT count(*), * FROM j1 INTERSECT SELECT *,* FROM j2 ORDER BY 1,2,3" 22 "SELECT * FROM j1 INTERSECT SELECT * FROM j2,j3 LIMIT 10" 23 "SELECT * FROM j1 INTERSECT SELECT * FROM j2,j3 LIMIT 10 OFFSET 5" 24 "SELECT a FROM j1 INTERSECT SELECT g FROM j2,j3 LIMIT (SELECT 10)" } { do_test e_select-7.3.$tn { catch {execsql $select} msg } 0 } # EVIDENCE-OF: R-08531-36543 A compound SELECT created using UNION ALL # operator returns all the rows from the SELECT to the left of the UNION # ALL operator, and all the rows from the SELECT to the right of it. # drop_all_tables do_execsql_test e_select-7.4.0 { CREATE TABLE q1(a TEXT, b INTEGER, c); CREATE TABLE q2(d NUMBER, e BLOB); CREATE TABLE q3(f REAL, g); INSERT INTO q1 VALUES(16, -87.66, NULL); INSERT INTO q1 VALUES('legible', 94, -42.47); INSERT INTO q1 VALUES('beauty', 36, NULL); INSERT INTO q2 VALUES('legible', 1); INSERT INTO q2 VALUES('beauty', 2); INSERT INTO q2 VALUES(-65.91, 4); INSERT INTO q2 VALUES('emanating', -16.56); INSERT INTO q3 VALUES('beauty', 2); INSERT INTO q3 VALUES('beauty', 2); } {} do_select_tests e_select-7.4 { 1 {SELECT a FROM q1 UNION ALL SELECT d FROM q2} {16 legible beauty legible beauty -65.91 emanating} 2 {SELECT * FROM q1 WHERE a=16 UNION ALL SELECT 'x', * FROM q2 WHERE oid=1} {16 -87.66 {} x legible 1} 3 {SELECT count(*) FROM q1 UNION ALL SELECT min(e) FROM q2} {3 -16.56} 4 {SELECT * FROM q2 UNION ALL SELECT * FROM q3} {legible 1 beauty 2 -65.91 4 emanating -16.56 beauty 2 beauty 2} } # EVIDENCE-OF: R-20560-39162 The UNION operator works the same way as # UNION ALL, except that duplicate rows are removed from the final # result set. # do_select_tests e_select-7.5 { 1 {SELECT a FROM q1 UNION SELECT d FROM q2} {-65.91 16 beauty emanating legible} 2 {SELECT * FROM q1 WHERE a=16 UNION SELECT 'x', * FROM q2 WHERE oid=1} {16 -87.66 {} x legible 1} 3 {SELECT count(*) FROM q1 UNION SELECT min(e) FROM q2} {-16.56 3} 4 {SELECT * FROM q2 UNION SELECT * FROM q3} {-65.91 4 beauty 2 emanating -16.56 legible 1} } # EVIDENCE-OF: R-45764-31737 The INTERSECT operator returns the # intersection of the results of the left and right SELECTs. # do_select_tests e_select-7.6 { 1 {SELECT a FROM q1 INTERSECT SELECT d FROM q2} {beauty legible} 2 {SELECT * FROM q2 INTERSECT SELECT * FROM q3} {beauty 2} } # EVIDENCE-OF: R-25787-28949 The EXCEPT operator returns the subset of # rows returned by the left SELECT that are not also returned by the # right-hand SELECT. # do_select_tests e_select-7.7 { 1 {SELECT a FROM q1 EXCEPT SELECT d FROM q2} {16} 2 {SELECT * FROM q2 EXCEPT SELECT * FROM q3} {-65.91 4 emanating -16.56 legible 1} } # EVIDENCE-OF: R-40729-56447 Duplicate rows are removed from the results # of INTERSECT and EXCEPT operators before the result set is returned. # do_select_tests e_select-7.8 { 0 {SELECT * FROM q3} {beauty 2 beauty 2} 1 {SELECT * FROM q3 INTERSECT SELECT * FROM q3} {beauty 2} 2 {SELECT * FROM q3 EXCEPT SELECT a,b FROM q1} {beauty 2} } # EVIDENCE-OF: R-46765-43362 For the purposes of determining duplicate # rows for the results of compound SELECT operators, NULL values are # considered equal to other NULL values and distinct from all non-NULL # values. # db nullvalue null do_select_tests e_select-7.9 { 1 {SELECT NULL UNION ALL SELECT NULL} {null null} 2 {SELECT NULL UNION SELECT NULL} {null} 3 {SELECT NULL INTERSECT SELECT NULL} {null} 4 {SELECT NULL EXCEPT SELECT NULL} {} 5 {SELECT NULL UNION ALL SELECT 'ab'} {null ab} 6 {SELECT NULL UNION SELECT 'ab'} {null ab} 7 {SELECT NULL INTERSECT SELECT 'ab'} {} 8 {SELECT NULL EXCEPT SELECT 'ab'} {null} 9 {SELECT NULL UNION ALL SELECT 0} {null 0} 10 {SELECT NULL UNION SELECT 0} {null 0} 11 {SELECT NULL INTERSECT SELECT 0} {} 12 {SELECT NULL EXCEPT SELECT 0} {null} 13 {SELECT c FROM q1 UNION ALL SELECT g FROM q3} {null -42.47 null 2 2} 14 {SELECT c FROM q1 UNION SELECT g FROM q3} {null -42.47 2} 15 {SELECT c FROM q1 INTERSECT SELECT g FROM q3} {} 16 {SELECT c FROM q1 EXCEPT SELECT g FROM q3} {null -42.47} } db nullvalue {} # EVIDENCE-OF: R-51232-50224 The collation sequence used to compare two # text values is determined as if the columns of the left and right-hand # SELECT statements were the left and right-hand operands of the equals # (=) operator, except that greater precedence is not assigned to a # collation sequence specified with the postfix COLLATE operator. # drop_all_tables do_execsql_test e_select-7.10.0 { CREATE TABLE y1(a COLLATE nocase, b COLLATE binary, c); INSERT INTO y1 VALUES('Abc', 'abc', 'aBC'); } {} do_select_tests e_select-7.10 { 1 {SELECT 'abc' UNION SELECT 'ABC'} {ABC abc} 2 {SELECT 'abc' COLLATE nocase UNION SELECT 'ABC'} {ABC} 3 {SELECT 'abc' UNION SELECT 'ABC' COLLATE nocase} {ABC} 4 {SELECT 'abc' COLLATE binary UNION SELECT 'ABC' COLLATE nocase} {ABC abc} 5 {SELECT 'abc' COLLATE nocase UNION SELECT 'ABC' COLLATE binary} {ABC} 6 {SELECT a FROM y1 UNION SELECT b FROM y1} {abc} 7 {SELECT b FROM y1 UNION SELECT a FROM y1} {Abc abc} 8 {SELECT a FROM y1 UNION SELECT c FROM y1} {aBC} 9 {SELECT a FROM y1 UNION SELECT c COLLATE binary FROM y1} {aBC} } # EVIDENCE-OF: R-32706-07403 No affinity transformations are applied to # any values when comparing rows as part of a compound SELECT. # drop_all_tables do_execsql_test e_select-7.10.0 { CREATE TABLE w1(a TEXT, b NUMBER); CREATE TABLE w2(a, b TEXT); INSERT INTO w1 VALUES('1', 4.1); INSERT INTO w2 VALUES(1, 4.1); } {} do_select_tests e_select-7.11 { 1 { SELECT a FROM w1 UNION SELECT a FROM w2 } {1 1} 2 { SELECT a FROM w2 UNION SELECT a FROM w1 } {1 1} 3 { SELECT b FROM w1 UNION SELECT b FROM w2 } {4.1 4.1} 4 { SELECT b FROM w2 UNION SELECT b FROM w1 } {4.1 4.1} 5 { SELECT a FROM w1 INTERSECT SELECT a FROM w2 } {} 6 { SELECT a FROM w2 INTERSECT SELECT a FROM w1 } {} 7 { SELECT b FROM w1 INTERSECT SELECT b FROM w2 } {} 8 { SELECT b FROM w2 INTERSECT SELECT b FROM w1 } {} 9 { SELECT a FROM w1 EXCEPT SELECT a FROM w2 } {1} 10 { SELECT a FROM w2 EXCEPT SELECT a FROM w1 } {1} 11 { SELECT b FROM w1 EXCEPT SELECT b FROM w2 } {4.1} 12 { SELECT b FROM w2 EXCEPT SELECT b FROM w1 } {4.1} } # EVIDENCE-OF: R-32562-20566 When three or more simple SELECTs are # connected into a compound SELECT, they group from left to right. In # other words, if "A", "B" and "C" are all simple SELECT statements, (A # op B op C) is processed as ((A op B) op C). # # e_select-7.12.1: Precedence of UNION vs. INTERSECT # e_select-7.12.2: Precedence of UNION vs. UNION ALL # e_select-7.12.3: Precedence of UNION vs. EXCEPT # e_select-7.12.4: Precedence of INTERSECT vs. UNION ALL # e_select-7.12.5: Precedence of INTERSECT vs. EXCEPT # e_select-7.12.6: Precedence of UNION ALL vs. EXCEPT # e_select-7.12.7: Check that "a EXCEPT b EXCEPT c" is processed as # "(a EXCEPT b) EXCEPT c". # # The INTERSECT and EXCEPT operations are mutually commutative. So # the e_select-7.12.5 test cases do not prove very much. # drop_all_tables do_execsql_test e_select-7.12.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); } {} foreach {tn select res} { 1a "(1,2) INTERSECT (1) UNION (3)" {1 3} 1b "(3) UNION (1,2) INTERSECT (1)" {1} 2a "(1,2) UNION (3) UNION ALL (1)" {1 2 3 1} 2b "(1) UNION ALL (3) UNION (1,2)" {1 2 3} 3a "(1,2) UNION (3) EXCEPT (1)" {2 3} 3b "(1,2) EXCEPT (3) UNION (1)" {1 2} 4a "(1,2) INTERSECT (1) UNION ALL (3)" {1 3} 4b "(3) UNION (1,2) INTERSECT (1)" {1} 5a "(1,2) INTERSECT (2) EXCEPT (2)" {} 5b "(2,3) EXCEPT (2) INTERSECT (2)" {} 6a "(2) UNION ALL (2) EXCEPT (2)" {} 6b "(2) EXCEPT (2) UNION ALL (2)" {2} 7 "(2,3) EXCEPT (2) EXCEPT (3)" {} } { set select [string map {( {SELECT x FROM t1 WHERE x IN (}} $select] do_execsql_test e_select-7.12.$tn $select [list {*}$res] } #------------------------------------------------------------------------- # ORDER BY clauses # drop_all_tables do_execsql_test e_select-8.1.0 { CREATE TABLE d1(x, y, z); INSERT INTO d1 VALUES(1, 2, 3); INSERT INTO d1 VALUES(2, 5, -1); INSERT INTO d1 VALUES(1, 2, 8); INSERT INTO d1 VALUES(1, 2, 7); INSERT INTO d1 VALUES(2, 4, 93); INSERT INTO d1 VALUES(1, 2, -20); INSERT INTO d1 VALUES(1, 4, 93); INSERT INTO d1 VALUES(1, 5, -1); CREATE TABLE d2(a, b); INSERT INTO d2 VALUES('gently', 'failings'); INSERT INTO d2 VALUES('commercials', 'bathrobe'); INSERT INTO d2 VALUES('iterate', 'sexton'); INSERT INTO d2 VALUES('babied', 'charitableness'); INSERT INTO d2 VALUES('solemnness', 'annexed'); INSERT INTO d2 VALUES('rejoicing', 'liabilities'); INSERT INTO d2 VALUES('pragmatist', 'guarded'); INSERT INTO d2 VALUES('barked', 'interrupted'); INSERT INTO d2 VALUES('reemphasizes', 'reply'); INSERT INTO d2 VALUES('lad', 'relenting'); } {} # EVIDENCE-OF: R-44988-41064 Rows are first sorted based on the results # of evaluating the left-most expression in the ORDER BY list, then ties # are broken by evaluating the second left-most expression and so on. # do_select_tests e_select-8.1 { 1 "SELECT * FROM d1 ORDER BY x, y, z" { 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 2 4 93 2 5 -1 } } # EVIDENCE-OF: R-06617-54588 Each ORDER BY expression may be optionally # followed by one of the keywords ASC (smaller values are returned # first) or DESC (larger values are returned first). # # Test cases e_select-8.2.* test the above. # # EVIDENCE-OF: R-18705-33393 If neither ASC or DESC are specified, rows # are sorted in ascending (smaller values first) order by default. # # Test cases e_select-8.3.* test the above. All 8.3 test cases are # copies of 8.2 test cases with the explicit "ASC" removed. # do_select_tests e_select-8 { 2.1 "SELECT * FROM d1 ORDER BY x ASC, y ASC, z ASC" { 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 2 4 93 2 5 -1 } 2.2 "SELECT * FROM d1 ORDER BY x DESC, y DESC, z DESC" { 2 5 -1 2 4 93 1 5 -1 1 4 93 1 2 8 1 2 7 1 2 3 1 2 -20 } 2.3 "SELECT * FROM d1 ORDER BY x DESC, y ASC, z DESC" { 2 4 93 2 5 -1 1 2 8 1 2 7 1 2 3 1 2 -20 1 4 93 1 5 -1 } 2.4 "SELECT * FROM d1 ORDER BY x DESC, y ASC, z ASC" { 2 4 93 2 5 -1 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 } 3.1 "SELECT * FROM d1 ORDER BY x, y, z" { 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 2 4 93 2 5 -1 } 3.3 "SELECT * FROM d1 ORDER BY x DESC, y, z DESC" { 2 4 93 2 5 -1 1 2 8 1 2 7 1 2 3 1 2 -20 1 4 93 1 5 -1 } 3.4 "SELECT * FROM d1 ORDER BY x DESC, y, z" { 2 4 93 2 5 -1 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 } } # EVIDENCE-OF: R-29779-04281 If the ORDER BY expression is a constant # integer K then the expression is considered an alias for the K-th # column of the result set (columns are numbered from left to right # starting with 1). # do_select_tests e_select-8.4 { 1 "SELECT * FROM d1 ORDER BY 1 ASC, 2 ASC, 3 ASC" { 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 2 4 93 2 5 -1 } 2 "SELECT * FROM d1 ORDER BY 1 DESC, 2 DESC, 3 DESC" { 2 5 -1 2 4 93 1 5 -1 1 4 93 1 2 8 1 2 7 1 2 3 1 2 -20 } 3 "SELECT * FROM d1 ORDER BY 1 DESC, 2 ASC, 3 DESC" { 2 4 93 2 5 -1 1 2 8 1 2 7 1 2 3 1 2 -20 1 4 93 1 5 -1 } 4 "SELECT * FROM d1 ORDER BY 1 DESC, 2 ASC, 3 ASC" { 2 4 93 2 5 -1 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 } 5 "SELECT * FROM d1 ORDER BY 1, 2, 3" { 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 2 4 93 2 5 -1 } 6 "SELECT * FROM d1 ORDER BY 1 DESC, 2, 3 DESC" { 2 4 93 2 5 -1 1 2 8 1 2 7 1 2 3 1 2 -20 1 4 93 1 5 -1 } 7 "SELECT * FROM d1 ORDER BY 1 DESC, 2, 3" { 2 4 93 2 5 -1 1 2 -20 1 2 3 1 2 7 1 2 8 1 4 93 1 5 -1 } 8 "SELECT z, x FROM d1 ORDER BY 2" { 3 1 8 1 7 1 -20 1 93 1 -1 1 -1 2 93 2 } 9 "SELECT z, x FROM d1 ORDER BY 1" { -20 1 -1 2 -1 1 3 1 7 1 8 1 93 2 93 1 } } # EVIDENCE-OF: R-63286-51977 If the ORDER BY expression is an identifier # that corresponds to the alias of one of the output columns, then the # expression is considered an alias for that column. # do_select_tests e_select-8.5 { 1 "SELECT z+1 AS abc FROM d1 ORDER BY abc" { -19 0 0 4 8 9 94 94 } 2 "SELECT z+1 AS abc FROM d1 ORDER BY abc DESC" { 94 94 9 8 4 0 0 -19 } 3 "SELECT z AS x, x AS z FROM d1 ORDER BY z" { 3 1 8 1 7 1 -20 1 93 1 -1 1 -1 2 93 2 } 4 "SELECT z AS x, x AS z FROM d1 ORDER BY x" { -20 1 -1 2 -1 1 3 1 7 1 8 1 93 2 93 1 } } # EVIDENCE-OF: R-27923-38747 Otherwise, if the ORDER BY expression is # any other expression, it is evaluated and the the returned value used # to order the output rows. # # EVIDENCE-OF: R-03421-57988 If the SELECT statement is a simple SELECT, # then an ORDER BY may contain any arbitrary expressions. # do_select_tests e_select-8.6 { 1 "SELECT * FROM d1 ORDER BY x+y+z" { 1 2 -20 1 5 -1 1 2 3 2 5 -1 1 2 7 1 2 8 1 4 93 2 4 93 } 2 "SELECT * FROM d1 ORDER BY x*z" { 1 2 -20 2 5 -1 1 5 -1 1 2 3 1 2 7 1 2 8 1 4 93 2 4 93 } 3 "SELECT * FROM d1 ORDER BY y*z" { 1 2 -20 2 5 -1 1 5 -1 1 2 3 1 2 7 1 2 8 2 4 93 1 4 93 } } # EVIDENCE-OF: R-28853-08147 However, if the SELECT is a compound # SELECT, then ORDER BY expressions that are not aliases to output # columns must be exactly the same as an expression used as an output # column. # do_select_tests e_select-8.7.1 -error { %s ORDER BY term does not match any column in the result set } { 1 "SELECT x FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z" 1st 2 "SELECT x,z FROM d1 UNION ALL SELECT a,b FROM d2 ORDER BY x, x/z" 2nd } do_select_tests e_select-8.7.2 { 1 "SELECT x*z FROM d1 UNION ALL SELECT a FROM d2 ORDER BY x*z" { -20 -2 -1 3 7 8 93 186 babied barked commercials gently iterate lad pragmatist reemphasizes rejoicing solemnness } 2 "SELECT x, x/z FROM d1 UNION ALL SELECT a,b FROM d2 ORDER BY x, x/z" { 1 -1 1 0 1 0 1 0 1 0 1 0 2 -2 2 0 babied charitableness barked interrupted commercials bathrobe gently failings iterate sexton lad relenting pragmatist guarded reemphasizes reply rejoicing liabilities solemnness annexed } } do_execsql_test e_select-8.8.0 { CREATE TABLE d3(a); INSERT INTO d3 VALUES('text'); INSERT INTO d3 VALUES(14.1); INSERT INTO d3 VALUES(13); INSERT INTO d3 VALUES(X'78787878'); INSERT INTO d3 VALUES(15); INSERT INTO d3 VALUES(12.9); INSERT INTO d3 VALUES(null); CREATE TABLE d4(x COLLATE nocase); INSERT INTO d4 VALUES('abc'); INSERT INTO d4 VALUES('ghi'); INSERT INTO d4 VALUES('DEF'); INSERT INTO d4 VALUES('JKL'); } {} # EVIDENCE-OF: R-10883-17697 For the purposes of sorting rows, values # are compared in the same way as for comparison expressions. # # The following tests verify that values of different types are sorted # correctly, and that mixed real and integer values are compared properly. # do_execsql_test e_select-8.8.1 { SELECT a FROM d3 ORDER BY a } {{} 12.9 13 14.1 15 text xxxx} do_execsql_test e_select-8.8.2 { SELECT a FROM d3 ORDER BY a DESC } {xxxx text 15 14.1 13 12.9 {}} # EVIDENCE-OF: R-64199-22471 If the ORDER BY expression is assigned a # collation sequence using the postfix COLLATE operator, then the # specified collation sequence is used. # do_execsql_test e_select-8.9.1 { SELECT x FROM d4 ORDER BY 1 COLLATE binary } {DEF JKL abc ghi} do_execsql_test e_select-8.9.2 { SELECT x COLLATE binary FROM d4 ORDER BY 1 COLLATE nocase } {abc DEF ghi JKL} # EVIDENCE-OF: R-09398-26102 Otherwise, if the ORDER BY expression is # an alias to an expression that has been assigned a collation sequence # using the postfix COLLATE operator, then the collation sequence # assigned to the aliased expression is used. # # In the test 8.10.2, the only result-column expression has no alias. So the # ORDER BY expression is not a reference to it and therefore does not inherit # the collation sequence. In test 8.10.3, "x" is the alias (as well as the # column name), so the ORDER BY expression is interpreted as an alias and the # collation sequence attached to the result column is used for sorting. # do_execsql_test e_select-8.10.1 { SELECT x COLLATE binary FROM d4 ORDER BY 1 } {DEF JKL abc ghi} do_execsql_test e_select-8.10.2 { SELECT x COLLATE binary FROM d4 ORDER BY x } {abc DEF ghi JKL} do_execsql_test e_select-8.10.3 { SELECT x COLLATE binary AS x FROM d4 ORDER BY x } {DEF JKL abc ghi} # EVIDENCE-OF: R-27301-09658 Otherwise, if the ORDER BY expression is a # column or an alias of an expression that is a column, then the default # collation sequence for the column is used. # do_execsql_test e_select-8.11.1 { SELECT x AS y FROM d4 ORDER BY y } {abc DEF ghi JKL} do_execsql_test e_select-8.11.2 { SELECT x||'' FROM d4 ORDER BY x } {abc DEF ghi JKL} # EVIDENCE-OF: R-49925-55905 Otherwise, the BINARY collation sequence is # used. # do_execsql_test e_select-8.12.1 { SELECT x FROM d4 ORDER BY x||'' } {DEF JKL abc ghi} # EVIDENCE-OF: R-44130-32593 If an ORDER BY expression is not an integer # alias, then SQLite searches the left-most SELECT in the compound for a # result column that matches either the second or third rules above. If # a match is found, the search stops and the expression is handled as an # alias for the result column that it has been matched against. # Otherwise, the next SELECT to the right is tried, and so on. # do_execsql_test e_select-8.13.0 { CREATE TABLE d5(a, b); CREATE TABLE d6(c, d); CREATE TABLE d7(e, f); INSERT INTO d5 VALUES(1, 'f'); INSERT INTO d6 VALUES(2, 'e'); INSERT INTO d7 VALUES(3, 'd'); INSERT INTO d5 VALUES(4, 'c'); INSERT INTO d6 VALUES(5, 'b'); INSERT INTO d7 VALUES(6, 'a'); CREATE TABLE d8(x COLLATE nocase); CREATE TABLE d9(y COLLATE nocase); INSERT INTO d8 VALUES('a'); INSERT INTO d9 VALUES('B'); INSERT INTO d8 VALUES('c'); INSERT INTO d9 VALUES('D'); } {} do_select_tests e_select-8.13 { 1 { SELECT a FROM d5 UNION ALL SELECT c FROM d6 UNION ALL SELECT e FROM d7 ORDER BY a } {1 2 3 4 5 6} 2 { SELECT a FROM d5 UNION ALL SELECT c FROM d6 UNION ALL SELECT e FROM d7 ORDER BY c } {1 2 3 4 5 6} 3 { SELECT a FROM d5 UNION ALL SELECT c FROM d6 UNION ALL SELECT e FROM d7 ORDER BY e } {1 2 3 4 5 6} 4 { SELECT a FROM d5 UNION ALL SELECT c FROM d6 UNION ALL SELECT e FROM d7 ORDER BY 1 } {1 2 3 4 5 6} 5 { SELECT a, b FROM d5 UNION ALL SELECT b, a FROM d5 ORDER BY b } {f 1 c 4 4 c 1 f} 6 { SELECT a, b FROM d5 UNION ALL SELECT b, a FROM d5 ORDER BY 2 } {f 1 c 4 4 c 1 f} 7 { SELECT a, b FROM d5 UNION ALL SELECT b, a FROM d5 ORDER BY a } {1 f 4 c c 4 f 1} 8 { SELECT a, b FROM d5 UNION ALL SELECT b, a FROM d5 ORDER BY 1 } {1 f 4 c c 4 f 1} 9 { SELECT a, b FROM d5 UNION ALL SELECT b, a+1 FROM d5 ORDER BY a+1 } {f 2 c 5 4 c 1 f} 10 { SELECT a, b FROM d5 UNION ALL SELECT b, a+1 FROM d5 ORDER BY 2 } {f 2 c 5 4 c 1 f} 11 { SELECT a+1, b FROM d5 UNION ALL SELECT b, a+1 FROM d5 ORDER BY a+1 } {2 f 5 c c 5 f 2} 12 { SELECT a+1, b FROM d5 UNION ALL SELECT b, a+1 FROM d5 ORDER BY 1 } {2 f 5 c c 5 f 2} } # EVIDENCE-OF: R-39265-04070 If no matching expression can be found in # the result columns of any constituent SELECT, it is an error. # do_select_tests e_select-8.14 -error { %s ORDER BY term does not match any column in the result set } { 1 { SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a+1 } 1st 2 { SELECT a FROM d5 UNION SELECT c FROM d6 ORDER BY a, a+1 } 2nd 3 { SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY 'hello' } 1st 4 { SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY blah } 1st 5 { SELECT * FROM d5 INTERSECT SELECT * FROM d6 ORDER BY c,d,c+d } 3rd 6 { SELECT * FROM d5 EXCEPT SELECT * FROM d7 ORDER BY 1,2,b,a/b } 4th } # EVIDENCE-OF: R-03407-11483 Each term of the ORDER BY clause is # processed separately and may be matched against result columns from # different SELECT statements in the compound. # do_select_tests e_select-8.15 { 1 { SELECT a, b FROM d5 UNION ALL SELECT c-1, d FROM d6 ORDER BY a, d } {1 e 1 f 4 b 4 c} 2 { SELECT a, b FROM d5 UNION ALL SELECT c-1, d FROM d6 ORDER BY c-1, b } {1 e 1 f 4 b 4 c} 3 { SELECT a, b FROM d5 UNION ALL SELECT c-1, d FROM d6 ORDER BY 1, 2 } {1 e 1 f 4 b 4 c} } #------------------------------------------------------------------------- # Tests related to statements made about the LIMIT/OFFSET clause. # do_execsql_test e_select-9.0 { CREATE TABLE f1(a, b); INSERT INTO f1 VALUES(26, 'z'); INSERT INTO f1 VALUES(25, 'y'); INSERT INTO f1 VALUES(24, 'x'); INSERT INTO f1 VALUES(23, 'w'); INSERT INTO f1 VALUES(22, 'v'); INSERT INTO f1 VALUES(21, 'u'); INSERT INTO f1 VALUES(20, 't'); INSERT INTO f1 VALUES(19, 's'); INSERT INTO f1 VALUES(18, 'r'); INSERT INTO f1 VALUES(17, 'q'); INSERT INTO f1 VALUES(16, 'p'); INSERT INTO f1 VALUES(15, 'o'); INSERT INTO f1 VALUES(14, 'n'); INSERT INTO f1 VALUES(13, 'm'); INSERT INTO f1 VALUES(12, 'l'); INSERT INTO f1 VALUES(11, 'k'); INSERT INTO f1 VALUES(10, 'j'); INSERT INTO f1 VALUES(9, 'i'); INSERT INTO f1 VALUES(8, 'h'); INSERT INTO f1 VALUES(7, 'g'); INSERT INTO f1 VALUES(6, 'f'); INSERT INTO f1 VALUES(5, 'e'); INSERT INTO f1 VALUES(4, 'd'); INSERT INTO f1 VALUES(3, 'c'); INSERT INTO f1 VALUES(2, 'b'); INSERT INTO f1 VALUES(1, 'a'); } {} # EVIDENCE-OF: R-30481-56627 Any scalar expression may be used in the # LIMIT clause, so long as it evaluates to an integer or a value that # can be losslessly converted to an integer. # do_select_tests e_select-9.1 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 5 } {a b c d e} 2 { SELECT b FROM f1 ORDER BY a LIMIT 2+3 } {a b c d e} 3 { SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b = 'e') } {a b c d e} 4 { SELECT b FROM f1 ORDER BY a LIMIT 5.0 } {a b c d e} 5 { SELECT b FROM f1 ORDER BY a LIMIT '5' } {a b c d e} } # EVIDENCE-OF: R-46155-47219 If the expression evaluates to a NULL value # or any other value that cannot be losslessly converted to an integer, # an error is returned. # do_select_tests e_select-9.2 -error "datatype mismatch" { 1 { SELECT b FROM f1 ORDER BY a LIMIT 'hello' } {} 2 { SELECT b FROM f1 ORDER BY a LIMIT NULL } {} 3 { SELECT b FROM f1 ORDER BY a LIMIT X'ABCD' } {} 4 { SELECT b FROM f1 ORDER BY a LIMIT 5.1 } {} 5 { SELECT b FROM f1 ORDER BY a LIMIT (SELECT group_concat(b) FROM f1) } {} } # EVIDENCE-OF: R-03014-26414 If the LIMIT expression evaluates to a # negative value, then there is no upper bound on the number of rows # returned. # do_select_tests e_select-9.4 { 1 { SELECT b FROM f1 ORDER BY a LIMIT -1 } {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} 2 { SELECT b FROM f1 ORDER BY a LIMIT length('abc')-100 } {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} 3 { SELECT b FROM f1 ORDER BY a LIMIT (SELECT count(*) FROM f1)/2 - 14 } {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} } # EVIDENCE-OF: R-33750-29536 Otherwise, the SELECT returns the first N # rows of its result set only, where N is the value that the LIMIT # expression evaluates to. # do_select_tests e_select-9.5 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 0 } {} 2 { SELECT b FROM f1 ORDER BY a DESC LIMIT 4 } {z y x w} 3 { SELECT b FROM f1 ORDER BY a DESC LIMIT 8 } {z y x w v u t s} 4 { SELECT b FROM f1 ORDER BY a DESC LIMIT '12.0' } {z y x w v u t s r q p o} } # EVIDENCE-OF: R-54935-19057 Or, if the SELECT statement would return # less than N rows without a LIMIT clause, then the entire result set is # returned. # do_select_tests e_select-9.6 { 1 { SELECT b FROM f1 WHERE a>21 ORDER BY a LIMIT 10 } {v w x y z} 2 { SELECT count(*) FROM f1 GROUP BY a/5 ORDER BY 1 LIMIT 10 } {2 4 5 5 5 5} } # EVIDENCE-OF: R-24188-24349 The expression attached to the optional # OFFSET clause that may follow a LIMIT clause must also evaluate to an # integer, or a value that can be losslessly converted to an integer. # foreach {tn select} { 1 { SELECT b FROM f1 ORDER BY a LIMIT 2 OFFSET 'hello' } 2 { SELECT b FROM f1 ORDER BY a LIMIT 2 OFFSET NULL } 3 { SELECT b FROM f1 ORDER BY a LIMIT 2 OFFSET X'ABCD' } 4 { SELECT b FROM f1 ORDER BY a LIMIT 2 OFFSET 5.1 } 5 { SELECT b FROM f1 ORDER BY a LIMIT 2 OFFSET (SELECT group_concat(b) FROM f1) } } { do_catchsql_test e_select-9.7.$tn $select {1 {datatype mismatch}} } # EVIDENCE-OF: R-20467-43422 If an expression has an OFFSET clause, then # the first M rows are omitted from the result set returned by the # SELECT statement and the next N rows are returned, where M and N are # the values that the OFFSET and LIMIT clauses evaluate to, # respectively. # do_select_tests e_select-9.8 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 10 OFFSET 5} {f g h i j k l m n o} 2 { SELECT b FROM f1 ORDER BY a LIMIT 2+3 OFFSET 10} {k l m n o} 3 { SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b='j') OFFSET (SELECT a FROM f1 WHERE b='b') } {c d e f g h i j k l} 4 { SELECT b FROM f1 ORDER BY a LIMIT '5' OFFSET 3.0 } {d e f g h} 5 { SELECT b FROM f1 ORDER BY a LIMIT '5' OFFSET 0 } {a b c d e} 6 { SELECT b FROM f1 ORDER BY a LIMIT 0 OFFSET 10 } {} 7 { SELECT b FROM f1 ORDER BY a LIMIT 3 OFFSET '1'||'5' } {p q r} } # EVIDENCE-OF: R-34648-44875 Or, if the SELECT would return less than # M+N rows if it did not have a LIMIT clause, then the first M rows are # skipped and the remaining rows (if any) are returned. # do_select_tests e_select-9.9 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 10 OFFSET 20} {u v w x y z} 2 { SELECT a FROM f1 ORDER BY a DESC LIMIT 100 OFFSET 18+4} {4 3 2 1} } # EVIDENCE-OF: R-23293-62447 If the OFFSET clause evaluates to a # negative value, the results are the same as if it had evaluated to # zero. # do_select_tests e_select-9.10 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 5 OFFSET -1 } {a b c d e} 2 { SELECT b FROM f1 ORDER BY a LIMIT 5 OFFSET -500 } {a b c d e} 3 { SELECT b FROM f1 ORDER BY a LIMIT 5 OFFSET 0 } {a b c d e} } # EVIDENCE-OF: R-19509-40356 Instead of a separate OFFSET clause, the # LIMIT clause may specify two scalar expressions separated by a comma. # # EVIDENCE-OF: R-33788-46243 In this case, the first expression is used # as the OFFSET expression and the second as the LIMIT expression. # do_select_tests e_select-9.11 { 1 { SELECT b FROM f1 ORDER BY a LIMIT 5, 10 } {f g h i j k l m n o} 2 { SELECT b FROM f1 ORDER BY a LIMIT 10, 2+3 } {k l m n o} 3 { SELECT b FROM f1 ORDER BY a LIMIT (SELECT a FROM f1 WHERE b='b'), (SELECT a FROM f1 WHERE b='j') } {c d e f g h i j k l} 4 { SELECT b FROM f1 ORDER BY a LIMIT 3.0, '5' } {d e f g h} 5 { SELECT b FROM f1 ORDER BY a LIMIT 0, '5' } {a b c d e} 6 { SELECT b FROM f1 ORDER BY a LIMIT 10, 0 } {} 7 { SELECT b FROM f1 ORDER BY a LIMIT '1'||'5', 3 } {p q r} 8 { SELECT b FROM f1 ORDER BY a LIMIT 20, 10 } {u v w x y z} 9 { SELECT a FROM f1 ORDER BY a DESC LIMIT 18+4, 100 } {4 3 2 1} 10 { SELECT b FROM f1 ORDER BY a LIMIT -1, 5 } {a b c d e} 11 { SELECT b FROM f1 ORDER BY a LIMIT -500, 5 } {a b c d e} 12 { SELECT b FROM f1 ORDER BY a LIMIT 0, 5 } {a b c d e} } finish_test |
Added test/e_select2.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 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 | # 2010 September 24 # # 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 to verify that the "testable statements" in # the lang_select.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl #------------------------------------------------------------------------- # te_* commands: # # # te_read_sql DB SELECT-STATEMENT # te_read_tbl DB TABLENAME # # These two commands are used to read a dataset from the database. A dataset # consists of N rows of M named columns of values each, where each value has a # type (null, integer, real, text or blob) and a value within the types domain. # The tcl format for a "dataset" is a list of two elements: # # * A list of the column names. # * A list of data rows. Each row is itself a list, where each element is # the contents of a column of the row. Each of these is a list of two # elements, the type name and the actual value. # # For example, the contents of table [t1] as a dataset is: # # CREATE TABLE t1(a, b); # INSERT INTO t1 VALUES('abc', NULL); # INSERT INTO t1 VALUES(43.1, 22); # # {a b} {{{TEXT abc} {NULL {}}} {{REAL 43.1} {INTEGER 22}}} # # The [te_read_tbl] command returns a dataset read from a table. The # [te_read_sql] returns the dataset that results from executing a SELECT # command. # # # te_tbljoin ?SWITCHES? LHS-TABLE RHS-TABLE # te_join ?SWITCHES? LHS-DATASET RHS-DATASET # # This command joins the two datasets and returns the resulting dataset. If # there are no switches specified, then the results is the cartesian product # of the two inputs. The [te_tbljoin] command reads the left and right-hand # datasets from the specified tables. The [te_join] command is passed the # datasets directly. # # Optional switches are as follows: # # -on SCRIPT # -using COLUMN-LIST # -left # # The -on option specifies a tcl script that is executed for each row in the # cartesian product of the two datasets. The script has 4 arguments appended # to it, in the following order: # # * The list of column-names from the left-hand dataset. # * A single row from the left-hand dataset (one "data row" list as # described above. # * The list of column-names from the right-hand dataset. # * A single row from the right-hand dataset. # # The script must return a boolean value - true if the combination of rows # should be included in the output dataset, or false otherwise. # # The -using option specifies a list of the columns from the right-hand # dataset that should be omitted from the output dataset. # # If the -left option is present, the join is done LEFT JOIN style. # Specifically, an extra row is inserted if after the -on script is run there # exist rows in the left-hand dataset that have no corresponding rows in # the output. See the implementation for more specific comments. # # # te_equals ?SWITCHES? COLNAME1 COLNAME2 <-on script args> # # The only supported switch is "-nocase". If it is present, then text values # are compared in a case-independent fashion. Otherwise, they are compared # as if using the SQLite BINARY collation sequence. # # # te_and ONSCRIPT1 ONSCRIPT2... # # # # te_read_tbl DB TABLENAME # te_read_sql DB SELECT-STATEMENT # # These two procs are used to extract datasets from the database, either # by reading the contents of a named table (te_read_tbl), or by executing # a SELECT statement (t3_read_sql). # # See the comment above, describing "te_* commands", for details of the # return values. # proc te_read_tbl {db tbl} { te_read_sql $db "SELECT * FROM '$tbl'" } proc te_read_sql {db sql} { set S [sqlite3_prepare_v2 $db $sql -1 DUMMY] set cols [list] for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} { lappend cols [sqlite3_column_name $S $i] } set rows [list] while {[sqlite3_step $S] == "SQLITE_ROW"} { set r [list] for {set i 0} {$i < [sqlite3_column_count $S]} {incr i} { lappend r [list [sqlite3_column_type $S $i] [sqlite3_column_text $S $i]] } lappend rows $r } sqlite3_finalize $S return [list $cols $rows] } #------- # Usage: te_join <table-data1> <table-data2> <join spec>... # # Where a join-spec is an optional list of arguments as follows: # # ?-left? # ?-using colname-list? # ?-on on-expr-proc? # proc te_join {data1 data2 args} { set testproc "" set usinglist [list] set isleft 0 for {set i 0} {$i < [llength $args]} {incr i} { set a [lindex $args $i] switch -- $a { -on { set testproc [lindex $args [incr i]] } -using { set usinglist [lindex $args [incr i]] } -left { set isleft 1 } default { error "Unknown argument: $a" } } } set c1 [lindex $data1 0] set c2 [lindex $data2 0] set omitlist [list] set nullrowlist [list] set cret $c1 set cidx 0 foreach col $c2 { set idx [lsearch $usinglist $col] if {$idx>=0} {lappend omitlist $cidx} if {$idx<0} { lappend nullrowlist {NULL {}} lappend cret $col } incr cidx } set omitlist [lsort -integer -decreasing $omitlist] set rret [list] foreach r1 [lindex $data1 1] { set one 0 foreach r2 [lindex $data2 1] { set ok 1 if {$testproc != ""} { set ok [eval $testproc [list $c1 $r1 $c2 $r2]] } if {$ok} { set one 1 foreach idx $omitlist {set r2 [lreplace $r2 $idx $idx]} lappend rret [concat $r1 $r2] } } if {$isleft && $one==0} { lappend rret [concat $r1 $nullrowlist] } } list $cret $rret } proc te_tbljoin {db t1 t2 args} { te_join [te_read_tbl $db $t1] [te_read_tbl $db $t2] {*}$args } proc te_apply_affinity {affinity typevar valvar} { upvar $typevar type upvar $valvar val switch -- $affinity { integer { if {[string is double $val]} { set type REAL } if {[string is wideinteger $val]} { set type INTEGER } if {$type == "REAL" && int($val)==$val} { set type INTEGER set val [expr {int($val)}] } } text { set type TEXT } none { } default { error "invalid affinity: $affinity" } } } #---------- # te_equals ?SWITCHES? c1 c2 cols1 row1 cols2 row2 # proc te_equals {args} { if {[llength $args]<6} {error "invalid arguments to te_equals"} foreach {c1 c2 cols1 row1 cols2 row2} [lrange $args end-5 end] break set nocase 0 set affinity none for {set i 0} {$i < ([llength $args]-6)} {incr i} { set a [lindex $args $i] switch -- $a { -nocase { set nocase 1 } -affinity { set affinity [string tolower [lindex $args [incr i]]] } default { error "invalid arguments to te_equals" } } } set idx2 [if {[string is integer $c2]} { set c2 } else { lsearch $cols2 $c2 }] set idx1 [if {[string is integer $c1]} { set c1 } else { lsearch $cols1 $c1 }] set t1 [lindex $row1 $idx1 0] set t2 [lindex $row2 $idx2 0] set v1 [lindex $row1 $idx1 1] set v2 [lindex $row2 $idx2 1] te_apply_affinity $affinity t1 v1 te_apply_affinity $affinity t2 v2 if {$t1 == "NULL" || $t2 == "NULL"} { return 0 } if {$nocase && $t1 == "TEXT"} { set v1 [string tolower $v1] } if {$nocase && $t2 == "TEXT"} { set v2 [string tolower $v2] } set res [expr {$t1 == $t2 && [string equal $v1 $v2]}] return $res } proc te_false {args} { return 0 } proc te_true {args} { return 1 } proc te_and {args} { foreach a [lrange $args 0 end-4] { set res [eval $a [lrange $args end-3 end]] if {$res == 0} {return 0} } return 1 } proc te_dataset_eq {testname got expected} { uplevel #0 [list do_test $testname [list set {} $got] $expected] } proc te_dataset_eq_unordered {testname got expected} { lset got 1 [lsort [lindex $got 1]] lset expected 1 [lsort [lindex $expected 1]] te_dataset_eq $testname $got $expected } proc te_dataset_ne {testname got unexpected} { uplevel #0 [list do_test $testname [list string equal $got $unexpected] 0] } proc te_dataset_ne_unordered {testname got unexpected} { lset got 1 [lsort [lindex $got 1]] lset unexpected 1 [lsort [lindex $unexpected 1]] te_dataset_ne $testname $got $unexpected } #------------------------------------------------------------------------- # proc test_join {tn sqljoin tbljoinargs} { set sql [te_read_sql db "SELECT * FROM $sqljoin"] set te [te_tbljoin db {*}$tbljoinargs] te_dataset_eq_unordered $tn $sql $te } drop_all_tables do_execsql_test e_select-2.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); CREATE TABLE t3(b COLLATE nocase); INSERT INTO t1 VALUES(2, 'B'); INSERT INTO t1 VALUES(1, 'A'); INSERT INTO t1 VALUES(4, 'D'); INSERT INTO t1 VALUES(NULL, NULL); INSERT INTO t1 VALUES(3, NULL); INSERT INTO t2 VALUES(1, 'A'); INSERT INTO t2 VALUES(2, NULL); INSERT INTO t2 VALUES(5, 'E'); INSERT INTO t2 VALUES(NULL, NULL); INSERT INTO t2 VALUES(3, 'C'); INSERT INTO t3 VALUES('a'); INSERT INTO t3 VALUES('c'); INSERT INTO t3 VALUES('b'); } {} foreach {tn indexes} { e_select-2.1.1 { } e_select-2.1.2 { CREATE INDEX i1 ON t1(a) } e_select-2.1.3 { CREATE INDEX i1 ON t2(a) } e_select-2.1.4 { CREATE INDEX i1 ON t3(b) } } { catchsql { DROP INDEX i1 } catchsql { DROP INDEX i2 } catchsql { DROP INDEX i3 } execsql $indexes # EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER # JOIN", "JOIN" or a comma (",") and there is no ON or USING clause, # then the result of the join is simply the cartesian product of the # left and right-hand datasets. # # EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER # JOIN", "JOIN" and "," join operators. # # EVIDENCE-OF: R-07544-24155 The "CROSS JOIN" join operator produces the # same data as the "INNER JOIN", "JOIN" and "," operators # test_join $tn.1.1 "t1, t2" {t1 t2} test_join $tn.1.2 "t1 INNER JOIN t2" {t1 t2} test_join $tn.1.3 "t1 CROSS JOIN t2" {t1 t2} test_join $tn.1.4 "t1 JOIN t2" {t1 t2} test_join $tn.1.5 "t2, t3" {t2 t3} test_join $tn.1.6 "t2 INNER JOIN t3" {t2 t3} test_join $tn.1.7 "t2 CROSS JOIN t3" {t2 t3} test_join $tn.1.8 "t2 JOIN t3" {t2 t3} test_join $tn.1.9 "t2, t2 AS x" {t2 t2} test_join $tn.1.10 "t2 INNER JOIN t2 AS x" {t2 t2} test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2} test_join $tn.1.12 "t2 JOIN t2 AS x" {t2 t2} # EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then # the ON expression is evaluated for each row of the cartesian product # as a boolean expression. All rows for which the expression evaluates # to false are excluded from the dataset. # test_join $tn.2.1 "t1, t2 ON (t1.a=t2.a)" {t1 t2 -on {te_equals a a}} test_join $tn.2.2 "t2, t1 ON (t1.a=t2.a)" {t2 t1 -on {te_equals a a}} test_join $tn.2.3 "t2, t1 ON (1)" {t2 t1 -on te_true} test_join $tn.2.4 "t2, t1 ON (NULL)" {t2 t1 -on te_false} test_join $tn.2.5 "t2, t1 ON (1.1-1.1)" {t2 t1 -on te_false} test_join $tn.2.6 "t1, t2 ON (1.1-1.0)" {t1 t2 -on te_true} test_join $tn.3 "t1 LEFT JOIN t2 ON (t1.a=t2.a)" {t1 t2 -left -on {te_equals a a}} test_join $tn.4 "t1 LEFT JOIN t2 USING (a)" { t1 t2 -left -using a -on {te_equals a a} } test_join $tn.5 "t1 CROSS JOIN t2 USING(b, a)" { t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.6 "t1 NATURAL JOIN t2" { t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.7 "t1 NATURAL INNER JOIN t2" { t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.8 "t1 NATURAL CROSS JOIN t2" { t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.9 "t1 NATURAL INNER JOIN t2" { t1 t2 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.10 "t1 NATURAL LEFT JOIN t2" { t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.11 "t1 NATURAL LEFT OUTER JOIN t2" { t1 t2 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.12 "t2 NATURAL JOIN t1" { t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.13 "t2 NATURAL INNER JOIN t1" { t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.14 "t2 NATURAL CROSS JOIN t1" { t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.15 "t2 NATURAL INNER JOIN t1" { t2 t1 -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.16 "t2 NATURAL LEFT JOIN t1" { t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.17 "t2 NATURAL LEFT OUTER JOIN t1" { t2 t1 -left -using {a b} -on {te_and {te_equals a a} {te_equals b b}} } test_join $tn.18 "t1 LEFT JOIN t2 USING (b)" { t1 t2 -left -using b -on {te_equals b b} } test_join $tn.19 "t1 JOIN t3 USING(b)" {t1 t3 -using b -on {te_equals b b}} test_join $tn.20 "t3 JOIN t1 USING(b)" { t3 t1 -using b -on {te_equals -nocase b b} } test_join $tn.21 "t1 NATURAL JOIN t3" { t1 t3 -using b -on {te_equals b b} } test_join $tn.22 "t3 NATURAL JOIN t1" { t3 t1 -using b -on {te_equals -nocase b b} } test_join $tn.23 "t1 NATURAL LEFT JOIN t3" { t1 t3 -left -using b -on {te_equals b b} } test_join $tn.24 "t3 NATURAL LEFT JOIN t1" { t3 t1 -left -using b -on {te_equals -nocase b b} } test_join $tn.25 "t1 LEFT JOIN t3 ON (t3.b=t1.b)" { t1 t3 -left -on {te_equals -nocase b b} } test_join $tn.26 "t1 LEFT JOIN t3 ON (t1.b=t3.b)" { t1 t3 -left -on {te_equals b b} } test_join $tn.27 "t1 JOIN t3 ON (t1.b=t3.b)" { t1 t3 -on {te_equals b b} } # EVIDENCE-OF: R-28760-53843 When more than two tables are joined # together as part of a FROM clause, the join operations are processed # in order from left to right. In other words, the FROM clause (A # join-op-1 B join-op-2 C) is computed as ((A join-op-1 B) join-op-2 C). # # Tests 28a and 28b show that the statement above is true for this case. # Test 28c shows that if the parenthesis force a different order of # evaluation the result is different. Test 28d verifies that the result # of the query with the parenthesis forcing a different order of evaluation # is as calculated by the [te_*] procs. # set t3_natural_left_join_t2 [ te_tbljoin db t3 t2 -left -using {b} -on {te_equals -nocase b b} ] set t1 [te_read_tbl db t1] te_dataset_eq_unordered $tn.28a [ te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN t2 NATURAL JOIN t1" ] [te_join $t3_natural_left_join_t2 $t1 \ -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ ] te_dataset_eq_unordered $tn.28b [ te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1" ] [te_join $t3_natural_left_join_t2 $t1 \ -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ ] te_dataset_ne_unordered $tn.28c [ te_read_sql db "SELECT * FROM (t3 NATURAL LEFT JOIN t2) NATURAL JOIN t1" ] [ te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)" ] set t2_natural_join_t1 [te_tbljoin db t2 t1 -using {a b} \ -using {a b} -on {te_and {te_equals a a} {te_equals -nocase b b}} \ ] set t3 [te_read_tbl db t3] te_dataset_eq_unordered $tn.28d [ te_read_sql db "SELECT * FROM t3 NATURAL LEFT JOIN (t2 NATURAL JOIN t1)" ] [te_join $t3 $t2_natural_join_t1 \ -left -using {b} -on {te_equals -nocase b b} \ ] } do_execsql_test e_select-2.2.0 { CREATE TABLE t4(x TEXT COLLATE nocase); CREATE TABLE t5(y INTEGER, z TEXT COLLATE binary); INSERT INTO t4 VALUES('2.0'); INSERT INTO t4 VALUES('TWO'); INSERT INTO t5 VALUES(2, 'two'); } {} # EVIDENCE-OF: R-55824-40976 A sub-select specified in the join-source # following the FROM clause in a simple SELECT statement is handled as # if it was a table containing the data returned by executing the # sub-select statement. # # EVIDENCE-OF: R-42612-06757 Each column of the sub-select dataset # inherits the collation sequence and affinity of the corresponding # expression in the sub-select statement. # foreach {tn subselect select spec} { 1 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss%" {t1 %ss%} 2 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss% AS x ON (t1.a=x.a)" {t1 %ss% -on {te_equals 0 0}} 3 "SELECT * FROM t2" "SELECT * FROM %ss% AS x JOIN t1 ON (t1.a=x.a)" {%ss% t1 -on {te_equals 0 0}} 4 "SELECT * FROM t1, t2" "SELECT * FROM %ss% AS x JOIN t3" {%ss% t3} 5 "SELECT * FROM t1, t2" "SELECT * FROM %ss% NATURAL JOIN t3" {%ss% t3 -using b -on {te_equals 1 0}} 6 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL JOIN %ss%" {t3 %ss% -using b -on {te_equals -nocase 0 1}} 7 "SELECT * FROM t1, t2" "SELECT * FROM t3 NATURAL LEFT JOIN %ss%" {t3 %ss% -left -using b -on {te_equals -nocase 0 1}} 8 "SELECT count(*) AS y FROM t4" "SELECT * FROM t5, %ss% USING (y)" {t5 %ss% -using y -on {te_equals -affinity text 0 0}} 9 "SELECT count(*) AS y FROM t4" "SELECT * FROM %ss%, t5 USING (y)" {%ss% t5 -using y -on {te_equals -affinity text 0 0}} 10 "SELECT x AS y FROM t4" "SELECT * FROM %ss% JOIN t5 USING (y)" {%ss% t5 -using y -on {te_equals -nocase -affinity integer 0 0}} 11 "SELECT x AS y FROM t4" "SELECT * FROM t5 JOIN %ss% USING (y)" {t5 %ss% -using y -on {te_equals -nocase -affinity integer 0 0}} 12 "SELECT y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)" {%ss% t4 -using x -on {te_equals -nocase -affinity integer 0 0}} 13 "SELECT y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)" {t4 %ss% -using x -on {te_equals -nocase -affinity integer 0 0}} 14 "SELECT +y AS x FROM t5" "SELECT * FROM %ss% JOIN t4 USING (x)" {%ss% t4 -using x -on {te_equals -nocase -affinity text 0 0}} 15 "SELECT +y AS x FROM t5" "SELECT * FROM t4 JOIN %ss% USING (x)" {t4 %ss% -using x -on {te_equals -nocase -affinity text 0 0}} } { # Create a temporary table named %ss% containing the data returned by # the sub-select. Then have the [te_tbljoin] proc use this table to # compute the expected results of the $select query. Drop the temporary # table before continuing. # execsql "CREATE TEMP TABLE '%ss%' AS $subselect" set te [eval te_tbljoin db $spec] execsql "DROP TABLE '%ss%'" # Check that the actual data returned by the $select query is the same # as the expected data calculated using [te_tbljoin] above. # te_dataset_eq_unordered e_select-2.2.1.$tn [ te_read_sql db [string map [list %ss% "($subselect)"] $select] ] $te } finish_test |
Added test/e_update.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 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 | # 2010 September 20 # # 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 to verify that the "testable statements" in # the lang_update.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl #-------------------- # Test organization: # # e_update-1.*: Test statements describing the workings of UPDATE statements. # # e_update-2.*: Test the restrictions on the UPDATE statement syntax that # can be used within triggers. # # e_update-3.*: Test the special LIMIT/OFFSET and ORDER BY clauses that can # be used with UPDATE when SQLite is compiled with # SQLITE_ENABLE_UPDATE_DELETE_LIMIT. # forcedelete test.db2 do_execsql_test e_update-0.0 { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b, c); CREATE TABLE t3(a, b UNIQUE); CREATE TABLE t6(x, y); CREATE INDEX i1 ON t1(a); CREATE TEMP TABLE t4(x, y); CREATE TEMP TABLE t6(x, y); CREATE TABLE aux.t1(a, b); CREATE TABLE aux.t5(a, b); } {} proc do_update_tests {args} { uplevel do_select_tests $args } # EVIDENCE-OF: R-05685-44205 -- syntax diagram update-stmt # do_update_tests e_update-0 { 1 "UPDATE t1 SET a=10" {} 2 "UPDATE t1 SET a=10, b=5" {} 3 "UPDATE t1 SET a=10 WHERE b=5" {} 4 "UPDATE t1 SET b=5,a=10 WHERE 1" {} 5 "UPDATE main.t1 SET a=10" {} 6 "UPDATE main.t1 SET a=10, b=5" {} 7 "UPDATE main.t1 SET a=10 WHERE b=5" {} 9 "UPDATE OR ROLLBACK t1 SET a=10" {} 10 "UPDATE OR ROLLBACK t1 SET a=10, b=5" {} 11 "UPDATE OR ROLLBACK t1 SET a=10 WHERE b=5" {} 12 "UPDATE OR ROLLBACK t1 SET b=5,a=10 WHERE 1" {} 13 "UPDATE OR ROLLBACK main.t1 SET a=10" {} 14 "UPDATE OR ROLLBACK main.t1 SET a=10, b=5" {} 15 "UPDATE OR ROLLBACK main.t1 SET a=10 WHERE b=5" {} 16 "UPDATE OR ROLLBACK main.t1 SET b=5,a=10 WHERE 1" {} 17 "UPDATE OR ABORT t1 SET a=10" {} 18 "UPDATE OR ABORT t1 SET a=10, b=5" {} 19 "UPDATE OR ABORT t1 SET a=10 WHERE b=5" {} 20 "UPDATE OR ABORT t1 SET b=5,a=10 WHERE 1" {} 21 "UPDATE OR ABORT main.t1 SET a=10" {} 22 "UPDATE OR ABORT main.t1 SET a=10, b=5" {} 23 "UPDATE OR ABORT main.t1 SET a=10 WHERE b=5" {} 24 "UPDATE OR ABORT main.t1 SET b=5,a=10 WHERE 1" {} 25 "UPDATE OR REPLACE t1 SET a=10" {} 26 "UPDATE OR REPLACE t1 SET a=10, b=5" {} 27 "UPDATE OR REPLACE t1 SET a=10 WHERE b=5" {} 28 "UPDATE OR REPLACE t1 SET b=5,a=10 WHERE 1" {} 29 "UPDATE OR REPLACE main.t1 SET a=10" {} 30 "UPDATE OR REPLACE main.t1 SET a=10, b=5" {} 31 "UPDATE OR REPLACE main.t1 SET a=10 WHERE b=5" {} 32 "UPDATE OR REPLACE main.t1 SET b=5,a=10 WHERE 1" {} 33 "UPDATE OR FAIL t1 SET a=10" {} 34 "UPDATE OR FAIL t1 SET a=10, b=5" {} 35 "UPDATE OR FAIL t1 SET a=10 WHERE b=5" {} 36 "UPDATE OR FAIL t1 SET b=5,a=10 WHERE 1" {} 37 "UPDATE OR FAIL main.t1 SET a=10" {} 38 "UPDATE OR FAIL main.t1 SET a=10, b=5" {} 39 "UPDATE OR FAIL main.t1 SET a=10 WHERE b=5" {} 40 "UPDATE OR FAIL main.t1 SET b=5,a=10 WHERE 1" {} 41 "UPDATE OR IGNORE t1 SET a=10" {} 42 "UPDATE OR IGNORE t1 SET a=10, b=5" {} 43 "UPDATE OR IGNORE t1 SET a=10 WHERE b=5" {} 44 "UPDATE OR IGNORE t1 SET b=5,a=10 WHERE 1" {} 45 "UPDATE OR IGNORE main.t1 SET a=10" {} 46 "UPDATE OR IGNORE main.t1 SET a=10, b=5" {} 47 "UPDATE OR IGNORE main.t1 SET a=10 WHERE b=5" {} 48 "UPDATE OR IGNORE main.t1 SET b=5,a=10 WHERE 1" {} } # EVIDENCE-OF: R-38515-45264 An UPDATE statement is used to modify a # subset of the values stored in zero or more rows of the database table # identified by the qualified-table-name specified as part of the UPDATE # statement. # # Test cases e_update-1.1.1.* test the "identified by the # qualified-table-name" part of the statement above. Tests # e_update-1.1.2.* show that the "zero or more rows" part is # accurate. # do_execsql_test e_update-1.1.0 { INSERT INTO main.t1 VALUES(1, 'i'); INSERT INTO main.t1 VALUES(2, 'ii'); INSERT INTO main.t1 VALUES(3, 'iii'); INSERT INTO aux.t1 VALUES(1, 'I'); INSERT INTO aux.t1 VALUES(2, 'II'); INSERT INTO aux.t1 VALUES(3, 'III'); } {} do_update_tests e_update-1.1 { 1.1 "UPDATE t1 SET a = a+1; SELECT * FROM t1" {2 i 3 ii 4 iii} 1.2 "UPDATE main.t1 SET a = a+1; SELECT * FROM main.t1" {3 i 4 ii 5 iii} 1.3 "UPDATE aux.t1 SET a = a+1; SELECT * FROM aux.t1" {2 I 3 II 4 III} 2.1 "UPDATE t1 SET a = a+1 WHERE a = 1; SELECT * FROM t1" {3 i 4 ii 5 iii} 2.2 "UPDATE t1 SET a = a+1 WHERE a = 4; SELECT * FROM t1" {3 i 5 ii 5 iii} } # EVIDENCE-OF: R-55869-30521 If the UPDATE statement does not have a # WHERE clause, all rows in the table are modified by the UPDATE. # do_execsql_test e_update-1.2.0 { DELETE FROM main.t1; INSERT INTO main.t1 VALUES(1, 'i'); INSERT INTO main.t1 VALUES(2, 'ii'); INSERT INTO main.t1 VALUES(3, 'iii'); } {} do_update_tests e_update-1.2 { 1 "UPDATE t1 SET b = 'roman' ; SELECT * FROM t1" {1 roman 2 roman 3 roman} 2 "UPDATE t1 SET a = 'greek' ; SELECT * FROM t1" {greek roman greek roman greek roman} } # EVIDENCE-OF: R-42117-40023 Otherwise, the UPDATE affects only those # rows for which the result of evaluating the WHERE clause expression as # a boolean expression is true. # do_execsql_test e_update-1.3.0 { DELETE FROM main.t1; INSERT INTO main.t1 VALUES(NULL, ''); INSERT INTO main.t1 VALUES(1, 'i'); INSERT INTO main.t1 VALUES(2, 'ii'); INSERT INTO main.t1 VALUES(3, 'iii'); } {} do_update_tests e_update-1.3 { 1 "UPDATE t1 SET b = 'roman' WHERE a<2 ; SELECT * FROM t1" {{} {} 1 roman 2 ii 3 iii} 2 "UPDATE t1 SET b = 'egyptian' WHERE (a-3)/10.0 ; SELECT * FROM t1" {{} {} 1 egyptian 2 egyptian 3 iii} 3 "UPDATE t1 SET b = 'macedonian' WHERE a; SELECT * FROM t1" {{} {} 1 macedonian 2 macedonian 3 macedonian} 4 "UPDATE t1 SET b = 'lithuanian' WHERE a IS NULL; SELECT * FROM t1" {{} lithuanian 1 macedonian 2 macedonian 3 macedonian} } # EVIDENCE-OF: R-58129-20729 It is not an error if the WHERE clause does # not evaluate to true for any row in the table - this just means that # the UPDATE statement affects zero rows. # do_execsql_test e_update-1.4.0 { DELETE FROM main.t1; INSERT INTO main.t1 VALUES(NULL, ''); INSERT INTO main.t1 VALUES(1, 'i'); INSERT INTO main.t1 VALUES(2, 'ii'); INSERT INTO main.t1 VALUES(3, 'iii'); } {} do_update_tests e_update-1.4 -query { SELECT * FROM t1 } { 1 "UPDATE t1 SET b = 'burmese' WHERE a=5" {{} {} 1 i 2 ii 3 iii} 2 "UPDATE t1 SET b = 'burmese' WHERE length(b)<1 AND a IS NOT NULL" {{} {} 1 i 2 ii 3 iii} 3 "UPDATE t1 SET b = 'burmese' WHERE 0" {{} {} 1 i 2 ii 3 iii} 4 "UPDATE t1 SET b = 'burmese' WHERE (SELECT a FROM t1 WHERE rowid=1)" {{} {} 1 i 2 ii 3 iii} } # EVIDENCE-OF: R-40598-36595 For each affected row, the named columns # are set to the values found by evaluating the corresponding scalar # expressions. # # EVIDENCE-OF: R-40472-60438 Columns that do not appear in the list of # assignments are left unmodified. # do_execsql_test e_update-1.5.0 { INSERT INTO t2(rowid, a, b, c) VALUES(1, 3, 1, 4); INSERT INTO t2(rowid, a, b, c) VALUES(2, 1, 5, 9); INSERT INTO t2(rowid, a, b, c) VALUES(3, 2, 6, 5); } {} do_update_tests e_update-1.5 -query { SELECT * FROM t2 } { 1 "UPDATE t2 SET c = 1+1 WHERE a=2" {3 1 4 1 5 9 2 6 2} 2 "UPDATE t2 SET b = 4/2, c=CAST((0.4*5) AS INTEGER) WHERE a<3" {3 1 4 1 2 2 2 2 2} 3 "UPDATE t2 SET a = 1" {1 1 4 1 2 2 1 2 2} 4 "UPDATE t2 SET b = (SELECT count(*)+2 FROM t2), c = 24/3+1 WHERE rowid=2" {1 1 4 1 5 9 1 2 2} 5 "UPDATE t2 SET a = 3 WHERE c = 4" {3 1 4 1 5 9 1 2 2} 6 "UPDATE t2 SET a = b WHERE rowid>2" {3 1 4 1 5 9 2 2 2} 6 "UPDATE t2 SET b=6, c=5 WHERE a=b AND b=c" {3 1 4 1 5 9 2 6 5} } # EVIDENCE-OF: R-09060-20018 If a single column-name appears more than # once in the list of assignment expressions, all but the rightmost # occurence is ignored. # do_update_tests e_update-1.6 -query { SELECT * FROM t2 } { 1 "UPDATE t2 SET c=5, c=6, c=7 WHERE rowid=1" {3 1 7 1 5 9 2 6 5} 2 "UPDATE t2 SET c=7, c=6, c=5 WHERE rowid=1" {3 1 5 1 5 9 2 6 5} 3 "UPDATE t2 SET c=5, b=6, c=7 WHERE rowid=1" {3 6 7 1 5 9 2 6 5} } # EVIDENCE-OF: R-36239-04077 The scalar expressions may refer to columns # of the row being updated. # # EVIDENCE-OF: R-04558-24451 In this case all scalar expressions are # evaluated before any assignments are made. # do_execsql_test e_update-1.7.0 { DELETE FROM t2; INSERT INTO t2(rowid, a, b, c) VALUES(1, 3, 1, 4); INSERT INTO t2(rowid, a, b, c) VALUES(2, 1, 5, 9); INSERT INTO t2(rowid, a, b, c) VALUES(3, 2, 6, 5); } {} do_update_tests e_update-1.7 -query { SELECT * FROM t2 } { 1 "UPDATE t2 SET a=b+c" {5 1 4 14 5 9 11 6 5} 2 "UPDATE t2 SET a=b, b=a" {1 5 4 5 14 9 6 11 5} 3 "UPDATE t2 SET a=c||c, c=NULL" {44 5 {} 99 14 {} 55 11 {}} } # EVIDENCE-OF: R-12619-24112 The optional conflict-clause allows the # user to nominate a specific constraint conflict resolution algorithm # to use during this one UPDATE command. # do_execsql_test e_update-1.8.0 { DELETE FROM t3; INSERT INTO t3 VALUES(1, 'one'); INSERT INTO t3 VALUES(2, 'two'); INSERT INTO t3 VALUES(3, 'three'); INSERT INTO t3 VALUES(4, 'four'); } {} foreach {tn sql error ac data } { 1 "UPDATE t3 SET b='one' WHERE a=3" {column b is not unique} 1 {1 one 2 two 3 three 4 four} 2 "UPDATE OR REPLACE t3 SET b='one' WHERE a=3" {} 1 {2 two 3 one 4 four} 3 "UPDATE OR FAIL t3 SET b='three'" {column b is not unique} 1 {2 three 3 one 4 four} 4 "UPDATE OR IGNORE t3 SET b='three' WHERE a=3" {} 1 {2 three 3 one 4 four} 5 "UPDATE OR ABORT t3 SET b='three' WHERE a=3" {column b is not unique} 1 {2 three 3 one 4 four} 6 "BEGIN" {} 0 {2 three 3 one 4 four} 7 "UPDATE t3 SET b='three' WHERE a=3" {column b is not unique} 0 {2 three 3 one 4 four} 8 "UPDATE OR ABORT t3 SET b='three' WHERE a=3" {column b is not unique} 0 {2 three 3 one 4 four} 9 "UPDATE OR FAIL t3 SET b='two'" {column b is not unique} 0 {2 two 3 one 4 four} 10 "UPDATE OR IGNORE t3 SET b='four' WHERE a=3" {} 0 {2 two 3 one 4 four} 11 "UPDATE OR REPLACE t3 SET b='four' WHERE a=3" {} 0 {2 two 3 four} 12 "UPDATE OR ROLLBACK t3 SET b='four'" {column b is not unique} 1 {2 three 3 one 4 four} } { do_catchsql_test e_update-1.8.$tn.1 $sql [list [expr {$error!=""}] $error] do_execsql_test e_update-1.8.$tn.2 {SELECT * FROM t3} [list {*}$data] do_test e_update-1.8.$tn.3 {sqlite3_get_autocommit db} $ac } # EVIDENCE-OF: R-12123-54095 The table-name specified as part of an # UPDATE statement within a trigger body must be unqualified. # # EVIDENCE-OF: R-09690-36749 In other words, the database-name. prefix # on the table name of the UPDATE is not allowed within triggers. # do_update_tests e_update-2.1 -error { qualified table names are not allowed on INSERT, UPDATE, and DELETE statements within triggers } { 1 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN UPDATE main.t2 SET a=1, b=2, c=3; END; } {} 2 { CREATE TRIGGER tr1 BEFORE UPDATE ON t2 BEGIN UPDATE aux.t1 SET a=1, b=2; END; } {} 3 { CREATE TRIGGER tr1 AFTER DELETE ON t4 BEGIN UPDATE main.t1 SET a=1, b=2; END; } {} } # EVIDENCE-OF: R-06085-13761 Unless the table to which the trigger is # attached is in the TEMP database, the table being updated by the # trigger program must reside in the same database as it. # do_update_tests e_update-2.2 -error { no such table: %s } { 1 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN UPDATE t4 SET x=x+1; END; INSERT INTO t1 VALUES(1, 2); } "main.t4" 2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t5 BEGIN UPDATE t4 SET x=x+1; END; INSERT INTO t5 VALUES(1, 2); } "aux.t4" } do_execsql_test e_update-2.2.X { DROP TRIGGER tr1; DROP TRIGGER aux.tr1; } {} # EVIDENCE-OF: R-29512-54644 If the table to which the trigger is # attached is in the TEMP database, then the unqualified name of the # table being updated is resolved in the same way as it is for a # top-level statement (by searching first the TEMP database, then the # main database, then any other databases in the order they were # attached). # do_execsql_test e_update-2.3.0 { SELECT 'main', tbl_name FROM main.sqlite_master WHERE type = 'table' UNION ALL SELECT 'temp', tbl_name FROM sqlite_temp_master WHERE type = 'table' UNION ALL SELECT 'aux', tbl_name FROM aux.sqlite_master WHERE type = 'table' } [list {*}{ main t1 main t2 main t3 main t6 temp t4 temp t6 aux t1 aux t5 }] do_execsql_test e_update-2.3.1 { DELETE FROM main.t6; DELETE FROM temp.t6; INSERT INTO main.t6 VALUES(1, 2); INSERT INTO temp.t6 VALUES(1, 2); CREATE TRIGGER temp.tr1 AFTER INSERT ON t4 BEGIN UPDATE t6 SET x=x+1; END; INSERT INTO t4 VALUES(1, 2); SELECT * FROM main.t6; SELECT * FROM temp.t6; } {1 2 2 2} do_execsql_test e_update-2.3.2 { DELETE FROM main.t1; DELETE FROM aux.t1; INSERT INTO main.t1 VALUES(1, 2); INSERT INTO aux.t1 VALUES(1, 2); CREATE TRIGGER temp.tr2 AFTER DELETE ON t4 BEGIN UPDATE t1 SET a=a+1; END; DELETE FROM t4; SELECT * FROM main.t1; SELECT * FROM aux.t1; } {2 2 1 2} do_execsql_test e_update-2.3.3 { DELETE FROM aux.t5; INSERT INTO aux.t5 VALUES(1, 2); INSERT INTO t4 VALUES('x', 'y'); CREATE TRIGGER temp.tr3 AFTER UPDATE ON t4 BEGIN UPDATE t5 SET a=a+1; END; UPDATE t4 SET x=10; SELECT * FROM aux.t5; } {2 2} # EVIDENCE-OF: R-19619-42762 The INDEXED BY and NOT INDEXED clauses are # not allowed on UPDATE statements within triggers. # do_update_tests e_update-2.4 -error { the %s %s clause is not allowed on UPDATE or DELETE statements within triggers } { 1 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 INDEXED BY i1 SET a=a+1; END; } {INDEXED BY} 2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 NOT INDEXED SET a=a+1; END; } {NOT INDEXED} } ifcapable update_delete_limit { # EVIDENCE-OF: R-57359-59558 The LIMIT and ORDER BY clauses for UPDATE # are unsupported within triggers, regardless of the compilation options # used to build SQLite. # do_update_tests e_update-2.5 -error { near "%s": syntax error } { 1 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 SET a=a+1 LIMIT 10; END; } {LIMIT} 2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 SET a=a+1 ORDER BY a LIMIT 10; END; } {ORDER} 3 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 SET a=a+1 ORDER BY a LIMIT 10 OFFSET 2; END; } {ORDER} 4 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN UPDATE t1 SET a=a+1 LIMIT 10 OFFSET 2; END; } {LIMIT} } # EVIDENCE-OF: R-59581-44104 If SQLite is built with the # SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option then the syntax # of the UPDATE statement is extended with optional ORDER BY and LIMIT # clauses # # EVIDENCE-OF: R-08948-01887 -- syntax diagram update-stmt-limited # do_update_tests e_update-3.0 { 1 "UPDATE t1 SET a=b LIMIT 5" {} 2 "UPDATE t1 SET a=b LIMIT 5-1 OFFSET 2+2" {} 3 "UPDATE t1 SET a=b LIMIT 2+2, 16/4" {} 4 "UPDATE t1 SET a=b ORDER BY a LIMIT 5" {} 5 "UPDATE t1 SET a=b ORDER BY a LIMIT 5-1 OFFSET 2+2" {} 6 "UPDATE t1 SET a=b ORDER BY a LIMIT 2+2, 16/4" {} 7 "UPDATE t1 SET a=b WHERE a>2 LIMIT 5" {} 8 "UPDATE t1 SET a=b WHERE a>2 LIMIT 5-1 OFFSET 2+2" {} 9 "UPDATE t1 SET a=b WHERE a>2 LIMIT 2+2, 16/4" {} 10 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 5" {} 11 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 5-1 OFFSET 2+2" {} 12 "UPDATE t1 SET a=b WHERE a>2 ORDER BY a LIMIT 2+2, 16/4" {} } do_execsql_test e_update-3.1.0 { CREATE TABLE t7(q, r, s); INSERT INTO t7 VALUES(1, 'one', 'X'); INSERT INTO t7 VALUES(2, 'two', 'X'); INSERT INTO t7 VALUES(3, 'three', 'X'); INSERT INTO t7 VALUES(4, 'four', 'X'); INSERT INTO t7 VALUES(5, 'five', 'X'); INSERT INTO t7 VALUES(6, 'six', 'X'); INSERT INTO t7 VALUES(7, 'seven', 'X'); INSERT INTO t7 VALUES(8, 'eight', 'X'); INSERT INTO t7 VALUES(9, 'nine', 'X'); INSERT INTO t7 VALUES(10, 'ten', 'X'); } {} # EVIDENCE-OF: R-58862-44169 If an UPDATE statement has a LIMIT clause, # the maximum number of rows that will be updated is found by evaluating # the accompanying expression and casting it to an integer value. # do_update_tests e_update-3.1 -query { SELECT s FROM t7 } { 1 "UPDATE t7 SET s = q LIMIT 5" {1 2 3 4 5 X X X X X} 2 "UPDATE t7 SET s = r WHERE q>2 LIMIT 4" {1 2 three four five six X X X X} 3 "UPDATE t7 SET s = q LIMIT 0" {1 2 three four five six X X X X} } # EVIDENCE-OF: R-63582-45120 A negative value is interpreted as "no limit". # do_update_tests e_update-3.2 -query { SELECT s FROM t7 } { 1 "UPDATE t7 SET s = q LIMIT -1" {1 2 3 4 5 6 7 8 9 10} 2 "UPDATE t7 SET s = r WHERE q>4 LIMIT -1" {1 2 3 4 five six seven eight nine ten} 3 "UPDATE t7 SET s = 'X' LIMIT -1" {X X X X X X X X X X} } # EVIDENCE-OF: R-18628-11938 If the LIMIT expression evaluates to # non-negative value N and the UPDATE statement has an ORDER BY clause, # then all rows that would be updated in the absence of the LIMIT clause # are sorted according to the ORDER BY and the first N updated. # do_update_tests e_update-3.3 -query { SELECT s FROM t7 } { 1 "UPDATE t7 SET s = q ORDER BY r LIMIT 3" {X X X 4 5 X X 8 X X} 2 "UPDATE t7 SET s = r ORDER BY r DESC LIMIT 2" {X two three 4 5 X X 8 X X} 3 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 5" {X two three 4 5 6 7 8 9 10} X "UPDATE t7 SET s = 'X'" {X X X X X X X X X X} } # EVIDENCE-OF: R-30955-38324 If the UPDATE statement also has an OFFSET # clause, then it is similarly evaluated and cast to an integer value. # If the OFFSET expression evaluates to a non-negative value M, then the # first M rows are skipped and the following N rows updated instead. # do_update_tests e_update-3.3 -query { SELECT s FROM t7 } { 1 "UPDATE t7 SET s = q ORDER BY q LIMIT 3 OFFSET 2" {X X 3 4 5 X X X X X} 2 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 2, 3 " {X X 3 4 5 6 7 8 X X} X "UPDATE t7 SET s = 'X'" {X X X X X X X X X X} } # EVIDENCE-OF: R-19486-35828 If the UPDATE statement has no ORDER BY # clause, then all rows that would be updated in the absence of the # LIMIT clause are assembled in an arbitrary order before applying the # LIMIT and OFFSET clauses to determine which are actually updated. # # In practice, "arbitrary order" is rowid order. This is also tested # by e_update-3.2.* above. # do_update_tests e_update-3.4 -query { SELECT s FROM t7 } { 1 "UPDATE t7 SET s = q LIMIT 4, 2" {X X X X 5 6 X X X X} 2 "UPDATE t7 SET s = q LIMIT 2 OFFSET 7" {X X X X 5 6 X 8 9 X} } # EVIDENCE-OF: R-10927-26133 The ORDER BY clause on an UPDATE statement # is used only to determine which rows fall within the LIMIT. The order # in which rows are modified is arbitrary and is not influenced by the # ORDER BY clause. # do_execsql_test e_update-3.5.0 { CREATE TABLE t8(x); CREATE TRIGGER tr7 BEFORE UPDATE ON t7 BEGIN INSERT INTO t8 VALUES(old.q); END; } {} do_update_tests e_update-3.5 -query { SELECT x FROM t8 ; DELETE FROM t8 } { 1 "UPDATE t7 SET s = q ORDER BY r LIMIT -1" {1 2 3 4 5 6 7 8 9 10} 2 "UPDATE t7 SET s = q ORDER BY r ASC LIMIT -1" {1 2 3 4 5 6 7 8 9 10} 3 "UPDATE t7 SET s = q ORDER BY r DESC LIMIT -1" {1 2 3 4 5 6 7 8 9 10} 4 "UPDATE t7 SET s = q ORDER BY q DESC LIMIT 5" {6 7 8 9 10} } } ;# ifcapable update_delete_limit finish_test |
Added test/e_vacuum.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 | # 2010 September 24 # # 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 to verify that the "testable statements" in # the lang_vacuum.html document are correct. # set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_test_control_pending_byte 0x1000000 proc create_db {{sql ""}} { catch { db close } forcedelete test.db sqlite3 db test.db db transaction { execsql { PRAGMA page_size = 1024; } execsql $sql execsql { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(1, randomblob(400)); INSERT INTO t1 SELECT a+1, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+2, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+4, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+8, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+16, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+32, randomblob(400) FROM t1; INSERT INTO t1 SELECT a+64, randomblob(400) FROM t1; CREATE TABLE t2(a PRIMARY KEY, b UNIQUE); INSERT INTO t2 SELECT * FROM t1; } } return [expr {[file size test.db] / 1024}] } # This proc returns the number of contiguous blocks of pages that make up # the table or index named by the only argument. For example, if the table # occupies database pages 3, 4, 8 and 9, then this command returns 2 (there # are 2 fragments - one consisting of pages 3 and 4, the other of fragments # 8 and 9). # proc fragment_count {name} { execsql { CREATE VIRTUAL TABLE temp.stat USING dbstat } set nFrag 1 db eval {SELECT pageno FROM stat WHERE name = 't1' ORDER BY pageno} { if {[info exists prevpageno] && $prevpageno != $pageno-1} { incr nFrag } set prevpageno $pageno } execsql { DROP TABLE temp.stat } set nFrag } # EVIDENCE-OF: R-63707-33375 -- syntax diagram vacuum-stmt # do_execsql_test e_vacuum-0.1 { VACUUM } {} # EVIDENCE-OF: R-51469-36013 Unless SQLite is running in # "auto_vacuum=FULL" mode, when a large amount of data is deleted from # the database file it leaves behind empty space, or "free" database # pages. # # EVIDENCE-OF: R-60541-63059 Running VACUUM to rebuild the database # reclaims this space and reduces the size of the database file. # foreach {tn avmode sz} { 1 none 7 2 full 8 3 incremental 8 } { set nPage [create_db "PRAGMA auto_vacuum = $avmode"] do_execsql_test e_vacuum-1.1.$tn.1 { DELETE FROM t1; DELETE FROM t2; } {} if {$avmode == "full"} { # This branch tests the "unless ... auto_vacuum=FULL" in the requirement # above. If auto_vacuum is set to FULL, then no empty space is left in # the database file. do_execsql_test e_vacuum-1.1.$tn.2 {PRAGMA freelist_count} 0 } else { set freelist [expr {$nPage - $sz}] if {$avmode == "incremental"} { # The page size is 1024 bytes. Therefore, assuming the database contains # somewhere between 207 and 411 pages (it does), there are 2 pointer-map # pages. incr freelist -2 } do_execsql_test e_vacuum-1.1.$tn.3 {PRAGMA freelist_count} $freelist do_execsql_test e_vacuum-1.1.$tn.4 {VACUUM} {} } do_test e_vacuum-1.1.$tn.5 { expr {[file size test.db] / 1024} } $sz } # EVIDENCE-OF: R-50943-18433 Frequent inserts, updates, and deletes can # cause the database file to become fragmented - where data for a single # table or index is scattered around the database file. # # EVIDENCE-OF: R-05791-54928 Running VACUUM ensures that each table and # index is largely stored contiguously within the database file. # # e_vacuum-1.2.1 - Perform many INSERT, UPDATE and DELETE ops on table t1. # e_vacuum-1.2.2 - Verify that t1 and its indexes are now quite fragmented. # e_vacuum-1.2.3 - Run VACUUM. # e_vacuum-1.2.4 - Verify that t1 and its indexes are now much # less fragmented. # ifcapable vtab { create_db register_dbstat_vtab db do_execsql_test e_vacuum-1.2.1 { DELETE FROM t1 WHERE a%2; INSERT INTO t1 SELECT b, a FROM t2 WHERE a%2; UPDATE t1 SET b=randomblob(600) WHERE (a%2)==0; } {} do_test e_vacuum-1.2.2.1 { expr [fragment_count t1]>100 } 1 do_test e_vacuum-1.2.2.2 { expr [fragment_count sqlite_autoindex_t1_1]>100 } 1 do_test e_vacuum-1.2.2.3 { expr [fragment_count sqlite_autoindex_t1_2]>100 } 1 do_execsql_test e_vacuum-1.2.3 { VACUUM } {} # In practice, the tables and indexes each end up stored as two fragments - # one containing the root page and another containing all other pages. # do_test e_vacuum-1.2.4.1 { fragment_count t1 } 2 do_test e_vacuum-1.2.4.2 { fragment_count sqlite_autoindex_t1_1 } 2 do_test e_vacuum-1.2.4.3 { fragment_count sqlite_autoindex_t1_2 } 2 } # EVIDENCE-OF: R-20474-44465 Normally, the database page_size and # whether or not the database supports auto_vacuum must be configured # before the database file is actually created. # do_test e_vacuum-1.3.1.1 { create_db "PRAGMA page_size = 1024 ; PRAGMA auto_vacuum = FULL" execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {1024 1} do_test e_vacuum-1.3.1.2 { execsql { PRAGMA page_size = 2048 } execsql { PRAGMA auto_vacuum = NONE } execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {1024 1} # EVIDENCE-OF: R-08570-19916 However, when not in write-ahead log mode, # the page_size and/or auto_vacuum properties of an existing database # may be changed by using the page_size and/or pragma auto_vacuum # pragmas and then immediately VACUUMing the database. # do_test e_vacuum-1.3.2.1 { execsql { PRAGMA journal_mode = delete } execsql { PRAGMA page_size = 2048 } execsql { PRAGMA auto_vacuum = NONE } execsql VACUUM execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {2048 0} # EVIDENCE-OF: R-48521-51450 When in write-ahead log mode, only the # auto_vacuum support property can be changed using VACUUM. # do_test e_vacuum-1.3.3.1 { execsql { PRAGMA journal_mode = wal } execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {2048 0} do_test e_vacuum-1.3.3.2 { execsql { PRAGMA page_size = 1024 } execsql { PRAGMA auto_vacuum = FULL } execsql VACUUM execsql { PRAGMA page_size ; PRAGMA auto_vacuum } } {2048 1} # EVIDENCE-OF: R-38001-03952 VACUUM only works on the main database. It # is not possible to VACUUM an attached database file. forcedelete test.db2 create_db { PRAGMA auto_vacuum = NONE } do_execsql_test e_vacuum-2.1.1 { ATTACH 'test.db2' AS aux; PRAGMA aux.page_size = 1024; CREATE TABLE aux.t3 AS SELECT * FROM t1; DELETE FROM t3; } {} set original_size [file size test.db2] # Try everything we can think of to get the aux database vacuumed: do_execsql_test e_vacuum-2.1.3 { VACUUM } {} do_execsql_test e_vacuum-2.1.4 { VACUUM aux } {} do_execsql_test e_vacuum-2.1.5 { VACUUM 'test.db2' } {} # Despite our efforts, space in the aux database has not been reclaimed: do_test e_vacuum-2.1.6 { expr {[file size test.db2]==$::original_size} } 1 # EVIDENCE-OF: R-17495-17419 The VACUUM command may change the ROWIDs of # entries in any tables that do not have an explicit INTEGER PRIMARY # KEY. # # Tests e_vacuum-3.1.1 - 3.1.2 demonstrate that rowids can change when # a database is VACUUMed. Tests e_vacuum-3.1.3 - 3.1.4 show that adding # an INTEGER PRIMARY KEY column to a table stops this from happening. # do_execsql_test e_vacuum-3.1.1 { CREATE TABLE t4(x); INSERT INTO t4(x) VALUES('x'); INSERT INTO t4(x) VALUES('y'); INSERT INTO t4(x) VALUES('z'); DELETE FROM t4 WHERE x = 'y'; SELECT rowid, x FROM t4; } {1 x 3 z} do_execsql_test e_vacuum-3.1.2 { VACUUM; SELECT rowid, x FROM t4; } {1 x 2 z} do_execsql_test e_vacuum-3.1.3 { CREATE TABLE t5(x, y INTEGER PRIMARY KEY); INSERT INTO t5(x) VALUES('x'); INSERT INTO t5(x) VALUES('y'); INSERT INTO t5(x) VALUES('z'); DELETE FROM t5 WHERE x = 'y'; SELECT rowid, x FROM t5; } {1 x 3 z} do_execsql_test e_vacuum-3.1.4 { VACUUM; SELECT rowid, x FROM t5; } {1 x 3 z} # EVIDENCE-OF: R-49563-33883 A VACUUM will fail if there is an open # transaction, or if there are one or more active SQL statements when it # is run. # do_execsql_test e_vacuum-3.2.1.1 { BEGIN } {} do_catchsql_test e_vacuum-3.2.1.2 { VACUUM } {1 {cannot VACUUM from within a transaction}} do_execsql_test e_vacuum-3.2.1.3 { COMMIT } {} do_execsql_test e_vacuum-3.2.1.4 { VACUUM } {} do_execsql_test e_vacuum-3.2.1.5 { SAVEPOINT x } {} do_catchsql_test e_vacuum-3.2.1.6 { VACUUM } {1 {cannot VACUUM from within a transaction}} do_execsql_test e_vacuum-3.2.1.7 { COMMIT } {} do_execsql_test e_vacuum-3.2.1.8 { VACUUM } {} create_db do_test e_vacuum-3.2.2.1 { set res "" db eval { SELECT a FROM t1 } { if {$a == 10} { set res [catchsql VACUUM] } } set res } {1 {cannot VACUUM - SQL statements in progress}} # EVIDENCE-OF: R-38735-12540 As of SQLite version 3.1, an alternative to # using the VACUUM command to reclaim space after data has been deleted # is auto-vacuum mode, enabled using the auto_vacuum pragma. # do_test e_vacuum-3.3.1 { create_db { PRAGMA auto_vacuum = FULL } execsql { PRAGMA auto_vacuum } } {1} # EVIDENCE-OF: R-64844-34873 When auto_vacuum is enabled for a database # free pages may be reclaimed after deleting data, causing the file to # shrink, without rebuilding the entire database using VACUUM. # do_test e_vacuum-3.3.2.1 { create_db { PRAGMA auto_vacuum = FULL } execsql { DELETE FROM t1; DELETE FROM t2; } expr {[file size test.db] / 1024} } {8} do_test e_vacuum-3.3.2.2 { create_db { PRAGMA auto_vacuum = INCREMENTAL } execsql { DELETE FROM t1; DELETE FROM t2; PRAGMA incremental_vacuum; } expr {[file size test.db] / 1024} } {8} finish_test |
Added test/enc4.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 | # 2010 Sept 29 # # 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 regression tests for SQLite library. The focus of # this file is testing the SQLite routines used for converting between the # various suported unicode encodings (UTF-8, UTF-16, UTF-16le and # UTF-16be). # # $Id: enc4.test,v 1.0 2010/09/29 08:29:32 shaneh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # If UTF16 support is disabled, ignore the tests in this file # ifcapable {!utf16} { finish_test return } db close # The three unicode encodings understood by SQLite. set encodings [list UTF-8 UTF-16le UTF-16be] # initial value to use in SELECT set inits [list 1 1.0 1. 1e0] # vals set vals [list\ "922337203685477580792233720368547758079223372036854775807"\ "100000000000000000000000000000000000000000000000000000000"\ "1.0000000000000000000000000000000000000000000000000000000"\ ] set i 1 foreach enc $encodings { file delete -force test.db sqlite3 db test.db db eval "PRAGMA encoding = \"$enc\"" do_test enc4-$i.1 { db eval {PRAGMA encoding} } $enc set j 1 foreach init $inits { do_test enc4-$i.$j.2 { set S [sqlite3_prepare_v2 db "SELECT $init+?" -1 dummy] sqlite3_expired $S } {0} set k 1 foreach val $vals { for {set x 1} {$x<18} {incr x} { set part [expr $init + [string range $val 0 [expr $x-1]]] regsub {e\+0} $part {e+} part regsub {^1e} $part {1.0e} part do_test enc4-$i.$j.$k.3.$x { sqlite3_reset $S sqlite3_bind_text $S 1 $val $x sqlite3_step $S sqlite3_column_text $S 0 } [list $part] do_test enc4-$i.$j.$k.4.$x { sqlite3_reset $S sqlite3_bind_text16 $S 1 [encoding convertto unicode $val] [expr $x*2] sqlite3_step $S sqlite3_column_text $S 0 } [list $part] } incr k } do_test enc4-$i.$j.5 { sqlite3_finalize $S } {SQLITE_OK} incr j } db close incr i } file delete -force test.db sqlite3 db test.db do_test enc4-4.1 { db eval "select 1+1." } {2.0} do_test enc4-4.2.1 { set S [sqlite3_prepare_v2 db "SELECT 1+1." -1 dummy] sqlite3_step $S sqlite3_column_text $S 0 } {2.0} do_test enc4-4.2.2 { sqlite3_finalize $S } {SQLITE_OK} do_test enc4-4.3.1 { set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy] sqlite3_bind_text $S 1 "1." 2 sqlite3_step $S sqlite3_column_text $S 0 } {2.0} do_test enc4-4.3.2 { sqlite3_finalize $S } {SQLITE_OK} do_test enc4-4.4.1 { set S [sqlite3_prepare_v2 db "SELECT 1+?" -1 dummy] sqlite3_bind_text $S 1 "1.0" 2 sqlite3_step $S sqlite3_column_text $S 0 } {2.0} do_test enc4-4.4.2 { sqlite3_finalize $S } {SQLITE_OK} db close finish_test |
Added test/eqp.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 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 | # 2010 November 6 # # 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 eqp #------------------------------------------------------------------------- # # eqp-1.*: Assorted tests. # eqp-2.*: Tests for single select statements. # eqp-3.*: Select statements that execute sub-selects. # eqp-4.*: Compound select statements. # proc det {args} { uplevel do_eqp_test $args } do_execsql_test 1.1 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a); CREATE INDEX i2 ON t1(b); CREATE TABLE t2(a, b); CREATE TABLE t3(a, b); } do_eqp_test 1.2 { SELECT * FROM t2, t1 WHERE t1.a=1 OR t1.b=2; } { 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)} 0 1 0 {SCAN TABLE t2 (~1000000 rows)} } do_eqp_test 1.3 { SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=1 OR t1.b=2; } { 0 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)} } do_eqp_test 1.3 { SELECT a FROM t1 ORDER BY a } { 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} } do_eqp_test 1.4 { SELECT a FROM t1 ORDER BY +a } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 1.5 { SELECT a FROM t1 WHERE a=4 } { 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)} } do_eqp_test 1.6 { SELECT DISTINCT count(*) FROM t3 GROUP BY a; } { 0 0 0 {SCAN TABLE t3 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } do_eqp_test 1.7 { SELECT * FROM t3 JOIN (SELECT 1) } { 0 0 1 {SCAN SUBQUERY 1 (~1 rows)} 0 1 0 {SCAN TABLE t3 (~1000000 rows)} } do_eqp_test 1.8 { SELECT * FROM t3 JOIN (SELECT 1 UNION SELECT 2) } { 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} 0 0 1 {SCAN SUBQUERY 1 (~2 rows)} 0 1 0 {SCAN TABLE t3 (~1000000 rows)} } do_eqp_test 1.9 { SELECT * FROM t3 JOIN (SELECT 1 EXCEPT SELECT a FROM t3 LIMIT 17) } { 3 0 0 {SCAN TABLE t3 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)} 0 0 1 {SCAN SUBQUERY 1 (~17 rows)} 0 1 0 {SCAN TABLE t3 (~1000000 rows)} } do_eqp_test 1.10 { SELECT * FROM t3 JOIN (SELECT 1 INTERSECT SELECT a FROM t3 LIMIT 17) } { 3 0 0 {SCAN TABLE t3 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)} 0 0 1 {SCAN SUBQUERY 1 (~1 rows)} 0 1 0 {SCAN TABLE t3 (~1000000 rows)} } do_eqp_test 1.11 { SELECT * FROM t3 JOIN (SELECT 1 UNION ALL SELECT a FROM t3 LIMIT 17) } { 3 0 0 {SCAN TABLE t3 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)} 0 0 1 {SCAN SUBQUERY 1 (~17 rows)} 0 1 0 {SCAN TABLE t3 (~1000000 rows)} } #------------------------------------------------------------------------- # Test cases eqp-2.* - tests for single select statements. # drop_all_tables do_execsql_test 2.1 { CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); CREATE INDEX t2i1 ON t2(x); } det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.2 "SELECT DISTINCT min(x), max(x) FROM t2 GROUP BY x ORDER BY 1" { 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.3 "SELECT DISTINCT * FROM t1" { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } det 2.2.4 "SELECT DISTINCT * FROM t1, t2" { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 1 1 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} } det 2.2.5 "SELECT DISTINCT * FROM t1, t2 ORDER BY t1.x" { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 1 1 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR DISTINCT} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 2.2.6 "SELECT DISTINCT t2.x FROM t1, t2 ORDER BY t2.x" { 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 0 1 0 {SCAN TABLE t1 (~1000000 rows)} } det 2.3.1 "SELECT max(x) FROM t2" { 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} } det 2.3.2 "SELECT min(x) FROM t2" { 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)} } det 2.3.3 "SELECT min(x), max(x) FROM t2" { 0 0 0 {SCAN TABLE t2 (~1000000 rows)} } det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } #------------------------------------------------------------------------- # Test cases eqp-3.* - tests for select statements that use sub-selects. # do_eqp_test 3.1.1 { SELECT (SELECT x FROM t1 AS sub) FROM t1; } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} } do_eqp_test 3.1.2 { SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub); } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} } do_eqp_test 3.1.3 { SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub ORDER BY y); } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_eqp_test 3.1.4 { SELECT * FROM t1 WHERE (SELECT x FROM t2 ORDER BY x); } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} } det 3.2.1 { SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) ORDER BY y LIMIT 5 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {SCAN SUBQUERY 1 (~10 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 3.2.2 { SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) AS x1, (SELECT * FROM t2 ORDER BY x LIMIT 10) AS x2 ORDER BY x2.y LIMIT 5 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)} 0 0 0 {SCAN SUBQUERY 1 AS x1 (~10 rows)} 0 1 1 {SCAN SUBQUERY 2 AS x2 (~10 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } det 3.3.1 { SELECT * FROM t1 WHERE y IN (SELECT y FROM t2) } { 0 0 0 {SCAN TABLE t1 (~100000 rows)} 0 0 0 {EXECUTE LIST SUBQUERY 1} 1 0 0 {SCAN TABLE t2 (~1000000 rows)} } det 3.3.2 { SELECT * FROM t1 WHERE y IN (SELECT y FROM t2 WHERE t1.x!=t2.x) } { 0 0 0 {SCAN TABLE t1 (~500000 rows)} 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1} 1 0 0 {SCAN TABLE t2 (~500000 rows)} } det 3.3.3 { SELECT * FROM t1 WHERE EXISTS (SELECT y FROM t2 WHERE t1.x!=t2.x) } { 0 0 0 {SCAN TABLE t1 (~500000 rows)} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1} 1 0 0 {SCAN TABLE t2 (~500000 rows)} } #------------------------------------------------------------------------- # Test cases eqp-4.* - tests for composite select statements. # do_eqp_test 4.1.1 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.1.2 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.1.3 { SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)} } do_eqp_test 4.1.4 { SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)} } do_eqp_test 4.1.5 { SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } do_eqp_test 4.2.2 { SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 1 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)} } do_eqp_test 4.2.3 { SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)} } do_eqp_test 4.2.4 { SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)} } do_eqp_test 4.2.5 { SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 1 0 0 {USE TEMP B-TREE FOR ORDER BY} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } do_eqp_test 4.3.1 { SELECT x FROM t1 UNION SELECT x FROM t2 } { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.2 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 } { 2 0 0 {SCAN TABLE t1 (~1000000 rows)} 3 0 0 {SCAN TABLE t2 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)} 4 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)} } do_eqp_test 4.3.3 { SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1 } { 2 0 0 {SCAN TABLE t1 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)} 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION)} 4 0 0 {SCAN TABLE t1 (~1000000 rows)} 4 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 4 (UNION)} } #------------------------------------------------------------------------- # This next block of tests verifies that the examples on the # lang_explain.html page are correct. # drop_all_tables # EVIDENCE-OF: R-64208-08323 sqlite> EXPLAIN QUERY PLAN SELECT a, b # FROM t1 WHERE a=1; 0|0|0|SCAN TABLE t1 (~100000 rows) do_execsql_test 5.1.0 { CREATE TABLE t1(a, b) } det 5.1.1 "SELECT a, b FROM t1 WHERE a=1" { 0 0 0 {SCAN TABLE t1 (~100000 rows)} } # EVIDENCE-OF: R-09022-44606 sqlite> CREATE INDEX i1 ON t1(a); # sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1; # 0|0|0|SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows) do_execsql_test 5.2.0 { CREATE INDEX i1 ON t1(a) } det 5.2.1 "SELECT a, b FROM t1 WHERE a=1" { 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} } # EVIDENCE-OF: R-62228-34103 sqlite> CREATE INDEX i2 ON t1(a, b); # sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1; # 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) do_execsql_test 5.3.0 { CREATE INDEX i2 ON t1(a, b) } det 5.3.1 "SELECT a, b FROM t1 WHERE a=1" { 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} } # EVIDENCE-OF: R-22253-05302 sqlite> EXPLAIN QUERY PLAN SELECT t1.*, # t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2; 0|0|0|SEARCH TABLE t1 # USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|1|SCAN TABLE t2 # (~1000000 rows) do_execsql_test 5.4.0 {CREATE TABLE t2(c, d)} det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" { 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)} 0 1 1 {SCAN TABLE t2 (~1000000 rows)} } # EVIDENCE-OF: R-21040-07025 sqlite> EXPLAIN QUERY PLAN SELECT t1.*, # t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2; 0|0|1|SEARCH TABLE t1 # USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|0|SCAN TABLE t2 # (~1000000 rows) det 5.5 "SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2" { 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~3 rows)} 0 1 0 {SCAN TABLE t2 (~1000000 rows)} } # EVIDENCE-OF: R-39007-61103 sqlite> CREATE INDEX i3 ON t1(b); # sqlite> EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=1 OR b=2; # 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) # 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows) do_execsql_test 5.5.0 {CREATE INDEX i3 ON t1(b)} det 5.6.1 "SELECT * FROM t1 WHERE a=1 OR b=2" { 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} 0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)} } # EVIDENCE-OF: R-33025-54904 sqlite> EXPLAIN QUERY PLAN SELECT c, d # FROM t2 ORDER BY c; 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|USE TEMP # B-TREE FOR ORDER BY det 5.7 "SELECT c, d FROM t2 ORDER BY c" { 0 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } # EVIDENCE-OF: R-38854-22809 sqlite> CREATE INDEX i4 ON t2(c); # sqlite> EXPLAIN QUERY PLAN SELECT c, d FROM t2 ORDER BY c; # 0|0|0|SCAN TABLE t2 USING INDEX i4 (~1000000 rows) do_execsql_test 5.8.0 {CREATE INDEX i4 ON t2(c)} det 5.8.1 "SELECT c, d FROM t2 ORDER BY c" { 0 0 0 {SCAN TABLE t2 USING INDEX i4 (~1000000 rows)} } # EVIDENCE-OF: R-29884-43993 sqlite> EXPLAIN QUERY PLAN SELECT # (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2; # 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|EXECUTE SCALAR SUBQUERY 1 # 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows) # 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2 2|0|0|SEARCH TABLE t1 USING # INDEX i3 (b=?) (~10 rows) det 5.9 { SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2 } { 0 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {EXECUTE SCALAR SUBQUERY 1} 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)} 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2} 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)} } # EVIDENCE-OF: R-17911-16445 sqlite> EXPLAIN QUERY PLAN SELECT # count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x; # 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) 0|0|0|SCAN # SUBQUERY 1 (~1000000 rows) 0|0|0|USE TEMP B-TREE FOR GROUP BY det 5.10 { SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x } { 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} 0 0 0 {SCAN SUBQUERY 1 (~100 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} } # EVIDENCE-OF: R-18544-33103 sqlite> EXPLAIN QUERY PLAN SELECT * FROM # (SELECT * FROM t2 WHERE c=1), t1; 0|0|0|SEARCH TABLE t2 USING INDEX i4 # (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows) det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" { 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)} 0 1 1 {SCAN TABLE t1 (~1000000 rows)} } # EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM # t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 (~1000000 rows) # 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # USING TEMP B-TREE (UNION) det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" { 1 0 0 {SCAN TABLE t1 (~1000000 rows)} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)} } # EVIDENCE-OF: R-61538-24748 sqlite> EXPLAIN QUERY PLAN SELECT a FROM # t1 EXCEPT SELECT d FROM t2 ORDER BY 1; 1|0|0|SCAN TABLE t1 USING # COVERING INDEX i2 (~1000000 rows) 2|0|0|SCAN TABLE t2 (~1000000 rows) # 2|0|0|USE TEMP B-TREE FOR ORDER BY 0|0|0|COMPOUND SUBQUERIES 1 AND 2 # (EXCEPT) det 5.13 "SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1" { 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)} 2 0 0 {SCAN TABLE t2 (~1000000 rows)} 2 0 0 {USE TEMP B-TREE FOR ORDER BY} 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)} } #------------------------------------------------------------------------- # The following tests - eqp-6.* - test that the example C code on # documentation page eqp.html works. The C code is duplicated in test1.c # and wrapped in Tcl command [print_explain_query_plan] # set boilerplate { proc explain_query_plan {db sql} { set stmt [sqlite3_prepare_v2 db $sql -1 DUMMY] print_explain_query_plan $stmt sqlite3_finalize $stmt } sqlite3 db test.db explain_query_plan db {%SQL%} db close exit } proc do_peqp_test {tn sql res} { set fd [open script.tcl w] puts $fd [string map [list %SQL% $sql] $::boilerplate] close $fd uplevel do_test $tn [list { set fd [open "|[info nameofexec] script.tcl"] set data [read $fd] close $fd set data }] [list $res] } do_peqp_test 6.1 { SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1 } [string trimleft { 1 0 0 SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) 2 0 0 SCAN TABLE t2 (~1000000 rows) 2 0 0 USE TEMP B-TREE FOR ORDER BY 0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT) }] finish_test |
Changes to test/exclusive.test.
︙ | ︙ | |||
392 393 394 395 396 397 398 | # instead of deleted when in exclusive access mode. # # Close and reopen the database so that the temp database is no # longer active. # db close | | | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 | # instead of deleted when in exclusive access mode. # # Close and reopen the database so that the temp database is no # longer active. # db close sqlite3 db test.db # if we're using proxy locks, we use 3 filedescriptors for a db # that is open but NOT writing changes, normally # sqlite uses 1 (proxy locking adds the conch and the local lock) set extrafds 0 if {[forced_proxy_locking]} { set extrafds 2 |
︙ | ︙ | |||
461 462 463 464 465 466 467 468 | } {normal 1 2 3 2 3 4 5 6 7 11 12 13 12 13 14 15 16 17} do_test exclusive-5.7 { # Just the db open. set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {1} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } {normal 1 2 3 2 3 4 5 6 7 11 12 13 12 13 14 15 16 17} do_test exclusive-5.7 { # Just the db open. set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {1} #------------------------------------------------------------------------- do_execsql_test exclusive-6.1 { CREATE TABLE t4(a, b); INSERT INTO t4 VALUES('Eden', 1955); BEGIN; INSERT INTO t4 VALUES('Macmillan', 1957); INSERT INTO t4 VALUES('Douglas-Home', 1963); INSERT INTO t4 VALUES('Wilson', 1964); } do_test exclusive-6.2 { forcedelete test2.db test2.db-journal file copy test.db test2.db file copy test.db-journal test2.db-journal sqlite3 db test2.db } {} do_execsql_test exclusive-6.3 { PRAGMA locking_mode = EXCLUSIVE; SELECT * FROM t4; } {exclusive Eden 1955} do_test exclusive-6.4 { db close forcedelete test.db test.db-journal set fd [open test.db-journal w] puts $fd x close $fd sqlite3 db test.db } {} do_execsql_test exclusive-6.5 { PRAGMA locking_mode = EXCLUSIVE; SELECT * FROM sqlite_master; } {exclusive} finish_test |
Changes to test/exclusive2.test.
︙ | ︙ | |||
79 80 81 82 83 84 85 | readPagerChangeCounter test.db } {0} #----------------------------------------------------------------------- # The following tests - exclusive2-1.X - check that: # # 1-3: Build a database with connection 1, calculate a signature. | | > > > > > | 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 | readPagerChangeCounter test.db } {0} #----------------------------------------------------------------------- # The following tests - exclusive2-1.X - check that: # # 1-3: Build a database with connection 1, calculate a signature. # 4-7: Modify the database using a second connection in a way that # does not modify the freelist, then reset the pager change-counter # to the value it had before the modifications. # 8: Check that using the first connection, the database signature # is still the same. This is because it uses the in-memory cache. # It can't tell the db has changed because we reset the change-counter. # 9: Increment the change-counter. # 10: Ensure that the first connection now sees the updated database. It # sees the change-counter has been incremented and discards the # invalid in-memory cache. # # This will only work if the database cache is large enough to hold # the entire database. In the case of 1024 byte pages, this means # the cache size must be at least 17. Otherwise, some pages will be # loaded from the database file in step 8. # # For similar reasons, this test does not work with the memsubsys1 permutation. # Permutation memsubsys1 configures the pcache subsystem to use a static # allocation of 24 pages (shared between all pagers). This is not enough for # this test. # do_test exclusive2-1.1 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1(a) VALUES(randstr(10, 400)); INSERT INTO t1(a) VALUES(randstr(10, 400)); |
︙ | ︙ | |||
142 143 144 145 146 147 148 | } 0 do_test exclusive2-1.6 { readPagerChangeCounter test.db } {2} do_test exclusive2-1.7 { pagerChangeCounter test.db 1 } {1} | > | | | | > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | } 0 do_test exclusive2-1.6 { readPagerChangeCounter test.db } {2} do_test exclusive2-1.7 { pagerChangeCounter test.db 1 } {1} if {[permutation] != "memsubsys1"} { do_test exclusive2-1.9 { t1sig expr {[t1sig] eq $::sig} } {1} } do_test exclusive2-1.10 { pagerChangeCounter test.db 2 } {2} do_test exclusive2-1.11 { expr {[t1sig] eq $::sig} } {0} |
︙ | ︙ |
Changes to test/expr.test.
︙ | ︙ | |||
286 287 288 289 290 291 292 | test_expr expr-4.10 {r1='0.0', r2='abc'} {r1>r2} 0 test_expr expr-4.11 {r1='abc', r2='Abc'} {r1<r2} 0 test_expr expr-4.12 {r1='abc', r2='Abc'} {r1>r2} 1 test_expr expr-4.13 {r1='abc', r2='Bbc'} {r1<r2} 0 test_expr expr-4.14 {r1='abc', r2='Bbc'} {r1>r2} 1 test_expr expr-4.15 {r1='0', r2='0.0'} {r1==r2} 1 test_expr expr-4.16 {r1='0.000', r2='0.0'} {r1==r2} 1 | | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | test_expr expr-4.10 {r1='0.0', r2='abc'} {r1>r2} 0 test_expr expr-4.11 {r1='abc', r2='Abc'} {r1<r2} 0 test_expr expr-4.12 {r1='abc', r2='Abc'} {r1>r2} 1 test_expr expr-4.13 {r1='abc', r2='Bbc'} {r1<r2} 0 test_expr expr-4.14 {r1='abc', r2='Bbc'} {r1>r2} 1 test_expr expr-4.15 {r1='0', r2='0.0'} {r1==r2} 1 test_expr expr-4.16 {r1='0.000', r2='0.0'} {r1==r2} 1 test_expr expr-4.17 {r1=' 0.000', r2=' 0.0'} {r1==r2} 1 test_expr expr-4.18 {r1='0.0', r2='abc'} {r1<r2} 1 test_expr expr-4.19 {r1='0.0', r2='abc'} {r1==r2} 0 test_expr expr-4.20 {r1='0.0', r2='abc'} {r1>r2} 0 } # CSL is true if LIKE is case sensitive and false if not. # NCSL is the opposite. Use these variables as the result |
︙ | ︙ |
Changes to test/fts3ah.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 | # 2006 October 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. # #************************************************************************* # This file implements regression tests for SQLite library. The focus # here is testing correct handling of very long terms. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test return } # Generate a document of bigterms based on characters from the list # chars. proc bigtermdoc {chars len} { set doc "" foreach char $chars { append doc " " [string repeat $char $len] } return $doc } set len 5000 set doc1 [bigtermdoc {a b c d} $len] set doc2 [bigtermdoc {b d e f} $len] set doc3 [bigtermdoc {a c e} $len] set aterm [string repeat a $len] set bterm [string repeat b $len] set xterm [string repeat x $len] db eval { CREATE VIRTUAL TABLE t1 USING fts3(content); INSERT INTO t1 (rowid, content) VALUES(1, $doc1); INSERT INTO t1 (rowid, content) VALUES(2, $doc2); INSERT INTO t1 (rowid, content) VALUES(3, $doc3); } # No hits at all. Returns empty doclists from termSelect(). do_test fts3ah-1.1 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'something'} } {} do_test fts3ah-1.2 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH $aterm} } {1 3} do_test fts3ah-1.3 { execsql {SELECT rowid FROM t1 WHERE t1 MATCH $xterm} } {} do_test fts3ah-1.4 { execsql "SELECT rowid FROM t1 WHERE t1 MATCH '$aterm -$xterm'" } {1 3} do_test fts3ah-1.5 { execsql "SELECT rowid FROM t1 WHERE t1 MATCH '\"$aterm $bterm\"'" } {1} finish_test |
Changes to test/fts3ao.test.
︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 | source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test return } #--------------------------------------------------------------------- # These tests, fts3ao-1.*, test that ticket #2429 is fixed. # db eval { CREATE VIRTUAL TABLE t1 USING fts3(a, b, c); INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two'); | > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test return } set ::testprefix fts3ao #--------------------------------------------------------------------- # These tests, fts3ao-1.*, test that ticket #2429 is fixed. # db eval { CREATE VIRTUAL TABLE t1 USING fts3(a, b, c); INSERT INTO t1(a, b, c) VALUES('one three four', 'one four', 'one four two'); |
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 | do_test fts3ao-3.2 { execsql { SELECT a, b, c FROM t2 WHERE a MATCH 'song'; } } {{neung song sahm} {neung see} {neung see song}} do_test fts3ao-3.3 { execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; } } {{one three four} {one four} {one two}} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test fts3ao-3.2 { execsql { SELECT a, b, c FROM t2 WHERE a MATCH 'song'; } } {{neung song sahm} {neung see} {neung see song}} do_test fts3ao-3.3 { execsql { SELECT a, b, c FROM t1 WHERE c MATCH 'two'; } } {{one three four} {one four} {one two}} #--------------------------------------------------------------------- # Test that it is possible to rename an fts3 table within a # transaction. # do_test fts3ao-4.1 { execsql { CREATE VIRTUAL TABLE t4 USING fts3; INSERT INTO t4 VALUES('the quick brown fox'); } } {} do_test fts3ao-4.2 { execsql { BEGIN; INSERT INTO t4 VALUES('jumped over the'); } } {} do_test fts3ao-4.3 { execsql { ALTER TABLE t4 RENAME TO t5; } } {} do_test fts3ao-4.4 { execsql { INSERT INTO t5 VALUES('lazy dog'); } } {} do_test fts3ao-4.5 { execsql COMMIT } {} do_test fts3ao-4.6 { execsql { SELECT * FROM t5 } } {{the quick brown fox} {jumped over the} {lazy dog}} do_test fts3ao-4.7 { execsql { BEGIN; INSERT INTO t5 VALUES('Down came a jumbuck to drink at that billabong'); ALTER TABLE t5 RENAME TO t6; INSERT INTO t6 VALUES('Down came the troopers, one, two, three'); ROLLBACK; SELECT * FROM t5; } } {{the quick brown fox} {jumped over the} {lazy dog}} # Test that it is possible to rename an FTS4 table. Renaming an FTS4 table # involves renaming the extra %_docsize and %_stat tables. # do_execsql_test 5.1 { CREATE VIRTUAL TABLE t7 USING FTS4; INSERT INTO t7 VALUES('coined by a German clinician'); SELECT count(*) FROM sqlite_master WHERE name LIKE 't7%'; SELECT count(*) FROM sqlite_master WHERE name LIKE 't8%'; } {6 0} do_execsql_test 5.2 { ALTER TABLE t7 RENAME TO t8; SELECT count(*) FROM sqlite_master WHERE name LIKE 't7%'; SELECT count(*) FROM sqlite_master WHERE name LIKE 't8%'; } {0 6} finish_test |
Added test/fts3corrupt.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 | # 2010 October 27 # # 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. # #*********************************************************************** # Test that the FTS3 extension does not crash when it encounters a # corrupt data structure on disk. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } set ::testprefix fts3corrupt # Test that a doclist with a length field that indicates that the doclist # extends past the end of the node on which it resides is correctly identified # as database corruption. # do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts3; INSERT INTO t1 VALUES('hello'); } {} do_test fts3corrupt-1.1 { set blob [db one {SELECT root from t1_segdir}] set blob [binary format a7ca* $blob 24 [string range $blob 8 end]] execsql { UPDATE t1_segdir SET root = $blob } } {} do_test fts3corrupt-1.2 { foreach w {a b c d e f g h i j k l m n o} { execsql { INSERT INTO t1 VALUES($w) } } } {} do_catchsql_test 1.3 { INSERT INTO t1 VALUES('world'); } {1 {database disk image is malformed}} do_execsql_test 1.4 { DROP TABLE t1; } # This block of tests checks that corruption is correctly detected if the # length field of a term on a leaf node indicates that the term extends past # the end of the node on which it resides. There are two cases: # # 1. The first term on the node. # 2. The second or subsequent term on the node (prefix compressed term). # do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts3; BEGIN; INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES('hello'); COMMIT; } {} do_test fts3corrupt-2.1 { set blob [db one {SELECT root from t1_segdir}] set blob [binary format a*a* "\x00\x7F" [string range $blob 2 end]] execsql { UPDATE t1_segdir SET root = $blob } } {} do_catchsql_test 2.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'hello' } {1 {database disk image is malformed}} do_execsql_test 3.0 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING fts3; BEGIN; INSERT INTO t1 VALUES('hello'); INSERT INTO t1 VALUES('world'); COMMIT; } {} do_test fts3corrupt-3.1 { set blob [db one {SELECT quote(root) from t1_segdir}] set blob [binary format a11a*a* $blob "\x7F" [string range $blob 12 end]] execsql { UPDATE t1_segdir SET root = $blob } } {} do_catchsql_test 3.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'world' } {1 {database disk image is malformed}} do_execsql_test 4.0 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING fts3; INSERT INTO t1(t1) VALUES('nodesize=24'); } do_test fts3corrupt-4.1 { execsql BEGIN foreach s { "amxtvoo adqwroyhz auq aithtir avniqnuynvf axp ahibayfynig agbicpm" "ajdtebs anteaxr aieynenwmd awpl alo akxcrwow aoxftge aoqvgul" "amcfvdr auz apu aebelm ahuxyz aqc asyafdb agulvhvqu" "apepwfyz azkhdvkw aenyelxzbk aslnitbyet aycdsdcpgr aqzzdbc agfi axnypydou" "aaqrzzcm apcxdxo atumltzj aevvivo aodknoft aqoyytoz alobx apldt" } { execsql { INSERT INTO t1 VALUES($s) } } execsql COMMIT } {} do_catchsql_test 4.2 { UPDATE t1_segdir SET root = X'FFFFFFFFFFFFFFFF'; SELECT rowid FROM t1 WHERE t1 MATCH 'world'; } {1 {database disk image is malformed}} set blob [binary format cca*cca*cca*cca*cca*cca*cca*cca*cca*cca*a* \ 22 120 [string repeat a 120] \ 22 120 [string repeat b 120] \ 22 120 [string repeat c 120] \ 22 120 [string repeat d 120] \ 22 120 [string repeat e 120] \ 22 120 [string repeat f 120] \ 22 120 [string repeat g 120] \ 22 120 [string repeat h 120] \ 22 120 [string repeat i 120] \ 22 120 [string repeat j 120] \ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ] do_catchsql_test 4.3 { UPDATE t1_segdir SET root = $blob; SELECT rowid FROM t1 WHERE t1 MATCH 'world'; } {1 {database disk image is malformed}} finish_test |
Added test/fts3corrupt2.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 | # 2010 October 30 # # 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. # #*********************************************************************** # Test that the FTS3 extension does not crash when it encounters a # corrupt data structure on disk. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } set ::testprefix fts3corrupt2 set data [list] lappend data {*}{ "amxtvoo adqwroyhz auq aithtir avniqnuynvf axp ahibayfynig agbicpm" "ajdtebs anteaxr aieynenwmd awpl alo akxcrwow aoxftge aoqvgul" "amcfvdr auz apu aebelm ahuxyz aqc asyafdb agulvhvqu" "apepwfyz azkhdvkw aenyelxzbk aslnitbyet aycdsdcpgr aqzzdbc agfi axnypydou" "aaqrzzcm apcxdxo atumltzj aevvivo aodknoft aqoyytoz alobx apldt" "adjllxlhnmj aiuhvuj adwppceuht atvj azrsam ahkjqdhny audlqxr aotgcd" "aira azflsceos awj auzbobfkc awmezplr aeh awec ahndxlmv" "aydwnied alk auoap agihyqeix aymqxzajnl aydwnied aojkarx agbo" "ahajsmcl anvx amdhjm aoptsj agugzjjm apkevm acnj acjg" "amwtkw aogttbykvt aubwrfqnbjf ajow agsj aerkqzjdqst anenlvbalkn arfajzzgckx" "adqqqofkmz amjpavjuhw aqgehgnb awvvxlbtqzn agstqko akmkzehyh atagzey agwja" "amag ahe autkllywhr avnk atmt akn anvdh aixfrv" "aqdyerbws avefykly awl azaduojgzo anxfsmw axpt abgbvk ati" "attyqkwz aiweypiczul afy asitaqbczhh aitxisizpv auhviq aibql ajfqc" "aylzprtmta aiuemihqrpi awluvgsw ampbuy axlifpzfqr aems aoaxwads apianfn" "aodrkijelq acdb aaserrdxm aqyasgofqu aevvivo afi apmwu aeoqysl" "amqnk ankaotm ayfy ajcupeeoc advcbukan aucahlwnyk adbfyo azqjpeant" "afczpp asqrs ahslvda akhlf aiqgdp atyd aznuglxqbrg awirndrh" "aqhiajp amxeazb asxuehg akod axvolvsp agcz asmovmohy acmqa" "avvomv aafms ashuaec arevx audtq alrwqhjvao avgsgpg ajbrctpsel" "atxoirr ayopboobqdu ajunntua arh aernimxid aipljda aglo aefk" "aonxf acmnnkna abgviaswe aulvcbv axp apemgakpzo aibql acioaid" "axo alrwqhjvao ayqounftdzl azmoakdyh apajze ajk artvy apxiamy" "ayjafsraz addjj agsj asejtziqws acatvhegu aoxdjqblsvv aekdmmbs aaobe" "abjjvzubkwt alczv ati awz auyxgcxeb aymjoym anqoukprtyt atwfhpmbooh" "ajfqz aethlgir aclcx aowlyvetby aproqm afjlqtkv anebfy akzrcpfrrvw" "aoledfotm aiwlfm aeejlaej anz abgbvk aktfn aayoh anpywgdvgz" "acvmldguld asdvz aqb aeomsyzyu aggylhprbdz asrfkwz auipybpsn agsnszzfb" } do_test fts3corrupt2-1.0 { execsql BEGIN execsql { CREATE VIRTUAL TABLE t2 USING FTS3(a, b); } execsql { INSERT INTO t2(t2) VALUES('nodesize=32') } foreach d $data { execsql { INSERT INTO t2 VALUES($d, $d) } } execsql COMMIT execsql { SELECT count(*) FROM t2_segments } } {163} proc set_byte {blob byte val} { binary format a*ca* \ [string range $blob 0 [expr $byte-1]] \ $val \ [string range $blob [expr $byte+1] end] \ } set tn 0 set c 256 foreach {rowid sz blob} [ db eval {SELECT rowid, length(block), block FROM t2_segments} ] { incr tn set c [expr (($c+255)%256)] for {set i 0} {$i < $sz} {incr i} { set b2 [set_byte $blob $i $c] execsql { UPDATE t2_segments SET block = $b2 WHERE rowid = $rowid } do_test fts3corrupt2-1.$tn.$i { catchsql { SELECT * FROM t2 WHERE t2 MATCH 'a*' } set {} {} } {} } execsql { UPDATE t2_segments SET block = $blob WHERE rowid = $rowid } } foreach c {50 100 150 200 250} { foreach {rowid sz blob} [ db eval {SELECT rowid, length(root), root FROM t2_segdir} ] { incr tn for {set i 0} {$i < $sz} {incr i} { set b2 [set_byte $blob $i $c] execsql { UPDATE t2_segdir SET root = $b2 WHERE rowid = $rowid } do_test fts3corrupt2-2.$c.$tn.$i { catchsql { SELECT * FROM t2 WHERE t2 MATCH 'a*' } set {} {} } {} } execsql { UPDATE t2_segdir SET root = $blob WHERE rowid = $rowid } } } finish_test |
Changes to test/fts3cov.test.
1 2 3 4 5 6 7 8 | # 2009 December 03 # # 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. # #*********************************************************************** # | | < < > | 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 | # 2009 December 03 # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # The tests in this file are structural coverage tests for FTS3. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl source $testdir/malloc_common.tcl set DO_MALLOC_TEST 0 set testprefix fts3cov #-------------------------------------------------------------------------- # When it first needs to read a block from the %_segments table, the FTS3 # module compiles an SQL statement for that purpose. The statement is # stored and reused each subsequent time a block is read. This test case # tests the effects of an OOM error occuring while compiling the statement. # |
︙ | ︙ | |||
78 79 80 81 82 83 84 | INSERT INTO t1 VALUES('What makes her in the wood so late,'); INSERT INTO t1 VALUES('A furlong from the castle gate?'); INSERT INTO t1 VALUES('She had dreams all yesternight'); INSERT INTO t1 VALUES('Of her own betrothed knight;'); INSERT INTO t1 VALUES('And she in the midnight wood will pray'); INSERT INTO t1 VALUES('For the weal of her lover that''s far away.'); COMMIT; | | > | | | | | | | 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 | INSERT INTO t1 VALUES('What makes her in the wood so late,'); INSERT INTO t1 VALUES('A furlong from the castle gate?'); INSERT INTO t1 VALUES('She had dreams all yesternight'); INSERT INTO t1 VALUES('Of her own betrothed knight;'); INSERT INTO t1 VALUES('And she in the midnight wood will pray'); INSERT INTO t1 VALUES('For the weal of her lover that''s far away.'); COMMIT; } execsql { INSERT INTO t1(t1) VALUES('optimize'); SELECT substr(hex(root), 1, 2) FROM t1_segdir; } } {03} # Test the "missing entry" case: do_test fts3cov-2.2 { set root [db one {SELECT root FROM t1_segdir}] read_fts3varint [string range $root 1 end] left_child execsql { DELETE FROM t1_segments WHERE blockid = $left_child } } {} do_error_test fts3cov-2.3 { SELECT * FROM t1 WHERE t1 MATCH 'c*' } {SQL logic error or missing database} # Test the "replaced with NULL" case: do_test fts3cov-2.4 { execsql { INSERT INTO t1_segments VALUES($left_child, NULL) } } {} do_error_test fts3cov-2.5 { SELECT * FROM t1 WHERE t1 MATCH 'cloud' } {SQL logic error or missing database} #-------------------------------------------------------------------------- # The following tests are to test the effects of OOM errors while storing # terms in the pending-hash table. Specifically, while creating doclist # blobs to store in the table. More specifically, to test OOM errors while # appending column numbers to doclists. For example, if a doclist consists # of: |
︙ | ︙ | |||
365 366 367 368 369 370 371 372 373 | INSERT INTO t13 VALUES('scalar two functions'); INSERT INTO t13 VALUES('functions scalar two'); } -sqlbody { SELECT snippet(t13, '%%', '%%', '#') FROM t13 WHERE t13 MATCH 'two'; SELECT snippet(t13, '%%', '%%') FROM t13 WHERE t13 MATCH 'two'; SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two'; } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | INSERT INTO t13 VALUES('scalar two functions'); INSERT INTO t13 VALUES('functions scalar two'); } -sqlbody { SELECT snippet(t13, '%%', '%%', '#') FROM t13 WHERE t13 MATCH 'two'; SELECT snippet(t13, '%%', '%%') FROM t13 WHERE t13 MATCH 'two'; SELECT snippet(t13, '%%') FROM t13 WHERE t13 MATCH 'two'; } do_execsql_test 14.0 { CREATE VIRTUAL TABLE t14 USING fts4(a, b); INSERT INTO t14 VALUES('one two three', 'one three four'); INSERT INTO t14 VALUES('a b c', 'd e a'); } do_execsql_test 14.1 { SELECT rowid FROM t14 WHERE t14 MATCH '"one two three"' } {1} do_execsql_test 14.2 { SELECT rowid FROM t14 WHERE t14 MATCH '"one four"' } {} do_execsql_test 14.3 { SELECT rowid FROM t14 WHERE t14 MATCH '"e a"' } {2} do_execsql_test 14.5 { SELECT rowid FROM t14 WHERE t14 MATCH '"e b"' } {} do_catchsql_test 14.6 { SELECT rowid FROM t14 WHERE rowid MATCH 'one' } {1 {unable to use function MATCH in the requested context}} do_catchsql_test 14.7 { SELECT rowid FROM t14 WHERE docid MATCH 'one' } {1 {unable to use function MATCH in the requested context}} do_execsql_test 15.0 { CREATE VIRTUAL TABLE t15 USING fts4(a, b, c); INSERT INTO t15 VALUES('abc def ghi', 'abc2 def2 ghi2', 'abc3 def3 ghi3'); INSERT INTO t15 VALUES('abc2 def2 ghi2', 'abc2 def2 ghi2', 'abc def3 ghi3'); } do_execsql_test 15.1 { SELECT rowid FROM t15 WHERE t15 MATCH '"abc* def2"' } {1 2} # Test a corruption case. # do_execsql_test 16.1 { CREATE VIRTUAL TABLE t16 USING fts4; INSERT INTO t16 VALUES('theoretical work to examine the relationship'); INSERT INTO t16 VALUES('solution of our problems on the invisible'); DELETE FROM t16_content WHERE rowid = 2; } do_catchsql_test 16.2 { SELECT * FROM t16 WHERE t16 MATCH 'invisible' } {1 {database disk image is malformed}} # And another corruption test case. # do_execsql_test 17.1 { CREATE VIRTUAL TABLE t17 USING fts4; INSERT INTO t17(content) VALUES('one one one'); UPDATE t17_segdir SET root = X'00036F6E65FFFFFFFFFFFFFFFFFFFFFF02030300' } {} do_catchsql_test 17.2 { SELECT * FROM t17 WHERE t17 MATCH 'one' } {1 {database disk image is malformed}} finish_test |
Added test/fts3defer.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 | # 2010 October 15 # # 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 source $testdir/malloc_common.tcl ifcapable !fts3 { finish_test return } set sqlite_fts3_enable_parentheses 1 set ::testprefix fts3defer #-------------------------------------------------------------------------- # Test cases fts3defer-1.* are the "warm body" cases. The database contains # one row with 15000 instances of the token "a". This makes the doclist for # "a" so large that FTS3 will avoid loading it in most cases. # # To show this, test cases fts3defer-1.2.* execute a bunch of FTS3 queries # involving token "a". Then, fts3defer-1.3.* replaces the doclist for token # "a" with all zeroes and fts3defer-1.4.* repeats the tests from 1.2. If # the tests still work, we can conclude that the doclist for "a" was not # used. # set aaa [string repeat "a " 15000] do_execsql_test 1.1 { CREATE VIRTUAL TABLE t1 USING fts4; BEGIN; INSERT INTO t1 VALUES('this is a dog'); INSERT INTO t1 VALUES('an instance of a phrase'); INSERT INTO t1 VALUES('an instance of a longer phrase'); INSERT INTO t1 VALUES($aaa); COMMIT; } {} set tests { 1 {SELECT rowid FROM t1 WHERE t1 MATCH '"a dog"'} {1} 2 {SELECT rowid FROM t1 WHERE t1 MATCH '"is a dog"'} {1} 3 {SELECT rowid FROM t1 WHERE t1 MATCH '"a longer phrase"'} {3} 4 {SELECT snippet(t1) FROM t1 WHERE t1 MATCH '"a longer phrase"'} {"an instance of <b>a</b> <b>longer</b> <b>phrase</b>"} 5 {SELECT rowid FROM t1 WHERE t1 MATCH 'a dog'} {1} } do_select_tests 1.2 $tests do_execsql_test 1.3 { SELECT count(*) FROM t1_segments WHERE length(block)>10000; UPDATE t1_segments SET block = zeroblob(length(block)) WHERE length(block)>10000; } {1} do_select_tests 1.4 $tests # Drop the table. It is corrupt now anyhow, so not useful for subsequent tests. # do_execsql_test 1.5 { DROP TABLE t1 } #-------------------------------------------------------------------------- # These tests - fts3defer-2.* - are more rigorous. They test that for a # variety of queries, FTS3 and FTS4 return the same results. And that # zeroing the very large doclists that FTS4 does not load does not change # the results. # # They use the following pseudo-randomly generated document data. The # tokens "zm" and "jk" are especially common in this dataset. Additionally, # two documents are added to the pseudo-random data before it is loaded # into FTS4 containing 100,000 instances of the "zm" and "jk" tokens. This # makes the doclists for those tokens so large that FTS4 avoids loading them # into memory if possible. # set data [list] lappend data [string repeat "zm " 100000] lappend data [string repeat "jk " 100000] lappend data {*}{ "zm zm agmckuiu uhzq nsab jk rrkx duszemmzl hyq jk" "jk uhzq zm zm rgpzmlnmd zm zk jk jk zm" "duszemmzl zm jk xldlpy zm jk sbptoa xh jk xldlpy" "zm xh zm xqf azavwm jk jk trqd rgpzmlnmd jk" "zm vwq urvysbnykk ubwrfqnbjf zk lsz jk doiwavhwwo jk jk" "jk xduvfhk orpfawpx zkhdvkw jk mjpavjuhw zm jk duszemmzl zm" "jk igju jk jk zm hmjf xh zm gwdfhwurx zk" "vgsld jk jk zm hrlipdm jn zm zsmhnf vgsld duszemmzl" "gtuiexzsu aayxpmve zm zm zm drir scpgna xh azavwm uhzq" "farlehdhq hkfoudzftq igju duszemmzl xnxhf ewle zm hrlipdm urvysbnykk kn" "xnxhf jk jk agmckuiu duszemmzl jk zm zm jk vgsld" "zm zm zm jk jk urvysbnykk ogttbykvt zm zm jk" "iasrqgqv zm azavwm zidhxhbtv jk jk mjpavjuhw zm zm ajmvcydy" "rgpzmlnmd tmt mjpavjuhw xh igju jk azavwm fibokdry vgsld ofm" "zm jk vgsld jk xh jk csjqxhgj drir jk pmrb" "xh jk jk zm rrkx duszemmzl mjpavjuhw xldlpy igju zm" "jk hkfoudzftq zf rrkx wdmy jupk jk zm urvysbnykk npywgdvgz" "zm jk zm zm zhbrzadb uenvbm aayxpmve urvysbnykk duszemmzl jk" "uenvbm jk zm fxw xh bdilwmjw mjpavjuhw uv jk zm" "nk jk bnhc pahlds jk igju dzadnqzprr jk jk jk" "uhzq uv zm duszemmzl tlqix jk jk xh jk zm" "jk zm agmckuiu urvysbnykk jk jk zm zm jk jk" "azavwm mjpavjuhw lsgshn trqd xldlpy ogyavjvv agmckuiu ryvwwhlbc jk jk" "tmt jk zk zm azavwm ofm acpgim bvgimjik iasrqgqv wuvajhwqz" "igju ogyavjvv xrbdak rrkx fibokdry zf ujfhmrllq jk zm hxgwvib" "zm pahlds jk uenvbm aayxpmve iaf hmjf xph vnlyvtkgx zm" "jk xnxhf igju jk xh jk nvfasfh zm js jk" "zm zm rwaj igju xr rrkx xnxhf nvfasfh skxbsqzvmt xatbxeqq" "vgsld zm ujfhmrllq uhzq ogyavjvv nsab azavwm zm vgsld jmfiqhwnjg" "ymjoym duszemmzl urvysbnykk azavwm jk jmfiqhwnjg bu qcdziqomqk vnlyvtkgx" "zm nbilqcnz dzadnqzprr xh bkfgzsxn urvysbnykk xrujfzxqf zm zf agmckuiu" "jk urvysbnykk nvfasfh zf xh zm zm qcdziqomqk qvxtclg wdmy" "fibokdry jk urvysbnykk jk xr osff zm cvnnsl zm vgsld" "jk mjpavjuhw hkfoudzftq jk zm xh xqf urvysbnykk jk iasrqgqv" "jk csjqxhgj duszemmzl iasrqgqv aayxpmve zm brsuoqww jk qpmhtvl wluvgsw" "jk mj azavwm jk zm jn dzadnqzprr zm jk uhzq" "zk xqf jupk fxw nbilqcnz zm jk jcpiwj tznlvbfcv nvfasfh" "jk jcpiwj zm xnxhf zm mjpavjuhw mj drir pa pvjrjlas" "duszemmzl dzadnqzprr jk swc duszemmzl tmt jk jk pahlds jk" "zk zm jk zm zm eczkjblu zm hi pmrb jk" "azavwm zm iz agmckuiu jk sntk jk duszemmzl duszemmzl zm" "jk zm jk eczkjblu urvysbnykk sk gnl jk ttvgf hmjf" "jk bnhc jjrxpjkb mjpavjuhw fibokdry igju jk zm zm xh" "wxe ogttbykvt uhzq xr iaf zf urvysbnykk aayxpmve oacaxgjoo mjpavjuhw" "gazrt jk ephknonq myjp uenvbm wuvajhwqz jk zm xnxhf nvfasfh" "zm aayxpmve csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm" "sokcyf zm ogyavjvv jk zm fibokdry zm jk igju igju" "vgsld bvgimjik xuprtlyle jk akmikrqyt jk aayxpmve hkfoudzftq ddjj ithtir" "zm uhzq ovkyevlgv zk uenvbm csjqxhgj jk vgsld pgybs jk" "zm agmckuiu zexh fibokdry jk uhzq bu tugflixoex xnxhf sk" "zm zf uenvbm jk azavwm zm zm agmckuiu zm jk" "rrkx jk zf jt zm oacaxgjoo fibokdry wdmy igju csjqxhgj" "hi igju zm jk zidhxhbtv dzadnqzprr jk jk trqd duszemmzl" "zm zm mjpavjuhw xrbdak qrvbjruc jk qzzqdxq guwq cvnnsl zm" "ithtir jk jk qcdziqomqk zm farlehdhq zm zm xrbdak jk" "ixfipk csjqxhgj azavwm sokcyf ttvgf vgsld jk sk xh zk" "nvfasfh azavwm zm zm zm fxw nvfasfh zk gnl trqd" "zm fibokdry csjqxhgj ofm dzadnqzprr jk akmikrqyt orpfawpx duszemmzl vwq" "csjqxhgj jk jk vgsld urvysbnykk jk nxum jk jk nxum" "zm hkfoudzftq jk ryvwwhlbc mjpavjuhw ephknonq jk zm ogyavjvv zm" "lwa hi xnxhf qdyerbws zk njtc jk uhzq zm jk" "trqd zm dzadnqzprr zm urvysbnykk jk lsz jk mjpavjuhw cmnnkna" "duszemmzl zk jk jk fibokdry jseuhjnzo zm aayxpmve zk jk" "fibokdry jk sviq qvxtclg wdmy jk doiwavhwwo zexh jk zm" "jupk zm xh jk mjpavjuhw zm jk nsab npywgdvgz duszemmzl" "zm igju zm zm nvfasfh eh hkfoudzftq fibokdry fxw xkblf" "jk zm jk jk zm xh zk abthnzcv zf csjqxhgj" "zm zm jk nkaotm urvysbnykk sbptoa bq jk ktxdty ubwrfqnbjf" "nvfasfh aayxpmve xdcuz zm tugflixoex jcpiwj zm mjpavjuhw fibokdry doiwavhwwo" "iaf jk mjpavjuhw zm duszemmzl jk jk uhzq pahlds fibokdry" "ddjj zk azavwm jk swc zm gjtexkv jk xh jk" "igju jk csjqxhgj zm jk dzadnqzprr duszemmzl ulvcbv jk jk" "jk fibokdry zm csjqxhgj jn zm zm zm zf uhzq" "duszemmzl jk xkblf zk hrlipdm aayxpmve uenvbm uhzq jk zf" "dzadnqzprr jk zm zdu nvfasfh zm jk urvysbnykk hmjf jk" "jk aayxpmve aserrdxm acpgim fibokdry jk drir wxe brsuoqww rrkx" "uhzq csjqxhgj nvfasfh jk rrkx qbamok trqd uenvbm sntk zm" "ps azavwm zkhdvkw jk zm jk jk zm csjqxhgj xedlrcfo" "jk jk ogyavjvv jk zm farlehdhq duszemmzl jk agitgxamxe jk" "qzzqdxq rwaj jk jk zm xqf jk uenvbm jk zk" "zm hxgwvib akmikrqyt zf agmckuiu uenvbm bq npywgdvgz azavwm jk" "zf jmfiqhwnjg js igju zm aayxpmve zm mbxnljomiv csjqxhgj nvfasfh" "zm jk jk gazrt jk jk lkc jk nvfasfh jk" "xldlpy orpfawpx zkhdvkw jk zm igju zm urvysbnykk dzadnqzprr mbxnljomiv" "urvysbnykk jk zk igju zm uenvbm jk zm ithtir jk" "zm zk zm zf ofm zm xdcuz dzadnqzprr zm vgsld" "sbptoa jk tugflixoex jk zm zm vgsld zm xh zm" "uhzq jk zk evvivo vgsld vniqnuynvf agmckuiu jk zm zm" "zm nvfasfh zm zm zm abthnzcv uenvbm jk zk dzadnqzprr" "zm azavwm igju qzzqdxq jk xnxhf abthnzcv jk nvfasfh zm" "qbamok fxw vgsld igju cmnnkna xnxhf vniqnuynvf zk xh zm" "nvfasfh zk zm mjpavjuhw dzadnqzprr jk jk duszemmzl xldlpy nvfasfh" "xnxhf sviq nsab npywgdvgz osff vgsld farlehdhq fibokdry wjbkhzsa hhac" "zm azavwm scpgna jk jk bq jk duszemmzl fibokdry ovkyevlgv" "csjqxhgj zm jk jk duszemmzl zk xh zm jk zf" "urvysbnykk dzadnqzprr csjqxhgj mjpavjuhw ubwrfqnbjf nkaotm jk jk zm drir" "nvfasfh xh igju zm wluvgsw jk zm srwwnezqk ewle ovnq" "jk nvfasfh eh ktxdty urvysbnykk vgsld zm jk eh uenvbm" "orpfawpx pahlds jk uhzq hi zm zm zf jk dzadnqzprr" "srwwnezqk csjqxhgj rbwzuf nvfasfh jcpiwj xldlpy nvfasfh jk vgsld wjybxmieki" } proc add_empty_records {n} { execsql BEGIN for {set i 0} {$i < $n} {incr i} { execsql { INSERT INTO t1 VALUES('') } } execsql COMMIT } #set e [list] #foreach d $data {set e [concat $e $d]} #puts [lsort -unique $e] #exit set zero_long_doclists { UPDATE t1_segments SET block=zeroblob(length(block)) WHERE length(block)>10000 } foreach {tn setup} { 1 { set dmt_modes 0 execsql { CREATE VIRTUAL TABLE t1 USING FTS3 } foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } } } 2 { set dmt_modes 0 execsql { CREATE VIRTUAL TABLE t1 USING FTS4 } foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } } } 3 { set dmt_modes {0 1 2} execsql { CREATE VIRTUAL TABLE t1 USING FTS4 } foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } } add_empty_records 1000 execsql $zero_long_doclists } 4 { set dmt_modes 0 execsql { CREATE VIRTUAL TABLE t1 USING FTS4 } foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } } add_empty_records 1000 execsql "INSERT INTO t1(t1) VALUES('optimize')" execsql $zero_long_doclists } 5 { set dmt_modes 0 execsql { CREATE VIRTUAL TABLE t1 USING FTS4(matchinfo=fts3) } foreach doc $data { execsql { INSERT INTO t1 VALUES($doc) } } add_empty_records 1000 execsql $zero_long_doclists } } { execsql { DROP TABLE IF EXISTS t1 } eval $setup set ::testprefix fts3defer-2.$tn set DO_MALLOC_TEST 0 do_execsql_test 0 { SELECT count(*) FROM t1_segments WHERE length(block)>10000 } {2} do_select_test 1.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'jk xnxhf' } {13 29 40 47 48 52 63 92} do_select_test 1.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'jk eh' } {100} if {$tn==3} breakpoint do_select_test 1.3 { SELECT rowid FROM t1 WHERE t1 MATCH 'jk ubwrfqnbjf' } {7 70 98} do_select_test 1.4 { SELECT rowid FROM t1 WHERE t1 MATCH 'duszemmzl jk' } {3 5 8 10 13 18 20 23 32 37 41 43 55 60 65 67 72 74 76 81 94 96 97} do_select_test 1.5 { SELECT rowid FROM t1 WHERE t1 MATCH 'ubwrfqnbjf jk' } {7 70 98} do_select_test 1.6 { SELECT rowid FROM t1 WHERE t1 MATCH 'jk ubwrfqnbjf jk jk jk jk' } {7 70 98} do_select_test 1.7 { SELECT rowid FROM t1 WHERE t1 MATCH 'zm xnxhf' } {12 13 29 30 40 47 48 52 63 92 93} do_select_test 1.8 { SELECT rowid FROM t1 WHERE t1 MATCH 'zm eh' } {68 100} do_select_test 1.9 { SELECT rowid FROM t1 WHERE t1 MATCH 'zm ubwrfqnbjf' } {7 70 98} do_select_test 1.10 { SELECT rowid FROM t1 WHERE t1 MATCH 'z* vgsld' } {10 13 17 31 35 51 58 88 89 90 93 100} do_select_test 1.11 { SELECT rowid FROM t1 WHERE t1 MATCH '( zdu OR zexh OR zf OR zhbrzadb OR zidhxhbtv OR zk OR zkhdvkw OR zm OR zsmhnf ) vgsld' } {10 13 17 31 35 51 58 88 89 90 93 100} do_select_test 2.1 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm agmckuiu"' } {3 24 52 53} do_select_test 2.2 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm zf"' } {33 53 75 88 101} do_select_test 2.3 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm aayxpmve"' } {48 65 84} do_select_test 2.4 { SELECT rowid FROM t1 WHERE t1 MATCH '"aayxpmve zm"' } {11 37 84} do_select_test 2.5 { SELECT rowid FROM t1 WHERE t1 MATCH '"jk azavwm"' } {16 53} do_select_test 2.6 { SELECT rowid FROM t1 WHERE t1 MATCH '"xh jk jk"' } {18} do_select_test 2.7 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld"' } {13 17} do_select_test 2.8 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm jk vgsld lkjlkjlkj"' } {} do_select_test 3.1 { SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH '"zm agmckuiu"' } { {zm [zm] [agmckuiu] uhzq nsab jk rrkx duszemmzl hyq jk} {jk [zm] [agmckuiu] urvysbnykk jk jk zm zm jk jk} {[zm] [agmckuiu] zexh fibokdry jk uhzq bu tugflixoex xnxhf sk} {zm zf uenvbm jk azavwm zm [zm] [agmckuiu] zm jk} } do_select_test 3.2 { SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH 'xnxhf jk' } { {[xnxhf] [jk] [jk] agmckuiu duszemmzl [jk] zm zm [jk] vgsld} {[jk] [xnxhf] igju [jk] xh [jk] nvfasfh zm js [jk]} {[jk] jcpiwj zm [xnxhf] zm mjpavjuhw mj drir pa pvjrjlas} {gazrt [jk] ephknonq myjp uenvbm wuvajhwqz [jk] zm [xnxhf] nvfasfh} {zm aayxpmve csjqxhgj [xnxhf] xr [jk] aayxpmve [xnxhf] zm zm} {zm agmckuiu zexh fibokdry [jk] uhzq bu tugflixoex [xnxhf] sk} {lwa hi [xnxhf] qdyerbws zk njtc [jk] uhzq zm [jk]} {zm azavwm igju qzzqdxq [jk] [xnxhf] abthnzcv [jk] nvfasfh zm} } do_select_test 4.1 { SELECT offsets(t1) FROM t1 WHERE t1 MATCH '"jk uenvbm"' } { {0 0 10 2 0 1 13 6} {0 0 26 2 0 1 29 6} } do_select_test 4.2 { SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'duszemmzl jk fibokdry' } { {0 2 3 8 0 1 36 2 0 0 58 9} {0 0 0 9 0 1 13 2 0 1 16 2 0 2 19 8 0 1 53 2} {0 1 4 2 0 0 20 9 0 1 30 2 0 1 33 2 0 2 48 8} {0 1 17 2 0 1 20 2 0 1 26 2 0 0 29 9 0 2 39 8} } do_select_test 4.3 { SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'vgsld (hrlipdm OR (aapmve NEAR duszemmzl))' } {{0 0 0 5 0 1 15 7 0 0 36 5}} # The following block of tests runs normally with FTS3 or FTS4 without the # long doclists zeroed. And with OOM-injection for FTS4 with long doclists # zeroed. Change this by messing with the [set dmt_modes] commands above. # foreach DO_MALLOC_TEST $dmt_modes { # Phrase search. do_select_test 5.$DO_MALLOC_TEST.1 { SELECT rowid FROM t1 WHERE t1 MATCH '"jk mjpavjuhw"' } {8 15 36 64 67 72} # Multiple tokens search. do_select_test 5.$DO_MALLOC_TEST.2 { SELECT rowid FROM t1 WHERE t1 MATCH 'duszemmzl zm' } {3 5 8 10 12 13 18 20 23 37 43 55 60 65 67 72 74 81 94 96 97} # snippet() function with phrase. do_select_test 5.$DO_MALLOC_TEST.3 { SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH '"zm aayxpmve"' } { {[zm] [aayxpmve] csjqxhgj xnxhf xr jk aayxpmve xnxhf zm zm} {duszemmzl zk jk jk fibokdry jseuhjnzo [zm] [aayxpmve] zk jk} {zf jmfiqhwnjg js igju [zm] [aayxpmve] zm mbxnljomiv csjqxhgj nvfasfh} } # snippet() function with multiple tokens. do_select_test 5.$DO_MALLOC_TEST.4 { SELECT snippet(t1, '[', ']') FROM t1 WHERE t1 MATCH 'zm zhbrzadb' } { {[zm] jk [zm] [zm] [zhbrzadb] uenvbm aayxpmve urvysbnykk duszemmzl jk} } # snippet() function with phrase. do_select_test 5.$DO_MALLOC_TEST.5 { SELECT offsets(t1) FROM t1 WHERE t1 MATCH '"zm aayxpmve"' } { {0 0 0 2 0 1 3 8} {0 0 38 2 0 1 41 8} {0 0 22 2 0 1 25 8} } # snippet() function with multiple tokens. do_select_test 5.$DO_MALLOC_TEST.6 { SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'zm zhbrzadb' } { {0 0 0 2 0 0 6 2 0 0 9 2 0 1 12 8} } set DO_MALLOC_TEST 0 } do_select_test 6.1 { SELECT rowid FROM t1 WHERE t1 MATCH 'vgsld (hrlipdm OR (aayxpmve duszemmzl))' } {10} do_select_test 6.2.1 { SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk"' } {8} do_select_test 6.2.2 { SELECT rowid FROM t1 WHERE t1 MATCH '"zm azavwm"' } {15 26 92 96} do_select_test 6.2.3 { SELECT rowid FROM t1 WHERE t1 MATCH '"jk xduvfhk" OR "zm azavwm"' } {8 15 26 92 96} } set testprefix fts3defer do_execsql_test 3.1 { CREATE VIRTUAL TABLE x1 USING fts4(a, b); INSERT INTO x1 VALUES('a b c', 'd e f'); INSERT INTO x1 SELECT * FROM x1; INSERT INTO x1 SELECT * FROM x1; INSERT INTO x1 SELECT * FROM x1; INSERT INTO x1 SELECT * FROM x1; } do_execsql_test 3.2 " INSERT INTO x1 VALUES( '[string repeat {d } 3000]', '[string repeat {f } 30000]' ); INSERT INTO x1(x1) VALUES('optimize'); " do_execsql_test 3.3 { SELECT count(*) FROM x1 WHERE x1 MATCH '"d e f"' } {16} finish_test |
Added test/fts3defer2.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 | # 2010 October 23 # # 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 source $testdir/malloc_common.tcl ifcapable !fts3 { finish_test ; return } set testprefix fts3defer2 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 #----------------------------------------------------------------------------- # This block of tests - fts3defer2-1.* - test the interaction of deferred NEAR # expressions and the snippet, offsets and matchinfo functions. # do_execsql_test 1.1.1 { CREATE VIRTUAL TABLE t1 USING fts4; } do_execsql_test 1.1.2 "INSERT INTO t1 VALUES('[string repeat {a } 20000]')" do_execsql_test 1.1.3 "INSERT INTO t1 VALUES('[string repeat {z } 20000]')" do_execsql_test 1.1.4 { INSERT INTO t1 VALUES('a b c d e f a x y'); INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(''); INSERT INTO t1 VALUES(''); INSERT INTO t1(t1) VALUES('optimize'); } do_execsql_test 1.1.4 { SELECT count(*) FROM t1_segments WHERE length(block)>10000; UPDATE t1_segments SET block = zeroblob(length(block)) WHERE length(block)>10000; } {2} do_execsql_test 1.2.1 { SELECT content FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)'; } {{a b c d e f a x y}} do_execsql_test 1.2.2 { SELECT snippet(t1, '[', ']'), offsets(t1), mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)'; } [list \ {a b c d [e] [f] [a] x y} \ {0 1 8 1 0 0 10 1 0 2 12 1} \ [list 3 1 1 1 1 1 8 8 1 8 8 8 5001 9] ] do_execsql_test 1.2.3 { SELECT snippet(t1, '[', ']'), offsets(t1), mit(matchinfo(t1)) FROM t1 WHERE t1 MATCH 'f (e NEAR/3 a)'; } [list \ {[a] b c d [e] [f] [a] x y} \ {0 2 0 1 0 1 8 1 0 0 10 1 0 2 12 1} \ [list 3 1 1 1 1 1 8 8 2 8 8 8 5001 9] ] do_execsql_test 1.3.1 { DROP TABLE t1 } #----------------------------------------------------------------------------- # Test cases fts3defer2-2.* focus specifically on the matchinfo function. # do_execsql_test 2.1.1 "CREATE VIRTUAL TABLE t2 USING fts4" do_execsql_test 2.1.2 "INSERT INTO t2 VALUES('[string repeat {a } 10000]')" do_execsql_test 2.1.3 "INSERT INTO t2 VALUES('b [string repeat {z } 10000]')" do_execsql_test 2.1.4 [string repeat "INSERT INTO t2 VALUES('x');" 50] do_execsql_test 2.1.5 { INSERT INTO t2 VALUES('a b c d e f g'); INSERT INTO t2 VALUES('a b c d e f g'); } foreach {tn sql} { 1 {} 2 { INSERT INTO t2(t2) VALUES('optimize') } 3 { UPDATE t2_segments SET block = zeroblob(length(block)) WHERE length(block)>10000; } } { execsql $sql do_execsql_test 2.2.$tn { SELECT mit(matchinfo(t2)) FROM t2 WHERE t2 MATCH 'a b'; } [list \ [list 2 1 1 54 54 1 3 3 54 372 7] \ [list 2 1 1 54 54 1 3 3 54 372 7] \ ] } do_execsql_test 2.3.1 { CREATE VIRTUAL TABLE t3 USING fts4; INSERT INTO t3 VALUES('a b c d e f'); INSERT INTO t3 VALUES('x b c d e f'); INSERT INTO t3 VALUES('d e f a b c'); INSERT INTO t3 VALUES('b c d e f'); INSERT INTO t3 VALUES(''); INSERT INTO t3 VALUES(''); INSERT INTO t3 VALUES(''); INSERT INTO t3 VALUES(''); INSERT INTO t3 VALUES(''); INSERT INTO t3 VALUES(''); } do_execsql_test 2.3.2 " INSERT INTO t3 VALUES('f e d c b [string repeat {a } 10000]') " foreach {tn sql} { 1 {} 2 { INSERT INTO t3(t3) VALUES('optimize') } 3 { UPDATE t3_segments SET block = zeroblob(length(block)) WHERE length(block)>10000; } } { execsql $sql do_execsql_test 2.4.$tn { SELECT docid, mit(matchinfo(t3)) FROM t3 WHERE t3 MATCH '"a b c"'; } {1 {1 1 1 4 4 11 912 6} 3 {1 1 1 4 4 11 912 6}} } finish_test |
Added test/fts3fault.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 | # 2010 June 15 # # 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 fts3fault # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } # Test error handling in the sqlite3Fts3Init() function. This is the # function that registers the FTS3 module and various support functions # with SQLite. # do_faultsim_test 1 -body { sqlite3 db test.db expr 0 } -test { catch { db close } } # Test error handling in an "ALTER TABLE ... RENAME TO" statement on an # FTS3 table. Specifically, test renaming the table within a transaction # after it has been written to. # faultsim_delete_and_reopen do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts3; INSERT INTO t1 VALUES('test renaming the table'); INSERT INTO t1 VALUES(' after it has been written'); } do_faultsim_test 2 -prep { sqlite3 db test.db execsql { BEGIN; INSERT INTO t1 VALUES('registers the FTS3 module'); INSERT INTO t1 VALUES('various support functions'); } } -body { execsql { ALTER TABLE t1 RENAME TO t2 } } -test { faultsim_test_result {0 {}} } # Test error handling in the special case where a single prefix query # matches terms that reside on a large range of leaf nodes. # do_test fts3fault-3.0 { sqlite3 db test.db execsql { CREATE VIRTUAL TABLE t3 USING fts4; } execsql { INSERT INTO t3(t3) VALUES('nodesize=50') } execsql { BEGIN } for {set i 0} {$i < 1000} {incr i} { execsql { INSERT INTO t3 VALUES('aaa' || $i) } } execsql { COMMIT } } {} do_faultsim_test 3 -faults oom-transient -prep { sqlite3 db test.db execsql { SELECT * FROM t3 WHERE t3 MATCH 'x' } } -body { execsql { SELECT count(rowid) FROM t3 WHERE t3 MATCH 'aa*' } } -test { faultsim_test_result {0 1000} } do_test fts3fault-4.0 { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE t4 USING fts4; INSERT INTO t4 VALUES('The British Government called on'); INSERT INTO t4 VALUES('as pesetas then became much'); } } {} faultsim_save_and_close do_faultsim_test 4 -prep { faultsim_restore_and_reopen execsql { SELECT content FROM t4 } } -body { execsql { SELECT optimize(t4) FROM t4 LIMIT 1 } } -test { faultsim_test_result {0 {{Index optimized}}} } do_test fts3fault-5.0 { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE t5 USING fts4; INSERT INTO t5 VALUES('The British Government called on'); INSERT INTO t5 VALUES('as pesetas then became much'); } } {} faultsim_save_and_close do_faultsim_test 5 -prep { faultsim_restore_and_reopen execsql { BEGIN; INSERT INTO t5 VALUES('influential in shaping his future outlook'); INSERT INTO t5 VALUES('might be acceptable to the British electorate'); } } -body { execsql { SELECT rowid FROM t5 WHERE t5 MATCH 'british' } } -test { faultsim_test_result {0 {1 4}} } do_test fts3fault-6.0 { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE t6 USING fts4 } } {} faultsim_save_and_close do_faultsim_test 6 -prep { faultsim_restore_and_reopen execsql { SELECT rowid FROM t6 } } -body { execsql { DROP TABLE t6 } } -test { faultsim_test_result {0 {}} } # Test various malloc failures while processing FTS4 parameters. # do_faultsim_test 7.1 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fts3) } } -test { faultsim_test_result {0 {}} } do_faultsim_test 7.2 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchinfo=fs3) } } -test { faultsim_test_result {1 {unrecognized matchinfo: fs3}} \ {1 {vtable constructor failed: t1}} } do_faultsim_test 7.3 -prep { faultsim_delete_and_reopen } -body { execsql { CREATE VIRTUAL TABLE t1 USING fts4(a, b, matchnfo=fts3) } } -test { faultsim_test_result {1 {unrecognized parameter: matchnfo=fts3}} \ {1 {vtable constructor failed: t1}} } finish_test |
Changes to test/fts3malloc.test.
︙ | ︙ | |||
39 40 41 42 43 44 45 | proc normal_list {l} { set ret [list] foreach elem $l {lappend ret $elem} set ret } | < | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | proc normal_list {l} { set ret [list] foreach elem $l {lappend ret $elem} set ret } do_write_test fts3_malloc-1.1 sqlite_master { CREATE VIRTUAL TABLE ft1 USING fts3(a, b) } do_write_test fts3_malloc-1.2 sqlite_master { CREATE VIRTUAL TABLE ft2 USING fts3([a], [b]); } |
︙ | ︙ |
Added test/fts3matchinfo.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 | # 2010 November 02 # # 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 regression tests for the FTS3 module. The focus # of this file is tables created with the "matchinfo=fts3" option. # set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } set testprefix fts3matchinfo 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 do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts4(matchinfo=fts3); SELECT name FROM sqlite_master WHERE type = 'table'; } {t1 t1_content t1_segments t1_segdir t1_stat} 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. The # %_docsize table is created in this case and the array of integers returned # by matchinfo() includes the extra data. # do_execsql_test 1.2 { CREATE VIRTUAL TABLE t2 USING fts4; INSERT INTO t2 SELECT * FROM t1; SELECT mit(matchinfo(t2)) FROM t2 WHERE t2 MATCH 'I'; } {{1 1 1 2 2 4 7 6} {1 1 1 2 2 4 7 8}} # Test some syntax-error handling. # do_catchsql_test 2.0 { CREATE VIRTUAL TABLE x1 USING fts4(matchinfo=fs3); } {1 {unrecognized matchinfo: fs3}} do_catchsql_test 2.1 { CREATE VIRTUAL TABLE x2 USING fts4(mtchinfo=fts3); } {1 {unrecognized parameter: mtchinfo=fts3}} # Check that with fts3, the "=" character is permitted in column definitions. # do_execsql_test 3.1 { CREATE VIRTUAL TABLE t3 USING fts3(mtchinfo=fts3); INSERT INTO t3(mtchinfo) VALUES('Beside the lake, beneath the trees'); SELECT mtchinfo FROM t3; } {{Beside the lake, beneath the trees}} finish_test |
Changes to test/fts3query.test.
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 | # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/malloc_common.tcl source $testdir/fts3_common.tcl set DO_MALLOC_TEST 0 do_test fts3query-1.1 { execsql { CREATE VIRTUAL TABLE t1 USING fts3(x); BEGIN; INSERT INTO t1 VALUES('The source code for SQLite is in the public'); } } {} | > > | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # If this build does not include FTS3, skip the tests in this file. # ifcapable !fts3 { finish_test ; return } source $testdir/malloc_common.tcl source $testdir/fts3_common.tcl set DO_MALLOC_TEST 0 set testprefix fts3query do_test fts3query-1.1 { execsql { CREATE VIRTUAL TABLE t1 USING fts3(x); BEGIN; INSERT INTO t1 VALUES('The source code for SQLite is in the public'); } } {} |
︙ | ︙ | |||
100 101 102 103 104 105 106 | do_test fts3query-3.3 { execsql { SELECT mit(matchinfo(foobar)) FROM foobar WHERE foobar MATCH 'the' } } {{1 1 3 3 1}} # The following tests check that ticket 775b39dd3c has been fixed. # | < < < | | > > | > | | > > | > | | > > | > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test fts3query-3.3 { execsql { SELECT mit(matchinfo(foobar)) FROM foobar WHERE foobar MATCH 'the' } } {{1 1 3 3 1}} # The following tests check that ticket 775b39dd3c has been fixed. # do_test fts3query-4.1 { execsql { DROP TABLE IF EXISTS t1; CREATE TABLE t1(number INTEGER PRIMARY KEY, date); CREATE INDEX i1 ON t1(date); CREATE VIRTUAL TABLE ft USING fts3(title); CREATE TABLE bt(title); } } {} do_eqp_test fts3query-4.2 { SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date } { 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)} } do_eqp_test fts3query-4.3 { SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date } { 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)} } do_eqp_test fts3query-4.4 { SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date } { 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_eqp_test fts3query-4.5 { SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date } { 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)} 0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } # Test that calling matchinfo() with the wrong number of arguments, or with # an invalid argument returns an error. # do_execsql_test 5.1 { CREATE VIRTUAL TABLE t2 USING FTS4; INSERT INTO t2 VALUES('it was the first time in history'); } do_select_tests 5.2 -errorformat { wrong number of arguments to function %s() } { 1 "SELECT matchinfo() FROM t2 WHERE t2 MATCH 'history'" matchinfo 2 "SELECT matchinfo(t2, t2) FROM t2 WHERE t2 MATCH 'history'" matchinfo 3 "SELECT snippet(t2, 1, 2, 3, 4, 5, 6) FROM t2 WHERE t2 MATCH 'history'" snippet } do_select_tests 5.3 -errorformat { illegal first argument to %s } { 1 "SELECT matchinfo(content) FROM t2 WHERE t2 MATCH 'history'" matchinfo 2 "SELECT offsets(content) FROM t2 WHERE t2 MATCH 'history'" offsets 3 "SELECT snippet(content) FROM t2 WHERE t2 MATCH 'history'" snippet 4 "SELECT optimize(content) FROM t2 WHERE t2 MATCH 'history'" optimize } do_execsql_test 5.4.0 { UPDATE t2_content SET c0content = X'1234' } do_select_tests 5.4 -errorformat { illegal first argument to %s } { 1 "SELECT matchinfo(content) FROM t2 WHERE t2 MATCH 'history'" matchinfo 2 "SELECT offsets(content) FROM t2 WHERE t2 MATCH 'history'" offsets 3 "SELECT snippet(content) FROM t2 WHERE t2 MATCH 'history'" snippet 4 "SELECT optimize(content) FROM t2 WHERE t2 MATCH 'history'" optimize } do_execsql_test 5.5 { DROP TABLE t2 } # Test the snippet() function with 1 to 6 arguments. # do_execsql_test 6.1 { CREATE VIRTUAL TABLE t3 USING FTS4(a, b); INSERT INTO t3 VALUES('no gestures', 'another intriguing discovery by observing the hand gestures (called beats) people make while speaking. Research has shown that such gestures do more than add visual emphasis to our words (many people gesture while they''re on the telephone, for example); it seems they actually help our brains find words'); } do_select_tests 6.2 { 1 "SELECT snippet(t3) FROM t3 WHERE t3 MATCH 'gestures'" {{<b>...</b>hand <b>gestures</b> (called beats) people make while speaking. Research has shown that such <b>gestures</b> do<b>...</b>}} 2 "SELECT snippet(t3, 'XXX') FROM t3 WHERE t3 MATCH 'gestures'" {{<b>...</b>hand XXXgestures</b> (called beats) people make while speaking. Research has shown that such XXXgestures</b> do<b>...</b>}} 3 "SELECT snippet(t3, 'XXX', 'YYY') FROM t3 WHERE t3 MATCH 'gestures'" {{<b>...</b>hand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY do<b>...</b>}} 4 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ') FROM t3 WHERE t3 MATCH 'gestures'" {{ZZZhand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY doZZZ}} 5 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 1) FROM t3 WHERE t3 MATCH 'gestures'" {{ZZZhand XXXgesturesYYY (called beats) people make while speaking. Research has shown that such XXXgesturesYYY doZZZ}} 6 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 0) FROM t3 WHERE t3 MATCH 'gestures'" {{no XXXgesturesYYY}} 7 "SELECT snippet(t3, 'XXX', 'YYY', 'ZZZ', 1, 5) FROM t3 WHERE t3 MATCH 'gestures'" {{ZZZthe hand XXXgesturesYYY (called beatsZZZ}} } finish_test |
Added test/fts3shared.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 | # 2010 September 17 # # 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 ifcapable !fts3||!shared_cache { finish_test return } db close set ::enable_shared_cache [sqlite3_enable_shared_cache 1] # Open two connections to the database in shared-cache mode. # sqlite3 db test.db sqlite3 db2 test.db # Create a virtual FTS3 table. Populate it with some initial data. # do_execsql_test fts3shared-1.1 { CREATE VIRTUAL TABLE t1 USING fts3(x); BEGIN; INSERT INTO t1 VALUES('We listened and looked sideways up!'); INSERT INTO t1 VALUES('Fear at my heart, as at a cup,'); INSERT INTO t1 VALUES('My life-blood seemed to sip!'); INSERT INTO t1 VALUES('The stars were dim, and thick the night'); COMMIT; } {} # Open a write transaction and insert rows into the FTS3 table. This takes # a write-lock on the underlying t1_content table. # do_execsql_test fts3shared-1.2 { BEGIN; INSERT INTO t1 VALUES('The steersman''s face by his lamp gleamed white;'); } {} # Now try a SELECT on the full-text table. This particular SELECT does not # read data from the %_content table. But it still attempts to obtain a lock # on that table and so the SELECT fails. # do_test fts3shared-1.3 { catchsql { BEGIN; SELECT rowid FROM t1 WHERE t1 MATCH 'stars' } db2 } {1 {database table is locked}} # Verify that the first connection can commit its transaction. # do_test fts3shared-1.4 { sqlite3_get_autocommit db } 0 do_execsql_test fts3shared-1.5 { COMMIT } {} do_test fts3shared-1.6 { sqlite3_get_autocommit db } 1 # Verify that the second connection still has an open transaction. # do_test fts3shared-1.6 { sqlite3_get_autocommit db2 } 0 db close db2 close sqlite3_enable_shared_cache $::enable_shared_cache finish_test |
Changes to test/fts3snippet.test.
︙ | ︙ | |||
16 17 18 19 20 21 22 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl | < | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | set testdir [file dirname $argv0] source $testdir/tester.tcl # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } source $testdir/fts3_common.tcl set sqlite_fts3_enable_parentheses 1 set DO_MALLOC_TEST 0 # Transform the list $L to its "normal" form. So that it can be compared to # another list with the same set of elements using [string compare]. # |
︙ | ︙ | |||
429 430 431 432 433 434 435 | {2 2 1 3 3 1 6 3 0 0 0 0 3 2} {2 2 1 3 3 2 6 3 0 0 0 1 3 2} {2 2 1 3 3 2 6 3 0 0 0 1 3 2} {2 2 1 3 3 3 6 3 0 0 0 2 3 2} {2 2 1 3 3 3 6 3 0 0 0 2 3 2} }] | | | | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | {2 2 1 3 3 1 6 3 0 0 0 0 3 2} {2 2 1 3 3 2 6 3 0 0 0 1 3 2} {2 2 1 3 3 2 6 3 0 0 0 1 3 2} {2 2 1 3 3 3 6 3 0 0 0 2 3 2} {2 2 1 3 3 3 6 3 0 0 0 2 3 2} }] # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the # "query by rowid" or "linear scan" strategies, then the snippet and # offsets both return an empty string, and the matchinfo function # returns a blob value zero bytes in size. # set r 1000000 ;# A rowid that exists in table ft do_select_test $T.10.0 { SELECT rowid FROM ft WHERE rowid = $r } $r do_select_test $T.10.1 { SELECT length(offsets(ft)), typeof(offsets(ft)) FROM ft; } {0 text 0 text 0 text} |
︙ | ︙ | |||
455 456 457 458 459 460 461 | do_select_test $T.10.5 { SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft; } {0 blob 0 blob 0 blob} do_select_test $T.10.6 { SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft WHERE rowid = $r } {0 blob} } | < | 454 455 456 457 458 459 460 461 462 463 | do_select_test $T.10.5 { SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft; } {0 blob 0 blob 0 blob} do_select_test $T.10.6 { SELECT length(matchinfo(ft)), typeof(matchinfo(ft)) FROM ft WHERE rowid = $r } {0 blob} } set sqlite_fts3_enable_parentheses 0 finish_test |
Added test/func3.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 | # 2010 August 27 # # 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 regression tests for SQLite library. The # focus of this file is testing that destructor functions associated # with functions created using sqlite3_create_function_v2() is # correctly invoked. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test func3-1.1 { set destroyed 0 proc destroy {} { set ::destroyed 1 } sqlite3_create_function_v2 db f2 -1 any -func f2 -destroy destroy set destroyed } 0 do_test func3-1.2 { sqlite3_create_function_v2 db f2 -1 utf8 -func f2 set destroyed } 0 do_test func3-1.3 { sqlite3_create_function_v2 db f2 -1 utf16le -func f2 set destroyed } 0 do_test func3-1.4 { sqlite3_create_function_v2 db f2 -1 utf16be -func f2 set destroyed } 1 do_test func3-2.1 { set destroyed 0 proc destroy {} { set ::destroyed 1 } sqlite3_create_function_v2 db f3 -1 utf8 -func f3 -destroy destroy set destroyed } 0 do_test func3-2.2 { sqlite3_create_function_v2 db f3 -1 utf8 -func f3 set destroyed } 1 do_test func3-3.1 { set destroyed 0 proc destroy {} { set ::destroyed 1 } sqlite3_create_function_v2 db f3 -1 any -func f3 -destroy destroy set destroyed } 0 do_test func3-3.2 { db close set destroyed } 1 sqlite3 db test.db do_test func3-4.1 { set destroyed 0 set rc [catch { sqlite3_create_function_v2 db f3 -1 any -func f3 -step f3 -destroy destroy } msg] list $rc $msg } {1 SQLITE_MISUSE} do_test func3-4.2 { set destroyed } 1 finish_test |
Changes to test/in.test.
︙ | ︙ | |||
400 401 402 403 404 405 406 | SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} do_test in-12.6 { catchsql { SELECT * FROM t2 WHERE a IN ( | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | SELECT a, b FROM t3 INTERSECT SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} do_test in-12.6 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a, b FROM t3 UNION ALL SELECT a FROM t2 ); } } {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}} do_test in-12.7 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a, b FROM t3 UNION SELECT a FROM t2 ); } } {1 {SELECTs to the left and right of UNION do not have the same number of result columns}} do_test in-12.8 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a, b FROM t3 EXCEPT SELECT a FROM t2 ); } } {1 {SELECTs to the left and right of EXCEPT do not have the same number of result columns}} do_test in-12.9 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a, b FROM t3 INTERSECT SELECT a FROM t2 ); } } {1 {SELECTs to the left and right of INTERSECT do not have the same number of result columns}} } do_test in-12.10 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a FROM t3 UNION ALL SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} do_test in-12.11 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a FROM t3 UNION SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} do_test in-12.12 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a FROM t3 EXCEPT SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} do_test in-12.13 { catchsql { SELECT * FROM t2 WHERE a IN ( SELECT a FROM t3 INTERSECT SELECT a, b FROM t2 ); } } {1 {only a single result allowed for a SELECT that is part of an expression}} #------------------------------------------------------------------------ # The following tests check that NULL is handled correctly when it # appears as part of a set of values on the right-hand side of an # IN or NOT IN operator. # # When it appears in such a set, NULL is handled as an "unknown value". |
︙ | ︙ |
Changes to test/incrblob.test.
︙ | ︙ | |||
416 417 418 419 420 421 422 423 | # another connection has the database RESERVED lock. # # Then test that blob writes that take place inside of a # transaction are not visible to external connections until # after the transaction is commited and the blob channel # closed. # sqlite3_soft_heap_limit 0 | > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | # another connection has the database RESERVED lock. # # Then test that blob writes that take place inside of a # transaction are not visible to external connections until # after the transaction is commited and the blob channel # closed. # # This test does not work with the "memsubsys1" configuration. # Permutation memsubsys1 configures a very small static allocation # for use as page-cache memory. This causes SQLite to upgrade # to an exclusive lock when writing earlier than usual, which # makes some of these tests fail. # sqlite3_soft_heap_limit 0 if {[permutation] != "memsubsys1"} { do_test incrblob-6.1 { sqlite3 db2 test.db execsql { BEGIN; INSERT INTO blobs(k, v, i) VALUES('a', 'different', 'connection'); } db2 } {} do_test incrblob-6.2 { execsql { SELECT rowid FROM blobs } } {1 2 3} do_test incrblob-6.3 { set rc [catch { db incrblob blobs v 1 } msg] list $rc $msg } {1 {database is locked}} do_test incrblob-6.4 { set rc [catch { db incrblob blobs v 3 } msg] list $rc $msg } {1 {database is locked}} do_test incrblob-6.5 { set ::blob [db incrblob -readonly blobs v 3] read $::blob } {hello} do_test incrblob-6.6 { close $::blob } {} do_test incrblob-6.7 { set ::blob [db2 incrblob blobs i 4] gets $::blob } {connection} do_test incrblob-6.8 { tell $::blob } {10} do_test incrblob-6.9 { seek $::blob 0 puts -nonewline $::blob "invocation" flush $::blob } {} # At this point rollback should be illegal (because # there is an open blob channel). But commit is also illegal because # the open blob is read-write. # do_test incrblob-6.10 { catchsql { ROLLBACK; } db2 } {1 {cannot rollback transaction - SQL statements in progress}} do_test incrblob-6.11 { catchsql { COMMIT; } db2 } {1 {cannot commit transaction - SQL statements in progress}} do_test incrblob-6.12 { execsql { SELECT * FROM blobs WHERE rowid = 4; } } {} do_test incrblob-6.13 { close $::blob } {} do_test incrblob-6.14 { catchsql { COMMIT; } db2 } {0 {}} do_test incrblob-6.15 { execsql { SELECT * FROM blobs WHERE rowid = 4; } } {a different invocation} db2 close } sqlite3_soft_heap_limit $cmdlinearg(soft-heap-limit) #----------------------------------------------------------------------- # The following tests verify the behaviour of the incremental IO # APIs in the following cases: # # 7.1 A row that containing an open blob is modified. |
︙ | ︙ | |||
664 665 666 667 668 669 670 671 672 673 | do_test incrblob-8.6 { set rc [catch {sqlite3_blob_write $::b 0 etilqs 6} msg] lappend rc $msg } {0 {}} do_test incrblob-8.7 { execsql {SELECT b FROM t1 WHERE a = 314159} } {etilqs} finish_test | > > > > > > > > > | 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 | do_test incrblob-8.6 { set rc [catch {sqlite3_blob_write $::b 0 etilqs 6} msg] lappend rc $msg } {0 {}} do_test incrblob-8.7 { execsql {SELECT b FROM t1 WHERE a = 314159} } {etilqs} # The following test case exposes an instance in the blob code where # an error message was set using a call similar to sqlite3_mprintf(zErr), # where zErr is an arbitrary string. This is no good if the string contains # characters that can be mistaken for printf() formatting directives. # do_test incrblob-9.1 { list [catch { db incrblob t1 "A tricky column name %s%s" 1 } msg] $msg } {1 {no such column: "A tricky column name %s%s"}} finish_test |
Added test/incrblob3.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 | # 2010 October 20 # # 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 sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 do_execsql_test incrblob3-1.1 { CREATE TABLE blobs(k INTEGER PRIMARY KEY, v BLOB); INSERT INTO blobs VALUES(1, zeroblob(100)); INSERT INTO blobs VALUES(2, zeroblob(100)); } {} # Test the sqlite3_blob_reopen()/read()/write() functions. # do_test incrblob3-1.2 { set ::blob [db incrblob blobs v 1] puts $::blob "hello world" } {} do_test incrblob3-1.3 { sqlite3_blob_reopen $::blob 2 puts $::blob "world hello" } {} do_test incrblob3-1.4 { sqlite3_blob_reopen $::blob 1 gets $::blob } {hello world} do_test incrblob3-1.5 { sqlite3_blob_reopen $::blob 2 gets $::blob } {world hello} do_test incrblob3-1.6 { close $::blob } {} # Test some error conditions. # # incrblob3-2.1: Attempting to reopen a row that does not exist. # incrblob3-2.2: Attempting to reopen a row that does not contain a blob # or text value. # do_test incrblob3-2.1.1 { set ::blob [db incrblob blobs v 1] list [catch {sqlite3_blob_reopen $::blob 3} msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-2.1.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } {SQLITE_ERROR {no such rowid: 3}} do_test incrblob3-2.1.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg } {1 SQLITE_ABORT} do_test incrblob3-2.1.4 { close $::blob } {} do_execsql_test incrblob3-2.2.1 { INSERT INTO blobs VALUES(3, 42); INSERT INTO blobs VALUES(4, 54.4); INSERT INTO blobs VALUES(5, NULL); } foreach {tn rowid type} { 1 3 integer 2 4 real 3 5 null } { do_test incrblob3-2.2.$tn.1 { set ::blob [db incrblob blobs v 1] list [catch {sqlite3_blob_reopen $::blob $rowid} msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-2.2.$tn.2 { list [sqlite3_errcode db] [sqlite3_errmsg db] } "SQLITE_ERROR {cannot open value of type $type}" do_test incrblob3-2.2.$tn.3 { list [catch {sqlite3_blob_reopen $::blob 1} msg] $msg } {1 SQLITE_ABORT} do_test incrblob3-2.2.$tn.4 { list [catch {sqlite3_blob_read $::blob 0 10} msg] $msg } {1 SQLITE_ABORT} do_test incrblob3-2.2.$tn.5 { list [catch {sqlite3_blob_write $::blob 0 "abcd"} msg] $msg } {1 SQLITE_ABORT} do_test incrblob3-2.2.$tn.4 { close $::blob } {} } # Test that passing NULL to sqlite3_blob_XXX() APIs returns SQLITE_MISUSE. # # incrblob3-3.1: sqlite3_blob_reopen() # incrblob3-3.2: sqlite3_blob_read() # incrblob3-3.3: sqlite3_blob_write() # incrblob3-3.4: sqlite3_blob_bytes() # do_test incrblob3-3.1 { list [catch {sqlite3_blob_reopen {} 3} msg] $msg } {1 SQLITE_MISUSE} do_test incrblob3-3.2 { list [catch {sqlite3_blob_read {} 0 10} msg] $msg } {1 SQLITE_MISUSE} do_test incrblob3-3.3 { list [catch {sqlite3_blob_write {} 0 "abcd"} msg] $msg } {1 SQLITE_MISUSE} do_test incrblob3-3.4 { sqlite3_blob_bytes {} } {0} do_test incrblob3-3.5 { sqlite3_blob_close {} } {} # Test out-of-range reading and writing # do_test incrblob3-4.1 { set ::blob [db incrblob blobs v 1] sqlite3_blob_bytes $::blob } {100} do_test incrblob3-4.2 { list [catch { sqlite3_blob_read $::blob -1 10 } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.3 { list [catch { sqlite3_blob_read $::blob 0 -10 } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.4 { list [catch { sqlite3_blob_read $::blob 95 10 } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.5 { list [catch { sqlite3_blob_write $::blob -1 "abcdefghij" 10 } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.6 { list [catch { sqlite3_blob_write $::blob 0 "abcdefghij" -10 } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.7 { list [catch { sqlite3_blob_write $::blob 95 "abcdefghij" } msg] $msg } {1 SQLITE_ERROR} do_test incrblob3-4.8 { close $::blob } {} # Test that modifying the row a blob handle points to aborts the blob. # do_test incrblob3-5.1 { set ::blob [db incrblob blobs v 1] sqlite3_blob_bytes $::blob } {100} do_test incrblob3-5.2 { execsql { UPDATE blobs SET v = '123456789012345678901234567890' WHERE k = 1 } list [catch { sqlite3_blob_read $::blob 0 10 } msg] $msg } {1 SQLITE_ABORT} # Test various errors that can occur in sqlite3_blob_open(): # # 1. Trying to open a virtual table column. # 2. Trying to open a view column. # 3. Trying to open a column that does not exist. # 4. Trying to open a read/write handle on an indexed column. # 5. Trying to open a read/write handle on the child key of an FK constraint. # ifcapable fts3 { do_test incrblob3-6.1 { execsql { CREATE VIRTUAL TABLE ft USING fts3; INSERT INTO ft VALUES('rules to open a column to which'); } list [catch { db incrblob ft content 1 } msg] $msg } {1 {cannot open virtual table: ft}} } ifcapable view { do_test incrblob3-6.2 { execsql { CREATE VIEW v1 AS SELECT * FROM blobs } list [catch { db incrblob v1 content 1 } msg] $msg } {1 {cannot open view: v1}} } do_test incrblob3-6.3 { list [catch { db incrblob blobs content 1 } msg] $msg } {1 {no such column: "content"}} do_test incrblob3-6.4.1 { execsql { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b); INSERT INTO t1 VALUES(zeroblob(100), zeroblob(100)); } list [catch { db incrblob t1 b 1 } msg] $msg } {1 {cannot open indexed column for writing}} do_test incrblob3-6.4.2 { set ::blob [db incrblob t1 a 1] close $::blob } {} do_test incrblob3-6.4.3 { set ::blob [db incrblob -readonly t1 b 1] close $::blob } {} do_test incrblob3-6.5.1 { execsql { CREATE TABLE p1(a PRIMARY KEY); CREATE TABLE c1(a, b REFERENCES p1); PRAGMA foreign_keys = 1; INSERT INTO p1 VALUES(zeroblob(100)); INSERT INTO c1 VALUES(zeroblob(100), zeroblob(100)); } list [catch { db incrblob c1 b 1 } msg] $msg } {1 {cannot open foreign key column for writing}} do_test incrblob3-6.5.2 { set ::blob [db incrblob c1 a 1] close $::blob } {} do_test incrblob3-6.5.3 { set ::blob [db incrblob -readonly c1 b 1] close $::blob } {} do_test incrblob3-6.5.4 { execsql { PRAGMA foreign_keys = 0 } set ::blob [db incrblob c1 b 1] close $::blob } {} # Test that sqlite3_blob_open() handles transient and persistent schema # errors correctly. # do_test incrblob3-7.1 { sqlite3 db2 test.db sqlite3_db_config_lookaside db2 0 0 0 execsql { CREATE TABLE t2(x) } db2 set ::blob [db incrblob blobs v 1] close $::blob } {} db2 close testvfs tvfs -default 1 tvfs filter xAccess tvfs script access_method proc access_method {args} { set schemacookie [hexio_get_int [hexio_read test.db 40 4]] incr schemacookie hexio_write test.db 40 [hexio_render_int32 $schemacookie] set dbversion [hexio_get_int [hexio_read test.db 24 4]] incr dbversion hexio_write test.db 24 [hexio_render_int32 $dbversion] return "" } do_test incrblob3-7.2 { sqlite3 db test.db sqlite3_db_config_lookaside db 0 0 0 list [catch {db incrblob blobs v 1} msg] $msg } {1 {database schema has changed}} db close tvfs delete finish_test |
Added test/incrblobfault.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 | # 2010 October 26 # # 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 incrblobfault do_execsql_test 1.0 { CREATE TABLE blob(x INTEGER PRIMARY KEY, v BLOB); INSERT INTO blob VALUES(1, 'hello world'); INSERT INTO blob VALUES(2, 'world hello'); INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; INSERT INTO blob SELECT NULL, v FROM blob; } do_faultsim_test 1 -prep { sqlite3 db test.db set ::blob [db incrblob blob v 1] } -body { if {[catch {sqlite3_blob_reopen $::blob 1000}]} { error [sqlite3_errmsg db] } } -test { faultsim_test_result {0 {}} close $::blob } do_faultsim_test 2 -prep { sqlite3 db test.db set ::blob [db incrblob blob v 1] } -body { if {[catch {sqlite3_blob_reopen $::blob -1}]} { error [sqlite3_errmsg db] } } -test { faultsim_test_result {1 {no such rowid: -1}} close $::blob } do_faultsim_test 3 -prep { sqlite3 db test.db } -body { set ::blob [db incrblob blob v 1] gets $::blob } -test { faultsim_test_result {0 {hello world}} catch { close $::blob } } finish_test |
Changes to test/index.test.
︙ | ︙ | |||
528 529 530 531 532 533 534 | INSERT INTO t1 VALUES('12.32e+4',5); INSERT INTO t1 VALUES('12.36E+04',6); INSERT INTO t1 VALUES('12.36E+',7); INSERT INTO t1 VALUES('+123.10000E+0003',8); INSERT INTO t1 VALUES('+',9); INSERT INTO t1 VALUES('+12347.E+02',10); INSERT INTO t1 VALUES('+12347E+02',11); | > > > > | | > > > > > | | 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 | INSERT INTO t1 VALUES('12.32e+4',5); INSERT INTO t1 VALUES('12.36E+04',6); INSERT INTO t1 VALUES('12.36E+',7); INSERT INTO t1 VALUES('+123.10000E+0003',8); INSERT INTO t1 VALUES('+',9); INSERT INTO t1 VALUES('+12347.E+02',10); INSERT INTO t1 VALUES('+12347E+02',11); INSERT INTO t1 VALUES('+.125E+04',12); INSERT INTO t1 VALUES('-.125E+04',13); INSERT INTO t1 VALUES('.125E+0',14); INSERT INTO t1 VALUES('.125',15); SELECT b FROM t1 ORDER BY a, b; } } {13 14 15 12 8 5 2 1 3 6 10 11 9 4 7} do_test index-15.3 { execsql { SELECT b FROM t1 WHERE typeof(a) IN ('integer','real') ORDER BY b; } } {1 2 3 5 6 8 10 11 12 13 14 15} integrity_check index-15.4 # The following tests - index-16.* - test that when a table definition # includes qualifications that specify the same constraint twice only a # single index is generated to enforce the constraint. # # For example: "CREATE TABLE abc( x PRIMARY KEY, UNIQUE(x) );" # |
︙ | ︙ |
Changes to test/indexedby.test.
︙ | ︙ | |||
36 37 38 39 40 41 42 | # proc EQP {sql} { uplevel "execsql {EXPLAIN QUERY PLAN $sql}" } # These tests are to check that "EXPLAIN QUERY PLAN" is working as expected. # | | | | | | | | | > > | > | 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 | # proc EQP {sql} { uplevel "execsql {EXPLAIN QUERY PLAN $sql}" } # These tests are to check that "EXPLAIN QUERY PLAN" is working as expected. # do_execsql_test indexedby-1.2 { EXPLAIN QUERY PLAN select * from t1 WHERE a = 10; } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}} do_execsql_test indexedby-1.3 { EXPLAIN QUERY PLAN select * from t1 ; } {0 0 0 {SCAN TABLE t1 (~1000000 rows)}} do_execsql_test indexedby-1.4 { EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10; } { 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} 0 1 0 {SCAN TABLE t1 (~1000000 rows)} } # Parser tests. Test that an INDEXED BY or NOT INDEX clause can be # attached to a table in the FROM clause, but not to a sub-select or # SQL view. Also test that specifying an index that does not exist or # is attached to a different table is detected as an error. # do_test indexedby-2.1 { |
︙ | ︙ | |||
76 77 78 79 80 81 82 | } {1 {near "WHERE": syntax error}} do_test indexedby-2.7 { catchsql { SELECT * FROM v1 INDEXED BY i1 WHERE a = 'one' } } {1 {no such index: i1}} # Tests for single table cases. # | | | | | > | | | > | | | > | | | > | | | | > | > > | | > | > > | < | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | > | | | 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 | } {1 {near "WHERE": syntax error}} do_test indexedby-2.7 { catchsql { SELECT * FROM v1 INDEXED BY i1 WHERE a = 'one' } } {1 {no such index: i1}} # Tests for single table cases. # do_execsql_test indexedby-3.1 { EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two' } {0 0 0 {SCAN TABLE t1 (~10000 rows)}} do_execsql_test indexedby-3.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two' } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} do_execsql_test indexedby-3.3 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two' } {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-3.4 { catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' } } {1 {cannot use index: i2}} do_test indexedby-3.5 { catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a } } {1 {cannot use index: i2}} do_test indexedby-3.6 { catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' } } {0 {}} do_test indexedby-3.7 { catchsql { SELECT * FROM t1 INDEXED BY i1 ORDER BY a } } {0 {}} do_execsql_test indexedby-3.8 { EXPLAIN QUERY PLAN SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e } {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1 (~1000000 rows)}} do_execsql_test indexedby-3.9 { EXPLAIN QUERY PLAN SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10 } {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?) (~1 rows)}} do_test indexedby-3.10 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 } } {1 {cannot use index: sqlite_autoindex_t3_1}} do_test indexedby-3.11 { catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 } } {1 {no such index: sqlite_autoindex_t3_2}} # Tests for multiple table cases. # do_execsql_test indexedby-4.1 { EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c } { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)} } do_execsql_test indexedby-4.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c } { 0 0 1 {SCAN TABLE t2 (~1000000 rows)} 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)} } do_test indexedby-4.3 { catchsql { SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c } } {1 {cannot use index: i1}} do_test indexedby-4.4 { catchsql { SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c } } {1 {cannot use index: i3}} # Test embedding an INDEXED BY in a CREATE VIEW statement. This block # also tests that nothing bad happens if an index refered to by # a CREATE VIEW statement is dropped and recreated. # do_execsql_test indexedby-5.1 { CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5; EXPLAIN QUERY PLAN SELECT * FROM v2 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~330000 rows)}} do_execsql_test indexedby-5.2 { EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~33000 rows)}} do_test indexedby-5.3 { execsql { DROP INDEX i1 } catchsql { SELECT * FROM v2 } } {1 {no such index: i1}} do_test indexedby-5.4 { # Recreate index i1 in such a way as it cannot be used by the view query. execsql { CREATE INDEX i1 ON t1(b) } catchsql { SELECT * FROM v2 } } {1 {cannot use index: i1}} do_test indexedby-5.5 { # Drop and recreate index i1 again. This time, create it so that it can # be used by the query. execsql { DROP INDEX i1 ; CREATE INDEX i1 ON t1(a) } catchsql { SELECT * FROM v2 } } {0 {}} # Test that "NOT INDEXED" may use the rowid index, but not others. # do_execsql_test indexedby-6.1 { EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid } {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}} do_execsql_test indexedby-6.2 { EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid } {0 0 0 {SCAN TABLE t1 USING INTEGER PRIMARY KEY (~100000 rows)}} # Test that "INDEXED BY" can be used in a DELETE statement. # do_execsql_test indexedby-7.1 { EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5 } {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} do_execsql_test indexedby-7.2 { EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5 } {0 0 0 {SCAN TABLE t1 (~100000 rows)}} do_execsql_test indexedby-7.3 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 } {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} do_execsql_test indexedby-7.4 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} do_execsql_test indexedby-7.5 { EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10 } {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-7.6 { catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5} } {1 {cannot use index: i2}} # Test that "INDEXED BY" can be used in an UPDATE statement. # do_execsql_test indexedby-8.1 { EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5 } {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} do_execsql_test indexedby-8.2 { EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5 } {0 0 0 {SCAN TABLE t1 (~100000 rows)}} do_execsql_test indexedby-8.3 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 } {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}} do_execsql_test indexedby-8.4 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10 } {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}} do_execsql_test indexedby-8.5 { EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10 } {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}} do_test indexedby-8.6 { catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5} } {1 {cannot use index: i2}} # Test that bug #3560 is fixed. # do_test indexedby-9.1 { |
︙ | ︙ |
Changes to test/ioerr.test.
︙ | ︙ | |||
131 132 133 134 135 136 137 138 | CREATE TABLE test2.t2(a,b,c); COMMIT; } -exclude $ex } # Test IO errors when replaying two hot journals from a 2-file # transaction. This test only runs on UNIX. ifcapable crashtest&&attach { | > > > > > | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | CREATE TABLE test2.t2(a,b,c); COMMIT; } -exclude $ex } # Test IO errors when replaying two hot journals from a 2-file # transaction. This test only runs on UNIX. # # It cannot be run under the "exclusive" permutation. In that case, the # locks held by the connection in the local (this) process prevent a # second connection from attempting the multi-file transaction. # ifcapable crashtest&&attach { if {![catch {sqlite3 -has-codec} r] && !$r && [permutation]!="exclusive"} { do_ioerr_test ioerr-6 -ckrefcount true -tclprep { execsql { ATTACH 'test2.db' as aux; CREATE TABLE tx(a, b); CREATE TABLE aux.ty(a, b); } set rc [crashsql -delay 2 -file test2.db-journal { |
︙ | ︙ |
Changes to test/like.test.
︙ | ︙ | |||
111 112 113 114 115 116 117 | # Tests of the REGEXP operator # do_test like-2.1 { proc test_regexp {a b} { return [regexp $a $b] } | | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | # Tests of the REGEXP operator # do_test like-2.1 { proc test_regexp {a b} { return [regexp $a $b] } db function regexp -argcount 2 test_regexp execsql { SELECT x FROM t1 WHERE x REGEXP 'abc' ORDER BY 1; } } {{ABC abc xyz} abc abcd} do_test like-2.2 { execsql { SELECT x FROM t1 WHERE x REGEXP '^abc' ORDER BY 1; |
︙ | ︙ | |||
604 605 606 607 608 609 610 | db eval { SELECT 1, x FROM t8 WHERE x LIKE '%h%'; SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; } } {1 abcdef 1 ghijkl 1 mnopqr 2 abcdef 2 ghijkl 2 mnopqr} | | | 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 | db eval { SELECT 1, x FROM t8 WHERE x LIKE '%h%'; SELECT 2, x FROM t8 WHERE x LIKE '%h%' ESCAPE 'x'; } } {1 abcdef 1 ghijkl 1 mnopqr 2 abcdef 2 ghijkl 2 mnopqr} ifcapable like_opt&&!icu { # Evaluate SQL. Return the result set followed by the # and the number of full-scan steps. # db close sqlite3 db test.db proc count_steps {sql} { set r [db eval $sql] |
︙ | ︙ | |||
669 670 671 672 673 674 675 | do_test like-9.5.2 { set res [sqlite3_exec_hex db { EXPLAIN QUERY PLAN SELECT x FROM t2 WHERE x LIKE '%fe%25' }] regexp {INDEX i2} $res } {1} } | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 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 | do_test like-9.5.2 { set res [sqlite3_exec_hex db { EXPLAIN QUERY PLAN SELECT x FROM t2 WHERE x LIKE '%fe%25' }] regexp {INDEX i2} $res } {1} } # Do an SQL statement. Append the search count to the end of the result. # proc count sql { set ::sqlite_search_count 0 set ::sqlite_like_count 0 return [concat [execsql $sql] scan $::sqlite_search_count \ like $::sqlite_like_count] } # The LIKE and GLOB optimizations do not work on columns with # affinity other than TEXT. # Ticket #3901 # do_test like-10.1 { db close sqlite3 db test.db execsql { CREATE TABLE t10( a INTEGER PRIMARY KEY, b INTEGER COLLATE nocase UNIQUE, c NUMBER COLLATE nocase UNIQUE, d BLOB COLLATE nocase UNIQUE, e COLLATE nocase UNIQUE, f TEXT COLLATE nocase UNIQUE ); INSERT INTO t10 VALUES(1,1,1,1,1,1); INSERT INTO t10 VALUES(12,12,12,12,12,12); INSERT INTO t10 VALUES(123,123,123,123,123,123); INSERT INTO t10 VALUES(234,234,234,234,234,234); INSERT INTO t10 VALUES(345,345,345,345,345,345); INSERT INTO t10 VALUES(45,45,45,45,45,45); } count { SELECT a FROM t10 WHERE b LIKE '12%' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.2 { count { SELECT a FROM t10 WHERE c LIKE '12%' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.3 { count { SELECT a FROM t10 WHERE d LIKE '12%' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.4 { count { SELECT a FROM t10 WHERE e LIKE '12%' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.5 { count { SELECT a FROM t10 WHERE f LIKE '12%' ORDER BY a; } } {12 123 scan 3 like 0} do_test like-10.6 { count { SELECT a FROM t10 WHERE a LIKE '12%' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.10 { execsql { CREATE TABLE t10b( a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c NUMBER UNIQUE, d BLOB UNIQUE, e UNIQUE, f TEXT UNIQUE ); INSERT INTO t10b SELECT * FROM t10; } count { SELECT a FROM t10b WHERE b GLOB '12*' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.11 { count { SELECT a FROM t10b WHERE c GLOB '12*' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.12 { count { SELECT a FROM t10b WHERE d GLOB '12*' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.13 { count { SELECT a FROM t10b WHERE e GLOB '12*' ORDER BY a; } } {12 123 scan 5 like 6} do_test like-10.14 { count { SELECT a FROM t10b WHERE f GLOB '12*' ORDER BY a; } } {12 123 scan 3 like 0} do_test like-10.15 { count { SELECT a FROM t10b WHERE a GLOB '12*' ORDER BY a; } } {12 123 scan 5 like 6} } # LIKE and GLOB where the default collating sequence is not appropriate # but an index with the appropriate collating sequence exists. # do_test like-11.0 { execsql { CREATE TABLE t11( |
︙ | ︙ |
Changes to test/lock.test.
︙ | ︙ | |||
394 395 396 397 398 399 400 | # retain a RESERVED or EXCLUSIVE lock after the transaction was committed: # # * The journal-mode is set to something other than 'delete', and # * there exists one or more active read-only statements, and # * a transaction that modified zero database pages is committed. # set temp_status unlocked | | | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | # retain a RESERVED or EXCLUSIVE lock after the transaction was committed: # # * The journal-mode is set to something other than 'delete', and # * there exists one or more active read-only statements, and # * a transaction that modified zero database pages is committed. # set temp_status unlocked if {$TEMP_STORE>=2} {set temp_status unknown} do_test lock-7.1 { set STMT [sqlite3_prepare $DB "SELECT * FROM sqlite_master" -1 TAIL] sqlite3_step $STMT } {SQLITE_ROW} do_test lock-7.2 { execsql { PRAGMA lock_status } } [list main shared temp $temp_status] |
︙ | ︙ |
Changes to test/lock6.test.
︙ | ︙ | |||
108 109 110 111 112 113 114 | set sqlite_hostid_num 3 do_test lock6-1.2 { execsql {pragma lock_status} } {main unlocked temp closed} sqlite3_soft_heap_limit 0 do_test lock6-1.3 { | > | < | | | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | set sqlite_hostid_num 3 do_test lock6-1.2 { execsql {pragma lock_status} } {main unlocked temp closed} sqlite3_soft_heap_limit 0 do_test lock6-1.3 { list [catch { sqlite3 db test.db execsql { select * from sqlite_master } } msg] $msg } {1 {database is locked}} do_test lock6-1.4 { set lockpath [execsql { PRAGMA lock_proxy_file=":auto:"; PRAGMA lock_proxy_file; } db] |
︙ | ︙ |
Changes to test/lock_common.tcl.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 | uplevel set $varname $tn uplevel $script code2 { db2 close } code3 { db3 close } catch { close $::code2_chan } catch { close $::code3_chan } } } # Launch another testfixture process to be controlled by this one. A # channel name is returned that may be passed as the first argument to proc # 'testfixture' to execute a command. The child testfixture process is shut # down by closing the channel. | > | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | uplevel set $varname $tn uplevel $script code2 { db2 close } code3 { db3 close } catch { close $::code2_chan } catch { close $::code3_chan } catch { db close } } } # Launch another testfixture process to be controlled by this one. A # channel name is returned that may be passed as the first argument to proc # 'testfixture' to execute a command. The child testfixture process is shut # down by closing the channel. |
︙ | ︙ |
Changes to test/malloc3.test.
︙ | ︙ | |||
651 652 653 654 655 656 657 | # Close and reopen the db. db close file delete -force test.db test.db-journal test2.db test2.db-journal sqlite3 db test.db sqlite3_extended_result_codes db 1 set ::DB [sqlite3_connection_pointer db] | | | 651 652 653 654 655 656 657 658 659 660 661 662 663 664 | # Close and reopen the db. db close file delete -force test.db test.db-journal test2.db test2.db-journal sqlite3 db test.db sqlite3_extended_result_codes db 1 set ::DB [sqlite3_connection_pointer db] # Turn off the Tcl interface's prepared statement caching facility in # the new connnection. Then run the tests with "transient" malloc failures. db cache size 0 run_test $::run_test_script 0 sqlite3_memdebug_fail -1 finish_test |
Changes to test/malloc_common.tcl.
︙ | ︙ | |||
109 110 111 112 113 114 115 116 117 118 119 120 121 122 | proc do_faultsim_test {name args} { global FAULTSIM set DEFAULT(-faults) [array names FAULTSIM] set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" array set O [array get DEFAULT] array set O $args foreach o [array names O] { if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" } } | > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | proc do_faultsim_test {name args} { global FAULTSIM set DEFAULT(-faults) [array names FAULTSIM] set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" fix_testname name array set O [array get DEFAULT] array set O $args foreach o [array names O] { if {[info exists DEFAULT($o)]==0} { error "unknown option: $o" } } |
︙ | ︙ | |||
139 140 141 142 143 144 145 | # faultsim_save # faultsim_restore # faultsim_save_and_close # faultsim_restore_and_reopen # faultsim_delete_and_reopen # proc faultsim_save {} { | | | | 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 | # faultsim_save # faultsim_restore # faultsim_save_and_close # faultsim_restore_and_reopen # faultsim_delete_and_reopen # proc faultsim_save {} { foreach f [glob -nocomplain sv_test.db*] { forcedelete $f } foreach f [glob -nocomplain test.db*] { set f2 "sv_$f" file copy -force $f $f2 } } proc faultsim_save_and_close {} { faultsim_save catch { db close } return "" } proc faultsim_restore {} { foreach f [glob -nocomplain test.db*] { forcedelete $f } foreach f2 [glob -nocomplain sv_test.db*] { set f [string range $f2 3 end] file copy -force $f2 $f } } proc faultsim_restore_and_reopen {{dbfile test.db}} { catch { db close } |
︙ | ︙ | |||
181 182 183 184 185 186 187 | } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting OOM faults into test cases. # proc oom_injectstart {nRepeat iFail} { | | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting OOM faults into test cases. # proc oom_injectstart {nRepeat iFail} { sqlite3_memdebug_fail [expr $iFail-1] -repeat $nRepeat } proc oom_injectstop {} { sqlite3_memdebug_fail -1 } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting IO error faults into test cases. |
︙ | ︙ | |||
418 419 420 421 422 423 424 | # Remove all traces of database files test.db and test2.db # from the file-system. Then open (empty database) "test.db" # with the handle [db]. # catch {db close} catch {db2 close} | | | | | | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 | # Remove all traces of database files test.db and test2.db # from the file-system. Then open (empty database) "test.db" # with the handle [db]. # catch {db close} catch {db2 close} forcedelete test.db forcedelete test.db-journal forcedelete test.db-wal forcedelete test2.db forcedelete test2.db-journal forcedelete test2.db-wal if {[info exists ::mallocopts(-testdb)]} { file copy $::mallocopts(-testdb) test.db } catch { sqlite3 db test.db } if {[info commands db] ne ""} { sqlite3_extended_result_codes db 1 } |
︙ | ︙ | |||
522 523 524 525 526 527 528 | # by parameter $result, or (b) TCL throws an "out of memory" error. # # If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement # is executed just once. In this case the test case passes if the results # match the expected results passed via parameter $result. # proc do_select_test {name sql result} { | | > > > > > > | 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 | # by parameter $result, or (b) TCL throws an "out of memory" error. # # If DO_MALLOC_TEST is defined and set to zero, then the SELECT statement # is executed just once. In this case the test case passes if the results # match the expected results passed via parameter $result. # proc do_select_test {name sql result} { uplevel [list doPassiveTest 0 $name $sql [list 0 [list {*}$result]]] } proc do_restart_select_test {name sql result} { uplevel [list doPassiveTest 1 $name $sql [list 0 $result]] } proc do_error_test {name sql error} { uplevel [list doPassiveTest 0 $name $sql [list 1 $error]] } proc doPassiveTest {isRestart name sql catchres} { if {![info exists ::DO_MALLOC_TEST]} { set ::DO_MALLOC_TEST 1 } if {[info exists ::testprefix] && [string is integer [string range $name 0 0]] } { set name $::testprefix.$name } switch $::DO_MALLOC_TEST { 0 { # No malloc failures. do_test $name [list set {} [uplevel [list catchsql $sql]]] $catchres return } 1 { # Simulate transient failures. |
︙ | ︙ |
Changes to test/memsubsys1.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 2008 June 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 contains tests of the memory allocation subsystem # | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 2008 June 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 contains tests of the memory allocation subsystem # set testdir [file dirname $argv0] source $testdir/tester.tcl sqlite3_reset_auto_extension # This test assumes that no page-cache or scratch buffers are installed # by default when a new database connection is opened. As a result, it |
︙ | ︙ | |||
95 96 97 98 99 100 101 | sqlite3_config_pagecache [expr 1024+$xtra_size] 20 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-2 {PRAGMA page_size=1024} #show_memstats do_test memsubsys1-2.3 { set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2] | < | < < < < | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | sqlite3_config_pagecache [expr 1024+$xtra_size] 20 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-2 {PRAGMA page_size=1024} #show_memstats do_test memsubsys1-2.3 { set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2] } [expr ($AUTOVACUUM+$TEMP_STORE>=2)*1024] do_test memsubsys1-2.4 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 20 do_test memsubsys1-2.5 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 0 # Test 3: Activate PAGECACHE with 20 pages but use the wrong page size # so that PAGECACHE is not used. # |
︙ | ︙ | |||
139 140 141 142 143 144 145 | build_test_db memsubsys1-3.2 {PRAGMA page_size=2048} #show_memstats do_test memsubsys1-3.2.3 { db eval {PRAGMA page_size} } 2048 do_test memsubsys1-3.2.4 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] | | > | < < < < < | | 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 | build_test_db memsubsys1-3.2 {PRAGMA page_size=2048} #show_memstats do_test memsubsys1-3.2.3 { db eval {PRAGMA page_size} } 2048 do_test memsubsys1-3.2.4 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 20 do_test memsubsys1-3.2.5 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 0 # Test 4: Activate both PAGECACHE and SCRATCH. # db close sqlite3_shutdown sqlite3_config_pagecache [expr 1024+$xtra_size] 50 sqlite3_config_scratch 6000 2 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-4 {PRAGMA page_size=1024} #show_memstats do_test memsubsys1-4.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] expr {$pg_used>=45 && $pg_used<=50} } 1 do_test memsubsys1-4.4 { set pg_ovfl [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_OVERFLOW 0] 2] } 0 do_test memsubsys1-4.5 { set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] expr {$maxreq<7000} } 1 do_test memsubsys1-4.6 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 1 |
︙ | ︙ | |||
186 187 188 189 190 191 192 | sqlite3_config_scratch 6000 2 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-5 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-5.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | sqlite3_config_scratch 6000 2 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-5 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-5.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 24 set msize [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] if {[lindex $msize 2]!=0} { do_test memsubsys1-5.4 { set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] expr {$maxreq>4096} } 1 do_test memsubsys1-5.5 { |
︙ | ︙ | |||
215 216 217 218 219 220 221 | sqlite3_config_scratch 25300 1 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-6 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-6.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] | | < < | | | | < | 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | sqlite3_config_scratch 25300 1 sqlite3_initialize reset_highwater_marks build_test_db memsubsys1-6 {PRAGMA page_size=4096} #show_memstats do_test memsubsys1-6.3 { set pg_used [lindex [sqlite3_status SQLITE_STATUS_PAGECACHE_USED 0] 2] } 24 #do_test memsubsys1-6.4 { # set maxreq [lindex [sqlite3_status SQLITE_STATUS_MALLOC_SIZE 0] 2] # expr {$maxreq>4096 && $maxreq<=(4096+$xtra_size)} #} 1 do_test memsubsys1-6.5 { set s_used [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_USED 0] 2] } 1 do_test memsubsys1-6.6 { set s_ovfl [lindex [sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 0] 2] } 0 |
︙ | ︙ |
Changes to test/misc4.test.
︙ | ︙ | |||
147 148 149 150 151 152 153 | insert into b values ('01',1); insert into b values ('01',2); insert into b values ('+1',3); insert into b values ('+1',4); select a.*, x.* from a, (select key,sum(period) from b group by key) as x | | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | insert into b values ('01',1); insert into b values ('01',2); insert into b values ('+1',3); insert into b values ('+1',4); select a.*, x.* from a, (select key,sum(period) from b group by key) as x where a.key=x.key order by 1 desc; } } {01 data01 01 3 +1 data+1 +1 7} # This test case tests the same property as misc4-4.1, but it is # a bit smaller which makes it easier to work with while debugging. do_test misc4-4.2 { execsql { |
︙ | ︙ |
Added test/multiplex.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 | # 2010 October 29 # # 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 source $testdir/malloc_common.tcl set g_chunk_size 2147483648 set g_max_chunks 32 # This handles appending the chunk number # to the end of the filename. if # SQLITE_MULTIPLEX_EXT_OVWR is defined, then # it overwrites the last 2 bytes of the # file name with the chunk number. proc multiplex_name {name chunk} { if {$chunk==0} { return $name } set num [format "%02d" $chunk] ifcapable {multiplex_ext_overwrite} { set name [string range $name 0 [expr [string length $name]-2-1]] } return $name$num } # This saves off the parameters and calls the # underlying sqlite3_multiplex_set() API. proc multiplex_set {chunk_size max_chunks} { global g_chunk_size global g_max_chunks set g_chunk_size $chunk_size set g_max_chunks $max_chunks sqlite3_multiplex_set $chunk_size $max_chunks } # This attempts to delete the base file and # and files with the chunk extension. proc multiplex_delete {name} { global g_max_chunks for {set i 0} {$i<$g_max_chunks} {incr i} { forcedelete [multiplex_name $name $i] forcedelete [multiplex_name $name-journal $i] forcedelete [multiplex_name $name-wal $i] } } db close #------------------------------------------------------------------------- # multiplex-1.1.*: Test initialize and shutdown. do_test multiplex-1.1 { sqlite3_multiplex_initialize nosuchvfs 1 } {SQLITE_ERROR} do_test multiplex-1.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.3 { sqlite3_multiplex_initialize "" 1 } {SQLITE_MISUSE} do_test multiplex-1.4 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 } {SQLITE_OK} do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.9 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.10.1 { multiplex_set 32768 16 } {SQLITE_OK} do_test multiplex-1.10.2 { multiplex_set 32768 -1 } {SQLITE_MISUSE} do_test multiplex-1.10.3 { multiplex_set -1 16 } {SQLITE_MISUSE} do_test multiplex-1.10.4 { multiplex_set 31 16 } {SQLITE_MISUSE} do_test multiplex-1.10.5 { multiplex_set 32768 100 } {SQLITE_MISUSE} do_test multiplex-1.11 { sqlite3_multiplex_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Some simple warm-body tests with a single database file in rollback # mode: # # multiplex-2.1.*: Test simple writing to a multiplex file. # # multiplex-2.2.*: More writing. # # multiplex-2.3.*: Open and close a second db. # # multiplex-2.4.*: Try to shutdown the multiplex system before closing the db # file. Check that this fails and the multiplex system still works # afterwards. Then close the database and successfully shut # down the multiplex system. # # multiplex-2.5.*: More reading/writing. # # multiplex-2.6.*: More reading/writing with varying small chunk sizes, as # well as varying journal mode. sqlite3_multiplex_initialize "" 1 multiplex_set 32768 16 do_test multiplex-2.1.2 { sqlite3 db test.db execsql { PRAGMA page_size=1024; PRAGMA auto_vacuum=OFF; PRAGMA journal_mode=DELETE; } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(1100)); INSERT INTO t1 VALUES(2, randomblob(1100)); } } {} do_test multiplex-2.1.3 { file size [multiplex_name test.db 0] } {4096} do_test multiplex-2.1.4 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.2.1 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} do_test multiplex-2.3.1 { sqlite3 db2 bak.db db2 close } {} do_test multiplex-2.4.1 { sqlite3_multiplex_shutdown } {SQLITE_MISUSE} do_test multiplex-2.4.2 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168} do_test multiplex-2.4.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-2.5.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 multiplex_set 4096 16 } {SQLITE_OK} do_test multiplex-2.5.2 { sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a PRIMARY KEY, b); } } {delete} do_test multiplex-2.5.3 { execsql { INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, randomblob(4000)); INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(4, randomblob(4000)); INSERT INTO t1 VALUES(5, 'five') } } {} do_test multiplex-2.5.4 { db eval {SELECT * FROM t1 WHERE a=1} } {1 one} do_test multiplex-2.5.5 { db eval {SELECT * FROM t1 WHERE a=3} } {3 three} do_test multiplex-2.5.6 { db eval {SELECT * FROM t1 WHERE a=5} } {5 five} do_test multiplex-2.5.7 { db eval {SELECT a,length(b) FROM t1 WHERE a=2} } {2 4000} do_test multiplex-2.5.8 { db eval {SELECT a,length(b) FROM t1 WHERE a=4} } {4 4000} do_test multiplex-2.5.9 { file size [multiplex_name test.db 0] } [list $g_chunk_size] do_test multiplex-2.5.10 { file size [multiplex_name test.db 1] } [list $g_chunk_size] do_test multiplex-2.5.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} set all_journal_modes {delete persist truncate memory off} foreach jmode $all_journal_modes { for {set sz 151} {$sz<8000} {set sz [expr $sz+419]} { do_test multiplex-2.6.1.$sz.$jmode { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 multiplex_set $sz 32 } {SQLITE_OK} do_test multiplex-2.6.2.$sz.$jmode { sqlite3 db test.db db eval { PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode do_test multiplex-2.6.3.$sz.$jmode { execsql { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, randomblob($g_chunk_size)); } } {} do_test multiplex-2.6.4.$sz.$jmode { db eval {SELECT b FROM t1 WHERE a=1} } {one} do_test multiplex-2.6.5.$sz.$jmode { db eval {SELECT length(b) FROM t1 WHERE a=2} } [list $g_chunk_size] do_test multiplex-2.6.6.$sz.$jmode { file size [multiplex_name test.db 0] } [list $g_chunk_size] do_test multiplex-2.6.99.$sz.$jmode { db close sqlite3_multiplex_shutdown } {SQLITE_OK} } } #------------------------------------------------------------------------- # Try some tests with more than one connection to a database file. Still # in rollback mode. # # multiplex-3.1.*: Two connections to a single database file. # # multiplex-3.2.*: Two connections to each of several database files (that # are in the same multiplex group). # do_test multiplex-3.1.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 multiplex_set 32768 16 } {SQLITE_OK} do_test multiplex-3.1.2 { sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); } file size [multiplex_name test.db 0] } {3072} do_test multiplex-3.1.3 { sqlite3 db2 test.db execsql { CREATE TABLE t2(a, b) } db2 } {} do_test multiplex-3.1.4 { execsql { CREATE TABLE t3(a, b) } } {} do_test multiplex-3.1.5 { catchsql { CREATE TABLE t3(a, b) } } {1 {table t3 already exists}} do_test multiplex-3.1.6 { db close db2 close } {} do_test multiplex-3.2.1a { multiplex_delete test.db multiplex_delete test2.db sqlite3 db1a test.db sqlite3 db2a test2.db foreach db {db1a db2a} { execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a, b); } $db } list [file size [multiplex_name test.db 0]] [file size [multiplex_name test2.db 0]] } {2048 2048} do_test multiplex-3.2.1b { sqlite3 db1b test.db sqlite3 db2b test2.db } {} do_test multiplex-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {} do_test multiplex-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {} do_test multiplex-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {} do_test multiplex-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {} do_test multiplex-3.2.6 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a } {} do_test multiplex-3.2.7 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b } {} do_test multiplex-3.2.8 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a } {} do_test multiplex-3.2.9 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b } {} do_test multiplex-3.3.1 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b } {} do_test multiplex-3.2.X { foreach db {db1a db2a db2b db1b} { catch { $db close } } } {} #------------------------------------------------------------------------- # sqlite3_multiplex_initialize "" 1 multiplex_set 32768 16 # Return a list of all currently defined multiplexs. proc multiplex_list {} { set allq {} foreach q [sqlite3_multiplex_dump] { lappend allq [lindex $q 0] } return [lsort $allq] } do_test multiplex-4.1.6 { multiplex_delete test2.db sqlite3 db test2.db db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');} set res [multiplex_list] list [regexp {test2.db} $res] } {1} do_test multiplex-4.1.6a { sqlite3 db2 test2.db db2 eval {SELECT * FROM t2} } {tab-t2} do_test multiplex-4.1.7 { execsql {INSERT INTO t2 VALUES(zeroblob(200000))} } {} do_test multiplex-4.1.8 { sqlite3 db2 test2.db db2 eval {SELECT count(*) FROM t2} } {2} do_test multiplex-4.1.8a { db2 eval { DELETE FROM t2 WHERE x = 'tab-t2' } } {} do_test multiplex-4.1.8b { sqlite3 db2 test2.db db2 eval {SELECT count(*) FROM t2} } {1} do_test multiplex-4.1.9 { execsql {INSERT INTO t2 VALUES(zeroblob(200000))} } {} do_test multiplex-4.1.10 { set res [multiplex_list] list [regexp {test2.db} $res] } {1} do_test multiplex-4.1.11 { db2 close set res [multiplex_list] list [regexp {test2.db} $res] } {1} do_test multiplex-4.1.12 { db close multiplex_list } {} #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # sqlite3_multiplex_initialize "" 1 multiplex_set 32768 16 do_faultsim_test multiplex-5.1 -prep { catch {db close} } -body { sqlite3 db test2.db } do_faultsim_test multiplex-5.2 -prep { catch {db close} } -body { sqlite3 db test.db } catch { db close } multiplex_delete test.db multiplex_delete test2.db do_test multiplex-5.3.prep { sqlite3 db test.db execsql { PRAGMA auto_vacuum = 1; PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(10, zeroblob(1200)); } faultsim_save_and_close } {} do_faultsim_test multiplex-5.3 -prep { faultsim_restore_and_reopen } -body { execsql { DELETE FROM t1 } } do_test multiplex-5.4.1 { catch { db close } multiplex_delete test.db file mkdir test.db list [catch { sqlite3 db test.db } msg] $msg } {1 {unable to open database file}} catch { file delete test.db } do_faultsim_test multiplex-5.5 -prep { catch { sqlite3_multiplex_shutdown } } -body { sqlite3_multiplex_initialize "" 1 multiplex_set 32768 16 } catch { sqlite3_multiplex_shutdown } finish_test |
Changes to test/pager1.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl source $testdir/wal_common.tcl # Do not use a codec for tests in this file, as the database file is # manipulated directly using tcl scripts (using the [hexio_write] command). # do_not_use_codec # # pager1-1.*: Test inter-process locking (clients in multiple processes). |
︙ | ︙ | |||
536 537 538 539 540 541 542 | 2 { # This test depends on the underlying VFS being able to open paths # 512 bytes in length. The idea is to create a hot-journal file that # contains a master-journal pointer so large that it could contain # a valid page record (if the file page-size is 512 bytes). So as to # make sure SQLite doesn't get confused by this. # | < < < < | < | 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 | 2 { # This test depends on the underlying VFS being able to open paths # 512 bytes in length. The idea is to create a hot-journal file that # contains a master-journal pointer so large that it could contain # a valid page record (if the file page-size is 512 bytes). So as to # make sure SQLite doesn't get confused by this. # set nPadding [expr 511 - $::mj_filename_length] if {$tcl_platform(platform)=="windows"} { # TBD need to figure out how to do this correctly for Windows!!! set nPadding [expr 255 - $::mj_filename_length] } # We cannot just create a really long database file name to open, as # Linux limits a single component of a path to 255 bytes by default # (and presumably other systems have limits too). So create a directory # hierarchy to work in. # set dirname "d123456789012345678901234567890/" set nDir [expr $nPadding / 32] if { $nDir } { set p [string repeat $dirname $nDir] file mkdir $p cd $p } set padding [string repeat x [expr $nPadding %32]] set prefix "test.db${padding}" } } { eval $tcl foreach {tn2 sql} { o { PRAGMA main.synchronous=OFF; PRAGMA aux.synchronous=OFF; |
︙ | ︙ | |||
1082 1083 1084 1085 1086 1087 1088 | # $sql: SQL to execute. # $res: Expected result of executing $sql. # $js: The expected size of the journal file, in bytes, after executing # the SQL script. Or -1 if the journal is not expected to exist. # $ws: The expected size of the WAL file, in bytes, after executing # the SQL script. Or -1 if the WAL is not expected to exist. # | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | 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 | # $sql: SQL to execute. # $res: Expected result of executing $sql. # $js: The expected size of the journal file, in bytes, after executing # the SQL script. Or -1 if the journal is not expected to exist. # $ws: The expected size of the WAL file, in bytes, after executing # the SQL script. Or -1 if the WAL is not expected to exist. # ifcapable wal { faultsim_delete_and_reopen foreach {tn sql res js ws} [subst { 1 { CREATE TABLE t1(a, b); PRAGMA auto_vacuum=OFF; PRAGMA synchronous=NORMAL; PRAGMA page_size=1024; PRAGMA locking_mode=EXCLUSIVE; PRAGMA journal_mode=TRUNCATE; INSERT INTO t1 VALUES(1, 2); } {exclusive truncate} 0 -1 2 { BEGIN IMMEDIATE; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 3 { BEGIN; SELECT * FROM t1; COMMIT; } {1 2} 0 -1 4 { PRAGMA journal_mode = WAL } wal -1 -1 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024] 6 { PRAGMA locking_mode = NORMAL } exclusive -1 [wal_file_size 1 1024] 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024] 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1 }] { do_execsql_test pager1-7.1.$tn.1 $sql $res catch { set J -1 ; set J [file size test.db-journal] } catch { set W -1 ; set W [file size test.db-wal] } do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws] } } do_test pager1-7.2.1 { faultsim_delete_and_reopen execsql { PRAGMA locking_mode = EXCLUSIVE; |
︙ | ︙ | |||
1298 1299 1300 1301 1302 1303 1304 | PRAGMA page_size = 4096; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); } db2 sqlite3_backup B db2 main db main list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} | < < < < < | | | < | 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 | PRAGMA page_size = 4096; CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); } db2 sqlite3_backup B db2 main db main list [B step 10000] [B finish] } {SQLITE_DONE SQLITE_OK} do_test pager1-9.4.2 { list [file size test.db2] [file size test.db] } {0 0} db2 close #------------------------------------------------------------------------- # Test that regardless of the value returned by xSectorSize(), the # minimum effective sector-size is 512 and the maximum 65536 bytes. # testvfs tv -default 1 |
︙ | ︙ | |||
1929 1930 1931 1932 1933 1934 1935 | do_test pager1-20.2.2 { execsql { BEGIN EXCLUSIVE; COMMIT; } } {} | | | 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 | do_test pager1-20.2.2 { execsql { BEGIN EXCLUSIVE; COMMIT; } } {} ifcapable wal { do_test pager1-20.3.1 { faultsim_delete_and_reopen db func a_string a_string execsql { PRAGMA cache_size = 10; PRAGMA journal_mode = wal; BEGIN; |
︙ | ︙ | |||
1964 1965 1966 1967 1968 1969 1970 | #------------------------------------------------------------------------- # Test that a WAL database may not be opened if: # # pager1-21.1.*: The VFS has an iVersion less than 2, or # pager1-21.2.*: The VFS does not provide xShmXXX() methods. # | | | 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 | #------------------------------------------------------------------------- # Test that a WAL database may not be opened if: # # pager1-21.1.*: The VFS has an iVersion less than 2, or # pager1-21.2.*: The VFS does not provide xShmXXX() methods. # ifcapable wal { do_test pager1-21.0 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def'); INSERT INTO ko DEFAULT VALUES; } |
︙ | ︙ | |||
2268 2269 2270 2271 2272 2273 2274 | #------------------------------------------------------------------------- # Test that attempting to open a write-transaction with # locking_mode=exclusive in WAL mode fails if there are other clients on # the same database. # catch { db close } | | | 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 | #------------------------------------------------------------------------- # Test that attempting to open a write-transaction with # locking_mode=exclusive in WAL mode fails if there are other clients on # the same database. # catch { db close } ifcapable wal { do_multiclient_test tn { do_test pager1-28.$tn.1 { sql1 { PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('a', 'b'); } |
︙ | ︙ | |||
2362 2363 2364 2365 2366 2367 2368 2369 | do_test pager1-29.2 { execsql { PRAGMA page_size = 4096; VACUUM; } file size test.db } [expr 4096*3] | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 | do_test pager1-29.2 { execsql { PRAGMA page_size = 4096; VACUUM; } file size test.db } [expr 4096*3] #------------------------------------------------------------------------- # Test that if an empty database file (size 0 bytes) is opened in # exclusive-locking mode, any journal file is deleted from the file-system # without being rolled back. And that the RESERVED lock obtained while # doing this is not released. # do_test pager1-30.1 { db close file delete test.db file delete test.db-journal set fd [open test.db-journal w] seek $fd [expr 512+1032*2] puts -nonewline $fd x close $fd sqlite3 db test.db execsql { PRAGMA locking_mode=EXCLUSIVE; SELECT count(*) FROM sqlite_master; PRAGMA lock_status; } } {exclusive 0 main reserved temp closed} #------------------------------------------------------------------------- # Test that if the "page-size" field in a journal-header is 0, the journal # file can still be rolled back. This is required for backward compatibility - # versions of SQLite prior to 3.5.8 always set this field to zero. # do_test pager1-31.1 { faultsim_delete_and_reopen execsql { PRAGMA cache_size = 10; PRAGMA page_size = 1024; CREATE TABLE t1(x, y, UNIQUE(x, y)); INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500)); INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; INSERT INTO t1 SELECT randomblob(1500), randomblob(1500) FROM t1; BEGIN; UPDATE t1 SET y = randomblob(1499); } file copy test.db test.db2 file copy test.db-journal test.db2-journal hexio_write test.db2-journal 24 00000000 sqlite3 db2 test.db2 execsql { PRAGMA integrity_check } db2 } {ok} finish_test |
Changes to test/pagerfault.test.
︙ | ︙ | |||
669 670 671 672 673 674 675 | do_faultsim_test pagerfault-14a -prep { faultsim_restore_and_reopen } -body { if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } } -test { faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error or missing database}} } | > > > > > > > | | | | | | | | | | | | | | | > > | | 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 | do_faultsim_test pagerfault-14a -prep { faultsim_restore_and_reopen } -body { if {[catch {db backup test.db2} msg]} { error [regsub {.*: } $msg {}] } } -test { faultsim_test_result {0 {}} {1 {}} {1 {SQL logic error or missing database}} } # If TEMP_STORE is 2 or greater, then the database [db2] will be created # as an in-memory database. This test will not work in that case, as it # is not possible to change the page-size of an in-memory database. Even # using the backup API. # if {$TEMP_STORE<2} { do_faultsim_test pagerfault-14b -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 "" db2 eval { PRAGMA page_size = 4096; CREATE TABLE xx(a) } } -body { sqlite3_backup B db2 main db main B step 200 set rc [B finish] if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } set {} {} } -test { faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}} } } do_faultsim_test pagerfault-14c -prep { catch { db2 close } faultsim_restore_and_reopen sqlite3 db2 test.db2 db2 eval { PRAGMA synchronous = off; PRAGMA page_size = 4096; CREATE TABLE xx(a); } } -body { sqlite3_backup B db2 main db main B step 200 set rc [B finish] if {[string match SQLITE_IOERR_* $rc]} {set rc SQLITE_IOERR} if {$rc != "SQLITE_OK"} { error [sqlite3_test_errstr $rc] } set {} {} } -test { faultsim_test_result {0 {}} {1 {sqlite3_backup_init() failed}} } do_test pagerfault-15-pre1 { faultsim_delete_and_reopen db func a_string a_string; execsql { BEGIN; |
︙ | ︙ | |||
1120 1121 1122 1123 1124 1125 1126 | # # PagerCommitPhaseOne(<in-memory-db>) -> SQLITE_OK # PagerCommitPhaseOne(<file-db>) -> SQLITE_IOERR # PagerRollback(<in-memory-db>) # PagerRollback(<file-db>) # do_faultsim_test pagerfault-23 -prep { | < > | 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 | # # PagerCommitPhaseOne(<in-memory-db>) -> SQLITE_OK # PagerCommitPhaseOne(<file-db>) -> SQLITE_IOERR # PagerRollback(<in-memory-db>) # PagerRollback(<file-db>) # do_faultsim_test pagerfault-23 -prep { sqlite3 db :memory: foreach f [glob -nocomplain test.db*] { file delete -force $f } db eval { ATTACH 'test.db2' AS aux; CREATE TABLE t1(a, b); CREATE TABLE aux.t2(a, b); } } -body { execsql { |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
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 | error "Unknown switch: $a" } } } elseif {$isExclude == 0} { foreach f $a { set t($f) 1 } } else { foreach f $a { array unset t $f } } } return [array names t] } #------------------------------------------------------------------------- # Set up the following global list variables containing the names of # various test scripts: # # $alltests # $allquicktests # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } if {$::tcl_platform(platform)!="unix"} { set alltests [test_set $alltests -exclude crash.test crash2.test] } set alltests [test_set $alltests -exclude { all.test async.test quick.test veryquick.test memleak.test permutations.test soak.test fts3.test | > > > > > | | | 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 | error "Unknown switch: $a" } } } elseif {$isExclude == 0} { foreach f $a { set t($f) 1 } } else { foreach f $a { array unset t $f } foreach f $a { array unset t */$f } } } return [array names t] } #------------------------------------------------------------------------- # Set up the following global list variables containing the names of # various test scripts: # # $alltests # $allquicktests # set alltests [list] foreach f [glob $testdir/*.test] { lappend alltests [file tail $f] } foreach f [glob -nocomplain $testdir/../ext/rtree/*.test] { lappend alltests $f } if {$::tcl_platform(platform)!="unix"} { set alltests [test_set $alltests -exclude crash.test crash2.test] } set alltests [test_set $alltests -exclude { all.test async.test quick.test veryquick.test memleak.test permutations.test soak.test fts3.test mallocAll.test rtree.test }] set allquicktests [test_set $alltests -exclude { async2.test async3.test backup_ioerr.test corrupt.test corruptC.test crash.test crash2.test crash3.test crash4.test crash5.test crash6.test crash7.test delete3.test e_fts3.test fts3rnd.test fkey_malloc.test fuzz.test fuzz3.test fuzz_malloc.test in2.test loadext.test misc7.test mutex2.test notify2.test onefile.test pagerfault2.test savepoint4.test savepoint6.test select9.test speed1.test speed1p.test speed2.test speed3.test speed4.test speed4p.test sqllimits1.test tkt2686.test thread001.test thread002.test thread003.test thread004.test thread005.test trans2.test vacuum3.test incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test vtab_err.test walslow.test walcrash.test walthread.test rtree3.test }] if {[info exists ::env(QUICKTEST_INCLUDE)]} { set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] } ############################################################################# # Start of tests |
︙ | ︙ | |||
150 151 152 153 154 155 156 | All multi-threaded tests. } -files { notify2.test thread001.test thread002.test thread003.test thread004.test thread005.test walthread.test } test_suite "fts3" -prefix "" -description { | | | | > > | | 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 | All multi-threaded tests. } -files { notify2.test thread001.test thread002.test thread003.test thread004.test thread005.test walthread.test } test_suite "fts3" -prefix "" -description { All FTS3 tests except fts3rnd.test. } -files { fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3atoken.test fts3b.test fts3c.test fts3cov.test fts3d.test fts3defer.test fts3defer2.test fts3e.test fts3expr.test fts3expr2.test fts3near.test fts3query.test fts3shared.test fts3snippet.test fts3fault.test fts3malloc.test fts3matchinfo.test } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the coverage related test suites: # # coverage-wal # test_suite "coverage-wal" -description { Coverage tests for file wal.c. } -files { wal.test wal2.test wal3.test walmode.test walbak.test walhook.test walcrash2.test walcksum.test walfault.test walbig.test walnoshm.test } test_suite "coverage-pager" -description { Coverage tests for file pager.c. } -files { pager1.test pager2.test pagerfault.test pagerfault2.test walfault.test walbak.test journal2.test tkt-9d68c883.test |
︙ | ︙ | |||
578 579 580 581 582 583 584 585 586 587 588 589 590 591 | test_suite "memsys5-2" -description { Run tests using the allocator in mem5.c in a different configuration. } -files { select1.test } -initialize { catch {db close} sqlite3_shutdown sqlite3_config_heap 40000000 16 sqlite3_config_lookaside 0 0 install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} | > | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 | test_suite "memsys5-2" -description { Run tests using the allocator in mem5.c in a different configuration. } -files { select1.test } -initialize { catch {db close} sqlite3_shutdown sqlite3_config_memstatus 0 sqlite3_config_heap 40000000 16 sqlite3_config_lookaside 0 0 install_malloc_faultsim 1 sqlite3_initialize autoinstall_test_functions } -shutdown { catch {db close} |
︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 | fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3b.test fts3c.test fts3d.test fts3e.test fts3query.test } # End of tests ############################################################################# # run_tests NAME OPTIONS # # where available options are: # | > > > > > | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 | fts3aa.test fts3ab.test fts3ac.test fts3ad.test fts3ae.test fts3af.test fts3ag.test fts3ah.test fts3ai.test fts3aj.test fts3ak.test fts3al.test fts3am.test fts3an.test fts3ao.test fts3b.test fts3c.test fts3d.test fts3e.test fts3query.test } test_suite "rtree" -description { All R-tree related tests. Provides coverage of source file rtree.c. } -files [glob -nocomplain $::testdir/../ext/rtree/*.test] # End of tests ############################################################################# # run_tests NAME OPTIONS # # where available options are: # |
︙ | ︙ | |||
755 756 757 758 759 760 761 | set ::G(perm:prefix) $options(-prefix) set ::G(perm:presql) $options(-presql) set ::G(isquick) 1 uplevel $options(-initialize) foreach file [lsort $options(-files)] { | > | | 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | set ::G(perm:prefix) $options(-prefix) set ::G(perm:presql) $options(-presql) set ::G(isquick) 1 uplevel $options(-initialize) foreach file [lsort $options(-files)] { if {[file tail $file] == $file} { set file [file join $::testdir $file] } slave_test_file $file } uplevel $options(-shutdown) unset ::G(perm:name) unset ::G(perm:prefix) unset ::G(perm:presql) |
︙ | ︙ |
Changes to test/pragma.test.
︙ | ︙ | |||
1289 1290 1291 1292 1293 1294 1295 | # Reset the sqlite3_temp_directory variable for the next run of tests: sqlite3 dbX :memory: dbX eval {PRAGMA temp_store_directory = ""} dbX close set skip_lock_proxy_tests [path_is_dos "."] | | | 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 | # Reset the sqlite3_temp_directory variable for the next run of tests: sqlite3 dbX :memory: dbX eval {PRAGMA temp_store_directory = ""} dbX close set skip_lock_proxy_tests [path_is_dos "."] ifcapable !(lock_proxy_pragmas&&prefer_proxy_locking) { set skip_lock_proxy_tests 1 } if !$skip_lock_proxy_tests { set sqlite_hostid_num 1 set using_proxy 0 |
︙ | ︙ | |||
1381 1382 1383 1384 1385 1386 1387 | } db2] string match "*test2.db:auto:" $lockpath } {1} set sqlite_hostid_num 2 # db access should be limited to one host at a time (simulate 2nd host id) do_test pragma-16.7 { | > | | | < < | | > > | < | | | 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 | } db2] string match "*test2.db:auto:" $lockpath } {1} set sqlite_hostid_num 2 # db access should be limited to one host at a time (simulate 2nd host id) do_test pragma-16.7 { list [catch { sqlite3 db test2.db execsql { PRAGMA lock_proxy_file=":auto:"; select * from sqlite_master; } } msg] $msg } {1 {database is locked}} db close # default to using proxy locking (simulate network file system detection) do_test pragma-16.8 { list [catch { sqlite3 db test2.db execsql { select * from sqlite_master } } msg] $msg } {1 {database is locked}} db2 close set lpp4 [exec mktemp -t 'proxy4'] # check that db is unlocked after first host connection closes do_test pragma-16.8.1 { |
︙ | ︙ |
Added test/quota.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 | # 2010 September 1 # # 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 source $testdir/malloc_common.tcl db close do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR} do_test quota-1.2 { sqlite3_quota_initialize "" 1 } {SQLITE_OK} do_test quota-1.3 { sqlite3_quota_initialize "" 1 } {SQLITE_MISUSE} do_test quota-1.4 { sqlite3_quota_shutdown } {SQLITE_OK} do_test quota-1.5 { sqlite3_quota_initialize "" 0 } {SQLITE_OK} do_test quota-1.6 { sqlite3_quota_shutdown } {SQLITE_OK} do_test quota-1.7 { sqlite3_quota_initialize "" 1 } {SQLITE_OK} do_test quota-1.8 { sqlite3_quota_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Some simple warm-body tests with a single database file in rollback # mode: # # quota-2.1.*: Test that SQLITE_FULL is returned if the database would # exceed the configured quota. # # quota-2.2.*: Test that SQLITE_FULL is not returned and the database # grows if the callback extends the quota when the database # attempts to grow beyond the configured quota. # # quota-2.3.*: Open and close a db that is not part of any quota group. At # one point this was causing mutex refs to be leaked. # # quota-2.4.*: Try to shutdown the quota system before closing the db # file. Check that this fails and the quota system still works # afterwards. Then close the database and successfully shut # down the quota system. # sqlite3_quota_initialize "" 1 proc quota_check {filename limitvar size} { upvar $limitvar limit lappend ::quota [set limit] $size if {[info exists ::quota_request_ok]} { set limit $size } } do_test quota-2.1.1 { sqlite3_quota_set *test.db 4096 quota_check } {SQLITE_OK} do_test quota-2.1.2 { sqlite3 db test.db execsql { PRAGMA page_size=1024; PRAGMA auto_vacuum=OFF; PRAGMA journal_mode=DELETE; } set ::quota [list] execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(1100)); INSERT INTO t1 VALUES(2, randomblob(1100)); } set ::quota } {} do_test quota-2.1.3 { file size test.db } {4096} do_test quota-2.1.4 { catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {1 {database or disk is full}} do_test quota-2.1.5 { set ::quota } {4096 5120} set ::quota_request_ok 1 set ::quota [list] do_test quota-2.2.1 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test quota-2.2.2 { set ::quota } {4096 5120} do_test quota-2.2.3 { file size test.db } {5120} unset ::quota_request_ok do_test quota-2.3.1 { sqlite3 db2 bak.db db2 close } {} do_test quota-2.4.1 { sqlite3_quota_shutdown } {SQLITE_MISUSE} set ::quota [list] do_test quota-2.4.2 { catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {1 {database or disk is full}} do_test quota-2.4.3 { set ::quota } {5120 6144} do_test quota-2.4.4 { file size test.db } {5120} do_test quota-2.4.99 { db close sqlite3_quota_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Try some tests with more than one connection to a database file. Still # in rollback mode. # # quota-3.1.*: Two connections to a single database file. # # quota-3.2.*: Two connections to each of several database files (that # are in the same quota group). # proc quota_check {filename limitvar size} { upvar $limitvar limit lappend ::quota [set limit] $size if {[info exists ::quota_request_ok]} { set limit $size } } do_test quota-3.1.1 { file delete -force test.db sqlite3_quota_initialize "" 1 sqlite3_quota_set *test.db 4096 quota_check } {SQLITE_OK} do_test quota-3.1.2 { sqlite3 db test.db execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); } file size test.db } {3072} do_test quota-3.1.3 { sqlite3 db2 test.db set ::quota [list] execsql { CREATE TABLE t2(a, b) } db2 set ::quota } {} do_test quota-3.1.4 { catchsql { CREATE TABLE t3(a, b) } } {1 {database or disk is full}} do_test quota-3.1.5 { set ::quota_request_ok 1 execsql { CREATE TABLE t3(a, b) } } {} do_test quota-3.1.6 { db close db2 close sqlite3_quota_set *test.db 0 {} } {SQLITE_OK} do_test quota-3.2.1 { file delete force test.db test2.db sqlite3_quota_set * 4096 {} sqlite3 db1a test.db sqlite3 db2a test2.db foreach db {db1a db2a} { execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a, b); } $db } sqlite3 db1b test.db sqlite3 db2b test2.db list [file size test.db] [file size test2.db] } {2048 2048} catch { unset ::quota_request_ok } do_test quota-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {} do_test quota-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {} do_test quota-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {} do_test quota-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {} do_test quota-3.2.6 { catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a } {1 {database or disk is full}} do_test quota-3.2.7 { catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b } {1 {database or disk is full}} do_test quota-3.2.8 { catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a } {1 {database or disk is full}} do_test quota-3.2.9 { catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b } {1 {database or disk is full}} set ::quota [list] proc quota_callback {file limitvar size} { upvar $limitvar limit lappend ::quota $file $size set limit 0 } sqlite3_quota_set * 4096 quota_callback do_test quota-3.3.1 { execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b set ::quota } [list [file join [pwd] test.db] 5120] do_test quota-3.2.X { foreach db {db1a db2a db2b db1b} { catch { $db close } } sqlite3_quota_set * 0 {} } {SQLITE_OK} #------------------------------------------------------------------------- # Quotas are deleted when unused and when there limit is set to zero # # Return a list of all currently defined quotas. Each quota is identified # by its pattern. proc quota_list {} { set allq {} foreach q [sqlite3_quota_dump] { lappend allq [lindex $q 0] } return [lsort $allq] } do_test quota-4.1.1 { sqlite3_quota_set *test.db 0 {} quota_list } {} do_test quota-4.1.2 { sqlite3_quota_set *test.db 4096 {} quota_list } {*test.db} do_test quota-4.1.3 { sqlite3_quota_set *test2.db 0 {} quota_list } {*test.db} do_test quota-4.1.4 { sqlite3_quota_set *test2.db 100000 {} quota_list } {*test.db *test2.db} do_test quota-4.1.5 { sqlite3_quota_set *test.db 0 {} quota_list } {*test2.db} do_test quota-4.1.6 { file delete -force test2.db test2.db-journal test2.db-wal sqlite3 db test2.db db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');} quota_list } {*test2.db} do_test quota-4.1.7 { catchsql {INSERT INTO t2 VALUES(zeroblob(200000))} } {1 {database or disk is full}} do_test quota-4.1.8 { sqlite3 db2 test2.db db2 eval {SELECT * FROM t2} } {tab-t2} do_test quota-4.1.9 { sqlite3_quota_set *test2.db 0 {} catchsql {INSERT INTO t2 VALUES(zeroblob(200000))} } {0 {}} do_test quota-4.1.10 { quota_list } {*test2.db} do_test quota-4.1.11 { db2 close quota_list } {*test2.db} do_test quota-4.1.12 { db close quota_list } {} do_test quota-4.2.1 { sqlite3_quota_set A 1000 {} sqlite3_quota_set B 1000 {} sqlite3_quota_set C 1000 {} sqlite3_quota_set D 1000 {} quota_list } {A B C D} do_test quota-4.2.2 { sqlite3_quota_set C 0 {} sqlite3_quota_set B 0 {} quota_list } {A D} do_test quota-4.2.3 { sqlite3_quota_set A 0 {} sqlite3_quota_set D 0 {} quota_list } {} do_test quota-4.2.4 { sqlite3_quota_set A 1000 {} sqlite3_quota_set B 1000 {} sqlite3_quota_set C 1000 {} sqlite3_quota_set A 0 {} sqlite3_quota_set B 0 {} sqlite3_quota_set C 0 {} quota_list } {} do_test quota-4.2.5 { sqlite3_quota_set A 1000 {} sqlite3_quota_set B 1000 {} sqlite3_quota_set C 1000 {} sqlite3_quota_set C 0 {} sqlite3_quota_set B 0 {} sqlite3_quota_set A 0 {} quota_list } {} do_test quota-4.3.1 { sqlite3_quota_set A 1000 quota_callback sqlite3 db A sqlite3_quota_set A 0 quota_callback db close quota_list } {} do_test quota-4.4.1 { sqlite3_quota_set A 1000 quota_callback sqlite3_quota_shutdown } {SQLITE_OK} do_test quota-4.4.2 { quota_list } {} #------------------------------------------------------------------------- # The following tests test that the quota VFS handles malloc and IO # errors. # sqlite3_quota_initialize "" 1 sqlite3_quota_set *test.db 4096 {} do_faultsim_test quota-5.1 -prep { catch {db close} } -body { sqlite3 db test2.db } do_faultsim_test quota-5.2 -prep { catch {db close} } -body { sqlite3 db test.db } catch { db close } file delete -force test.db do_test quota-5.3.prep { sqlite3 db test.db execsql { PRAGMA auto_vacuum = 1; PRAGMA page_size = 1024; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(10, zeroblob(1200)); } faultsim_save_and_close } {} do_faultsim_test quota-5.3 -prep { faultsim_restore_and_reopen } -body { execsql { DELETE FROM t1 } } do_test quota-5.4.1 { catch { db close } file delete -force test.db file mkdir test.db list [catch { sqlite3 db test.db } msg] $msg } {1 {unable to open database file}} do_faultsim_test quota-5.5 -prep { catch { sqlite3_quota_shutdown } } -body { sqlite3_quota_initialize "" 1 } do_faultsim_test quota-5.6 -prep { catch { sqlite3_quota_shutdown } sqlite3_quota_initialize "" 1 } -body { sqlite3_quota_set * 4096 {} } catch { sqlite3_quota_shutdown } finish_test |
Added test/releasetest.mk.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | ######################################################## TOP=/home/drh/sqlite/sqlite TCL_FLAGS=-I/home/drh/tcltk/86linux LIBTCL=/home/drh/tcltk/86linux/libtcl8.6.a -lm -ldl -lpthread BCC = gcc TCC = gcc -ansi -g $(CFLAGS) NAWK = awk AR = ar cr RANLIB = ranlib THREADLIB = -lpthread -ldl -lz include $(TOP)/main.mk ######################################################## |
Added test/releasetest.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 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 | set rcsid {$Id: $} # Documentation for this script. This may be output to stderr # if the script is invoked incorrectly. See the [process_options] # proc below. # set ::USAGE_MESSAGE { This Tcl script is used to test the various configurations required before releasing a new version. Supported command line options (all optional) are: -makefile PATH-TO-MAKEFILE (default "releasetest.mk") -platform PLATFORM (see below) -quick BOOLEAN (default "0") The default value for -makefile is "./releasetest.mk". The script determines the default value for -platform using the $tcl_platform(os) and $tcl_platform(machine) variables. Supported platforms are "Linux-x86", "Linux-x86_64" and "Darwin-i386". If the -quick option is set to true, then the "veryquick.test" script is run for all compilation configurations. Otherwise, sometimes "all.test" is run, sometimes "veryquick.test". Almost any SQLite makefile (except those generated by configure - see below) should work. The following properties are required: * The makefile should support the "fulltest" target. * The makefile should support the variable "OPTS" as a way to pass options from the make command line to lemon and the C compiler. More precisely, the following invocation must be supported: make -f $::MAKEFILE fulltest OPTS="-DSQLITE_SECURE_DELETE=1 -DSQLITE_DEBUG=1" Makefiles generated by the sqlite configure program cannot be used as they do not respect the OPTS variable. Example Makefile contents: ######################################################## TOP=/home/dan/work/sqlite/sqlite TCL_FLAGS=-I/home/dan/tcl/include LIBTCL=-L/home/dan/tcl/lib -ltcl BCC = gcc TCC = gcc -ansi -g $(CFLAGS) NAWK = awk AR = ar cr RANLIB = ranlib THREADLIB = -lpthread -ldl include $(TOP)/main.mk ######################################################## } array set ::Configs { "Default" { -O2 } "Unlock-Notify" { -O2 -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_THREADSAFE -DOS_UNIX -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 } "Secure-Delete" { -O2 -DSQLITE_SECURE_DELETE=1 -DSQLITE_SOUNDEX=1 } "Update-Delete-Limit" { -O2 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 } "Debug-One" { -O2 -DSQLITE_DEBUG=1 -DSQLITE_MEMDEBUG=1 -DSQLITE_MUTEX_NOOP=1 -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_MEMSYS5=1 -DSQLITE_ENABLE_MEMSYS3=1 -DSQLITE_ENABLE_COLUMN_METADATA=1 } "Device-One" { -O2 -DSQLITE_DEBUG=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_DEFAULT_CACHE_SIZE=64 -DSQLITE_DEFAULT_PAGE_SIZE=1024 -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32 -DSQLITE_DISABLE_LFS=1 -DSQLITE_ENABLE_ATOMIC_WRITE=1 -DSQLITE_ENABLE_IOTRACE=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_MAX_PAGE_SIZE=4096 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_OMIT_VIRTUALTABLE=1 -DSQLITE_TEMP_STORE=3 } "Device-Two" { -DSQLITE_4_BYTE_ALIGNED_MALLOC=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_DEFAULT_CACHE_SIZE=1000 -DSQLITE_DEFAULT_LOCKING_MODE=0 -DSQLITE_DEFAULT_PAGE_SIZE=1024 -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000 -DSQLITE_DISABLE_LFS=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_MAX_COMPOUND_SELECT=50 -DSQLITE_MAX_PAGE_SIZE=32768 -DSQLITE_OMIT_TRACE=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_THREADSAFE=2 } "Locking-Style" { -O2 -DSQLITE_ENABLE_LOCKING_STYLE=1 } "OS-X" { -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_THREADSAFE=2 -DSQLITE_OS_UNIX=1 -DSQLITE_ENABLE_LOCKING_STYLE=1 -DUSE_PREAD=1 -DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 -DSQLITE_DEFAULT_CACHE_SIZE=1000 -DSQLITE_MAX_LENGTH=2147483645 -DSQLITE_MAX_VARIABLE_NUMBER=500000 -DSQLITE_DEBUG=1 -DSQLITE_PREFER_PROXY_LOCKING=1 } "Extra-Robustness" { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 } } array set ::Platforms { Linux-x86_64 { "Secure-Delete" test "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test "Debug-One" test "Extra-Robustness" test "Device-Two" test "Default" "threadtest test" "Device-One" fulltest } Linux-i686 { "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Device-One" test "Device-Two" test "Default" "threadtest fulltest" } Darwin-i386 { "Locking-Style" test "OS-X" "threadtest fulltest" } } # End of configuration section. ######################################################################### ######################################################################### foreach {key value} [array get ::Platforms] { foreach {v t} $value { if {0==[info exists ::Configs($v)]} { puts stderr "No such configuration: \"$v\"" exit -1 } } } proc run_test_suite {name testtarget config} { # Tcl variable $opts is used to build up the value used to set the # OPTS Makefile variable. Variable $cflags holds the value for # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but # CFLAGS is only passed to gcc. # set cflags "" set opts "" foreach arg $config { if {[string match -D* $arg]} { lappend opts $arg } else { lappend cflags $arg } } set cflags [join $cflags " "] set opts [join $opts " "] append opts " -DSQLITE_NO_SYNC=1 -DHAVE_USLEEP" # Set the sub-directory to use. # set dir [string tolower [string map {- _ " " _} $name]] if {$::tcl_platform(platform)=="windows"} { append opts " -DSQLITE_OS_WIN=1" } elseif {$::tcl_platform(platform)=="os2"} { append opts " -DSQLITE_OS_OS2=1" } else { append opts " -DSQLITE_OS_UNIX=1" } # Run the test. # set makefile [file normalize $::MAKEFILE] file mkdir $dir puts -nonewline "Testing configuration \"$name\" (logfile=$dir/test.log)..." flush stdout set makecmd [concat \ [list exec make -C $dir -f $makefile clean] \ $testtarget \ [list CFLAGS=$cflags OPTS=$opts >& $dir/test.log] \ ] set tm1 [clock seconds] set rc [catch $makecmd] set tm2 [clock seconds] set minutes [expr {($tm2-$tm1)/60}] set seconds [expr {($tm2-$tm1)%60}] puts -nonewline [format " (%d:%.2d) " $minutes $seconds] if {$rc} { puts "FAILED." } else { puts "Ok." } } # This proc processes the command line options passed to this script. # Currently the only option supported is "-makefile", default # "releasetest.mk". Set the ::MAKEFILE variable to the value of this # option. # proc process_options {argv} { set ::MAKEFILE releasetest.mk ;# Default value set ::QUICK 0 ;# Default value set platform $::tcl_platform(os)-$::tcl_platform(machine) for {set i 0} {$i < [llength $argv]} {incr i} { switch -- [lindex $argv $i] { -makefile { incr i set ::MAKEFILE [lindex $argv $i] } -platform { incr i set platform [lindex $argv $i] } -quick { incr i set ::QUICK [lindex $argv $i] } default { puts stderr "" puts stderr [string trim $::USAGE_MESSAGE] exit -1 } } } set ::MAKEFILE [file normalize $::MAKEFILE] 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 } set ::CONFIGLIST $::Platforms($platform) puts "Running the following configurations for $platform:" puts " [string trim $::CONFIGLIST]" } # Main routine. # proc main {argv} { # Process any command line options. process_options $argv foreach {zConfig target} $::CONFIGLIST { if {$::QUICK} {set target test} set config_options $::Configs($zConfig) run_test_suite $zConfig $target $config_options # If the configuration included the SQLITE_DEBUG option, then remove # it and run veryquick.test. If it did not include the SQLITE_DEBUG option # add it and run veryquick.test. set debug_idx [lsearch -glob $config_options -DSQLITE_DEBUG*] if {$debug_idx < 0} { run_test_suite "${zConfig}_debug" test [ concat $config_options -DSQLITE_DEBUG=1 ] } else { run_test_suite "${zConfig}_ndebug" test [ lreplace $config_options $debug_idx $debug_idx ] } } } main $argv |
Changes to test/rtree.test.
1 2 3 4 5 6 7 8 | # # 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 runs all rtree related tests. # | > < | < < | < < < < | < | < < < < < < < < < | < < < | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 2008 June 23 # # 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 runs all rtree related tests. # set testdir [file dirname $argv0] source $testdir/permutations.test ifcapable rtree { run_test_suite rtree } finish_test |
Changes to test/savepoint.test.
︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 1021 1022 | sql2 { CREATE INDEX fooidx ON foo(x); } sql3 { PRAGMA integrity_check } } {ok} do_test savepoint-16.$tn.3 { sql1 { SELECT * FROM foo } } {1 2 3 4} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > | 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 | sql2 { CREATE INDEX fooidx ON foo(x); } sql3 { PRAGMA integrity_check } } {ok} do_test savepoint-16.$tn.3 { sql1 { SELECT * FROM foo } } {1 2 3 4} } #------------------------------------------------------------------------- # This next block of tests verifies that a problem reported on the mailing # list has been resolved. At one point the second "CREATE TABLE t6" would # fail as table t6 still existed in the internal cache of the db schema # (even though it had been removed from the database by the ROLLBACK # command). # sqlite3 db test.db do_execsql_test savepoint-17.1 { BEGIN; CREATE TABLE t6(a, b); INSERT INTO t6 VALUES(1, 2); SAVEPOINT one; INSERT INTO t6 VALUES(3, 4); ROLLBACK TO one; SELECT * FROM t6; ROLLBACK; } {1 2} do_execsql_test savepoint-17.2 { CREATE TABLE t6(a, b); } {} finish_test |
Added test/schema4.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 | # 2010 September 28 # # 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 regression tests for SQLite library. The # focus of this file is testing that a trigger may have the same # name as an index, view or table in the same database. # set testdir [file dirname $argv0] source $testdir/tester.tcl #-------------------------------------------------------------------------- # Test organization: # # schema4-1.*: Dropping and creating triggers and other objects where # triggers and at least on other object share a name. # # schema4-2.*: Renaming tables where there is a trigger that shares the # name of the table or one of its indices. # do_execsql_test schema4-1.1 { CREATE TABLE log(x, a, b); CREATE TABLE tbl(a, b); CREATE TABLE t1(a, b); CREATE VIEW v1 AS SELECT * FROM tbl; CREATE INDEX i1 ON tbl(a); } {} do_execsql_test schema4-1.2 { CREATE TRIGGER t1 AFTER INSERT ON tbl BEGIN INSERT INTO log VALUES('after insert', new.a, new.b); END; CREATE TRIGGER v1 AFTER UPDATE ON tbl BEGIN INSERT INTO log VALUES('after update', new.a, new.b); END; CREATE TRIGGER i1 AFTER DELETE ON tbl BEGIN INSERT INTO log VALUES('after delete', old.a, old.b); END; } {} do_execsql_test schema4-1.3 { INSERT INTO tbl VALUES(1, 2); UPDATE tbl SET b=a+b, a=a+1; DELETE FROM tbl; SELECT x, a, b FROM log; } {{after insert} 1 2 {after update} 2 3 {after delete} 2 3} do_execsql_test schema4-1.4 { DELETE FROM log; DROP INDEX i1; DROP TABLE t1; DROP VIEW v1; INSERT INTO tbl VALUES(1, 2); UPDATE tbl SET b=a+b, a=a+1; DELETE FROM tbl; SELECT x, a, b FROM log; } {{after insert} 1 2 {after update} 2 3 {after delete} 2 3} db close sqlite3 db test.db do_execsql_test schema4-1.5 { DELETE FROM log; INSERT INTO tbl VALUES(1, 2); UPDATE tbl SET b=a+b, a=a+1; DELETE FROM tbl; SELECT x, a, b FROM log; } {{after insert} 1 2 {after update} 2 3 {after delete} 2 3} do_execsql_test schema4-1.6 { CREATE TABLE t1(a, b); CREATE VIEW v1 AS SELECT * FROM tbl; CREATE INDEX i1 ON tbl(a); } {} ifcapable fts3 { do_execsql_test schema4-1.7 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING fts3; } {} do_execsql_test schema4-1.8 { DELETE FROM log; DROP TABLE t1; INSERT INTO tbl VALUES(1, 2); UPDATE tbl SET b=a+b, a=a+1; DELETE FROM tbl; SELECT x, a, b FROM log; } {{after insert} 1 2 {after update} 2 3 {after delete} 2 3} } ifcapable altertable { drop_all_tables do_execsql_test schema4-2.1 { CREATE TABLE log(x, a, b); CREATE TABLE tbl(a, b); CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); } {} do_execsql_test schema4-2.2 { CREATE TRIGGER t1 AFTER INSERT ON tbl BEGIN INSERT INTO log VALUES('after insert', new.a, new.b); END; CREATE TRIGGER i1 AFTER DELETE ON tbl BEGIN INSERT INTO log VALUES('after delete', old.a, old.b); END; } {} do_execsql_test schema4-2.3 { ALTER TABLE t1 RENAME TO t2 } {} do_execsql_test schema4-2.4 { INSERT INTO tbl VALUES('a', 'b'); DELETE FROM tbl; SELECT * FROM log; } {{after insert} a b {after delete} a b} db close sqlite3 db test.db do_execsql_test schema4-2.5 { DELETE FROM log; INSERT INTO tbl VALUES('c', 'd'); DELETE FROM tbl; SELECT * FROM log; } {{after insert} c d {after delete} c d} do_execsql_test schema4-2.6 { CREATE TEMP TRIGGER x1 AFTER UPDATE ON tbl BEGIN INSERT INTO log VALUES('after update', new.a, new.b); END; CREATE TEMP TABLE x1(x); INSERT INTO x1 VALUES(123); } {} do_execsql_test schema4-2.8 { select sql from sqlite_temp_master WHERE type='table'; } {{CREATE TABLE x1(x)}} do_execsql_test schema4-2.7 { ALTER TABLE tbl RENAME TO tbl2 } {} do_execsql_test schema4-2.9 { select sql from sqlite_temp_master WHERE type='table'; } {{CREATE TABLE x1(x)}} do_execsql_test schema4-2.10 { DELETE FROM log; INSERT INTO tbl2 VALUES('e', 'f'); UPDATE tbl2 SET a='g', b='h'; DELETE FROM tbl2; SELECT * FROM log; } {{after insert} e f {after update} g h {after delete} g h} do_execsql_test schema4-2.11 { INSERT INTO x1 VALUES(456); SELECT * FROM x1 } {123 456} } finish_test |
Changes to test/select6.test.
︙ | ︙ | |||
454 455 456 457 458 459 460 461 462 463 464 465 466 467 | } ;# ifcapable view # Ticket #1634 # do_test select6-9.1 { execsql { SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b } } {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2} do_test select6-9.2 { execsql { SELECT x FROM (SELECT x FROM t1 LIMIT 2); } } {1 2} | > | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | } ;# ifcapable view # Ticket #1634 # do_test select6-9.1 { execsql { SELECT a.x, b.x FROM t1 AS a, (SELECT x FROM t1 LIMIT 2) AS b ORDER BY 1, 2 } } {1 1 1 2 2 1 2 2 3 1 3 2 4 1 4 2} do_test select6-9.2 { execsql { SELECT x FROM (SELECT x FROM t1 LIMIT 2); } } {1 2} |
︙ | ︙ |
Changes to test/shared2.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # #*********************************************************************** # # $Id: shared2.test,v 1.8 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl db close ifcapable !shared_cache { finish_test return } set ::enable_shared_cache [sqlite3_enable_shared_cache 1] | > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # #*********************************************************************** # # $Id: shared2.test,v 1.8 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl db close ifcapable !shared_cache { finish_test return } set ::enable_shared_cache [sqlite3_enable_shared_cache 1] |
︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 | # The following statement would crash when attempting to sqlite3_free() # a pointer allocated from a lookaside buffer. execsql { CREATE INDEX i1 ON t2(a) } db2 } {} db close db2 close sqlite3_enable_shared_cache $::enable_shared_cache finish_test | > > > > > > > > > | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | # The following statement would crash when attempting to sqlite3_free() # a pointer allocated from a lookaside buffer. execsql { CREATE INDEX i1 ON t2(a) } db2 } {} db close db2 close # The following test verifies that shared-cache mode does not automatically # turn on exclusive-locking mode for some reason. do_multiclient_test {tn} { sql1 { CREATE TABLE t1(a, b) } sql2 { CREATE TABLE t2(a, b) } do_test shared2-6.$tn.1 { sql1 { SELECT * FROM t2 } } {} do_test shared2-6.$tn.2 { sql2 { SELECT * FROM t1 } } {} } sqlite3_enable_shared_cache $::enable_shared_cache finish_test |
Changes to test/stat.test.
︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 | set ::asc 1 proc a_string {n} { string range [string repeat [incr ::asc]. $n] 1 $n } db func a_string a_string register_dbstat_vtab db do_execsql_test stat-0.0 { CREATE VIRTUAL TABLE temp.stat USING dbstat; SELECT * FROM stat; } {} | > | > > | 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 | set ::asc 1 proc a_string {n} { string range [string repeat [incr ::asc]. $n] 1 $n } db func a_string a_string register_dbstat_vtab db do_execsql_test stat-0.0 { PRAGMA auto_vacuum = OFF; CREATE VIRTUAL TABLE temp.stat USING dbstat; SELECT * FROM stat; } {} ifcapable wal { do_execsql_test stat-0.1 { PRAGMA journal_mode = WAL; PRAGMA journal_mode = delete; SELECT * FROM stat; } {wal delete sqlite_master / 1 leaf 0 0 916 0} } do_test stat-1.0 { execsql { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(b); INSERT INTO t1(rowid, a, b) VALUES(2, 2, 3); INSERT INTO t1(rowid, a, b) VALUES(3, 4, 5); } |
︙ | ︙ | |||
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | db close file delete -force test.db sqlite3 db test.db register_dbstat_vtab db breakpoint do_execsql_test stat-5.1 { CREATE VIRTUAL TABLE temp.stat USING dbstat; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(1513)); INSERT INTO t1 VALUES(zeroblob(1514)); SELECT * FROM stat WHERE name = 't1'; } [list \ t1 / 2 leaf 2 993 5 1517 \ t1 /000+000000 3 overflow 0 1020 0 0 \ t1 /001+000000 4 overflow 0 1020 0 0 \ ] finish_test | > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | db close file delete -force test.db sqlite3 db test.db register_dbstat_vtab db breakpoint do_execsql_test stat-5.1 { PRAGMA auto_vacuum = OFF; CREATE VIRTUAL TABLE temp.stat USING dbstat; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(1513)); INSERT INTO t1 VALUES(zeroblob(1514)); SELECT * FROM stat WHERE name = 't1'; } [list \ t1 / 2 leaf 2 993 5 1517 \ t1 /000+000000 3 overflow 0 1020 0 0 \ t1 /001+000000 4 overflow 0 1020 0 0 \ ] finish_test |
Changes to test/stmt.test.
︙ | ︙ | |||
42 43 44 45 46 47 48 49 50 51 52 53 54 55 | do_test stmt-1.2 { set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {1} do_test stmt-1.3 { execsql { BEGIN; INSERT INTO t1 VALUES(1, 1); } set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {2} do_test stmt-1.4 { | > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | do_test stmt-1.2 { set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {1} do_test stmt-1.3 { execsql { PRAGMA temp_store = file; BEGIN; INSERT INTO t1 VALUES(1, 1); } set sqlite_open_file_count expr $sqlite_open_file_count-$extrafds } {2} do_test stmt-1.4 { |
︙ | ︙ |
Added test/superlock.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 | # 2010 November 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set testprefix superlock do_execsql_test 1.1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); PRAGMA journal_mode = DELETE; } {delete} do_test 1.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 1.3 { SELECT * FROM t1 } {1 {database is locked}} do_test 1.4 { unlock } {} do_execsql_test 2.1 { INSERT INTO t1 VALUES(3, 4); PRAGMA journal_mode = WAL; } {wal} do_test 2.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 2.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 2.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} do_catchsql_test 2.5 { PRAGMA wal_checkpoint } {1 {database is locked}} do_test 2.6 { unlock } {} do_execsql_test 3.1 { INSERT INTO t1 VALUES(3, 4) } do_test 3.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 3.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {1 {database is locked}} do_test 3.6 { unlock } {} do_execsql_test 4.1 { PRAGMA wal_checkpoint } {} do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock} do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}} do_catchsql_test 4.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}} do_catchsql_test 4.5 { PRAGMA wal_checkpoint } {1 {database is locked}} do_test 4.6 { unlock } {} do_multiclient_test tn { proc busyhandler {x} { switch -- $x { 1 { sql1 "COMMIT" } 2 { sql2 "COMMIT" } 3 { sql3 "COMMIT" } } lappend ::busylist $x return 1 } set ::busylist [list] do_test 5.$tn.1 { sql1 { CREATE TABLE t1(a, b); PRAGMA journal_mode = WAL; INSERT INTO t1 VALUES(1, 2); } } {wal} do_test 5.$tn.2 { sql1 { BEGIN ; SELECT * FROM t1 } sql2 { BEGIN ; INSERT INTO t1 VALUES(3, 4) } sql3 { BEGIN ; SELECT * FROM t1 } } {1 2} do_test 5.$tn.3 { set ::busylist [list] sqlite3demo_superlock unlock test.db "" busyhandler set ::busylist } {0 1 2 3} do_test 5.$tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}} do_test 5.$tn.5 { csql3 { INSERT INTO t1 VALUES(5, 6) } } {1 {database is locked}} do_test 5.$tn.6 { csql1 "PRAGMA wal_checkpoint" } {1 {database is locked}} do_test 5.$tn.7 { unlock } {} } finish_test |
Changes to test/tempdb.test.
︙ | ︙ | |||
60 61 62 63 64 65 66 | do_test tempdb-2.1 { # Set $::jrnl_in_memory if the journal file is expected to be in-memory. # Similarly, set $::subj_in_memory if the sub-journal file is expected # to be in memory. These variables are used to calculate the expected # number of open files in the test cases below. # set jrnl_in_memory [expr {[permutation] eq "inmemory_journal"}] | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | do_test tempdb-2.1 { # Set $::jrnl_in_memory if the journal file is expected to be in-memory. # Similarly, set $::subj_in_memory if the sub-journal file is expected # to be in memory. These variables are used to calculate the expected # number of open files in the test cases below. # set jrnl_in_memory [expr {[permutation] eq "inmemory_journal"}] set subj_in_memory [expr {$jrnl_in_memory || $TEMP_STORE>=2}] db close sqlite3 db test.db } {} do_test tempdb-2.2 { execsql { CREATE TABLE t1 (a PRIMARY KEY, b, c); |
︙ | ︙ |
Changes to test/tester.tcl.
︙ | ︙ | |||
298 299 300 301 302 303 304 305 306 307 308 309 310 311 | # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg sqlite3_memdebug_settitle $name # if {[llength $argv]==0} { # set go 1 # } else { # set go 0 | > > | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { global argv cmdlinearg fix_testname name sqlite3_memdebug_settitle $name # if {[llength $argv]==0} { # set go 1 # } else { # set go 0 |
︙ | ︙ | |||
332 333 334 335 336 337 338 | fail_test $name } else { puts " Ok" } flush stdout } | > > > > > > > > > | > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | fail_test $name } else { puts " Ok" } flush stdout } proc fix_testname {varname} { upvar $varname testname if {[info exists ::testprefix] && [string is digit [string range $testname 0 0]] } { set testname "${::testprefix}-$testname" } } proc do_execsql_test {testname sql {result {}}} { fix_testname testname uplevel do_test $testname [list "execsql {$sql}"] [list [list {*}$result]] } proc do_catchsql_test {testname sql result} { fix_testname testname uplevel do_test $testname [list "catchsql {$sql}"] [list $result] } proc do_eqp_test {name sql res} { uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res] } #------------------------------------------------------------------------- # Usage: do_select_tests PREFIX ?SWITCHES? TESTLIST # # Where switches are: # # -errorformat FMTSTRING # -count # -query SQL # -tclquery TCL # -repair TCL # proc do_select_tests {prefix args} { set testlist [lindex $args end] set switches [lrange $args 0 end-1] set errfmt "" set countonly 0 set tclquery "" set repair "" for {set i 0} {$i < [llength $switches]} {incr i} { set s [lindex $switches $i] set n [string length $s] if {$n>=2 && [string equal -length $n $s "-query"]} { set tclquery [list execsql [lindex $switches [incr i]]] } elseif {$n>=2 && [string equal -length $n $s "-tclquery"]} { set tclquery [lindex $switches [incr i]] } elseif {$n>=2 && [string equal -length $n $s "-errorformat"]} { set errfmt [lindex $switches [incr i]] } elseif {$n>=2 && [string equal -length $n $s "-repair"]} { set repair [lindex $switches [incr i]] } elseif {$n>=2 && [string equal -length $n $s "-count"]} { set countonly 1 } else { error "unknown switch: $s" } } if {$countonly && $errfmt!=""} { error "Cannot use -count and -errorformat together" } set nTestlist [llength $testlist] if {$nTestlist%3 || $nTestlist==0 } { error "SELECT test list contains [llength $testlist] elements" } eval $repair foreach {tn sql res} $testlist { if {$tclquery != ""} { execsql $sql uplevel do_test ${prefix}.$tn [list $tclquery] [list [list {*}$res]] } elseif {$countonly} { set nRow 0 db eval $sql {incr nRow} uplevel do_test ${prefix}.$tn [list [list set {} $nRow]] [list $res] } elseif {$errfmt==""} { uplevel do_execsql_test ${prefix}.${tn} [list $sql] [list [list {*}$res]] } else { set res [list 1 [string trim [format $errfmt {*}$res]]] uplevel do_catchsql_test ${prefix}.${tn} [list $sql] [list $res] } eval $repair } } proc delete_all_data {} { db eval {SELECT tbl_name AS t FROM sqlite_master WHERE type = 'table'} { db eval "DELETE FROM '[string map {' ''} $t]'" } } # Run an SQL script. # Return the number of microseconds per statement. # proc speed_trial {name numstmt units sql} { puts -nonewline [format {%-21.21s } $name...] flush stdout set speed [time {sqlite3_exec_nr db $sql}] set tm [lindex $speed 0] if {$tm == 0} { set rate [format %20s "many"] } else { set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]] } set u2 $units/s puts [format {%12d uS %s %s} $tm $rate $u2] global total_time set total_time [expr {$total_time+$tm}] lappend ::speed_trial_times $name $tm } proc speed_trial_tcl {name numstmt units script} { puts -nonewline [format {%-21.21s } $name...] flush stdout set speed [time {eval $script}] set tm [lindex $speed 0] if {$tm == 0} { set rate [format %20s "many"] } else { set rate [format %20.5f [expr {1000000.0*$numstmt/$tm}]] } set u2 $units/s puts [format {%12d uS %s %s} $tm $rate $u2] global total_time set total_time [expr {$total_time+$tm}] lappend ::speed_trial_times $name $tm } proc speed_trial_init {name} { global total_time set total_time 0 set ::speed_trial_times [list] sqlite3 versdb :memory: set vers [versdb one {SELECT sqlite_source_id()}] versdb close puts "SQLite $vers" } proc speed_trial_summary {name} { global total_time puts [format {%-21.21s %12d uS TOTAL} $name $total_time] if { 0 } { sqlite3 versdb :memory: set vers [lindex [versdb one {SELECT sqlite_source_id()}] 0] versdb close puts "CREATE TABLE IF NOT EXISTS time(version, script, test, us);" foreach {test us} $::speed_trial_times { puts "INSERT INTO time VALUES('$vers', '$name', '$test', $us);" } } } # Run this routine last # proc finish_test {} { catch {db close} catch {db2 close} |
︙ | ︙ | |||
600 601 602 603 604 605 606 | } } return $r } # Delete a file or directory # | | > > > > > > > > > > > > > | > | > > | 701 702 703 704 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 | } } return $r } # Delete a file or directory # proc forcedelete {args} { foreach filename $args { # On windows, sometimes even a [file delete -force] can fail just after # a file is closed. The cause is usually "tag-alongs" - programs like # anti-virus software, automatic backup tools and various explorer # extensions that keep a file open a little longer than we expect, causing # the delete to fail. # # The solution is to wait a short amount of time before retrying the # delete. # set nRetry 50 ;# Maximum number of retries. set nDelay 100 ;# Delay in ms before retrying. for {set i 0} {$i<$nRetry} {incr i} { set rc [catch {file delete -force $filename} msg] if {$rc==0} break after $nDelay } if {$rc} { error $msg } } } # Do an integrity check of the entire database # proc integrity_check {name {db db}} { ifcapable integrityck { |
︙ | ︙ | |||
1104 1105 1106 1107 1108 1109 1110 | if {$idx==1} { set master sqlite_temp_master } else { set master $name.sqlite_master } foreach {t type} [$db eval " SELECT name, type FROM $master | | | | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 | if {$idx==1} { set master sqlite_temp_master } else { set master $name.sqlite_master } foreach {t type} [$db eval " SELECT name, type FROM $master WHERE type IN('table', 'view') AND name NOT LIKE 'sqliteX_%' ESCAPE 'X' "] { $db eval "DROP $type \"$t\"" } } ifcapable trigger&&foreignkey { $db eval "PRAGMA foreign_keys = $pk" } } |
︙ | ︙ | |||
1292 1293 1294 1295 1296 1297 1298 | } # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) source $testdir/thread_common.tcl | > | 1409 1410 1411 1412 1413 1414 1415 1416 | } # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set # to non-zero, then set the global variable $AUTOVACUUM to 1. set AUTOVACUUM $sqlite_options(default_autovacuum) source $testdir/thread_common.tcl source $testdir/malloc_common.tcl |
Changes to test/threadtest3.c.
︙ | ︙ | |||
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 | xSub = cgt_pager_1_populate; xSub(&err, &db); xSub = cgt_pager_1_update; xSub(&err, &db); xSub = cgt_pager_1_read; xSub(&err, &db); closedb(&err, &db); print_and_free_err(&err); } int main(int argc, char **argv){ struct ThreadTest { void (*xTest)(int); const char *zTest; int nMs; } aTest[] = { { walthread1, "walthread1", 20000 }, { walthread2, "walthread2", 20000 }, { walthread3, "walthread3", 20000 }, { walthread4, "walthread4", 20000 }, { walthread5, "walthread5", 1000 }, { walthread5, "walthread5", 1000 }, { cgt_pager_1, "cgt_pager_1", 0 }, }; int i; char *zTest = 0; int nTest = 0; int bTestfound = 0; int bPrefix = 0; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 | xSub = cgt_pager_1_populate; xSub(&err, &db); xSub = cgt_pager_1_update; xSub(&err, &db); xSub = cgt_pager_1_read; xSub(&err, &db); closedb(&err, &db); print_and_free_err(&err); } /*------------------------------------------------------------------------ ** Test case "dynamic_triggers" ** ** Two threads executing statements that cause deeply nested triggers ** to fire. And one thread busily creating and deleting triggers. This ** is an attempt to find a bug reported to us. */ static char *dynamic_triggers_1(int iTid, int iArg){ Error err = {0}; /* Error code and message */ Sqlite db = {0}; /* SQLite database connection */ int nDrop = 0; int nCreate = 0; opendb(&err, &db, "test.db", 0); while( !timetostop(&err) ){ int i; for(i=1; i<9; i++){ char *zSql = sqlite3_mprintf( "CREATE TRIGGER itr%d BEFORE INSERT ON t%d BEGIN " "INSERT INTO t%d VALUES(new.x, new.y);" "END;", i, i, i+1 ); execsql(&err, &db, zSql); sqlite3_free(zSql); nCreate++; } for(i=1; i<9; i++){ char *zSql = sqlite3_mprintf( "CREATE TRIGGER dtr%d BEFORE DELETE ON t%d BEGIN " "DELETE FROM t%d WHERE x = old.x; " "END;", i, i, i+1 ); execsql(&err, &db, zSql); sqlite3_free(zSql); nCreate++; } for(i=1; i<9; i++){ char *zSql = sqlite3_mprintf("DROP TRIGGER itr%d", i); execsql(&err, &db, zSql); sqlite3_free(zSql); nDrop++; } for(i=1; i<9; i++){ char *zSql = sqlite3_mprintf("DROP TRIGGER dtr%d", i); execsql(&err, &db, zSql); sqlite3_free(zSql); nDrop++; } } print_and_free_err(&err); return sqlite3_mprintf("%d created, %d dropped", nCreate, nDrop); } static char *dynamic_triggers_2(int iTid, int iArg){ Error err = {0}; /* Error code and message */ Sqlite db = {0}; /* SQLite database connection */ i64 iVal = 0; int nInsert = 0; int nDelete = 0; opendb(&err, &db, "test.db", 0); while( !timetostop(&err) ){ do { iVal = (iVal+1)%100; execsql(&err, &db, "INSERT INTO t1 VALUES(:iX, :iY+1)", &iVal, &iVal); nInsert++; } while( iVal ); do { iVal = (iVal+1)%100; execsql(&err, &db, "DELETE FROM t1 WHERE x = :iX", &iVal); nDelete++; } while( iVal ); } print_and_free_err(&err); return sqlite3_mprintf("%d inserts, %d deletes", nInsert, nDelete); } static void dynamic_triggers(int nMs){ Error err = {0}; Sqlite db = {0}; Threadset threads = {0}; opendb(&err, &db, "test.db", 1); sql_script(&err, &db, "PRAGMA page_size = 1024;" "PRAGMA journal_mode = WAL;" "CREATE TABLE t1(x, y);" "CREATE TABLE t2(x, y);" "CREATE TABLE t3(x, y);" "CREATE TABLE t4(x, y);" "CREATE TABLE t5(x, y);" "CREATE TABLE t6(x, y);" "CREATE TABLE t7(x, y);" "CREATE TABLE t8(x, y);" "CREATE TABLE t9(x, y);" ); setstoptime(&err, nMs); sqlite3_enable_shared_cache(1); launch_thread(&err, &threads, dynamic_triggers_2, 0); launch_thread(&err, &threads, dynamic_triggers_2, 0); sqlite3_enable_shared_cache(0); sleep(2); launch_thread(&err, &threads, dynamic_triggers_2, 0); launch_thread(&err, &threads, dynamic_triggers_1, 0); join_all_threads(&err, &threads); print_and_free_err(&err); } int main(int argc, char **argv){ struct ThreadTest { void (*xTest)(int); const char *zTest; int nMs; } aTest[] = { { walthread1, "walthread1", 20000 }, { walthread2, "walthread2", 20000 }, { walthread3, "walthread3", 20000 }, { walthread4, "walthread4", 20000 }, { walthread5, "walthread5", 1000 }, { walthread5, "walthread5", 1000 }, { cgt_pager_1, "cgt_pager_1", 0 }, { dynamic_triggers, "dynamic_triggers", 20000 }, }; int i; char *zTest = 0; int nTest = 0; int bTestfound = 0; int bPrefix = 0; |
︙ | ︙ |
Added test/tkt-313723c356.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 | # 2010 September 20 # # 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 regression tests for SQLite library. # # This file implements tests to verify that ticket [313723c356] has been # fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl ifcapable !wal { finish_test ; return } do_execsql_test tkt-313723c356.1 { PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a, b); INSERT INTO t1 VALUES(randomblob(400), randomblob(400)); INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1; INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1; INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1; INSERT INTO t1 SELECT randomblob(400), randomblob(400) FROM t1; } {wal} faultsim_save_and_close do_faultsim_test tkt-313723c356.2 -faults shmerr* -prep { faultsim_restore_and_reopen sqlite3 db2 test.db db eval { SELECT * FROM t1 } db2 eval { UPDATE t1 SET a = randomblob(399) } db2 close } -body { # At this point, the cache contains all of table t1 and none of index i1. The # cache is out of date. When the bug existed and the right xShmLock() fails # in the following statement, the internal cache of the WAL header was # being updated, but the contents of the page-cache not flushed. This causes # the integrity-check in the "-test" code to fail, as it is comparing the # cached (out-of-date) version of table t1 with the on disk (up-to-date) # version of index i1. # execsql { SELECT min(rowid) FROM t1 } } -test { faultsim_test_result {0 1} faultsim_integrity_check } finish_test |
Added test/tkt-38cb5df375.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 | # 2010 October 6 # # 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 regression tests for SQLite library. Specifically, # it tests that ticket [38cb5df375078d3f9711482d2a1615d09f6b3f33] has # been resolved. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-38cb5df375.0 { execsql { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 SELECT a+2 FROM t1; INSERT INTO t1 SELECT a+4 FROM t1; } } {} foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.1.$ii { execsql { SELECT * FROM (SELECT * FROM t1 ORDER BY a) UNION ALL SELECT 9 FROM (SELECT a FROM t1) LIMIT $::ii; } } [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.2.$ii { execsql { SELECT 9 FROM (SELECT * FROM t1) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a) LIMIT $::ii; } } [lrange {9 9 9 9 9 9 9 9 1 2 3 4 5 6 7 8} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.3.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a) LIMIT $::ii; } } [lrange {1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.4.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1) UNION ALL SELECT 9 FROM (SELECT a FROM t1) LIMIT $::ii; } } [lrange {0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4} { do_test tkt-38cb5df375.5.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1) UNION SELECT 9 FROM (SELECT a FROM t1) LIMIT $::ii; } } [lrange {0 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11} { do_test tkt-38cb5df375.11.$ii { execsql { SELECT * FROM (SELECT * FROM t1 ORDER BY a LIMIT 3) UNION ALL SELECT 9 FROM (SELECT a FROM t1) LIMIT $::ii; } } [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11} { do_test tkt-38cb5df375.12.$ii { execsql { SELECT 9 FROM (SELECT * FROM t1) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3) LIMIT $::ii; } } [lrange {9 9 9 9 9 9 9 9 1 2 3} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6} { do_test tkt-38cb5df375.13.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3) LIMIT $::ii; } } [lrange {1 2 3 1 2 3} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6} { do_test tkt-38cb5df375.14.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1 LIMIT 3) UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 3) LIMIT $::ii; } } [lrange {0 0 0 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.21.$ii { execsql { SELECT * FROM (SELECT * FROM t1 ORDER BY a) UNION ALL SELECT 9 FROM (SELECT a FROM t1) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.22.$ii { execsql { SELECT 9 FROM (SELECT * FROM t1) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 4 5 6 7 8 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.23.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a) ORDER BY 1 DESC LIMIT $::ii; } } [lrange {8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16} { do_test tkt-38cb5df375.24.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1) UNION ALL SELECT 9 FROM (SELECT a FROM t1) ORDER BY 1 LIMIT $::ii; } } [lrange {0 0 0 0 0 0 0 0 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11} { do_test tkt-38cb5df375.31.$ii { execsql { SELECT * FROM (SELECT * FROM t1 ORDER BY a LIMIT 3) UNION ALL SELECT 9 FROM (SELECT a FROM t1) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9 10 11} { do_test tkt-38cb5df375.32.$ii { execsql { SELECT 9 FROM (SELECT * FROM t1) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 9 9 9 9 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.33.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 4) UNION ALL SELECT 90+a FROM (SELECT a FROM t1 ORDER BY a LIMIT 3) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 4 91 92 93} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.34.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 2) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 5) ORDER BY 1 LIMIT $::ii; } } [lrange {1 1 2 2 3 4 5} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.35.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5) UNION ALL SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 1 2 2 3 4 5} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.35b.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5) UNION ALL SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 4 5 11 12} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.35c.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5) UNION SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 4 5 11 12} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.35d.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5) INTERSECT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.35e.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 5) EXCEPT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {3 4 5} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.36.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1 LIMIT 3) UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 4) ORDER BY 1 LIMIT $::ii; } } [lrange {0 0 0 9 9 9 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.37.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1 LIMIT 3) UNION SELECT 9 FROM (SELECT a FROM t1 LIMIT 4) ORDER BY 1 LIMIT $::ii; } } [lrange {0 9} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { do_test tkt-38cb5df375.38.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1 LIMIT 3) EXCEPT SELECT 9 FROM (SELECT a FROM t1 LIMIT 4) ORDER BY 1 LIMIT $::ii; } } [lrange {0} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9} { do_test tkt-38cb5df375.41.$ii { execsql { SELECT 0 FROM (SELECT * FROM t1 LIMIT 3) UNION ALL SELECT 9 FROM (SELECT a FROM t1 LIMIT 4) UNION ALL SELECT 88 FROM (SELECT a FROM t1 LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {0 0 0 9 9 9 9 88 88} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9} { do_test tkt-38cb5df375.42.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3) UNION ALL SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 4) UNION ALL SELECT a+20 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 11 12 13 14 21 22} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7 8 9} { do_test tkt-38cb5df375.43.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a LIMIT 3) UNION SELECT a+10 FROM (SELECT a FROM t1 ORDER BY a LIMIT 4) UNION SELECT a+20 FROM (SELECT a FROM t1 ORDER BY a LIMIT 2) ORDER BY 1 LIMIT $::ii; } } [lrange {1 2 3 11 12 13 14 21 22} 0 [expr {$ii-1}]] } foreach ii {1 2 3 4 5 6 7} { set jj [expr {7-$ii}] do_test tkt-38cb5df375.51.$ii { execsql { SELECT a FROM (SELECT * FROM t1 ORDER BY a) EXCEPT SELECT a FROM (SELECT a FROM t1 ORDER BY a LIMIT $::ii) ORDER BY a DESC LIMIT $::jj; } } [lrange {8 7 6 5 4 3 2 1} 0 [expr {$jj-1}]] } finish_test |
Added test/tkt-3998683a16.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 | # 2010 September 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 implements regression tests for SQLite library. Specifically, # it tests that ticket [3998683a16a7076e08f5585c1f4816414c8c653a] where in # floating point values with a decimal point at the beginning or end # of the mantissa are used. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-3998683a16.1 { db eval { CREATE TABLE t1(x, y REAL); INSERT INTO t1 VALUES(1, '1.0'); INSERT INTO t1 VALUES(2, '.125'); INSERT INTO t1 VALUES(3, '123.'); INSERT INTO t1 VALUES(4, '123.e+2'); INSERT INTO t1 VALUES(5, '.125e+3'); INSERT INTO t1 VALUES(6, '123e4'); INSERT INTO t1 VALUES(11, ' 1.0'); INSERT INTO t1 VALUES(12, ' .125'); INSERT INTO t1 VALUES(13, ' 123.'); INSERT INTO t1 VALUES(14, ' 123.e+2'); INSERT INTO t1 VALUES(15, ' .125e+3'); INSERT INTO t1 VALUES(16, ' 123e4'); INSERT INTO t1 VALUES(21, '1.0 '); INSERT INTO t1 VALUES(22, '.125 '); INSERT INTO t1 VALUES(23, '123. '); INSERT INTO t1 VALUES(24, '123.e+2 '); INSERT INTO t1 VALUES(25, '.125e+3 '); INSERT INTO t1 VALUES(26, '123e4 '); SELECT x FROM t1 WHERE typeof(y)=='real' ORDER BY x; } } {1 2 3 4 5 6 11 12 13 14 15 16 21 22 23 24 25 26} finish_test |
Changes to test/tkt-78e04e52ea.test.
︙ | ︙ | |||
40 41 42 43 44 45 46 | CREATE INDEX i1 ON ""("" COLLATE nocase); } } {} do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%'; } | | | | | 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 | CREATE INDEX i1 ON ""("" COLLATE nocase); } } {} do_test tkt-78e04-1.4 { execsql { EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%'; } } {0 0 0 {SCAN TABLE (~500000 rows)}} do_test tkt-78e04-1.5 { execsql { DROP TABLE ""; SELECT name FROM sqlite_master; } } {t2} do_test tkt-78e04-2.1 { execsql { CREATE INDEX "" ON t2(x); EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=5; } } {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?) (~10 rows)}} do_test tkt-78e04-2.2 { execsql { DROP INDEX ""; EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=2; } } {0 0 0 {SCAN TABLE t2 (~100000 rows)}} finish_test |
Added test/tkt-8454a207b9.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 | # 2010 September 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 implements regression tests for SQLite library. Specifically, # it tests that ticket [8454a207b9fd2243c4c6b7a73f67ea0315717c1a]. Verify # that a negative default value on an added text column actually comes # out negative. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-8454a207b9.1 { db eval { CREATE TABLE t1(a); INSERT INTO t1 VALUES(1); ALTER TABLE t1 ADD COLUMN b TEXT DEFAULT -123.0; SELECT b, typeof(b) FROM t1; } } {-123.0 text} do_test tkt-8454a207b9.2 { db eval { ALTER TABLE t1 ADD COLUMN c TEXT DEFAULT -123.5; SELECT c, typeof(c) FROM t1; } } {-123.5 text} do_test tkt-8454a207b9.3 { db eval { ALTER TABLE t1 ADD COLUMN d TEXT DEFAULT -'hello'; SELECT d, typeof(d) FROM t1; } } {0 text} do_test tkt-8454a207b9.4 { db eval { ALTER TABLE t1 ADD COLUMN e DEFAULT -123.0; SELECT e, typeof(e) FROM t1; } } {-123 integer} do_test tkt-8454a207b9.5 { db eval { ALTER TABLE t1 ADD COLUMN f DEFAULT -123.5; SELECT f, typeof(f) FROM t1; } } {-123.5 real} do_test tkt-8454a207b9.6 { db eval { ALTER TABLE t1 ADD COLUMN g DEFAULT -9223372036854775808; SELECT g, typeof(g) FROM t1; } } {-9223372036854775808 integer} do_test tkt-8454a207b9.7 { db eval { ALTER TABLE t1 ADD COLUMN h DEFAULT 9223372036854775807; SELECT h, typeof(h) FROM t1; } } {9223372036854775807 integer} finish_test |
Added test/tkt-b351d95f9.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 | # 2010 September 28 # # 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 regression tests for SQLite library. Specifically, # it tests that ticket [b351d95f9cd5ef17e9d9dbae18f5ca8611190001] has been # resolved. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl do_test tkt-b351d95.1 { execsql { CREATE table t1(a,b); INSERT INTO t1 VALUES('name1','This is a test'); INSERT INTO t1 VALUES('name2','xyz'); CREATE TABLE t2(x,y); INSERT INTO t2 SELECT a, CASE b WHEN 'xyz' THEN null ELSE b END FROM t1; SELECT x, y FROM t2 ORDER BY x; } } {name1 {This is a test} name2 {}} do_test tkt-b351d95.2 { execsql { DELETE FROM t2; INSERT INTO t2 SELECT a, coalesce(b,a) FROM t1; SELECT x, y FROM t2 ORDER BY x; } } {name1 {This is a test} name2 xyz} do_test tkt-b351d95.3 { execsql { DELETE FROM t2; INSERT INTO t2 SELECT a, coalesce(b,a) FROM t1; SELECT x, y BETWEEN 'xy' AND 'xz' FROM t2 ORDER BY x; } } {name1 0 name2 1} finish_test |
Changes to test/tkt-f3e5abed55.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 | # Set up a testvfs so that the next time SQLite tries to delete the # file "test.db-journal", a snapshot of the current file-system contents # is taken. # | > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 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 | # Set up a testvfs so that the next time SQLite tries to delete the # file "test.db-journal", a snapshot of the current file-system contents # is taken. # # This test will not work with an in-memory journal. # if {[permutation]!="inmemory_journal"} { testvfs tvfs -default 1 tvfs script xDelete tvfs filter xDelete proc xDelete {method file args} { if {[file tail $file] == "test.db-journal"} { faultsim_save tvfs filter {} } return "SQLITE_OK" } sqlite3 db test.db sqlite3 db2 test.db do_test tkt-f3e5abed55-2.1 { execsql { ATTACH 'test.db2' AS aux; BEGIN; INSERT INTO t1 VALUES(3, 4); INSERT INTO t2 VALUES(3, 4); } } {} do_test tkt-f3e5abed55-2.2 { execsql { BEGIN; SELECT * FROM t1 } db2 } {1 2} do_test tkt-f3e5abed55-2.3 { catchsql COMMIT } {1 {database is locked}} do_test tkt-f3e5abed55-2.4 { execsql COMMIT db2 execsql { COMMIT; SELECT * FROM t1; SELECT * FROM t2; } } {1 2 3 4 1 2 3 4} do_test tkt-f3e5abed55-2.5 { db close db2 close faultsim_restore_and_reopen execsql { ATTACH 'test.db2' AS aux; SELECT * FROM t1; SELECT * FROM t2; } } {1 2 3 4 1 2 3 4} } finish_test |
Changes to test/tkt1667.test.
︙ | ︙ | |||
30 31 32 33 34 35 36 | # Set the pending byte offset such that the page it is on is # the first autovacuum pointer map page in the file (assume a page # size of 1024). set first_ptrmap_page [expr 1024/5 + 3] sqlite3_test_control_pending_byte [expr 1024 * ($first_ptrmap_page-1)] | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | # Set the pending byte offset such that the page it is on is # the first autovacuum pointer map page in the file (assume a page # size of 1024). set first_ptrmap_page [expr 1024/5 + 3] sqlite3_test_control_pending_byte [expr 1024 * ($first_ptrmap_page-1)] sqlite3 db test.db do_test tkt1667-1 { execsql { PRAGMA auto_vacuum = 1; BEGIN; CREATE TABLE t1(a, b); } |
︙ | ︙ |
Changes to test/tkt3442.test.
︙ | ︙ | |||
45 46 47 48 49 50 51 | # These tests perform an EXPLAIN QUERY PLAN on both versions of the # SELECT referenced in ticket #3442 (both '5000' and "5000") # and verify that the query plan is the same. # ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } | | | | | 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 | # These tests perform an EXPLAIN QUERY PLAN on both versions of the # SELECT referenced in ticket #3442 (both '5000' and "5000") # and verify that the query plan is the same. # ifcapable explain { do_test tkt3442-1.2 { EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; } } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} do_test tkt3442-1.3 { EQP { SELECT node FROM listhash WHERE id="5000" LIMIT 1; } } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} } # Some extra tests testing other permutations of 5000. # ifcapable explain { do_test tkt3442-1.4 { EQP { SELECT node FROM listhash WHERE id=5000 LIMIT 1; } } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}} } do_test tkt3442-1.5 { catchsql { SELECT node FROM listhash WHERE id=[5000] LIMIT 1; } } {1 {no such column: 5000}} |
︙ | ︙ |
Changes to test/tkt3757.test.
︙ | ︙ | |||
31 32 33 34 35 36 37 | db eval { CREATE TABLE t1(x INTEGER, y INTEGER, z TEXT); CREATE INDEX t1i1 ON t1(y,z); INSERT INTO t1 VALUES(1,2,'three'); CREATE TABLE t2(a INTEGER, b TEXT); INSERT INTO t2 VALUES(2, 'two'); ANALYZE; | | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | db eval { CREATE TABLE t1(x INTEGER, y INTEGER, z TEXT); CREATE INDEX t1i1 ON t1(y,z); INSERT INTO t1 VALUES(1,2,'three'); CREATE TABLE t2(a INTEGER, b TEXT); INSERT INTO t2 VALUES(2, 'two'); ANALYZE; SELECT * FROM sqlite_stat1 ORDER BY 1, 2; } } {t1 t1i1 {1 1 1} t2 {} 1} # Modify statistics in order to make the optimizer then that: # # (1) Table T1 has about 250K entries # (2) There are only about 5 distinct values of T1. # # Then run a query with "t1.y IN (SELECT ..)" in the WHERE clause. |
︙ | ︙ |
Changes to test/types.test.
︙ | ︙ | |||
131 132 133 134 135 136 137 | DROP TABLE t1; } # Open the table with root-page $rootpage at the btree # level. Return a list that is the length of each record # in the table, in the tables default scanning order. proc record_sizes {rootpage} { | | | 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | DROP TABLE t1; } # Open the table with root-page $rootpage at the btree # level. Return a list that is the length of each record # in the table, in the tables default scanning order. proc record_sizes {rootpage} { set bt [btree_open test.db 10] btree_begin_transaction $bt set c [btree_cursor $bt $rootpage 0] btree_first $c while 1 { lappend res [btree_payload_size $c] if {[btree_next $c]} break } |
︙ | ︙ |
Changes to test/vacuum.test.
︙ | ︙ | |||
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | file delete -force :memory: do_test vacuum-7.0 { sqlite3 db2 :memory: execsql { CREATE TABLE t1(t); VACUUM; } db2 execsql { CREATE TABLE t2(t); CREATE TABLE t3(t); DROP TABLE t2; VACUUM; pragma integrity_check; } db2 } {ok} db2 close # Ticket #873. VACUUM a database that has ' in its name. # do_test vacuum-8.1 { file delete -force a'z.db file delete -force a'z.db-journal | > > > > > > > > > > > > > > > > > > > > > > > > | 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 | file delete -force :memory: do_test vacuum-7.0 { sqlite3 db2 :memory: execsql { CREATE TABLE t1(t); VACUUM; } db2 } {} do_test vacuum-7.1 { execsql { CREATE TABLE t2(t); CREATE TABLE t3(t); DROP TABLE t2; PRAGMA freelist_count; } } {1} do_test vacuum-7.2 { execsql { VACUUM; pragma integrity_check; } db2 } {ok} do_test vacuum-7.3 { execsql { PRAGMA freelist_count; } db2 } {0} ifcapable autovacuum { do_test vacuum-7.4 { execsql { PRAGMA auto_vacuum } db2 } {0} do_test vacuum-7.5 { execsql { PRAGMA auto_vacuum = 1} db2 execsql { PRAGMA auto_vacuum } db2 } {0} do_test vacuum-7.6 { execsql { PRAGMA auto_vacuum = 1} db2 execsql { VACUUM } db2 execsql { PRAGMA auto_vacuum } db2 } {1} } db2 close # Ticket #873. VACUUM a database that has ' in its name. # do_test vacuum-8.1 { file delete -force a'z.db file delete -force a'z.db-journal |
︙ | ︙ |
Changes to test/vacuum2.test.
︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 | db close sqlite3 db test.db execsql { pragma auto_vacuum; } } {2} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | db close sqlite3 db test.db execsql { pragma auto_vacuum; } } {2} } #------------------------------------------------------------------------- # The following block of tests verify the behaviour of the library when # a database is VACUUMed when there are one or more unfinalized SQL # statements reading the same database using the same db handle. # db close forcedelete test.db sqlite3 db test.db do_execsql_test vacuum2-5.1 { CREATE TABLE t1(a PRIMARY KEY, b UNIQUE); INSERT INTO t1 VALUES(1, randomblob(500)); INSERT INTO t1 SELECT a+1, randomblob(500) FROM t1; -- 2 INSERT INTO t1 SELECT a+2, randomblob(500) FROM t1; -- 4 INSERT INTO t1 SELECT a+4, randomblob(500) FROM t1; -- 8 INSERT INTO t1 SELECT a+8, randomblob(500) FROM t1; -- 16 } {} do_test vacuum2-5.2 { list [catch { db eval {SELECT a, b FROM t1} { if {$a == 8} { execsql VACUUM } } } msg] $msg } {1 {cannot VACUUM - SQL statements in progress}} do_test vacuum2-5.3 { list [catch { db eval {SELECT 1, 2, 3} { execsql VACUUM } } msg] $msg } {1 {cannot VACUUM - SQL statements in progress}} do_test vacuum2-5.4 { set res "" set res2 "" db eval {SELECT a, b FROM t1 WHERE a<=10} { if {$a==6} { set res [catchsql VACUUM] } lappend res2 $a } lappend res2 $res } {1 2 3 4 5 6 7 8 9 10 {1 {cannot VACUUM - SQL statements in progress}}} finish_test |
Changes to test/vtab1.test.
︙ | ︙ | |||
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 | do_test vtab1-16.$tn { set echo_module_fail(xRename,t2) "the xRename method has failed" catchsql { ALTER TABLE echo_t2 RENAME TO another_name } } "1 {echo-vtab-error: the xRename method has failed}" unset echo_module_fail(xRename,t2) incr tn } unset -nocomplain echo_module_begin_fail finish_test | > > > > > > > > > > > > > > > | 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 | do_test vtab1-16.$tn { set echo_module_fail(xRename,t2) "the xRename method has failed" catchsql { ALTER TABLE echo_t2 RENAME TO another_name } } "1 {echo-vtab-error: the xRename method has failed}" unset echo_module_fail(xRename,t2) incr tn } # The following test case exposes an instance in sqlite3_declare_vtab() # an error message was set using a call similar to sqlite3_mprintf(zErr), # where zErr is an arbitrary string. This is no good if the string contains # characters that can be mistaken for printf() formatting directives. # do_test vtab1-17.1 { execsql { PRAGMA writable_schema = 1; INSERT INTO sqlite_master VALUES( 'table', 't3', 't3', 0, 'INSERT INTO "%s%s" VALUES(1)' ); } catchsql { CREATE VIRTUAL TABLE t4 USING echo(t3); } } {1 {vtable constructor failed: t4}} unset -nocomplain echo_module_begin_fail finish_test |
Changes to test/wal2.test.
︙ | ︙ | |||
430 431 432 433 434 435 436 | # connection silently remains in exclusive mode. # do_test wal2-6.1.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Journal_Mode = Wal; | < | > > > > | 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 | # connection silently remains in exclusive mode. # do_test wal2-6.1.1 { file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { Pragma Journal_Mode = Wal; } } {wal} do_test wal2-6.1.2 { execsql { PRAGMA lock_status } } {main unlocked temp closed} do_test wal2-6.1.3 { execsql { SELECT * FROM sqlite_master; Pragma Locking_Mode = Exclusive; } execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; PRAGMA lock_status; } |
︙ | ︙ | |||
486 487 488 489 490 491 492 493 494 495 496 497 498 499 | COMMIT; Pragma loCK_STATus; } } {main exclusive temp closed} do_test wal2-6.2.3 { db close sqlite3 db test.db execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } } {exclusive} do_test wal2-6.2.4 { execsql { SELECT * FROM t1; pragma lock_status; } | > | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 | COMMIT; Pragma loCK_STATus; } } {main exclusive temp closed} do_test wal2-6.2.3 { db close sqlite3 db test.db execsql { SELECT * FROM sqlite_master } execsql { PRAGMA LOCKING_MODE = EXCLUSIVE } } {exclusive} do_test wal2-6.2.4 { execsql { SELECT * FROM t1; pragma lock_status; } |
︙ | ︙ | |||
732 733 734 735 736 737 738 739 740 741 742 743 744 745 | return SQLITE_OK } do_test wal2-6.6.1 { testvfs T T script lock_control T filter {} sqlite3 db test.db -vfs T execsql { PRAGMA locking_mode = exclusive } execsql { INSERT INTO t2 VALUES('V', 'VI') } } {} do_test wal2-6.6.2 { execsql { PRAGMA locking_mode = normal } T filter xShmLock execsql { INSERT INTO t2 VALUES('VII', 'VIII') } | > | 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 | return SQLITE_OK } do_test wal2-6.6.1 { testvfs T T script lock_control T filter {} sqlite3 db test.db -vfs T execsql { SELECT * FROM sqlite_master } execsql { PRAGMA locking_mode = exclusive } execsql { INSERT INTO t2 VALUES('V', 'VI') } } {} do_test wal2-6.6.2 { execsql { PRAGMA locking_mode = normal } T filter xShmLock execsql { INSERT INTO t2 VALUES('VII', 'VIII') } |
︙ | ︙ | |||
755 756 757 758 759 760 761 | catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test wal2-6.6.2 { db2 close T filter {} execsql { INSERT INTO t2 VALUES('IX', 'X') } } {} | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test wal2-6.6.2 { db2 close T filter {} execsql { INSERT INTO t2 VALUES('IX', 'X') } } {} do_test wal2-6.6.4 { # This time, we have successfully exited exclusive mode. So the second # connection can read the database. sqlite3 db2 test.db -vfs T catchsql { SELECT * FROM t2 } db2 } {0 {I II III IV V VI VII VIII IX X}} db close |
︙ | ︙ | |||
1148 1149 1150 1151 1152 1153 1154 1155 1156 | do_test wal2-13.$tn.4 { catchsql { INSERT INTO t1 DEFAULT VALUES } } $b($can_read,$can_write) } catch { db close } } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | do_test wal2-13.$tn.4 { catchsql { INSERT INTO t1 DEFAULT VALUES } } $b($can_read,$can_write) } catch { db close } } } #------------------------------------------------------------------------- # Test that "PRAGMA checkpoint_fullsync" appears to be working. # foreach {tn sql reslist} { 1 { } {8 0 3 0 5 0} 2 { PRAGMA checkpoint_fullfsync = 1 } {8 4 3 2 5 2} 3 { PRAGMA checkpoint_fullfsync = 0 } {8 0 3 0 5 0} } { faultsim_delete_and_reopen execsql $sql do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal} set sqlite_sync_count 0 set sqlite_fullsync_count 0 do_execsql_test wal2-14.$tn.2 { PRAGMA wal_autocheckpoint = 10; CREATE TABLE t1(a, b); -- 2 wal syncs INSERT INTO t1 VALUES(1, 2); -- 1 wal sync PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync BEGIN; INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); COMMIT; -- 1 wal sync PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync } {10} do_test wal2-14.$tn.3 { list $sqlite_sync_count $sqlite_fullsync_count } [lrange $reslist 0 1] set sqlite_sync_count 0 set sqlite_fullsync_count 0 do_test wal2-14.$tn.4 { execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) } list $sqlite_sync_count $sqlite_fullsync_count } [lrange $reslist 2 3] set sqlite_sync_count 0 set sqlite_fullsync_count 0 do_test wal2-14.$tn.5 { execsql { PRAGMA wal_autocheckpoint = 1000 } execsql { INSERT INTO t1 VALUES(9, 10) } execsql { INSERT INTO t1 VALUES(11, 12) } execsql { INSERT INTO t1 VALUES(13, 14) } db close list $sqlite_sync_count $sqlite_fullsync_count } [lrange $reslist 4 5] } finish_test |
Changes to test/wal3.test.
︙ | ︙ | |||
744 745 746 747 748 749 750 | do_test wal3-9.4 { db49 close execsql { PRAGMA wal_checkpoint } set sz2 [file size test.db] byte_is_zero test.db [expr $sz-1024] } {0} | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > | 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 | do_test wal3-9.4 { db49 close execsql { PRAGMA wal_checkpoint } set sz2 [file size test.db] byte_is_zero test.db [expr $sz-1024] } {0} do_multiclient_test tn { do_test wal3-10.$tn.1 { sql1 { PRAGMA page_size = 1024; CREATE TABLE t1(x); PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 100000; BEGIN; INSERT INTO t1 VALUES(randomblob(800)); INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 16 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 32 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 64 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 128 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 256 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 512 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 1024 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 2048 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 4096 INSERT INTO t1 SELECT randomblob(800) FROM t1; -- 8192 COMMIT; CREATE INDEX i1 ON t1(x); } expr {[file size test.db-wal] > [expr 1032*9000]} } 1 do_test wal3-10.$tn.2 { sql2 {PRAGMA integrity_check} } {ok} } finish_test |
Changes to test/walfault.test.
︙ | ︙ | |||
446 447 448 449 450 451 452 453 454 | set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test simple recovery, reading and writing a database file using a # heap-memory wal-index. # do_test walfault-13-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close file delete sv_test.db-shm } {} do_faultsim_test walfault-13.1 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA locking_mode = exclusive } db eval { SELECT count(*) FROM abc } } -test { faultsim_test_result {0 2} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check } do_faultsim_test walfault-13.2 -prep { faultsim_restore_and_reopen db eval { PRAGMA locking_mode = exclusive } } -body { db eval { PRAGMA journal_mode = delete } } -test { faultsim_test_result {0 delete} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check } do_test walfault-13-pre-2 { faultsim_delete_and_reopen execsql { BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-13.3 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; INSERT INTO abc VALUES(randomblob(1500)); } } -test { faultsim_test_result {0 {exclusive wal}} if {[file exists test.db-shm]} { error "Not using heap-memory mode" } faultsim_integrity_check set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } } #------------------------------------------------------------------------- # Test fault-handling when wrapping around to the start of a WAL file. # do_test walfault-14-pre { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-14 -prep { faultsim_restore_and_reopen } -body { db eval { PRAGMA wal_checkpoint; INSERT INTO abc VALUES(randomblob(1500)); } } -test { faultsim_test_result {0 {}} faultsim_integrity_check set nRow [db eval {SELECT count(*) FROM abc}] if {!(($nRow==2 && $testrc) || $nRow==3)} { error "Bad db content" } } finish_test |
Changes to test/walmode.test.
︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 | execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 3 4 memory} do_test walmode-5.2.1 { sqlite3 db "" execsql { PRAGMA main.journal_mode } | > > > > > | | | | < < < | < < | | 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 | execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA main.journal_mode; } } {1 2 3 4 memory} if {$TEMP_STORE>=2} { set tempJrnlMode memory } else { set tempJrnlMode delete } do_test walmode-5.2.1 { sqlite3 db "" execsql { PRAGMA main.journal_mode } } $tempJrnlMode do_test walmode-5.2.2 { execsql { PRAGMA main.journal_mode = wal } } $tempJrnlMode do_test walmode-5.2.3 { execsql { BEGIN; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); COMMIT; SELECT * FROM t1; PRAGMA main.journal_mode; } } [list 1 2 $tempJrnlMode] do_test walmode-5.2.4 { execsql { PRAGMA main.journal_mode = wal } } $tempJrnlMode do_test walmode-5.2.5 { execsql { INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; PRAGMA main.journal_mode; } } [list 1 2 3 4 $tempJrnlMode] do_test walmode-5.3.1 { sqlite3 db test.db execsql { PRAGMA temp.journal_mode } } $tempJrnlMode do_test walmode-5.3.2 { execsql { PRAGMA temp.journal_mode = wal } } $tempJrnlMode |
︙ | ︙ |
Added test/walnoshm.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 | # 2010 November 1 # # 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 regression tests for SQLite library. The # focus of this file is testing that WAL databases may be accessed without # using the xShm primitives if the connection is in exclusive-mode. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix walnoshm ifcapable !wal {finish_test ; return } db close testvfs tvfsshm testvfs tvfs -default 1 -iversion 1 sqlite3 db test.db #-------------------------------------------------------------------------- # Test that when using a version 1 VFS, a database can only be converted # to WAL mode after setting locking_mode=EXCLUSIVE. Also, test that if a # WAL database is opened using heap-memory for the WAL index, the connection # cannot change back to locking_mode=NORMAL while the database is still in # WAL mode. # do_execsql_test 1.1 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 2); } do_execsql_test 1.2 { PRAGMA journal_mode = WAL; SELECT * FROM t1; } {delete 1 2} do_test 1.3 { file exists test.db-wal } {0} do_execsql_test 1.4 { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; SELECT * FROM t1; } {exclusive wal 1 2} do_test 1.5 { file exists test.db-wal } {1} do_execsql_test 1.6 { INSERT INTO t1 VALUES(3, 4) } do_execsql_test 1.7 { PRAGMA locking_mode = normal; } {exclusive} do_execsql_test 1.8 { PRAGMA journal_mode = delete; PRAGMA main.locking_mode; } {delete exclusive} do_execsql_test 1.9 { PRAGMA locking_mode = normal; } {normal} do_execsql_test 1.10 { SELECT * FROM t1; } {1 2 3 4} do_test 1.11 { file exists test.db-wal } {0} #------------------------------------------------------------------------- # # 2.1.*: Test that a connection using a version 1 VFS can open a WAL database # and convert it to rollback mode if it is set to use # locking_mode=exclusive. # # 2.2.*: Test that if the exclusive lock cannot be obtained while attempting # the above, the operation fails and the WAL file is not opened. # do_execsql_test 2.1.1 { CREATE TABLE t2(x, y); INSERT INTO t2 VALUES('a', 'b'); INSERT INTO t2 VALUES('c', 'd'); } do_execsql_test 2.1.2 { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = WAL; INSERT INTO t2 VALUES('e', 'f'); INSERT INTO t2 VALUES('g', 'h'); } {exclusive wal} do_test 2.1.3 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db2 test2.db catchsql { SELECT * FROM t2 } db2 } {1 {unable to open database file}} do_test 2.1.4 { catchsql { PRAGMA journal_mode = delete } db2 } {1 {unable to open database file}} do_test 2.1.5 { execsql { PRAGMA locking_mode = exclusive; PRAGMA journal_mode = delete; SELECT * FROM t2; } db2 } {exclusive delete a b c d e f g h} do_test 2.2.1 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3 db3 test2.db -vfs tvfsshm sqlite3 db2 test2.db execsql { SELECT * FROM t2 } db3 } {a b c d e f g h} do_test 2.2.2 { execsql { PRAGMA locking_mode = exclusive } db2 catchsql { PRAGMA journal_mode = delete } db2 } {1 {database is locked}} do_test 2.2.3 { # This is to test that [db2] is not holding a PENDING lock (which can # happen when an attempt to obtain an EXCLUSIVE lock fails). sqlite3 db4 test2.db -vfs tvfsshm execsql { SELECT * FROM t2 } db4 } {a b c d e f g h} do_test 2.2.4 { catchsql { SELECT * FROM t2 } db2 } {1 {database is locked}} do_test 2.2.5 { db4 close sqlite3 db4 test2.db -vfs tvfsshm execsql { SELECT * FROM t2 } db4 } {a b c d e f g h} do_test 2.2.6 { db3 close db4 close execsql { SELECT * FROM t2 } db2 } {a b c d e f g h} db2 close db close #------------------------------------------------------------------------- # # 3.1: Test that if locking_mode=EXCLUSIVE is set after the wal file is # opened, it is possible to drop back to locking_mode=NORMAL. # # 3.2: Test that if locking_mode=EXCLUSIVE is set before the wal file is # opened, it is not. # do_test 3.1 { sqlite3 db test.db -vfs tvfsshm execsql { SELECT * FROM t1; PRAGMA locking_mode = EXCLUSIVE; INSERT INTO t1 VALUES(5, 6); PRAGMA locking_mode = NORMAL; INSERT INTO t1 VALUES(7, 8); } sqlite3 db2 test.db -vfs tvfsshm execsql { SELECT * FROM t1 } db2 } {1 2 3 4 5 6 7 8} db close db2 close do_test 3.2 { sqlite3 db test.db -vfs tvfsshm execsql { PRAGMA locking_mode = EXCLUSIVE; INSERT INTO t1 VALUES(9, 10); PRAGMA locking_mode = NORMAL; INSERT INTO t1 VALUES(11, 12); } sqlite3 db2 test.db -vfs tvfsshm catchsql { SELECT * FROM t1 } db2 } {1 {database is locked}} db close db2 close tvfs delete tvfsshm delete finish_test |
Changes to test/walshared.test.
︙ | ︙ | |||
13 14 15 16 17 18 19 | # "PRAGMA journal_mode=WAL" mode with shared-cache turned on. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !wal {finish_test ; return } | < < < < | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # "PRAGMA journal_mode=WAL" mode with shared-cache turned on. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !wal {finish_test ; return } db close set ::enable_shared_cache [sqlite3_enable_shared_cache 1] sqlite3 db test.db sqlite3 db2 test.db |
︙ | ︙ | |||
60 61 62 63 64 65 66 | execsql { PRAGMA integrity_check } db2 } {ok} sqlite3_enable_shared_cache $::enable_shared_cache finish_test | < | 56 57 58 59 60 61 62 | execsql { PRAGMA integrity_check } db2 } {ok} sqlite3_enable_shared_cache $::enable_shared_cache finish_test |
Changes to test/where3.test.
︙ | ︙ | |||
213 214 215 216 217 218 219 | } {tB {} tC * tA * tD *} # Ticket [13f033c865f878953] # If the outer loop must be a full table scan, do not let ANALYZE trick # the planner into use a table for the outer loop that might be indexable # if held until an inner loop. # | | < | | | | | < | > > > | < | < | | > > > | < | < | | | | | > > > > | < | > > > > > > > | | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > > > > > > | | | > > > > > > > > > | > > > > > > > > > > > > > | | 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 | } {tB {} tC * tA * tD *} # Ticket [13f033c865f878953] # If the outer loop must be a full table scan, do not let ANALYZE trick # the planner into use a table for the outer loop that might be indexable # if held until an inner loop. # do_execsql_test where3-3.0 { CREATE TABLE t301(a INTEGER PRIMARY KEY,b,c); CREATE INDEX t301c ON t301(c); INSERT INTO t301 VALUES(1,2,3); CREATE TABLE t302(x, y); ANALYZE; explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y; } { 0 0 0 {SCAN TABLE t302 (~0 rows)} 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } do_execsql_test where3-3.1 { explain query plan SELECT * FROM t301, t302 WHERE t302.x=5 AND t301.a=t302.y; } { 0 0 1 {SCAN TABLE t302 (~0 rows)} 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} } # Verify that when there are multiple tables in a join which must be # full table scans that the query planner attempts put the table with # the fewest number of output rows as the outer loop. # do_execsql_test where3-4.0 { CREATE TABLE t400(a INTEGER PRIMARY KEY, b, c); CREATE TABLE t401(p INTEGER PRIMARY KEY, q, r); CREATE TABLE t402(x INTEGER PRIMARY KEY, y, z); EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*'; } { 0 0 2 {SCAN TABLE t402 (~500000 rows)} 0 1 0 {SCAN TABLE t400 (~1000000 rows)} 0 2 1 {SCAN TABLE t401 (~1000000 rows)} } do_execsql_test where3-4.1 { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*'; } { 0 0 1 {SCAN TABLE t401 (~500000 rows)} 0 1 0 {SCAN TABLE t400 (~1000000 rows)} 0 2 2 {SCAN TABLE t402 (~1000000 rows)} } do_execsql_test where3-4.2 { EXPLAIN QUERY PLAN SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*'; } { 0 0 0 {SCAN TABLE t400 (~500000 rows)} 0 1 1 {SCAN TABLE t401 (~1000000 rows)} 0 2 2 {SCAN TABLE t402 (~1000000 rows)} } # Verify that a performance regression encountered by firefox # has been fixed. # do_execsql_test where3-5.0 { CREATE TABLE aaa (id INTEGER PRIMARY KEY, type INTEGER, fk INTEGER DEFAULT NULL, parent INTEGER, position INTEGER, title LONGVARCHAR, keyword_id INTEGER, folder_type TEXT, dateAdded INTEGER, lastModified INTEGER); CREATE INDEX aaa_111 ON aaa (fk, type); CREATE INDEX aaa_222 ON aaa (parent, position); CREATE INDEX aaa_333 ON aaa (fk, lastModified); CREATE TABLE bbb (id INTEGER PRIMARY KEY, type INTEGER, fk INTEGER DEFAULT NULL, parent INTEGER, position INTEGER, title LONGVARCHAR, keyword_id INTEGER, folder_type TEXT, dateAdded INTEGER, lastModified INTEGER); CREATE INDEX bbb_111 ON bbb (fk, type); CREATE INDEX bbb_222 ON bbb (parent, position); CREATE INDEX bbb_333 ON bbb (fk, lastModified); EXPLAIN QUERY PLAN SELECT bbb.title AS tag_title FROM aaa JOIN bbb ON bbb.id = aaa.parent WHERE aaa.fk = 'constant' AND LENGTH(bbb.title) > 0 AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} 0 1 1 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.1 { EXPLAIN QUERY PLAN SELECT bbb.title AS tag_title FROM aaa JOIN aaa AS bbb ON bbb.id = aaa.parent WHERE aaa.fk = 'constant' AND LENGTH(bbb.title) > 0 AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} 0 1 1 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.2 { EXPLAIN QUERY PLAN SELECT bbb.title AS tag_title FROM bbb JOIN aaa ON bbb.id = aaa.parent WHERE aaa.fk = 'constant' AND LENGTH(bbb.title) > 0 AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} 0 1 0 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } do_execsql_test where3-5.3 { EXPLAIN QUERY PLAN SELECT bbb.title AS tag_title FROM aaa AS bbb JOIN aaa ON bbb.id = aaa.parent WHERE aaa.fk = 'constant' AND LENGTH(bbb.title) > 0 AND bbb.parent = 4 ORDER BY bbb.title COLLATE NOCASE ASC; } { 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)} 0 1 0 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } finish_test |
Changes to test/where7.test.
︙ | ︙ | |||
23295 23296 23297 23298 23299 23300 23301 | OR a=23 OR (f GLOB '?defg*' AND f GLOB 'cdef*') OR d<0.0 OR (d>=22.0 AND d<23.0 AND d NOT NULL) OR a=91 } } {2 22 23 28 54 80 91 scan 0 sort 0} | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 23295 23296 23297 23298 23299 23300 23301 23302 23303 23304 23305 23306 23307 23308 23309 23310 23311 23312 23313 23314 23315 23316 23317 23318 23319 23320 23321 23322 23323 23324 23325 23326 23327 23328 23329 23330 23331 23332 23333 23334 23335 23336 23337 23338 23339 23340 23341 23342 23343 23344 23345 23346 23347 23348 23349 23350 | OR a=23 OR (f GLOB '?defg*' AND f GLOB 'cdef*') OR d<0.0 OR (d>=22.0 AND d<23.0 AND d NOT NULL) OR a=91 } } {2 22 23 28 54 80 91 scan 0 sort 0} # test case for the performance regression fixed by # check-in 28ba6255282b on 2010-10-21 02:05:06 # # The test case that follows is code from an actual # application with identifiers change and unused columns # remove. # do_execsql_test where7-3.1 { CREATE TABLE t301 ( c8 INTEGER PRIMARY KEY, c6 INTEGER, c4 INTEGER, c7 INTEGER, FOREIGN KEY (c4) REFERENCES series(c4) ); CREATE INDEX t301_c6 on t301(c6); CREATE INDEX t301_c4 on t301(c4); CREATE INDEX t301_c7 on t301(c7); CREATE TABLE t302 ( c1 INTEGER PRIMARY KEY, c8 INTEGER, c5 INTEGER, c3 INTEGER, c2 INTEGER, c4 INTEGER, FOREIGN KEY (c8) REFERENCES t301(c8) ); CREATE INDEX t302_c3 on t302(c3); CREATE INDEX t302_c8_c3 on t302(c8, c3); CREATE INDEX t302_c5 on t302(c5); EXPLAIN QUERY PLAN SELECT t302.c1 FROM t302 JOIN t301 ON t302.c8 = t301.c8 WHERE t302.c2 = 19571 AND t302.c3 > 1287603136 AND (t301.c4 = 1407449685622784 OR t301.c8 = 1407424651264000) ORDER BY t302.c5 LIMIT 200; } { 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)} 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } finish_test |
Changes to test/where9.test.
︙ | ︙ | |||
354 355 356 357 358 359 360 | WHERE t1.a=t3.y OR t1.b=t3.y*11 OR (t1.c=27027 AND round(t1.d)==80) ORDER BY 1, 2, 3 } } {1 80 2 1 80 28 1 80 54 1 80 80 2 80 2 2 80 28 2 80 54 2 80 80 scan 1 sort 1} ifcapable explain { | | < | | < | | | | < | < < > | < | | | | | | | < | < < > | 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 | WHERE t1.a=t3.y OR t1.b=t3.y*11 OR (t1.c=27027 AND round(t1.d)==80) ORDER BY 1, 2, 3 } } {1 80 2 1 80 28 1 80 54 1 80 80 2 80 2 2 80 28 2 80 54 2 80 80 scan 1 sort 1} ifcapable explain { do_execsql_test where9-3.1 { EXPLAIN QUERY PLAN SELECT t2.a FROM t1, t2 WHERE t1.a=80 AND ((t1.c=t2.c AND t1.d=t2.d) OR t1.f=t2.f) } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} } do_execsql_test where9-3.2 { EXPLAIN QUERY PLAN SELECT coalesce(t2.a,9999) FROM t1 LEFT JOIN t2 ON (t1.c+1=t2.c AND t1.d=t2.d) OR (t1.f||'x')=t2.f WHERE t1.a=80 } { 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)} 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)} } } # Make sure that INDEXED BY and multi-index OR clauses play well with # one another. # do_test where9-4.1 { count_steps { |
︙ | ︙ | |||
454 455 456 457 458 459 460 | } } {1 {cannot use index: t1d}} ifcapable explain { # The (c=31031 OR d IS NULL) clause is preferred over b>1000 because # the former is an equality test which is expected to return fewer rows. # | | < | < < < | | | < < | > | < | < < < | < | < < | > | < | < < < | | < < < > | 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 | } } {1 {cannot use index: t1d}} ifcapable explain { # The (c=31031 OR d IS NULL) clause is preferred over b>1000 because # the former is an equality test which is expected to return fewer rows. # do_execsql_test where9-5.1 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL) } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~10 rows)} 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~10 rows)} } # In contrast, b=1000 is preferred over any OR-clause. # do_execsql_test where9-5.2 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b=1000 AND (c=31031 OR d IS NULL) } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~5 rows)} } # Likewise, inequalities in an AND are preferred over inequalities in # an OR. # do_execsql_test where9-5.3 { EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c>=31031 OR d IS NULL) } { 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~165000 rows)} } } ############################################################################ # Make sure OR-clauses work correctly on UPDATE and DELETE statements. do_test where9-6.2.1 { db eval {SELECT count(*) FROM t1 UNION ALL SELECT a FROM t1 WHERE a>=85} |
︙ | ︙ |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
50 51 52 53 54 55 56 | puts $out [subst \ {/****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version $VERSION. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements | | | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | puts $out [subst \ {/****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version $VERSION. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start |
︙ | ︙ |
Changes to tool/mksqlite3h.tcl.
︙ | ︙ | |||
61 62 63 64 65 66 67 | close $in # Set up patterns for recognizing API declarations. # set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+sqlite3_[_a-zA-Z0-9]+(\[|;| =)} set declpattern {^ *[a-zA-Z][a-zA-Z_0-9 ]+ \**sqlite3_[_a-zA-Z0-9]+\(} | | > | | | | > > > > > | | | | | | | | | | | | | | | | | | | | | | > | 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 | close $in # Set up patterns for recognizing API declarations. # set varpattern {^[a-zA-Z][a-zA-Z_0-9 *]+sqlite3_[_a-zA-Z0-9]+(\[|;| =)} set declpattern {^ *[a-zA-Z][a-zA-Z_0-9 ]+ \**sqlite3_[_a-zA-Z0-9]+\(} # Process the src/sqlite.h.in ext/rtree/sqlite3rtree.h files. # foreach file [list $TOP/src/sqlite.h.in $TOP/ext/rtree/sqlite3rtree.h] { set in [open $file] while {![eof $in]} { set line [gets $in] # File sqlite3rtree.h contains a line "#include <sqlite3.h>". Omit this # line when copying sqlite3rtree.h into sqlite3.h. # if {[string match {*#include*<sqlite3.h>*} $line]} continue regsub -- --VERS-- $line $zVersion line regsub -- --VERSION-NUMBER-- $line $nVersion line regsub -- --SOURCE-ID-- $line "$zDate $zUuid" line if {[regexp {define SQLITE_EXTERN extern} $line]} { puts $line puts [gets $in] puts "" puts "#ifndef SQLITE_API" puts "# define SQLITE_API" puts "#endif" set line "" } if {([regexp $varpattern $line] && ![regexp {^ *typedef} $line]) || ([regexp $declpattern $line]) } { set line "SQLITE_API $line" } puts $line } close $in } |
Changes to tool/shell1.test.
︙ | ︙ | |||
196 197 198 199 200 201 202 | list $rc \ [regexp {Error: missing argument for option: -nullvalue} $res] } {1 1} # -version show SQLite version do_test shell1-1.16.1 { catchcmd "-version test.db" "" | | | 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | list $rc \ [regexp {Error: missing argument for option: -nullvalue} $res] } {1 1} # -version show SQLite version do_test shell1-1.16.1 { catchcmd "-version test.db" "" } {0 3.7.3} #---------------------------------------------------------------------------- # Test cases shell1-2.*: Basic "dot" command token parsing. # # check first token handling do_test shell1-2.1.1 { |
︙ | ︙ |
Changes to tool/showjournal.c.
1 2 3 4 5 | /* ** A utility for printing an SQLite database journal. */ #include <stdio.h> #include <ctype.h> | < < | < | | > > | | > > | > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > | | | > > > | > > > > | > | < | > | | > > > > > > > | > > > > > > > > > > > > > > > > > | < | > > > > > > < < | | | | | < | | | > | > > > | < < | | < | < | < > | | 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 | /* ** A utility for printing an SQLite database journal. */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> /* ** state information */ static int pageSize = 1024; static int sectorSize = 512; static FILE *db = 0; static int showPageContent = 0; static int fileSize = 0; static unsigned cksumNonce = 0; /* Report a memory allocation error */ static void out_of_memory(void){ fprintf(stderr,"Out of memory...\n"); exit(1); } /* ** Read N bytes of memory starting at iOfst into space obtained ** from malloc(). */ static char *read_content(int N, int iOfst){ int got; char *pBuf = malloc(N); if( pBuf==0 ) out_of_memory(); fseek(db, iOfst, SEEK_SET); got = fread(pBuf, 1, N, db); if( got<0 ){ fprintf(stderr, "I/O error reading %d bytes from %d\n", N, iOfst); memset(pBuf, 0, N); }else if( got<N ){ fprintf(stderr, "Short read: got only %d of %d bytes from %d\n", got, N, iOfst); memset(&pBuf[got], 0, N-got); } return pBuf; } /* Print a line of decode output showing a 4-byte integer. */ static unsigned print_decode_line( unsigned char *aData, /* Content being decoded */ int ofst, int nByte, /* Start and size of decode */ const char *zMsg /* Message to append */ ){ int i, j; unsigned val = aData[ofst]; char zBuf[100]; sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); i = strlen(zBuf); for(j=1; j<4; j++){ if( j>=nByte ){ sprintf(&zBuf[i], " "); }else{ sprintf(&zBuf[i], " %02x", aData[ofst+j]); val = val*256 + aData[ofst+j]; } i += strlen(&zBuf[i]); } sprintf(&zBuf[i], " %10u", val); printf("%s %s\n", zBuf, zMsg); return val; } /* ** Read and print a journal header. Store key information (page size, etc) ** in global variables. */ static unsigned decode_journal_header(int iOfst){ char *pHdr = read_content(64, iOfst); unsigned nPage; printf("Header at offset %d:\n", iOfst); print_decode_line(pHdr, 0, 4, "Header part 1 (3654616569)"); print_decode_line(pHdr, 4, 4, "Header part 2 (547447767)"); nPage = print_decode_line(pHdr, 8, 4, "page count"); cksumNonce = print_decode_line(pHdr, 12, 4, "chksum nonce"); print_decode_line(pHdr, 16, 4, "initial database size in pages"); sectorSize = print_decode_line(pHdr, 20, 4, "sector size"); pageSize = print_decode_line(pHdr, 24, 4, "page size"); print_decode_line(pHdr, 28, 4, "zero"); print_decode_line(pHdr, 32, 4, "zero"); print_decode_line(pHdr, 36, 4, "zero"); print_decode_line(pHdr, 40, 4, "zero"); free(pHdr); return nPage; } static void print_page(int iOfst){ unsigned char *aData; char zTitle[50]; aData = read_content(pageSize+8, iOfst); sprintf(zTitle, "page number for page at offset %d", iOfst); print_decode_line(aData, 0, 4, zTitle); free(aData); } int main(int argc, char **argv){ int rc; int nPage, cnt; int iOfst; if( argc!=2 ){ fprintf(stderr,"Usage: %s FILENAME\n", argv[0]); exit(1); } db = fopen(argv[1], "rb"); if( db==0 ){ fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); exit(1); } fseek(db, 0, SEEK_END); fileSize = ftell(db); printf("journal file size: %d bytes\n", fileSize); fseek(db, 0, SEEK_SET); iOfst = 0; while( iOfst<fileSize ){ cnt = nPage = (int)decode_journal_header(iOfst); if( cnt==0 ){ cnt = (fileSize - sectorSize)/(pageSize+8); } iOfst += sectorSize; while( cnt && iOfst<fileSize ){ print_page(iOfst); iOfst += pageSize+8; } iOfst = (iOfst/sectorSize + 1)*sectorSize; } fclose(db); } |