Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge in the latest changes from trunk. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | apple-osx |
Files: | files | file ages | folders |
SHA1: |
6d78a25ddce75f63581be60c34dbc75f |
User & Date: | drh 2011-04-04 13:11:07.058 |
Context
2011-04-05
| ||
13:38 | Pull the latest changes from trunk (and hence from schema-parse-refactor) into the apple-osx branch. (check-in: 8e885ddea0 user: drh tags: apple-osx) | |
2011-04-04
| ||
13:11 | Merge in the latest changes from trunk. (check-in: 6d78a25ddc user: drh tags: apple-osx) | |
12:29 | Move the expired-statement test for OP_Function until after all memory has been freed. The test is still commented out, however. (check-in: 425e3edb14 user: drh tags: trunk) | |
2011-02-10
| ||
01:49 | This is a version of the SQLite 3.7.5 release with Apple's changes for MacOS. (check-in: 55d2e55b7b user: drh tags: apple-osx) | |
Changes
Changes to Makefile.in.
︙ | ︙ | |||
161 162 163 164 165 166 167 | # 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 \ | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | # 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_aux.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 \ |
︙ | ︙ | |||
302 303 304 305 306 307 308 309 310 311 312 313 314 315 | $(TOP)/ext/fts2/fts2_tokenizer.h \ $(TOP)/ext/fts2/fts2_tokenizer.c \ $(TOP)/ext/fts2/fts2_tokenizer1.c SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_icu.c \ $(TOP)/ext/fts3/fts3_porter.c \ $(TOP)/ext/fts3/fts3_snippet.c \ $(TOP)/ext/fts3/fts3_tokenizer.h \ | > | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | $(TOP)/ext/fts2/fts2_tokenizer.h \ $(TOP)/ext/fts2/fts2_tokenizer.c \ $(TOP)/ext/fts2/fts2_tokenizer1.c SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_icu.c \ $(TOP)/ext/fts3/fts3_porter.c \ $(TOP)/ext/fts3/fts3_snippet.c \ $(TOP)/ext/fts3/fts3_tokenizer.h \ |
︙ | ︙ | |||
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 | $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(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_superlock.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 # Source code to the library files needed by the test fixture # TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ | > > > | 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 | $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_fuzzer.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_superlock.c \ $(TOP)/src/test_syscall.c \ $(TOP)/src/test_stat.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c # Source code to the library files needed by the test fixture # TESTSRC2 = \ $(TOP)/src/attach.c \ $(TOP)/src/backup.c \ |
︙ | ︙ | |||
413 414 415 416 417 418 419 420 421 422 423 424 425 426 | $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # | > | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/vdbetrace.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # |
︙ | ︙ | |||
821 822 823 824 825 826 827 828 829 830 831 832 833 834 | fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c fts3_expr.lo: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c fts3_hash.lo: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c fts3_icu.lo: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR) | > > > | 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 | fts2_tokenizer1.lo: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c fts3.lo: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c fts3_aux.lo: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c fts3_expr.lo: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c fts3_hash.lo: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) $(LTCOMPILE) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c fts3_icu.lo: $(TOP)/ext/fts3/fts3_icu.c $(HDR) $(EXTHDR) |
︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 3.7.6 |
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.6. # # 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.6' PACKAGE_STRING='sqlite 3.7.6' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include <stdio.h> #ifdef HAVE_SYS_TYPES_H # include <sys/types.h> |
︙ | ︙ | |||
1481 1482 1483 1484 1485 1486 1487 | # # 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 | | | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 | # # 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.6 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. |
︙ | ︙ | |||
1546 1547 1548 1549 1550 1551 1552 | --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 | | | 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 | --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.6:";; 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] |
︙ | ︙ | |||
1662 1663 1664 1665 1666 1667 1668 | cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF | | | | 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 | 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.6 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.6, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { |
︙ | ︙ | |||
13938 13939 13940 13941 13942 13943 13944 | 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=" | | | 13938 13939 13940 13941 13942 13943 13944 13945 13946 13947 13948 13949 13950 13951 13952 | 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.6, 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 $@ |
︙ | ︙ | |||
13991 13992 13993 13994 13995 13996 13997 | $config_commands Report bugs to <bug-autoconf@gnu.org>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ | | | 13991 13992 13993 13994 13995 13996 13997 13998 13999 14000 14001 14002 14003 14004 14005 | $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.6 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/fts3/fts3.c.
︙ | ︙ | |||
444 445 446 447 448 449 450 451 452 453 454 455 456 457 | 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; } | > > | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | 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); sqlite3_free(p->zReadExprlist); sqlite3_free(p->zWriteExprlist); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); sqlite3_free(p); return SQLITE_OK; } |
︙ | ︙ | |||
660 661 662 663 664 665 666 667 668 669 670 671 672 673 | 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: ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | zValue = sqlite3_mprintf("%s", &zCsr[1]); if( zValue ){ sqlite3Fts3Dequote(zValue); } *pzValue = zValue; return 1; } /* ** Append the output of a printf() style formatting to an existing string. */ static void fts3Appendf( int *pRc, /* IN/OUT: Error code */ char **pz, /* IN/OUT: Pointer to string buffer */ const char *zFormat, /* Printf format string to append */ ... /* Arguments for printf format string */ ){ if( *pRc==SQLITE_OK ){ va_list ap; char *z; va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); if( z && *pz ){ char *z2 = sqlite3_mprintf("%s%s", *pz, z); sqlite3_free(z); z = z2; } if( z==0 ) *pRc = SQLITE_NOMEM; sqlite3_free(*pz); *pz = z; } } /* ** Return a copy of input string zInput enclosed in double-quotes (") and ** with all double quote characters escaped. For example: ** ** fts3QuoteId("un \"zip\"") -> "un \"\"zip\"\"" ** ** The pointer returned points to memory obtained from sqlite3_malloc(). It ** is the callers responsibility to call sqlite3_free() to release this ** memory. */ static char *fts3QuoteId(char const *zInput){ int nRet; char *zRet; nRet = 2 + strlen(zInput)*2 + 1; zRet = sqlite3_malloc(nRet); if( zRet ){ int i; char *z = zRet; *(z++) = '"'; for(i=0; zInput[i]; i++){ if( zInput[i]=='"' ) *(z++) = '"'; *(z++) = zInput[i]; } *(z++) = '"'; *(z++) = '\0'; } return zRet; } /* ** Return a list of comma separated SQL expressions that could be used ** in a SELECT statement such as the following: ** ** SELECT <list of expressions> FROM %_content AS x ... ** ** to return the docid, followed by each column of text data in order ** from left to write. If parameter zFunc is not NULL, then instead of ** being returned directly each column of text data is passed to an SQL ** function named zFunc first. For example, if zFunc is "unzip" and the ** table has the three user-defined columns "a", "b", and "c", the following ** string is returned: ** ** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')" ** ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It ** is the responsibility of the caller to eventually free it. ** ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and ** a NULL pointer is returned). Otherwise, if an OOM error is encountered ** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If ** no error occurs, *pRc is left unmodified. */ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){ char *zRet = 0; char *zFree = 0; char *zFunction; int i; if( !zFunc ){ zFunction = ""; }else{ zFree = zFunction = fts3QuoteId(zFunc); } fts3Appendf(pRc, &zRet, "docid"); for(i=0; i<p->nColumn; i++){ fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]); } sqlite3_free(zFree); return zRet; } /* ** Return a list of N comma separated question marks, where N is the number ** of columns in the %_content table (one for the docid plus one for each ** user-defined text column). ** ** If argument zFunc is not NULL, then all but the first question mark ** is preceded by zFunc and an open bracket, and followed by a closed ** bracket. For example, if zFunc is "zip" and the FTS3 table has three ** user-defined text columns, the following string is returned: ** ** "?, zip(?), zip(?), zip(?)" ** ** The pointer returned points to a buffer allocated by sqlite3_malloc(). It ** is the responsibility of the caller to eventually free it. ** ** If *pRc is not SQLITE_OK when this function is called, it is a no-op (and ** a NULL pointer is returned). Otherwise, if an OOM error is encountered ** by this function, NULL is returned and *pRc is set to SQLITE_NOMEM. If ** no error occurs, *pRc is left unmodified. */ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){ char *zRet = 0; char *zFree = 0; char *zFunction; int i; if( !zFunc ){ zFunction = ""; }else{ zFree = zFunction = fts3QuoteId(zFunc); } fts3Appendf(pRc, &zRet, "?"); for(i=0; i<p->nColumn; i++){ fts3Appendf(pRc, &zRet, ",%s(?)", zFunction); } sqlite3_free(zFree); return zRet; } /* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. ** ** The argv[] array contains the following: ** |
︙ | ︙ | |||
697 698 699 700 701 702 703 704 705 706 707 708 709 710 | 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; | > > > | 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 | 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 */ char *zCompress = 0; char *zUncompress = 0; 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; |
︙ | ︙ | |||
747 748 749 750 751 752 753 754 755 756 757 758 759 760 | 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); } | > > > > > > | 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 | 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 if( nKey==8 && 0==sqlite3_strnicmp(z, "compress", 8) ){ zCompress = zVal; zVal = 0; }else if( nKey==10 && 0==sqlite3_strnicmp(z, "uncompress", 10) ){ zUncompress = zVal; zVal = 0; }else{ *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z); rc = SQLITE_ERROR; } sqlite3_free(zVal); } |
︙ | ︙ | |||
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 | 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: | > > > > > > > > > | > | 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 | memcpy(zCsr, z, n); zCsr[n] = '\0'; sqlite3Fts3Dequote(zCsr); p->azColumn[iCol] = zCsr; zCsr += n+1; assert( zCsr <= &((char *)p)[nByte] ); } if( (zCompress==0)!=(zUncompress==0) ){ char const *zMiss = (zCompress==0 ? "compress" : "uncompress"); rc = SQLITE_ERROR; *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss); } p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc); p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc); if( rc!=SQLITE_OK ) goto fts3_init_out; /* 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(zCompress); sqlite3_free(zUncompress); sqlite3_free((void *)aCol); if( rc!=SQLITE_OK ){ if( p ){ fts3DisconnectMethod((sqlite3_vtab *)p); }else if( pTokenizer ){ pTokenizer->pModule->xDestroy(pTokenizer); } |
︙ | ︙ | |||
1931 1932 1933 1934 1935 1936 1937 | if( !*ppOut ) return SQLITE_NOMEM; sqlite3Fts3PutVarint(*ppOut, docid); } return SQLITE_OK; } | < < < < | | > > | > > | > > > > > | | > > > > > > > > > | > > > | > | < | | > > | | < > | < | | > > > > > > > > > > > > | > > | > | | > > > > > | > > > > > > > | | < | < > | | | < > < < < | < < | > > > > < | > | | < < | < < < | < < < < | | < < < < < < | < < < | | < | < < | > < < < < < < < < < | < < < < | < < < | < < | < < < < < < < | | > > > > > | 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 | if( !*ppOut ) return SQLITE_NOMEM; sqlite3Fts3PutVarint(*ppOut, docid); } return SQLITE_OK; } int sqlite3Fts3SegReaderCursor( Fts3Table *p, /* FTS3 table handle */ int iLevel, /* Level of segments to scan */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ int isPrefix, /* True for a prefix search */ int isScan, /* True to scan from zTerm to EOF */ Fts3SegReaderCursor *pCsr /* Cursor object to populate */ ){ int rc = SQLITE_OK; int rc2; int iAge = 0; sqlite3_stmt *pStmt = 0; Fts3SegReader *pPending = 0; assert( iLevel==FTS3_SEGCURSOR_ALL || iLevel==FTS3_SEGCURSOR_PENDING || iLevel>=0 ); assert( FTS3_SEGCURSOR_PENDING<0 ); assert( FTS3_SEGCURSOR_ALL<0 ); assert( iLevel==FTS3_SEGCURSOR_ALL || (zTerm==0 && isPrefix==1) ); assert( isPrefix==0 || isScan==0 ); memset(pCsr, 0, sizeof(Fts3SegReaderCursor)); /* If iLevel is less than 0, include a seg-reader for the pending-terms. */ assert( isScan==0 || fts3HashCount(&p->pendingTerms)==0 ); if( iLevel<0 && isScan==0 ){ rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &pPending); if( rc==SQLITE_OK && pPending ){ int nByte = (sizeof(Fts3SegReader *) * 16); pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); if( pCsr->apSegment==0 ){ rc = SQLITE_NOMEM; }else{ pCsr->apSegment[0] = pPending; pCsr->nSegment = 1; pPending = 0; } } } if( iLevel!=FTS3_SEGCURSOR_PENDING ){ if( rc==SQLITE_OK ){ rc = sqlite3Fts3AllSegdirs(p, iLevel, &pStmt); } while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ /* Read the values returned by the SELECT into local variables. */ sqlite3_int64 iStartBlock = sqlite3_column_int64(pStmt, 1); sqlite3_int64 iLeavesEndBlock = sqlite3_column_int64(pStmt, 2); sqlite3_int64 iEndBlock = sqlite3_column_int64(pStmt, 3); int nRoot = sqlite3_column_bytes(pStmt, 4); char const *zRoot = sqlite3_column_blob(pStmt, 4); /* If nSegment is a multiple of 16 the array needs to be extended. */ if( (pCsr->nSegment%16)==0 ){ Fts3SegReader **apNew; int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); if( !apNew ){ rc = SQLITE_NOMEM; goto finished; } pCsr->apSegment = apNew; } /* If zTerm is not NULL, and this segment is not stored entirely on its ** root node, the range of leaves scanned can be reduced. Do this. */ if( iStartBlock && zTerm ){ sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); if( rc!=SQLITE_OK ) goto finished; if( isPrefix==0 && isScan==0 ) iLeavesEndBlock = iStartBlock; } rc = sqlite3Fts3SegReaderNew(iAge, iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pCsr->apSegment[pCsr->nSegment] ); if( rc!=SQLITE_OK ) goto finished; pCsr->nSegment++; iAge++; } } finished: rc2 = sqlite3_reset(pStmt); if( rc==SQLITE_DONE ) rc = rc2; sqlite3Fts3SegReaderFree(pPending); return rc; } static int fts3TermSegReaderCursor( 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 */ Fts3SegReaderCursor **ppSegcsr /* OUT: Allocated seg-reader cursor */ ){ Fts3SegReaderCursor *pSegcsr; /* Object to allocate and return */ int rc = SQLITE_NOMEM; /* Return code */ pSegcsr = sqlite3_malloc(sizeof(Fts3SegReaderCursor)); if( pSegcsr ){ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; int i; int nCost = 0; rc = sqlite3Fts3SegReaderCursor( p, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr); for(i=0; rc==SQLITE_OK && i<pSegcsr->nSegment; i++){ rc = sqlite3Fts3SegReaderCost(pCsr, pSegcsr->apSegment[i], &nCost); } pSegcsr->nCost = nCost; } *ppSegcsr = pSegcsr; return rc; } static void fts3SegReaderCursorFree(Fts3SegReaderCursor *pSegcsr){ sqlite3Fts3SegReaderFinish(pSegcsr); sqlite3_free(pSegcsr); } /* ** 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 |
︙ | ︙ | |||
2077 2078 2079 2080 2081 2082 2083 | 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 */ | | | | | | > > > | > | > > < | | | 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 | 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 */ Fts3SegReaderCursor *pSegcsr; /* Seg-reader cursor for this term */ TermSelect tsc; /* Context object for fts3TermSelectCb() */ Fts3SegFilter filter; /* Segment term filter configuration */ pSegcsr = pTok->pSegcsr; 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 = sqlite3Fts3SegReaderStart(p, pSegcsr, &filter); while( SQLITE_OK==rc && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pSegcsr)) ){ rc = fts3TermSelectCb(p, (void *)&tsc, pSegcsr->zTerm, pSegcsr->nTerm, pSegcsr->aDoclist, pSegcsr->nDoclist ); } 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]); } } fts3SegReaderCursorFree(pSegcsr); pTok->pSegcsr = 0; return rc; } /* ** This function counts the total number of docids in the doclist stored ** in buffer aList[], size nList bytes. ** |
︙ | ︙ | |||
2234 2235 2236 2237 2238 2239 2240 | /* 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]; | | | | | 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 | /* 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->pSegcsr==0 ){ if( (pCsr->eEvalmode==FTS3_EVAL_FILTER) || (pCsr->eEvalmode==FTS3_EVAL_NEXT && pCsr->pDeferred==0) || (pCsr->eEvalmode==FTS3_EVAL_MATCHINFO && pTok->bFulltext) ){ rc = fts3TermSegReaderCursor( pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr ); if( rc!=SQLITE_OK ) return rc; } } } for(ii=0; ii<pPhrase->nToken; ii++){ |
︙ | ︙ | |||
2271 2272 2273 2274 2275 2276 2277 | 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++){ | | | | | | | 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 2473 2474 2475 2476 | 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++){ Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[jj].pSegcsr; if( pSegcsr && pSegcsr->nCost<nMinCost ){ iTok = jj; nMinCost = pSegcsr->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{ if( pTok->pSegcsr ){ rc = fts3TermSelect(p, pTok, iCol, isTermPos, &nList, &pList); } pTok->bFulltext = 1; } assert( rc!=SQLITE_OK || pCsr->eEvalmode || pTok->pSegcsr==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); |
︙ | ︙ | |||
2476 2477 2478 2479 2480 2481 2482 | 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]; | | | | | 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 | 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->pSegcsr==0 ){ rc = fts3TermSegReaderCursor( pCsr, pTok->z, pTok->n, pTok->isPrefix, &pTok->pSegcsr ); } } }else{ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pLeft, pnExpr); if( rc==SQLITE_OK ){ rc = fts3ExprAllocateSegReaders(pCsr, pExpr->pRight, pnExpr); |
︙ | ︙ | |||
2502 2503 2504 2505 2506 2507 2508 | */ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ int kk; for(kk=0; kk<pPhrase->nToken; kk++){ | | | | < | < | 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 | */ static void fts3ExprFreeSegReaders(Fts3Expr *pExpr){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase ){ int kk; for(kk=0; kk<pPhrase->nToken; kk++){ fts3SegReaderCursorFree(pPhrase->aToken[kk].pSegcsr); pPhrase->aToken[kk].pSegcsr = 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++){ Fts3SegReaderCursor *pSegcsr = pPhrase->aToken[ii].pSegcsr; if( pSegcsr ) nCost += pSegcsr->nCost; } }else{ nCost = fts3ExprCost(pExpr->pLeft) + fts3ExprCost(pExpr->pRight); } return nCost; } |
︙ | ︙ | |||
2868 2869 2870 2871 2872 2873 2874 | 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 */ ){ const char *azSql[] = { | | | | 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 | 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 */ ){ const char *azSql[] = { "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?", /* non-full-scan */ "SELECT %s FROM %Q.'%q_content' AS x ", /* full-scan */ }; int rc; /* Return code */ char *zSql; /* SQL statement used to access %_content */ Fts3Table *p = (Fts3Table *)pCursor->pVtab; Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; UNUSED_PARAMETER(idxStr); |
︙ | ︙ | |||
2924 2925 2926 2927 2928 2929 2930 | } /* 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. */ | | > | 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 | } /* 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 = (char *)azSql[idxNum==FTS3_FULLSCAN_SEARCH]; zSql = sqlite3_mprintf(zSql, p->zReadExprlist, 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 ){ |
︙ | ︙ | |||
3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 | const sqlite3_tokenizer_module *pPorter = 0; #ifdef SQLITE_ENABLE_ICU const sqlite3_tokenizer_module *pIcu = 0; sqlite3Fts3IcuTokenizerModule(&pIcu); #endif sqlite3Fts3SimpleTokenizerModule(&pSimple); sqlite3Fts3PorterTokenizerModule(&pPorter); /* Allocate and initialise the hash-table used to store tokenizers. */ pHash = sqlite3_malloc(sizeof(Fts3Hash)); if( !pHash ){ rc = SQLITE_NOMEM; | > > > | 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 | const sqlite3_tokenizer_module *pPorter = 0; #ifdef SQLITE_ENABLE_ICU const sqlite3_tokenizer_module *pIcu = 0; sqlite3Fts3IcuTokenizerModule(&pIcu); #endif rc = sqlite3Fts3InitAux(db); if( rc!=SQLITE_OK ) return rc; sqlite3Fts3SimpleTokenizerModule(&pSimple); sqlite3Fts3PorterTokenizerModule(&pPorter); /* Allocate and initialise the hash-table used to store tokenizers. */ pHash = sqlite3_malloc(sizeof(Fts3Hash)); if( !pHash ){ rc = SQLITE_NOMEM; |
︙ | ︙ |
Changes to ext/fts3/fts3Int.h.
︙ | ︙ | |||
103 104 105 106 107 108 109 | typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3PhraseToken Fts3PhraseToken; typedef struct Fts3SegFilter Fts3SegFilter; typedef struct Fts3DeferredToken Fts3DeferredToken; typedef struct Fts3SegReader Fts3SegReader; | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | 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 Fts3SegReaderCursor Fts3SegReaderCursor; /* ** 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. |
︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 136 137 138 139 | 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 */ | > > > | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 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]; char *zReadExprlist; char *zWriteExprlist; 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 */ |
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** 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 */ | | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | ** 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 */ Fts3SegReaderCursor *pSegcsr; /* 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 */ |
︙ | ︙ | |||
281 282 283 284 285 286 287 | int sqlite3Fts3PendingTermsFlush(Fts3Table *); void sqlite3Fts3PendingTermsClear(Fts3Table *); int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3SegReader *); | < < < < | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | 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 | int sqlite3Fts3PendingTermsFlush(Fts3Table *); void sqlite3Fts3PendingTermsClear(Fts3Table *); int sqlite3Fts3Optimize(Fts3Table *); int sqlite3Fts3SegReaderNew(int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); void sqlite3Fts3SegReaderFree(Fts3SegReader *); int sqlite3Fts3SegReaderCost(Fts3Cursor *, Fts3SegReader *, int *); int sqlite3Fts3AllSegdirs(Fts3Table*, int, sqlite3_stmt **); int sqlite3Fts3ReadLock(Fts3Table *); int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*); int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **); int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **); void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *); int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int); int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *); void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *); char *sqlite3Fts3DeferredDoclist(Fts3DeferredToken *, int *); void sqlite3Fts3SegmentsClose(Fts3Table *); #define FTS3_SEGCURSOR_PENDING -1 #define FTS3_SEGCURSOR_ALL -2 int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3SegReaderCursor*, Fts3SegFilter*); int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3SegReaderCursor *); void sqlite3Fts3SegReaderFinish(Fts3SegReaderCursor *); int sqlite3Fts3SegReaderCursor( Fts3Table *, int, const char *, int, int, int, Fts3SegReaderCursor *); /* 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 #define FTS3_SEGMENT_SCAN 0x00000010 /* Type passed as 4th argument to SegmentReaderIterate() */ struct Fts3SegFilter { const char *zTerm; int nTerm; int iCol; int flags; }; struct Fts3SegReaderCursor { /* Used internally by sqlite3Fts3SegReaderXXX() calls */ Fts3SegReader **apSegment; /* Array of Fts3SegReader objects */ int nSegment; /* Size of apSegment array */ int nAdvance; /* How many seg-readers to advance */ Fts3SegFilter *pFilter; /* Pointer to filter object */ char *aBuffer; /* Buffer to merge doclists in */ int nBuffer; /* Allocated size of aBuffer[] in bytes */ /* Cost of running this iterator. Used by fts3.c only. */ int nCost; /* Output values. Valid only after Fts3SegReaderStep() returns SQLITE_ROW. */ char *zTerm; /* Pointer to term buffer */ int nTerm; /* Size of zTerm in bytes */ char *aDoclist; /* Pointer to doclist buffer */ int nDoclist; /* Size of aDoclist[] in bytes */ }; /* fts3.c */ int sqlite3Fts3PutVarint(char *, sqlite3_int64); int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); int sqlite3Fts3GetVarint32(const char *, int *); int sqlite3Fts3VarintLen(sqlite3_uint64); void sqlite3Fts3Dequote(char *); |
︙ | ︙ | |||
351 352 353 354 355 356 357 358 | char **, int, int, const char *, int, Fts3Expr ** ); void sqlite3Fts3ExprFree(Fts3Expr *); #ifdef SQLITE_TEST int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); #endif #endif /* _FTSINT_H */ | > > > | 378 379 380 381 382 383 384 385 386 387 388 | char **, int, int, const char *, int, Fts3Expr ** ); void sqlite3Fts3ExprFree(Fts3Expr *); #ifdef SQLITE_TEST int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); #endif /* fts3_aux.c */ int sqlite3Fts3InitAux(sqlite3 *db); #endif /* _FTSINT_H */ |
Added ext/fts3/fts3_aux.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 | /* ** 2011 Jan 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. ** ****************************************************************************** ** */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) #include "fts3Int.h" #include <string.h> #include <assert.h> typedef struct Fts3auxTable Fts3auxTable; typedef struct Fts3auxCursor Fts3auxCursor; struct Fts3auxTable { sqlite3_vtab base; /* Base class used by SQLite core */ Fts3Table *pFts3Tab; }; struct Fts3auxCursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ Fts3SegReaderCursor csr; /* Must be right after "base" */ Fts3SegFilter filter; char *zStop; int nStop; /* Byte-length of string zStop */ int isEof; /* True if cursor is at EOF */ sqlite3_int64 iRowid; /* Current rowid */ int iCol; /* Current value of 'col' column */ int nStat; /* Size of aStat[] array */ struct Fts3auxColstats { sqlite3_int64 nDoc; /* 'documents' values for current csr row */ sqlite3_int64 nOcc; /* 'occurrences' values for current csr row */ } *aStat; }; /* ** Schema of the terms table. */ #define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)" /* ** This function does all the work for both the xConnect and xCreate methods. ** These tables have no persistent representation of their own, so xConnect ** and xCreate are identical operations. */ static int fts3auxConnectMethod( sqlite3 *db, /* Database connection */ void *pUnused, /* Unused */ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ char **pzErr /* OUT: sqlite3_malloc'd error message */ ){ char const *zDb; /* Name of database (e.g. "main") */ char const *zFts3; /* Name of fts3 table */ int nDb; /* Result of strlen(zDb) */ int nFts3; /* Result of strlen(zFts3) */ int nByte; /* Bytes of space to allocate here */ int rc; /* value returned by declare_vtab() */ Fts3auxTable *p; /* Virtual table object to return */ /* The user should specify a single argument - the name of an fts3 table. */ if( argc!=4 ){ *pzErr = sqlite3_mprintf( "wrong number of arguments to fts4aux constructor" ); return SQLITE_ERROR; } zDb = argv[1]; nDb = strlen(zDb); zFts3 = argv[3]; nFts3 = strlen(zFts3); rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA); if( rc!=SQLITE_OK ) return rc; nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; p = (Fts3auxTable *)sqlite3_malloc(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); p->pFts3Tab = (Fts3Table *)&p[1]; p->pFts3Tab->zDb = (char *)&p->pFts3Tab[1]; p->pFts3Tab->zName = &p->pFts3Tab->zDb[nDb+1]; p->pFts3Tab->db = db; memcpy((char *)p->pFts3Tab->zDb, zDb, nDb); memcpy((char *)p->pFts3Tab->zName, zFts3, nFts3); sqlite3Fts3Dequote((char *)p->pFts3Tab->zName); *ppVtab = (sqlite3_vtab *)p; return SQLITE_OK; } /* ** This function does the work for both the xDisconnect and xDestroy methods. ** These tables have no persistent representation of their own, so xDisconnect ** and xDestroy are identical operations. */ static int fts3auxDisconnectMethod(sqlite3_vtab *pVtab){ Fts3auxTable *p = (Fts3auxTable *)pVtab; Fts3Table *pFts3 = p->pFts3Tab; int i; /* Free any prepared statements held */ for(i=0; i<SizeofArray(pFts3->aStmt); i++){ sqlite3_finalize(pFts3->aStmt[i]); } sqlite3_free(pFts3->zSegmentsTbl); sqlite3_free(p); return SQLITE_OK; } #define FTS4AUX_EQ_CONSTRAINT 1 #define FTS4AUX_GE_CONSTRAINT 2 #define FTS4AUX_LE_CONSTRAINT 4 /* ** xBestIndex - Analyze a WHERE and ORDER BY clause. */ static int fts3auxBestIndexMethod( sqlite3_vtab *pVTab, sqlite3_index_info *pInfo ){ int i; int iEq = -1; int iGe = -1; int iLe = -1; /* This vtab delivers always results in "ORDER BY term ASC" order. */ if( pInfo->nOrderBy==1 && pInfo->aOrderBy[0].iColumn==0 && pInfo->aOrderBy[0].desc==0 ){ pInfo->orderByConsumed = 1; } /* Search for equality and range constraints on the "term" column. */ for(i=0; i<pInfo->nConstraint; i++){ if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){ int op = pInfo->aConstraint[i].op; if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i; if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i; if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i; if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i; if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i; } } if( iEq>=0 ){ pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT; pInfo->aConstraintUsage[iEq].argvIndex = 1; pInfo->estimatedCost = 5; }else{ pInfo->idxNum = 0; pInfo->estimatedCost = 20000; if( iGe>=0 ){ pInfo->idxNum += FTS4AUX_GE_CONSTRAINT; pInfo->aConstraintUsage[iGe].argvIndex = 1; pInfo->estimatedCost /= 2; } if( iLe>=0 ){ pInfo->idxNum += FTS4AUX_LE_CONSTRAINT; pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0); pInfo->estimatedCost /= 2; } } return SQLITE_OK; } /* ** xOpen - Open a cursor. */ static int fts3auxOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ Fts3auxCursor *pCsr; /* Pointer to cursor object to return */ pCsr = (Fts3auxCursor *)sqlite3_malloc(sizeof(Fts3auxCursor)); if( !pCsr ) return SQLITE_NOMEM; memset(pCsr, 0, sizeof(Fts3auxCursor)); *ppCsr = (sqlite3_vtab_cursor *)pCsr; return SQLITE_OK; } /* ** xClose - Close a cursor. */ static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; sqlite3Fts3SegmentsClose(pFts3); sqlite3Fts3SegReaderFinish(&pCsr->csr); sqlite3_free((void *)pCsr->filter.zTerm); sqlite3_free(pCsr->zStop); sqlite3_free(pCsr->aStat); sqlite3_free(pCsr); return SQLITE_OK; } static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ if( nSize>pCsr->nStat ){ struct Fts3auxColstats *aNew; aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, sizeof(struct Fts3auxColstats) * nSize ); if( aNew==0 ) return SQLITE_NOMEM; memset(&aNew[pCsr->nStat], 0, sizeof(struct Fts3auxColstats) * (nSize - pCsr->nStat) ); pCsr->aStat = aNew; pCsr->nStat = nSize; } return SQLITE_OK; } /* ** xNext - Advance the cursor to the next row, if any. */ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){ Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; int rc; /* Increment our pretend rowid value. */ pCsr->iRowid++; for(pCsr->iCol++; pCsr->iCol<pCsr->nStat; pCsr->iCol++){ if( pCsr->aStat[pCsr->iCol].nDoc>0 ) return SQLITE_OK; } rc = sqlite3Fts3SegReaderStep(pFts3, &pCsr->csr); if( rc==SQLITE_ROW ){ int i = 0; int nDoclist = pCsr->csr.nDoclist; char *aDoclist = pCsr->csr.aDoclist; int iCol; int eState = 0; if( pCsr->zStop ){ int n = (pCsr->nStop<pCsr->csr.nTerm) ? pCsr->nStop : pCsr->csr.nTerm; int mc = memcmp(pCsr->zStop, pCsr->csr.zTerm, n); if( mc<0 || (mc==0 && pCsr->csr.nTerm>pCsr->nStop) ){ pCsr->isEof = 1; return SQLITE_OK; } } if( fts3auxGrowStatArray(pCsr, 2) ) return SQLITE_NOMEM; memset(pCsr->aStat, 0, sizeof(struct Fts3auxColstats) * pCsr->nStat); iCol = 0; while( i<nDoclist ){ sqlite3_int64 v = 0; i += sqlite3Fts3GetVarint(&aDoclist[i], &v); switch( eState ){ /* State 0. In this state the integer just read was a docid. */ case 0: pCsr->aStat[0].nDoc++; eState = 1; iCol = 0; break; /* State 1. In this state we are expecting either a 1, indicating ** that the following integer will be a column number, or the ** start of a position list for column 0. ** ** The only difference between state 1 and state 2 is that if the ** integer encountered in state 1 is not 0 or 1, then we need to ** increment the column 0 "nDoc" count for this term. */ case 1: assert( iCol==0 ); if( v>1 ){ pCsr->aStat[1].nDoc++; } eState = 2; /* fall through */ case 2: if( v==0 ){ /* 0x00. Next integer will be a docid. */ eState = 0; }else if( v==1 ){ /* 0x01. Next integer will be a column number. */ eState = 3; }else{ /* 2 or greater. A position. */ pCsr->aStat[iCol+1].nOcc++; pCsr->aStat[0].nOcc++; } break; /* State 3. The integer just read is a column number. */ default: assert( eState==3 ); iCol = (int)v; if( fts3auxGrowStatArray(pCsr, iCol+2) ) return SQLITE_NOMEM; pCsr->aStat[iCol+1].nDoc++; eState = 2; break; } } pCsr->iCol = 0; rc = SQLITE_OK; }else{ pCsr->isEof = 1; } return rc; } /* ** xFilter - Initialize a cursor to point at the start of its data. */ static int fts3auxFilterMethod( 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 */ ){ Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab; int rc; int isScan; assert( idxStr==0 ); assert( idxNum==FTS4AUX_EQ_CONSTRAINT || idxNum==0 || idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT || idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ); isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT); /* In case this cursor is being reused, close and zero it. */ testcase(pCsr->filter.zTerm); sqlite3Fts3SegReaderFinish(&pCsr->csr); sqlite3_free((void *)pCsr->filter.zTerm); sqlite3_free(pCsr->aStat); memset(&pCsr->csr, 0, ((u8*)&pCsr[1]) - (u8*)&pCsr->csr); pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY; if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN; if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){ const unsigned char *zStr = sqlite3_value_text(apVal[0]); if( zStr ){ pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; } } if( idxNum&FTS4AUX_LE_CONSTRAINT ){ int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0; pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx])); pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]); if( pCsr->zStop==0 ) return SQLITE_NOMEM; } rc = sqlite3Fts3SegReaderCursor(pFts3, FTS3_SEGCURSOR_ALL, pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr ); if( rc==SQLITE_OK ){ rc = sqlite3Fts3SegReaderStart(pFts3, &pCsr->csr, &pCsr->filter); } if( rc==SQLITE_OK ) rc = fts3auxNextMethod(pCursor); return rc; } /* ** xEof - Return true if the cursor is at EOF, or false otherwise. */ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){ Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; return pCsr->isEof; } /* ** xColumn - Return a column value. */ static int fts3auxColumnMethod( sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ int iCol /* Index of column to read value from */ ){ Fts3auxCursor *p = (Fts3auxCursor *)pCursor; assert( p->isEof==0 ); if( iCol==0 ){ /* Column "term" */ sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT); }else if( iCol==1 ){ /* Column "col" */ if( p->iCol ){ sqlite3_result_int(pContext, p->iCol-1); }else{ sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC); } }else if( iCol==2 ){ /* Column "documents" */ sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc); }else{ /* Column "occurrences" */ sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc); } return SQLITE_OK; } /* ** xRowid - Return the current rowid for the cursor. */ static int fts3auxRowidMethod( sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ sqlite_int64 *pRowid /* OUT: Rowid value */ ){ Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor; *pRowid = pCsr->iRowid; return SQLITE_OK; } /* ** Register the fts3aux module with database connection db. Return SQLITE_OK ** if successful or an error code if sqlite3_create_module() fails. */ int sqlite3Fts3InitAux(sqlite3 *db){ static const sqlite3_module fts3aux_module = { 0, /* iVersion */ fts3auxConnectMethod, /* xCreate */ fts3auxConnectMethod, /* xConnect */ fts3auxBestIndexMethod, /* xBestIndex */ fts3auxDisconnectMethod, /* xDisconnect */ fts3auxDisconnectMethod, /* xDestroy */ fts3auxOpenMethod, /* xOpen */ fts3auxCloseMethod, /* xClose */ fts3auxFilterMethod, /* xFilter */ fts3auxNextMethod, /* xNext */ fts3auxEofMethod, /* xEof */ fts3auxColumnMethod, /* xColumn */ fts3auxRowidMethod, /* xRowid */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindFunction */ 0 /* xRename */ }; int rc; /* Return code */ rc = sqlite3_create_module(db, "fts4aux", &fts3aux_module, 0); return rc; } #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ |
Changes to ext/fts3/fts3_snippet.c.
︙ | ︙ | |||
876 877 878 879 880 881 882 883 884 885 | */ static int fts3ExprLocalHitsCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; if( pExpr->aDoclist ){ char *pCsr; | > > > > < < < < | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 | */ static int fts3ExprLocalHitsCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; int iStart = iPhrase * p->nCol * 3; int i; for(i=0; i<p->nCol; i++) p->aMatchinfo[iStart+i*3] = 0; if( pExpr->aDoclist ){ char *pCsr; pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1); if( pCsr ){ fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 0); } } |
︙ | ︙ | |||
956 957 958 959 960 961 962 963 964 965 966 967 968 969 | if( rc!=SQLITE_OK ) return rc; } pStmt = *ppStmt; assert( sqlite3_data_count(pStmt)==1 ); a = sqlite3_column_blob(pStmt, 0); a += sqlite3Fts3GetVarint(a, &nDoc); *pnDoc = (u32)nDoc; if( paLen ) *paLen = a; return SQLITE_OK; } /* | > | 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 | if( rc!=SQLITE_OK ) return rc; } pStmt = *ppStmt; assert( sqlite3_data_count(pStmt)==1 ); a = sqlite3_column_blob(pStmt, 0); a += sqlite3Fts3GetVarint(a, &nDoc); if( nDoc==0 ) return SQLITE_CORRUPT; *pnDoc = (u32)nDoc; if( paLen ) *paLen = a; return SQLITE_OK; } /* |
︙ | ︙ | |||
1162 1163 1164 1165 1166 1167 1168 1169 1170 | sqlite3_int64 nDoc; /* Number of rows in table */ const char *a; /* Aggregate column length array */ rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a); if( rc==SQLITE_OK ){ int iCol; for(iCol=0; iCol<pInfo->nCol; iCol++){ sqlite3_int64 nToken; a += sqlite3Fts3GetVarint(a, &nToken); | > | > | 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 | sqlite3_int64 nDoc; /* Number of rows in table */ const char *a; /* Aggregate column length array */ rc = fts3MatchinfoSelectDoctotal(pTab, &pSelect, &nDoc, &a); if( rc==SQLITE_OK ){ int iCol; for(iCol=0; iCol<pInfo->nCol; iCol++){ u32 iVal; sqlite3_int64 nToken; a += sqlite3Fts3GetVarint(a, &nToken); iVal = (u32)(((u32)(nToken&0xffffffff)+nDoc/2)/nDoc); pInfo->aMatchinfo[iCol] = iVal; } } } break; case FTS3_MATCHINFO_LENGTH: { sqlite3_stmt *pSelectDocsize = 0; |
︙ | ︙ |
Changes to ext/fts3/fts3_write.c.
︙ | ︙ | |||
208 209 210 211 212 213 214 | /* 0 */ "DELETE FROM %Q.'%q_content' WHERE rowid = ?", /* 1 */ "SELECT NOT EXISTS(SELECT docid FROM %Q.'%q_content' WHERE rowid!=?)", /* 2 */ "DELETE FROM %Q.'%q_content'", /* 3 */ "DELETE FROM %Q.'%q_segments'", /* 4 */ "DELETE FROM %Q.'%q_segdir'", /* 5 */ "DELETE FROM %Q.'%q_docsize'", /* 6 */ "DELETE FROM %Q.'%q_stat'", | | | < < | | < < < < < < < < < | | 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 | /* 0 */ "DELETE FROM %Q.'%q_content' WHERE rowid = ?", /* 1 */ "SELECT NOT EXISTS(SELECT docid FROM %Q.'%q_content' WHERE rowid!=?)", /* 2 */ "DELETE FROM %Q.'%q_content'", /* 3 */ "DELETE FROM %Q.'%q_segments'", /* 4 */ "DELETE FROM %Q.'%q_segdir'", /* 5 */ "DELETE FROM %Q.'%q_docsize'", /* 6 */ "DELETE FROM %Q.'%q_stat'", /* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?", /* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1", /* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", /* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)", /* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)", /* Return segments in order from oldest to newest.*/ /* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root " "FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC", /* 13 */ "SELECT idx, start_block, leaves_end_block, end_block, root " "FROM %Q.'%q_segdir' ORDER BY level DESC, idx ASC", /* 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(%s)", /* 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 ); pStmt = p->aStmt[eStmt]; if( !pStmt ){ char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); } if( !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, NULL); |
︙ | ︙ | |||
296 297 298 299 300 301 302 | rc = fts3SqlStmt(pTab, eStmt, &pStmt, 0); if( rc==SQLITE_OK ){ if( eStmt==SQL_SELECT_DOCSIZE ){ sqlite3_bind_int64(pStmt, 1, iDocid); } rc = sqlite3_step(pStmt); | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | rc = fts3SqlStmt(pTab, eStmt, &pStmt, 0); if( rc==SQLITE_OK ){ if( eStmt==SQL_SELECT_DOCSIZE ){ sqlite3_bind_int64(pStmt, 1, iDocid); } rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT; pStmt = 0; }else{ rc = SQLITE_OK; } } |
︙ | ︙ | |||
397 398 399 400 401 402 403 | ** ** 0: idx ** 1: start_block ** 2: leaves_end_block ** 3: end_block ** 4: root */ | | > > > | > > > > > > | 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 | ** ** 0: idx ** 1: start_block ** 2: leaves_end_block ** 3: end_block ** 4: root */ int sqlite3Fts3AllSegdirs(Fts3Table *p, int iLevel, sqlite3_stmt **ppStmt){ int rc; sqlite3_stmt *pStmt = 0; if( iLevel<0 ){ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, &pStmt, 0); }else{ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0); if( rc==SQLITE_OK ) sqlite3_bind_int(pStmt, 1, iLevel); } *ppStmt = pStmt; return rc; } /* ** Append a single varint to a PendingList buffer. SQLITE_OK is returned ** if successful, or an SQLite error code otherwise. ** |
︙ | ︙ | |||
1100 1101 1102 1103 1104 1105 1106 1107 1108 | ** 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; sqlite3_int64 nDoc = 0; sqlite3_int64 nByte = 0; const char *a; rc = sqlite3Fts3SelectDoctotal(p, &pStmt); | > > | | > | | | | < | 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 | ** 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; sqlite3_int64 nDoc = 0; sqlite3_int64 nByte = 0; const char *pEnd; const char *a; rc = sqlite3Fts3SelectDoctotal(p, &pStmt); if( rc!=SQLITE_OK ) return rc; a = sqlite3_column_blob(pStmt, 0); assert( a ); pEnd = &a[sqlite3_column_bytes(pStmt, 0)]; a += sqlite3Fts3GetVarint(a, &nDoc); while( a<pEnd ){ a += sqlite3Fts3GetVarint(a, &nByte); } if( nDoc==0 || nByte==0 ){ sqlite3_reset(pStmt); return SQLITE_CORRUPT; } pCsr->nRowAvg = (int)(((nByte / nDoc) + pgsz) / pgsz); |
︙ | ︙ | |||
1299 1300 1301 1302 1303 1304 1305 | if( isPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 | if( isPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; return rc; } /* ** Compare the entries pointed to by two Fts3SegReader structures. ** Comparison is as follows: ** ** 1) EOF is greater than not EOF. ** ** 2) The current terms (if any) are compared using memcmp(). If one |
︙ | ︙ | |||
1939 1940 1941 1942 1943 1944 1945 | *pisEmpty = sqlite3_column_int(pStmt, 0); } rc = sqlite3_reset(pStmt); } return rc; } | < < < < < < < < < < < < < < < < < < < | 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 | *pisEmpty = sqlite3_column_int(pStmt, 0); } rc = sqlite3_reset(pStmt); } return rc; } /* ** Set *pnSegment to the total number of segments in the database. Set ** *pnMax to the largest segment level in the database (segment levels ** are stored in the 'level' column of the %_segdir table). ** ** Return SQLITE_OK if successful, or an SQLite error code if not. */ |
︙ | ︙ | |||
2016 2017 2018 2019 2020 2021 2022 | rc = sqlite3_reset(pDelete); } } if( rc!=SQLITE_OK ){ return rc; } | > > > > > | < < | 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 | rc = sqlite3_reset(pDelete); } } if( rc!=SQLITE_OK ){ return rc; } if( iLevel==FTS3_SEGCURSOR_ALL ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); }else if( iLevel==FTS3_SEGCURSOR_PENDING ){ sqlite3Fts3PendingTermsClear(p); }else{ assert( iLevel>=0 ); rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0); if( rc==SQLITE_OK ){ sqlite3_bind_int(pDelete, 1, iLevel); sqlite3_step(pDelete); rc = sqlite3_reset(pDelete); } } return rc; } /* ** When this function is called, buffer *ppList (size *pnList bytes) contains |
︙ | ︙ | |||
2073 2074 2075 2076 2077 2078 2079 | p += sqlite3Fts3GetVarint32(p, &iCurrent); } *ppList = pList; *pnList = nList; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | < < | < < < < < < < | < < < < > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > | | < | | | | | | > | | | | | | | 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 | p += sqlite3Fts3GetVarint32(p, &iCurrent); } *ppList = pList; *pnList = nList; } int sqlite3Fts3SegReaderStart( Fts3Table *p, /* Virtual table handle */ Fts3SegReaderCursor *pCsr, /* Cursor object */ Fts3SegFilter *pFilter /* Restrictions on range of iteration */ ){ int i; /* Initialize the cursor object */ pCsr->pFilter = pFilter; /* 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<pCsr->nSegment; i++){ int nTerm = pFilter->nTerm; const char *zTerm = pFilter->zTerm; Fts3SegReader *pSeg = pCsr->apSegment[i]; do { int rc = fts3SegReaderNext(p, pSeg); if( rc!=SQLITE_OK ) return rc; }while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ); } fts3SegReaderSort( pCsr->apSegment, pCsr->nSegment, pCsr->nSegment, fts3SegReaderCmp); return SQLITE_OK; } int sqlite3Fts3SegReaderStep( Fts3Table *p, /* Virtual table handle */ Fts3SegReaderCursor *pCsr /* Cursor object */ ){ int rc = SQLITE_OK; int isIgnoreEmpty = (pCsr->pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); int isRequirePos = (pCsr->pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX); int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN); Fts3SegReader **apSegment = pCsr->apSegment; int nSegment = pCsr->nSegment; Fts3SegFilter *pFilter = pCsr->pFilter; if( pCsr->nSegment==0 ) return SQLITE_OK; do { int nMerge; int i; /* Advance the first pCsr->nAdvance entries in the apSegment[] array ** forward. Then sort the list in order of current term again. */ for(i=0; i<pCsr->nAdvance; i++){ rc = fts3SegReaderNext(p, apSegment[i]); if( rc!=SQLITE_OK ) return rc; } fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp); pCsr->nAdvance = 0; /* If all the seg-readers are at EOF, we're finished. return SQLITE_OK. */ assert( rc==SQLITE_OK ); if( apSegment[0]->aNode==0 ) break; pCsr->nTerm = apSegment[0]->nTerm; pCsr->zTerm = apSegment[0]->zTerm; /* If this is a prefix-search, and if the term that apSegment[0] points ** to does not share a suffix with pFilter->zTerm/nTerm, then all ** required callbacks have been made. In this case exit early. ** ** Similarly, if this is a search for an exact match, and the first term ** of segment apSegment[0] is not a match, exit early. */ if( pFilter->zTerm && !isScan ){ if( pCsr->nTerm<pFilter->nTerm || (!isPrefix && pCsr->nTerm>pFilter->nTerm) || memcmp(pCsr->zTerm, pFilter->zTerm, pFilter->nTerm) ){ break; } } nMerge = 1; while( nMerge<nSegment && apSegment[nMerge]->aNode && apSegment[nMerge]->nTerm==pCsr->nTerm && 0==memcmp(pCsr->zTerm, apSegment[nMerge]->zTerm, pCsr->nTerm) ){ nMerge++; } assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); if( nMerge==1 && !isIgnoreEmpty ){ pCsr->aDoclist = apSegment[0]->aDoclist; pCsr->nDoclist = apSegment[0]->nDoclist; rc = SQLITE_ROW; }else{ int nDoclist = 0; /* Size of doclist */ sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */ /* The current term of the first nMerge entries in the array ** of Fts3SegReader objects is the same. The doclists must be merged ** and a single term returned with the merged doclist. */ for(i=0; i<nMerge; i++){ fts3SegReaderFirstDocid(apSegment[i]); } fts3SegReaderSort(apSegment, nMerge, nMerge, fts3SegReaderDoclistCmp); while( apSegment[0]->pOffsetList ){ int j; /* Number of segments that share a docid */ |
︙ | ︙ | |||
2238 2239 2240 2241 2242 2243 2244 | if( isColFilter ){ fts3ColumnFilter(pFilter->iCol, &pList, &nList); } if( !isIgnoreEmpty || nList>0 ){ nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0); | | | | | < | | > > | | < > | | > > < | < < < < | > > > > > | | < | > | > > > | < < < < | < | < < < > > > > | | < < < < < < | | | > > | < < < < > | < | < | < > | < | < < < < < < | < < | | < < < | < < < > < > < < < | < < < < < | < < < < < < < | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | if( isColFilter ){ fts3ColumnFilter(pFilter->iCol, &pList, &nList); } if( !isIgnoreEmpty || nList>0 ){ nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0); if( nDoclist+nByte>pCsr->nBuffer ){ char *aNew; pCsr->nBuffer = (nDoclist+nByte)*2; aNew = sqlite3_realloc(pCsr->aBuffer, pCsr->nBuffer); if( !aNew ){ return SQLITE_NOMEM; } pCsr->aBuffer = aNew; } nDoclist += sqlite3Fts3PutVarint( &pCsr->aBuffer[nDoclist], iDocid-iPrev ); iPrev = iDocid; if( isRequirePos ){ memcpy(&pCsr->aBuffer[nDoclist], pList, nList); nDoclist += nList; pCsr->aBuffer[nDoclist++] = '\0'; } } fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp); } if( nDoclist>0 ){ pCsr->aDoclist = pCsr->aBuffer; pCsr->nDoclist = nDoclist; rc = SQLITE_ROW; } } pCsr->nAdvance = nMerge; }while( rc==SQLITE_OK ); return rc; } void sqlite3Fts3SegReaderFinish( Fts3SegReaderCursor *pCsr /* Cursor object */ ){ if( pCsr ){ int i; for(i=0; i<pCsr->nSegment; i++){ sqlite3Fts3SegReaderFree(pCsr->apSegment[i]); } sqlite3_free(pCsr->apSegment); sqlite3_free(pCsr->aBuffer); pCsr->nSegment = 0; pCsr->apSegment = 0; pCsr->aBuffer = 0; } } /* ** Merge all level iLevel segments in the database into a single ** iLevel+1 segment. Or, if iLevel<0, merge all segments into a ** single segment with a level equal to the numerically largest level ** currently present in the database. ** ** If this function is called with iLevel<0, but there is only one ** segment in the database, SQLITE_DONE is returned immediately. ** 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 rc; /* Return code */ int iIdx = 0; /* Index of new segment */ int iNewLevel = 0; /* Level to create new segment at */ SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */ Fts3SegFilter filter; /* Segment term filter condition */ Fts3SegReaderCursor csr; /* Cursor to iterate through level(s) */ rc = sqlite3Fts3SegReaderCursor(p, iLevel, 0, 0, 1, 0, &csr); if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished; if( iLevel==FTS3_SEGCURSOR_ALL ){ /* This call is to merge all segments in the database to a single ** segment. The level of the new segment is equal to the the numerically ** greatest segment level currently present in the database. The index ** of the new segment is always 0. */ int nDummy; /* TODO: Remove this */ if( csr.nSegment==1 ){ rc = SQLITE_DONE; goto finished; } rc = fts3SegmentCountMax(p, &nDummy, &iNewLevel); }else{ /* This call is to merge all segments at level iLevel. Find the next ** available segment index at level iLevel+1. The call to ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to ** a single iLevel+2 segment if necessary. */ iNewLevel = iLevel+1; rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx); } if( rc!=SQLITE_OK ) goto finished; assert( csr.nSegment>0 ); assert( iNewLevel>=0 ); memset(&filter, 0, sizeof(Fts3SegFilter)); filter.flags = FTS3_SEGMENT_REQUIRE_POS; filter.flags |= (iLevel==FTS3_SEGCURSOR_ALL ? FTS3_SEGMENT_IGNORE_EMPTY : 0); rc = sqlite3Fts3SegReaderStart(p, &csr, &filter); while( SQLITE_OK==rc ){ rc = sqlite3Fts3SegReaderStep(p, &csr); if( rc!=SQLITE_ROW ) break; rc = fts3SegWriterAdd(p, &pWriter, 1, csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist); } if( rc!=SQLITE_OK ) goto finished; assert( pWriter ); rc = fts3DeleteSegdir(p, iLevel, csr.apSegment, csr.nSegment); if( rc!=SQLITE_OK ) goto finished; rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); finished: fts3SegWriterFree(pWriter); sqlite3Fts3SegReaderFinish(&csr); return rc; } /* ** Flush the contents of pendingTerms to a level 0 segment. */ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ return fts3SegmentMerge(p, FTS3_SEGCURSOR_PENDING); } /* ** Encode N integers as varints into a blob. */ static void fts3EncodeIntArray( int N, /* The number of integers to encode */ |
︙ | ︙ | |||
2615 2616 2617 2618 2619 2620 2621 | int rc; /* Return Code */ const char *zVal = (const char *)sqlite3_value_text(pVal); int nVal = sqlite3_value_bytes(pVal); if( !zVal ){ return SQLITE_NOMEM; }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ | | | 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 | int rc; /* Return Code */ const char *zVal = (const char *)sqlite3_value_text(pVal); int nVal = sqlite3_value_bytes(pVal); if( !zVal ){ return SQLITE_NOMEM; }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL); if( rc==SQLITE_DONE ){ rc = SQLITE_OK; }else{ sqlite3Fts3PendingTermsClear(p); } #ifdef SQLITE_TEST }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ |
︙ | ︙ | |||
2873 2874 2875 2876 2877 2878 2879 | ** merge all segments in the database (including the new segment, if ** there was any data to flush) into a single segment. */ int sqlite3Fts3Optimize(Fts3Table *p){ int rc; rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); if( rc==SQLITE_OK ){ | | | 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 | ** merge all segments in the database (including the new segment, if ** there was any data to flush) into a single segment. */ int sqlite3Fts3Optimize(Fts3Table *p){ int rc; rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); if( rc==SQLITE_OK ){ rc = fts3SegmentMerge(p, FTS3_SEGCURSOR_ALL); if( rc==SQLITE_OK ){ rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); if( rc==SQLITE_OK ){ sqlite3Fts3PendingTermsClear(p); } }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); |
︙ | ︙ |
Changes to ext/rtree/rtree.c.
︙ | ︙ | |||
1350 1351 1352 1353 1354 1355 1356 | ** ** 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){ int rc = SQLITE_OK; | | | | 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 | ** ** 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){ int rc = SQLITE_OK; int ii; int iIdx = 0; char zIdxStr[RTREE_MAX_DIMENSIONS*8+1]; memset(zIdxStr, 0, sizeof(zIdxStr)); UNUSED_PARAMETER(tab); assert( pIdxInfo->idxStr==0 ); for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(sizeof(zIdxStr)-1); ii++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii]; if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ /* We have an equality constraint on the rowid. Use strategy 1. */ int jj; for(jj=0; jj<ii; jj++){ pIdxInfo->aConstraintUsage[jj].argvIndex = 0; |
︙ | ︙ | |||
1382 1383 1384 1385 1386 1387 1388 | ** 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) ){ | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | < | 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 | ** 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) ){ u8 op; 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; } zIdxStr[iIdx++] = op; zIdxStr[iIdx++] = p->iColumn - 1 + 'a'; pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); pIdxInfo->aConstraintUsage[ii].omit = 1; } } pIdxInfo->idxNum = 2; pIdxInfo->needToFreeIdxStr = 1; if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){ return SQLITE_NOMEM; |
︙ | ︙ | |||
3130 3131 3132 3133 3134 3135 3136 | for(ii=0; ii<NCELL(&node); ii++){ char zCell[512]; int nCell = 0; RtreeCell cell; int jj; nodeGetCell(&tree, &node, ii, &cell); | | | 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 | for(ii=0; ii<NCELL(&node); ii++){ char zCell[512]; int nCell = 0; RtreeCell cell; int jj; nodeGetCell(&tree, &node, ii, &cell); sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); nCell = strlen(zCell); for(jj=0; jj<tree.nDim*2; jj++){ sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f); nCell = strlen(zCell); } if( zText ){ |
︙ | ︙ |
Changes to ext/rtree/rtree6.test.
︙ | ︙ | |||
101 102 103 104 105 106 107 108 109 | 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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)} } do_execsql_test rtree6-3.1 { CREATE VIRTUAL TABLE t3 USING rtree(id, x1, x2, y1, y2); INSERT INTO t3 VALUES(NULL, 1, 1, 2, 2); SELECT * FROM t3 WHERE x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5; } {1 1.0 1.0 2.0 2.0} do_test rtree6.3.2 { rtree_strategy { SELECT * FROM t3 WHERE x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 } } {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} do_test rtree6.3.3 { rtree_strategy { SELECT * FROM t3 WHERE x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 } } {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa} do_execsql_test rtree6-3.4 { SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1 } {} do_execsql_test rtree6-3.5 { SELECT * FROM t3 WHERE x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>1.1 } {} finish_test |
Added ext/rtree/rtreeB.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 | # 2011 March 2 # # 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. # #*********************************************************************** # Make sure the rtreenode() testing function can handle entries with # 64-bit rowids. # if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl ifcapable !rtree { finish_test ; return } do_test rtreeB-1.1 { db eval { CREATE VIRTUAL TABLE t1 USING rtree(ii, x0, y0, x1, y1); INSERT INTO t1 VALUES(1073741824, 0.0, 0.0, 100.0, 100.0); INSERT INTO t1 VALUES(2147483646, 0.0, 0.0, 200.0, 200.0); INSERT INTO t1 VALUES(4294967296, 0.0, 0.0, 300.0, 300.0); INSERT INTO t1 VALUES(8589934592, 20.0, 20.0, 150.0, 150.0); INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400); SELECT rtreenode(2, data) FROM t1_node; } } {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}} finish_test |
Changes to main.mk.
︙ | ︙ | |||
49 50 51 52 53 54 55 | TCCX += -I$(TOP)/ext/async # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o \ backup.o bitvec.o btmutex.o btree.o build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | TCCX += -I$(TOP)/ext/async # Object files for the SQLite library. # LIBOBJ+= alter.o analyze.o attach.o auth.o \ backup.o bitvec.o btmutex.o btree.o build.o \ callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \ fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \ fts3_snippet.o fts3_tokenizer.o fts3_tokenizer1.o fts3_write.o \ func.o global.o hash.o \ icu.o insert.o journal.o legacy.o loadext.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memjournal.o \ mutex.o mutex_noop.o mutex_os2.o mutex_unix.o mutex_w32.o \ notify.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ |
︙ | ︙ | |||
183 184 185 186 187 188 189 190 191 192 193 194 195 196 | $(TOP)/ext/fts2/fts2_tokenizer.h \ $(TOP)/ext/fts2/fts2_tokenizer.c \ $(TOP)/ext/fts2/fts2_tokenizer1.c SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_icu.c \ $(TOP)/ext/fts3/fts3_porter.c \ $(TOP)/ext/fts3/fts3_snippet.c \ $(TOP)/ext/fts3/fts3_tokenizer.h \ | > | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | $(TOP)/ext/fts2/fts2_tokenizer.h \ $(TOP)/ext/fts2/fts2_tokenizer.c \ $(TOP)/ext/fts2/fts2_tokenizer1.c SRC += \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3.h \ $(TOP)/ext/fts3/fts3Int.h \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_hash.c \ $(TOP)/ext/fts3/fts3_hash.h \ $(TOP)/ext/fts3/fts3_icu.c \ $(TOP)/ext/fts3/fts3_porter.c \ $(TOP)/ext/fts3/fts3_snippet.c \ $(TOP)/ext/fts3/fts3_tokenizer.h \ |
︙ | ︙ | |||
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 | $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(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 TESTSRC2 = \ $(TOP)/src/attach.c \ | > > > | 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 | $(TOP)/src/test_async.c \ $(TOP)/src/test_backup.c \ $(TOP)/src/test_btree.c \ $(TOP)/src/test_config.c \ $(TOP)/src/test_demovfs.c \ $(TOP)/src/test_devsym.c \ $(TOP)/src/test_func.c \ $(TOP)/src/test_fuzzer.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_syscall.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_wholenumber.c \ $(TOP)/src/test_wsd.c #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c #TESTSRC += $(TOP)/ext/fts3/fts3_tokenizer.c TESTSRC2 = \ $(TOP)/src/attach.c \ |
︙ | ︙ | |||
291 292 293 294 295 296 297 298 299 300 301 302 303 304 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # | > | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | $(TOP)/src/vdbeapi.c \ $(TOP)/src/vdbeaux.c \ $(TOP)/src/vdbe.c \ $(TOP)/src/vdbemem.c \ $(TOP)/src/where.c \ parse.c \ $(TOP)/ext/fts3/fts3.c \ $(TOP)/ext/fts3/fts3_aux.c \ $(TOP)/ext/fts3/fts3_expr.c \ $(TOP)/ext/fts3/fts3_tokenizer.c \ $(TOP)/ext/fts3/fts3_write.c \ $(TOP)/ext/async/sqlite3async.c # Header files used by all library source files. # |
︙ | ︙ | |||
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c | > > > | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer.c fts2_tokenizer1.o: $(TOP)/ext/fts2/fts2_tokenizer1.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts2/fts2_tokenizer1.c fts3.o: $(TOP)/ext/fts3/fts3.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3.c fts3_aux.o: $(TOP)/ext/fts3/fts3_aux.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_aux.c fts3_expr.o: $(TOP)/ext/fts3/fts3_expr.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_expr.c fts3_hash.o: $(TOP)/ext/fts3/fts3_hash.c $(HDR) $(EXTHDR) $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/fts3/fts3_hash.c |
︙ | ︙ | |||
531 532 533 534 535 536 537 | 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. # | | | | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | 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.o $(TOP)/test/threadtest3.c $(TOP)/test/tt3_checkpoint.c $(TCCX) -O2 sqlite3.o $(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 |
︙ | ︙ |
Changes to src/alter.c.
︙ | ︙ | |||
365 366 367 368 369 370 371 372 373 374 375 376 377 378 | ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. */ if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC); } #endif } /* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. */ void sqlite3AlterRenameTable( Parse *pParse, /* Parser context. */ | > > > > > > > > > > > > > > > > | 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 | ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. */ if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ sqlite3VdbeAddOp4(v, OP_ParseSchema, 1, 0, 0, zWhere, P4_DYNAMIC); } #endif } /* ** Parameter zName is the name of a table that is about to be altered ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). ** If the table is a system table, this function leaves an error message ** in pParse->zErr (system tables may not be altered) and returns non-zero. ** ** Or, if zName is not a system table, zero is returned. */ static int isSystemTable(Parse *pParse, const char *zName){ if( sqlite3Strlen30(zName)>6 && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); return 1; } return 0; } /* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. */ void sqlite3AlterRenameTable( Parse *pParse, /* Parser context. */ |
︙ | ︙ | |||
416 417 418 419 420 421 422 | "there is already another table or index with this name: %s", zName); goto exit_rename_table; } /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ | < < < | | | | 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 | "there is already another table or index with this name: %s", zName); goto exit_rename_table; } /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ goto exit_rename_table; } if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto exit_rename_table; } #ifndef SQLITE_OMIT_VIEW if( pTab->pSelect ){ sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName); goto exit_rename_table; } |
︙ | ︙ | |||
754 755 756 757 758 759 760 761 762 763 764 765 766 767 | } #endif /* Make sure this is not an attempt to ALTER a view. */ if( pTab->pSelect ){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } assert( pTab->addColOffset>0 ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); /* Put a copy of the Table struct in Parse.pNewTable for the ** sqlite3AddColumn() function and friends to modify. But modify | > > > | 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 | } #endif /* Make sure this is not an attempt to ALTER a view. */ if( pTab->pSelect ){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ goto exit_begin_add_column; } assert( pTab->addColOffset>0 ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); /* Put a copy of the Table struct in Parse.pNewTable for the ** sqlite3AddColumn() function and friends to modify. But modify |
︙ | ︙ |
Changes to src/analyze.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 | ** with the named table are deleted. If zWhere==0, then code is generated ** to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ | > | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | ** with the named table are deleted. If zWhere==0, then code is generated ** to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ int iDb, /* The database we are looking in */ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ const char *zWhere, /* Delete entries for this table or index */ const char *zWhereType /* Either "tbl" or "idx" */ ){ static const struct { const char *zName; const char *zCols; } aTable[] = { { "sqlite_stat1", "tbl,idx,stat" }, #ifdef SQLITE_ENABLE_STAT2 |
︙ | ︙ | |||
75 76 77 78 79 80 81 | /* The table already exists. If zWhere is not NULL, delete all entries ** associated with the table zWhere. If zWhere is NULL, delete the ** entire contents of the table. */ aRoot[i] = pStat->tnum; sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, | | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | /* The table already exists. If zWhere is not NULL, delete all entries ** associated with the table zWhere. If zWhere is NULL, delete the ** entire contents of the table. */ aRoot[i] = pStat->tnum; sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); if( zWhere ){ sqlite3NestedParse(pParse, "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere ); }else{ /* The sqlite_stat[12] table already exists. Delete all rows. */ sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); } } } |
︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | /* ** Generate code to do an analysis of all indices associated with ** a single table. */ static void analyzeOneTable( Parse *pParse, /* Parser context */ Table *pTab, /* Table whose indices are to be analyzed */ int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ int iMem /* Available memory locations begin here */ ){ 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 */ | > < | > | 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 | /* ** Generate code to do an analysis of all indices associated with ** a single table. */ static void analyzeOneTable( Parse *pParse, /* Parser context */ Table *pTab, /* Table whose indices are to be analyzed */ Index *pOnlyIdx, /* If not NULL, only analyze this one index */ int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */ int iMem /* Available memory locations begin here */ ){ 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 jZeroRows = -1; /* 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 addr = 0; /* Instruction address */ 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 |
︙ | ︙ | |||
156 157 158 159 160 161 162 | /* 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){ | | | > > > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | /* 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; KeyInfo *pKey; if( pOnlyIdx && pOnlyIdx!=pIdx ) continue; nCol = pIdx->nColumn; 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, |
︙ | ︙ | |||
315 316 317 318 319 320 321 | ** 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); | | | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | ** 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); |
︙ | ︙ | |||
341 342 343 344 345 346 347 348 | ** 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{ | > | | < < | < | 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 | ** 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); jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno); }else{ sqlite3VdbeJumpHere(v, jZeroRows); jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto); } 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; sqlite3VdbeJumpHere(v, jZeroRows); } /* ** 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){ |
︙ | ︙ | |||
381 382 383 384 385 386 387 | HashElem *k; int iStatCur; int iMem; sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; | | | | > | > | > > > | | 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 | HashElem *k; int iStatCur; int iMem; sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); } loadAnalysis(pParse, iDb); } /* ** Generate code that will do an analysis of a single table in ** a database. If pOnlyIdx is not NULL then it is a single index ** in pTab that should be analyzed. */ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){ int iDb; int iStatCur; assert( pTab!=0 ); assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); iStatCur = pParse->nTab; pParse->nTab += 2; if( pOnlyIdx ){ openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx"); }else{ openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl"); } analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1); loadAnalysis(pParse, iDb); } /* ** Generate code for the ANALYZE command. The parser calls this routine ** when it recognizes an ANALYZE command. ** |
︙ | ︙ | |||
427 428 429 430 431 432 433 434 435 436 437 438 439 440 | */ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ sqlite3 *db = pParse->db; int iDb; int i; char *z, *zDb; Table *pTab; Token *pTableName; /* Read the database schema. If an error occurs, leave an error message ** and code in pParse and return NULL. */ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ return; | > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 | */ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){ sqlite3 *db = pParse->db; int iDb; int i; char *z, *zDb; Table *pTab; Index *pIdx; Token *pTableName; /* Read the database schema. If an error occurs, leave an error message ** and code in pParse and return NULL. */ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ return; |
︙ | ︙ | |||
451 452 453 454 455 456 457 | /* Form 2: Analyze the database or table named */ iDb = sqlite3FindDb(db, pName1); if( iDb>=0 ){ analyzeDatabase(pParse, iDb); }else{ z = sqlite3NameFromToken(db, pName1); if( z ){ | > > | < < | > > > | < < | > | 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 | /* Form 2: Analyze the database or table named */ iDb = sqlite3FindDb(db, pName1); if( iDb>=0 ){ analyzeDatabase(pParse, iDb); }else{ z = sqlite3NameFromToken(db, pName1); if( z ){ if( (pIdx = sqlite3FindIndex(db, z, 0))!=0 ){ analyzeTable(pParse, pIdx->pTable, pIdx); }else if( (pTab = sqlite3LocateTable(pParse, 0, z, 0))!=0 ){ analyzeTable(pParse, pTab, 0); } sqlite3DbFree(db, z); } } }else{ /* Form 3: Analyze the fully qualified table name */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName); if( iDb>=0 ){ zDb = db->aDb[iDb].zName; z = sqlite3NameFromToken(db, pTableName); if( z ){ if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){ analyzeTable(pParse, pIdx->pTable, pIdx); }else if( (pTab = sqlite3LocateTable(pParse, 0, z, zDb))!=0 ){ analyzeTable(pParse, pTab, 0); } sqlite3DbFree(db, z); } } } } /* ** Used to pass information from the analyzer reader through to the |
︙ | ︙ |
Changes to src/attach.c.
︙ | ︙ | |||
308 309 310 311 312 313 314 | ){ pParse->nErr++; goto attach_end; } #ifndef SQLITE_OMIT_AUTHORIZATION if( pAuthArg ){ | > > | > | < | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | ){ pParse->nErr++; goto attach_end; } #ifndef SQLITE_OMIT_AUTHORIZATION if( pAuthArg ){ char *zAuthArg; if( pAuthArg->op==TK_STRING ){ zAuthArg = pAuthArg->u.zToken; }else{ zAuthArg = 0; } rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0); if(rc!=SQLITE_OK ){ goto attach_end; } } #endif /* SQLITE_OMIT_AUTHORIZATION */ |
︙ | ︙ |
Changes to src/backup.c.
︙ | ︙ | |||
484 485 486 487 488 489 490 | } }else{ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc | | | | 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 | } }else{ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); } /* Finish committing the transaction to the destination database. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0)) ){ rc = SQLITE_DONE; } } /* If bCloseTrans is true, then this function opened a read transaction ** on the source database. Close the read transaction here. There is ** no need to check the return values of the btree methods here, as ** "committing" a read-only transaction cannot fail. */ if( bCloseTrans ){ TESTONLY( int rc2 ); TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0); TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0); assert( rc2==SQLITE_OK ); } if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } p->rc = rc; |
︙ | ︙ |
Changes to src/btmutex.c.
︙ | ︙ | |||
35 36 37 38 39 40 41 42 | } /* ** Release the BtShared mutex associated with B-Tree handle p and ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p){ assert( p->locked==1 ); | > | | > | > > > > > > > > > > > > > > > > > | 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 | } /* ** Release the BtShared mutex associated with B-Tree handle p and ** clear the p->locked boolean. */ static void unlockBtreeMutex(Btree *p){ BtShared *pBt = p->pBt; assert( p->locked==1 ); assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3_mutex_held(p->db->mutex) ); assert( p->db==pBt->db ); pBt->iMutexCounter++; sqlite3_mutex_leave(pBt->mutex); p->locked = 0; } #ifdef SQLITE_DEBUG /* ** Return the number of times that the mutex has been exited for ** the given btree. ** ** This is a small circular counter that wraps around to zero on ** overflow. It is used only for sanity checking - to verify that ** mutexes are held continously by asserting that the value of ** this counter at the beginning of a region is the same as at ** the end. */ u32 sqlite3BtreeMutexCounter(Btree *p){ assert( p->locked==1 || p->sharable==0 ); return p->pBt->iMutexCounter; } #endif /* ** Enter a mutex on the given BTree object. ** ** If the object is not sharable, then no mutex is ever required ** and this routine is a no-op. The underlying mutex is non-recursive. ** But we keep a reference count in Btree.wantToLock so the behavior |
︙ | ︙ | |||
87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /* Unless the database is sharable and unlocked, then BtShared.db ** should already be set correctly. */ assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); if( !p->sharable ) return; p->wantToLock++; if( p->locked ) return; /* In most cases, we should be able to acquire the lock we ** want without having to go throught the ascending lock ** procedure that follows. Just be sure not to block. */ if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ p->pBt->db = p->db; | > > > > > > > > > > > > > > > > > > | 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 | /* Unless the database is sharable and unlocked, then BtShared.db ** should already be set correctly. */ assert( (p->locked==0 && p->sharable) || p->pBt->db==p->db ); if( !p->sharable ) return; p->wantToLock++; if( p->locked ) return; /* Increment the mutex counter on all locked btrees in the same ** database connection. This simulates the unlocking that would ** occur on a worst-case mutex dead-lock avoidance scenario. */ #ifdef SQLITE_DEBUG { int ii; sqlite3 *db = p->db; Btree *pOther; for(ii=0; ii<db->nDb; ii++){ if( ii==1 ) continue; pOther = db->aDb[ii].pBt; if( pOther==0 || pOther->sharable==0 || pOther->locked==0 ) continue; pOther->pBt->iMutexCounter++; } } #endif /* In most cases, we should be able to acquire the lock we ** want without having to go throught the ascending lock ** procedure that follows. Just be sure not to block. */ if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ p->pBt->db = p->db; |
︙ | ︙ | |||
191 192 193 194 195 196 197 | p = db->aDb[i].pBt; assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db ); if( p && p->sharable ){ p->wantToLock++; if( !p->locked ){ assert( p->wantToLock==1 ); while( p->pPrev ) p = p->pPrev; | | | 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | p = db->aDb[i].pBt; assert( !p || (p->locked==0 && p->sharable) || p->pBt->db==p->db ); if( p && p->sharable ){ p->wantToLock++; if( !p->locked ){ assert( p->wantToLock==1 ); while( p->pPrev ) p = p->pPrev; /* Reason for ALWAYS: There must be at least one unlocked Btree in ** the chain. Otherwise the !p->locked test above would have failed */ while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; for(pLater = p->pNext; pLater; pLater=pLater->pNext){ if( pLater->locked ){ unlockBtreeMutex(pLater); } } |
︙ | ︙ | |||
247 248 249 250 251 252 253 | return 0; } } return 1; } #endif /* NDEBUG */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < | < < | < < < | < < < < < < | | | > < < < < < < < < < < < < < < < < < < < | 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 | return 0; } } return 1; } #endif /* NDEBUG */ #else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* ** The following are special cases for mutex enter routines for use ** in single threaded applications that use shared cache. Except for ** these two routines, all mutex operations are no-ops in that case and ** are null #defines in btree.h. ** ** If shared cache is disabled, then all btree mutex routines, including ** the ones below, are no-ops and are null #defines in btree.h. */ void sqlite3BtreeEnter(Btree *p){ p->pBt->db = p->db; } void sqlite3BtreeEnterAll(sqlite3 *db){ int i; for(i=0; i<db->nDb; i++){ Btree *p = db->aDb[i].pBt; |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
2377 2378 2379 2380 2381 2382 2383 | pBt->usableSize = usableSize; pBt->pageSize = pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } | | | 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 | pBt->usableSize = usableSize; pBt->pageSize = pageSize; freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); return rc; } if( (pBt->db->flags & SQLITE_RecoveryMode)==0 && nPage>nPageFile ){ rc = SQLITE_CORRUPT_BKPT; goto page1_init_failed; } if( usableSize<480 ){ goto page1_init_failed; } pBt->pageSize = pageSize; |
︙ | ︙ | |||
3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 | ** sqlite3BtreeCommitPhaseOne() routine does the first phase and should ** be invoked prior to calling this routine. The sqlite3BtreeCommitPhaseOne() ** routine did all the work of writing information out to disk and flushing the ** contents so that they are written onto the disk platter. All this ** routine has to do is delete or truncate or zero the header in the ** 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. */ | > > > > > > > > > > > | | | | 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 | ** sqlite3BtreeCommitPhaseOne() routine does the first phase and should ** be invoked prior to calling this routine. The sqlite3BtreeCommitPhaseOne() ** routine did all the work of writing information out to disk and flushing the ** contents so that they are written onto the disk platter. All this ** routine has to do is delete or truncate or zero the header in the ** the rollback journal (which causes the transaction to commit) and ** drop locks. ** ** Normally, if an error occurs while the pager layer is attempting to ** finalize the underlying journal file, this function returns an error and ** the upper layer will attempt a rollback. However, if the second argument ** is non-zero then this b-tree transaction is part of a multi-file ** transaction. In this case, the transaction has already been committed ** (by deleting a master journal file) and the caller will ignore this ** functions return code. So, even if an error occurs in the pager layer, ** reset the b-tree objects internal state to indicate that the write ** transaction has been closed. This is quite safe, as the pager will have ** transitioned to the error state. ** ** 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, int bCleanup){ 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 && bCleanup==0 ){ sqlite3BtreeLeave(p); return rc; } pBt->inTransaction = TRANS_READ; } btreeEndTransaction(p); sqlite3BtreeLeave(p); return SQLITE_OK; } /* ** Do both phases of a commit. */ int sqlite3BtreeCommit(Btree *p){ int rc; sqlite3BtreeEnter(p); rc = sqlite3BtreeCommitPhaseOne(p, 0); if( rc==SQLITE_OK ){ rc = sqlite3BtreeCommitPhaseTwo(p, 0); } sqlite3BtreeLeave(p); return rc; } #ifndef NDEBUG /* |
︙ | ︙ | |||
4897 4898 4899 4900 4901 4902 4903 | if( rc ){ goto end_allocate_page; } if( nearby>0 ){ u32 i; int dist; closest = 0; | | < | < | 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 | if( rc ){ goto end_allocate_page; } if( nearby>0 ){ u32 i; int dist; closest = 0; dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); for(i=1; i<k; i++){ int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby); if( d2<dist ){ closest = i; dist = d2; } } }else{ closest = 0; |
︙ | ︙ | |||
6175 6176 6177 6178 6179 6180 6181 | for(j=i+1; j<k; j++){ if( apNew[j]->pgno<(unsigned)minV ){ minI = j; minV = apNew[j]->pgno; } } if( minI>i ){ | < < | 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 | for(j=i+1; j<k; j++){ if( apNew[j]->pgno<(unsigned)minV ){ minI = j; minV = apNew[j]->pgno; } } if( minI>i ){ MemPage *pT; pT = apNew[i]; apNew[i] = apNew[minI]; apNew[minI] = pT; } } TRACE(("new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n", apNew[0]->pgno, szNew[0], |
︙ | ︙ | |||
7928 7929 7930 7931 7932 7933 7934 7935 | #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on the Btree passed as the first argument. ** ** Return SQLITE_LOCKED if this or any other connection has an open ** transaction on the shared-cache the argument Btree is connected to. */ | > > | | | 7935 7936 7937 7938 7939 7940 7941 7942 7943 7944 7945 7946 7947 7948 7949 7950 7951 7952 7953 7954 7955 7956 7957 7958 7959 7960 | #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on the Btree passed as the first argument. ** ** Return SQLITE_LOCKED if this or any other connection has an open ** transaction on the shared-cache the argument Btree is connected to. ** ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( p ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); if( pBt->inTransaction!=TRANS_NONE ){ rc = SQLITE_LOCKED; }else{ rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt); } sqlite3BtreeLeave(p); } return rc; } #endif |
︙ | ︙ | |||
7977 7978 7979 7980 7981 7982 7983 | ** ** If the nBytes parameter is 0 and the blob of memory has not yet been ** allocated, a null pointer is returned. If the blob has already been ** allocated, it is returned as normal. ** ** Just before the shared-btree is closed, the function passed as the ** xFree argument when the memory allocation was made is invoked on the | | | 7986 7987 7988 7989 7990 7991 7992 7993 7994 7995 7996 7997 7998 7999 8000 | ** ** If the nBytes parameter is 0 and the blob of memory has not yet been ** allocated, a null pointer is returned. If the blob has already been ** allocated, it is returned as normal. ** ** Just before the shared-btree is closed, the function passed as the ** xFree argument when the memory allocation was made is invoked on the ** blob of allocated memory. The xFree function should not call sqlite3_free() ** on the memory, the btree layer does that. */ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); if( !pBt->pSchema && nBytes ){ pBt->pSchema = sqlite3DbMallocZero(0, nBytes); |
︙ | ︙ |
Changes to src/btree.h.
︙ | ︙ | |||
35 36 37 38 39 40 41 | /* ** Forward declarations of structure */ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; | < < < < < < < < < < < < | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /* ** Forward declarations of structure */ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; int sqlite3BtreeOpen( const char *zFilename, /* Name of database file to open */ sqlite3 *db, /* Associated database connection */ Btree **ppBtree, /* Return open Btree* here */ int flags, /* Flags */ |
︙ | ︙ | |||
83 84 85 86 87 88 89 | u32 sqlite3BtreeLastPage(Btree*); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | u32 sqlite3BtreeLastPage(Btree*); int sqlite3BtreeSecureDelete(Btree*,int); int sqlite3BtreeGetReserve(Btree*); int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); int sqlite3BtreeCommitPhaseTwo(Btree*, int); int sqlite3BtreeCommit(Btree*); int sqlite3BtreeRollback(Btree*); int sqlite3BtreeBeginStmt(Btree*,int); int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); int sqlite3BtreeIsInBackup(Btree*); |
︙ | ︙ | |||
203 204 205 206 207 208 209 | #ifdef SQLITE_TEST int sqlite3BtreeCursorInfo(BtCursor*, int*, int); void sqlite3BtreeCursorList(Btree*); #endif #ifndef SQLITE_OMIT_WAL | | < < < > > < < < | 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 | #ifdef SQLITE_TEST int sqlite3BtreeCursorInfo(BtCursor*, int*, int); void sqlite3BtreeCursorList(Btree*); #endif #ifndef SQLITE_OMIT_WAL int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); #endif /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the ** Enter and Leave procedures no-ops. */ #ifndef SQLITE_OMIT_SHARED_CACHE void sqlite3BtreeEnter(Btree*); void sqlite3BtreeEnterAll(sqlite3*); #else # define sqlite3BtreeEnter(X) # define sqlite3BtreeEnterAll(X) #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE void sqlite3BtreeLeave(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); void sqlite3BtreeLeaveCursor(BtCursor*); void sqlite3BtreeLeaveAll(sqlite3*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ int sqlite3BtreeHoldsMutex(Btree*); int sqlite3BtreeHoldsAllMutexes(sqlite3*); u32 sqlite3BtreeMutexCounter(Btree*); #endif #else # define sqlite3BtreeLeave(X) # define sqlite3BtreeMutexCounter(X) 0 # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 #endif #endif /* _BTREE_H_ */ |
Changes to src/btreeInt.h.
︙ | ︙ | |||
332 333 334 335 336 337 338 | ** points to the same BtShared object. The database cache and the ** schema associated with the database file are all contained within ** the BtShared object. ** ** All fields in this structure are accessed under sqlite3.mutex. ** The pBt pointer itself may not be changed while there exists cursors ** in the referenced BtShared that point back to this Btree since those | | | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | ** points to the same BtShared object. The database cache and the ** schema associated with the database file are all contained within ** the BtShared object. ** ** All fields in this structure are accessed under sqlite3.mutex. ** The pBt pointer itself may not be changed while there exists cursors ** in the referenced BtShared that point back to this Btree since those ** cursors have to go through this Btree to find their BtShared and ** they often do so without holding sqlite3.mutex. */ struct Btree { sqlite3 *db; /* The database connection holding this btree */ BtShared *pBt; /* Sharable content of this btree */ u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ u8 sharable; /* True if we can share pBt with another db */ |
︙ | ︙ | |||
422 423 424 425 426 427 428 | u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u32 pageSize; /* Total number of bytes on a page */ u32 usableSize; /* Number of usable bytes on each page */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ | | > | 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 | u16 minLeaf; /* Minimum local payload in a LEAFDATA table */ u32 pageSize; /* Total number of bytes on a page */ u32 usableSize; /* Number of usable bytes on each page */ int nTransaction; /* Number of open transactions (read + write) */ u32 nPage; /* Number of pages in the database */ void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */ Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */ #ifndef SQLITE_OMIT_SHARED_CACHE int nRef; /* Number of references to this structure */ BtShared *pNext; /* Next on a list of sharable BtShared structs */ BtLock *pLock; /* List of locks held on this shared-btree struct */ Btree *pWriter; /* Btree with currently open write transaction */ u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */ u8 isPending; /* If waiting for read-locks to clear */ u16 iMutexCounter; /* The number of mutex_leave(mutex) calls */ #endif u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */ }; /* ** An instance of the following structure is used to hold information ** about a cell. The parseCellPtr() function fills in this structure |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
144 145 146 147 148 149 150 | /* The cookie mask contains one bit for each database file open. ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are ** set for each database that is used. Generate code to start a ** transaction on each used database and to verify the schema cookie ** on each used database. */ if( pParse->cookieGoto>0 ){ | | > | > | 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 | /* The cookie mask contains one bit for each database file open. ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are ** set for each database that is used. Generate code to start a ** transaction on each used database and to verify the schema cookie ** on each used database. */ if( pParse->cookieGoto>0 ){ yDbMask mask; int iDb; sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); if( db->init.busy==0 ){ sqlite3VdbeAddOp3(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb], db->aDb[iDb].pSchema->iGeneration); } } #ifndef SQLITE_OMIT_VIRTUALTABLE { int i; for(i=0; i<pParse->nVtabLock; i++){ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); |
︙ | ︙ | |||
362 363 364 365 366 367 368 | void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; Hash *pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 | void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; Hash *pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); if( ALWAYS(pIndex) ){ if( pIndex->pTable->pIndex==pIndex ){ pIndex->pTable->pIndex = pIndex->pNext; }else{ Index *p; /* Justification of ALWAYS(); The index must be on the list of ** indices. */ p = pIndex->pTable->pIndex; |
︙ | ︙ | |||
3438 3439 3440 3441 3442 3443 3444 | if( pToplevel->cookieGoto==0 ){ Vdbe *v = sqlite3GetVdbe(pToplevel); if( v==0 ) return; /* This only happens if there was a prior error */ pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; } if( iDb>=0 ){ sqlite3 *db = pToplevel->db; | | | | 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 | if( pToplevel->cookieGoto==0 ){ Vdbe *v = sqlite3GetVdbe(pToplevel); if( v==0 ) return; /* This only happens if there was a prior error */ pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; } if( iDb>=0 ){ sqlite3 *db = pToplevel->db; yDbMask mask; assert( iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDb<SQLITE_MAX_ATTACHED+2 ); mask = ((yDbMask)1)<<iDb; if( (pToplevel->cookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; if( !OMIT_TEMPDB && iDb==1 ){ sqlite3OpenTempDatabase(pToplevel); } } |
︙ | ︙ | |||
3470 3471 3472 3473 3474 3475 3476 | ** rollback the whole transaction. For operations where all constraints ** can be checked before any changes are made to the database, it is never ** necessary to undo a write and the checkpoint should not be set. */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); | | | 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 | ** rollback the whole transaction. For operations where all constraints ** can be checked before any changes are made to the database, it is never ** necessary to undo a write and the checkpoint should not be set. */ void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); pToplevel->writeMask |= ((yDbMask)1)<<iDb; pToplevel->isMultiWrite |= setStatement; } /* ** Indicate that the statement currently under construction might write ** more than one entry (example: deleting one row then inserting another, ** inserting multiple rows in a table, or inserting a row and index entries.) |
︙ | ︙ |
Changes to src/callback.c.
︙ | ︙ | |||
423 424 425 426 427 428 429 | for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); sqlite3DeleteTable(0, pTab); } sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; | > > | > | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); sqlite3DeleteTable(0, pTab); } sqlite3HashClear(&temp1); sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; if( pSchema->flags & DB_SchemaLoaded ){ pSchema->iGeneration++; pSchema->flags &= ~DB_SchemaLoaded; } } /* ** Find and return the schema associated with a BTree. Create ** a new one if necessary. */ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ |
︙ | ︙ |
Changes to src/ctime.c.
︙ | ︙ | |||
297 298 299 300 301 302 303 304 305 306 307 308 309 310 | "OMIT_TRACE", #endif #ifdef SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif #ifdef SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif #ifdef SQLITE_OMIT_VACUUM "OMIT_VACUUM", #endif | > > > | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | "OMIT_TRACE", #endif #ifdef SQLITE_OMIT_TRIGGER "OMIT_TRIGGER", #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif #ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT "OMIT_UNIQUE_ENFORCEMENT", #endif #ifdef SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif #ifdef SQLITE_OMIT_VACUUM "OMIT_VACUUM", #endif |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
88 89 90 91 92 93 94 | /* ** Return the default collation sequence for the expression pExpr. If ** there is no default collation type, return 0. */ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ CollSeq *pColl = 0; Expr *p = pExpr; | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | /* ** Return the default collation sequence for the expression pExpr. If ** there is no default collation type, return 0. */ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ CollSeq *pColl = 0; Expr *p = pExpr; while( p ){ int op; pColl = p->pColl; if( pColl ) break; op = p->op; if( p->pTab!=0 && ( op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER )){ |
︙ | ︙ | |||
385 386 387 388 389 390 391 392 393 394 395 396 397 398 | int nExtra = 0; int iValue = 0; if( pToken ){ if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; } } pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); if( pNew ){ pNew->op = (u8)op; pNew->iAgg = -1; if( pToken ){ | > | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | int nExtra = 0; int iValue = 0; if( pToken ){ if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; assert( iValue>=0 ); } } pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); if( pNew ){ pNew->op = (u8)op; pNew->iAgg = -1; if( pToken ){ |
︙ | ︙ | |||
610 611 612 613 614 615 616 617 618 619 620 621 622 623 | } /* ** Recursively delete an expression tree. */ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p==0 ) return; if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){ sqlite3DbFree(db, p->u.zToken); } if( ExprHasProperty(p, EP_xIsSelect) ){ | > > | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 | } /* ** Recursively delete an expression tree. */ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p==0 ) return; /* Sanity check: Assert that the IntValue is non-negative if it exists */ assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){ sqlite3DbFree(db, p->u.zToken); } if( ExprHasProperty(p, EP_xIsSelect) ){ |
︙ | ︙ | |||
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 | ** If the expression p codes a constant integer that is small enough ** to fit in a 32-bit integer, return 1 and put the value of the integer ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ int sqlite3ExprIsInteger(Expr *p, int *pValue){ int rc = 0; if( p->flags & EP_IntValue ){ *pValue = p->u.iValue; return 1; } switch( p->op ){ | > > > > > > < < < < < < < < < < < < | 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 | ** If the expression p codes a constant integer that is small enough ** to fit in a 32-bit integer, return 1 and put the value of the integer ** in *pValue. If the expression is not an integer or if it is too big ** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged. */ int sqlite3ExprIsInteger(Expr *p, int *pValue){ int rc = 0; /* If an expression is an integer literal that fits in a signed 32-bit ** integer, then the EP_IntValue flag will have already been set */ assert( p->op!=TK_INTEGER || (p->flags & EP_IntValue)!=0 || sqlite3GetInt32(p->u.zToken, &rc)==0 ); if( p->flags & EP_IntValue ){ *pValue = p->u.iValue; return 1; } switch( p->op ){ case TK_UPLUS: { rc = sqlite3ExprIsInteger(p->pLeft, pValue); break; } case TK_UMINUS: { int v; if( sqlite3ExprIsInteger(p->pLeft, &v) ){ *pValue = -v; rc = 1; } break; } default: break; } return rc; } /* ** Return FALSE if there is no chance that the expression can be NULL. ** ** If the expression might be NULL or if the expression is too complex |
︙ | ︙ | |||
1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 | ** ** 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; | > | | 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 | ** ** 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; assert( i>=0 ); 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 = c==2 ? SMALLEST_INT64 : -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); |
︙ | ︙ | |||
3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 | break; } case TK_BETWEEN: { testcase( jumpIfNull==0 ); exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); break; } case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = jumpIfNull ? dest : destIfFalse; sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); sqlite3VdbeResolveLabel(v, destIfFalse); break; } default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); break; } | > > | 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 | break; } case TK_BETWEEN: { testcase( jumpIfNull==0 ); exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); break; } #ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { int destIfFalse = sqlite3VdbeMakeLabel(v); int destIfNull = jumpIfNull ? dest : destIfFalse; sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); sqlite3VdbeResolveLabel(v, destIfFalse); break; } #endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); break; } |
︙ | ︙ | |||
3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 | break; } case TK_BETWEEN: { testcase( jumpIfNull==0 ); exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); break; } case TK_IN: { if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); }else{ int destIfNull = sqlite3VdbeMakeLabel(v); sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); sqlite3VdbeResolveLabel(v, destIfNull); } break; } default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); break; } | > > | 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 | break; } case TK_BETWEEN: { testcase( jumpIfNull==0 ); exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); break; } #ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); }else{ int destIfNull = sqlite3VdbeMakeLabel(v); sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); sqlite3VdbeResolveLabel(v, destIfNull); } break; } #endif default: { r1 = sqlite3ExprCodeTemp(pParse, pExpr, ®Free1); sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0); testcase( regFree1==0 ); testcase( jumpIfNull==0 ); break; } |
︙ | ︙ |
Changes to src/fkey.c.
︙ | ︙ | |||
683 684 685 686 687 688 689 | void sqlite3FkCheck( Parse *pParse, /* Parse context */ Table *pTab, /* Row is being deleted from this table */ int regOld, /* Previous row data is stored here */ int regNew /* New row data is stored here */ ){ sqlite3 *db = pParse->db; /* Database handle */ | < < | 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 | void sqlite3FkCheck( Parse *pParse, /* Parse context */ Table *pTab, /* Row is being deleted from this table */ int regOld, /* Previous row data is stored here */ int regNew /* New row data is stored here */ ){ sqlite3 *db = pParse->db; /* Database handle */ FKey *pFKey; /* Used to iterate through FKs */ int iDb; /* Index of database containing pTab */ const char *zDb; /* Name of database containing pTab */ int isIgnoreErrors = pParse->disableTriggers; /* Exactly one of regOld and regNew should be non-zero. */ assert( (regOld==0)!=(regNew==0) ); /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zName; /* Loop through all the foreign key constraints for which pTab is the ** child table (the table that the foreign key definition is part of). */ for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ Table *pTo; /* Parent table of foreign key pFKey */ |
︙ | ︙ |
Changes to src/func.c.
︙ | ︙ | |||
1238 1239 1240 1241 1242 1243 1244 | p = sqlite3_aggregate_context(context, sizeof(*p)); type = sqlite3_value_numeric_type(argv[0]); if( p && type!=SQLITE_NULL ){ p->cnt++; if( type==SQLITE_INTEGER ){ i64 v = sqlite3_value_int64(argv[0]); p->rSum += v; | | < < < < | < | 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | p = sqlite3_aggregate_context(context, sizeof(*p)); type = sqlite3_value_numeric_type(argv[0]); if( p && type!=SQLITE_NULL ){ p->cnt++; if( type==SQLITE_INTEGER ){ i64 v = sqlite3_value_int64(argv[0]); p->rSum += v; if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ p->overflow = 1; } }else{ p->rSum += sqlite3_value_double(argv[0]); p->approx = 1; } } } |
︙ | ︙ |
Changes to src/insert.c.
︙ | ︙ | |||
461 462 463 464 465 466 467 | /* Register allocations */ int regFromSelect = 0;/* Base register for data coming from SELECT */ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ int regRowCount = 0; /* Memory cell used for the row counter */ int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ | < | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | /* Register allocations */ int regFromSelect = 0;/* Base register for data coming from SELECT */ int regAutoinc = 0; /* Register holding the AUTOINCREMENT counter */ int regRowCount = 0; /* Memory cell used for the row counter */ int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ int tmask; /* Mask of trigger times */ |
︙ | ︙ | |||
790 791 792 793 794 795 796 | addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); } /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ | < | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof); } /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ regRowid = regIns = pParse->nMem+1; pParse->nMem += pTab->nCol + 1; if( IsVirtual(pTab) ){ regRowid++; pParse->nMem++; } regData = regRowid+1; |
︙ | ︙ | |||
1184 1185 1186 1187 1188 1189 1190 | || onError==OE_Ignore || onError==OE_Replace ); switch( onError ){ case OE_Abort: sqlite3MayAbort(pParse); case OE_Rollback: case OE_Fail: { char *zMsg; | | | 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 | || onError==OE_Ignore || onError==OE_Replace ); switch( onError ){ case OE_Abort: sqlite3MayAbort(pParse); case OE_Rollback: case OE_Fail: { char *zMsg; sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC); break; } case OE_Ignore: { |
︙ | ︙ | |||
1308 1309 1310 1311 1312 1313 1314 1315 | /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; int regR; | > | > > > > > | 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 | /* Test all UNIQUE constraints by creating entries for each UNIQUE ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; #ifndef SQLITE_OMIT_UNIQUE_ENFORCEMENT int regR; #endif if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create a key for accessing the index entry */ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); for(i=0; i<pIdx->nColumn; i++){ int idx = pIdx->aiColumn[i]; if( idx==pTab->iPKey ){ sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); }else{ sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i); } } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); #ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); continue; /* Treat pIdx as if it is not a UNIQUE index */ #else /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); continue; /* pIdx is not a UNIQUE index */ } |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | ); seenReplace = 1; break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); } if( pbMayReplace ){ *pbMayReplace = seenReplace; } } | > | 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 | ); seenReplace = 1; break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); #endif } if( pbMayReplace ){ *pbMayReplace = seenReplace; } } |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
373 374 375 376 377 378 379 380 381 382 383 384 385 386 | #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) case SQLITE_CONFIG_HEAP: { /* Designate a buffer for heap memory space */ sqlite3GlobalConfig.pHeap = va_arg(ap, void*); sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); if( sqlite3GlobalConfig.pHeap==0 ){ /* If the heap pointer is NULL, then restore the malloc implementation ** back to NULL pointers too. This will cause the malloc to go ** back to its default implementation when sqlite3_initialize() is ** run. */ | > > > > > > > | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) case SQLITE_CONFIG_HEAP: { /* Designate a buffer for heap memory space */ sqlite3GlobalConfig.pHeap = va_arg(ap, void*); sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); if( sqlite3GlobalConfig.mnReq<1 ){ sqlite3GlobalConfig.mnReq = 1; }else if( sqlite3GlobalConfig.mnReq>(1<<12) ){ /* cap min request size at 2^12 */ sqlite3GlobalConfig.mnReq = (1<<12); } if( sqlite3GlobalConfig.pHeap==0 ){ /* If the heap pointer is NULL, then restore the malloc implementation ** back to NULL pointers too. This will cause the malloc to go ** back to its default implementation when sqlite3_initialize() is ** run. */ |
︙ | ︙ | |||
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 | 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; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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: { static const struct { int op; /* The opcode */ u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */ } aFlagOp[] = { { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys }, { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger }, }; unsigned int i; rc = SQLITE_ERROR; /* IMP: R-42790-23372 */ for(i=0; i<ArraySize(aFlagOp); i++){ if( aFlagOp[i].op==op ){ int onoff = va_arg(ap, int); int *pRes = va_arg(ap, int*); int oldFlags = db->flags; if( onoff>0 ){ db->flags |= aFlagOp[i].mask; }else if( onoff==0 ){ db->flags &= ~aFlagOp[i].mask; } if( oldFlags!=db->flags ){ sqlite3ExpirePreparedStatements(db); } if( pRes ){ *pRes = (db->flags & aFlagOp[i].mask)!=0; } rc = SQLITE_OK; break; } } break; } } va_end(ap); return rc; } |
︙ | ︙ | |||
1343 1344 1345 1346 1347 1348 1349 | sqlite3_mutex_leave(db->mutex); return pRet; #else return 0; #endif } | < | < < | > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > | > > > > > > | | 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 | sqlite3_mutex_leave(db->mutex); return pRet; #else return 0; #endif } /* ** Checkpoint database zDb. */ int sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ int *pnLog, /* OUT: Size of WAL log in frames */ int *pnCkpt /* OUT: Total number of frames checkpointed */ ){ #ifdef SQLITE_OMIT_WAL return SQLITE_OK; #else int rc; /* Return code */ int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */ /* Initialize the output variables to -1 in case an error occurs. */ if( pnLog ) *pnLog = -1; if( pnCkpt ) *pnCkpt = -1; assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE ); assert( SQLITE_CHECKPOINT_FULL<SQLITE_CHECKPOINT_RESTART ); assert( SQLITE_CHECKPOINT_PASSIVE+2==SQLITE_CHECKPOINT_RESTART ); if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_RESTART ){ return SQLITE_MISUSE; } sqlite3_mutex_enter(db->mutex); if( zDb && zDb[0] ){ iDb = sqlite3FindDbName(db, zDb); } if( iDb<0 ){ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb); }else{ rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt); sqlite3Error(db, rc, 0); } rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; #endif } /* ** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points ** to contains a zero-length string, all attached databases are ** checkpointed. */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){ return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0); } #ifndef SQLITE_OMIT_WAL /* ** Run a checkpoint on database iDb. This is a no-op if database iDb is ** not currently open in WAL mode. ** ** If a transaction is open on the database being checkpointed, this ** function returns SQLITE_LOCKED and a checkpoint is not attempted. If ** an error occurs while running the checkpoint, an SQLite error code is ** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK. ** ** The mutex on database handle db should be held by the caller. The mutex ** associated with the specific b-tree being checkpointed is taken by ** this function while the checkpoint is running. ** ** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are ** checkpointed. If an error is encountered it is returned immediately - ** no attempt is made to checkpoint any remaining databases. ** ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; /* Return code */ int i; /* Used to iterate through attached dbs */ int bBusy = 0; /* True if SQLITE_BUSY has been encountered */ assert( sqlite3_mutex_held(db->mutex) ); assert( !pnLog || *pnLog==-1 ); assert( !pnCkpt || *pnCkpt==-1 ); for(i=0; i<db->nDb && rc==SQLITE_OK; i++){ if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){ rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt); pnLog = 0; pnCkpt = 0; if( rc==SQLITE_BUSY ){ bBusy = 1; rc = SQLITE_OK; } } } return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc; } #endif /* SQLITE_OMIT_WAL */ /* ** This function returns true if main-memory should be used instead of ** a temporary file for transient pager files and statement journals. ** The value returned depends on the value of db->temp_store (runtime |
︙ | ︙ | |||
1656 1657 1658 1659 1660 1661 1662 | #endif #if SQLITE_MAX_VDBE_OP<40 # error SQLITE_MAX_VDBE_OP must be at least 40 #endif #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 #endif | | | | 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 | #endif #if SQLITE_MAX_VDBE_OP<40 # error SQLITE_MAX_VDBE_OP must be at least 40 #endif #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000 #endif #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62 # error SQLITE_MAX_ATTACHED must be between 0 and 62 #endif #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 #endif #if SQLITE_MAX_COLUMN>32767 # error SQLITE_MAX_COLUMN must not exceed 32767 #endif |
︙ | ︙ | |||
1782 1783 1784 1785 1786 1787 1788 | } /* Remove harmful bits from the flags parameter ** ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, | | > | 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 | } /* Remove harmful bits from the flags parameter ** ** The SQLITE_OPEN_NOMUTEX and SQLITE_OPEN_FULLMUTEX flags were ** dealt with in the previous code block. Besides these, the only ** valid input flags for sqlite3_open_v2() are SQLITE_OPEN_READONLY, ** SQLITE_OPEN_READWRITE, SQLITE_OPEN_CREATE, SQLITE_OPEN_SHAREDCACHE, ** SQLITE_OPEN_PRIVATECACHE, and some reserved bits. Silently mask ** off all other flags. */ flags &= ~( SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_TRANSIENT_DB | |
︙ | ︙ | |||
1821 1822 1823 1824 1825 1826 1827 | db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; | | | 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 | db->aDb = db->aDbStatic; assert( sizeof(db->aLimit)==sizeof(aHardLimit) ); memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit)); db->autoCommit = 1; db->nextAutovac = -1; db->nextPagesize = 0; db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger #if SQLITE_DEFAULT_FILE_FORMAT<4 | SQLITE_LegacyFileFmt #endif #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif #if SQLITE_DEFAULT_RECURSIVE_TRIGGERS |
︙ | ︙ |
Changes to src/mem5.c.
︙ | ︙ | |||
438 439 440 441 442 443 444 | ** memsys5Log(4) -> 2 ** memsys5Log(5) -> 3 ** memsys5Log(8) -> 3 ** memsys5Log(9) -> 4 */ static int memsys5Log(int iValue){ int iLog; | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 | ** memsys5Log(4) -> 2 ** memsys5Log(5) -> 3 ** memsys5Log(8) -> 3 ** memsys5Log(9) -> 4 */ static int memsys5Log(int iValue){ int iLog; for(iLog=0; (iLog<((sizeof(int)*8)-1)) && (1<<iLog)<iValue; iLog++); return iLog; } /* ** Initialize the memory allocator. ** ** This routine is not threadsafe. The caller must be holding a mutex |
︙ | ︙ | |||
469 470 471 472 473 474 475 476 477 478 479 480 481 482 | */ assert( (sizeof(Mem5Link)&(sizeof(Mem5Link)-1))==0 ); nByte = sqlite3GlobalConfig.nHeap; zByte = (u8*)sqlite3GlobalConfig.pHeap; assert( zByte!=0 ); /* sqlite3_config() does not allow otherwise */ nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq); mem5.szAtom = (1<<nMinLog); while( (int)sizeof(Mem5Link)>mem5.szAtom ){ mem5.szAtom = mem5.szAtom << 1; } mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); | > | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | */ assert( (sizeof(Mem5Link)&(sizeof(Mem5Link)-1))==0 ); nByte = sqlite3GlobalConfig.nHeap; zByte = (u8*)sqlite3GlobalConfig.pHeap; assert( zByte!=0 ); /* sqlite3_config() does not allow otherwise */ /* boundaries on sqlite3GlobalConfig.mnReq are enforced in sqlite3_config() */ nMinLog = memsys5Log(sqlite3GlobalConfig.mnReq); mem5.szAtom = (1<<nMinLog); while( (int)sizeof(Mem5Link)>mem5.szAtom ){ mem5.szAtom = mem5.szAtom << 1; } mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); |
︙ | ︙ |
Changes to src/mutex_os2.c.
︙ | ︙ | |||
27 28 29 30 31 32 33 | /* ** The mutex object ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { HMTX mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ | | | > > > > | > | | | | | > > > | | 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 | /* ** The mutex object ** Each recursive mutex is an instance of the following structure. */ struct sqlite3_mutex { HMTX mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ #ifdef SQLITE_DEBUG int trace; /* True to trace changes */ #endif }; #ifdef SQLITE_DEBUG #define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 } #else #define SQLITE3_MUTEX_INITIALIZER { 0, 0 } #endif /* ** Initialize and deinitialize the mutex subsystem. */ static int os2MutexInit(void){ return SQLITE_OK; } static int os2MutexEnd(void){ return SQLITE_OK; } /* ** The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. If it returns NULL ** that means that a mutex could not be allocated. ** SQLite will unwind its stack and return an error. The argument ** to sqlite3_mutex_alloc() is one of these integer constants: ** ** <ul> ** <li> SQLITE_MUTEX_FAST ** <li> SQLITE_MUTEX_RECURSIVE ** <li> SQLITE_MUTEX_STATIC_MASTER ** <li> SQLITE_MUTEX_STATIC_MEM ** <li> SQLITE_MUTEX_STATIC_MEM2 ** <li> SQLITE_MUTEX_STATIC_PRNG ** <li> SQLITE_MUTEX_STATIC_LRU ** <li> SQLITE_MUTEX_STATIC_LRU2 ** </ul> ** ** The first two constants cause sqlite3_mutex_alloc() to create ** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. ** The mutex implementation does not need to make a distinction ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does ** not want to. But SQLite will only request a recursive mutex in ** cases where it really needs one. If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return ** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. ** ** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST |
︙ | ︙ | |||
95 96 97 98 99 100 101 | p = NULL; } } break; } default: { static volatile int isInit = 0; | | | | | | | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | p = NULL; } } break; } default: { static volatile int isInit = 0; static sqlite3_mutex staticMutexes[6] = { SQLITE3_MUTEX_INITIALIZER, SQLITE3_MUTEX_INITIALIZER, SQLITE3_MUTEX_INITIALIZER, SQLITE3_MUTEX_INITIALIZER, SQLITE3_MUTEX_INITIALIZER, SQLITE3_MUTEX_INITIALIZER, }; if ( !isInit ){ APIRET rc; PTIB ptib; PPIB ppib; HMTX mutex; char name[32]; |
︙ | ︙ | |||
147 148 149 150 151 152 153 | /* ** This routine deallocates a previously allocated mutex. ** SQLite is careful to deallocate every mutex that it allocates. */ static void os2MutexFree(sqlite3_mutex *p){ | > > | > > | > < | > | | | < < < | > | | | | > | > > > > < < < < > | < | | < < < < | < < < < | > > < < < < < | < < < < > > > | > > > | 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 | /* ** This routine deallocates a previously allocated mutex. ** SQLite is careful to deallocate every mutex that it allocates. */ static void os2MutexFree(sqlite3_mutex *p){ #ifdef SQLITE_DEBUG TID tid; PID pid; ULONG ulCount; DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); assert( ulCount==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); #endif DosCloseMutexSem( p->mutex ); sqlite3_free( p ); } #ifdef SQLITE_DEBUG /* ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are ** intended for use inside assert() statements. */ static int os2MutexHeld(sqlite3_mutex *p){ TID tid; PID pid; ULONG ulCount; PTIB ptib; DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) ) return 0; DosGetInfoBlocks(&ptib, NULL); return tid==ptib->tib_ptib2->tib2_ultid; } static int os2MutexNotheld(sqlite3_mutex *p){ TID tid; PID pid; ULONG ulCount; PTIB ptib; DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); if( ulCount==0 ) return 1; DosGetInfoBlocks(&ptib, NULL); return tid!=ptib->tib_ptib2->tib2_ultid; } static void os2MutexTrace(sqlite3_mutex *p, char *pAction){ TID tid; PID pid; ULONG ulCount; DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount); } #endif /* ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return ** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK ** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can ** be entered multiple times by the same thread. In such cases the, ** mutex must be exited an equal number of times before another thread ** can enter. If the same thread tries to enter any other kind of mutex ** more than once, the behavior is undefined. */ static void os2MutexEnter(sqlite3_mutex *p){ assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT); #ifdef SQLITE_DEBUG if( p->trace ) os2MutexTrace(p, "enter"); #endif } static int os2MutexTry(sqlite3_mutex *p){ int rc = SQLITE_BUSY; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) { rc = SQLITE_OK; #ifdef SQLITE_DEBUG if( p->trace ) os2MutexTrace(p, "try"); #endif } return rc; } /* ** The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered or ** is not currently allocated. SQLite will never do either. */ static void os2MutexLeave(sqlite3_mutex *p){ assert( os2MutexHeld(p) ); DosReleaseMutexSem(p->mutex); #ifdef SQLITE_DEBUG if( p->trace ) os2MutexTrace(p, "leave"); #endif } SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { os2MutexInit, os2MutexEnd, os2MutexAlloc, os2MutexFree, os2MutexEnter, os2MutexTry, os2MutexLeave, #ifdef SQLITE_DEBUG os2MutexHeld, os2MutexNotheld #else 0, 0 #endif }; return &sMutex; } #endif /* SQLITE_MUTEX_OS2 */ |
Changes to src/mutex_w32.c.
︙ | ︙ | |||
276 277 278 279 280 281 282 | rc = SQLITE_OK; } #else UNUSED_PARAMETER(p); #endif #ifdef SQLITE_DEBUG if( rc==SQLITE_OK && p->trace ){ | | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | rc = SQLITE_OK; } #else UNUSED_PARAMETER(p); #endif #ifdef SQLITE_DEBUG if( rc==SQLITE_OK && p->trace ){ printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif return rc; } /* ** The sqlite3_mutex_leave() routine exits a mutex that was |
︙ | ︙ |
Changes to src/os_os2.c.
︙ | ︙ | |||
642 643 644 645 646 647 648 | } /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { | | | | | | | | | | | | | | > > > > | 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 | } /* ** This vector defines all the methods that can operate on an ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { 1, /* iVersion */ os2Close, /* xClose */ os2Read, /* xRead */ os2Write, /* xWrite */ os2Truncate, /* xTruncate */ os2Sync, /* xSync */ os2FileSize, /* xFileSize */ os2Lock, /* xLock */ os2Unlock, /* xUnlock */ os2CheckReservedLock, /* xCheckReservedLock */ os2FileControl, /* xFileControl */ os2SectorSize, /* xSectorSize */ os2DeviceCharacteristics, /* xDeviceCharacteristics */ 0, /* xShmMap */ 0, /* xShmLock */ 0, /* xShmBarrier */ 0 /* xShmUnmap */ }; /*************************************************************************** ** Here ends the I/O methods that form the sqlite3_io_methods object. ** ** The next block of code implements the VFS methods. ****************************************************************************/ |
︙ | ︙ | |||
746 747 748 749 750 751 752 | /* ** Open a file. */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ | | < > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | < < < < < < < < < < < < < < | < < < < < < < | < < < < < < < < < < < < | < > > > > > > > > > > > > > > > > > > > > > > > > | | > | | | | < | | < | | > > > | | 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 | /* ** Open a file. */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HFILE h; ULONG ulOpenFlags = 0; ULONG ulOpenMode = 0; ULONG ulAction = 0; ULONG rc; os2File *pFile = (os2File*)id; const char *zUtf8Name = zName; char *zNameCp; char zTmpname[CCHMAXPATH]; int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadWrite = (flags & SQLITE_OPEN_READWRITE); #ifndef NDEBUG int isReadonly = (flags & SQLITE_OPEN_READONLY); int eType = (flags & 0xFFFFFF00); int isOpenJournal = (isCreate && ( eType==SQLITE_OPEN_MASTER_JOURNAL || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_WAL )); #endif UNUSED_PARAMETER(pVfs); assert( id!=0 ); /* 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 ); memset( pFile, 0, sizeof(*pFile) ); pFile->pMethod = &os2IoMethod; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ if( !zUtf8Name ){ assert(isDelete && !isOpenJournal); rc = getTempname(CCHMAXPATH, zTmpname); if( rc!=SQLITE_OK ){ return rc; } zUtf8Name = zTmpname; } if( isReadWrite ){ ulOpenMode |= OPEN_ACCESS_READWRITE; }else{ ulOpenMode |= OPEN_ACCESS_READONLY; } /* Open in random access mode for possibly better speed. Allow full ** sharing because file locks will provide exclusive access when needed. */ ulOpenMode |= OPEN_FLAGS_RANDOM; ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; ulOpenMode |= OPEN_FLAGS_NOINHERIT; ulOpenMode |= OPEN_SHARE_DENYNONE; /* 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. */ ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS; }else if( isCreate ){ /* Open existing file, or create if it doesn't exist */ ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; }else{ /* Opens a file, only if it exists. */ ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; } /* For DELETEONCLOSE, save a pointer to the converted filename */ if( isDelete ){ char pathUtf8[CCHMAXPATH]; os2FullPathname( pVfs, zUtf8Name, CCHMAXPATH, pathUtf8 ); pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); } zNameCp = convertUtf8PathToCp( zUtf8Name ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, FILE_NORMAL, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); if( rc != NO_ERROR ){ OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n", rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode )); if( pFile->pathToDel ) free( pFile->pathToDel ); pFile->pathToDel = NULL; if( isReadWrite ){ return os2Open( pVfs, zName, id, ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags ); }else{ return SQLITE_CANTOPEN; } } if( pOutFlags ){ *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } pFile->h = h; OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); return SQLITE_OK; } /* ** Delete the named file. */ static int os2Delete( sqlite3_vfs *pVfs, /* Not used on os2 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ APIRET rc; char *zFilenameCp; SimulateIOError( return SQLITE_IOERR_DELETE ); zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); return (rc == NO_ERROR || rc == ERROR_FILE_NOT_FOUND || rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* ** Check the existance and status of a file. */ static int os2Access( sqlite3_vfs *pVfs, /* Not used on os2 */ |
︙ | ︙ | |||
936 937 938 939 940 941 942 | /* ** A no-op since the error code is returned on the DosLoadModule call. ** os2Dlopen returns zero if DosLoadModule is not successful. */ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } | | | | 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 | /* ** A no-op since the error code is returned on the DosLoadModule call. ** os2Dlopen returns zero if DosLoadModule is not successful. */ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ PFN pfn; APIRET rc; rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); if( rc != NO_ERROR ){ /* if the symbol itself was not found, search again for the same * symbol with an extra underscore, that might be needed depending * on the calling convention */ char _zSymbol[256] = "_"; strncat(_zSymbol, zSymbol, 255); rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); } return rc != NO_ERROR ? 0 : (void(*)(void))pfn; } static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ DosFreeModule((HMODULE)pHandle); } #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ #define os2DlOpen 0 #define os2DlError 0 |
︙ | ︙ | |||
1052 1053 1054 1055 1056 1057 1058 | ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ double now; SHORT minute; /* needs to be able to cope with negative timezone offset */ | | > > > > > > > > > > > > > > > > > > | > > > > | 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 | ** Find the current time (in Universal Coordinated Time). Write the ** current time and date as a Julian Day number into *prNow and ** return 0. Return 1 if the time and date cannot be found. */ int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ double now; SHORT minute; /* needs to be able to cope with negative timezone offset */ USHORT hundredths, second, hour, day, month, year; DATETIME dt; DosGetDateTime( &dt ); hundredths = (USHORT)dt.hundredths; second = (USHORT)dt.seconds; minute = (SHORT)dt.minutes + dt.timezone; hour = (USHORT)dt.hours; day = (USHORT)dt.day; month = (USHORT)dt.month; year = (USHORT)dt.year; /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c */ /* Calculate the Julian days */ now = day - 32076 + 1461*(year + 4800 + (month - 14)/12)/4 + 367*(month - 2 - (month - 14)/12*12)/12 - 3*((year + 4900 + (month - 14)/12)/100)/4; /* Add the fractional hours, mins and seconds */ now += (hour + 12.0)/24.0; now += minute/1440.0; now += second/86400.0; now += hundredths/8640000.0; *prNow = now; #ifdef SQLITE_TEST if( sqlite3_current_time ){ *prNow = sqlite3_current_time/86400.0 + 2440587.5; } #endif return 0; } /* ** Find the current time (in Universal Coordinated Time). Write into *piNow ** the current time and date as a Julian Day number times 86_400_000. In ** other words, write into *piNow the number of milliseconds since the Julian ** epoch of noon in Greenwich on November 24, 4714 B.C according to the ** proleptic Gregorian calendar. ** ** On success, return 0. Return 1 if the time and date cannot be found. */ static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ double now; os2CurrentTime(pVfs, &now); *piNow = now * 86400000; return 0; } static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ return 0; } /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs os2Vfs = { 3, /* iVersion */ sizeof(os2File), /* szOsFile */ CCHMAXPATH, /* mxPathname */ 0, /* pNext */ "os2", /* zName */ 0, /* pAppData */ os2Open, /* xOpen */ os2Delete, /* xDelete */ os2Access, /* xAccess */ os2FullPathname, /* xFullPathname */ os2DlOpen, /* xDlOpen */ os2DlError, /* xDlError */ os2DlSym, /* xDlSym */ os2DlClose, /* xDlClose */ os2Randomness, /* xRandomness */ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ os2CurrentTimeInt64 /* xCurrentTimeInt64 */ 0, /* xSetSystemCall */ 0, /* xGetSystemCall */ 0, /* xNextSystemCall */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); return SQLITE_OK; } int sqlite3_os_end(void){ freeUconvObjects(); return SQLITE_OK; } #endif /* SQLITE_OS_OS2 */ |
Changes to src/os_unix.c.
︙ | ︙ | |||
202 203 204 205 206 207 208 209 210 211 | typedef struct unixFile unixFile; struct unixFile { sqlite3_io_methods const *pMethod; /* Always the first entry */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ | > < | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | typedef struct unixFile unixFile; struct unixFile { sqlite3_io_methods const *pMethod; /* Always the first entry */ unixInodeInfo *pInode; /* Info about locks on this inode */ int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) |
︙ | ︙ | |||
240 241 242 243 244 245 246 | ** it is larger than the struct CrashFile defined in test6.c. */ char aPadding[32]; #endif }; /* | | > | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | ** it is larger than the struct CrashFile defined in test6.c. */ char aPadding[32]; #endif }; /* ** Allowed values for the unixFile.ctrlFlags bitmask: */ #define UNIXFILE_EXCL 0x01 /* Connections from one process only */ #define UNIXFILE_RDONLY 0x02 /* Connection is read only */ /* ** Include code that is common to all os_*.c files */ #include "os_common.h" /* |
︙ | ︙ | |||
266 267 268 269 270 271 272 | #ifndef O_NOFOLLOW # define O_NOFOLLOW 0 #endif #ifndef O_BINARY # define O_BINARY 0 #endif | < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | #ifndef O_NOFOLLOW # define O_NOFOLLOW 0 #endif #ifndef O_BINARY # define O_BINARY 0 #endif /* ** The threadid macro resolves to the thread-id or to 0. Used for ** testing and debugging only. */ #if SQLITE_THREADSAFE #define threadid pthread_self() #else #define threadid 0 #endif /* ** Many system calls are accessed through pointer-to-functions so that ** they may be overridden at runtime to facilitate fault injection during ** testing and sandboxing. The following array holds the names and pointers ** to all overrideable system calls. */ static struct unix_syscall { const char *zName; /* Name of the sytem call */ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ sqlite3_syscall_ptr pDefault; /* Default value */ } aSyscall[] = { { "open", (sqlite3_syscall_ptr)open, 0 }, #define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) { "close", (sqlite3_syscall_ptr)close, 0 }, #define osClose ((int(*)(int))aSyscall[1].pCurrent) { "access", (sqlite3_syscall_ptr)access, 0 }, #define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 }, #define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) { "stat", (sqlite3_syscall_ptr)stat, 0 }, #define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) /* ** The DJGPP compiler environment looks mostly like Unix, but it ** lacks the fcntl() system call. So redefine fcntl() to be something ** that always succeeds. This means that locking does not occur under ** DJGPP. But it is DOS - what did you expect? */ #ifdef __DJGPP__ { "fstat", 0, 0 }, #define osFstat(a,b,c) 0 #else { "fstat", (sqlite3_syscall_ptr)fstat, 0 }, #define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) #endif { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 }, #define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 }, #define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) { "read", (sqlite3_syscall_ptr)read, 0 }, #define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) #if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) { "pread", (sqlite3_syscall_ptr)pread, 0 }, #else { "pread", (sqlite3_syscall_ptr)0, 0 }, #endif #define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) #if defined(USE_PREAD64) { "pread64", (sqlite3_syscall_ptr)pread64, 0 }, #else { "pread64", (sqlite3_syscall_ptr)0, 0 }, #endif #define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) { "write", (sqlite3_syscall_ptr)write, 0 }, #define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) #if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, #else { "pwrite", (sqlite3_syscall_ptr)0, 0 }, #endif #define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[12].pCurrent) #if defined(USE_PREAD64) { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 }, #else { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, #endif #define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[13].pCurrent) { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, #define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, #else { "fallocate", (sqlite3_syscall_ptr)0, 0 }, #endif #define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) }; /* End of the overrideable system calls */ /* ** This is the xSetSystemCall() method of sqlite3_vfs for all of the ** "unix" VFSes. Return SQLITE_OK opon successfully updating the ** system call pointer, or SQLITE_NOTFOUND if there is no configurable ** system call named zName. */ static int unixSetSystemCall( sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ const char *zName, /* Name of system call to override */ sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ ){ unsigned int i; int rc = SQLITE_NOTFOUND; UNUSED_PARAMETER(pNotUsed); if( zName==0 ){ /* If no zName is given, restore all system calls to their default ** settings and return NULL */ rc = SQLITE_OK; for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ if( aSyscall[i].pDefault ){ aSyscall[i].pCurrent = aSyscall[i].pDefault; } } }else{ /* If zName is specified, operate on only the one system call ** specified. */ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ if( strcmp(zName, aSyscall[i].zName)==0 ){ if( aSyscall[i].pDefault==0 ){ aSyscall[i].pDefault = aSyscall[i].pCurrent; } rc = SQLITE_OK; if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault; aSyscall[i].pCurrent = pNewFunc; break; } } } return rc; } /* ** Return the value of a system call. Return NULL if zName is not a ** recognized system call name. NULL is also returned if the system call ** is currently undefined. */ static sqlite3_syscall_ptr unixGetSystemCall( sqlite3_vfs *pNotUsed, const char *zName ){ unsigned int i; UNUSED_PARAMETER(pNotUsed); for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent; } return 0; } /* ** Return the name of the first system call after zName. If zName==NULL ** then return the name of the first system call. Return NULL if zName ** is the last system call or if zName is not the name of a valid ** system call. */ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ int i = -1; UNUSED_PARAMETER(p); if( zName ){ for(i=0; i<ArraySize(aSyscall)-1; i++){ if( strcmp(zName, aSyscall[i].zName)==0 ) break; } } for(i++; i<ArraySize(aSyscall); i++){ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName; } return 0; } /* ** Retry open() calls that fail due to EINTR */ static int robust_open(const char *z, int f, int m){ int rc; do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR ); return rc; } /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the unixInodeInfo and ** vxworksFileId objects used by this file, all of which may be ** shared by multiple threads. ** |
︙ | ︙ | |||
350 351 352 353 354 355 356 | int s; int savedErrno; if( op==F_GETLK ){ zOpName = "GETLK"; }else if( op==F_SETLK ){ zOpName = "SETLK"; }else{ | | | | > | > > > > > > > | > > > > > > > > > > > > > | | 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 | int s; int savedErrno; if( op==F_GETLK ){ zOpName = "GETLK"; }else if( op==F_SETLK ){ zOpName = "SETLK"; }else{ s = osFcntl(fd, op, p); sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); return s; } if( p->l_type==F_RDLCK ){ zType = "RDLCK"; }else if( p->l_type==F_WRLCK ){ zType = "WRLCK"; }else if( p->l_type==F_UNLCK ){ zType = "UNLCK"; }else{ assert( 0 ); } assert( p->l_whence==SEEK_SET ); s = osFcntl(fd, op, p); savedErrno = errno; sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, (int)p->l_pid, s); if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ struct flock l2; l2 = *p; osFcntl(fd, F_GETLK, &l2); if( l2.l_type==F_RDLCK ){ zType = "RDLCK"; }else if( l2.l_type==F_WRLCK ){ zType = "WRLCK"; }else if( l2.l_type==F_UNLCK ){ zType = "UNLCK"; }else{ assert( 0 ); } sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); } errno = savedErrno; return s; } #undef osFcntl #define osFcntl lockTrace #endif /* SQLITE_LOCK_TRACE */ /* ** Retry ftruncate() calls that fail due to EINTR */ static int robust_ftruncate(int h, sqlite3_int64 sz){ int rc; do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); return rc; } /* ** This routine translates a standard POSIX errno code into something ** useful to the clients of the sqlite3 functions. Specifically, it is ** intended to translate a variety of "try again" errors into SQLITE_BUSY ** and a variety of "please close the file descriptor NOW" errors into ** SQLITE_IOERR ** ** Errors during initialization of locks, or file system support for locks, ** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. */ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { switch (posixError) { #if 0 /* At one point this code was not commented out. In theory, this branch ** should never be hit, as this function should only be called after ** a locking-related function (i.e. fcntl()) has returned non-zero with ** the value of errno as the first argument. Since a system call has failed, ** errno should be non-zero. ** ** Despite this, if errno really is zero, we still don't want to return ** SQLITE_OK. The system call failed, and *some* SQLite error should be ** propagated back to the caller. Commenting this branch out means errno==0 ** will be handled by the "default:" case below. */ case 0: return SQLITE_OK; #endif case EAGAIN: case ETIMEDOUT: case EBUSY: case EINTR: case ENOLCK: /* random NFS retry error, unless during file system support * introspection, in which it actually means what it says */ |
︙ | ︙ | |||
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ return SQLITE_BUSY; } /* else fall through */ case EPERM: return SQLITE_PERM; case EDEADLK: return SQLITE_IOERR_BLOCKED; #if EOPNOTSUPP!=ENOTSUP case EOPNOTSUPP: /* something went terribly awry, unless during file system support * introspection, in which it actually means what it says */ #endif #ifdef ENOTSUP | > > > > > > > | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 | (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){ return SQLITE_BUSY; } /* else fall through */ case EPERM: return SQLITE_PERM; /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And ** this module never makes such a call. And the code in SQLite itself ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons ** this case is also commented out. If the system does set errno to EDEADLK, ** the default SQLITE_IOERR_XXX code will be returned. */ #if 0 case EDEADLK: return SQLITE_IOERR_BLOCKED; #endif #if EOPNOTSUPP!=ENOTSUP case EOPNOTSUPP: /* something went terribly awry, unless during file system support * introspection, in which it actually means what it says */ #endif #ifdef ENOTSUP |
︙ | ︙ | |||
713 714 715 716 717 718 719 | ** A single inode can have multiple file descriptors, so each unixFile ** structure contains a pointer to an instance of this object and this ** object keeps a count of the number of unixFile pointing to it. */ struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ | | > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | > > > | > > | < < | < < < < < | | < | < | | 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 | ** A single inode can have multiple file descriptors, so each unixFile ** structure contains a pointer to an instance of this object and this ** object keeps a count of the number of unixFile pointing to it. */ struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ unsigned char bProcessLock; /* An exclusive process lock is held */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ int nLock; /* Number of outstanding file locks */ UnixUnusedFd *pUnused; /* Unused file descriptors to close */ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ unixInodeInfo *pPrev; /* .... doubly linked */ #if defined(SQLITE_ENABLE_LOCKING_STYLE) unsigned long long sharedByte; /* for AFP simulated shared lock */ #endif #if OS_VXWORKS sem_t *pSem; /* Named POSIX semaphore */ char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif }; /* ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* ** ** This function - unixLogError_x(), is only ever called via the macro ** unixLogError(). ** ** It is invoked after an error occurs in an OS function and errno has been ** set. It logs a message using sqlite3_log() containing the current value of ** errno and, if possible, the human-readable equivalent from strerror() or ** strerror_r(). ** ** The first argument passed to the macro should be the error code that ** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). ** The two subsequent arguments should be the name of the OS function that ** failed (e.g. "unlink", "open") and the the associated file-system path, ** if any. */ #define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) static int unixLogErrorAtLine( int errcode, /* SQLite error code */ const char *zFunc, /* Name of OS function that failed */ const char *zPath, /* File path associated with error */ int iLine /* Source line number where error occurred */ ){ char *zErr; /* Message from strerror() or equivalent */ int iErrno = errno; /* Saved syscall error number */ /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use ** the strerror() function to obtain the human-readable error message ** equivalent to errno. Otherwise, use strerror_r(). */ #if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) char aErr[80]; memset(aErr, 0, sizeof(aErr)); zErr = aErr; /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, ** assume that the system provides the the GNU version of strerror_r() that ** returns a pointer to a buffer containing the error message. That pointer ** may point to aErr[], or it may point to some static storage somewhere. ** Otherwise, assume that the system provides the POSIX version of ** strerror_r(), which always writes an error message into aErr[]. ** ** If the code incorrectly assumes that it is the POSIX version that is ** available, the error message will often be an empty string. Not a ** huge problem. Incorrectly concluding that the GNU version is available ** could lead to a segfault though. */ #if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) zErr = # endif strerror_r(iErrno, aErr, sizeof(aErr)-1); #elif SQLITE_THREADSAFE /* This is a threadsafe build, but strerror_r() is not available. */ zErr = ""; #else /* Non-threadsafe build, use strerror(). */ zErr = strerror(iErrno); #endif assert( errcode!=SQLITE_OK ); if( zPath==0 ) zPath = ""; sqlite3_log(errcode, "os_unix.c:%d: (%d) %s(%s) - %s", iLine, iErrno, zFunc, zPath, zErr ); return errcode; } /* ** Close a file descriptor. ** ** We assume that close() almost always works, since it is only in a ** very sick application or on a very sick platform that it might fail. ** If it does fail, simply leak the file descriptor, but do log the ** error. ** ** Note that it is not safe to retry close() after EINTR since the ** file descriptor might have already been reused by another thread. ** So we don't even try to recover from an EINTR. Just log the error ** and move on. */ static void robust_close(unixFile *pFile, int h, int lineno){ if( osClose(h) ){ unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", pFile ? pFile->zPath : 0, lineno); } } /* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. */ static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; robust_close(pFile, p->fd, __LINE__); sqlite3_free(p); } pInode->pUnused = 0; } /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** ** The mutex entered using the unixEnterMutex() function must be held ** when this function is called. */ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); if( ALWAYS(pInode) ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); closePendingFds(pFile); if( pInode->pPrev ){ assert( pInode->pPrev->pNext==pInode ); pInode->pPrev->pNext = pInode->pNext; |
︙ | ︙ | |||
820 821 822 823 824 825 826 | assert( unixMutexHeld() ); /* Get low-level information about the file that we can used to ** create a unique name for the file. */ fd = pFile->h; | | | | | 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 | assert( unixMutexHeld() ); /* Get low-level information about the file that we can used to ** create a unique name for the file. */ fd = pFile->h; rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; #ifdef EOVERFLOW if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; #endif return SQLITE_IOERR; } #ifdef __APPLE__ /* On OS X on an msdos filesystem, the inode number is reported ** incorrectly for zero-size files. See ticket #3260. To work ** around this problem (we consider it a bug in OS X, not SQLite) ** we always increase the file size to 1 by writing a single byte ** prior to accessing the inode number. The one byte written is ** an ASCII 'S' character which also happens to be the first byte ** in the header of every SQLite database. In this way, if there ** is a race condition such that another thread has already populated ** the first page of the database, no damage is done. */ if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } } #endif |
︙ | ︙ | |||
909 910 911 912 913 914 915 | if( pFile->pInode->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ | | | < | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( pFile->pInode->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ if( !reserved && !pFile->pInode->bProcessLock ){ struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = RESERVED_BYTE; lock.l_len = 1; lock.l_type = F_WRLCK; if( osFcntl(pFile->h, F_GETLK, &lock) ){ rc = SQLITE_IOERR_CHECKRESERVEDLOCK; pFile->lastErrno = errno; } else if( lock.l_type!=F_UNLCK ){ reserved = 1; } } #endif unixLeaveMutex(); OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* ** Attempt to set a system-lock on the file pFile. The lock is ** described by pLock. ** ** If the pFile was opened read/write from unix-excl, then the only lock ** ever obtained is an exclusive lock, and it is obtained exactly once ** the first time any lock is attempted. All subsequent system locking ** operations become no-ops. Locking operations still happen internally, ** in order to coordinate access between separate database connections ** within this process, but all of that is handled in memory and the ** operating system does not participate. ** ** This function is a pass-through to fcntl(F_SETLK) if pFile is using ** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" ** and is read-only. ** ** Zero is returned if the call completes successfully, or -1 if a call ** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). */ static int unixFileLock(unixFile *pFile, struct flock *pLock){ int rc; unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); assert( pInode!=0 ); if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) ){ if( pInode->bProcessLock==0 ){ struct flock lock; assert( pInode->nLock==0 ); lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; lock.l_type = F_WRLCK; rc = osFcntl(pFile->h, F_SETLK, &lock); if( rc<0 ) return rc; pInode->bProcessLock = 1; pInode->nLock++; }else{ rc = 0; } }else{ rc = osFcntl(pFile->h, F_SETLK, pLock); } return rc; } /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** ** (1) SHARED_LOCK ** (2) RESERVED_LOCK |
︙ | ︙ | |||
999 1000 1001 1002 1003 1004 1005 | ** locking a random byte from a range, concurrent SHARED locks may exist ** even if the locking primitive used is always a write-lock. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; | < | 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 | ** locking a random byte from a range, concurrent SHARED locks may exist ** even if the locking primitive used is always a write-lock. */ int rc = SQLITE_OK; unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode = pFile->pInode; struct flock lock; int tErrno = 0; assert( pFile ); OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, azFileLock(eFileLock), azFileLock(pFile->eFileLock), azFileLock(pInode->eFileLock), pInode->nShared , getpid())); |
︙ | ︙ | |||
1068 1069 1070 1071 1072 1073 1074 | lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; | | < | > | > > | < | | | < < | < | < | | < > | > | | > | < | | < < < | | | | 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 | lock.l_len = 1L; lock.l_whence = SEEK_SET; if( eFileLock==SHARED_LOCK || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_lock; } } /* If control gets to this point, then actually go ahead and make ** operating system calls for the specified lock. */ if( eFileLock==SHARED_LOCK ){ assert( pInode->nShared==0 ); assert( pInode->eFileLock==0 ); assert( rc==SQLITE_OK ); /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); } /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ /* This could happen with a network mount */ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; } if( rc ){ if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } goto end_lock; }else{ pFile->eFileLock = SHARED_LOCK; pInode->nLock++; pInode->nShared = 1; } }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ /* We are trying for an exclusive lock but another thread in this ** same process is still holding a shared lock. */ rc = SQLITE_BUSY; }else{ /* The request was for a RESERVED or EXCLUSIVE lock. It is ** assumed that there is a SHARED or greater lock on the file ** already. */ assert( 0!=pFile->eFileLock ); lock.l_type = F_WRLCK; assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); if( eFileLock==RESERVED_LOCK ){ lock.l_start = RESERVED_BYTE; lock.l_len = 1L; }else{ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; } if( unixFileLock(pFile, &lock) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( rc!=SQLITE_BUSY ){ pFile->lastErrno = tErrno; } } } #ifndef NDEBUG |
︙ | ︙ | |||
1209 1210 1211 1212 1213 1214 1215 | ** ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED ** the byte range is divided into 2 parts and the first part is unlocked then ** set to a read lock, then the other part is simply unlocked. This works ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ | | < | 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 | ** ** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED ** the byte range is divided into 2 parts and the first part is unlocked then ** set to a read lock, then the other part is simply unlocked. This works ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; int rc = SQLITE_OK; int h; assert( pFile ); OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, getpid())); assert( eFileLock<=SHARED_LOCK ); |
︙ | ︙ | |||
1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 | ** write lock until the rest is covered by a read lock: ** 1: [WWWWW] ** 2: [....W] ** 3: [RRRRW] ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ if( handleNFSUnlock ){ off_t divSize = SHARED_SIZE - 1; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; | > > > > > > > | | | | | | > > | > > > > > | | < | < | < | < | < | < | < | < | < < < | < | | < < < < | | < | | < | < | < | | | | | | | | | | | < < | | | > > > > > | | | | | | | | | | | < | 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 | ** write lock until the rest is covered by a read lock: ** 1: [WWWWW] ** 2: [....W] ** 3: [RRRRW] ** 4: [RRRR.] */ if( eFileLock==SHARED_LOCK ){ #if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE (void)handleNFSUnlock; assert( handleNFSUnlock==0 ); #endif #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE if( handleNFSUnlock ){ int tErrno; /* Error code from system call errors */ off_t divSize = SHARED_SIZE - 1; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST+divSize; lock.l_len = SHARED_SIZE-divSize; if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } goto end_unlock; } }else #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ { lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; if( unixFileLock(pFile, &lock) ){ /* In theory, the call to unixFileLock() cannot fail because another ** process is holding an incompatible lock. If it does, this ** indicates that the other process is not following the locking ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning ** SQLITE_BUSY would confuse the upper layer (in practice it causes ** an assert to fail). */ rc = SQLITE_IOERR_RDLOCK; pFile->lastErrno = errno; goto end_unlock; } } } lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = SHARED_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; pFile->lastErrno = errno; goto end_unlock; } } if( eFileLock==NO_LOCK ){ /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released ** the lock. */ pInode->nShared--; if( pInode->nShared==0 ){ lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = lock.l_len = 0L; SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); if( unixFileLock(pFile, &lock)==0 ){ pInode->eFileLock = NO_LOCK; }else{ rc = SQLITE_IOERR_UNLOCK; pFile->lastErrno = errno; pInode->eFileLock = NO_LOCK; pFile->eFileLock = NO_LOCK; } } /* Decrement the count of locks against this same file. When the ** count reaches zero, close any other file descriptors whose close ** was deferred because of outstanding locks. */ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ closePendingFds(pFile); } } end_unlock: unixLeaveMutex(); if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; return rc; } /* ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ return posixUnlock(id, eFileLock, 0); } /* ** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file ** handles, if they are valid, and sets all fields of the unixFile ** structure to 0. ** ** It is *not* necessary to hold the mutex when this routine is called, ** even on VxWorks. A mutex will be acquired on VxWorks by the ** vxworksReleaseFileId() routine. */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; if( pFile->dirfd>=0 ){ robust_close(pFile, pFile->dirfd, __LINE__); pFile->dirfd=-1; } if( pFile->h>=0 ){ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; } #if OS_VXWORKS if( pFile->pId ){ if( pFile->isDelete ){ unlink(pFile->pId->zCanonicalName); } vxworksReleaseFileId(pFile->pId); pFile->pId = 0; } #endif OSTRACE(("CLOSE %-3d\n", pFile->h)); OpenCounter(-1); sqlite3_free(pFile->pUnused); memset(pFile, 0, sizeof(unixFile)); return SQLITE_OK; } /* ** Close a file. */ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; unixFile *pFile = (unixFile *)id; unixUnlock(id, NO_LOCK); unixEnterMutex(); /* unixFile.pInode is always valid here. Otherwise, a different close ** routine (e.g. nolockClose()) would be called instead. */ assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->pUnused list. It will be automatically closed ** when the last lock is cleared. */ setPendingFd(pFile); } releaseInodeInfo(pFile); rc = closeUnixFile(id); unixLeaveMutex(); return rc; } /************** End of the posix advisory lock implementation ***************** ******************************************************************************/ /****************************************************************************** |
︙ | ︙ | |||
1558 1559 1560 1561 1562 1563 1564 | if( pFile->eFileLock>SHARED_LOCK ){ /* Either this connection or some other connection in the same process ** holds a lock on the file. No need to check further. */ reserved = 1; }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; | | | 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 | if( pFile->eFileLock>SHARED_LOCK ){ /* Either this connection or some other connection in the same process ** holds a lock on the file. No need to check further. */ reserved = 1; }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; reserved = osAccess(zLockFile, 0)==0; } OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; return rc; } /* |
︙ | ︙ | |||
1612 1613 1614 1615 1616 1617 1618 | /* Always update the timestamp on the old file */ utimes(zLockFile, NULL); #endif return SQLITE_OK; } /* grab an exclusive lock */ | | | < < < | 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 | /* Always update the timestamp on the old file */ utimes(zLockFile, NULL); #endif return SQLITE_OK; } /* grab an exclusive lock */ fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); if( fd<0 ){ /* failed to open/create the file, someone else may have stolen the lock */ int tErrno = errno; if( EEXIST == tErrno ){ rc = SQLITE_BUSY; } else { rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } return rc; } robust_close(pFile, fd, __LINE__); /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; return rc; } /* |
︙ | ︙ | |||
1673 1674 1675 1676 1677 1678 1679 | /* To fully unlock the database, delete the lock file */ assert( eFileLock==NO_LOCK ); if( unlink(zLockFile) ){ int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ | | | 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 | /* To fully unlock the database, delete the lock file */ assert( eFileLock==NO_LOCK ); if( unlink(zLockFile) ){ int rc = 0; int tErrno = errno; if( ENOENT != tErrno ){ rc = SQLITE_IOERR_UNLOCK; } if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } return rc; } pFile->eFileLock = NO_LOCK; |
︙ | ︙ | |||
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 | ** only a single process can be reading the database at a time. ** ** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if ** compiling for VXWORKS. */ #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ | > > > > > > > > > > > > > > | 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 | ** only a single process can be reading the database at a time. ** ** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if ** compiling for VXWORKS. */ #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS /* ** Retry flock() calls that fail with EINTR */ #ifdef EINTR static int robust_flock(int fd, int op){ int rc; do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); return rc; } #else # define robust_flock(a,b) flock(a,b) #endif /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut ** to a non-zero value otherwise *pResOut is set to zero. The return value ** is set to SQLITE_OK unless an I/O error occurs during lock checking. */ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ |
︙ | ︙ | |||
1740 1741 1742 1743 1744 1745 1746 | if( pFile->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ if( !reserved ){ /* attempt to get the lock */ | | | | | 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 | if( pFile->eFileLock>SHARED_LOCK ){ reserved = 1; } /* Otherwise see if some other process holds it. */ if( !reserved ){ /* attempt to get the lock */ int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); if( !lrc ){ /* got the lock, unlock it */ lrc = robust_flock(pFile->h, LOCK_UN); if ( lrc ) { int tErrno = errno; /* unlock failed with an error */ lrc = SQLITE_IOERR_UNLOCK; if( IS_LOCK_ERROR(lrc) ){ pFile->lastErrno = tErrno; rc = lrc; } } } else { int tErrno = errno; |
︙ | ︙ | |||
1820 1821 1822 1823 1824 1825 1826 | if (pFile->eFileLock > NO_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* grab an exclusive lock */ | | | 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 | if (pFile->eFileLock > NO_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* grab an exclusive lock */ if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { int tErrno = errno; /* didn't get, must be busy */ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; } } else { |
︙ | ︙ | |||
1869 1870 1871 1872 1873 1874 1875 | /* shared can just be set because we always have an exclusive */ if (eFileLock==SHARED_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* no, really, unlock. */ | | < < < < < < < | < < | | | 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 | /* shared can just be set because we always have an exclusive */ if (eFileLock==SHARED_LOCK) { pFile->eFileLock = eFileLock; return SQLITE_OK; } /* no, really, unlock. */ if( robust_flock(pFile->h, LOCK_UN) ){ #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS return SQLITE_OK; #endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ return SQLITE_IOERR_UNLOCK; }else{ pFile->eFileLock = NO_LOCK; return SQLITE_OK; } } /* ** Close a file. |
︙ | ︙ | |||
2507 2508 2509 2510 2511 2512 2513 | pFile->eFileLock = NO_LOCK; } } if( rc==SQLITE_OK ){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ | | | 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 | pFile->eFileLock = NO_LOCK; } } if( rc==SQLITE_OK ){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ closePendingFds(pFile); } } } unixLeaveMutex(); if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; return rc; |
︙ | ︙ | |||
2564 2565 2566 2567 2568 2569 2570 | ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ | | | 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 | ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock ** must be either NO_LOCK or SHARED_LOCK. ** ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ return posixUnlock(id, eFileLock, 1); } #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ /* ** The code above is the NFS lock implementation. The code is specific ** to MacOSX and does not work on other unix platforms. No alternative ** is available. |
︙ | ︙ | |||
2606 2607 2608 2609 2610 2611 2612 | static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ int got; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif TIMER_START; #if defined(USE_PREAD) | | | | | 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 | static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ int got; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif TIMER_START; #if defined(USE_PREAD) do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); SimulateIOError( got = -1 ); #else newOffset = lseek(id->h, offset, SEEK_SET); SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; }else{ ((unixFile*)id)->lastErrno = 0; } return -1; } do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); return got; |
︙ | ︙ | |||
2684 2685 2686 2687 2688 2689 2690 | static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ int got; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif TIMER_START; #if defined(USE_PREAD) | | | > | | 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 | static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ int got; #if (!defined(USE_PREAD) && !defined(USE_PREAD64)) i64 newOffset; #endif TIMER_START; #if defined(USE_PREAD) do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); #elif defined(USE_PREAD64) do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); #else newOffset = lseek(id->h, offset, SEEK_SET); SimulateIOError( newOffset-- ); if( newOffset!=offset ){ if( newOffset == -1 ){ ((unixFile*)id)->lastErrno = errno; }else{ ((unixFile*)id)->lastErrno = 0; } return -1; } do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ ((unixFile*)id)->lastErrno = errno; } OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED)); |
︙ | ︙ | |||
2872 2873 2874 2875 2876 2877 2878 | ** no-op */ #ifdef SQLITE_NO_SYNC rc = SQLITE_OK; #elif HAVE_FULLFSYNC if( fullSync ){ #ifdef SQLITE_USE_REQUEST_FULLFSYNC | | | | 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 | ** no-op */ #ifdef SQLITE_NO_SYNC rc = SQLITE_OK; #elif HAVE_FULLFSYNC if( fullSync ){ #ifdef SQLITE_USE_REQUEST_FULLFSYNC rc = osFsync(fd); if (!rc) { OSSpinLockLock(¬ify_lock); rc = notify_post(REQUEST_FULLSYNC_NOTIFICATION); OSSpinLockUnlock(¬ify_lock); } #else rc = osFcntl(fd, F_FULLFSYNC, 0); #endif }else{ rc = 1; } /* If the FULLFSYNC failed, fall back to attempting an fsync(). ** It shouldn't be possible for fullfsync to fail on the local ** file system (on OSX), so failure indicates that FULLFSYNC |
︙ | ︙ | |||
2952 2953 2954 2955 2956 2957 2958 | assert( pFile ); OSTRACE(("SYNC %-3d\n", pFile->h)); rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ pFile->lastErrno = errno; | | < | | | < < < < | 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 | assert( pFile ); OSTRACE(("SYNC %-3d\n", pFile->h)); rc = full_fsync(pFile->h, isFullsync, isDataOnly); SimulateIOError( rc=1 ); if( rc ){ pFile->lastErrno = errno; return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } if( pFile->dirfd>=0 ){ OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync)); #ifndef SQLITE_DISABLE_DIRSYNC /* The directory sync is only attempted if full_fsync is ** turned off or unavailable. If a full_fsync occurred above, ** then the directory sync is superfluous. */ if( (!HAVE_FULLFSYNC || !isFullsync) && full_fsync(pFile->dirfd,0,0) ){ /* ** We have received multiple reports of fsync() returning ** errors when applied to directories on certain file systems. ** A failed directory sync is not a big deal. So it seems ** better to ignore the error. Ticket #1657 */ /* pFile->lastErrno = errno; */ /* return SQLITE_IOERR; */ } #endif /* Only need to sync once, so close the directory when we are done */ robust_close(pFile, pFile->dirfd, __LINE__); pFile->dirfd = -1; } return rc; } /* ** Truncate an open file to a specified size */ |
︙ | ︙ | |||
3003 3004 3005 3006 3007 3008 3009 | ** actual file size after the operation may be larger than the requested ** size). */ if( pFile->szChunk ){ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } | | | | 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 | ** actual file size after the operation may be larger than the requested ** size). */ if( pFile->szChunk ){ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; } rc = robust_ftruncate(pFile->h, (off_t)nByte); if( rc ){ pFile->lastErrno = errno; return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); }else{ #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to ** doing a hot-journal rollback or a write to some file other than a ** normal database file) and we truncate the file to zero length, ** that effectively updates the change counter. This might happen ** when restoring a database using the backup API from a zero-length |
︙ | ︙ | |||
3032 3033 3034 3035 3036 3037 3038 | /* ** Determine the current size of a file in bytes */ static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); | | | 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 | /* ** Determine the current size of a file in bytes */ static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_FSTAT; } *pSize = buf.st_size; |
︙ | ︙ | |||
3073 3074 3075 3076 3077 3078 3079 | ** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix. */ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ if( pFile->szChunk ){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ | | > > > > > > | > | < | | | 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 | ** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix. */ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ if( pFile->szChunk ){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE /* The code below is handling the return value of osFallocate() ** correctly. posix_fallocate() is defined to "returns zero on success, ** or an error number on failure". See the manpage for details. */ int err; do{ err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); }while( err==EINTR ); if( err ) return SQLITE_IOERR_WRITE; #else /* If the OS does not have posix_fallocate(), fake it. First use ** ftruncate() to set the file size, then write a single byte to ** the last byte in each block within the extended region. This ** is the same technique used by glibc to implement posix_fallocate() ** on systems that do not have a real fallocate() system call. */ int nBlk = buf.st_blksize; /* File-system block size */ i64 iWrite; /* Next offset to write to */ int nWrite; /* Return value from seekAndWrite() */ if( robust_ftruncate(pFile->h, nSize) ){ pFile->lastErrno = errno; return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; do { nWrite = seekAndWrite(pFile, iWrite, "", 1); iWrite += nBlk; } while( nWrite==1 && iWrite<nSize ); if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
︙ | ︙ | |||
3498 3499 3500 3501 3502 3503 3504 | /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<SQLITE_SHM_NLOCK ); | > | | | | | | | | > | 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 | /* Shared locks never span more than one byte */ assert( n==1 || lockType!=F_RDLCK ); /* Locks are within range */ assert( n>=1 && n<SQLITE_SHM_NLOCK ); if( pShmNode->h>=0 ){ /* Initialize the locking parameters */ memset(&f, 0, sizeof(f)); f.l_type = lockType; f.l_whence = SEEK_SET; f.l_start = ofst; f.l_len = n; rc = osFcntl(pShmNode->h, F_SETLK, &f); rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; } /* Update the global lock state and do debug tracing */ #ifdef SQLITE_DEBUG { u16 mask; OSTRACE(("SHM-LOCK ")); mask = (1<<(ofst+n)) - (1<<ofst); if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
3561 3562 3563 3564 3565 3566 3567 | unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); if( p && p->nRef==0 ){ int i; assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ | > | > > | > | > > > | 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 | unixShmNode *p = pFd->pInode->pShmNode; assert( unixMutexHeld() ); if( p && p->nRef==0 ){ int i; assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; i<p->nRegion; i++){ if( p->h>=0 ){ munmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } } sqlite3_free(p->apRegion); if( p->h>=0 ){ robust_close(pFd, p->h, __LINE__); p->h = -1; } p->pInode->pShmNode = 0; sqlite3_free(p); } } static int isProxyLockingMode(unixFile *); static const char *proxySharedMemoryBasePath(unixFile *); |
︙ | ︙ | |||
3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 | ** same database file at the same time, database corruption will likely ** result. The SQLITE_SHM_DIRECTORY compile-time option is considered ** "unsupported" and may go away in a future SQLite release. ** ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ struct unixShmNode *pShmNode; /* The underlying mmapped file */ int rc; /* Result code */ unixInodeInfo *pInode; /* The inode of fd */ char *zShmFilename; /* Name of the file used for SHM */ | > > > > > > | 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 | ** same database file at the same time, database corruption will likely ** result. The SQLITE_SHM_DIRECTORY compile-time option is considered ** "unsupported" and may go away in a future SQLite release. ** ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. ** ** If the original database file (pDbFd) is using the "unix-excl" VFS ** that means that an exclusive lock is held on the database file and ** that no other processes are able to read or write the database. In ** that case, we do not really need shared memory. No shared memory ** file is created. The shared memory will be simulated with heap memory. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ struct unixShmNode *pShmNode; /* The underlying mmapped file */ int rc; /* Result code */ unixInodeInfo *pInode; /* The inode of fd */ char *zShmFilename; /* Name of the file used for SHM */ |
︙ | ︙ | |||
3630 3631 3632 3633 3634 3635 3636 | struct stat sStat; /* fstat() info for database file */ /* Call fstat() to figure out the permissions on the database file. If ** a new *-shm file is created, an attempt will be made to create it ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ | | | 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 | struct stat sStat; /* fstat() info for database file */ /* Call fstat() to figure out the permissions on the database file. If ** a new *-shm file is created, an attempt will be made to create it ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } const char *zBasePath = pDbFd->zPath; #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE /* If pDbFd is configured with proxy locking mode, use the local |
︙ | ︙ | |||
3677 3678 3679 3680 3681 3682 3683 | pShmNode->pInode = pDbFd->pInode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } | > > | | | | | | | | | | | | | | | | | | | > | 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 | pShmNode->pInode = pDbFd->pInode; pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( pShmNode->mutex==0 ){ rc = SQLITE_NOMEM; goto shm_open_err; } if( pInode->bProcessLock==0 ){ pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, (sStat.st_mode & 0777)); if( pShmNode->h<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); goto shm_open_err; } /* Check to see if another process is holding the dead-man switch. ** If not, truncate the file to zero length. */ rc = SQLITE_OK; if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ if( robust_ftruncate(pShmNode->h, 0) ){ rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); } } if( rc==SQLITE_OK ){ rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); } if( rc ) goto shm_open_err; } } /* Make the new connection a child of the unixShmNode */ p->pShmNode = pShmNode; #ifdef SQLITE_DEBUG p->id = pShmNode->nextShmId++; #endif |
︙ | ︙ | |||
3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 | if( rc!=SQLITE_OK ) return rc; } p = pDbFd->pShm; pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ struct stat sStat; /* Used by fstat() */ pShmNode->szRegion = szRegion; | > > > > | | | | | | | | | | | | | | | | | | | > | > > > | | | | | | > > > > > > > > | 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 | if( rc!=SQLITE_OK ) return rc; } p = pDbFd->pShm; pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); assert( pShmNode->pInode==pDbFd->pInode ); assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ struct stat sStat; /* Used by fstat() */ pShmNode->szRegion = szRegion; if( pShmNode->h>=0 ){ /* The requested region is not mapped into this processes address space. ** Check to see if it has been allocated (i.e. if the wal-index file is ** large enough to contain the requested region). */ if( osFstat(pShmNode->h, &sStat) ){ rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } if( sStat.st_size<nByte ){ /* The requested memory region does not exist. If bExtend is set to ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. ** ** Alternatively, if bExtend is true, use ftruncate() to allocate ** the requested memory region. */ if( !bExtend ) goto shmpage_out; if( robust_ftruncate(pShmNode->h, nByte) ){ rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", pShmNode->zFilename); goto shmpage_out; } } } /* Map the requested memory region into this processes address space. */ apNew = (char **)sqlite3_realloc( pShmNode->apRegion, (iRegion+1)*sizeof(char *) ); if( !apNew ){ rc = SQLITE_IOERR_NOMEM; goto shmpage_out; } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ void *pMem; if( pShmNode->h>=0 ){ 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; } }else{ pMem = sqlite3_malloc(szRegion); if( pMem==0 ){ rc = SQLITE_NOMEM; goto shmpage_out; } memset(pMem, 0, szRegion); } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; } } shmpage_out: |
︙ | ︙ | |||
3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 | assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); assert( n>=1 ); assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); mask = (1<<(ofst+n)) - (1<<ofst); assert( n>1 || mask==(1<<ofst) ); sqlite3_mutex_enter(pShmNode->mutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ | > > | 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 | assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); assert( n>=1 ); assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); mask = (1<<(ofst+n)) - (1<<ofst); assert( n>1 || mask==(1<<ofst) ); sqlite3_mutex_enter(pShmNode->mutex); if( flags & SQLITE_SHM_UNLOCK ){ u16 allMask = 0; /* Mask of locks held by siblings */ |
︙ | ︙ | |||
3999 4000 4001 4002 4003 4004 4005 | /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ unixEnterMutex(); assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ | | | 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 | /* If pShmNode->nRef has reached 0, then close the underlying ** shared-memory file, too */ unixEnterMutex(); assert( pShmNode->nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename); unixShmPurge(pDbFd); } unixLeaveMutex(); return SQLITE_OK; } |
︙ | ︙ | |||
4240 4241 4242 4243 4244 4245 4246 | ** Test byte-range lock using fcntl(). If the call succeeds, ** assume that the file-system supports POSIX style locks. */ lockInfo.l_len = 1; lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; | | | 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 | ** Test byte-range lock using fcntl(). If the call succeeds, ** assume that the file-system supports POSIX style locks. */ lockInfo.l_len = 1; lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ return &nfsIoMethods; } else { return &posixIoMethods; } }else{ return &dotlockIoMethods; |
︙ | ︙ | |||
4282 4283 4284 4285 4286 4287 4288 | /* Test if fcntl() is supported and use POSIX style locks. ** Otherwise fall back to the named semaphore method. */ lockInfo.l_len = 1; lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; | | | 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 | /* Test if fcntl() is supported and use POSIX style locks. ** Otherwise fall back to the named semaphore method. */ lockInfo.l_len = 1; lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; } } static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; |
︙ | ︙ | |||
4316 4317 4318 4319 4320 4321 4322 | static int fillInUnixFile( sqlite3_vfs *pVfs, /* Pointer to vfs object */ int h, /* Open file descriptor of file being opened */ int dirfd, /* Directory file descriptor */ sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int noLock, /* Omit locking if true */ | | > | 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 | static int fillInUnixFile( sqlite3_vfs *pVfs, /* Pointer to vfs object */ int h, /* Open file descriptor of file being opened */ int dirfd, /* Directory file descriptor */ sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int noLock, /* Omit locking if true */ int isDelete, /* Delete on close if true */ int isReadOnly /* True if the file is opened read-only */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; int rc = SQLITE_OK; assert( pNew->pInode==NULL ); |
︙ | ︙ | |||
4343 4344 4345 4346 4347 4348 4349 | #else assert( zFilename==0 || zFilename[0]=='/' ); #endif OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; | < > > > > > > > > | 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 | #else assert( zFilename==0 || zFilename[0]=='/' ); #endif OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; pNew->zPath = zFilename; if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ pNew->ctrlFlags = UNIXFILE_EXCL; }else{ pNew->ctrlFlags = 0; } if( isReadOnly ){ pNew->ctrlFlags |= UNIXFILE_RDONLY; } #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; rc = SQLITE_NOMEM; } |
︙ | ︙ | |||
4392 4393 4394 4395 4396 4397 4398 | ** handle h - as it is guaranteed that no posix locks will be released ** by doing so. ** ** If scenario (a) caused the error then things are not so safe. The ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ | | | 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 | ** handle h - as it is guaranteed that no posix locks will be released ** by doing so. ** ** If scenario (a) caused the error then things are not so safe. The ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) else if( pLockingStyle == &afpIoMethods ){ |
︙ | ︙ | |||
4418 4419 4420 4421 4422 4423 4424 | pCtx->dbPath = zFilename; pCtx->reserved = 0; srandomdev(); unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); | | | 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 | pCtx->dbPath = zFilename; pCtx->reserved = 0; srandomdev(); unixEnterMutex(); rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); } } #endif |
︙ | ︙ | |||
4469 4470 4471 4472 4473 4474 4475 | unixLeaveMutex(); } #endif pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ | | | | | 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 | unixLeaveMutex(); } #endif pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; unlink(zFilename); isDelete = 0; } pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__); if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); } return rc; } |
︙ | ︙ | |||
4505 4506 4507 4508 4509 4510 4511 | int fd = -1; char zDirname[MAX_PATHNAME+1]; sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); if( ii>0 ){ zDirname[ii] = '\0'; | | | | | 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 | int fd = -1; char zDirname[MAX_PATHNAME+1]; sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); if( ii>0 ){ zDirname[ii] = '\0'; fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } } *pFd = fd; return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname)); } /* ** Return the name of a directory in which to put temporary files. ** If no suitable temporary file directory can be found, return NULL. */ static const char *unixTempFileDir(void){ |
︙ | ︙ | |||
4538 4539 4540 4541 4542 4543 4544 | struct stat buf; const char *zDir = 0; azDirs[0] = sqlite3_temp_directory; if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ if( zDir==0 ) continue; | | | | 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 | struct stat buf; const char *zDir = 0; azDirs[0] = sqlite3_temp_directory; if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ if( zDir==0 ) continue; if( osStat(zDir, &buf) ) continue; if( !S_ISDIR(buf.st_mode) ) continue; if( osAccess(zDir, 07) ) continue; break; } return zDir; } /* ** Create a temporary file name in zBuf. zBuf must be allocated |
︙ | ︙ | |||
4583 4584 4585 4586 4587 4588 4589 | sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); j = (int)strlen(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; | | | 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 | sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir); j = (int)strlen(zBuf); sqlite3_randomness(15, &zBuf[j]); for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; }while( osAccess(zBuf,0)==0 ); return SQLITE_OK; } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) /* ** Routine to transform a unixFile into a proxy-locking unixFile. ** Implementation in the proxy-lock division, but used by unixOpen() |
︙ | ︙ | |||
4844 4845 4846 4847 4848 4849 4850 | mode_t openMode; /* Permissions to create file with */ rc = findCreateFileMode(zName, flags, &openMode); if( rc!=SQLITE_OK ){ assert( !p->pUnused ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); return rc; } | | > | | | 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 | mode_t openMode; /* Permissions to create file with */ rc = findCreateFileMode(zName, flags, &openMode); if( rc!=SQLITE_OK ){ assert( !p->pUnused ); assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); return rc; } fd = robust_open(zName, openFlags, openMode); OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ /* Failed to open the file for read/write access. Try read-only. */ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); openFlags &= ~(O_RDWR|O_CREAT); flags |= SQLITE_OPEN_READONLY; openFlags |= O_RDONLY; isReadonly = 1; fd = robust_open(zName, openFlags, openMode); } if( fd<0 ){ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); goto open_finished; } } assert( fd>=0 ); if( pOutFlags ){ *pOutFlags = flags; } |
︙ | ︙ | |||
4890 4891 4892 4893 4894 4895 4896 | rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ /* It is safe to close fd at this point, because it is guaranteed not ** to be open on a database file. If it were open on a database file, ** it would not be safe to close as this would release any locks held ** on the file by this process. */ assert( eType!=SQLITE_OPEN_MAIN_DB ); | | | | | | 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 | rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ /* It is safe to close fd at this point, because it is guaranteed not ** to be open on a database file. If it were open on a database file, ** it would not be safe to close as this would release any locks held ** on the file by this process. */ assert( eType!=SQLITE_OPEN_MAIN_DB ); robust_close(p, fd, __LINE__); goto open_finished; } } #ifdef FD_CLOEXEC osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif noLock = eType!=SQLITE_OPEN_MAIN_DB; #if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE struct statfs fsInfo; if( fstatfs(fd, &fsInfo) == -1 ){ ((unixFile*)pFile)->lastErrno = errno; if( dirfd>=0 ) robust_close(p, dirfd, __LINE__); robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; } if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; |
︙ | ︙ | |||
4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 | int useProxy = 0; /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means ** never use proxy, NULL means use proxy for non-local files only. */ if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ | > > > > > > > > > > > > > > > > > | > | 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 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 | int useProxy = 0; /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means ** never use proxy, NULL means use proxy for non-local files only. */ if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ struct statfs fsInfo; if( statfs(zPath, &fsInfo) == -1 ){ /* In theory, the close(fd) call is sub-optimal. If the file opened ** with fd is a database file, and there are other connections open ** on that file that are currently holding advisory locks on it, ** then the call to close() will cancel those locks. In practice, ** we're assuming that statfs() doesn't fail very often. At least ** not while other file descriptors opened by the same process on ** the same file are working. */ p->lastErrno = errno; if( dirfd>=0 ){ robust_close(p, dirfd, __LINE__); } robust_close(p, fd, __LINE__); rc = SQLITE_IOERR_ACCESS; goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete, isReadonly); if( rc==SQLITE_OK ){ /* cache the pMethod in case the transform fails */ const struct sqlite3_io_methods *pMethod = pFile->pMethods; rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); if( rc!=SQLITE_OK ){ /* Use unixClose to clean up the resources added in fillInUnixFile ** and clear all the structure's references. Specifically, |
︙ | ︙ | |||
4957 4958 4959 4960 4961 4962 4963 | } } goto open_finished; } } #endif | | > | 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 | } } goto open_finished; } } #endif rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete, isReadonly); open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pUnused); } return rc; } |
︙ | ︙ | |||
4979 4980 4981 4982 4983 4984 4985 | const char *zPath, /* Name of file to be deleted */ int dirSync /* If true, fsync() directory after deleting file */ ){ int rc = SQLITE_OK; UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); if( unlink(zPath)==(-1) && errno!=ENOENT ){ | | | | < < | 5362 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 | const char *zPath, /* Name of file to be deleted */ int dirSync /* If true, fsync() directory after deleting file */ ){ int rc = SQLITE_OK; UNUSED_PARAMETER(NotUsed); SimulateIOError(return SQLITE_IOERR_DELETE); if( unlink(zPath)==(-1) && errno!=ENOENT ){ return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); } #ifndef SQLITE_DISABLE_DIRSYNC if( dirSync ){ int fd; rc = openDirectory(zPath, &fd); if( rc==SQLITE_OK ){ #if OS_VXWORKS if( fsync(fd)==-1 ) #else if( fsync(fd) ) #endif { rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } robust_close(0, fd, __LINE__); } } #endif return rc; } /* |
︙ | ︙ | |||
5036 5037 5038 5039 5040 5041 5042 | case SQLITE_ACCESS_READ: amode = R_OK; break; default: assert(!"Invalid flags argument"); } | | | 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 | case SQLITE_ACCESS_READ: amode = R_OK; break; default: assert(!"Invalid flags argument"); } *pResOut = (osAccess(zPath, amode)==0); if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ struct stat buf; if( 0==stat(zPath, &buf) && buf.st_size==0 ){ *pResOut = 0; } } return SQLITE_OK; |
︙ | ︙ | |||
5078 5079 5080 5081 5082 5083 5084 | UNUSED_PARAMETER(pVfs); zOut[nOut-1] = '\0'; if( zPath[0]=='/' ){ sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ int nCwd; | | | | 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 | UNUSED_PARAMETER(pVfs); zOut[nOut-1] = '\0'; if( zPath[0]=='/' ){ sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ int nCwd; if( osGetcwd(zOut, nOut-1)==0 ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } nCwd = (int)strlen(zOut); sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath); } return SQLITE_OK; } |
︙ | ︙ | |||
5173 5174 5175 5176 5177 5178 5179 | ** that we always use the same random number sequence. This makes the ** tests repeatable. */ memset(zBuf, 0, nBuf); #if !defined(SQLITE_TEST) { int pid, fd; | | | | | 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 | ** that we always use the same random number sequence. This makes the ** tests repeatable. */ memset(zBuf, 0, nBuf); #if !defined(SQLITE_TEST) { int pid, fd; fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); memcpy(zBuf, &t, sizeof(t)); pid = getpid(); memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); nBuf = sizeof(t) + sizeof(pid); }else{ do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); robust_close(0, fd, __LINE__); } } #endif return nBuf; } |
︙ | ︙ | |||
5604 5605 5606 5607 5608 5609 5610 | }else{ pUnused = sqlite3_malloc(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } } if( fd<0 ){ | | | | | 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 | }else{ pUnused = sqlite3_malloc(sizeof(*pUnused)); if( !pUnused ){ return SQLITE_NOMEM; } } if( fd<0 ){ fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; if( fd<0 && errno==ENOENT && islockfile ){ if( proxyCreateLockPath(path) == SQLITE_OK ){ fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); } } } if( fd<0 ){ openFlags = O_RDONLY; fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; } if( fd<0 ){ sqlite3_free(pUnused); if( islockfile ){ return SQLITE_BUSY; } |
︙ | ︙ | |||
5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 | pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew)); if( pNew==NULL ){ rc = SQLITE_NOMEM; goto end_create_proxy; } memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; dummyVfs.pAppData = (void*)&autolockIoFinder; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pUnused = pUnused; | > > | | | 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 | pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew)); if( pNew==NULL ){ rc = SQLITE_NOMEM; goto end_create_proxy; } memset(pNew, 0, sizeof(unixFile)); pNew->openFlags = openFlags; memset(&dummyVfs, 0, sizeof(dummyVfs)); dummyVfs.pAppData = (void*)&autolockIoFinder; dummyVfs.zName = "dummy"; pUnused->fd = fd; pUnused->flags = openFlags; pNew->pUnused = pUnused; rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: robust_close(pNew, fd, __LINE__); sqlite3_free(pNew); sqlite3_free(pUnused); return rc; } #ifdef SQLITE_TEST /* simulate multiple hosts by creating unique hostid file paths */ |
︙ | ︙ | |||
5729 5730 5731 5732 5733 5734 5735 | pathLen = strlcpy(tPath, cPath, MAXPATHLEN); if( pathLen>MAXPATHLEN || pathLen<6 || (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); goto end_breaklock; } /* read the conch content */ | | > | | | | | 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 | pathLen = strlcpy(tPath, cPath, MAXPATHLEN); if( pathLen>MAXPATHLEN || pathLen<6 || (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); goto end_breaklock; } /* read the conch content */ readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLen<PROXY_PATHINDEX ){ sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen); goto end_breaklock; } /* write it out to the temporary break file */ fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), SQLITE_DEFAULT_FILE_PERMISSIONS); if( fd<0 ){ sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno); goto end_breaklock; } if( osPwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){ sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno); goto end_breaklock; } if( rename(tPath, cPath) ){ sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno); goto end_breaklock; } rc = 0; fprintf(stderr, "broke stale lock on %s\n", cPath); robust_close(pFile, conchFile->h, __LINE__); conchFile->h = fd; conchFile->openFlags = O_RDWR | O_CREAT; end_breaklock: if( rc ){ if( fd>=0 ){ unlink(tPath); robust_close(pFile, fd, __LINE__); } fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); } return rc; } /* Take the requested lock on the conch file and break a stale lock if the |
︙ | ︙ | |||
5786 5787 5788 5789 5790 5791 5792 | /* If the lock failed (busy): * 1st try: get the mod time of the conch, wait 0.5s and try again. * 2nd try: fail if the mod time changed or host id is different, wait * 10 sec and try again * 3rd try: break the lock unless the mod time has changed. */ struct stat buf; | | | | 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 | /* If the lock failed (busy): * 1st try: get the mod time of the conch, wait 0.5s and try again. * 2nd try: fail if the mod time changed or host id is different, wait * 10 sec and try again * 3rd try: break the lock unless the mod time has changed. */ struct stat buf; if( osFstat(conchFile->h, &buf) ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; } if( nTries==1 ){ conchModTime = buf.st_mtimespec; usleep(500000); /* wait 0.5 sec and try the lock again*/ continue; } assert( nTries>1 ); if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ return SQLITE_BUSY; } if( nTries==2 ){ char tBuf[PROXY_MAXCONCHLEN]; int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; } if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ /* don't break the lock if the host id doesn't match */ if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ |
︙ | ︙ | |||
5967 5968 5969 5970 5971 5972 5973 | memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); if( pCtx->lockProxyPath!=NULL ){ strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN); }else{ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); | | | | > | > > < | < < | < < < < | | 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 | memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); if( pCtx->lockProxyPath!=NULL ){ strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN); }else{ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); } writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); robust_ftruncate(conchFile->h, writeSize); rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0); fsync(conchFile->h); /* If we created a new conch file (not just updated the contents of a ** valid conch file), try to match the permissions of the database */ if( rc==SQLITE_OK && createConch ){ struct stat buf; int err = osFstat(pFile->h, &buf); if( err==0 ){ mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); /* try to match the database file R/W permissions, ignore failure */ #ifndef SQLITE_PROXY_DEBUG osFchmod(conchFile->h, cmode); #else do{ rc = osFchmod(conchFile->h, cmode); }while( rc==(-1) && errno==EINTR ); if( rc!=0 ){ int code = errno; fprintf(stderr, "fchmod %o FAILED with %d %s\n", cmode, code, strerror(code)); } else { fprintf(stderr, "fchmod %o SUCCEDED\n",cmode); } }else{ int code = errno; fprintf(stderr, "STAT FAILED[%d] with %d %s\n", err, code, strerror(code)); #endif } } } conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); end_takeconch: OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ robust_close(pFile, pFile->h, __LINE__); } pFile->h = -1; int fd = robust_open(pCtx->dbPath, pFile->openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); if( fd>=0 ){ pFile->h = fd; }else{ rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called during locking */ |
︙ | ︙ | |||
6240 6241 6242 6243 6244 6245 6246 | ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts ** that openFlags will have only one of O_RDONLY or O_RDWR. */ struct statfs fsInfo; struct stat conchInfo; int goLockless = 0; | | | 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 6633 6634 | ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts ** that openFlags will have only one of O_RDONLY or O_RDWR. */ struct statfs fsInfo; struct stat conchInfo; int goLockless = 0; if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { int err = errno; if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; } } if( goLockless ){ pCtx->conchHeld = -1; /* read only FS/ lockless */ |
︙ | ︙ | |||
6531 6532 6533 6534 6535 6536 6537 | ** Most finders simply return a pointer to a fixed sqlite3_io_methods ** object. But the "autolockIoFinder" available on MacOSX does a little ** more than that; it looks at the filesystem type that hosts the ** database file and tries to choose an locking method appropriate for ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ | | > > > > | 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 6934 6935 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 | ** Most finders simply return a pointer to a fixed sqlite3_io_methods ** object. But the "autolockIoFinder" available on MacOSX does a little ** more than that; it looks at the filesystem type that hosts the ** database file and tries to choose an locking method appropriate for ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ 3, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ VFSNAME, /* zName */ \ (void*)&FINDER, /* pAppData */ \ unixOpen, /* xOpen */ \ unixDelete, /* xDelete */ \ unixAccess, /* xAccess */ \ unixFullPathname, /* xFullPathname */ \ unixDlOpen, /* xDlOpen */ \ unixDlError, /* xDlError */ \ unixDlSym, /* xDlSym */ \ unixDlClose, /* xDlClose */ \ unixRandomness, /* xRandomness */ \ unixSleep, /* xSleep */ \ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ unixSetSystemCall, /* xSetSystemCall */ \ unixGetSystemCall, /* xGetSystemCall */ \ unixNextSystemCall, /* xNextSystemCall */ \ } /* ** All default VFSes for unix are contained in the following array. ** ** Note that the sqlite3_vfs.pNext field of the VFS object is modified ** by the SQLite core when the VFS is registered. So the following ** array cannot be const. */ static sqlite3_vfs aVfs[] = { #if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__)) UNIXVFS("unix", autolockIoFinder ), #else UNIXVFS("unix", posixIoFinder ), #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), UNIXVFS("unix-excl", posixIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif #if SQLITE_ENABLE_LOCKING_STYLE UNIXVFS("unix-posix", posixIoFinder ), #if !OS_VXWORKS UNIXVFS("unix-flock", flockIoFinder ), |
︙ | ︙ |
Changes to src/os_win.c.
︙ | ︙ | |||
2742 2743 2744 2745 2746 2747 2748 | /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { | | > > > | 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 | /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { 3, /* iVersion */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ "win32", /* zName */ 0, /* pAppData */ winOpen, /* xOpen */ winDelete, /* xDelete */ winAccess, /* xAccess */ winFullPathname, /* xFullPathname */ winDlOpen, /* xDlOpen */ winDlError, /* xDlError */ winDlSym, /* xDlSym */ winDlClose, /* xDlClose */ winRandomness, /* xRandomness */ winSleep, /* xSleep */ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ 0, /* xSetSystemCall */ 0, /* xGetSystemCall */ 0, /* xNextSystemCall */ }; #ifndef SQLITE_OMIT_WAL /* get memory map allocation granularity */ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); GetSystemInfo(&winSysInfo); assert(winSysInfo.dwAllocationGranularity > 0); |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 | PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pPg))); return rc; } #ifndef SQLITE_OMIT_WAL /* ** This function is invoked once for each page that has already been ** written into the log file when a WAL transaction is rolled back. ** Parameter iPg is the page number of said page. The pCtx argument ** is actually a pointer to the Pager structure. | > > > > > > > > > > > > > > > > > > > > > > | 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 | PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); PAGERTRACE(("FETCH %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_pagehash(pPg))); return rc; } /* ** Update the value of the change-counter at offsets 24 and 92 in ** the header and the sqlite version number at offset 96. ** ** This is an unconditional update. See also the pager_incr_changecounter() ** routine which only updates the change-counter if the update is actually ** needed, as determined by the pPager->changeCountDone state variable. */ static void pager_write_changecounter(PgHdr *pPg){ u32 change_counter; /* Increment the value just read and write it back to byte 24. */ change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1; put32bits(((char*)pPg->pData)+24, change_counter); /* Also store the SQLite version number in bytes 96..99 and in ** bytes 92..95 store the change counter for which the version number ** is valid. */ put32bits(((char*)pPg->pData)+92, change_counter); put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER); } #ifndef SQLITE_OMIT_WAL /* ** This function is invoked once for each page that has already been ** written into the log file when a WAL transaction is rolled back. ** Parameter iPg is the page number of said page. The pCtx argument ** is actually a pointer to the Pager structure. |
︙ | ︙ | |||
2917 2918 2919 2920 2921 2922 2923 | rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } return rc; } | < < < < < < < < < < < < < < < < < < < < < < < | | 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 | rc = pagerUndoCallback((void *)pPager, pList->pgno); pList = pNext; } return rc; } /* ** This function is a wrapper around sqlite3WalFrames(). As well as logging ** the contents of the list of pages headed by pList (connected by pDirty), ** this function notifies any active backup processes that the pages have ** changed. ** ** The list of pages passed into this routine is always sorted by page number. ** Hence, if page 1 appears anywhere on the list, it will be the first page. */ static int pagerWalFrames( Pager *pPager, /* Pager object */ PgHdr *pList, /* List of frames to log */ |
︙ | ︙ | |||
6596 6597 6598 6599 6600 6601 6602 | */ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ return &pPager->pBackup; } #ifndef SQLITE_OMIT_WAL /* | | > > > > | < | | > > > | 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 | */ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){ return &pPager->pBackup; } #ifndef SQLITE_OMIT_WAL /* ** This function is called when the user invokes "PRAGMA wal_checkpoint", ** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() ** or wal_blocking_checkpoint() API functions. ** ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){ int rc = SQLITE_OK; if( pPager->pWal ){ rc = sqlite3WalCheckpoint(pPager->pWal, eMode, pPager->xBusyHandler, pPager->pBusyHandlerArg, pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace, pnLog, pnCkpt ); } return rc; } int sqlite3PagerWalCallback(Pager *pPager){ return sqlite3WalCallback(pPager->pWal); } |
︙ | ︙ | |||
6631 6632 6633 6634 6635 6636 6637 | */ 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 ){ | | | | 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 | */ 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 exclusive lock failed, release the ** pending lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } return rc; } /* |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
134 135 136 137 138 139 140 | int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | int sqlite3PagerSync(Pager *pPager); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager); /* Functions used to query pager state and configuration. */ u8 sqlite3PagerIsreadonly(Pager*); |
︙ | ︙ |
Changes to src/pragma.c.
︙ | ︙ | |||
381 382 383 384 385 386 387 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); pParse->nMem += 2; addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqlite3VdbeChangeP1(v, addr, iDb); sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ | | < | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 | sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC); pParse->nMem += 2; addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqlite3VdbeChangeP1(v, addr, iDb); sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } }else |
︙ | ︙ | |||
692 693 694 695 696 697 698 | */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; if( !zRight ){ i64 cacheSize = pDb->pSchema->cache_size; returnSingleInt(pParse, "cache_size", &cacheSize); }else{ | | < | 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; if( !zRight ){ i64 cacheSize = pDb->pSchema->cache_size; returnSingleInt(pParse, "cache_size", &cacheSize); }else{ int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } }else /* ** PRAGMA temp_store |
︙ | ︙ | |||
1386 1387 1388 1389 1390 1391 1392 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } }else #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ #ifndef SQLITE_OMIT_WAL /* | | > > > > > > > > > > > > > > > | > | 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 | sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1); } }else #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ #ifndef SQLITE_OMIT_WAL /* ** PRAGMA [database.]wal_checkpoint = passive|full|restart ** ** Checkpoint the database. */ if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){ int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED); int eMode = SQLITE_CHECKPOINT_PASSIVE; if( zRight ){ if( sqlite3StrICmp(zRight, "full")==0 ){ eMode = SQLITE_CHECKPOINT_FULL; }else if( sqlite3StrICmp(zRight, "restart")==0 ){ eMode = SQLITE_CHECKPOINT_RESTART; } } if( sqlite3ReadSchema(pParse) ) goto pragma_out; sqlite3VdbeSetNumCols(v, 3); pParse->nMem = 3; sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC); sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC); sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC); sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1); sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3); }else /* ** PRAGMA wal_autocheckpoint ** PRAGMA wal_autocheckpoint = N ** ** Configure a database connection to automatically checkpoint a database |
︙ | ︙ |
Changes to src/prepare.c.
︙ | ︙ | |||
140 141 142 143 144 145 146 | int size; Table *pTab; Db *pDb; char const *azArg[4]; int meta[5]; InitData initData; char const *zMasterSchema; | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | int size; Table *pTab; Db *pDb; char const *azArg[4]; int meta[5]; InitData initData; char const *zMasterSchema; char const *zMasterName; int openedTransaction = 0; /* ** The master database table has a structure like this */ static const char master_schema[] = "CREATE TABLE sqlite_master(\n" |
︙ | ︙ | |||
277 278 279 280 281 282 283 | } }else{ DbSetProperty(db, iDb, DB_Empty); } pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ | | < | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | } }else{ DbSetProperty(db, iDb, DB_Empty); } pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } /* ** file_format==1 Version 3.0.0. ** file_format==2 Version 3.1.3. // ALTER TABLE ADD COLUMN |
︙ | ︙ |
Changes to src/printf.c.
︙ | ︙ | |||
396 397 398 399 400 401 402 | v = va_arg(ap,i64); }else if( flag_long ){ v = va_arg(ap,long int); }else{ v = va_arg(ap,int); } if( v<0 ){ | > > > | > | 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | v = va_arg(ap,i64); }else if( flag_long ){ v = va_arg(ap,long int); }else{ v = va_arg(ap,int); } if( v<0 ){ if( v==SMALLEST_INT64 ){ longvalue = ((u64)1)<<63; }else{ longvalue = -v; } prefix = '-'; }else{ longvalue = v; if( flag_plussign ) prefix = '+'; else if( flag_blanksign ) prefix = ' '; else prefix = 0; } |
︙ | ︙ |
Changes to src/select.c.
︙ | ︙ | |||
2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 | ** have a WHERE clause. ** ** (20) If the sub-query is a compound select, then it must not use ** an ORDER BY clause. Ticket #3773. We could relax this constraint ** somewhat by saying that the terms of the ORDER BY clause must ** appear as unmodified result columns in the outer query. But ** have other optimizations in mind to deal with that case. ** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. ** ** If flattening is not attempted, this routine is a no-op and returns 0. ** If flattening is attempted this routine returns 1. | > > > | 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 | ** have a WHERE clause. ** ** (20) If the sub-query is a compound select, then it must not use ** an ORDER BY clause. Ticket #3773. We could relax this constraint ** somewhat by saying that the terms of the ORDER BY clause must ** appear as unmodified result columns in the outer query. But ** have other optimizations in mind to deal with that case. ** ** (21) The subquery does not use LIMIT or the outer query is not ** DISTINCT. (See ticket [752e1646fc]). ** ** In this routine, the "p" parameter is a pointer to the outer query. ** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query ** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. ** ** If flattening is not attempted, this routine is a no-op and returns 0. ** If flattening is attempted this routine returns 1. |
︙ | ︙ | |||
2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 | return 0; /* Restriction (6) */ } if( p->pOrderBy && pSub->pOrderBy ){ return 0; /* Restriction (11) */ } if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */ if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */ /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is ** not used as the right operand of an outer join. Examples of why this ** is not allowed: ** ** t1 LEFT OUTER JOIN (t2 JOIN t3) | > > > | 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 | return 0; /* Restriction (6) */ } if( p->pOrderBy && pSub->pOrderBy ){ return 0; /* Restriction (11) */ } if( isAgg && pSub->pOrderBy ) return 0; /* Restriction (16) */ if( pSub->pLimit && p->pWhere ) return 0; /* Restriction (19) */ if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){ return 0; /* Restriction (21) */ } /* OBSOLETE COMMENT 1: ** Restriction 3: If the subquery is a join, make sure the subquery is ** not used as the right operand of an outer join. Examples of why this ** is not allowed: ** ** t1 LEFT OUTER JOIN (t2 JOIN t3) |
︙ | ︙ | |||
3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 | for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); } pAggInfo->directMode = 0; sqlite3ExprCacheClear(pParse); } /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are distributed in various ways depending on the ** contents of the SelectDest structure pointed to by argument pDest ** as follows: ** | > > > > > > > > > > > > > > > > > > > > > > > > > > | 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 | for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){ sqlite3ExprCode(pParse, pC->pExpr, pC->iMem); } pAggInfo->directMode = 0; sqlite3ExprCacheClear(pParse); } /* ** Add a single OP_Explain instruction to the VDBE to explain a simple ** count(*) query ("SELECT count(*) FROM pTab"). */ #ifndef SQLITE_OMIT_EXPLAIN static void explainSimpleCount( Parse *pParse, /* Parse context */ Table *pTab, /* Table being queried */ Index *pIdx /* Index used to optimize scan, or NULL */ ){ if( pParse->explain==2 ){ char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)", pTab->zName, pIdx ? "USING COVERING INDEX " : "", pIdx ? pIdx->zName : "", pTab->nRowEst ); sqlite3VdbeAddOp4( pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC ); } } #else # define explainSimpleCount(a,b,c) #endif /* ** Generate code for the SELECT statement given in the p argument. ** ** The results are distributed in various ways depending on the ** contents of the SelectDest structure pointed to by argument pDest ** as follows: ** |
︙ | ︙ | |||
4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 | /* Open a read-only cursor, execute the OP_Count, close the cursor. */ sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb); if( pKeyInfo ){ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); }else #endif /* SQLITE_OMIT_BTREECOUNT */ { /* Check if the query is of one of the following forms: ** ** SELECT min(x) FROM ... ** SELECT max(x) FROM ... | > | 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 | /* Open a read-only cursor, execute the OP_Count, close the cursor. */ sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb); if( pKeyInfo ){ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF); } sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, iCsr); explainSimpleCount(pParse, pTab, pBest); }else #endif /* SQLITE_OMIT_BTREECOUNT */ { /* Check if the query is of one of the following forms: ** ** SELECT min(x) FROM ... ** SELECT max(x) FROM ... |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
415 416 417 418 419 420 421 422 423 424 425 426 427 428 | char nullvalue[20]; /* The text to print when a NULL comes back from ** the database */ struct previous_mode_data explainPrev; /* Holds the mode information just before ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; /* ** These are the allowed modes. */ | > | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | char nullvalue[20]; /* The text to print when a NULL comes back from ** the database */ struct previous_mode_data explainPrev; /* Holds the mode information just before ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; /* ** These are the allowed modes. */ |
︙ | ︙ | |||
1846 1847 1848 1849 1850 1851 1852 | fprintf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif | | | 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 | fprintf(stderr, "Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); rc = 1; } }else #endif if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){ const char *zFile = azArg[1]; if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ fclose(p->pLog); p->pLog = 0; } if( strcmp(zFile,"stdout")==0 ){ p->pLog = stdout; |
︙ | ︙ | |||
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 | printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } printf("\n"); } } sqlite3_free_table(azResult); }else if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ open_db(p); sqlite3_busy_timeout(p->db, atoi(azArg[1])); }else | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 | printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : ""); } printf("\n"); } } sqlite3_free_table(azResult); }else if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ } aCtrl[] = { { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, { "pghdrsz", SQLITE_TESTCTRL_PGHDRSZ }, { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, }; int testctrl = -1; int rc = 0; int i, n; open_db(p); /* convert testctrl text option to value. allow any unique prefix ** of the option name, or a numerical value. */ n = strlen(azArg[1]); for(i=0; i<sizeof(aCtrl)/sizeof(aCtrl[0]); i++){ if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){ if( testctrl<0 ){ testctrl = aCtrl[i].ctrlCode; }else{ fprintf(stderr, "ambiguous option name: \"%s\"\n", azArg[i]); testctrl = -1; break; } } } if( testctrl<0 ) testctrl = atoi(azArg[1]); if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){ fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); }else{ switch(testctrl){ /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_OPTIMIZATIONS: case SQLITE_TESTCTRL_RESERVE: if( nArg==3 ){ int opt = (int)strtol(azArg[2], 0, 0); rc = sqlite3_test_control(testctrl, p->db, opt); printf("%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int) */ case SQLITE_TESTCTRL_PRNG_SAVE: case SQLITE_TESTCTRL_PRNG_RESTORE: case SQLITE_TESTCTRL_PRNG_RESET: case SQLITE_TESTCTRL_PGHDRSZ: if( nArg==2 ){ rc = sqlite3_test_control(testctrl); printf("%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes no options\n", azArg[1]); } break; /* sqlite3_test_control(int, uint) */ case SQLITE_TESTCTRL_PENDING_BYTE: if( nArg==3 ){ unsigned int opt = (unsigned int)atoi(azArg[2]); rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single unsigned" " int option\n", azArg[1]); } break; /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: case SQLITE_TESTCTRL_ALWAYS: if( nArg==3 ){ int opt = atoi(azArg[2]); rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); } break; /* sqlite3_test_control(int, char *) */ #ifdef SQLITE_N_KEYWORD case SQLITE_TESTCTRL_ISKEYWORD: if( nArg==3 ){ const char *opt = azArg[2]; rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); } break; #endif case SQLITE_TESTCTRL_BITVEC_TEST: case SQLITE_TESTCTRL_FAULT_INSTALL: case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: case SQLITE_TESTCTRL_SCRATCHMALLOC: default: fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } } }else if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){ open_db(p); sqlite3_busy_timeout(p->db, atoi(azArg[1])); }else if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){ enableTimer = booleanValue(azArg[1]); }else if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){ int j; assert( nArg<=ArraySize(azArg) ); for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){ |
︙ | ︙ | |||
2359 2360 2361 2362 2363 2364 2365 | } free(zSql); zSql = 0; nSql = 0; } } if( zSql ){ | > | > | 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 | } free(zSql); zSql = 0; nSql = 0; } } if( zSql ){ if( !_all_whitespace(zSql) ){ fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); } free(zSql); } free(zLine); return errCnt; } /* |
︙ | ︙ | |||
2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 | " -html set output mode to HTML\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -separator 'x' set output field separator (|)\n" " -stats print memory stats before each finalize\n" " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" ; static void usage(int showDetail){ fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n" "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist.\n", Argv0); if( showDetail ){ | > > > > | 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 | " -html set output mode to HTML\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -separator 'x' set output field separator (|)\n" " -stats print memory stats before each finalize\n" " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif ; static void usage(int showDetail){ fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n" "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist.\n", Argv0); if( showDetail ){ |
︙ | ︙ | |||
2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 | if( c=='K' ){ szHeap *= 1000; break; } if( c=='G' ){ szHeap *= 1000000000; break; } } if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif } } if( i<argc ){ #if defined(SQLITE_OS_OS2) && SQLITE_OS_OS2 data.zDbFilename = (const char *)convertCpPathToUtf8( argv[i++] ); #else data.zDbFilename = argv[i++]; | > > > > > > > > > > > > > > > > > > > | 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 | if( c=='K' ){ szHeap *= 1000; break; } if( c=='G' ){ szHeap *= 1000000000; break; } } if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000; #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif #ifdef SQLITE_ENABLE_VFSTRACE }else if( strcmp(argv[i],"-vfstrace")==0 ){ extern int vfstrace_register( const char *zTraceName, const char *zOldVfsName, int (*xOut)(const char*,void*), void *pOutArg, int makeDefault ); vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); #endif }else if( strcmp(argv[i],"-vfs")==0 ){ sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]); if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]); exit(1); } } } if( i<argc ){ #if defined(SQLITE_OS_OS2) && SQLITE_OS_OS2 data.zDbFilename = (const char *)convertCpPathToUtf8( argv[i++] ); #else data.zDbFilename = argv[i++]; |
︙ | ︙ | |||
2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 | return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } | > > > > | 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 | return 0; }else if( strcmp(z,"-interactive")==0 ){ stdin_is_interactive = 1; }else if( strcmp(z,"-batch")==0 ){ stdin_is_interactive = 0; }else if( strcmp(z,"-heap")==0 ){ i++; }else if( strcmp(z,"-vfs")==0 ){ i++; }else if( strcmp(z,"-vfstrace")==0 ){ i++; }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ usage(1); }else{ fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); return 1; } |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
474 475 476 477 478 479 480 481 482 483 484 485 486 487 | #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ /* ** CAPI3REF: Device Characteristics ** ** The xDeviceCharacteristics method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these ** bit values expressing I/O characteristics of the mass storage ** device that holds the file that the [sqlite3_io_methods] | > > | 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ /* Reserved: 0x00F00000 */ /* ** CAPI3REF: Device Characteristics ** ** The xDeviceCharacteristics method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these ** bit values expressing I/O characteristics of the mass storage ** device that holds the file that the [sqlite3_io_methods] |
︙ | ︙ | |||
887 888 889 890 891 892 893 894 895 896 | ** ^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. */ typedef struct sqlite3_vfs sqlite3_vfs; struct sqlite3_vfs { | > > > > > > > > > > > > > | | 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 | ** ^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. ** ** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces ** are not used by the SQLite core. These optional interfaces are provided ** by some VFSes to facilitate testing of the VFS code. By overriding ** system calls with functions under its control, a test program can ** simulate faults and error conditions that would otherwise be difficult ** or impossible to induce. The set of system calls that can be overridden ** varies from one VFS to another, and from one version of the same VFS to the ** next. Applications that use these interfaces must be prepared for any ** or all of these interfaces to be NULL or for their behavior to change ** from one release to the next. Applications must not attempt to access ** any of these methods if the iVersion of the VFS is less than 3. */ typedef struct sqlite3_vfs sqlite3_vfs; typedef void (*sqlite3_syscall_ptr)(void); struct sqlite3_vfs { int iVersion; /* Structure version number (currently 3) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int flags, int *pOutFlags); |
︙ | ︙ | |||
916 917 918 919 920 921 922 923 924 925 926 927 928 929 | /* ** The methods above are in version 1 of the sqlite_vfs object ** definition. Those that follow are added in version 2 or later */ int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ }; /* ** CAPI3REF: Flags for the xAccess VFS method | > > > > > > > | 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 | /* ** The methods above are in version 1 of the sqlite_vfs object ** definition. Those that follow are added in version 2 or later */ int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. ** Those below are for version 3 and greater. */ int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); /* ** The methods above are in versions 1 through 3 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ }; /* ** CAPI3REF: Flags for the xAccess VFS method |
︙ | ︙ | |||
1100 1101 1102 1103 1104 1105 1106 | /* ** CAPI3REF: Configure database connections ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single | | < < < | | < < | | 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | /* ** CAPI3REF: Configure database connections ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). ** ** The second argument to sqlite3_db_config(D,V,...) is the ** [SQLITE_DBCONIG_LOOKASIDE | configuration verb] - an integer code ** that indicates what aspect of the [database connection] is being configured. ** Subsequent arguments vary depending on the configuration verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ int sqlite3_db_config(sqlite3*, int op, ...); /* |
︙ | ︙ | |||
1335 1336 1337 1338 1339 1340 1341 | ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), ** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte | | > > | 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 | ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), ** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte ** boundary or subsequent behavior of SQLite will be undefined. ** The minimum allocation size is capped at 2^12. Reasonable values ** for the minimum allocation size are 2^5 through 2^8.</dd> ** ** <dt>SQLITE_CONFIG_MUTEX</dt> ** <dd> ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place ** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the ** content of the [sqlite3_mutex_methods] structure before the call to |
︙ | ︙ | |||
1456 1457 1458 1459 1460 1461 1462 1463 1464 | ** 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> */ | > > > > > > > > > > > > > > > > > > > > | > > | 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 | ** 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> ** ** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt> ** <dd> ^This option is used to enable or disable the enforcement of ** [foreign key constraints]. There should be two additional arguments. ** The first argument is an integer which is 0 to disable FK enforcement, ** positive to enable FK enforcement or negative to leave FK enforcement ** unchanged. The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether FK enforcement is off or on ** following this call. The second parameter may be a NULL pointer, in ** which case the FK enforcement setting is not reported back. </dd> ** ** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt> ** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers]. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable triggers, ** positive to enable trigers or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back. </dd> ** ** </dl> */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
︙ | ︙ | |||
2662 2663 2664 2665 2666 2667 2668 | ** 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 ** | | | 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 | ** 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 ** and only if the [prepared statement] X makes no direct changes to ** the content of the database file. ** ** Note that [application-defined SQL functions] or ** [virtual tables] might change the database indirectly as a side effect. ** ^(For example, if an application defines a function "eval()" that ** calls [sqlite3_exec()], then the following SQL statement would |
︙ | ︙ | |||
2930 2931 2932 2933 2934 2935 2936 | ** interface returns a pointer to a zero-terminated UTF-8 string ** and sqlite3_column_name16() returns a pointer to a zero-terminated ** UTF-16 string. ^The first parameter is the [prepared statement] ** that implements the [SELECT] statement. ^The second parameter is the ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] | | > > | 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 | ** interface returns a pointer to a zero-terminated UTF-8 string ** and sqlite3_column_name16() returns a pointer to a zero-terminated ** UTF-16 string. ^The first parameter is the [prepared statement] ** that implements the [SELECT] statement. ^The second parameter is the ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] ** is destroyed by [sqlite3_finalize()] or until the statement is automatically ** reprepared by the first call to [sqlite3_step()] for a particular run ** or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine ** (for example during a conversion from UTF-8 to UTF-16) then a ** NULL pointer is returned. ** ** ^The name of a result column is the value of the "AS" clause for |
︙ | ︙ | |||
2956 2957 2958 2959 2960 2961 2962 | ** table column that is the origin of a particular result column in ** [SELECT] statement. ** ^The name of the database or table or column can be returned as ** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed | | > > | 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 | ** table column that is the origin of a particular result column in ** [SELECT] statement. ** ^The name of the database or table or column can be returned as ** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed ** using [sqlite3_finalize()] or until the statement is automatically ** reprepared by the first call to [sqlite3_step()] for a particular run ** or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the ** database, table, and column. ** ** ^The first argument to these interfaces is a [prepared statement]. ** ^These functions return information about the Nth result column returned by |
︙ | ︙ | |||
5530 5531 5532 5533 5534 5535 5536 | ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> ** <dd>This parameter returns the number malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; | | < | < | < | 5575 5576 5577 5578 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 | ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_USED</dt> ** <dd>This parameter returns the number of lookaside memory slots currently ** checked out.</dd>)^ ** ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt> ** <dd>This parameter returns the number malloc attempts that were ** satisfied using lookaside memory. Only the high-water value is meaningful; ** the current value is always zero.)^ ** ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt> ** <dd>This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to the amount of ** memory requested being larger than the lookaside slot size. ** Only the high-water value is meaningful; ** the current value is always zero.)^ ** ** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt> ** <dd>This parameter returns the number malloc attempts that might have ** been satisfied using lookaside memory but failed due to all lookaside ** memory already being in use. ** Only the high-water value is meaningful; ** the current value is always zero.)^ ** ** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt> ** <dd>This parameter returns the approximate number of of bytes of heap ** memory used by all pager caches associated with the database connection.)^ ** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. ** ** ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt> |
︙ | ︙ | |||
6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 | ** connection D. ^If the database connection D is not in ** [WAL | write-ahead log mode] then this interface is a harmless no-op. ** ** ^The [wal_checkpoint pragma] can be used to invoke this interface ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 | ** connection D. ^If the database connection D is not in ** [WAL | write-ahead log mode] then this interface is a harmless no-op. ** ** ^The [wal_checkpoint pragma] can be used to invoke this interface ** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] can be used to cause this interface to be ** run whenever the WAL reaches a certain size threshold. ** ** See also: [sqlite3_wal_checkpoint_v2()] */ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Checkpoint a database ** ** Run a checkpoint operation on WAL database zDb attached to database ** handle db. The specific operation is determined by the value of the ** eMode parameter: ** ** <dl> ** <dt>SQLITE_CHECKPOINT_PASSIVE<dd> ** Checkpoint as many frames as possible without waiting for any database ** readers or writers to finish. Sync the db file if all frames in the log ** are checkpointed. This mode is the same as calling ** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. ** ** <dt>SQLITE_CHECKPOINT_FULL<dd> ** This mode blocks (calls the busy-handler callback) until there is no ** database writer and all readers are reading from the most recent database ** snapshot. It then checkpoints all frames in the log file and syncs the ** database file. This call blocks database writers while it is running, ** but not database readers. ** ** <dt>SQLITE_CHECKPOINT_RESTART<dd> ** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after ** checkpointing the log file it blocks (calls the busy-handler callback) ** until all readers are reading from the database file only. This ensures ** that the next client to write to the database file restarts the log file ** from the beginning. This call blocks database writers while it is running, ** but not database readers. ** </dl> ** ** If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to ** the total number of checkpointed frames (including any that were already ** checkpointed when this function is called). *pnLog and *pnCkpt may be ** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. ** If no values are available because of an error, they are both set to -1 ** before returning to communicate this to the caller. ** ** All calls obtain an exclusive "checkpoint" lock on the database file. If ** any other process is running a checkpoint operation at the same time, the ** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a ** busy-handler configured, it will not be invoked in this case. ** ** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive ** "writer" lock on the database file. If the writer lock cannot be obtained ** immediately, and a busy-handler is configured, it is invoked and the writer ** lock retried until either the busy-handler returns 0 or the lock is ** successfully obtained. The busy-handler is also invoked while waiting for ** database readers as described above. If the busy-handler returns 0 before ** the writer lock is obtained or while waiting for database readers, the ** checkpoint operation proceeds from that point in the same way as ** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible ** without blocking any further. SQLITE_BUSY is returned in this case. ** ** If parameter zDb is NULL or points to a zero length string, then the ** specified operation is attempted on all WAL databases. In this case the ** values written to output parameters *pnLog and *pnCkpt are undefined. If ** an SQLITE_BUSY error is encountered when processing one or more of the ** attached WAL databases, the operation is still attempted on any remaining ** attached databases and SQLITE_BUSY is returned to the caller. If any other ** error occurs while processing an attached database, processing is abandoned ** and the error code returned to the caller immediately. If no error ** (SQLITE_BUSY or otherwise) is encountered while processing the attached ** databases, SQLITE_OK is returned. ** ** If database zDb is the name of an attached database that is not in WAL ** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If ** zDb is not NULL (or a zero length string) and is not the name of any ** attached database, SQLITE_ERROR is returned to the caller. */ int sqlite3_wal_checkpoint_v2( sqlite3 *db, /* Database handle */ const char *zDb, /* Name of attached database (or NULL) */ int eMode, /* SQLITE_CHECKPOINT_* value */ int *pnLog, /* OUT: Size of WAL log in frames */ int *pnCkpt /* OUT: Total number of frames checkpointed */ ); /* ** CAPI3REF: Checkpoint operation parameters ** ** These constants can be used as the 3rd parameter to ** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] ** documentation for additional information about the meaning and use of ** each of these values. */ #define SQLITE_CHECKPOINT_PASSIVE 0 #define SQLITE_CHECKPOINT_FULL 1 #define SQLITE_CHECKPOINT_RESTART 2 /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif #endif |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
667 668 669 670 671 672 673 674 675 676 677 678 679 680 | }; /* ** An instance of the following structure stores a database schema. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ Hash fkeyHash; /* All foreign keys by referenced table name */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ u8 file_format; /* Schema format version for this file */ u8 enc; /* Text encoding used by this database */ | > | 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 | }; /* ** An instance of the following structure stores a database schema. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ int iGeneration; /* Generation counter. Incremented with each change */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ Hash fkeyHash; /* All foreign keys by referenced table name */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ u8 file_format; /* Schema format version for this file */ u8 enc; /* Text encoding used by this database */ |
︙ | ︙ | |||
920 921 922 923 924 925 926 927 928 929 930 931 932 933 | #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 */ | > | 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 | #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 */ #define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */ /* ** 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 */ |
︙ | ︙ | |||
1619 1620 1621 1622 1623 1624 1625 | */ struct Expr { u8 op; /* Operation performed by this node */ char affinity; /* The affinity of the column or 0 if not a column */ u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ | | | 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 | */ struct Expr { u8 op; /* Operation performed by this node */ char affinity; /* The affinity of the column or 0 if not a column */ u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ int iValue; /* Non-negative integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no ** space is allocated for the fields below this point. An attempt to ** access them will result in a segfault or malfunction. *********************************************************************/ |
︙ | ︙ | |||
2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 | Trigger *pTrigger; /* Trigger this program was coded from */ int orconf; /* Default ON CONFLICT policy */ SubProgram *pProgram; /* Program implementing pTrigger/orconf */ u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */ TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ }; /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. ** ** The structure is divided into two parts. When the parser and code ** generate call themselves recursively, the first part of the structure | > > > > > > > > > | 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 | Trigger *pTrigger; /* Trigger this program was coded from */ int orconf; /* Default ON CONFLICT policy */ SubProgram *pProgram; /* Program implementing pTrigger/orconf */ u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */ TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ }; /* ** The yDbMask datatype for the bitmask of all attached databases. */ #if SQLITE_MAX_ATTACHED>30 typedef sqlite3_uint64 yDbMask; #else typedef unsigned int yDbMask; #endif /* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. ** ** The structure is divided into two parts. When the parser and code ** generate call themselves recursively, the first part of the structure |
︙ | ︙ | |||
2167 2168 2169 2170 2171 2172 2173 | int iTable; /* Table cursor number */ int iColumn; /* Table column number */ u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ | | | | 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 | int iTable; /* Table cursor number */ int iColumn; /* Table column number */ u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ int iReg; /* Reg with value of this column. 0 means none. */ int lru; /* Least recently used entry has the smallest value */ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ yDbMask writeMask; /* Start a write transaction on these databases */ yDbMask cookieMask; /* Bitmask of schema verified databases */ u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ #ifndef SQLITE_OMIT_SHARED_CACHE int nTableLock; /* Number of locks in aTableLock */ TableLock *aTableLock; /* Required table locks for shared-cache mode */ |
︙ | ︙ | |||
2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 | CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); Expr *sqlite3ExprSetColl(Expr*, CollSeq*); Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*); int sqlite3CheckCollSeq(Parse *, CollSeq *); int sqlite3CheckObjectName(Parse *, const char *); void sqlite3VdbeSetChanges(sqlite3 *, int); const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); | > > > > | 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 | CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName); CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr); Expr *sqlite3ExprSetColl(Expr*, CollSeq*); Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*); int sqlite3CheckCollSeq(Parse *, CollSeq *); int sqlite3CheckObjectName(Parse *, const char *); void sqlite3VdbeSetChanges(sqlite3 *, int); int sqlite3AddInt64(i64*,i64); int sqlite3SubInt64(i64*,i64); int sqlite3MulInt64(i64*,i64); int sqlite3AbsInt32(int); const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(sqlite3 *); |
︙ | ︙ | |||
3037 3038 3039 3040 3041 3042 3043 | int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); const char *sqlite3JournalModename(int); | | | 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 | int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); int sqlite3Reprepare(Vdbe*); void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); int sqlite3TempInMemory(const sqlite3*); VTable *sqlite3GetVTable(sqlite3*, Table*); const char *sqlite3JournalModename(int); int sqlite3Checkpoint(sqlite3*, int, int, int*, int*); int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int); /* Declarations for functions in fkey.c. All of these are replaced by ** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign ** key functionality is available. If OMIT_TRIGGER is defined but ** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In ** this case foreign keys are parsed, but no other functionality is |
︙ | ︙ |
Changes to src/tclsqlite.c.
︙ | ︙ | |||
2464 2465 2466 2467 2468 2469 2470 | zProfile = Tcl_GetStringFromObj(objv[2], &len); if( zProfile && len>0 ){ pDb->zProfile = Tcl_Alloc( len + 1 ); memcpy(pDb->zProfile, zProfile, len+1); }else{ pDb->zProfile = 0; } | | | 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 | zProfile = Tcl_GetStringFromObj(objv[2], &len); if( zProfile && len>0 ){ pDb->zProfile = Tcl_Alloc( len + 1 ); memcpy(pDb->zProfile, zProfile, len+1); }else{ pDb->zProfile = 0; } #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( pDb->zProfile ){ pDb->interp = interp; sqlite3_profile(pDb->db, DbProfileHandler, pDb); }else{ sqlite3_profile(pDb->db, 0, 0); } #endif |
︙ | ︙ | |||
2648 2649 2650 2651 2652 2653 2654 | zTrace = Tcl_GetStringFromObj(objv[2], &len); if( zTrace && len>0 ){ pDb->zTrace = Tcl_Alloc( len + 1 ); memcpy(pDb->zTrace, zTrace, len+1); }else{ pDb->zTrace = 0; } | | | 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 | zTrace = Tcl_GetStringFromObj(objv[2], &len); if( zTrace && len>0 ){ pDb->zTrace = Tcl_Alloc( len + 1 ); memcpy(pDb->zTrace, zTrace, len+1); }else{ pDb->zTrace = 0; } #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) if( pDb->zTrace ){ pDb->interp = interp; sqlite3_trace(pDb->db, DbTraceHandler, pDb); }else{ sqlite3_trace(pDb->db, 0, 0); } #endif |
︙ | ︙ | |||
3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 | 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*); #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); Zipvfs_Init(interp); #endif Sqliteconfig_Init(interp); | > > > | 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 | 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*); extern int SqlitetestSyscall_Init(Tcl_Interp*); extern int Sqlitetestfuzzer_Init(Tcl_Interp*); extern int Sqlitetestwholenumber_Init(Tcl_Interp*); #ifdef SQLITE_ENABLE_ZIPVFS extern int Zipvfs_Init(Tcl_Interp*); Zipvfs_Init(interp); #endif Sqliteconfig_Init(interp); |
︙ | ︙ | |||
3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 | 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 } | > > > | 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 | Sqlitetestintarray_Init(interp); Sqlitetestvfs_Init(interp); SqlitetestStat_Init(interp); Sqlitetestrtree_Init(interp); Sqlitequota_Init(interp); Sqlitemultiplex_Init(interp); SqliteSuperlock_Init(interp); SqlitetestSyscall_Init(interp); Sqlitetestfuzzer_Init(interp); Sqlitetestwholenumber_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.
︙ | ︙ | |||
4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 | rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_CHUNK_SIZE, (void *)&nSize); if( rc ){ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_ERROR; } return TCL_OK; } /* ** tclcmd: file_control_lockproxy_test DB PWD ** ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 | rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_CHUNK_SIZE, (void *)&nSize); if( rc ){ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_ERROR; } return TCL_OK; } /* ** tclcmd: file_control_sizehint_test DB DBNAME SIZE ** ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. */ static int file_control_sizehint_test( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3_int64 nSize; /* Hinted size */ char *zDb; /* Db name ("main", "temp" etc.) */ sqlite3 *db; /* Database handle */ int rc; /* file_control() return code */ if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SIZE"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) || Tcl_GetWideIntFromObj(interp, objv[3], &nSize) ){ return TCL_ERROR; } zDb = Tcl_GetString(objv[2]); if( zDb[0]=='\0' ) zDb = NULL; rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_SIZE_HINT, (void *)&nSize); if( rc ){ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_ERROR; } return TCL_OK; } /* ** tclcmd: file_control_lockproxy_test DB PWD ** ** This TCL command runs the sqlite3_file_control interface and ** verifies correct operation of the SQLITE_GET_LOCKPROXYFILE and ** SQLITE_SET_LOCKPROXYFILE verbs. |
︙ | ︙ | |||
5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 | if( objc==3 ){ zDb = Tcl_GetString(objv[2]); } rc = sqlite3_wal_checkpoint(db, zDb); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: test_sqlite3_log ?SCRIPT? */ static struct LogCallback { Tcl_Interp *pInterp; Tcl_Obj *pObj; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 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 | if( objc==3 ){ zDb = Tcl_GetString(objv[2]); } rc = sqlite3_wal_checkpoint(db, zDb); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_wal_checkpoint_v2 db MODE ?NAME? ** ** This command calls the wal_checkpoint_v2() function with the specified ** mode argument (passive, full or restart). If present, the database name ** NAME is passed as the second argument to wal_checkpoint_v2(). If it the ** NAME argument is not present, a NULL pointer is passed instead. ** ** If wal_checkpoint_v2() returns any value other than SQLITE_BUSY or ** SQLITE_OK, then this command returns TCL_ERROR. The Tcl result is set ** to the error message obtained from sqlite3_errmsg(). ** ** Otherwise, this command returns a list of three integers. The first integer ** is 1 if SQLITE_BUSY was returned, or 0 otherwise. The following two integers ** are the values returned via the output paramaters by wal_checkpoint_v2() - ** the number of frames in the log and the number of frames in the log ** that have been checkpointed. */ static int test_wal_checkpoint_v2( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zDb = 0; sqlite3 *db; int rc; int eMode; int nLog = -555; int nCkpt = -555; Tcl_Obj *pRet; const char * aMode[] = { "passive", "full", "restart", 0 }; assert( SQLITE_CHECKPOINT_PASSIVE==0 ); assert( SQLITE_CHECKPOINT_FULL==1 ); assert( SQLITE_CHECKPOINT_RESTART==2 ); if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?"); return TCL_ERROR; } if( objc==4 ){ zDb = Tcl_GetString(objv[3]); } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) || Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) ){ return TCL_ERROR; } rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE); return TCL_ERROR; } pRet = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0)); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog)); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt)); Tcl_SetObjResult(interp, pRet); return TCL_OK; } /* ** tclcmd: test_sqlite3_log ?SCRIPT? */ static struct LogCallback { Tcl_Interp *pInterp; Tcl_Obj *pObj; |
︙ | ︙ | |||
5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 | { "vfs_initfail_test", vfs_initfail_test, 0 }, { "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 | > | 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 | { "vfs_initfail_test", vfs_initfail_test, 0 }, { "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 }, { "file_control_sizehint_test", file_control_sizehint_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 |
︙ | ︙ | |||
5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 | { "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; | > > > | 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 | { "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 }, { "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, #ifndef SQLITE_OMIT_EXPLAIN { "print_explain_query_plan", test_print_eqp, 0 }, #endif }; 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/test_config.c.
︙ | ︙ | |||
470 471 472 473 474 475 476 477 478 479 480 481 482 483 | #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); #endif | > > > > > > | 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 | #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "1", TCL_GLOBAL_ONLY); #endif #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); #endif |
︙ | ︙ |
Changes to src/test_func.c.
︙ | ︙ | |||
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | } /* ** The following aggregate function, test_agg_errmsg16(), takes zero ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){ } static void test_agg_errmsg16_final(sqlite3_context *ctx){ #ifndef SQLITE_OMIT_UTF16 const void *z; sqlite3 * db = sqlite3_context_db_handle(ctx); sqlite3_aggregate_context(ctx, 2048); | > > > > > | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | } /* ** The following aggregate function, test_agg_errmsg16(), takes zero ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ #ifndef SQLITE_OMIT_BUILTIN_TEST void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); #else #define sqlite3BeginBenignMalloc() #define sqlite3EndBenignMalloc() #endif static void test_agg_errmsg16_step(sqlite3_context *a, int b,sqlite3_value **c){ } static void test_agg_errmsg16_final(sqlite3_context *ctx){ #ifndef SQLITE_OMIT_UTF16 const void *z; sqlite3 * db = sqlite3_context_db_handle(ctx); sqlite3_aggregate_context(ctx, 2048); |
︙ | ︙ |
Added src/test_fuzzer.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 | /* ** 2011 March 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. ** ************************************************************************* ** ** Code for demonstartion virtual table that generates variations ** on an input word at increasing edit distances from the original. ** ** A fuzzer virtual table is created like this: ** ** CREATE VIRTUAL TABLE temp.f USING fuzzer; ** ** The name of the new virtual table in the example above is "f". ** Note that all fuzzer virtual tables must be TEMP tables. The ** "temp." prefix in front of the table name is required when the ** table is being created. The "temp." prefix can be omitted when ** using the table as long as the name is unambiguous. ** ** Before being used, the fuzzer needs to be programmed by giving it ** character transformations and a cost associated with each transformation. ** Examples: ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('','a',100); ** ** The above statement says that the cost of inserting a letter 'a' is ** 100. (All costs are integers. We recommend that costs be scaled so ** that the average cost is around 100.) ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('b','',87); ** ** The above statement says that the cost of deleting a single letter ** 'b' is 87. ** ** INSERT INTO f(cFrom,cTo,Cost) VALUES('o','oe',38); ** INSERT INTO f(cFrom,cTo,Cost) VALUES('oe','o',40); ** ** This third example says that the cost of transforming the single ** letter "o" into the two-letter sequence "oe" is 38 and that the ** cost of transforming "oe" back into "o" is 40. ** ** After all the transformation costs have been set, the fuzzer table ** can be queried as follows: ** ** SELECT word, distance FROM f ** WHERE word MATCH 'abcdefg' ** AND distance<200; ** ** This first query outputs the string "abcdefg" and all strings that ** can be derived from that string by appling the specified transformations. ** The strings are output together with their total transformation cost ** (called "distance") and appear in order of increasing cost. No string ** is output more than once. If there are multiple ways to transform the ** target string into the output string then the lowest cost transform is ** the one that is returned. In the example, the search is limited to ** strings with a total distance of less than 200. ** ** It is important to put some kind of a limit on the fuzzer output. This ** can be either in the form of a LIMIT clause at the end of the query, ** or better, a "distance<NNN" constraint where NNN is some number. The ** running time and memory requirement is exponential in the value of NNN ** so you want to make sure that NNN is not too big. A value of NNN that ** is about twice the average transformation cost seems to give good results. ** ** The fuzzer table can be useful for tasks such as spelling correction. ** Suppose there is a second table vocabulary(w) where the w column contains ** all correctly spelled words. Let $word be a word you want to look up. ** ** SELECT vocabulary.w FROM f, vocabulary ** WHERE f.word MATCH $word ** AND f.distance<=200 ** AND f.word=vocabulary.w ** LIMIT 20 ** ** The query above gives the 20 closest words to the $word being tested. ** (Note that for good performance, the vocubulary.w column should be ** indexed.) ** ** A similar query can be used to find all words in the dictionary that ** begin with some prefix $prefix: ** ** SELECT vocabulary.w FROM f, vocabulary ** WHERE f.word MATCH $prefix ** AND f.distance<=200 ** AND vocabulary.w BETWEEN f.word AND (f.word || x'F7BFBFBF') ** LIMIT 50 ** ** This last query will show up to 50 words out of the vocabulary that ** match or nearly match the $prefix. */ #include "sqlite3.h" #include <stdlib.h> #include <string.h> #include <assert.h> #include <stdio.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* ** Forward declaration of objects used by this implementation */ typedef struct fuzzer_vtab fuzzer_vtab; typedef struct fuzzer_cursor fuzzer_cursor; typedef struct fuzzer_rule fuzzer_rule; typedef struct fuzzer_seen fuzzer_seen; typedef struct fuzzer_stem fuzzer_stem; /* ** Type of the "cost" of an edit operation. Might be changed to ** "float" or "double" or "sqlite3_int64" in the future. */ typedef int fuzzer_cost; /* ** Each transformation rule is stored as an instance of this object. ** All rules are kept on a linked list sorted by rCost. */ struct fuzzer_rule { fuzzer_rule *pNext; /* Next rule in order of increasing rCost */ fuzzer_cost rCost; /* Cost of this transformation */ int nFrom, nTo; /* Length of the zFrom and zTo strings */ char *zFrom; /* Transform from */ char zTo[4]; /* Transform to (extra space appended) */ }; /* ** A stem object is used to generate variants. It is also used to record ** previously generated outputs. ** ** Every stem is added to a hash table as it is output. Generation of ** duplicate stems is suppressed. ** ** Active stems (those that might generate new outputs) are kepts on a linked ** list sorted by increasing cost. The cost is the sum of rBaseCost and ** pRule->rCost. */ struct fuzzer_stem { char *zBasis; /* Word being fuzzed */ int nBasis; /* Length of the zBasis string */ const fuzzer_rule *pRule; /* Current rule to apply */ int n; /* Apply pRule at this character offset */ fuzzer_cost rBaseCost; /* Base cost of getting to zBasis */ fuzzer_cost rCostX; /* Precomputed rBaseCost + pRule->rCost */ fuzzer_stem *pNext; /* Next stem in rCost order */ fuzzer_stem *pHash; /* Next stem with same hash on zBasis */ }; /* ** A fuzzer virtual-table object */ struct fuzzer_vtab { sqlite3_vtab base; /* Base class - must be first */ char *zClassName; /* Name of this class. Default: "fuzzer" */ fuzzer_rule *pRule; /* All active rules in this fuzzer */ fuzzer_rule *pNewRule; /* New rules to add when last cursor expires */ int nCursor; /* Number of active cursors */ }; #define FUZZER_HASH 4001 /* Hash table size */ #define FUZZER_NQUEUE 20 /* Number of slots on the stem queue */ /* A fuzzer cursor object */ struct fuzzer_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ sqlite3_int64 iRowid; /* The rowid of the current word */ fuzzer_vtab *pVtab; /* The virtual table this cursor belongs to */ fuzzer_cost rLimit; /* Maximum cost of any term */ fuzzer_stem *pStem; /* Stem with smallest rCostX */ fuzzer_stem *pDone; /* Stems already processed to completion */ fuzzer_stem *aQueue[FUZZER_NQUEUE]; /* Queue of stems with higher rCostX */ int mxQueue; /* Largest used index in aQueue[] */ char *zBuf; /* Temporary use buffer */ int nBuf; /* Bytes allocated for zBuf */ int nStem; /* Number of stems allocated */ fuzzer_rule nullRule; /* Null rule used first */ fuzzer_stem *apHash[FUZZER_HASH]; /* Hash of previously generated terms */ }; /* Methods for the fuzzer module */ static int fuzzerConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ fuzzer_vtab *pNew; int n; if( strcmp(argv[1],"temp")!=0 ){ *pzErr = sqlite3_mprintf("%s virtual tables must be TEMP", argv[0]); return SQLITE_ERROR; } n = strlen(argv[0]) + 1; pNew = sqlite3_malloc( sizeof(*pNew) + n ); if( pNew==0 ) return SQLITE_NOMEM; pNew->zClassName = (char*)&pNew[1]; memcpy(pNew->zClassName, argv[0], n); sqlite3_declare_vtab(db, "CREATE TABLE x(word,distance,cFrom,cTo,cost)"); memset(pNew, 0, sizeof(*pNew)); *ppVtab = &pNew->base; return SQLITE_OK; } /* Note that for this virtual table, the xCreate and xConnect ** methods are identical. */ static int fuzzerDisconnect(sqlite3_vtab *pVtab){ fuzzer_vtab *p = (fuzzer_vtab*)pVtab; assert( p->nCursor==0 ); do{ while( p->pRule ){ fuzzer_rule *pRule = p->pRule; p->pRule = pRule->pNext; sqlite3_free(pRule); } p->pRule = p->pNewRule; p->pNewRule = 0; }while( p->pRule ); sqlite3_free(p); return SQLITE_OK; } /* The xDisconnect and xDestroy methods are also the same */ /* ** The two input rule lists are both sorted in order of increasing ** cost. Merge them together into a single list, sorted by cost, and ** return a pointer to the head of that list. */ static fuzzer_rule *fuzzerMergeRules(fuzzer_rule *pA, fuzzer_rule *pB){ fuzzer_rule head; fuzzer_rule *pTail; pTail = &head; while( pA && pB ){ if( pA->rCost<=pB->rCost ){ pTail->pNext = pA; pTail = pA; pA = pA->pNext; }else{ pTail->pNext = pB; pTail = pB; pB = pB->pNext; } } if( pA==0 ){ pTail->pNext = pB; }else{ pTail->pNext = pA; } return head.pNext; } /* ** Open a new fuzzer cursor. */ static int fuzzerOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ fuzzer_vtab *p = (fuzzer_vtab*)pVTab; fuzzer_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); pCur->pVtab = p; *ppCursor = &pCur->base; if( p->nCursor==0 && p->pNewRule ){ unsigned int i; fuzzer_rule *pX; fuzzer_rule *a[15]; for(i=0; i<sizeof(a)/sizeof(a[0]); i++) a[i] = 0; while( (pX = p->pNewRule)!=0 ){ p->pNewRule = pX->pNext; pX->pNext = 0; for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){ pX = fuzzerMergeRules(a[i], pX); a[i] = 0; } a[i] = fuzzerMergeRules(a[i], pX); } for(pX=a[0], i=1; i<sizeof(a)/sizeof(a[0]); i++){ pX = fuzzerMergeRules(a[i], pX); } p->pRule = fuzzerMergeRules(p->pRule, pX); } p->nCursor++; return SQLITE_OK; } /* ** Free all stems in a list. */ static void fuzzerClearStemList(fuzzer_stem *pStem){ while( pStem ){ fuzzer_stem *pNext = pStem->pNext; sqlite3_free(pStem); pStem = pNext; } } /* ** Free up all the memory allocated by a cursor. Set it rLimit to 0 ** to indicate that it is at EOF. */ static void fuzzerClearCursor(fuzzer_cursor *pCur, int clearHash){ int i; fuzzerClearStemList(pCur->pStem); fuzzerClearStemList(pCur->pDone); for(i=0; i<FUZZER_NQUEUE; i++) fuzzerClearStemList(pCur->aQueue[i]); pCur->rLimit = (fuzzer_cost)0; if( clearHash && pCur->nStem ){ pCur->mxQueue = 0; pCur->pStem = 0; pCur->pDone = 0; memset(pCur->aQueue, 0, sizeof(pCur->aQueue)); memset(pCur->apHash, 0, sizeof(pCur->apHash)); } pCur->nStem = 0; } /* ** Close a fuzzer cursor. */ static int fuzzerClose(sqlite3_vtab_cursor *cur){ fuzzer_cursor *pCur = (fuzzer_cursor *)cur; fuzzerClearCursor(pCur, 0); sqlite3_free(pCur->zBuf); pCur->pVtab->nCursor--; sqlite3_free(pCur); return SQLITE_OK; } /* ** Compute the current output term for a fuzzer_stem. */ static int fuzzerRender( fuzzer_stem *pStem, /* The stem to be rendered */ char **pzBuf, /* Write results into this buffer. realloc if needed */ int *pnBuf /* Size of the buffer */ ){ const fuzzer_rule *pRule = pStem->pRule; int n; char *z; n = pStem->nBasis + pRule->nTo - pRule->nFrom; if( (*pnBuf)<n+1 ){ (*pzBuf) = sqlite3_realloc((*pzBuf), n+100); if( (*pzBuf)==0 ) return SQLITE_NOMEM; (*pnBuf) = n+100; } n = pStem->n; z = *pzBuf; if( n<0 ){ memcpy(z, pStem->zBasis, pStem->nBasis+1); }else{ memcpy(z, pStem->zBasis, n); memcpy(&z[n], pRule->zTo, pRule->nTo); memcpy(&z[n+pRule->nTo], &pStem->zBasis[n+pRule->nFrom], pStem->nBasis-n-pRule->nFrom+1); } return SQLITE_OK; } /* ** Compute a hash on zBasis. */ static unsigned int fuzzerHash(const char *z){ unsigned int h = 0; while( *z ){ h = (h<<3) ^ (h>>29) ^ *(z++); } return h % FUZZER_HASH; } /* ** Current cost of a stem */ static fuzzer_cost fuzzerCost(fuzzer_stem *pStem){ return pStem->rCostX = pStem->rBaseCost + pStem->pRule->rCost; } #if 0 /* ** Print a description of a fuzzer_stem on stderr. */ static void fuzzerStemPrint( const char *zPrefix, fuzzer_stem *pStem, const char *zSuffix ){ if( pStem->n<0 ){ fprintf(stderr, "%s[%s](%d)-->self%s", zPrefix, pStem->zBasis, pStem->rBaseCost, zSuffix ); }else{ char *zBuf = 0; int nBuf = 0; if( fuzzerRender(pStem, &zBuf, &nBuf)!=SQLITE_OK ) return; fprintf(stderr, "%s[%s](%d)-->{%s}(%d)%s", zPrefix, pStem->zBasis, pStem->rBaseCost, zBuf, pStem->, zSuffix ); sqlite3_free(zBuf); } } #endif /* ** Return 1 if the string to which the cursor is point has already ** been emitted. Return 0 if not. Return -1 on a memory allocation ** failures. */ static int fuzzerSeen(fuzzer_cursor *pCur, fuzzer_stem *pStem){ unsigned int h; fuzzer_stem *pLookup; if( fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ return -1; } h = fuzzerHash(pCur->zBuf); pLookup = pCur->apHash[h]; while( pLookup && strcmp(pLookup->zBasis, pCur->zBuf)!=0 ){ pLookup = pLookup->pHash; } return pLookup!=0; } /* ** Advance a fuzzer_stem to its next value. Return 0 if there are ** no more values that can be generated by this fuzzer_stem. Return ** -1 on a memory allocation failure. */ static int fuzzerAdvance(fuzzer_cursor *pCur, fuzzer_stem *pStem){ const fuzzer_rule *pRule; while( (pRule = pStem->pRule)!=0 ){ while( pStem->n < pStem->nBasis - pRule->nFrom ){ pStem->n++; if( pRule->nFrom==0 || memcmp(&pStem->zBasis[pStem->n], pRule->zFrom, pRule->nFrom)==0 ){ /* Found a rewrite case. Make sure it is not a duplicate */ int rc = fuzzerSeen(pCur, pStem); if( rc<0 ) return -1; if( rc==0 ){ fuzzerCost(pStem); return 1; } } } pStem->n = -1; pStem->pRule = pRule->pNext; if( pStem->pRule && fuzzerCost(pStem)>pCur->rLimit ) pStem->pRule = 0; } return 0; } /* ** The two input stem lists are both sorted in order of increasing ** rCostX. Merge them together into a single list, sorted by rCostX, and ** return a pointer to the head of that new list. */ static fuzzer_stem *fuzzerMergeStems(fuzzer_stem *pA, fuzzer_stem *pB){ fuzzer_stem head; fuzzer_stem *pTail; pTail = &head; while( pA && pB ){ if( pA->rCostX<=pB->rCostX ){ pTail->pNext = pA; pTail = pA; pA = pA->pNext; }else{ pTail->pNext = pB; pTail = pB; pB = pB->pNext; } } if( pA==0 ){ pTail->pNext = pB; }else{ pTail->pNext = pA; } return head.pNext; } /* ** Load pCur->pStem with the lowest-cost stem. Return a pointer ** to the lowest-cost stem. */ static fuzzer_stem *fuzzerLowestCostStem(fuzzer_cursor *pCur){ fuzzer_stem *pBest, *pX; int iBest; int i; if( pCur->pStem==0 ){ iBest = -1; pBest = 0; for(i=0; i<=pCur->mxQueue; i++){ pX = pCur->aQueue[i]; if( pX==0 ) continue; if( pBest==0 || pBest->rCostX>pX->rCostX ){ pBest = pX; iBest = i; } } if( pBest ){ pCur->aQueue[iBest] = pBest->pNext; pBest->pNext = 0; pCur->pStem = pBest; } } return pCur->pStem; } /* ** Insert pNew into queue of pending stems. Then find the stem ** with the lowest rCostX and move it into pCur->pStem. ** list. The insert is done such the pNew is in the correct order ** according to fuzzer_stem.zBaseCost+fuzzer_stem.pRule->rCost. */ static fuzzer_stem *fuzzerInsert(fuzzer_cursor *pCur, fuzzer_stem *pNew){ fuzzer_stem *pX; int i; /* If pCur->pStem exists and is greater than pNew, then make pNew ** the new pCur->pStem and insert the old pCur->pStem instead. */ if( (pX = pCur->pStem)!=0 && pX->rCostX>pNew->rCostX ){ pNew->pNext = 0; pCur->pStem = pNew; pNew = pX; } /* Insert the new value */ pNew->pNext = 0; pX = pNew; for(i=0; i<=pCur->mxQueue; i++){ if( pCur->aQueue[i] ){ pX = fuzzerMergeStems(pX, pCur->aQueue[i]); pCur->aQueue[i] = 0; }else{ pCur->aQueue[i] = pX; break; } } if( i>pCur->mxQueue ){ if( i<FUZZER_NQUEUE ){ pCur->mxQueue = i; pCur->aQueue[i] = pX; }else{ assert( pCur->mxQueue==FUZZER_NQUEUE-1 ); pX = fuzzerMergeStems(pX, pCur->aQueue[FUZZER_NQUEUE-1]); pCur->aQueue[FUZZER_NQUEUE-1] = pX; } } return fuzzerLowestCostStem(pCur); } /* ** Allocate a new fuzzer_stem. Add it to the hash table but do not ** link it into either the pCur->pStem or pCur->pDone lists. */ static fuzzer_stem *fuzzerNewStem( fuzzer_cursor *pCur, const char *zWord, fuzzer_cost rBaseCost ){ fuzzer_stem *pNew; unsigned int h; pNew = sqlite3_malloc( sizeof(*pNew) + strlen(zWord) + 1 ); if( pNew==0 ) return 0; memset(pNew, 0, sizeof(*pNew)); pNew->zBasis = (char*)&pNew[1]; pNew->nBasis = strlen(zWord); memcpy(pNew->zBasis, zWord, pNew->nBasis+1); pNew->pRule = pCur->pVtab->pRule; pNew->n = -1; pNew->rBaseCost = pNew->rCostX = rBaseCost; h = fuzzerHash(pNew->zBasis); pNew->pHash = pCur->apHash[h]; pCur->apHash[h] = pNew; pCur->nStem++; return pNew; } /* ** Advance a cursor to its next row of output */ static int fuzzerNext(sqlite3_vtab_cursor *cur){ fuzzer_cursor *pCur = (fuzzer_cursor*)cur; int rc; fuzzer_stem *pStem, *pNew; pCur->iRowid++; /* Use the element the cursor is currently point to to create ** a new stem and insert the new stem into the priority queue. */ pStem = pCur->pStem; if( pStem->rCostX>0 ){ rc = fuzzerRender(pStem, &pCur->zBuf, &pCur->nBuf); if( rc==SQLITE_NOMEM ) return SQLITE_NOMEM; pNew = fuzzerNewStem(pCur, pCur->zBuf, pStem->rCostX); if( pNew ){ if( fuzzerAdvance(pCur, pNew)==0 ){ pNew->pNext = pCur->pDone; pCur->pDone = pNew; }else{ if( fuzzerInsert(pCur, pNew)==pNew ){ return SQLITE_OK; } } }else{ return SQLITE_NOMEM; } } /* Adjust the priority queue so that the first element of the ** stem list is the next lowest cost word. */ while( (pStem = pCur->pStem)!=0 ){ if( fuzzerAdvance(pCur, pStem) ){ pCur->pStem = 0; pStem = fuzzerInsert(pCur, pStem); if( (rc = fuzzerSeen(pCur, pStem))!=0 ){ if( rc<0 ) return SQLITE_NOMEM; continue; } return SQLITE_OK; /* New word found */ } pCur->pStem = 0; pStem->pNext = pCur->pDone; pCur->pDone = pStem; if( fuzzerLowestCostStem(pCur) ){ rc = fuzzerSeen(pCur, pCur->pStem); if( rc<0 ) return SQLITE_NOMEM; if( rc==0 ){ return SQLITE_OK; } } } /* Reach this point only if queue has been exhausted and there is ** nothing left to be output. */ pCur->rLimit = (fuzzer_cost)0; return SQLITE_OK; } /* ** Called to "rewind" a cursor back to the beginning so that ** it starts its output over again. Always called at least once ** prior to any fuzzerColumn, fuzzerRowid, or fuzzerEof call. */ static int fuzzerFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ fuzzer_cursor *pCur = (fuzzer_cursor *)pVtabCursor; const char *zWord = 0; fuzzer_stem *pStem; fuzzerClearCursor(pCur, 1); pCur->rLimit = 2147483647; if( idxNum==1 ){ zWord = (const char*)sqlite3_value_text(argv[0]); }else if( idxNum==2 ){ pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[0]); }else if( idxNum==3 ){ zWord = (const char*)sqlite3_value_text(argv[0]); pCur->rLimit = (fuzzer_cost)sqlite3_value_int(argv[1]); } if( zWord==0 ) zWord = ""; pCur->pStem = pStem = fuzzerNewStem(pCur, zWord, (fuzzer_cost)0); if( pStem==0 ) return SQLITE_NOMEM; pCur->nullRule.pNext = pCur->pVtab->pRule; pCur->nullRule.rCost = 0; pCur->nullRule.nFrom = 0; pCur->nullRule.nTo = 0; pCur->nullRule.zFrom = ""; pStem->pRule = &pCur->nullRule; pStem->n = pStem->nBasis; pCur->iRowid = 1; return SQLITE_OK; } /* ** Only the word and distance columns have values. All other columns ** return NULL */ static int fuzzerColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ fuzzer_cursor *pCur = (fuzzer_cursor*)cur; if( i==0 ){ /* the "word" column */ if( fuzzerRender(pCur->pStem, &pCur->zBuf, &pCur->nBuf)==SQLITE_NOMEM ){ return SQLITE_NOMEM; } sqlite3_result_text(ctx, pCur->zBuf, -1, SQLITE_TRANSIENT); }else if( i==1 ){ /* the "distance" column */ sqlite3_result_int(ctx, pCur->pStem->rCostX); }else{ /* All other columns are NULL */ sqlite3_result_null(ctx); } return SQLITE_OK; } /* ** The rowid. */ static int fuzzerRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ fuzzer_cursor *pCur = (fuzzer_cursor*)cur; *pRowid = pCur->iRowid; return SQLITE_OK; } /* ** When the fuzzer_cursor.rLimit value is 0 or less, that is a signal ** that the cursor has nothing more to output. */ static int fuzzerEof(sqlite3_vtab_cursor *cur){ fuzzer_cursor *pCur = (fuzzer_cursor*)cur; return pCur->rLimit<=(fuzzer_cost)0; } /* ** Search for terms of these forms: ** ** word MATCH $str ** distance < $value ** distance <= $value ** ** The distance< and distance<= are both treated as distance<=. ** The query plan number is as follows: ** ** 0: None of the terms above are found ** 1: There is a "word MATCH" term with $str in filter.argv[0]. ** 2: There is a "distance<" term with $value in filter.argv[0]. ** 3: Both "word MATCH" and "distance<" with $str in argv[0] and ** $value in argv[1]. */ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int iPlan = 0; int iDistTerm = -1; int i; const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( (iPlan & 1)==0 && pConstraint->iColumn==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ iPlan |= 1; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; } if( (iPlan & 2)==0 && pConstraint->iColumn==1 && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) ){ iPlan |= 2; iDistTerm = i; } } if( iPlan==2 ){ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 1; }else if( iPlan==3 ){ pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = 2; } pIdxInfo->idxNum = iPlan; if( pIdxInfo->nOrderBy==1 && pIdxInfo->aOrderBy[0].iColumn==1 && pIdxInfo->aOrderBy[0].desc==0 ){ pIdxInfo->orderByConsumed = 1; } pIdxInfo->estimatedCost = (double)10000; return SQLITE_OK; } /* ** Disallow all attempts to DELETE or UPDATE. Only INSERTs are allowed. ** ** On an insert, the cFrom, cTo, and cost columns are used to construct ** a new rule. All other columns are ignored. The rule is ignored ** if cFrom and cTo are identical. A NULL value for cFrom or cTo is ** interpreted as an empty string. The cost must be positive. */ static int fuzzerUpdate( sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite_int64 *pRowid ){ fuzzer_vtab *p = (fuzzer_vtab*)pVTab; fuzzer_rule *pRule; const char *zFrom; int nFrom; const char *zTo; int nTo; fuzzer_cost rCost; if( argc!=7 ){ sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = sqlite3_mprintf("cannot delete from a %s virtual table", p->zClassName); return SQLITE_CONSTRAINT; } if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = sqlite3_mprintf("cannot update a %s virtual table", p->zClassName); return SQLITE_CONSTRAINT; } zFrom = (char*)sqlite3_value_text(argv[4]); if( zFrom==0 ) zFrom = ""; zTo = (char*)sqlite3_value_text(argv[5]); if( zTo==0 ) zTo = ""; if( strcmp(zFrom,zTo)==0 ){ /* Silently ignore null transformations */ return SQLITE_OK; } rCost = sqlite3_value_int(argv[6]); if( rCost<=0 ){ sqlite3_free(pVTab->zErrMsg); pVTab->zErrMsg = sqlite3_mprintf("cost must be positive"); return SQLITE_CONSTRAINT; } nFrom = strlen(zFrom); nTo = strlen(zTo); pRule = sqlite3_malloc( sizeof(*pRule) + nFrom + nTo ); if( pRule==0 ){ return SQLITE_NOMEM; } pRule->zFrom = &pRule->zTo[nTo+1]; pRule->nFrom = nFrom; memcpy(pRule->zFrom, zFrom, nFrom+1); memcpy(pRule->zTo, zTo, nTo+1); pRule->nTo = nTo; pRule->rCost = rCost; pRule->pNext = p->pNewRule; p->pNewRule = pRule; return SQLITE_OK; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module fuzzerModule = { 0, /* iVersion */ fuzzerConnect, fuzzerConnect, fuzzerBestIndex, fuzzerDisconnect, fuzzerDisconnect, fuzzerOpen, /* xOpen - open a cursor */ fuzzerClose, /* xClose - close a cursor */ fuzzerFilter, /* xFilter - configure scan constraints */ fuzzerNext, /* xNext - advance a cursor */ fuzzerEof, /* xEof - check for end of scan */ fuzzerColumn, /* xColumn - read data */ fuzzerRowid, /* xRowid - read data */ fuzzerUpdate, /* xUpdate - INSERT */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Register the fuzzer virtual table */ int fuzzer_register(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "fuzzer", &fuzzerModule, 0); #endif return rc; } #ifdef SQLITE_TEST #include <tcl.h> /* ** Decode a pointer to an sqlite3 object. */ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the echo virtual table module. */ static int register_fuzzer_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; fuzzer_register(db); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetestfuzzer_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { { "register_fuzzer_module", register_fuzzer_module, 0 }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, aObjCmd[i].clientData, 0); } return TCL_OK; } #endif /* SQLITE_TEST */ |
Changes to src/test_hexio.c.
︙ | ︙ | |||
308 309 310 311 312 313 314 | z = sqlite3_malloc( n+3 ); n = sqlite3TestHexToBin(zOrig, n, z); z[n] = 0; nOut = sqlite3Utf8To8(z); sqlite3TestBinToHex(z,nOut); Tcl_AppendResult(interp, (char*)z, 0); sqlite3_free(z); | > | > > > | > | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | z = sqlite3_malloc( n+3 ); n = sqlite3TestHexToBin(zOrig, n, z); z[n] = 0; nOut = sqlite3Utf8To8(z); sqlite3TestBinToHex(z,nOut); Tcl_AppendResult(interp, (char*)z, 0); sqlite3_free(z); return TCL_OK; #else Tcl_AppendResult(interp, "[utf8_to_utf8] unavailable - SQLITE_DEBUG not defined", 0 ); return TCL_ERROR; #endif } static int getFts3Varint(const char *p, sqlite_int64 *v){ const unsigned char *q = (const unsigned char *) p; sqlite_uint64 x = 0, y = 1; while( (*q & 0x80) == 0x80 ){ x += y * (*q++ & 0x7f); |
︙ | ︙ |
Changes to src/test_multiplex.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** "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> | > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > | | 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 | ** "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 "test_multiplex.h" #ifndef SQLITE_CORE #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ #endif #include "sqlite3ext.h" /* ** These should be defined to be the same as the values in ** sqliteInt.h. They are defined seperately here so that ** the multiplex VFS shim can be built as a loadable ** module. */ #define UNUSED_PARAMETER(x) (void)(x) #define MAX_PAGE_SIZE 0x10000 #define DEFAULT_SECTOR_SIZE 0x1000 /* ** For a build without mutexes, no-op the mutex calls. */ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 #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) #endif /* SQLITE_THREADSAFE==0 */ /************************ Shim Definitions ******************************/ #define SQLITE_MULTIPLEX_VFS_NAME "multiplex" /* This is the limit on the chunk size. It may be changed by calling ** the xFileControl() interface. It will be rounded up to a ** multiple of MAX_PAGE_SIZE. We default it here to 1GB. */ #define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384) /* 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 xFileControl() 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. |
︙ | ︙ | |||
60 61 62 63 64 65 66 | ** 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 */ | | > > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | ** 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; /* array of bools - 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 */ int nChunkSize; /* Chunk size used for this group */ int nMaxChunks; /* Max number of chunks for this group */ int bEnabled; /* TRUE to use Multiplex VFS for this file */ 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 |
︙ | ︙ | |||
122 123 124 125 126 127 128 | */ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; | < < < < < > > > > > > > > > > > > > > > | | 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 | */ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; /* 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); } /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. ** ** The value returned will never be negative. Nor will it ever be greater ** than the actual length of the string. For very long strings (greater ** than 1GiB) the value returned might be less than the true string length. */ int multiplexStrlen30(const char *z){ const char *z2 = z; if( z==0 ) return 0; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* 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<pGroup->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 |
︙ | ︙ | |||
172 173 174 175 176 177 178 179 180 181 182 183 184 185 | } *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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } *rc = SQLITE_OK; return pSubOpen; } *rc = SQLITE_FULL; return NULL; } /* ** This is the implementation of the multiplex_control() SQL function. */ static void multiplexControlFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ int rc = SQLITE_OK; sqlite3 *db = sqlite3_context_db_handle(context); int op; int iVal; if( !db || argc!=2 ){ rc = SQLITE_ERROR; }else{ /* extract params */ op = sqlite3_value_int(argv[0]); iVal = sqlite3_value_int(argv[1]); /* map function op to file_control op */ switch( op ){ case 1: op = MULTIPLEX_CTRL_ENABLE; break; case 2: op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; break; case 3: op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; break; default: rc = SQLITE_NOTFOUND; break; } } if( rc==SQLITE_OK ){ rc = sqlite3_file_control(db, 0, op, &iVal); } sqlite3_result_error_code(context, rc); } /* ** This is the entry point to register the auto-extension for the ** multiplex_control() function. */ static int multiplexFuncInit( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc; rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 0, multiplexControlFunc, 0, 0); return rc; } /************************* 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 |
︙ | ︙ | |||
194 195 196 197 198 199 200 | 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 */ | | | | | | | > > > | | > | > > > > > > > > | 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 | 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 = multiplexStrlen30(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 *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */ + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */ + SQLITE_MULTIPLEX_MAX_CHUNKS /* 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->bEnabled = -1; pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; pGroup->pReal = (sqlite3_file **)p; p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks); for(i=0; i<pGroup->nMaxChunks; i++){ pGroup->pReal[i] = (sqlite3_file *)p; p += pOrigVfs->szOsFile; } /* bOpen[] vals should all be zero from memset above */ pGroup->bOpen = p; p += pGroup->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 this file is already larger than chunk size, disable ** the multiplex feature. */ sqlite3_int64 sz; int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){ pGroup->bEnabled = 0; } 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; |
︙ | ︙ | |||
270 271 272 273 274 275 276 | 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; | | | | > > | > > | > | 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 | 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 = multiplexStrlen30(zName); int i; UNUSED_PARAMETER(pVfs); multiplexEnter(); memcpy(gMultiplex.zName, zName, nName+1); for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; 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; |
︙ | ︙ | |||
349 350 351 352 353 354 355 | 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 */ | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | 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<pGroup->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; } } |
︙ | ︙ | |||
380 381 382 383 384 385 386 387 388 | static int multiplexRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; int rc = SQLITE_OK; multiplexEnter(); | > > > > > | | | | | | | | | | | | | | | > > > > > > | | | | | | | | | | | | | | | > > > > > > | | | | < | | | | | | | | | | | > > | > > | | | | | | | | | > | | 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 | static int multiplexRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->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; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->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; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size); }else{ int rc2; int i; sqlite3_file *pSubOpen; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); /* delete the chunks above the truncate limit */ for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->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 / pGroup->nChunkSize), &rc2, NULL); if( pSubOpen ){ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->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<pGroup->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; } } |
︙ | ︙ | |||
509 510 511 512 513 514 515 | 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(); | > > > > | | | | | | | | | | | | > > | > > | | > | | | | | | | | | | | | | | | | | | | | | > | 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 | 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(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize); }else{ *pSize = 0; for(i=0; i<pGroup->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{ if( sz>pGroup->nChunkSize ){ rc = SQLITE_IOERR_FSTAT; } *pSize += sz; } }else{ break; } } } multiplexLeave(); return rc; } /* Pass xLock requests through to the original VFS unchanged. |
︙ | ︙ | |||
590 591 592 593 594 595 596 | sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } return SQLITE_IOERR_CHECKRESERVEDLOCK; } | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | | > > | | | 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 | 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, ** except for any MULTIPLEX_CTRL_* requests here. */ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_ERROR; sqlite3_file *pSubOpen; if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; switch( op ){ case MULTIPLEX_CTRL_ENABLE: if( pArg ) { int bEnabled = *(int *)pArg; pGroup->bEnabled = bEnabled; rc = SQLITE_OK; } break; case MULTIPLEX_CTRL_SET_CHUNK_SIZE: if( pArg ) { int nChunkSize = *(int *)pArg; if( nChunkSize<1 ){ rc = SQLITE_MISUSE; }else{ /* Round up to nearest multiple of MAX_PAGE_SIZE. */ nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1)); nChunkSize &= ~(MAX_PAGE_SIZE-1); pGroup->nChunkSize = nChunkSize; rc = SQLITE_OK; } } break; case MULTIPLEX_CTRL_SET_MAX_CHUNKS: if( pArg ) { int nMaxChunks = *(int *)pArg; if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){ rc = SQLITE_MISUSE; }else{ pGroup->nMaxChunks = nMaxChunks; rc = SQLITE_OK; } } break; case SQLITE_FCNTL_SIZE_HINT: case SQLITE_FCNTL_CHUNK_SIZE: /* no-op these */ rc = SQLITE_OK; break; default: pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); } break; } return rc; } /* 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 DEFAULT_SECTOR_SIZE; } /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. */ static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; |
︙ | ︙ | |||
688 689 690 691 692 693 694 | return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } /************************** Public Interfaces *****************************/ /* | | > | | | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 | return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } /************************** Public Interfaces *****************************/ /* ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() ** ** 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. */ |
︙ | ︙ | |||
713 714 715 716 717 718 719 | return SQLITE_NOMEM; } gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); if( !gMultiplex.zName ){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } | < < | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | return SQLITE_NOMEM; } gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); if( !gMultiplex.zName ){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } gMultiplex.pGroups = NULL; gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; 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; |
︙ | ︙ | |||
755 756 757 758 759 760 761 762 763 764 765 | 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; } /* | > > > | < < < < < < < < < < < < < < < < < < < < < < | 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 | 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); sqlite3_auto_extension((void*)multiplexFuncInit); return SQLITE_OK; } /* ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() ** ** 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; } /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include <tcl.h> extern const char *sqlite3TestErrorName(int); /* ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT */ static int test_multiplex_initialize( |
︙ | ︙ | |||
862 863 864 865 866 867 868 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); 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, |
︙ | ︙ | |||
925 926 927 928 929 930 931 | 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 */ | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | 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 | 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<pGroup->nMaxChunks; i++){ if( pGroup->bOpen[i] ) nChunks++; } Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(nChunks)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nChunkSize)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nMaxChunks)); Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } multiplexLeave(); Tcl_SetObjResult(interp, pResult); return TCL_OK; } /* ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? */ static int test_multiplex_control( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; /* Return code from file_control() */ int idx; /* Index in aSub[] */ Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ sqlite3 *db; /* Underlying db handle for HANDLE */ int iValue = 0; void *pArg = 0; struct SubCommand { const char *zName; int op; int argtype; } aSub[] = { { "enable", MULTIPLEX_CTRL_ENABLE, 1 }, { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 }, { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 }, { 0, 0, 0 } }; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE"); return TCL_ERROR; } if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ Tcl_AppendResult(interp, "expected database handle, got \"", 0); Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0); return TCL_ERROR; }else{ db = *(sqlite3 **)cmdInfo.objClientData; } rc = Tcl_GetIndexFromObjStruct( interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx ); if( rc!=TCL_OK ) return rc; switch( aSub[idx].argtype ){ case 1: if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){ return TCL_ERROR; } pArg = (void *)&iValue; break; default: Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND"); return TCL_ERROR; } rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR; } /* ** 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_dump", test_multiplex_dump }, { "sqlite3_multiplex_control", test_multiplex_control }, }; 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_multiplex.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 | /* ** 2011 March 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 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. ** */ #ifndef _TEST_MULTIPLEX_H #define _TEST_MULTIPLEX_H /* ** CAPI: File-control Operations Supported by Multiplex VFS ** ** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle. ** ** MULTIPLEX_CTRL_ENABLE: ** This file control is used to enable or disable the multiplex ** shim. ** ** MULTIPLEX_CTRL_SET_CHUNK_SIZE: ** This file control is used to set the maximum allowed chunk ** size for a multiplex file set. The chunk size should be ** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up ** if not. ** ** MULTIPLEX_CTRL_SET_MAX_CHUNKS: ** This file control is used to set the maximum number of chunks ** allowed to be used for a mutliplex file set. */ #define MULTIPLEX_CTRL_ENABLE 214014 #define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015 #define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016 /* ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() ** ** 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. ** ** An auto-extension is registered which will make the function ** multiplex_control() available to database connections. This ** function gives access to the xFileControl interface of the ** multiplex VFS shim. ** ** SELECT multiplex_control(<op>,<val>); ** ** <op>=1 MULTIPLEX_CTRL_ENABLE ** <val>=0 disable ** <val>=1 enable ** ** <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE ** <val> int, chunk size ** ** <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS ** <val> int, max chunks ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once ** during start-up. */ extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault); /* ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() ** ** 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. */ extern int sqlite3_multiplex_shutdown(void); #endif |
Added src/test_syscall.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 | /* ** 2011 March 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. ** ************************************************************************* ** ** The code in this file implements a Tcl interface used to test error ** handling in the os_unix.c module. Wrapper functions that support fault ** injection are registered as the low-level OS functions using the ** xSetSystemCall() method of the VFS. The Tcl interface is as follows: ** ** ** test_syscall install LIST ** Install wrapper functions for all system calls in argument LIST. ** LIST must be a list consisting of zero or more of the following ** literal values: ** ** open close access getcwd stat fstat ** ftruncate fcntl read pread pread64 write ** pwrite pwrite64 fchmod fallocate ** ** test_syscall uninstall ** Uninstall all wrapper functions. ** ** test_syscall fault ?COUNT PERSIST? ** If [test_syscall fault] is invoked without the two arguments, fault ** injection is disabled. Otherwise, fault injection is configured to ** cause a failure on the COUNT'th next call to a system call with a ** wrapper function installed. A COUNT value of 1 means fail the next ** system call. ** ** Argument PERSIST is interpreted as a boolean. If true, the all ** system calls following the initial failure also fail. Otherwise, only ** the single transient failure is injected. ** ** test_syscall errno CALL ERRNO ** Set the value that the global "errno" is set to following a fault ** in call CALL. Argument CALL must be one of the system call names ** listed above (under [test_syscall install]). ERRNO is a symbolic ** name (i.e. "EACCES"). Not all errno codes are supported. Add extra ** to the aErrno table in function test_syscall_errno() below as ** required. ** ** test_syscall reset ?SYSTEM-CALL? ** With no argument, this is an alias for the [uninstall] command. However, ** this command uses a VFS call of the form: ** ** xSetSystemCall(pVfs, 0, 0); ** ** To restore the default system calls. The [uninstall] command restores ** each system call individually by calling (i.e.): ** ** xSetSystemCall(pVfs, "open", 0); ** ** With an argument, this command attempts to reset the system call named ** by the parameter using the same method as [uninstall]. ** ** test_syscall exists SYSTEM-CALL ** Return true if the named system call exists. Or false otherwise. ** ** test_syscall list ** Return a list of all system calls. The list is constructed using ** the xNextSystemCall() VFS method. */ #include "sqlite3.h" #include "tcl.h" #include <stdlib.h> #include <string.h> #include <assert.h> #ifdef SQLITE_OS_UNIX /* From test1.c */ extern const char *sqlite3TestErrorName(int); #include <sys/types.h> #include <errno.h> static struct TestSyscallGlobal { int bPersist; /* 1 for persistent errors, 0 for transient */ int nCount; /* Fail after this many more calls */ int nFail; /* Number of failures that have occurred */ } gSyscall = { 0, 0 }; static int ts_open(const char *, int, int); static int ts_close(int fd); static int ts_access(const char *zPath, int mode); static char *ts_getcwd(char *zPath, size_t nPath); static int ts_stat(const char *zPath, struct stat *p); static int ts_fstat(int fd, struct stat *p); static int ts_ftruncate(int fd, off_t n); static int ts_fcntl(int fd, int cmd, ... ); static int ts_read(int fd, void *aBuf, size_t nBuf); static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off); static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off); static int ts_write(int fd, const void *aBuf, size_t nBuf); static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off); static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off); static int ts_fchmod(int fd, mode_t mode); static int ts_fallocate(int fd, off_t off, off_t len); struct TestSyscallArray { const char *zName; sqlite3_syscall_ptr xTest; sqlite3_syscall_ptr xOrig; int default_errno; /* Default value for errno following errors */ int custom_errno; /* Current value for errno if error */ } aSyscall[] = { /* 0 */ { "open", (sqlite3_syscall_ptr)ts_open, 0, EACCES, 0 }, /* 1 */ { "close", (sqlite3_syscall_ptr)ts_close, 0, 0, 0 }, /* 2 */ { "access", (sqlite3_syscall_ptr)ts_access, 0, 0, 0 }, /* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 }, /* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 }, /* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 }, /* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 }, /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, EACCES, 0 }, /* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 }, /* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 }, /* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 }, /* 11 */ { "write", (sqlite3_syscall_ptr)ts_write, 0, 0, 0 }, /* 12 */ { "pwrite", (sqlite3_syscall_ptr)ts_pwrite, 0, 0, 0 }, /* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 }, /* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 }, /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 }, { 0, 0, 0, 0, 0 } }; #define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig) #define orig_close ((int(*)(int))aSyscall[1].xOrig) #define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig) #define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig) #define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig) #define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig) #define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig) #define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig) #define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig) #define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig) #define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig) #define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig) #define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[12].xOrig) #define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ aSyscall[13].xOrig) #define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig) #define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig) /* ** This function is called exactly once from within each invocation of a ** system call wrapper in this file. It returns 1 if the function should ** fail, or 0 if it should succeed. */ static int tsIsFail(void){ gSyscall.nCount--; if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){ gSyscall.nFail++; return 1; } return 0; } /* ** Return the current error-number value for function zFunc. zFunc must be ** the name of a system call in the aSyscall[] table. ** ** Usually, the current error-number is the value that errno should be set ** to if the named system call fails. The exception is "fallocate". See ** comments above the implementation of ts_fallocate() for details. */ static int tsErrno(const char *zFunc){ int i; int nFunc = strlen(zFunc); for(i=0; aSyscall[i].zName; i++){ if( strlen(aSyscall[i].zName)!=nFunc ) continue; if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; return aSyscall[i].custom_errno; } assert(0); return 0; } /* ** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the ** value of errno before returning. */ static int tsIsFailErrno(const char *zFunc){ if( tsIsFail() ){ errno = tsErrno(zFunc); return 1; } return 0; } /* ** A wrapper around open(). */ static int ts_open(const char *zFile, int flags, int mode){ if( tsIsFailErrno("open") ){ return -1; } return orig_open(zFile, flags, mode); } /* ** A wrapper around close(). */ static int ts_close(int fd){ if( tsIsFail() ){ /* Even if simulating an error, close the original file-descriptor. ** This is to stop the test process from running out of file-descriptors ** when running a long test. If a call to close() appears to fail, SQLite ** never attempts to use the file-descriptor afterwards (or even to close ** it a second time). */ orig_close(fd); return -1; } return orig_close(fd); } /* ** A wrapper around access(). */ static int ts_access(const char *zPath, int mode){ if( tsIsFail() ){ return -1; } return orig_access(zPath, mode); } /* ** A wrapper around getcwd(). */ static char *ts_getcwd(char *zPath, size_t nPath){ if( tsIsFail() ){ return NULL; } return orig_getcwd(zPath, nPath); } /* ** A wrapper around stat(). */ static int ts_stat(const char *zPath, struct stat *p){ if( tsIsFail() ){ return -1; } return orig_stat(zPath, p); } /* ** A wrapper around fstat(). */ static int ts_fstat(int fd, struct stat *p){ if( tsIsFailErrno("fstat") ){ return -1; } return orig_fstat(fd, p); } /* ** A wrapper around ftruncate(). */ static int ts_ftruncate(int fd, off_t n){ if( tsIsFailErrno("ftruncate") ){ return -1; } return orig_ftruncate(fd, n); } /* ** A wrapper around fcntl(). */ static int ts_fcntl(int fd, int cmd, ... ){ va_list ap; void *pArg; if( tsIsFailErrno("fcntl") ){ return -1; } va_start(ap, cmd); pArg = va_arg(ap, void *); return orig_fcntl(fd, cmd, pArg); } /* ** A wrapper around read(). */ static int ts_read(int fd, void *aBuf, size_t nBuf){ if( tsIsFailErrno("read") ){ return -1; } return orig_read(fd, aBuf, nBuf); } /* ** A wrapper around pread(). */ static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){ if( tsIsFailErrno("pread") ){ return -1; } return orig_pread(fd, aBuf, nBuf, off); } /* ** A wrapper around pread64(). */ static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){ if( tsIsFailErrno("pread64") ){ return -1; } return orig_pread64(fd, aBuf, nBuf, off); } /* ** A wrapper around write(). */ static int ts_write(int fd, const void *aBuf, size_t nBuf){ if( tsIsFailErrno("write") ){ return -1; } return orig_write(fd, aBuf, nBuf); } /* ** A wrapper around pwrite(). */ static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){ if( tsIsFailErrno("pwrite") ){ return -1; } return orig_pwrite(fd, aBuf, nBuf, off); } /* ** A wrapper around pwrite64(). */ static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){ if( tsIsFailErrno("pwrite64") ){ return -1; } return orig_pwrite64(fd, aBuf, nBuf, off); } /* ** A wrapper around fchmod(). */ static int ts_fchmod(int fd, mode_t mode){ if( tsIsFail() ){ return -1; } return orig_fchmod(fd, mode); } /* ** A wrapper around fallocate(). ** ** SQLite assumes that the fallocate() function is compatible with ** posix_fallocate(). According to the Linux man page (2009-09-30): ** ** posix_fallocate() returns zero on success, or an error number on ** failure. Note that errno is not set. */ static int ts_fallocate(int fd, off_t off, off_t len){ if( tsIsFail() ){ return tsErrno("fallocate"); } return orig_fallocate(fd, off, len); } static int test_syscall_install( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_vfs *pVfs; int nElem; int i; Tcl_Obj **apElem; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST"); return TCL_ERROR; } if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){ return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); for(i=0; i<nElem; i++){ int iCall; int rc = Tcl_GetIndexFromObjStruct(interp, apElem[i], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall ); if( rc ) return rc; if( aSyscall[iCall].xOrig==0 ){ aSyscall[iCall].xOrig = pVfs->xGetSystemCall(pVfs, aSyscall[iCall].zName); pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest); } aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno; } return TCL_OK; } static int test_syscall_uninstall( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_vfs *pVfs; int i; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); for(i=0; aSyscall[i].zName; i++){ if( aSyscall[i].xOrig ){ pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0); aSyscall[i].xOrig = 0; } } return TCL_OK; } static int test_syscall_reset( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_vfs *pVfs; int i; int rc; if( objc!=2 && objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); if( objc==2 ){ rc = pVfs->xSetSystemCall(pVfs, 0, 0); for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0; }else{ int nFunc; char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc); rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0); for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){ if( strlen(aSyscall[i].zName)!=nFunc ) continue; if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue; aSyscall[i].xOrig = 0; } } if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1)); return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } static int test_syscall_exists( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_vfs *pVfs; sqlite3_syscall_ptr x; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2])); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0)); return TCL_OK; } static int test_syscall_fault( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int nCount = 0; int bPersist = 0; if( objc!=2 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?"); return TCL_ERROR; } if( objc==4 ){ if( Tcl_GetIntFromObj(interp, objv[2], &nCount) || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist) ){ return TCL_ERROR; } } Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail)); gSyscall.nCount = nCount; gSyscall.bPersist = bPersist; gSyscall.nFail = 0; return TCL_OK; } static int test_syscall_errno( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int iCall; int iErrno; int rc; struct Errno { const char *z; int i; } aErrno[] = { { "EACCES", EACCES }, { "EINTR", EINTR }, { "EIO", EIO }, { "EOVERFLOW", EOVERFLOW }, { "ENOMEM", ENOMEM }, { "EAGAIN", EAGAIN }, { "ETIMEDOUT", ETIMEDOUT }, { "EBUSY", EBUSY }, { "EPERM", EPERM }, { "EDEADLK", EDEADLK }, { "ENOLCK", ENOLCK }, { 0, 0 } }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO"); return TCL_ERROR; } rc = Tcl_GetIndexFromObjStruct(interp, objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall ); if( rc!=TCL_OK ) return rc; rc = Tcl_GetIndexFromObjStruct(interp, objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno ); if( rc!=TCL_OK ) return rc; aSyscall[iCall].custom_errno = aErrno[iErrno].i; return TCL_OK; } static int test_syscall_list( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zSys; sqlite3_vfs *pVfs; Tcl_Obj *pList; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); pList = Tcl_NewObj(); Tcl_IncrRefCount(pList); for(zSys = pVfs->xNextSystemCall(pVfs, 0); zSys!=0; zSys = pVfs->xNextSystemCall(pVfs, zSys) ){ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1)); } Tcl_SetObjResult(interp, pList); Tcl_DecrRefCount(pList); return TCL_OK; } static int test_syscall_defaultvfs( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite3_vfs *pVfs; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } pVfs = sqlite3_vfs_find(0); Tcl_SetObjResult(interp, Tcl_NewStringObj(pVfs->zName, -1)); return TCL_OK; } static int test_syscall( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ struct SyscallCmd { const char *zName; Tcl_ObjCmdProc *xCmd; } aCmd[] = { { "fault", test_syscall_fault }, { "install", test_syscall_install }, { "uninstall", test_syscall_uninstall }, { "reset", test_syscall_reset }, { "errno", test_syscall_errno }, { "exists", test_syscall_exists }, { "list", test_syscall_list }, { "defaultvfs", test_syscall_defaultvfs }, { 0, 0 } }; int iCmd; int rc; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ..."); return TCL_ERROR; } rc = Tcl_GetIndexFromObjStruct(interp, objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd ); if( rc!=TCL_OK ) return rc; return aCmd[iCmd].xCmd(clientData, interp, objc, objv); } int SqlitetestSyscall_Init(Tcl_Interp *interp){ struct SyscallCmd { const char *zName; Tcl_ObjCmdProc *xCmd; } aCmd[] = { { "test_syscall", test_syscall}, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0); } return TCL_OK; } #else int SqlitetestSyscall_Init(Tcl_Interp *interp){ return TCL_OK; } #endif |
Added src/test_vfstrace.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 | /* ** 2011 March 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 contains code implements a VFS shim that writes diagnostic ** output for each VFS call, similar to "strace". */ #include <stdlib.h> #include <string.h> #include "sqlite3.h" /* ** An instance of this structure is attached to the each trace VFS to ** provide auxiliary information. */ typedef struct vfstrace_info vfstrace_info; struct vfstrace_info { sqlite3_vfs *pRootVfs; /* The underlying real VFS */ int (*xOut)(const char*, void*); /* Send output here */ void *pOutArg; /* First argument to xOut */ const char *zVfsName; /* Name of this trace-VFS */ sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */ }; /* ** The sqlite3_file object for the trace VFS */ typedef struct vfstrace_file vfstrace_file; struct vfstrace_file { sqlite3_file base; /* Base class. Must be first */ vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */ const char *zFName; /* Base name of the file */ sqlite3_file *pReal; /* The real underlying file */ }; /* ** Method declarations for vfstrace_file. */ static int vfstraceClose(sqlite3_file*); static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size); static int vfstraceSync(sqlite3_file*, int flags); static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize); static int vfstraceLock(sqlite3_file*, int); static int vfstraceUnlock(sqlite3_file*, int); static int vfstraceCheckReservedLock(sqlite3_file*, int *); static int vfstraceFileControl(sqlite3_file*, int op, void *pArg); static int vfstraceSectorSize(sqlite3_file*); static int vfstraceDeviceCharacteristics(sqlite3_file*); static int vfstraceShmLock(sqlite3_file*,int,int,int); static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **); static void vfstraceShmBarrier(sqlite3_file*); static int vfstraceShmUnmap(sqlite3_file*,int); /* ** Method declarations for vfstrace_vfs. */ static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir); static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *); static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *); static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename); static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg); static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); static void vfstraceDlClose(sqlite3_vfs*, void*); static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut); static int vfstraceSleep(sqlite3_vfs*, int microseconds); static int vfstraceCurrentTime(sqlite3_vfs*, double*); static int vfstraceGetLastError(sqlite3_vfs*, int, char*); static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr); static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *); static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName); /* ** Return a pointer to the tail of the pathname. Examples: ** ** /home/drh/xyzzy.txt -> xyzzy.txt ** xyzzy.txt -> xyzzy.txt */ static const char *fileTail(const char *z){ int i; if( z==0 ) return 0; i = strlen(z)-1; while( i>0 && z[i-1]!='/' ){ i--; } return &z[i]; } /* ** Send trace output defined by zFormat and subsequent arguments. */ static void vfstrace_printf( vfstrace_info *pInfo, const char *zFormat, ... ){ va_list ap; char *zMsg; va_start(ap, zFormat); zMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); pInfo->xOut(zMsg, pInfo->pOutArg); sqlite3_free(zMsg); } /* ** Convert value rc into a string and print it using zFormat. zFormat ** should have exactly one %s */ static void vfstrace_print_errcode( vfstrace_info *pInfo, const char *zFormat, int rc ){ char zBuf[50]; char *zVal; switch( rc ){ case SQLITE_OK: zVal = "SQLITE_OK"; break; case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break; case SQLITE_PERM: zVal = "SQLITE_PERM"; break; case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break; case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break; case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break; case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break; case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break; case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break; case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break; case SQLITE_FULL: zVal = "SQLITE_FULL"; break; case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break; case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break; case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break; case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break; case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break; case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break; case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break; case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break; case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break; case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break; case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break; case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break; case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break; case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break; case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break; case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break; case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break; case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break; case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break; case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break; case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break; case SQLITE_IOERR_CHECKRESERVEDLOCK: zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break; case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break; case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break; case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break; case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break; case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break; case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break; case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break; case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break; default: { sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); zVal = zBuf; break; } } vfstrace_printf(pInfo, zFormat, zVal); } /* ** Append to a buffer. */ static void strappend(char *z, int *pI, const char *zAppend){ int i = *pI; while( zAppend[0] ){ z[i++] = *(zAppend++); } z[i] = 0; *pI = i; } /* ** Close an vfstrace-file. */ static int vfstraceClose(sqlite3_file *pFile){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName); rc = p->pReal->pMethods->xClose(p->pReal); vfstrace_print_errcode(pInfo, " -> %s\n", rc); if( rc==SQLITE_OK ){ sqlite3_free((void*)p->base.pMethods); p->base.pMethods = 0; } return rc; } /* ** Read data from an vfstrace-file. */ static int vfstraceRead( sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)", pInfo->zVfsName, p->zFName, iAmt, iOfst); rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Write data to an vfstrace-file. */ static int vfstraceWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)", pInfo->zVfsName, p->zFName, iAmt, iOfst); rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Truncate an vfstrace-file. */ static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName, size); rc = p->pReal->pMethods->xTruncate(p->pReal, size); vfstrace_printf(pInfo, " -> %d\n", rc); return rc; } /* ** Sync an vfstrace-file. */ static int vfstraceSync(sqlite3_file *pFile, int flags){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; int i; char zBuf[100]; memcpy(zBuf, "|0", 3); i = 0; if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL"); else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL"); if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY"); if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){ sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags); } vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName, &zBuf[1]); rc = p->pReal->pMethods->xSync(p->pReal, flags); vfstrace_printf(pInfo, " -> %d\n", rc); return rc; } /* ** Return the current file-size of an vfstrace-file. */ static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName); rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); vfstrace_print_errcode(pInfo, " -> %s,", rc); vfstrace_printf(pInfo, " size=%lld\n", *pSize); return rc; } /* ** Return the name of a lock. */ static const char *lockName(int eLock){ const char *azLockNames[] = { "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" }; if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){ return "???"; }else{ return azLockNames[eLock]; } } /* ** Lock an vfstrace-file. */ static int vfstraceLock(sqlite3_file *pFile, int eLock){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName, lockName(eLock)); rc = p->pReal->pMethods->xLock(p->pReal, eLock); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Unlock an vfstrace-file. */ static int vfstraceUnlock(sqlite3_file *pFile, int eLock){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName, lockName(eLock)); rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Check if another file-handle holds a RESERVED lock on an vfstrace-file. */ static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)", pInfo->zVfsName, p->zFName); rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); vfstrace_print_errcode(pInfo, " -> %s", rc); vfstrace_printf(pInfo, ", out=%d\n", *pResOut); return rc; } /* ** File control method. For custom operations on an vfstrace-file. */ static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; char zBuf[100]; char *zOp; switch( op ){ case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break; case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break; case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break; case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break; case SQLITE_FCNTL_SIZE_HINT: { sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld", *(sqlite3_int64*)pArg); zOp = zBuf; break; } case SQLITE_FCNTL_CHUNK_SIZE: { sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg); zOp = zBuf; break; } case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break; case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break; case 0xca093fa0: zOp = "DB_UNCHANGED"; break; default: { sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); zOp = zBuf; break; } } vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)", pInfo->zVfsName, p->zFName, zOp); rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Return the sector-size in bytes for an vfstrace-file. */ static int vfstraceSectorSize(sqlite3_file *pFile){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName); rc = p->pReal->pMethods->xSectorSize(p->pReal); vfstrace_printf(pInfo, " -> %d\n", rc); return rc; } /* ** Return the device characteristic flags supported by an vfstrace-file. */ static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)", pInfo->zVfsName, p->zFName); rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); vfstrace_printf(pInfo, " -> 0x%08x\n", rc); return rc; } /* ** Shared-memory operations. */ static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; char zLck[100]; int i = 0; memcpy(zLck, "|0", 3); if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK"); if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK"); if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED"); if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE"); if( flags & ~(0xf) ){ sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags); } vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d,n=%d,%s)", pInfo->zVfsName, p->zFName, ofst, n, &zLck[1]); rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } static int vfstraceShmMap( sqlite3_file *pFile, int iRegion, int szRegion, int isWrite, void volatile **pp ){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)", pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite); rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } static void vfstraceShmBarrier(sqlite3_file *pFile){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName); p->pReal->pMethods->xShmBarrier(p->pReal); } static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = p->pInfo; int rc; vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)", pInfo->zVfsName, p->zFName, delFlag); rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Open an vfstrace file handle. */ static int vfstraceOpen( sqlite3_vfs *pVfs, const char *zName, sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; vfstrace_file *p = (vfstrace_file *)pFile; vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; p->pInfo = pInfo; p->zFName = zName ? fileTail(zName) : "<temp>"; p->pReal = (sqlite3_file *)&p[1]; rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags); vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)", pInfo->zVfsName, p->zFName, flags); if( p->pReal->pMethods ){ sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) ); const sqlite3_io_methods *pSub = p->pReal->pMethods; memset(pNew, 0, sizeof(*pNew)); pNew->iVersion = pSub->iVersion; pNew->xClose = vfstraceClose; pNew->xRead = vfstraceRead; pNew->xWrite = vfstraceWrite; pNew->xTruncate = vfstraceTruncate; pNew->xSync = vfstraceSync; pNew->xFileSize = vfstraceFileSize; pNew->xLock = vfstraceLock; pNew->xUnlock = vfstraceUnlock; pNew->xCheckReservedLock = vfstraceCheckReservedLock; pNew->xFileControl = vfstraceFileControl; pNew->xSectorSize = vfstraceSectorSize; pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics; if( pNew->iVersion>=2 ){ pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0; pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0; pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; } pFile->pMethods = pNew; } vfstrace_print_errcode(pInfo, " -> %s", rc); if( pOutFlags ){ vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags); }else{ vfstrace_printf(pInfo, "\n"); } return rc; } /* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; int rc; vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", pInfo->zVfsName, zPath, dirSync); rc = pRoot->xDelete(pRoot, zPath, dirSync); vfstrace_print_errcode(pInfo, " -> %s\n", rc); return rc; } /* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int vfstraceAccess( sqlite3_vfs *pVfs, const char *zPath, int flags, int *pResOut ){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; int rc; vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", pInfo->zVfsName, zPath, flags); rc = pRoot->xAccess(pRoot, zPath, flags, pResOut); vfstrace_print_errcode(pInfo, " -> %s", rc); vfstrace_printf(pInfo, ", out=%d\n", *pResOut); return rc; } /* ** Populate buffer zOut with the full canonical pathname corresponding ** to the pathname in zPath. zOut is guaranteed to point to a buffer ** of at least (DEVSYM_MAX_PATHNAME+1) bytes. */ static int vfstraceFullPathname( sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; int rc; vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")", pInfo->zVfsName, zPath); rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut); vfstrace_print_errcode(pInfo, " -> %s", rc); vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut); return rc; } /* ** Open the dynamic library located at zPath and return a handle. */ static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath); return pRoot->xDlOpen(pRoot, zPath); } /* ** Populate the buffer zErrMsg (size nByte bytes) with a human readable ** utf-8 string describing the most recent error encountered associated ** with dynamic libraries. */ static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte); pRoot->xDlError(pRoot, nByte, zErrMsg); vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg); } /* ** Return a pointer to the symbol zSymbol in the dynamic library pHandle. */ static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym); return pRoot->xDlSym(pRoot, p, zSym); } /* ** Close the dynamic library handle pHandle. */ static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); pRoot->xDlClose(pRoot, pHandle); } /* ** Populate the buffer pointed to by zBufOut with nByte bytes of ** random data. */ static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte); return pRoot->xRandomness(pRoot, nByte, zBufOut); } /* ** Sleep for nMicro microseconds. Return the number of microseconds ** actually slept. */ static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xSleep(pRoot, nMicro); } /* ** Return the current time as a Julian Day number in *pTimeOut. */ static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xCurrentTime(pRoot, pTimeOut); } static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xCurrentTimeInt64(pRoot, pTimeOut); } /* ** Return th3 emost recent error code and message */ static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xGetLastError(pRoot, iErr, zErr); } /* ** Override system calls. */ static int vfstraceSetSystemCall( sqlite3_vfs *pVfs, const char *zName, sqlite3_syscall_ptr pFunc ){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xSetSystemCall(pRoot, zName, pFunc); } static sqlite3_syscall_ptr vfstraceGetSystemCall( sqlite3_vfs *pVfs, const char *zName ){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xGetSystemCall(pRoot, zName); } static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; sqlite3_vfs *pRoot = pInfo->pRootVfs; return pRoot->xNextSystemCall(pRoot, zName); } /* ** Clients invoke this routine to construct a new trace-vfs shim. ** ** Return SQLITE_OK on success. ** ** SQLITE_NOMEM is returned in the case of a memory allocation error. ** SQLITE_NOTFOUND is returned if zOldVfsName does not exist. */ int vfstrace_register( const char *zTraceName, /* Name of the newly constructed VFS */ const char *zOldVfsName, /* Name of the underlying VFS */ int (*xOut)(const char*,void*), /* Output routine. ex: fputs */ void *pOutArg, /* 2nd argument to xOut. ex: stderr */ int makeDefault /* True to make the new VFS the default */ ){ sqlite3_vfs *pNew; sqlite3_vfs *pRoot; vfstrace_info *pInfo; int nName; int nByte; pRoot = sqlite3_vfs_find(zOldVfsName); if( pRoot==0 ) return SQLITE_NOTFOUND; nName = strlen(zTraceName); nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1; pNew = sqlite3_malloc( nByte ); if( pNew==0 ) return SQLITE_NOMEM; memset(pNew, 0, nByte); pInfo = (vfstrace_info*)&pNew[1]; pNew->iVersion = pRoot->iVersion; pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file); pNew->mxPathname = pRoot->mxPathname; pNew->zName = (char*)&pInfo[1]; memcpy((char*)&pInfo[1], zTraceName, nName+1); pNew->pAppData = pInfo; pNew->xOpen = vfstraceOpen; pNew->xDelete = vfstraceDelete; pNew->xAccess = vfstraceAccess; pNew->xFullPathname = vfstraceFullPathname; pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen; pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError; pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym; pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose; pNew->xRandomness = vfstraceRandomness; pNew->xSleep = vfstraceSleep; pNew->xCurrentTime = vfstraceCurrentTime; pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError; if( pNew->iVersion>=2 ){ pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 : vfstraceCurrentTimeInt64; if( pNew->iVersion>=3 ){ pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 : vfstraceSetSystemCall; pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 : vfstraceGetSystemCall; pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 : vfstraceNextSystemCall; } } pInfo->pRootVfs = pRoot; pInfo->xOut = xOut; pInfo->pOutArg = pOutArg; pInfo->zVfsName = pNew->zName; pInfo->pTraceVfs = pNew; vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n", pInfo->zVfsName, pRoot->zName); return sqlite3_vfs_register(pNew, makeDefault); } |
Added src/test_wholenumber.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 | /* ** 2011 April 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 a virtual table that returns the whole numbers ** between 1 and 4294967295, inclusive. ** ** Example: ** ** CREATE VIRTUAL TABLE nums USING wholenumber; ** SELECT value FROM nums WHERE value<10; ** ** Results in: ** ** 1 2 3 4 5 6 7 8 9 */ #include "sqlite3.h" #include <assert.h> #include <string.h> #ifndef SQLITE_OMIT_VIRTUALTABLE /* A wholenumber cursor object */ typedef struct wholenumber_cursor wholenumber_cursor; struct wholenumber_cursor { sqlite3_vtab_cursor base; /* Base class - must be first */ unsigned iValue; /* Current value */ unsigned mxValue; /* Maximum value */ }; /* Methods for the wholenumber module */ static int wholenumberConnect( sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ sqlite3_vtab *pNew; pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); if( pNew==0 ) return SQLITE_NOMEM; sqlite3_declare_vtab(db, "CREATE TABLE x(value)"); memset(pNew, 0, sizeof(*pNew)); return SQLITE_OK; } /* Note that for this virtual table, the xCreate and xConnect ** methods are identical. */ static int wholenumberDisconnect(sqlite3_vtab *pVtab){ sqlite3_free(pVtab); return SQLITE_OK; } /* The xDisconnect and xDestroy methods are also the same */ /* ** Open a new wholenumber cursor. */ static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ wholenumber_cursor *pCur; pCur = sqlite3_malloc( sizeof(*pCur) ); if( pCur==0 ) return SQLITE_NOMEM; memset(pCur, 0, sizeof(*pCur)); *ppCursor = &pCur->base; return SQLITE_OK; } /* ** Close a wholenumber cursor. */ static int wholenumberClose(sqlite3_vtab_cursor *cur){ sqlite3_free(cur); return SQLITE_OK; } /* ** Advance a cursor to its next row of output */ static int wholenumberNext(sqlite3_vtab_cursor *cur){ wholenumber_cursor *pCur = (wholenumber_cursor*)cur; pCur->iValue++; return SQLITE_OK; } /* ** Return the value associated with a wholenumber. */ static int wholenumberColumn( sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i ){ wholenumber_cursor *pCur = (wholenumber_cursor*)cur; sqlite3_result_int64(ctx, pCur->iValue); return SQLITE_OK; } /* ** The rowid. */ static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ wholenumber_cursor *pCur = (wholenumber_cursor*)cur; *pRowid = pCur->iValue; return SQLITE_OK; } /* ** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal ** that the cursor has nothing more to output. */ static int wholenumberEof(sqlite3_vtab_cursor *cur){ wholenumber_cursor *pCur = (wholenumber_cursor*)cur; return pCur->iValue>pCur->mxValue || pCur->iValue==0; } /* ** Called to "rewind" a cursor back to the beginning so that ** it starts its output over again. Always called at least once ** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call. ** ** idxNum Constraints ** ------ --------------------- ** 0 (none) ** 1 value > $argv0 ** 2 value >= $argv0 ** 4 value < $argv0 ** 8 value <= $argv0 ** ** 5 value > $argv0 AND value < $argv1 ** 6 value >= $argv0 AND value < $argv1 ** 9 value > $argv0 AND value <= $argv1 ** 10 value >= $argv0 AND value <= $argv1 */ static int wholenumberFilter( sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor; sqlite3_int64 v; int i = 0; pCur->iValue = 1; pCur->mxValue = 0xffffffff; /* 4294967295 */ if( idxNum & 3 ){ v = sqlite3_value_int64(argv[0]) + (idxNum&1); if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v; i++; } if( idxNum & 12 ){ v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1); if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v; } return SQLITE_OK; } /* ** Search for terms of these forms: ** ** (1) value > $value ** (2) value >= $value ** (4) value < $value ** (8) value <= $value ** ** idxNum is an ORed combination of 1 or 2 with 4 or 8. */ static int wholenumberBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; int idxNum = 0; int argvIdx = 1; int ltIdx = -1; int gtIdx = -1; const struct sqlite3_index_constraint *pConstraint; pConstraint = pIdxInfo->aConstraint; for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ if( pConstraint->usable==0 ) continue; if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){ idxNum |= 1; ltIdx = i; } if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){ idxNum |= 2; ltIdx = i; } if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ idxNum |= 4; gtIdx = i; } if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){ idxNum |= 8; gtIdx = i; } } pIdxInfo->idxNum = idxNum; if( ltIdx>=0 ){ pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++; pIdxInfo->aConstraintUsage[ltIdx].omit = 1; } if( gtIdx>=0 ){ pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx; pIdxInfo->aConstraintUsage[gtIdx].omit = 1; } if( pIdxInfo->nOrderBy==1 && pIdxInfo->aOrderBy[0].desc==0 ){ pIdxInfo->orderByConsumed = 1; } pIdxInfo->estimatedCost = (double)1; return SQLITE_OK; } /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. */ static sqlite3_module wholenumberModule = { 0, /* iVersion */ wholenumberConnect, wholenumberConnect, wholenumberBestIndex, wholenumberDisconnect, wholenumberDisconnect, wholenumberOpen, /* xOpen - open a cursor */ wholenumberClose, /* xClose - close a cursor */ wholenumberFilter, /* xFilter - configure scan constraints */ wholenumberNext, /* xNext - advance a cursor */ wholenumberEof, /* xEof - check for end of scan */ wholenumberColumn, /* xColumn - read data */ wholenumberRowid, /* xRowid - read data */ 0, /* xUpdate */ 0, /* xBegin */ 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ 0, /* xFindMethod */ 0, /* xRename */ }; #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* ** Register the wholenumber virtual table */ int wholenumber_register(sqlite3 *db){ int rc = SQLITE_OK; #ifndef SQLITE_OMIT_VIRTUALTABLE rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0); #endif return rc; } #ifdef SQLITE_TEST #include <tcl.h> /* ** Decode a pointer to an sqlite3 object. */ extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb); /* ** Register the echo virtual table module. */ static int register_wholenumber_module( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite3 *db; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; wholenumber_register(db); return TCL_OK; } /* ** Register commands with the TCL interpreter. */ int Sqlitetestwholenumber_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; void *clientData; } aObjCmd[] = { { "register_wholenumber_module", register_wholenumber_module, 0 }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, aObjCmd[i].clientData, 0); } return TCL_OK; } #endif /* SQLITE_TEST */ |
Changes to src/trigger.c.
︙ | ︙ | |||
258 259 260 261 262 263 264 | Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ char *zName; /* Name of trigger */ sqlite3 *db = pParse->db; /* The database */ DbFixer sFix; /* Fixer object */ int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ | < | 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | Trigger *pTrig = pParse->pNewTrigger; /* Trigger being finished */ char *zName; /* Name of trigger */ sqlite3 *db = pParse->db; /* The database */ DbFixer sFix; /* Fixer object */ int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; zName = pTrig->zName; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); pTrig->step_list = pStepList; while( pStepList ){ pStepList->pTrig = pTrig; |
︙ | ︙ | |||
620 621 622 623 624 625 626 | Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ ExprList *pChanges, /* Columns that change in an UPDATE statement */ int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ int mask = 0; | | > > > > | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 | Parse *pParse, /* Parse context */ Table *pTab, /* The table the contains the triggers */ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */ ExprList *pChanges, /* Columns that change in an UPDATE statement */ int *pMask /* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ ){ int mask = 0; Trigger *pList = 0; Trigger *p; if( (pParse->db->flags & SQLITE_EnableTrigger)!=0 ){ pList = sqlite3TriggerList(pParse, pTab); } assert( pList==0 || IsVirtual(pTab)==0 ); for(p=pList; p; p=p->pNext){ if( p->op==op && checkColumnOverlap(p->pColumns, pChanges) ){ mask |= p->tr_tm; } } if( pMask ){ |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
124 125 126 127 128 129 130 | /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ | < | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto update_cleanup; } assert( pTabList->nSrc==1 ); |
︙ | ︙ | |||
282 283 284 285 286 287 288 | pParse->nMem += pTab->nCol; } if( chngRowid || pTrigger || hasFK ){ regNewRowid = ++pParse->nMem; } regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; | < | 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | pParse->nMem += pTab->nCol; } if( chngRowid || pTrigger || hasFK ){ regNewRowid = ++pParse->nMem; } regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; /* Start the view context. */ if( isView ){ sqlite3AuthContextPush(pParse, &sContext, pTab->zName); } /* If we are trying to update a view, realize that view into |
︙ | ︙ | |||
392 393 394 395 396 397 398 | ** with the required old.* column data. */ if( hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; i<pTab->nCol; i++){ | | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | ** with the required old.* column data. */ if( hasFK || pTrigger ){ u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerColmask(pParse, pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i); }else{ sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i); } } if( chngRowid==0 ){ sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); |
︙ | ︙ |
Changes to src/utf.c.
︙ | ︙ | |||
163 164 165 166 167 168 169 | || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } int sqlite3Utf8Read( const unsigned char *zIn, /* First byte of UTF-8 character */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ | | | 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | || (c&0xFFFFF800)==0xD800 \ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ } int sqlite3Utf8Read( const unsigned char *zIn, /* First byte of UTF-8 character */ const unsigned char **pzNext /* Write first byte past UTF-8 char here */ ){ unsigned int c; /* Same as READ_UTF8() above but without the zTerm parameter. ** For this routine, we assume the UTF8 string is always zero-terminated. */ c = *(zIn++); if( c>=0xc0 ){ c = sqlite3Utf8Trans1[c-0xc0]; |
︙ | ︙ | |||
406 407 408 409 410 411 412 | #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Translate UTF-8 to UTF-8. ** ** This has the effect of making sure that the string is well-formed ** UTF-8. Miscoded characters are removed. ** | | | | | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | #if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) /* ** Translate UTF-8 to UTF-8. ** ** This has the effect of making sure that the string is well-formed ** UTF-8. Miscoded characters are removed. ** ** The translation is done in-place and aborted if the output ** overruns the input. */ int sqlite3Utf8To8(unsigned char *zIn){ unsigned char *zOut = zIn; unsigned char *zStart = zIn; u32 c; while( zIn[0] && zOut<=zIn ){ c = sqlite3Utf8Read(zIn, (const u8**)&zIn); if( c!=0xfffd ){ WRITE_UTF8(zOut, c); } } *zOut = 0; return (int)(zOut - zStart); |
︙ | ︙ |
Changes to src/util.c.
︙ | ︙ | |||
437 438 439 440 441 442 443 | testcase( c==(+1) ); } return c; } /* | | > > | > | | > | | < | | | | | | | | | < > | > > > > > | > > < | < | > | > | > > > > > > > > > > | 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 | testcase( c==(+1) ); } return c; } /* ** Convert zNum to a 64-bit signed integer. ** ** If the zNum value is representable as a 64-bit twos-complement ** integer, then write that value into *pNum and return 0. ** ** If zNum is exactly 9223372036854665808, return 2. This special ** case is broken out because while 9223372036854665808 cannot be a ** signed 64-bit integer, its negative -9223372036854665808 can be. ** ** If zNum is too big for a 64-bit integer and is not ** 9223372036854665808 then return 1. ** ** 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); u64 u = 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 ){ if( *zNum=='-' ){ neg = 1; zNum+=incr; }else if( *zNum=='+' ){ zNum+=incr; } } 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){ u = u*10 + c - '0'; } if( u>LARGEST_INT64 ){ *pNum = SMALLEST_INT64; }else if( neg ){ *pNum = -(i64)u; }else{ *pNum = (i64)u; } 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 */ assert( u<=LARGEST_INT64 ); return 0; }else{ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ c = compare2pow63(zNum, incr); if( c<0 ){ /* zNum is less than 9223372036854775808 so it fits */ assert( u<=LARGEST_INT64 ); return 0; }else if( c>0 ){ /* zNum is greater than 9223372036854775808 so it overflows */ return 1; }else{ /* zNum is exactly 9223372036854775808. Fits if negative. The ** special case 2 overflow if positive */ assert( u-1==LARGEST_INT64 ); assert( (*pNum)==SMALLEST_INT64 ); return neg ? 0 : 2; } } } /* ** If zNum represents an integer that will fit in 32-bits, then set ** *pValue to that integer and return true. Otherwise return false. ** |
︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 | testcase( sqlite3GlobalConfig.xLog!=0 ); logBadConnection("invalid"); return 0; }else{ return 1; } } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | testcase( sqlite3GlobalConfig.xLog!=0 ); logBadConnection("invalid"); return 0; }else{ return 1; } } /* ** Attempt to add, substract, or multiply the 64-bit signed value iB against ** the other 64-bit signed integer at *pA and store the result in *pA. ** Return 0 on success. Or if the operation would have resulted in an ** overflow, leave *pA unchanged and return 1. */ int sqlite3AddInt64(i64 *pA, i64 iB){ i64 iA = *pA; testcase( iA==0 ); testcase( iA==1 ); testcase( iB==-1 ); testcase( iB==0 ); if( iB>=0 ){ testcase( iA>0 && LARGEST_INT64 - iA == iB ); testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; *pA += iB; }else{ testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; *pA += iB; } return 0; } int sqlite3SubInt64(i64 *pA, i64 iB){ testcase( iB==SMALLEST_INT64+1 ); if( iB==SMALLEST_INT64 ){ testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); if( (*pA)>=0 ) return 1; *pA -= iB; return 0; }else{ return sqlite3AddInt64(pA, -iB); } } #define TWOPOWER32 (((i64)1)<<32) #define TWOPOWER31 (((i64)1)<<31) int sqlite3MulInt64(i64 *pA, i64 iB){ i64 iA = *pA; i64 iA1, iA0, iB1, iB0, r; iA1 = iA/TWOPOWER32; iA0 = iA % TWOPOWER32; iB1 = iB/TWOPOWER32; iB0 = iB % TWOPOWER32; if( iA1*iB1 != 0 ) return 1; assert( iA1*iB0==0 || iA0*iB1==0 ); r = iA1*iB0 + iA0*iB1; testcase( r==(-TWOPOWER31)-1 ); testcase( r==(-TWOPOWER31) ); testcase( r==TWOPOWER31 ); testcase( r==TWOPOWER31-1 ); if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; r *= TWOPOWER32; if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; *pA = r; return 0; } /* ** Compute the absolute value of a 32-bit signed integer, of possible. Or ** if the integer has a value of -2147483648, return +2147483647 */ int sqlite3AbsInt32(int x){ if( x>=0 ) return x; if( x==(int)0x80000000 ) return 0x7fffffff; return -x; } |
Changes to src/vdbe.c.
︙ | ︙ | |||
567 568 569 570 571 572 573 | #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ #endif /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ | | | 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 | #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ #endif /*** INSERT STACK UNION HERE ***/ assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */ sqlite3VdbeEnter(p); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or ** sqlite3_column_text16() failed. */ goto no_mem; } assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || ((p->rc&0xFF) == SQLITE_LOCKED)); p->rc = SQLITE_OK; |
︙ | ︙ | |||
1242 1243 1244 1245 1246 1247 1248 | pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ iA = pIn1->u.i; iB = pIn2->u.i; switch( pOp->opcode ){ | | | | < < < < < < < | > | 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 | pOut = &aMem[pOp->p3]; flags = pIn1->flags | pIn2->flags; if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ iA = pIn1->u.i; iB = pIn2->u.i; switch( pOp->opcode ){ case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break; case OP_Divide: { if( iA==0 ) goto arithmetic_result_is_null; if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; iB /= iA; break; } default: { if( iA==0 ) goto arithmetic_result_is_null; if( iA==-1 ) iA = 1; iB %= iA; break; } } pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ case OP_Add: rB += rA; break; case OP_Subtract: rB -= rA; break; case OP_Multiply: rB *= rA; break; case OP_Divide: { |
︙ | ︙ | |||
1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 | 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); | > | 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 | 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 */ sqlite3VdbeMutexResync(p); 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); |
︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 | /* Copy the result of the function into register P3 */ sqlite3VdbeChangeEncoding(&ctx.s, encoding); sqlite3VdbeMemMove(pOut, &ctx.s); if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: BitAnd P1 P2 P3 * * ** | > > > > > > > > > | 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 | /* Copy the result of the function into register P3 */ sqlite3VdbeChangeEncoding(&ctx.s, encoding); sqlite3VdbeMemMove(pOut, &ctx.s); if( sqlite3VdbeMemTooBig(pOut) ){ goto too_big; } #if 0 /* The app-defined function has done something that as caused this ** statement to expire. (Perhaps the function called sqlite3_exec() ** with a CREATE TABLE statement.) */ if( p->expired ) rc = SQLITE_ABORT; #endif REGISTER_TRACE(pOp->p3, pOut); UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: BitAnd P1 P2 P3 * * ** |
︙ | ︙ | |||
1462 1463 1464 1465 1466 1467 1468 | ** Store the result in register P3. ** If either input is NULL, the result is NULL. */ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ | | > | > | | | | > > > > > | > > > | > > | > > > > > > > | > > | > > > | | 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 | ** Store the result in register P3. ** If either input is NULL, the result is NULL. */ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ i64 iA; u64 uA; i64 iB; u8 op; pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; pOut = &aMem[pOp->p3]; if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); break; } iA = sqlite3VdbeIntValue(pIn2); iB = sqlite3VdbeIntValue(pIn1); op = pOp->opcode; if( op==OP_BitAnd ){ iA &= iB; }else if( op==OP_BitOr ){ iA |= iB; }else if( iB!=0 ){ assert( op==OP_ShiftRight || op==OP_ShiftLeft ); /* If shifting by a negative amount, shift in the other direction */ if( iB<0 ){ assert( OP_ShiftRight==OP_ShiftLeft+1 ); op = 2*OP_ShiftLeft + 1 - op; iB = iB>(-64) ? -iB : 64; } if( iB>=64 ){ iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; }else{ memcpy(&uA, &iA, sizeof(uA)); if( op==OP_ShiftLeft ){ uA <<= iB; }else{ uA >>= iB; /* Sign-extend on a right shift of a negative number */ if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); } memcpy(&iA, &uA, sizeof(iA)); } } pOut->u.i = iA; MemSetTypeFlag(pOut, MEM_Int); break; } /* Opcode: AddImm P1 P2 * * * ** ** Add the constant P2 to the value in register P1. |
︙ | ︙ | |||
2407 2408 2409 2410 2411 2412 2413 | ** Each type field is a varint representing the serial type of the ** corresponding data element (see sqlite3VdbeSerialType()). The ** hdr-size field is also a varint which is the offset from the beginning ** of the record to data0. */ nData = 0; /* Number of bytes of data space */ nHdr = 0; /* Number of bytes of header space */ | < | 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 | ** Each type field is a varint representing the serial type of the ** corresponding data element (see sqlite3VdbeSerialType()). The ** hdr-size field is also a varint which is the offset from the beginning ** of the record to data0. */ nData = 0; /* Number of bytes of data space */ nHdr = 0; /* Number of bytes of header space */ nZero = 0; /* Number of zero bytes at the end of the record */ 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]; |
︙ | ︙ | |||
2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 | 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 ){ | > | 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 | if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); sqlite3VdbeMutexResync(p); 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 ){ |
︙ | ︙ | |||
2768 2769 2770 2771 2772 2773 2774 | ** ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); | | | 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 | ** ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2); if( rc==SQLITE_BUSY ){ p->pc = pc; p->rc = rc = SQLITE_BUSY; |
︙ | ︙ | |||
2824 2825 2826 2827 2828 2829 2830 | int iCookie; iDb = pOp->p1; iCookie = pOp->p3; assert( pOp->p3<SQLITE_N_BTREE_META ); assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); | | | | 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 | int iCookie; iDb = pOp->p1; iCookie = pOp->p3; assert( pOp->p3<SQLITE_N_BTREE_META ); assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta); pOut->u.i = iMeta; break; } /* Opcode: SetCookie P1 P2 P3 * * ** ** Write the content of register P3 (interpreted as an integer) ** into cookie number P2 of database P1. P2==1 is the schema version. ** P2==2 is the database format. P2==3 is the recommended pager cache ** size, and so forth. P1==0 is the main database file and P1==1 is the ** database file used to store temporary tables. ** ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ Db *pDb; assert( pOp->p2<SQLITE_N_BTREE_META ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i); if( pOp->p2==BTREE_SCHEMA_VERSION ){ |
︙ | ︙ | |||
2869 2870 2871 2872 2873 2874 2875 | ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); p->expired = 0; } break; } | | | > > > > | > | > | | 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 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 | ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); p->expired = 0; } break; } /* Opcode: VerifyCookie P1 P2 P3 * * ** ** Check the value of global database parameter number 0 (the ** schema version) and make sure it is equal to P2 and that the ** generation counter on the local schema parse equals P3. ** ** P1 is the database number which is 0 for the main database file ** and 1 for the file holding temporary tables and some higher number ** for auxiliary databases. ** ** The cookie changes its value whenever the database schema changes. ** This operation is used to detect when that the cookie has changed ** and that the current process needs to reread the schema. ** ** Either a transaction needs to have been started or an OP_Open needs ** to be executed (to establish a read lock) before this opcode is ** invoked. */ case OP_VerifyCookie: { int iMeta; int iGen; Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); iGen = db->aDb[pOp->p1].pSchema->iGeneration; }else{ iMeta = 0; } if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie ** stored with the in-memory representation of the schema, do ** not reload the schema from the database file. ** ** If virtual-tables are in use, this is not just an optimization. ** Often, v-tables store their data in other SQLite tables, which ** are queried from within xNext() and other v-table methods using ** prepared queries. If such a query is out-of-date, we do not want to ** discard the database schema, as the user code implementing the ** v-table would have to be ready for the sqlite3_vtab structure itself ** to be invalidated whenever sqlite3_step() is called from within ** a v-table method. */ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ sqlite3ResetInternalSchema(db, pOp->p1); sqlite3VdbeMutexResync(p); } p->expired = 1; rc = SQLITE_SCHEMA; } break; } /* Opcode: OpenRead P1 P2 P3 P4 P5 ** |
︙ | ︙ | |||
2992 2993 2994 2995 2996 2997 2998 | } nField = 0; pKeyInfo = 0; p2 = pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDb<db->nDb ); | | | 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 | } nField = 0; pKeyInfo = 0; p2 = pOp->p2; iDb = pOp->p3; assert( iDb>=0 && iDb<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); pDb = &db->aDb[iDb]; pX = pDb->pBt; assert( pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ wrFlag = 1; if( pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = pDb->pSchema->file_format; |
︙ | ︙ | |||
3681 3682 3683 3684 3685 3686 3687 | ** ** The second algorithm is to select a rowid at random and see if ** it already exists in the table. If it does not exist, we have ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ assert( pC->isTable ); | < | 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 | ** ** The second algorithm is to select a rowid at random and see if ** it already exists in the table. If it does not exist, we have ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ assert( pC->isTable ); #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff #else /* Some compilers complain about constants of the form 0x7fffffffffffffff. ** Others complain about 0x7ffffffffffffffffLL. The following macro seems ** to provide the constant while making all compilers happy. |
︙ | ︙ | |||
4488 4489 4490 4491 4492 4493 4494 | pOut->flags = MEM_Null; if( iCnt>1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ iDb = pOp->p3; assert( iCnt==1 ); | | | 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 | pOut->flags = MEM_Null; if( iCnt>1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ iDb = pOp->p3; assert( iCnt==1 ); assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 ); rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved); pOut->flags = MEM_Int; pOut->u.i = iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM if( rc==SQLITE_OK && iMoved!=0 ){ sqlite3RootPageMoved(&db->aDb[iDb], iMoved, pOp->p1); resetSchemaOnFault = 1; |
︙ | ︙ | |||
4524 4525 4526 4527 4528 4529 4530 | ** ** See also: Destroy */ case OP_Clear: { int nChange; nChange = 0; | | | 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 | ** ** See also: Destroy */ case OP_Clear: { int nChange; nChange = 0; assert( (p->btreeMask & (((yDbMask)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]) ); |
︙ | ︙ | |||
4569 4570 4571 4572 4573 4574 4575 | case OP_CreateTable: { /* out2-prerelease */ int pgno; int flags; Db *pDb; pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); | | | | < < < < < < | < < < < < < < < < < < | < | < < < < < < > > | > > > | > > > | > < | 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 | case OP_CreateTable: { /* out2-prerelease */ int pgno; int flags; Db *pDb; pgno = 0; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)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 * * P4 * ** ** Read and parse all entries from the SQLITE_MASTER table of database P1 ** that match the WHERE clause P4. ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { int iDb; const char *zMaster; char *zSql; InitData initData; /* Any prepared statement that invokes this opcode will hold mutexes ** on every btree. This is a prerequisite for invoking ** sqlite3InitCallback(). */ #ifdef SQLITE_DEBUG for(iDb=0; iDb<db->nDb; iDb++){ assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); } #endif assert( p->btreeMask == ~(yDbMask)0 ); iDb = pOp->p1; assert( iDb>=0 && iDb<db->nDb ); assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { zMaster = SCHEMA_TABLE(iDb); initData.db = db; initData.iDb = pOp->p1; initData.pzErrMsg = &p->zErrMsg; zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", db->aDb[iDb].zName, zMaster, pOp->p4.z); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ assert( db->init.busy==0 ); db->init.busy = 1; initData.rc = SQLITE_OK; assert( !db->mallocFailed ); rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); if( rc==SQLITE_OK ) rc = initData.rc; sqlite3DbFree(db, zSql); db->init.busy = 0; } } if( rc==SQLITE_NOMEM ){ goto no_mem; } break; } #if !defined(SQLITE_OMIT_ANALYZE) |
︙ | ︙ | |||
4749 4750 4751 4752 4753 4754 4755 | assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; for(j=0; j<nRoot; j++){ aRoot[j] = (int)sqlite3VdbeIntValue(&pIn1[j]); } aRoot[j] = 0; assert( pOp->p5<db->nDb ); | | | 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 | assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; for(j=0; j<nRoot; j++){ aRoot[j] = (int)sqlite3VdbeIntValue(&pIn1[j]); } aRoot[j] = 0; assert( pOp->p5<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 ); z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot, (int)pnErr->u.i, &nErr); sqlite3DbFree(db, aRoot); pnErr->u.i -= nErr; sqlite3VdbeMemSetNull(pIn1); if( nErr==0 ){ assert( z==0 ); |
︙ | ︙ | |||
5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 | 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; } /* Opcode: AggFinal P1 P2 * P4 * ** ** Execute the finalizer function for an aggregate. P1 is ** the memory location that is the accumulator for the aggregate. | > > > > > > > > > > > > > > | 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 | 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 */ sqlite3VdbeMutexResync(p); if( ctx.isError ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s)); rc = ctx.isError; } /* The app-defined function has done something that as caused this ** statement to expire. (Perhaps the function called sqlite3_exec() ** with a CREATE TABLE statement.) */ #if 0 if( p->expired ){ rc = SQLITE_ABORT; break; } #endif sqlite3VdbeMemRelease(&ctx.s); break; } /* Opcode: AggFinal P1 P2 * P4 * ** ** Execute the finalizer function for an aggregate. P1 is ** the memory location that is the accumulator for the aggregate. |
︙ | ︙ | |||
5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 | */ case OP_AggFinal: { Mem *pMem; assert( pOp->p1>0 && pOp->p1<=p->nMem ); pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); if( rc ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } #ifndef SQLITE_OMIT_WAL | > > > | | > > > > > > > > > > > > > > > > | > > > > > > > | 5230 5231 5232 5233 5234 5235 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 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 | */ case OP_AggFinal: { Mem *pMem; assert( pOp->p1>0 && pOp->p1<=p->nMem ); pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); sqlite3VdbeMutexResync(p); if( rc ){ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); }else if( p->expired ){ rc = SQLITE_ABORT; } sqlite3VdbeChangeEncoding(pMem, encoding); UPDATE_MAX_BLOBSIZE(pMem); if( sqlite3VdbeMemTooBig(pMem) ){ goto too_big; } break; } #ifndef SQLITE_OMIT_WAL /* Opcode: Checkpoint P1 P2 P3 * * ** ** Checkpoint database P1. This is a no-op if P1 is not currently in ** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL ** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns ** SQLITE_BUSY or not, respectively. Write the number of pages in the ** WAL after the checkpoint into mem[P3+1] and the number of pages ** in the WAL that have been checkpointed after the checkpoint ** completes into mem[P3+2]. However on an error, mem[P3+1] and ** mem[P3+2] are initialized to -1. */ case OP_Checkpoint: { int i; /* Loop counter */ int aRes[3]; /* Results */ Mem *pMem; /* Write results here */ aRes[0] = 0; aRes[1] = aRes[2] = -1; assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE || pOp->p2==SQLITE_CHECKPOINT_FULL || pOp->p2==SQLITE_CHECKPOINT_RESTART ); rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]); if( rc==SQLITE_BUSY ){ rc = SQLITE_OK; aRes[0] = 1; } for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){ sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]); } break; }; #endif #ifndef SQLITE_OMIT_PRAGMA /* Opcode: JournalMode P1 P2 P3 * P5 ** |
︙ | ︙ | |||
5254 5255 5256 5257 5258 5259 5260 | || eNew==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called | | < | | | 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 | || eNew==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_QUERY ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); /* This opcode is used in two places: PRAGMA journal_mode and ATTACH. ** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called ** when the statement is prepared and so p->btreeMask!=0. All mutexes ** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree() ** is not called when the statement is prepared because it requires the ** iDb index of the database as a parameter, and the database has not ** yet been attached so that index is unavailable. We have to wait ** until runtime (now) to get the mutex on the newly attached database. ** No other mutexes are required by the ATTACH command so this is safe ** to do. */ if( p->btreeMask==0 ){ /* This occurs right after ATTACH. Get a mutex on the newly ATTACHed ** database. */ sqlite3VdbeUsesBtree(p, pOp->p1); sqlite3VdbeEnter(p); } pBt = db->aDb[pOp->p1].pBt; pPager = sqlite3BtreePager(pBt); eOld = sqlite3PagerGetJournalMode(pPager); if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld; if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld; |
︙ | ︙ | |||
5368 5369 5370 5371 5372 5373 5374 | ** the P1 database. If the vacuum has finished, jump to instruction ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); | | | 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 | ** the P1 database. If the vacuum has finished, jump to instruction ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; } break; |
︙ | ︙ | |||
5417 5418 5419 5420 5421 5422 5423 | ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); | | | 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 | ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { u8 isWriteLock = (u8)pOp->p3; if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ int p1 = pOp->p1; assert( p1>=0 && p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 ); assert( isWriteLock==0 || isWriteLock==1 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); if( (rc&0xFF)==SQLITE_LOCKED ){ const char *z = pOp->p4.z; sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); } } |
︙ | ︙ | |||
5906 5907 5908 5909 5910 5911 5912 | p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", pc, p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; | | > > > | | 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 | p->rc = rc; testcase( sqlite3GlobalConfig.xLog!=0 ); sqlite3_log(rc, "statement aborts at %d: [%s] %s", pc, p->zSql, p->zErrMsg); sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; if( resetSchemaOnFault ){ sqlite3ResetInternalSchema(db, 0); sqlite3VdbeMutexResync(p); } /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the ** top. */ vdbe_return: sqlite3VdbeLeave(p); return rc; /* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH ** is encountered. */ too_big: sqlite3SetString(&p->zErrMsg, db, "string or blob too big"); |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
298 299 300 301 302 303 304 | u8 runOnlyOnce; /* Automatically expire on reset */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 inVtabMethod; /* See comments above */ u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ | | > < | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | u8 runOnlyOnce; /* Automatically expire on reset */ u8 minWriteFileFormat; /* Minimum file format for writable database files */ u8 inVtabMethod; /* See comments above */ u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ u32 iMutexCounter; /* Mutex counter upon sqlite3VdbeEnter() */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif i64 nFkConstraint; /* Number of imm. FK constraints this VM */ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ char *zSql; /* Text of the SQL statement that generated this */ void *pFree; /* Free this when deleting the vdbe */ |
︙ | ︙ | |||
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | 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 | > > > < < < < < < | 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 | 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); void sqlite3VdbeEnter(Vdbe*); void sqlite3VdbeLeave(Vdbe*); void sqlite3VdbeMutexResync(Vdbe*); #ifdef SQLITE_DEBUG void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY int sqlite3VdbeCheckFk(Vdbe *, int); #else # define sqlite3VdbeCheckFk(p,i) 0 #endif int sqlite3VdbeMemTranslate(Mem*, u8); #ifdef SQLITE_DEBUG void sqlite3VdbePrintSql(Vdbe*); void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf); #endif int sqlite3VdbeMemHandleBom(Mem *pMem); |
︙ | ︙ |
Changes to src/vdbeapi.c.
︙ | ︙ | |||
695 696 697 698 699 700 701 | ** Check to see if column iCol of the given statement is valid. If ** it is, return a pointer to the Mem for the value of that column. ** If iCol is not valid, return a pointer to a Mem which has a value ** of NULL. */ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ Vdbe *pVm; | < < | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 | ** Check to see if column iCol of the given statement is valid. If ** it is, return a pointer to the Mem for the value of that column. ** If iCol is not valid, return a pointer to a Mem which has a value ** of NULL. */ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ Vdbe *pVm; Mem *pOut; pVm = (Vdbe *)pStmt; if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){ sqlite3_mutex_enter(pVm->db->mutex); pOut = &pVm->pResultSet[i]; }else{ /* If the value passed as the second argument is out of range, return ** a pointer to the following static Mem object which contains the ** value SQL NULL. Even though the Mem structure contains an element ** of type i64, on certain architecture (x86) with certain compiler ** switches (-Os), gcc may align this Mem object on a 4-byte boundary |
︙ | ︙ |
Changes to src/vdbeaux.c.
︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | pOp->p5 = 0; pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; p->expired = 0; #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); #endif #ifdef VDBE_PROFILE pOp->cycles = 0; pOp->cnt = 0; | > > > > > | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | pOp->p5 = 0; pOp->p1 = p1; pOp->p2 = p2; pOp->p3 = p3; pOp->p4.p = 0; pOp->p4type = P4_NOTUSED; p->expired = 0; if( op==OP_ParseSchema ){ /* Any program that uses the OP_ParseSchema opcode needs to lock ** all btrees. */ p->btreeMask = ~(yDbMask)0; } #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); #endif #ifdef VDBE_PROFILE pOp->cycles = 0; pOp->cnt = 0; |
︙ | ︙ | |||
453 454 455 456 457 458 459 | ** returned program. */ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ VdbeOp *aOp = p->aOp; assert( aOp && !p->db->mallocFailed ); /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ | | | 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | ** returned program. */ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ VdbeOp *aOp = p->aOp; assert( aOp && !p->db->mallocFailed ); /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ assert( p->btreeMask==0 ); resolveP2Values(p, pnMaxArg); *pnOp = p->nOp; p->aOp = 0; return aOp; } |
︙ | ︙ | |||
555 556 557 558 559 560 561 562 563 564 565 566 567 568 | } /* ** Change the P2 operand of instruction addr so that it points to ** the address of the next instruction to be coded. */ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ sqlite3VdbeChangeP2(p, addr, p->nOp); } /* ** If the input FuncDef structure is ephemeral, then free it. If ** the FuncDef is not ephermal, then do nothing. | > | 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | } /* ** Change the P2 operand of instruction addr so that it points to ** the address of the next instruction to be coded. */ void sqlite3VdbeJumpHere(Vdbe *p, int addr){ assert( addr>=0 ); sqlite3VdbeChangeP2(p, addr, p->nOp); } /* ** If the input FuncDef structure is ephemeral, then free it. If ** the FuncDef is not ephermal, then do nothing. |
︙ | ︙ | |||
940 941 942 943 944 945 946 | return zP4; } #endif /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** | | | | < < | < < | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | return zP4; } #endif /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** ** The prepared statements need to know in advance the complete set of ** attached databases that they will be using. A mask of these databases ** is maintained in p->btreeMask and is used for locking and other purposes. */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ assert( i>=0 && i<p->db->nDb && i<sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); p->btreeMask |= ((yDbMask)1)<<i; } /* ** Compute the sum of all mutex counters for all btrees in the ** given prepared statement. */ #ifndef SQLITE_OMIT_SHARED_CACHE static u32 mutexCounterSum(Vdbe *p){ u32 cntSum = 0; #ifdef SQLITE_DEBUG int i; yDbMask mask; sqlite3 *db = p->db; Db *aDb = db->aDb; int nDb = db->nDb; for(i=0, mask=1; i<nDb; i++, mask += mask){ if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ cntSum += sqlite3BtreeMutexCounter(aDb[i].pBt); } } #else UNUSED_PARAMETER(p); #endif return cntSum; } #endif /* ** If SQLite is compiled to support shared-cache mode and to be threadsafe, ** this routine obtains the mutex associated with each BtShared structure ** that may be accessed by the VM passed as an argument. In doing so it also ** sets the BtShared.db member of each of the BtShared structures, ensuring ** that the correct busy-handler callback is invoked if required. ** ** If SQLite is not threadsafe but does support shared-cache mode, then ** sqlite3BtreeEnter() is invoked to set the BtShared.db variables ** of all of BtShared structures accessible via the database handle ** associated with the VM. ** ** If SQLite is not threadsafe and does not support shared-cache mode, this ** function is a no-op. ** ** The p->btreeMask field is a bitmask of all btrees that the prepared ** statement p will ever use. Let N be the number of bits in p->btreeMask ** corresponding to btrees that use shared cache. Then the runtime of ** this routine is N*N. But as N is rarely more than 1, this should not ** be a problem. */ void sqlite3VdbeEnter(Vdbe *p){ #ifndef SQLITE_OMIT_SHARED_CACHE int i; yDbMask mask; sqlite3 *db = p->db; Db *aDb = db->aDb; int nDb = db->nDb; for(i=0, mask=1; i<nDb; i++, mask += mask){ if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ sqlite3BtreeEnter(aDb[i].pBt); } } p->iMutexCounter = mutexCounterSum(p); #else UNUSED_PARAMETER(p); #endif } /* ** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). */ void sqlite3VdbeLeave(Vdbe *p){ #ifndef SQLITE_OMIT_SHARED_CACHE int i; yDbMask mask; sqlite3 *db = p->db; Db *aDb = db->aDb; int nDb = db->nDb; /* Assert that the all mutexes have been held continously since ** the most recent sqlite3VdbeEnter() or sqlite3VdbeMutexResync(). */ assert( mutexCounterSum(p) == p->iMutexCounter ); for(i=0, mask=1; i<nDb; i++, mask += mask){ if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ sqlite3BtreeLeave(aDb[i].pBt); } } #else UNUSED_PARAMETER(p); #endif } /* ** Recompute the sum of the mutex counters on all btrees used by the ** prepared statement p. ** ** Call this routine while holding a sqlite3VdbeEnter() after doing something ** that might cause one or more of the individual mutexes held by the ** prepared statement to be released. Calling sqlite3BtreeEnter() on ** any BtShared mutex which is not used by the prepared statement is one ** way to cause one or more of the mutexes in the prepared statement ** to be temporarily released. The anti-deadlocking logic in ** sqlite3BtreeEnter() can cause mutexes to be released temporarily then ** reacquired. ** ** Calling this routine is an acknowledgement that some of the individual ** mutexes in the prepared statement might have been released and reacquired. ** So checks to verify that mutex-protected content did not change ** unexpectedly should accompany any call to this routine. */ void sqlite3VdbeMutexResync(Vdbe *p){ #if !defined(SQLITE_OMIT_SHARED_CACHE) && defined(SQLITE_DEBUG) p->iMutexCounter = mutexCounterSum(p); #else UNUSED_PARAMETER(p); #endif } #if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) /* ** Print a single opcode. This routine is used for debugging only. */ void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){ char *zP4; |
︙ | ︙ | |||
1513 1514 1515 1516 1517 1518 1519 | ** Also release any dynamic memory held by the VM in the Vdbe.aMem memory ** cell array. This is necessary as the memory cell array may contain ** pointers to VdbeFrame objects, which may in turn contain pointers to ** open cursors. */ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ | | | 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 | ** Also release any dynamic memory held by the VM in the Vdbe.aMem memory ** cell array. This is necessary as the memory cell array may contain ** pointers to VdbeFrame objects, which may in turn contain pointers to ** open cursors. */ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); sqlite3VdbeFrameRestore(pFrame); } p->pFrame = 0; p->nFrame = 0; if( p->apCsr ){ |
︙ | ︙ | |||
1699 1700 1701 1702 1703 1704 1705 | ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an ** IO error while deleting or truncating a journal file. It is unlikely, ** but could happen. In this case abandon processing and return the error. */ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ | | | 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 | ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an ** IO error while deleting or truncating a journal file. It is unlikely, ** but could happen. In this case abandon processing and return the error. */ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ rc = sqlite3BtreeCommitPhaseTwo(pBt, 0); } } if( rc==SQLITE_OK ){ sqlite3VtabCommit(db); } } |
︙ | ︙ | |||
1831 1832 1833 1834 1835 1836 1837 | ** may be lying around. Returning an error code won't help matters. */ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ | | | 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 | ** may be lying around. Returning an error code won't help matters. */ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); sqlite3VtabCommit(db); } |
︙ | ︙ | |||
1954 1955 1956 1957 1958 1959 1960 | if( eOp==SAVEPOINT_ROLLBACK ){ db->nDeferredCons = p->nStmtDefCons; } } return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 | if( eOp==SAVEPOINT_ROLLBACK ){ db->nDeferredCons = p->nStmtDefCons; } } return rc; } /* ** This function is called when a transaction opened by the database ** handle associated with the VM passed as an argument is about to be ** committed. If there are outstanding deferred foreign key constraint ** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. ** ** If there are outstanding FK violations and this function returns |
︙ | ︙ | |||
2053 2054 2055 2056 2057 2058 2059 | /* No commit or rollback needed if the program never started */ if( p->pc>=0 ){ int mrc; /* Primary error code from p->rc */ int eStatementOp = 0; int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ | | | 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 | /* No commit or rollback needed if the program never started */ if( p->pc>=0 ){ int mrc; /* Primary error code from p->rc */ int eStatementOp = 0; int isSpecialError; /* Set to true if a 'special' error */ /* Lock all btrees used by the statement */ sqlite3VdbeEnter(p); /* Check for one of the special errors */ mrc = p->rc & 0xff; assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ |
︙ | ︙ | |||
2104 2105 2106 2107 2108 2109 2110 | ** above has occurred. */ if( !sqlite3VtabInSync(db) && db->autoCommit && db->writeVdbeCnt==(p->readOnly==0) ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ | | > > | | | > > | | | | | > | | | 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 | ** above has occurred. */ if( !sqlite3VtabInSync(db) && db->autoCommit && db->writeVdbeCnt==(p->readOnly==0) ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ rc = sqlite3VdbeCheckFk(p, 1); if( rc!=SQLITE_OK ){ if( NEVER(p->readOnly) ){ sqlite3VdbeLeave(p); return SQLITE_ERROR; } rc = SQLITE_CONSTRAINT; }else{ /* The auto-commit flag is true, the vdbe program was successful ** or hit an 'OR FAIL' constraint and there are no deferred foreign ** key constraints to hold up the transaction. This means a commit ** is required. */ rc = vdbeCommit(db, p); } if( rc==SQLITE_BUSY && p->readOnly ){ sqlite3VdbeLeave(p); return SQLITE_BUSY; }else if( rc!=SQLITE_OK ){ p->rc = rc; sqlite3RollbackAll(db); }else{ db->nDeferredCons = 0; sqlite3CommitInternalChanges(db); |
︙ | ︙ | |||
2186 2187 2188 2189 2190 2191 2192 | /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ sqlite3ResetInternalSchema(db, 0); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ | > | | 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 | /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ sqlite3ResetInternalSchema(db, 0); db->flags = (db->flags | SQLITE_InternChanges); } /* Release the locks */ sqlite3VdbeMutexResync(p); sqlite3VdbeLeave(p); } /* We have successfully halted and closed the VM. Record this fact. */ if( p->pc>=0 ){ db->activeVdbeCnt--; if( !p->readOnly ){ db->writeVdbeCnt--; |
︙ | ︙ | |||
2212 2213 2214 2215 2216 2217 2218 | ** to invoke any required unlock-notify callbacks. */ if( db->autoCommit ){ sqlite3ConnectionUnlocked(db); } assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); | | | 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 | ** to invoke any required unlock-notify callbacks. */ if( db->autoCommit ){ sqlite3ConnectionUnlocked(db); } assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 ); return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK); } /* ** Each VDBE holds the result of the most recent sqlite3_step() call ** in p->rc. This routine sets that result back to SQLITE_OK. */ |
︙ | ︙ | |||
2488 2489 2490 2491 2492 2493 2494 | /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ # define MAX_6BYTE ((((i64)0x00008000)<<32)-1) i64 i = pMem->u.i; u64 u; if( file_format>=4 && (i&1)==i ){ return 8+(u32)i; } | > > > > > | > | 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 | /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */ # define MAX_6BYTE ((((i64)0x00008000)<<32)-1) i64 i = pMem->u.i; u64 u; if( file_format>=4 && (i&1)==i ){ return 8+(u32)i; } if( i<0 ){ if( i<(-MAX_6BYTE) ) return 6; /* Previous test prevents: u = -(-9223372036854775808) */ u = -i; }else{ u = i; } if( u<=127 ) return 1; if( u<=32767 ) return 2; if( u<=8388607 ) return 3; if( u<=2147483647 ) return 4; if( u<=MAX_6BYTE ) return 5; return 6; } |
︙ | ︙ |
Changes to src/vdbeblob.c.
︙ | ︙ | |||
262 263 264 265 266 267 268 269 270 271 272 273 274 275 | /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); /* Configure the OP_TableLock instruction */ #ifdef SQLITE_OMIT_SHARED_CACHE sqlite3VdbeChangeToNoop(v, 2, 1); | > | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration); /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); /* Configure the OP_TableLock instruction */ #ifdef SQLITE_OMIT_SHARED_CACHE sqlite3VdbeChangeToNoop(v, 2, 1); |
︙ | ︙ |
Changes to src/vdbemem.c.
︙ | ︙ | |||
363 364 365 366 367 368 369 | assert( EIGHT_BYTE_ALIGNMENT(pMem) ); 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) ){ | | | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | assert( EIGHT_BYTE_ALIGNMENT(pMem) ); 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 = 0; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); return value; }else{ return 0; } |
︙ | ︙ | |||
1073 1074 1075 1076 1077 1078 1079 | 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); | > > > > > | < > | > > > | 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 | 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); if( pVal->u.i==SMALLEST_INT64 ){ pVal->flags &= MEM_Int; pVal->flags |= MEM_Real; pVal->r = (double)LARGEST_INT64; }else{ pVal->u.i = -pVal->u.i; } pVal->r = -pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ pVal = sqlite3ValueNew(db); if( pVal==0 ) goto no_mem; } #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]=='\'' ); pVal = sqlite3ValueNew(db); |
︙ | ︙ |
Changes to src/vtab.c.
︙ | ︙ | |||
368 369 370 371 372 373 374 | ); 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); | | | 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | ); 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, 0, 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 ** the first time the virtual table is used in an SQL statement. This |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 | if( rc!=SQLITE_OK ){ walIteratorFree(p); } *pp = p; return rc; } /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. ** ** The amount of information copies from WAL to database might be limited ** by active readers. This routine will never overwrite a database page | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( rc!=SQLITE_OK ){ walIteratorFree(p); } *pp = p; return rc; } /* ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and ** n. If the attempt fails and parameter xBusy is not NULL, then it is a ** busy-handler function. Invoke it and retry the lock until either the ** lock is successfully obtained or the busy-handler returns 0. */ static int walBusyLock( Wal *pWal, /* WAL connection */ int (*xBusy)(void*), /* Function to call when busy */ void *pBusyArg, /* Context argument for xBusyHandler */ int lockIdx, /* Offset of first byte to lock */ int n /* Number of bytes to lock */ ){ int rc; do { rc = walLockExclusive(pWal, lockIdx, n); }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) ); return rc; } /* ** The cache of the wal-index header must be valid to call this function. ** Return the page-size in bytes used by the database. */ static int walPagesize(Wal *pWal){ return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16); } /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. ** ** The amount of information copies from WAL to database might be limited ** by active readers. This routine will never overwrite a database page |
︙ | ︙ | |||
1587 1588 1589 1590 1591 1592 1593 1594 | ** ** The caller must be holding sufficient locks to ensure that no other ** checkpoint is running (in any other thread or process) at the same ** time. */ static int walCheckpoint( Wal *pWal, /* Wal connection */ int sync_flags, /* Flags for OsSync() (or 0) */ | > > > < > | < < | < < | | > | | 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 | ** ** The caller must be holding sufficient locks to ensure that no other ** checkpoint is running (in any other thread or process) at the same ** time. */ static int walCheckpoint( Wal *pWal, /* Wal connection */ int eMode, /* One of PASSIVE, FULL or RESTART */ int (*xBusyCall)(void*), /* Function to call when busy */ void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags for OsSync() (or 0) */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ int szPage; /* Database page-size */ WalIterator *pIter = 0; /* Wal iterator context */ u32 iDbpage = 0; /* Next database page to write */ u32 iFrame = 0; /* Wal frame containing data for iDbpage */ u32 mxSafeFrame; /* Max frame that can be backfilled */ u32 mxPage; /* Max database page to write */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */ szPage = walPagesize(pWal); testcase( szPage<=32768 ); testcase( szPage>=65536 ); pInfo = walCkptInfo(pWal); if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK; /* Allocate the iterator */ rc = walIteratorInit(pWal, &pIter); if( rc!=SQLITE_OK ){ return rc; } assert( pIter ); if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall; /* Compute in mxSafeFrame the index of the last frame of the WAL that is ** safe to write into the database. Frames beyond mxSafeFrame might ** overwrite database pages that are in use by active readers and thus ** cannot be backfilled from the WAL. */ mxSafeFrame = pWal->hdr.mxFrame; mxPage = pWal->hdr.nPage; for(i=1; i<WAL_NREADER; i++){ u32 y = pInfo->aReadMark[i]; if( mxSafeFrame>y ){ assert( y<=pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ pInfo->aReadMark[i] = READMARK_NOT_USED; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); }else if( rc==SQLITE_BUSY ){ mxSafeFrame = y; xBusy = 0; }else{ goto walcheckpoint_out; } } } if( pInfo->nBackfill<mxSafeFrame && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK ){ i64 nSize; /* Current size of database file */ u32 nBackfill = pInfo->nBackfill; /* Sync the WAL to disk */ if( sync_flags ){ rc = sqlite3OsSync(pWal->pWalFd, sync_flags); |
︙ | ︙ | |||
1697 1698 1699 1700 1701 1702 1703 | if( rc==SQLITE_OK ){ pInfo->nBackfill = mxSafeFrame; } } /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); | > > | | > > > > > > > | > > > | > > > > > | > > | 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 | if( rc==SQLITE_OK ){ pInfo->nBackfill = mxSafeFrame; } } /* Release the reader lock held while backfilling */ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1); } if( rc==SQLITE_BUSY ){ /* Reset the return code so as not to report a checkpoint failure ** just because there are active readers. */ rc = SQLITE_OK; } /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal ** file has been copied into the database file, then block until all ** readers have finished using the wal file. This ensures that the next ** process to write to the database restarts the wal file. */ if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){ assert( pWal->writeLock ); if( pInfo->nBackfill<pWal->hdr.mxFrame ){ rc = SQLITE_BUSY; }else if( eMode==SQLITE_CHECKPOINT_RESTART ){ assert( mxSafeFrame==pWal->hdr.mxFrame ); rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); } } } walcheckpoint_out: walIteratorFree(pIter); return rc; } /* |
︙ | ︙ | |||
1735 1736 1737 1738 1739 1740 1741 | ** 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; } | | > > | 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 | ** 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, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0 ); if( rc==SQLITE_OK ){ isDelete = 1; } } walIndexClose(pWal, isDelete); sqlite3OsClose(pWal->pWalFd); |
︙ | ︙ | |||
1947 1948 1949 1950 1951 1952 1953 | u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ assert( pWal->readLock<0 ); /* Not currently locked */ | | > > > > > > > > > > > > > > > > > | > > > > | | 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 | u32 mxReadMark; /* Largest aReadMark[] value */ int mxI; /* Index of largest aReadMark[] value */ int i; /* Loop counter */ int rc = SQLITE_OK; /* Return code */ assert( pWal->readLock<0 ); /* Not currently locked */ /* Take steps to avoid spinning forever if there is a protocol error. ** ** Circumstances that cause a RETRY should only last for the briefest ** instances of time. No I/O or other system calls are done while the ** locks are held, so the locks should not be held for very long. But ** if we are unlucky, another process that is holding a lock might get ** paged out or take a page-fault that is time-consuming to resolve, ** during the few nanoseconds that it is holding the lock. In that case, ** it might take longer than normal for the lock to free. ** ** After 5 RETRYs, we begin calling sqlite3OsSleep(). The first few ** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this ** is more of a scheduler yield than an actual delay. But on the 10th ** an subsequent retries, the delays start becoming longer and longer, ** so that on the 100th (and last) RETRY we delay for 21 milliseconds. ** The total delay time before giving up is less than 1 second. */ if( cnt>5 ){ int nDelay = 1; /* Pause time in microseconds */ if( cnt>100 ){ VVA_ONLY( pWal->lockError = 1; ) return SQLITE_PROTOCOL; } if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */ sqlite3OsSleep(pWal->pVfs, nDelay); } if( !useWal ){ rc = walIndexReadHdr(pWal, pChanged); if( rc==SQLITE_BUSY ){ /* If there is not a recovery running in another thread or process ** then convert BUSY errors to WAL_RETRY. If recovery is known to |
︙ | ︙ | |||
2032 2033 2034 2035 2036 2037 2038 | u32 thisMark = pInfo->aReadMark[i]; if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){ assert( thisMark!=READMARK_NOT_USED ); mxReadMark = thisMark; mxI = i; } } | | < < < < < < < < < < < < < < > | > > > > | 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 | u32 thisMark = pInfo->aReadMark[i]; if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){ assert( thisMark!=READMARK_NOT_USED ); mxReadMark = thisMark; mxI = i; } } /* There was once an "if" here. The extra "{" is to preserve indentation. */ { if( mxReadMark < pWal->hdr.mxFrame || mxI==0 ){ for(i=1; i<WAL_NREADER; i++){ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1); if( rc==SQLITE_OK ){ mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame; mxI = i; walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1); break; }else if( rc!=SQLITE_BUSY ){ return rc; } } } if( mxI==0 ){ assert( rc==SQLITE_BUSY ); return WAL_RETRY; } rc = walLockShared(pWal, WAL_READ_LOCK(mxI)); if( rc ){ return rc==SQLITE_BUSY ? WAL_RETRY : rc; } /* Now that the read-lock has been obtained, check that neither the ** value in the aReadMark[] array or the contents of the wal-index |
︙ | ︙ | |||
2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 | int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); return rc; } /* ** Finish with a read transaction. All this does is release the ** read-lock. */ | > > > > | 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 | int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){ int rc; /* Return code */ int cnt = 0; /* Number of TryBeginRead attempts */ do{ rc = walTryBeginRead(pWal, pChanged, 0, ++cnt); }while( rc==WAL_RETRY ); testcase( (rc&0xff)==SQLITE_BUSY ); testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); return rc; } /* ** Finish with a read transaction. All this does is release the ** read-lock. */ |
︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 | int rc = SQLITE_OK; int cnt; if( pWal->readLock==0 ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); if( pInfo->nBackfill>0 ){ rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions ** frames will overwrite the start of the existing log. Update the ** wal-index header to reflect this. ** ** In theory it would be Ok to update the cache of the header only ** at this point. But updating the actual wal-index header is also ** safe and means there is no special case for sqlite3WalUndo() ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); | > > | > > > > | 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 | int rc = SQLITE_OK; int cnt; if( pWal->readLock==0 ){ volatile WalCkptInfo *pInfo = walCkptInfo(pWal); assert( pInfo->nBackfill==pWal->hdr.mxFrame ); if( pInfo->nBackfill>0 ){ u32 salt1; sqlite3_randomness(4, &salt1); rc = walLockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1); if( rc==SQLITE_OK ){ /* If all readers are using WAL_READ_LOCK(0) (in other words if no ** readers are currently using the WAL), then the transactions ** frames will overwrite the start of the existing log. Update the ** wal-index header to reflect this. ** ** In theory it would be Ok to update the cache of the header only ** at this point. But updating the actual wal-index header is also ** safe and means there is no special case for sqlite3WalUndo() ** to handle if this transaction is rolled back. */ int i; /* Loop counter */ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */ pWal->nCkpt++; pWal->hdr.mxFrame = 0; sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0])); aSalt[1] = salt1; 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; rc = walTryBeginRead(pWal, ¬Used, 1, ++cnt); }while( rc==WAL_RETRY ); assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */ testcase( (rc&0xff)==SQLITE_IOERR ); testcase( rc==SQLITE_PROTOCOL ); testcase( rc==SQLITE_OK ); } return rc; } /* ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). |
︙ | ︙ | |||
2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 | /* ** This routine is called to implement sqlite3_wal_checkpoint() and ** related interfaces. ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > | | > > > > > > > > > | | 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 | /* ** This routine is called to implement sqlite3_wal_checkpoint() and ** related interfaces. ** ** Obtain a CHECKPOINT lock and then backfill as much information as ** we can from WAL into the database. ** ** If parameter xBusy is not NULL, it is a pointer to a busy-handler ** callback. In this case this function runs a blocking checkpoint. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ int eMode, /* PASSIVE, FULL or RESTART */ int (*xBusy)(void*), /* Function to call when busy */ void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ u8 *zBuf, /* Temporary buffer to use */ int *pnLog, /* OUT: Number of frames in WAL */ int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ){ int rc; /* Return code */ int isChanged = 0; /* True if a new wal-index header is loaded */ int eMode2 = eMode; /* Mode to pass to walCheckpoint() */ assert( pWal->ckptLock==0 ); assert( pWal->writeLock==0 ); WALTRACE(("WAL%p: checkpoint begins\n", pWal)); rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1); if( rc ){ /* Usually this is SQLITE_BUSY meaning that another thread or process ** is already running a checkpoint, or maybe a recovery. But it might ** also be SQLITE_IOERR. */ return rc; } pWal->ckptLock = 1; /* If this is a blocking-checkpoint, then obtain the write-lock as well ** to prevent any writers from running while the checkpoint is underway. ** This has to be done before the call to walIndexReadHdr() below. ** ** If the writer lock cannot be obtained, then a passive checkpoint is ** run instead. Since the checkpointer is not holding the writer lock, ** there is no point in blocking waiting for any readers. Assuming no ** other error occurs, this function will return SQLITE_BUSY to the caller. */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1); if( rc==SQLITE_OK ){ pWal->writeLock = 1; }else if( rc==SQLITE_BUSY ){ eMode2 = SQLITE_CHECKPOINT_PASSIVE; rc = SQLITE_OK; } } /* Read the wal-index header. */ if( rc==SQLITE_OK ){ rc = walIndexReadHdr(pWal, &isChanged); } /* Copy data from the log to the database file. */ if( rc==SQLITE_OK ){ if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){ rc = SQLITE_CORRUPT_BKPT; }else{ rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf); } /* If no error occurred, set the output variables. */ if( rc==SQLITE_OK || rc==SQLITE_BUSY ){ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame; if( pnCkpt ) *pnCkpt = (int)(walCkptInfo(pWal)->nBackfill); } } if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was ** performed, then the pager-cache associated with pWal is now ** out of date. So zero the cached wal-index header to ensure that ** next time the pager opens a snapshot on this database it knows that ** the cache needs to be reset. */ memset(&pWal->hdr, 0, sizeof(WalIndexHdr)); } /* Release the locks. */ sqlite3WalEndWriteTransaction(pWal); walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1); pWal->ckptLock = 0; WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok")); return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc); } /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ |
︙ | ︙ |
Changes to src/wal.h.
︙ | ︙ | |||
16 17 18 19 20 21 22 | #ifndef _WAL_H_ #define _WAL_H_ #include "sqliteInt.h" #ifdef SQLITE_OMIT_WAL | | | | | | | | | | | | | | | | 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 | #ifndef _WAL_H_ #define _WAL_H_ #include "sqliteInt.h" #ifdef SQLITE_OMIT_WAL # define sqlite3WalOpen(x,y,z) 0 # define sqlite3WalClose(w,x,y,z) 0 # define sqlite3WalBeginReadTransaction(y,z) 0 # define sqlite3WalEndReadTransaction(z) # define sqlite3WalRead(v,w,x,y,z) 0 # define sqlite3WalDbsize(y) 0 # define sqlite3WalBeginWriteTransaction(y) 0 # define sqlite3WalEndWriteTransaction(x) 0 # 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(r,s,t,u,v,w,x,y,z) 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. */ |
︙ | ︙ | |||
82 83 84 85 86 87 88 89 90 | /* Write a frame or frames to the log. */ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ | > > > | > > | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* Write a frame or frames to the log. */ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ int eMode, /* One of PASSIVE, FULL and RESTART */ int (*xBusy)(void*), /* Function to call when busy */ void *pBusyArg, /* Context argument for xBusyHandler */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ u8 *zBuf, /* Temporary buffer to use */ int *pnLog, /* OUT: Number of frames in WAL */ int *pnCkpt /* OUT: Number of backfilled frames in WAL */ ); /* Return the value to pass to a sqlite3_wal_hook callback, the ** number of frames in the WAL at the point of the last commit since ** sqlite3WalCallback() was called. If no commits have occurred since ** the last call, then return 0. */ |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ** the WHERE clause of SQL statements. This module is responsible for ** generating the code that loops through a table looking for applicable ** rows. Indices are selected and used to speed the search when doing ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". */ #include "sqliteInt.h" /* ** Trace output macros */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) int sqlite3WhereTrace = 0; #endif | > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ** the WHERE clause of SQL statements. This module is responsible for ** generating the code that loops through a table looking for applicable ** rows. Indices are selected and used to speed the search when doing ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". */ #include "sqliteInt.h" /* ** Trace output macros */ #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) int sqlite3WhereTrace = 0; #endif |
︙ | ︙ | |||
113 114 115 116 117 118 119 120 121 122 123 124 125 126 | #define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(db, pExpr) */ #define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */ #define TERM_CODED 0x04 /* This term is already coded */ #define TERM_COPIED 0x08 /* Has a child */ #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ /* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. */ struct WhereClause { Parse *pParse; /* The parser context */ | > > > > > | 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | #define TERM_DYNAMIC 0x01 /* Need to call sqlite3ExprDelete(db, pExpr) */ #define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */ #define TERM_CODED 0x04 /* This term is already coded */ #define TERM_COPIED 0x08 /* Has a child */ #define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ #define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ #define TERM_OR_OK 0x40 /* Used during OR-clause processing */ #ifdef SQLITE_ENABLE_STAT2 # define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */ #else # define TERM_VNULL 0x00 /* Disabled if not using stat2 */ #endif /* ** An instance of the following structure holds all information about a ** WHERE clause. Mostly this is a container for one or more WhereTerms. */ struct WhereClause { Parse *pParse; /* The parser context */ |
︙ | ︙ | |||
206 207 208 209 210 211 212 213 214 215 216 217 218 219 | #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* ** Value for wsFlags returned by bestIndex() and stored in ** WhereLevel.wsFlags. These flags determine which search | > | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | #define WO_LE (WO_EQ<<(TK_LE-TK_EQ)) #define WO_GT (WO_EQ<<(TK_GT-TK_EQ)) #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 #define WO_OR 0x100 /* Two or more OR-connected terms */ #define WO_AND 0x200 /* Two or more AND-connected terms */ #define WO_NOOP 0x800 /* This term does not restrict search space */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ #define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */ /* ** Value for wsFlags returned by bestIndex() and stored in ** WhereLevel.wsFlags. These flags determine which search |
︙ | ︙ | |||
1056 1057 1058 1059 1060 1061 1062 | exprAnalyze(pSrc, pWC, idxNew); pTerm = &pWC->a[idxTerm]; pWC->a[idxNew].iParent = idxTerm; pTerm->nChild = 1; }else{ sqlite3ExprListDelete(db, pList); } | | | 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 | exprAnalyze(pSrc, pWC, idxNew); pTerm = &pWC->a[idxTerm]; pWC->a[idxNew].iParent = idxTerm; pTerm->nChild = 1; }else{ sqlite3ExprListDelete(db, pList); } pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */ } } } #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ /* |
︙ | ︙ | |||
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 | pTerm->nChild = 1; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ pTerm->prereqRight |= extraRight; } /* | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | pTerm->nChild = 1; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_OMIT_VIRTUALTABLE */ #ifdef SQLITE_ENABLE_STAT2 /* When sqlite_stat2 histogram data is available an operator of the ** form "x IS NOT NULL" can sometimes be evaluated more efficiently ** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a ** virtual term of that form. ** ** Note that the virtual term must be tagged with TERM_VNULL. This ** TERM_VNULL tag will suppress the not-null check at the beginning ** of the loop. Without the TERM_VNULL flag, the not-null check at ** the start of the loop will prevent any results from being returned. */ if( pExpr->op==TK_NOTNULL && pExpr->pLeft->iColumn>=0 ){ Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; pNewExpr = sqlite3PExpr(pParse, TK_GT, sqlite3ExprDup(db, pLeft, 0), sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0); idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL); if( idxNew ){ pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = 0; pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_GT; pNewTerm->iParent = idxTerm; pTerm = &pWC->a[idxTerm]; pTerm->nChild = 1; pTerm->wtFlags |= TERM_COPIED; pNewTerm->prereqAll = pTerm->prereqAll; } } #endif /* SQLITE_ENABLE_STAT2 */ /* Prevent ON clause terms of a LEFT JOIN from being used to drive ** an index for tables to the left of the join. */ pTerm->prereqRight |= extraRight; } /* |
︙ | ︙ | |||
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 | static int isSortingIndex( Parse *pParse, /* Parsing context */ WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmaps */ Index *pIdx, /* The index we are testing */ int base, /* Cursor number for the table to be sorted */ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ int i, j; /* Loop counters */ int sortOrder = 0; /* XOR of index and ORDER BY sort direction */ int nTerm; /* Number of ORDER BY terms */ struct ExprList_item *pTerm; /* A term of the ORDER BY clause */ sqlite3 *db = pParse->db; | > | 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 | static int isSortingIndex( Parse *pParse, /* Parsing context */ WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmaps */ Index *pIdx, /* The index we are testing */ int base, /* Cursor number for the table to be sorted */ ExprList *pOrderBy, /* The ORDER BY clause */ int nEqCol, /* Number of index columns with == constraints */ int wsFlags, /* Index usages flags */ int *pbRev /* Set to 1 if ORDER BY is DESC */ ){ int i, j; /* Loop counters */ int sortOrder = 0; /* XOR of index and ORDER BY sort direction */ int nTerm; /* Number of ORDER BY terms */ struct ExprList_item *pTerm; /* A term of the ORDER BY clause */ sqlite3 *db = pParse->db; |
︙ | ︙ | |||
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 | *pbRev = sortOrder!=0; if( j>=nTerm ){ /* All terms of the ORDER BY clause are covered by this index so ** this index can be used for sorting. */ return 1; } if( pIdx->onError!=OE_None && i==pIdx->nColumn && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* All terms of this index match some prefix of the ORDER BY clause ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then | > | > > | 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 | *pbRev = sortOrder!=0; if( j>=nTerm ){ /* All terms of the ORDER BY clause are covered by this index so ** this index can be used for sorting. */ return 1; } if( pIdx->onError!=OE_None && i==pIdx->nColumn && (wsFlags & WHERE_COLUMN_NULL)==0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){ /* All terms of this index match some prefix of the ORDER BY clause ** and the index is UNIQUE and no terms on the tail of the ORDER BY ** clause reference other tables in a join. If this is all true then ** the order by clause is superfluous. Not that if the matching ** condition is IS NULL then the result is not necessarily unique ** even on a UNIQUE index, so disallow those cases. */ return 1; } return 0; } /* ** Prepare a crude estimate of the logarithm of the input value. |
︙ | ︙ | |||
1718 1719 1720 1721 1722 1723 1724 | return; } /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ | | | 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 | 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 %.1f to %.1f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->plan.nRow = logN + 1; pCost->plan.wsFlags = WHERE_TEMP_INDEX; pCost->used = pTerm->prereqRight; break; } |
︙ | ︙ | |||
1839 1840 1841 1842 1843 1844 1845 | int iCol = pTerm->u.leftColumn; Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; if( (idxCols & cMask)==0 ){ Expr *pX = pTerm->pExpr; idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); | | | 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 | int iCol = pTerm->u.leftColumn; Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol; if( (idxCols & cMask)==0 ){ Expr *pX = pTerm->pExpr; idxCols |= cMask; pIdx->aiColumn[n] = pTerm->u.leftColumn; pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY"; n++; } } } assert( (u32)n==pLevel->plan.nEq ); /* Add additional columns needed to make the automatic index into |
︙ | ︙ | |||
2197 2198 2199 2200 2201 2202 2203 | 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 | | | | > > | > | > > > > > > | > > > > > > > > > > | 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 | 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. These samples divide the domain of values stored ** the index into (SQLITE_INDEX_SAMPLES+1) regions. ** Region 0 contains all values less than the first sample value. Region ** 1 contains values between the first and second samples. Region 2 contains ** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES ** contains values larger than the last sample. ** ** If the index contains many duplicates of a single value, then it is ** possible that two or more adjacent samples can hold the same value. ** When that is the case, the smallest possible region code is returned ** when roundUp is false and the largest possible region code is returned ** when roundUp is true. ** ** If successful, this function determines which of the regions value ** pVal lies in, sets *piRegion to the region index (a value between 0 ** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK. ** Or, if an OOM occurs while converting text values between encodings, ** SQLITE_NOMEM is returned and *piRegion is undefined. */ #ifdef SQLITE_ENABLE_STAT2 static int whereRangeRegion( Parse *pParse, /* Database connection */ Index *pIdx, /* Index to consider domain of */ sqlite3_value *pVal, /* Value to consider */ int roundUp, /* Return largest valid region if true */ int *piRegion /* OUT: Region of domain in which value lies */ ){ assert( roundUp==0 || roundUp==1 ); if( ALWAYS(pVal) ){ IndexSample *aSample = pIdx->aSample; int i = 0; int eType = sqlite3_value_type(pVal); if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ double r = sqlite3_value_double(pVal); for(i=0; i<SQLITE_INDEX_SAMPLES; i++){ if( aSample[i].eType==SQLITE_NULL ) continue; if( aSample[i].eType>=SQLITE_TEXT ) break; if( roundUp ){ if( aSample[i].u.r>r ) break; }else{ if( aSample[i].u.r>=r ) break; } } }else if( eType==SQLITE_NULL ){ i = 0; if( roundUp ){ while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++; } }else{ sqlite3 *db = pParse->db; CollSeq *pColl; const u8 *z; int n; |
︙ | ︙ | |||
2256 2257 2258 2259 2260 2261 2262 | return SQLITE_NOMEM; } assert( z && pColl && pColl->xCmp ); } n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; i<SQLITE_INDEX_SAMPLES; i++){ | | | | | | 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 | return SQLITE_NOMEM; } assert( z && pColl && pColl->xCmp ); } n = sqlite3ValueBytes(pVal, pColl->enc); for(i=0; i<SQLITE_INDEX_SAMPLES; i++){ int c; int eSampletype = aSample[i].eType; if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue; if( (eSampletype!=eType) ) break; #ifndef SQLITE_OMIT_UTF16 if( pColl->enc!=SQLITE_UTF8 ){ int nSample; char *zSample = sqlite3Utf8to16( db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample ); if( !zSample ){ assert( db->mallocFailed ); return SQLITE_NOMEM; } c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); sqlite3DbFree(db, zSample); }else #endif { c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); } if( c-roundUp>=0 ) break; } } assert( i>=0 && i<=SQLITE_INDEX_SAMPLES ); *piRegion = i; } return SQLITE_OK; |
︙ | ︙ | |||
2360 2361 2362 2363 2364 2365 2366 | ** value of 1 indicates that the proposed range scan is expected to visit ** approximately 1/100th (1%) of the rows selected by the nEq equality ** constraints (if any). A return value of 100 indicates that it is expected ** that the range scan will visit every row (100%) selected by the equality ** constraints. ** ** In the absence of sqlite_stat2 ANALYZE data, each range inequality | | | | > > > > > > | | | | > | | > | < > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | 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 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 | ** value of 1 indicates that the proposed range scan is expected to visit ** approximately 1/100th (1%) of the rows selected by the nEq equality ** constraints (if any). A return value of 100 indicates that it is expected ** that the range scan will visit every row (100%) selected by the equality ** constraints. ** ** In the absence of sqlite_stat2 ANALYZE data, each range inequality ** reduces the search space by 3/4ths. Hence a single constraint (x>?) ** results in a return of 25 and a range constraint (x>? AND x<?) results ** in a return of 6. */ static int whereRangeScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index containing the range-compared column; "x" */ int nEq, /* index into p->aCol[] of the range-compared column */ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ int *piEst /* OUT: Return value */ ){ int rc = SQLITE_OK; #ifdef SQLITE_ENABLE_STAT2 if( nEq==0 && p->aSample ){ sqlite3_value *pLowerVal = 0; sqlite3_value *pUpperVal = 0; int iEst; int iLower = 0; int iUpper = SQLITE_INDEX_SAMPLES; int roundUpUpper = 0; int roundUpLower = 0; u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pLower ){ Expr *pExpr = pLower->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE ); roundUpLower = (pLower->eOperator==WO_GT) ?1:0; } if( rc==SQLITE_OK && pUpper ){ Expr *pExpr = pUpper->pExpr->pRight; rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE ); roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0; } if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); goto range_est_fallback; }else if( pLowerVal==0 ){ rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( pLower ) iLower = iUpper/2; }else if( pUpperVal==0 ){ rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; }else{ rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper); if( rc==SQLITE_OK ){ rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower); } } WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper)); iEst = iUpper - iLower; testcase( iEst==SQLITE_INDEX_SAMPLES ); assert( iEst<=SQLITE_INDEX_SAMPLES ); if( iEst<1 ){ *piEst = 50/SQLITE_INDEX_SAMPLES; }else{ *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES; } sqlite3ValueFree(pLowerVal); sqlite3ValueFree(pUpperVal); return rc; } range_est_fallback: #else UNUSED_PARAMETER(pParse); UNUSED_PARAMETER(p); UNUSED_PARAMETER(nEq); #endif assert( pLower || pUpper ); *piEst = 100; if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4; if( pUpper ) *piEst /= 4; return rc; } #ifdef SQLITE_ENABLE_STAT2 /* ** Estimate the number of rows that will be returned based on ** an equality constraint x=VALUE and where that VALUE occurs in ** the histogram data. This only works when x is the left-most ** column of an index and sqlite_stat2 histogram data is available ** for that index. When pExpr==NULL that means the constraint is ** "x IS NULL" instead of "x=VALUE". ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return ** non-zero. ** ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ double *pnRow /* Write the revised row estimate here */ ){ sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */ int iLower, iUpper; /* Range of histogram regions containing pRhs */ u8 aff; /* Column affinity */ int rc; /* Subfunction return code */ double nRowEst; /* New estimate of the number of rows */ assert( p->aSample!=0 ); aff = p->pTable->aCol[p->aiColumn[0]].affinity; if( pExpr ){ rc = valueFromExpr(pParse, pExpr, aff, &pRhs); if( rc ) goto whereEqualScanEst_cancel; }else{ pRhs = sqlite3ValueNew(pParse->db); } if( pRhs==0 ) return SQLITE_NOTFOUND; rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); if( rc ) goto whereEqualScanEst_cancel; rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper); if( rc ) goto whereEqualScanEst_cancel; WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper)); if( iLower>=iUpper ){ nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2); if( nRowEst<*pnRow ) *pnRow = nRowEst; }else{ nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES; *pnRow = nRowEst; } whereEqualScanEst_cancel: sqlite3ValueFree(pRhs); return rc; } #endif /* defined(SQLITE_ENABLE_STAT2) */ #ifdef SQLITE_ENABLE_STAT2 /* ** Estimate the number of rows that will be returned based on ** an IN constraint where the right-hand side of the IN operator ** is a list of values. Example: ** ** WHERE x IN (1,2,3,4) ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return ** non-zero. ** ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ double *pnRow /* Write the revised row estimate here */ ){ sqlite3_value *pVal = 0; /* One value from list */ int iLower, iUpper; /* Range of histogram regions containing pRhs */ u8 aff; /* Column affinity */ int rc = SQLITE_OK; /* Subfunction return code */ double nRowEst; /* New estimate of the number of rows */ int nSpan = 0; /* Number of histogram regions spanned */ int nSingle = 0; /* Histogram regions hit by a single value */ int nNotFound = 0; /* Count of values that are not constants */ int i; /* Loop counter */ u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */ u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */ assert( p->aSample!=0 ); aff = p->pTable->aCol[p->aiColumn[0]].affinity; memset(aSpan, 0, sizeof(aSpan)); memset(aSingle, 0, sizeof(aSingle)); for(i=0; i<pList->nExpr; i++){ sqlite3ValueFree(pVal); rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal); if( rc ) break; if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){ nNotFound++; continue; } rc = whereRangeRegion(pParse, p, pVal, 0, &iLower); if( rc ) break; rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper); if( rc ) break; if( iLower>=iUpper ){ aSingle[iLower] = 1; }else{ assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES ); while( iLower<iUpper ) aSpan[iLower++] = 1; } } if( rc==SQLITE_OK ){ for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){ if( aSpan[i] ){ nSpan++; }else if( aSingle[i] ){ nSingle++; } } nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES) + nNotFound*p->aiRowEst[1]; if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n", nSpan, nSingle, nNotFound, nRowEst)); } sqlite3ValueFree(pVal); return rc; } #endif /* defined(SQLITE_ENABLE_STAT2) */ /* ** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of ** CPU and disk I/O needed to process the requested result. ** Factors that influence cost include: ** ** * The estimated number of rows that will be retrieved. (The ** fewer the better.) ** ** * Whether or not sorting must occur. ** ** * Whether or not there must be separate lookups in the ** index and in the main table. ** ** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in ** the SQL statement, then this function only considers plans using the ** named index. If no such plan is found, then the returned cost is ** SQLITE_BIG_DBL. If a plan is found that uses the named index, ** then the cost is calculated in the usual way. ** ** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table ** in the SELECT statement, then no indexes are considered. However, the ** selected plan may still take advantage of the built-in rowid primary key ** 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 */ |
︙ | ︙ | |||
2506 2507 2508 2509 2510 2511 2512 | if( pSrc->pIndex ){ /* An INDEXED BY clause specifies a particular index to use */ pIdx = pProbe = pSrc->pIndex; wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ | | | > > | > > > | > > | 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 | if( pSrc->pIndex ){ /* An INDEXED BY clause specifies a particular index to use */ pIdx = pProbe = pSrc->pIndex; wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); eqTermMask = idxEqTermMask; }else{ /* There is no INDEXED BY clause. Create a fake Index object in local ** variable sPk to represent the rowid primary key index. Make this ** fake index the first in a chain of Index objects with all of the real ** indices to follow */ Index *pFirst; /* First of real indices on the table */ memset(&sPk, 0, sizeof(Index)); sPk.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 ){ /* The real indices of the table are only considered if the ** NOT INDEXED qualifier is omitted from the FROM clause */ sPk.pNext = pFirst; } pProbe = &sPk; wsFlagMask = ~( WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE ); eqTermMask = WO_EQ|WO_IN; pIdx = 0; } /* Loop over all indices looking for the best one to use */ for(; pProbe; pIdx=pProbe=pProbe->pNext){ const unsigned int * const aiRowEst = pProbe->aiRowEst; double cost; /* Cost of using pProbe */ double nRow; /* Estimated number of rows in result set */ double log10N; /* base-10 logarithm of nRow (inexact) */ int rev; /* True to scan in reverse order */ int wsFlags = 0; Bitmask used = 0; /* The following variables are populated based on the properties of ** index being evaluated. They are then used to determine the expected ** cost and number of rows returned. ** ** nEq: ** Number of equality terms that can be implemented using the index. ** In other words, the number of initial fields in the index that ** are used in == or IN or NOT NULL constraints of the WHERE clause. ** ** nInMul: ** The "in-multiplier". This is an estimate of how many seek operations ** SQLite must perform on the index in question. For example, if the ** WHERE clause is: ** ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6) |
︙ | ︙ | |||
2568 2569 2570 2571 2572 2573 2574 | ** ** If there exists a WHERE term of the form "x IN (SELECT ...)", then ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used | | > > | | | > | | | | | > > | | | | | | | | > > > > | > | > > > | 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 | ** ** If there exists a WHERE term of the form "x IN (SELECT ...)", then ** the sub-select is assumed to return 25 rows for the purposes of ** determining nInMul. ** ** bInEst: ** Set to true if there was at least one "x IN (SELECT ...)" term used ** in determining the value of nInMul. Note that the RHS of the ** IN operator must be a SELECT, not a value list, for this variable ** to be true. ** ** estBound: ** An estimate on the amount of the table that must be searched. A ** value of 100 means the entire table is searched. Range constraints ** might reduce this to a value less than 100 to indicate that only ** a fraction of the table needs searching. In the absence of ** sqlite_stat2 ANALYZE data, a single inequality reduces the search ** space to 1/4rd its original size. So an x>? constraint reduces ** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6. ** ** bSort: ** Boolean. True if there is an ORDER BY clause that will require an ** external sort (i.e. scanning the index being evaluated will not ** correctly order records). ** ** bLookup: ** Boolean. True if a table lookup is required for each index entry ** visited. In other words, true if this is not a covering index. ** This is always false for the rowid primary key index of a table. ** For other indexes, it is true unless all the columns of the table ** used by the SELECT statement are present in the index (such an ** index is sometimes described as a covering index). ** For example, given the index on (a, b), the second of the following ** two queries requires table b-tree lookups in order to find the value ** of column c, but the first does not because columns a and b are ** both available in the index. ** ** SELECT a, b FROM tbl WHERE a = 1; ** SELECT a, b, c FROM tbl WHERE a = 1; */ int nEq; /* Number of == or IN terms matching index */ int bInEst = 0; /* True if "x IN (SELECT...)" seen */ int nInMul = 1; /* Number of distinct equalities to lookup */ int estBound = 100; /* Estimated reduction in search space */ int nBound = 0; /* Number of range constraints seen */ int bSort = 0; /* True if external sort required */ int bLookup = 0; /* True if not a covering index */ WhereTerm *pTerm; /* A single term of the WHERE clause */ #ifdef SQLITE_ENABLE_STAT2 WhereTerm *pFirstTerm = 0; /* First term matching the index */ #endif /* Determine the values of nEq and nInMul */ for(nEq=0; nEq<pProbe->nColumn; nEq++){ int j = pProbe->aiColumn[nEq]; pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */ nInMul *= 25; bInEst = 1; }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){ /* "x IN (value, value, ...)" */ nInMul *= pExpr->x.pList->nExpr; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } #ifdef SQLITE_ENABLE_STAT2 if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm; #endif used |= pTerm->prereqRight; } /* Determine the value of estBound. */ if( nEq<pProbe->nColumn ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ |
︙ | ︙ | |||
2658 2659 2660 2661 2662 2663 2664 | } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ | | | > | 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 | } /* If there is an ORDER BY clause and the index being considered will ** naturally scan rows in the required order, set the appropriate flags ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ if( (wsFlags & WHERE_COLUMN_IN)==0 && isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev) ){ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; wsFlags |= (rev ? WHERE_REVERSE : 0); }else{ bSort = 1; } } |
︙ | ︙ | |||
2690 2691 2692 2693 2694 2695 2696 | wsFlags |= WHERE_IDX_ONLY; }else{ bLookup = 1; } } /* | | | > | > | < > | > > > > > > | > > > | | > > > > > > > > > > > > > > > | > > > > > > | > > > > > | > > > > > > > > > > > | | > > > > > > | > | > > | > | | | > | 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 | wsFlags |= WHERE_IDX_ONLY; }else{ bLookup = 1; } } /* ** Estimate the number of rows of output. For an "x IN (SELECT...)" ** constraint, do not let the estimate exceed half the rows in the table. */ nRow = (double)(aiRowEst[nEq] * nInMul); if( bInEst && nRow*2>aiRowEst[0] ){ nRow = aiRowEst[0]/2; nInMul = (int)(nRow / aiRowEst[nEq]); } #ifdef SQLITE_ENABLE_STAT2 /* If the constraint is of the form x=VALUE and histogram ** data is available for column x, then it might be possible ** to get a better estimate on the number of rows based on ** VALUE and how common that value is according to the histogram. */ if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){ if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ testcase( pFirstTerm->eOperator==WO_EQ ); testcase( pFirstTerm->eOperator==WO_ISNULL ); whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); } } #endif /* SQLITE_ENABLE_STAT2 */ /* Adjust the number of output rows and downward to reflect rows ** that are excluded by range constraints. */ nRow = (nRow * (double)estBound) / (double)100; if( nRow<1 ) nRow = 1; /* Experiments run on real SQLite databases show that the time needed ** to do a binary search to locate a row in a table or index is roughly ** log10(N) times the time to move from one row to the next row within ** a table or index. The actual times can vary, with the size of ** records being an important factor. Both moves and searches are ** slower with larger records, presumably because fewer records fit ** on one page and hence more pages have to be fetched. ** ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do ** not give us data on the relative sizes of table and index records. ** So this computation assumes table records are about twice as big ** as index records */ if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){ /* The cost of a full table scan is a number of move operations equal ** to the number of rows in the table. ** ** We add an additional 4x penalty to full table scans. This causes ** the cost function to err on the side of choosing an index over ** choosing a full scan. This 4x full-scan penalty is an arguable ** decision and one which we expect to revisit in the future. But ** it seems to be working well enough at the moment. */ cost = aiRowEst[0]*4; }else{ log10N = estLog(aiRowEst[0]); cost = nRow; if( pIdx ){ if( bLookup ){ /* For an index lookup followed by a table lookup: ** nInMul index searches to find the start of each index range ** + nRow steps through the index ** + nRow table searches to lookup the table entry using the rowid */ cost += (nInMul + nRow)*log10N; }else{ /* For a covering index: ** nInMul index searches to find the initial entry ** + nRow steps through the index */ cost += nInMul*log10N; } }else{ /* For a rowid primary key lookup: ** nInMult table searches to find the initial entry for each range ** + nRow steps through the table */ cost += nInMul*log10N; } } /* Add in the estimated cost of sorting the result. Actual experimental ** measurements of sorting performance in SQLite show that sorting time ** adds C*N*log10(N) to the cost, where N is the number of rows to be ** sorted and C is a factor between 1.95 and 4.3. We will split the ** difference and select C of 3.0. */ if( bSort ){ cost += nRow*estLog(nRow)*3; } /**** Cost of using this index has now been computed ****/ /* 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. |
︙ | ︙ | |||
2764 2765 2766 2767 2768 2769 2770 | }else{ /* Assume each additional equality match reduces the result ** set size by a factor of 10 */ nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ | | | > > > > | | | | 3053 3054 3055 3056 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 3092 3093 | }else{ /* Assume each additional equality match reduces the result ** set size by a factor of 10 */ nRow /= 10; } }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){ if( nSkipRange ){ /* Ignore the first nSkipRange range constraints since the index ** has already accounted for these */ nSkipRange--; }else{ /* Assume each additional range constraint reduces the result ** set size by a factor of 3. Indexed range constraints reduce ** the search space by a larger factor: 4. We make indexed range ** more selective intentionally because of the subjective ** observation that indexed range constraints really are more ** selective in practice, on average. */ nRow /= 3; } }else if( pTerm->eOperator!=WO_NOOP ){ /* Any other expression lowers the output row count by half */ nRow /= 2; } } if( nRow<2 ) nRow = 2; } WHERETRACE(( "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n" " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n", pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), nEq, nInMul, estBound, bSort, bLookup, wsFlags, notReady, log10N, 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)) |
︙ | ︙ | |||
3610 3611 3612 3613 3614 3615 3616 | start_constraints = pRangeStart || nEq>0; /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); | > | > | 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 | start_constraints = pRangeStart || nEq>0; /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ Expr *pRight = pRangeStart->pExpr->pRight; sqlite3ExprCode(pParse, pRight, regBase+nEq); if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){ sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); } if( zStartAff ){ if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ zStartAff[nEq] = SQLITE_AFF_NONE; } |
︙ | ︙ | |||
3649 3650 3651 3652 3653 3654 3655 | ** range (if any). */ nConstraint = nEq; if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); | > | > | 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 | ** range (if any). */ nConstraint = nEq; if( pRangeEnd ){ Expr *pRight = pRangeEnd->pExpr->pRight; sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); sqlite3ExprCode(pParse, pRight, regBase+nEq); if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){ sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); } if( zEndAff ){ if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){ /* Since the comparison is to be performed with no conversions ** applied to the operands, set the affinity to apply to pRight to ** SQLITE_AFF_NONE. */ zEndAff[nEq] = SQLITE_AFF_NONE; } |
︙ | ︙ | |||
3707 3708 3709 3710 3711 3712 3713 | sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ } /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ | > | > > > > > | 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 | sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg); sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */ } /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ pLevel->op = OP_Noop; }else if( bRev ){ pLevel->op = OP_Prev; }else{ pLevel->op = OP_Next; } pLevel->p1 = iIdxCur; }else #ifndef SQLITE_OMIT_OR_OPTIMIZATION if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ /* Case 4: Two or more separately indexed terms connected by OR ** |
︙ | ︙ | |||
3753 3754 3755 3756 3757 3758 3759 | ** ** Return 2 # Jump back to the Gosub ** ** B: <after the loop> ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ | < < | 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 | ** ** Return 2 # Jump back to the Gosub ** ** B: <after the loop> ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; pTerm = pLevel->plan.u.pTerm; assert( pTerm!=0 ); assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; pLevel->op = OP_Return; pLevel->p1 = regReturn; /* Set up a new SrcList ni pOrTab containing the table being scanned ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). */ |
︙ | ︙ | |||
3878 3879 3880 3881 3882 3883 3884 | /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. ** ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through ** the use of indices become tests that are evaluated against each row of ** the relevant input tables. */ | < < | 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 | /* Insert code to test every subexpression that can be completely ** computed using the current set of tables. ** ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through ** the use of indices become tests that are evaluated against each row of ** the relevant input tables. */ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; if( (pTerm->prereqAll & notReady)!=0 ){ testcase( pWInfo->untestedTerms==0 && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); pWInfo->untestedTerms = 1; continue; } pE = pTerm->pExpr; assert( pE!=0 ); if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ continue; } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; } /* For a LEFT OUTER JOIN, generate code that will record the fact that ** at least one row of the right table has matched the left table. */ if( pLevel->iLeftJoin ){ |
︙ | ︙ | |||
4204 4205 4206 4207 4208 4209 4210 | ** pWInfo->a[].iIdxCur The VDBE cursor for the index ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term ** ** This loop also figures out the nesting order of tables in the FROM ** clause. */ notReady = ~(Bitmask)0; | < < | 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 | ** pWInfo->a[].iIdxCur The VDBE cursor for the index ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term ** ** This loop also figures out the nesting order of tables in the FROM ** clause. */ notReady = ~(Bitmask)0; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ WhereCost bestPlan; /* Most efficient plan seen so far */ Index *pIdx; /* Index for FROM table at pTabItem */ int j; /* For looping over FROM tables */ int bestJ = -1; /* The value of j */ |
︙ | ︙ | |||
4316 4317 4318 4319 4320 4321 4322 | } /* Conditions under which this table becomes the best so far: ** ** (1) The table must not depend on other tables that have not ** yet run. ** | | | > | 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 | } /* Conditions under which this table becomes the best so far: ** ** (1) The table must not depend on other tables that have not ** yet run. ** ** (2) A full-table-scan plan cannot supercede indexed plan unless ** the full-table-scan is an "optimal" plan as defined above. ** ** (3) All tables have an INDEXED BY clause or this table lacks an ** INDEXED BY clause or this table uses the specific ** index specified by its INDEXED BY clause. This rule ensures ** that a best-so-far is always selected even if an impossible ** combination of INDEXED BY clauses are given. The error ** will be detected and relayed back to the application later. ** The NEVER() comes about because rule (2) above prevents ** An indexable full-table-scan from reaching rule (3). ** ** (4) The plan cost must be lower than prior plans or else the ** cost must be the same and the number of rows must be lower. */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 || (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)) ){ |
︙ | ︙ |
Changes to test/alter.test.
︙ | ︙ | |||
835 836 837 838 839 840 841 842 843 | } {1 {Cannot add a UNIQUE column}} do_test alter-14.2 { catchsql { ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY; } } {1 {Cannot add a PRIMARY KEY column}} finish_test | > > > > > > > > > > > > > > > > > > > | 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 | } {1 {Cannot add a UNIQUE column}} do_test alter-14.2 { catchsql { ALTER TABLE t3651 ADD COLUMN b PRIMARY KEY; } } {1 {Cannot add a PRIMARY KEY column}} #------------------------------------------------------------------------- # Test that it is not possible to use ALTER TABLE on any system table. # set system_table_list {1 sqlite_master} catchsql ANALYZE ifcapable analyze { lappend system_table_list 2 sqlite_stat1 } ifcapable stat2 { lappend system_table_list 3 sqlite_stat2 } foreach {tn tbl} $system_table_list { do_test alter-15.$tn.1 { catchsql "ALTER TABLE $tbl RENAME TO xyz" } [list 1 "table $tbl may not be altered"] do_test alter-15.$tn.2 { catchsql "ALTER TABLE $tbl ADD COLUMN xyz" } [list 1 "table $tbl may not be altered"] } finish_test |
Changes to test/analyze.test.
︙ | ︙ | |||
92 93 94 95 96 97 98 | ANALYZE main.t1; } } {0 {}} do_test analyze-1.11 { execsql { SELECT * FROM sqlite_stat1 } | | | | | | | 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 | ANALYZE main.t1; } } {0 {}} do_test analyze-1.11 { execsql { SELECT * FROM sqlite_stat1 } } {} do_test analyze-1.12 { catchsql { ANALYZE t1; } } {0 {}} do_test analyze-1.13 { execsql { SELECT * FROM sqlite_stat1 } } {} # 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; } } {} do_test analyze-2.2 { execsql { CREATE INDEX t1i2 ON t1(b); ANALYZE t1; SELECT * FROM sqlite_stat1 ORDER BY idx; } } {} do_test analyze-2.3 { execsql { CREATE INDEX t1i3 ON t1(a,b); ANALYZE main; SELECT * FROM sqlite_stat1 ORDER BY idx; } } {} # 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.
︙ | ︙ | |||
150 151 152 153 154 155 156 | 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 } { | | | | | | 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 | 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<?) (~12 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<?) (~12 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<?) (~12 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<?) (~12 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]] |
︙ | ︙ | |||
203 204 205 206 207 208 209 | 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' } { | | | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | 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<?) (~100 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 { |
︙ | ︙ | |||
238 239 240 241 242 243 244 245 246 247 | execsql { INSERT INTO t3 VALUES($str, $str) } } execsql COMMIT execsql ANALYZE } {} do_test analyze2-4.2 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3a' | > | > | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | execsql { INSERT INTO t3 VALUES($str, $str) } } execsql COMMIT execsql ANALYZE } {} do_test analyze2-4.2 { execsql { PRAGMA automatic_index=OFF; SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3a' GROUP BY tbl,idx; PRAGMA automatic_index=ON; } } {t3 t3a {AfA bEj CEj dEj EEj fEj GEj hEj IEj jEj}} do_test analyze2-4.3 { execsql { SELECT tbl,idx,group_concat(sample,' ') FROM sqlite_stat2 WHERE idx = 't3b' |
︙ | ︙ | |||
404 405 406 407 408 409 410 | 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 } | | | | | | | | 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 | 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<?) (~60000 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>?) (~1 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>?) (~1 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<?) (~60000 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<?) (~60000 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>?) (~1 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 { |
︙ | ︙ | |||
497 498 499 500 501 502 503 | } {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 | | | | | | | | 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 | } {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>?) (~1 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>?) (~1 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>?) (~1 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>?) (~1 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>?) (~1 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<?) (~1 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.
︙ | ︙ | |||
244 245 246 247 248 249 250 | 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%' | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 | 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<?) (~30000 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} |
︙ | ︙ |
Added test/analyze5.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 | # 2011 January 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for SQLite library. The focus of the tests # in this file is the use of the sqlite_stat2 histogram data on tables # with many repeated values and only a few distinct values. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } set testprefix analyze5 proc eqp {sql {db db}} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db } unset -nocomplain i t u v w x y z do_test analyze5-1.0 { db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)} for {set i 0} {$i < 1000} {incr i} { set y [expr {$i>=25 && $i<=50}] set z [expr {($i>=400) + ($i>=700) + ($i>=875)}] set x $z set w $z set t [expr {$z+0.5}] switch $z { 0 {set u "alpha"; unset x} 1 {set u "bravo"} 2 {set u "charlie"} 3 {set u "delta"; unset w} } if {$i%2} {set v $u} {set v [string toupper $u]} db eval {INSERT INTO t1 VALUES($t,$u,$v,$w,$x,$y,$z)} } db eval { CREATE INDEX t1t ON t1(t); -- 0.5, 1.5, 2.5, and 3.5 CREATE INDEX t1u ON t1(u); -- text CREATE INDEX t1v ON t1(v); -- mixed case text CREATE INDEX t1w ON t1(w); -- integers 0, 1, 2 and a few NULLs CREATE INDEX t1x ON t1(x); -- integers 1, 2, 3 and many NULLs CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3 ANALYZE; SELECT sample FROM sqlite_stat2 WHERE idx='t1u' ORDER BY sampleno; } } {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} do_test analyze5-1.1 { string tolower \ [db eval {SELECT sample from sqlite_stat2 WHERE idx='t1v' ORDER BY sampleno}] } {alpha alpha alpha alpha bravo bravo bravo charlie charlie delta} do_test analyze5-1.2 { db eval {SELECT sample from sqlite_stat2 WHERE idx='t1w' ORDER BY sampleno} } {{} 0 0 0 0 1 1 1 2 2} do_test analyze5-1.3 { db eval {SELECT sample from sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno} } {{} {} {} {} 1 1 1 2 2 3} do_test analyze5-1.4 { db eval {SELECT sample from sqlite_stat2 WHERE idx='t1y' ORDER BY sampleno} } {0 0 0 0 0 0 0 0 0 0} do_test analyze5-1.5 { db eval {SELECT sample from sqlite_stat2 WHERE idx='t1z' ORDER BY sampleno} } {0 0 0 0 1 1 1 2 2 3} do_test analyze5-1.6 { db eval {SELECT sample from sqlite_stat2 WHERE idx='t1t' ORDER BY sampleno} } {0.5 0.5 0.5 0.5 1.5 1.5 1.5 2.5 2.5 3.5} # Verify that range queries generate the correct row count estimates # foreach {testid where index rows} { 1 {z>=0 AND z<=0} t1z 400 2 {z>=1 AND z<=1} t1z 300 3 {z>=2 AND z<=2} t1z 200 4 {z>=3 AND z<=3} t1z 100 5 {z>=4 AND z<=4} t1z 50 6 {z>=-1 AND z<=-1} t1z 50 7 {z>1 AND z<3} t1z 200 8 {z>0 AND z<100} t1z 600 9 {z>=1 AND z<100} t1z 600 10 {z>1 AND z<100} t1z 300 11 {z>=2 AND z<100} t1z 300 12 {z>2 AND z<100} t1z 100 13 {z>=3 AND z<100} t1z 100 14 {z>3 AND z<100} t1z 50 15 {z>=4 AND z<100} t1z 50 16 {z>=-100 AND z<=-1} t1z 50 17 {z>=-100 AND z<=0} t1z 400 18 {z>=-100 AND z<0} t1z 50 19 {z>=-100 AND z<=1} t1z 700 20 {z>=-100 AND z<2} t1z 700 21 {z>=-100 AND z<=2} t1z 900 22 {z>=-100 AND z<3} t1z 900 31 {z>=0.0 AND z<=0.0} t1z 400 32 {z>=1.0 AND z<=1.0} t1z 300 33 {z>=2.0 AND z<=2.0} t1z 200 34 {z>=3.0 AND z<=3.0} t1z 100 35 {z>=4.0 AND z<=4.0} t1z 50 36 {z>=-1.0 AND z<=-1.0} t1z 50 37 {z>1.5 AND z<3.0} t1z 200 38 {z>0.5 AND z<100} t1z 600 39 {z>=1.0 AND z<100} t1z 600 40 {z>1.5 AND z<100} t1z 300 41 {z>=2.0 AND z<100} t1z 300 42 {z>2.1 AND z<100} t1z 100 43 {z>=3.0 AND z<100} t1z 100 44 {z>3.2 AND z<100} t1z 50 45 {z>=4.0 AND z<100} t1z 50 46 {z>=-100 AND z<=-1.0} t1z 50 47 {z>=-100 AND z<=0.0} t1z 400 48 {z>=-100 AND z<0.0} t1z 50 49 {z>=-100 AND z<=1.0} t1z 700 50 {z>=-100 AND z<2.0} t1z 700 51 {z>=-100 AND z<=2.0} t1z 900 52 {z>=-100 AND z<3.0} t1z 900 101 {z=-1} t1z 50 102 {z=0} t1z 400 103 {z=1} t1z 300 104 {z=2} t1z 200 105 {z=3} t1z 100 106 {z=4} t1z 50 107 {z=-10.0} t1z 50 108 {z=0.0} t1z 400 109 {z=1.0} t1z 300 110 {z=2.0} t1z 200 111 {z=3.0} t1z 100 112 {z=4.0} t1z 50 113 {z=1.5} t1z 50 114 {z=2.5} t1z 50 201 {z IN (-1)} t1z 50 202 {z IN (0)} t1z 400 203 {z IN (1)} t1z 300 204 {z IN (2)} t1z 200 205 {z IN (3)} t1z 100 206 {z IN (4)} t1z 50 207 {z IN (0.5)} t1z 50 208 {z IN (0,1)} t1z 700 209 {z IN (0,1,2)} t1z 900 210 {z IN (0,1,2,3)} {} 100 211 {z IN (0,1,2,3,4,5)} {} 100 212 {z IN (1,2)} t1z 500 213 {z IN (2,3)} t1z 300 214 {z=3 OR z=2} t1z 300 215 {z IN (-1,3)} t1z 150 216 {z=-1 OR z=3} t1z 150 300 {y=0} {} 100 301 {y=1} t1y 50 302 {y=0.1} t1y 50 400 {x IS NULL} t1x 400 } { # Verify that the expected index is used with the expected row count do_test analyze5-1.${testid}a { set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] set idx {} regexp {INDEX (t1.) } $x all idx regexp {~([0-9]+) rows} $x all nrow list $idx $nrow } [list $index $rows] # Verify that the same result is achieved regardless of whether or not # the index is used do_test analyze5-1.${testid}b { set w2 [string map {y +y z +z} $where] set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ ORDER BY +rowid"] set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] if {$a1==$a2} { set res ok } else { set res "a1=\[$a1\] a2=\[$a2\]" } set res } {ok} } # Increase the number of NULLs in column x # db eval { UPDATE t1 SET x=NULL; UPDATE t1 SET x=rowid WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5); ANALYZE; } # Verify that range queries generate the correct row count estimates # foreach {testid where index rows} { 500 {x IS NULL AND u='charlie'} t1u 20 501 {x=1 AND u='charlie'} t1x 5 502 {x IS NULL} {} 100 503 {x=1} t1x 50 504 {x IS NOT NULL} t1x 25 } { # Verify that the expected index is used with the expected row count do_test analyze5-1.${testid}a { set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] set idx {} regexp {INDEX (t1.) } $x all idx regexp {~([0-9]+) rows} $x all nrow list $idx $nrow } [list $index $rows] # Verify that the same result is achieved regardless of whether or not # the index is used do_test analyze5-1.${testid}b { set w2 [string map {y +y z +z} $where] set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ ORDER BY +rowid"] set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] if {$a1==$a2} { set res ok } else { set res "a1=\[$a1\] a2=\[$a2\]" } set res } {ok} } finish_test |
Added test/analyze6.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 | # 2011 March 3 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests for SQLite library. The focus of the tests # in this file a corner-case query planner optimization involving the # join order of two tables of different sizes. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !stat2 { finish_test return } set testprefix analyze6 proc eqp {sql {db db}} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db } do_test analyze6-1.0 { db eval { CREATE TABLE cat(x INT); CREATE UNIQUE INDEX catx ON cat(x); /* Give cat 16 unique integers */ INSERT INTO cat VALUES(1); INSERT INTO cat VALUES(2); INSERT INTO cat SELECT x+2 FROM cat; INSERT INTO cat SELECT x+4 FROM cat; INSERT INTO cat SELECT x+8 FROM cat; CREATE TABLE ev(y INT); CREATE INDEX evy ON ev(y); /* ev will hold 32 copies of 16 integers found in cat */ INSERT INTO ev SELECT x FROM cat; INSERT INTO ev SELECT x FROM cat; INSERT INTO ev SELECT y FROM ev; INSERT INTO ev SELECT y FROM ev; INSERT INTO ev SELECT y FROM ev; INSERT INTO ev SELECT y FROM ev; ANALYZE; SELECT count(*) FROM cat; SELECT count(*) FROM ev; } } {16 512} # The lowest cost plan is to scan CAT and for each integer there, do a single # lookup of the first corresponding entry in EV then read off the equal values # in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would # have used EV for the outer loop instead of CAT - which was about 3x slower.) # do_test analyze6-1.1 { eqp {SELECT count(*) FROM ev, cat WHERE x=y} } {0 0 1 {SCAN TABLE cat (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} # The same plan is chosen regardless of the order of the tables in the # FROM clause. # do_test analyze6-1.2 { eqp {SELECT count(*) FROM cat, ev WHERE x=y} } {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} # Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30 # If ANALYZE is run on an empty table, make sure indices are used # on the table. # do_test analyze6-2.1 { execsql { CREATE TABLE t201(x INTEGER PRIMARY KEY, y UNIQUE, z); CREATE INDEX t201z ON t201(z); ANALYZE; } eqp {SELECT * FROM t201 WHERE z=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} do_test analyze6-2.2 { eqp {SELECT * FROM t201 WHERE y=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} do_test analyze6-2.3 { eqp {SELECT * FROM t201 WHERE x=5} } {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze6-2.4 { execsql { INSERT INTO t201 VALUES(1,2,3); ANALYZE t201; } eqp {SELECT * FROM t201 WHERE z=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} do_test analyze6-2.5 { eqp {SELECT * FROM t201 WHERE y=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} do_test analyze6-2.6 { eqp {SELECT * FROM t201 WHERE x=5} } {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} do_test analyze6-2.7 { execsql { INSERT INTO t201 VALUES(4,5,7); INSERT INTO t201 SELECT x+100, y+100, z+100 FROM t201; INSERT INTO t201 SELECT x+200, y+200, z+200 FROM t201; INSERT INTO t201 SELECT x+400, y+400, z+400 FROM t201; ANALYZE t201; } eqp {SELECT * FROM t201 WHERE z=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}} do_test analyze6-2.8 { eqp {SELECT * FROM t201 WHERE y=5} } {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}} do_test analyze6-2.9 { eqp {SELECT * FROM t201 WHERE x=5} } {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}} finish_test |
Added test/analyze7.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 | # 2011 April 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. # This file implements tests for the ANALYZE command when an idnex # name is given as the argument. # set testdir [file dirname $argv0] source $testdir/tester.tcl # There is nothing to test if ANALYZE is disable for this build. # ifcapable {!analyze||!vtab} { finish_test return } # Generate some test data # do_test analyze7-1.0 { register_wholenumber_module db execsql { CREATE TABLE t1(a,b,c,d); CREATE INDEX t1a ON t1(a); CREATE INDEX t1b ON t1(b); CREATE INDEX t1cd ON t1(c,d); CREATE VIRTUAL TABLE nums USING wholenumber; INSERT INTO t1 SELECT value, value, value/100, value FROM nums WHERE value BETWEEN 1 AND 256; EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123; } } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}} do_test analyze7-1.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} do_test analyze7-1.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} # Run an analyze on one of the three indices. Verify that this # effects the row-count estimate on the one query that uses that # one index. # do_test analyze7-2.0 { execsql {ANALYZE t1a;} db cache flush execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} do_test analyze7-2.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} do_test analyze7-2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}} # Verify that since the query planner now things that t1a is more # selective than t1b, it prefers to use t1a. # do_test analyze7-2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} # Run an analysis on another of the three indices. Verify that this # new analysis works and does not disrupt the previous analysis. # do_test analyze7-3.0 { execsql {ANALYZE t1cd;} db cache flush; execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} do_test analyze7-3.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}} do_test analyze7-3.2.1 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} ifcapable stat2 { # If ENABLE_STAT2 is defined, SQLite comes up with a different estimated # row count for (c=2) than it does for (c=?). do_test analyze7-3.2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~102 rows)}} } else { # If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the # same as that for (c=?). do_test analyze7-3.2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}} } do_test analyze7-3.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} do_test analyze7-3.4 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}} do_test analyze7-3.5 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}} do_test analyze7-3.6 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123} } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}} finish_test |
Changes to test/attach.test.
︙ | ︙ | |||
148 149 150 151 152 153 154 | } {1 {database MAIN is already in use}} do_test attach-1.18 { catchsql { ATTACH 'test.db' as db10; ATTACH 'test.db' as db11; } } {0 {}} | > | | | | | | | | > > | | | | | | | | > | 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 | } {1 {database MAIN is already in use}} do_test attach-1.18 { catchsql { ATTACH 'test.db' as db10; ATTACH 'test.db' as db11; } } {0 {}} if {$SQLITE_MAX_ATTACHED==10} { do_test attach-1.19 { catchsql { ATTACH 'test.db' as db12; } } {1 {too many attached databases - max 10}} do_test attach-1.19.1 { db errorcode } {1} } do_test attach-1.20.1 { execsql { DETACH db5; } } {} ifcapable schema_pragmas { do_test attach-1.20.2 { db_list db } {0 main 2 db2 3 db3 4 db4 5 db6 6 db7 7 db8 8 db9 9 db10 10 db11} } ;# ifcapable schema_pragmas integrity_check attach-1.20.3 ifcapable tempdb { execsql {select * from sqlite_temp_master} } do_test attach-1.21 { catchsql { ATTACH 'test.db' as db12; } } {0 {}} if {$SQLITE_MAX_ATTACHED==10} { do_test attach-1.22 { catchsql { ATTACH 'test.db' as db13; } } {1 {too many attached databases - max 10}} do_test attach-1.22.1 { db errorcode } {1} } do_test attach-1.23 { catchsql { DETACH "db14"; } } {1 {no such database: db14}} do_test attach-1.24 { catchsql { |
︙ | ︙ | |||
832 833 834 835 836 837 838 839 | } } {noname inmem} do_test attach-10.2 { lrange [execsql { PRAGMA database_list; }] 9 end } {4 noname {} 5 inmem {}} finish_test | > | 836 837 838 839 840 841 842 843 844 | } } {noname inmem} do_test attach-10.2 { lrange [execsql { PRAGMA database_list; }] 9 end } {4 noname {} 5 inmem {}} finish_test |
Changes to test/auth.test.
︙ | ︙ | |||
1624 1625 1626 1627 1628 1629 1630 | } return SQLITE_OK } catchsql { ATTACH DATABASE ':memory:' AS test1 } } {0 {}} | | > > > > > > > > > > > | 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 | } return SQLITE_OK } catchsql { ATTACH DATABASE ':memory:' AS test1 } } {0 {}} do_test auth-1.252a { set ::authargs } {:memory: {} {} {}} do_test auth-1.252b { db eval {DETACH test1} set ::attachfilename :memory: db eval {ATTACH $::attachfilename AS test1} set ::authargs } {{} {} {} {}} do_test auth-1.252c { db eval {DETACH test1} db eval {ATTACH ':mem' || 'ory:' AS test1} set ::authargs } {{} {} {} {}} do_test auth-1.253 { catchsql {DETACH DATABASE test1} proc auth {code arg1 arg2 arg3 arg4} { if {$code=="SQLITE_ATTACH"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] return SQLITE_DENY } |
︙ | ︙ |
Added test/badutf2.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 | # 2011 March 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. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file checks to make sure SQLite is able to gracEFully # handle malformed UTF-8. # set testdir [file dirname $argv0] source $testdir/tester.tcl proc utf8_to_ustr2 {s} { set r "" foreach i [split $s ""] { scan $i %c c append r [format \\u%04.4X $c] } set r } proc utf8_to_hstr {in} { regsub -all -- {(..)} $in {%[format "%s" \1]} out subst $out } proc utf8_to_xstr {in} { regsub -all -- {(..)} $in {\\\\x[format "%s" \1]} out subst $out } proc utf8_to_ustr {in} { regsub -all -- {(..)} $in {\\\\u[format "%04.4X" 0x\1]} out subst $out } do_test badutf2-1.0 { db close forcedelete test.db sqlite3 db test.db db eval "PRAGMA encoding = 'UTF-8'" } {} do_test badutf2-4.0 { set S [sqlite3_prepare_v2 db "SELECT ?" -1 dummy] sqlite3_expired $S } {0} foreach { i len uval xstr ustr u2u } { 1 1 00 \x00 {} {} 2 1 01 \x01 "\\u0001" 01 3 1 3F \x3F "\\u003F" 3F 4 1 7F \x7F "\\u007F" 7F 5 1 80 \x80 "\\u0080" C280 6 1 C3BF \xFF "\\u00FF" C3BF 7 3 EFBFBD \xEF\xBF\xBD "\\uFFFD" {} } { set hstr [ utf8_to_hstr $uval ] ifcapable bloblit { if {$hstr != "%00"} { do_test badutf2-2.1.$i { set sql "SELECT '$hstr'=CAST(x'$uval' AS text) AS x;" set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } {1} do_test badutf2-2.2.$i { set sql "SELECT CAST('$hstr' AS blob)=x'$uval' AS x;" set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } {1} } do_test badutf2-2.3.$i { set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;" set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } $uval do_test badutf2-2.4.$i { set sql "SELECT hex(CAST(x'$uval' AS text)) AS x;" set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } $uval } if {$hstr != "%00"} { do_test badutf2-3.1.$i { set sql "SELECT hex('$hstr') AS x;" set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } $uval } do_test badutf2-4.1.$i { sqlite3_reset $S sqlite3_bind_text $S 1 $xstr $len sqlite3_step $S utf8_to_ustr2 [ sqlite3_column_text $S 0 ] } $ustr ifcapable debug { do_test badutf2-5.1.$i { utf8_to_utf8 $uval } $u2u } } do_test badutf2-4.2 { sqlite3_finalize $S } {SQLITE_OK} finish_test |
Changes to test/capi3.test.
︙ | ︙ | |||
647 648 649 650 651 652 653 | } {0} do_test capi3-6.1 { db cache flush sqlite3_close $DB } {SQLITE_BUSY} do_test capi3-6.2 { sqlite3_step $STMT | | | | | 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 | } {0} do_test capi3-6.1 { db cache flush sqlite3_close $DB } {SQLITE_BUSY} do_test capi3-6.2 { sqlite3_step $STMT } {SQLITE_ERROR} #check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1} do_test capi3-6.3 { sqlite3_finalize $STMT } {SQLITE_SCHEMA} do_test capi3-6.4-misuse { db cache flush sqlite3_close $DB } {SQLITE_OK} db close # This procedure sets the value of the file-format in file 'test.db' |
︙ | ︙ |
Changes to test/capi3e.test.
︙ | ︙ | |||
73 74 75 76 77 78 79 | sqlite3_close $db2 } {SQLITE_OK} do_test capi3e-1.3.$i { file isfile $name } {1} } | > | | | | | | | | | | | | | > | 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 | sqlite3_close $db2 } {SQLITE_OK} do_test capi3e-1.3.$i { file isfile $name } {1} } ifcapable {utf16} { 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 |
︙ | ︙ |
Changes to test/corrupt3.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 | # segfault if it sees a corrupt database file. # # $Id: corrupt3.test,v 1.2 2007/04/06 21:42:22 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl | > > > | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # segfault if it sees a corrupt database file. # # $Id: corrupt3.test,v 1.2 2007/04/06 21:42:22 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.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 # We must have the page_size pragma for these tests to work. # ifcapable !pager_pragmas { finish_test return } |
︙ | ︙ |
Changes to test/corruptA.test.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt database headers. # # $Id: corruptA.test,v 1.1 2008/07/11 16:39:23 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a database to work with. # do_test corruptA-1.1 { execsql { CREATE TABLE t1(x); | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | # segfault if it sees a corrupt database file. It specifically focuses # on corrupt database headers. # # $Id: corruptA.test,v 1.1 2008/07/11 16:39:23 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.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 # Create a database to work with. # do_test corruptA-1.1 { execsql { CREATE TABLE t1(x); |
︙ | ︙ |
Changes to test/corruptD.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # #*********************************************************************** # # $Id: corruptD.test,v 1.2 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl #-------------------------------------------------------------------------- # OVERVIEW # # This test file attempts to verify that SQLite does not read past the # end of any in-memory buffers as a result of corrupted database page # images. Usually this happens because a field within a database page | > > > > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # #*********************************************************************** # # $Id: corruptD.test,v 1.2 2009/06/05 17:09:12 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.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 #-------------------------------------------------------------------------- # OVERVIEW # # This test file attempts to verify that SQLite does not read past the # end of any in-memory buffers as a result of corrupted database page # images. Usually this happens because a field within a database page |
︙ | ︙ |
Changes to test/e_createtable.test.
︙ | ︙ | |||
1375 1376 1377 1378 1379 1380 1381 | 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" | | | 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 | 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>?) (~2 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 |
︙ | ︙ |
Changes to test/eqp.test.
︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #------------------------------------------------------------------------- # # 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); | > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #------------------------------------------------------------------------- # # eqp-1.*: Assorted tests. # eqp-2.*: Tests for single select statements. # eqp-3.*: Select statements that execute sub-selects. # eqp-4.*: Compound select statements. # ... # eqp-7.*: "SELECT count(*) FROM tbl" statements (VDBE code OP_Count). # proc det {args} { uplevel do_eqp_test $args } do_execsql_test 1.1 { CREATE TABLE t1(a, b); CREATE INDEX i1 ON t1(a); |
︙ | ︙ | |||
388 389 390 391 392 393 394 | # 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" { | | | | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | # 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>?) (~2 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>?) (~2 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) |
︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 512 513 514 515 516 | } 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"] | > | 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 | } sqlite3 db test.db explain_query_plan db {%SQL%} db close exit } # Do a "Print Explain Query Plan" test. 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"] |
︙ | ︙ | |||
525 526 527 528 529 530 531 | } [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) }] | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } [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) }] #------------------------------------------------------------------------- # The following tests - eqp-7.* - test that queries that use the OP_Count # optimization return something sensible with EQP. # drop_all_tables do_execsql_test 7.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); CREATE INDEX i1 ON t2(a); } det 7.1 "SELECT count(*) FROM t1" { 0 0 0 {SCAN TABLE t1 (~1000000 rows)} } det 7.2 "SELECT count(*) FROM t2" { 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~1000000 rows)} } do_execsql_test 7.3 { INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t2 VALUES(1, 2); INSERT INTO t2 VALUES(3, 4); INSERT INTO t2 VALUES(5, 6); ANALYZE; } db close sqlite3 db test.db det 7.4 "SELECT count(*) FROM t1" { 0 0 0 {SCAN TABLE t1 (~2 rows)} } det 7.5 "SELECT count(*) FROM t2" { 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~3 rows)} } finish_test |
Changes to test/exclusive2.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #*********************************************************************** # This file implements regression tests for SQLite library. # # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!pager_pragmas} { finish_test return } # This module does not work right if the cache spills at unexpected | > > > > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #*********************************************************************** # This file implements regression tests for SQLite library. # # $Id: exclusive2.test,v 1.10 2008/11/27 02:22:11 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.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 ifcapable {!pager_pragmas} { finish_test return } # This module does not work right if the cache spills at unexpected |
︙ | ︙ |
Changes to test/expr.test.
︙ | ︙ | |||
78 79 80 81 82 83 84 | test_expr expr-1.41 {i1=1, i2=2} {-(i2+i1)} {-3} test_expr expr-1.42 {i1=1, i2=2} {i1|i2} {3} test_expr expr-1.42b {i1=1, i2=2} {4|2} {6} test_expr expr-1.43 {i1=1, i2=2} {i1&i2} {0} test_expr expr-1.43b {i1=1, i2=2} {4&5} {4} test_expr expr-1.44 {i1=1} {~i1} {-2} test_expr expr-1.44b {i1=NULL} {~i1} {{}} | | > > > > > > | > > > > | 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 | test_expr expr-1.41 {i1=1, i2=2} {-(i2+i1)} {-3} test_expr expr-1.42 {i1=1, i2=2} {i1|i2} {3} test_expr expr-1.42b {i1=1, i2=2} {4|2} {6} test_expr expr-1.43 {i1=1, i2=2} {i1&i2} {0} test_expr expr-1.43b {i1=1, i2=2} {4&5} {4} test_expr expr-1.44 {i1=1} {~i1} {-2} test_expr expr-1.44b {i1=NULL} {~i1} {{}} test_expr expr-1.45a {i1=1, i2=3} {i1<<i2} {8} test_expr expr-1.45b {i1=1, i2=-3} {i1>>i2} {8} test_expr expr-1.45c {i1=1, i2=0} {i1<<i2} {1} test_expr expr-1.45d {i1=1, i2=62} {i1<<i2} {4611686018427387904} test_expr expr-1.45e {i1=1, i2=63} {i1<<i2} {-9223372036854775808} test_expr expr-1.45f {i1=1, i2=64} {i1<<i2} {0} test_expr expr-1.45g {i1=32, i2=-9223372036854775808} {i1>>i2} {0} test_expr expr-1.46a {i1=32, i2=3} {i1>>i2} {4} test_expr expr-1.46b {i1=32, i2=6} {i1>>i2} {0} test_expr expr-1.46c {i1=-32, i2=3} {i1>>i2} {-4} test_expr expr-1.46d {i1=-32, i2=100} {i1>>i2} {-1} test_expr expr-1.46e {i1=32, i2=-3} {i1>>i2} {256} test_expr expr-1.47 {i1=9999999999, i2=8888888888} {i1<i2} 0 test_expr expr-1.48 {i1=9999999999, i2=8888888888} {i1=i2} 0 test_expr expr-1.49 {i1=9999999999, i2=8888888888} {i1>i2} 1 test_expr expr-1.50 {i1=99999999999, i2=99999999998} {i1<i2} 0 test_expr expr-1.51 {i1=99999999999, i2=99999999998} {i1=i2} 0 test_expr expr-1.52 {i1=99999999999, i2=99999999998} {i1>i2} 1 test_expr expr-1.53 {i1=099999999999, i2=99999999999} {i1<i2} 0 |
︙ | ︙ | |||
150 151 152 153 154 155 156 | ifcapable floatingpoint { test_expr expr-1.103 {i1=0} {(-2147483648.0 % -1)} 0.0 test_expr expr-1.104 {i1=0} {(-9223372036854775808.0 % -1)} 0.0 test_expr expr-1.105 {i1=0} {(-9223372036854775808.0 / -1)>1} 1 } if {[working_64bit_int]} { | | | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | ifcapable floatingpoint { test_expr expr-1.103 {i1=0} {(-2147483648.0 % -1)} 0.0 test_expr expr-1.104 {i1=0} {(-9223372036854775808.0 % -1)} 0.0 test_expr expr-1.105 {i1=0} {(-9223372036854775808.0 / -1)>1} 1 } if {[working_64bit_int]} { test_expr expr-1.106 {i1=0} {-9223372036854775808/-1} 9.22337203685478e+18 } test_expr expr-1.107 {i1=0} {-9223372036854775808%-1} 0 test_expr expr-1.108 {i1=0} {1%0} {{}} test_expr expr-1.109 {i1=0} {1/0} {{}} if {[working_64bit_int]} { test_expr expr-1.110 {i1=0} {-9223372036854775807/-1} 9223372036854775807 } |
︙ | ︙ | |||
185 186 187 188 189 190 191 192 193 194 195 196 197 198 | {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.124 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.125 {i1=6, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no ifcapable floatingpoint { test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11 test_expr expr-2.3 {r1=1.23, r2=2.34} {r1*r2} 2.8782 } set tcl_precision 15 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.124 {i1=NULL, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no test_expr expr-1.125 {i1=6, i2=NULL} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} yes test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no ifcapable floatingpoint {if {[working_64bit_int]} { test_expr expr-1.200\ {i1=9223372036854775806, i2=1} {i1+i2} 9223372036854775807 test_expr expr-1.201\ {i1=9223372036854775806, i2=2} {i1+i2} 9.22337203685478e+18 test_expr expr-1.202\ {i1=9223372036854775806, i2=100000} {i1+i2} 9.22337203685488e+18 test_expr expr-1.203\ {i1=9223372036854775807, i2=0} {i1+i2} 9223372036854775807 test_expr expr-1.204\ {i1=9223372036854775807, i2=1} {i1+i2} 9.22337203685478e+18 test_expr expr-1.205\ {i2=9223372036854775806, i1=1} {i1+i2} 9223372036854775807 test_expr expr-1.206\ {i2=9223372036854775806, i1=2} {i1+i2} 9.22337203685478e+18 test_expr expr-1.207\ {i2=9223372036854775806, i1=100000} {i1+i2} 9.22337203685488e+18 test_expr expr-1.208\ {i2=9223372036854775807, i1=0} {i1+i2} 9223372036854775807 test_expr expr-1.209\ {i2=9223372036854775807, i1=1} {i1+i2} 9.22337203685478e+18 test_expr expr-1.210\ {i1=-9223372036854775807, i2=-1} {i1+i2} -9223372036854775808 test_expr expr-1.211\ {i1=-9223372036854775807, i2=-2} {i1+i2} -9.22337203685478e+18 test_expr expr-1.212\ {i1=-9223372036854775807, i2=-100000} {i1+i2} -9.22337203685488e+18 test_expr expr-1.213\ {i1=-9223372036854775808, i2=0} {i1+i2} -9223372036854775808 test_expr expr-1.214\ {i1=-9223372036854775808, i2=-1} {i1+i2} -9.22337203685478e+18 test_expr expr-1.215\ {i2=-9223372036854775807, i1=-1} {i1+i2} -9223372036854775808 test_expr expr-1.216\ {i2=-9223372036854775807, i1=-2} {i1+i2} -9.22337203685478e+18 test_expr expr-1.217\ {i2=-9223372036854775807, i1=-100000} {i1+i2} -9.22337203685488e+18 test_expr expr-1.218\ {i2=-9223372036854775808, i1=0} {i1+i2} -9223372036854775808 test_expr expr-1.219\ {i2=-9223372036854775808, i1=-1} {i1+i2} -9.22337203685478e+18 test_expr expr-1.220\ {i1=9223372036854775806, i2=-1} {i1-i2} 9223372036854775807 test_expr expr-1.221\ {i1=9223372036854775806, i2=-2} {i1-i2} 9.22337203685478e+18 test_expr expr-1.222\ {i1=9223372036854775806, i2=-100000} {i1-i2} 9.22337203685488e+18 test_expr expr-1.223\ {i1=9223372036854775807, i2=0} {i1-i2} 9223372036854775807 test_expr expr-1.224\ {i1=9223372036854775807, i2=-1} {i1-i2} 9.22337203685478e+18 test_expr expr-1.225\ {i2=-9223372036854775806, i1=1} {i1-i2} 9223372036854775807 test_expr expr-1.226\ {i2=-9223372036854775806, i1=2} {i1-i2} 9.22337203685478e+18 test_expr expr-1.227\ {i2=-9223372036854775806, i1=100000} {i1-i2} 9.22337203685488e+18 test_expr expr-1.228\ {i2=-9223372036854775807, i1=0} {i1-i2} 9223372036854775807 test_expr expr-1.229\ {i2=-9223372036854775807, i1=1} {i1-i2} 9.22337203685478e+18 test_expr expr-1.230\ {i1=-9223372036854775807, i2=1} {i1-i2} -9223372036854775808 test_expr expr-1.231\ {i1=-9223372036854775807, i2=2} {i1-i2} -9.22337203685478e+18 test_expr expr-1.232\ {i1=-9223372036854775807, i2=100000} {i1-i2} -9.22337203685488e+18 test_expr expr-1.233\ {i1=-9223372036854775808, i2=0} {i1-i2} -9223372036854775808 test_expr expr-1.234\ {i1=-9223372036854775808, i2=1} {i1-i2} -9.22337203685478e+18 test_expr expr-1.235\ {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 test_expr expr-1.236\ {i2=9223372036854775807, i1=-2} {i1-i2} -9.22337203685478e+18 test_expr expr-1.237\ {i2=9223372036854775807, i1=-100000} {i1-i2} -9.22337203685488e+18 test_expr expr-1.238\ {i2=9223372036854775807, i1=0} {i1-i2} -9223372036854775807 test_expr expr-1.239\ {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 test_expr expr-1.250\ {i1=4294967296, i2=2147483648} {i1*i2} 9.22337203685478e+18 test_expr expr-1.251\ {i1=4294967296, i2=2147483647} {i1*i2} 9223372032559808512 test_expr expr-1.252\ {i1=-4294967296, i2=2147483648} {i1*i2} -9223372036854775808 test_expr expr-1.253\ {i1=-4294967296, i2=2147483647} {i1*i2} -9223372032559808512 test_expr expr-1.254\ {i1=4294967296, i2=-2147483648} {i1*i2} -9223372036854775808 test_expr expr-1.255\ {i1=4294967296, i2=-2147483647} {i1*i2} -9223372032559808512 test_expr expr-1.256\ {i1=-4294967296, i2=-2147483648} {i1*i2} 9.22337203685478e+18 test_expr expr-1.257\ {i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512 }} ifcapable floatingpoint { test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11 test_expr expr-2.3 {r1=1.23, r2=2.34} {r1*r2} 2.8782 } set tcl_precision 15 |
︙ | ︙ |
Changes to test/filefmt.test.
︙ | ︙ | |||
188 189 190 191 192 193 194 195 196 | } {ok} integrity_check filefmt-2.2.5 do_execsql_test filefmt-2.2.6 { COMMIT } {} db close sqlite3 db test.db integrity_check filefmt-2.2.7 finish_test | > > > > > > > > > > > > > > > > > > > > | 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 | } {ok} integrity_check filefmt-2.2.5 do_execsql_test filefmt-2.2.6 { COMMIT } {} db close sqlite3 db test.db integrity_check filefmt-2.2.7 #-------------------------------------------------------------------------- # Check that ticket 89b8c9ac54 is fixed. Before the fix, the SELECT # statement would return SQLITE_CORRUPT. The database file was not actually # corrupted, but SQLite was reporting that it was. # db close forcedelete test.db sqlite3 db test.db do_execsql_test filefmt-3.1 { PRAGMA auto_vacuum = 1; CREATE TABLE t1(a, b); } {} do_test filefmt-3.2 { sql36231 { DROP TABLE t1 } } {} do_execsql_test filefmt-3.3 { SELECT * FROM sqlite_master; PRAGMA integrity_check; } {ok} finish_test |
Added test/fkey4.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 | # 2011 Feb 04 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file test deferred foreign key constraint processing to make # sure that when a statement not within BEGIN...END fails a constraint, # that statement doesn't hold the transaction open thus allowing # a subsequent statement to fail a deferred constraint with impunity. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {!foreignkey||!trigger} { finish_test return } # Create a table and some data to work with. # do_test fkey4-1.1 { execsql { PRAGMA foreign_keys = ON; CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE t2(c REFERENCES t1 DEFERRABLE INITIALLY DEFERRED, d); INSERT INTO t1 VALUES(1,2); INSERT INTO t2 VALUES(1,3); } } {} do_test fkey4-1.2 { set ::DB [sqlite3_connection_pointer db] set ::SQL {INSERT INTO t2 VALUES(2,4)} set ::STMT1 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT1 } {SQLITE_CONSTRAINT} do_test fkey4-1.3 { set ::STMT2 [sqlite3_prepare_v2 $::DB $::SQL -1 TAIL] sqlite3_step $::STMT2 } {SQLITE_CONSTRAINT} do_test fkey4-1.4 { db eval {SELECT * FROM t2} } {1 3} sqlite3_finalize $::STMT1 sqlite3_finalize $::STMT2 finish_test |
Added test/fts3aux1.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 | # 2011 January 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 script is testing the FTS3 module. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !fts3 { finish_test ; return } set ::testprefix fts3aux1 do_execsql_test 1.1 { CREATE VIRTUAL TABLE t1 USING fts4; INSERT INTO t1 VALUES('one two three four'); INSERT INTO t1 VALUES('three four five six'); INSERT INTO t1 VALUES('one three five seven'); CREATE VIRTUAL TABLE terms USING fts4aux(t1); SELECT term, documents, occurrences FROM terms WHERE col = '*'; } { five 2 2 four 2 2 one 2 2 seven 1 1 six 1 1 three 3 3 two 1 1 } do_execsql_test 1.2 { INSERT INTO t1 VALUES('one one one three three three'); SELECT term, documents, occurrences FROM terms WHERE col = '*'; } { five 2 2 four 2 2 one 3 5 seven 1 1 six 1 1 three 4 6 two 1 1 } do_execsql_test 1.3 { DELETE FROM t1; SELECT term, documents, occurrences FROM terms WHERE col = '*'; } {} do_execsql_test 1.4 { INSERT INTO t1 VALUES('a b a b a b a'); INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; SELECT term, documents, occurrences FROM terms WHERE col = '*'; } {a 256 1024 b 256 768} #------------------------------------------------------------------------- # The following tests verify that the fts4aux module uses the full-text # index to reduce the number of rows scanned in the following circumstances: # # * when there is equality comparison against the term column using the # BINARY collating sequence. # # * when there is a range constraint on the term column using the BINARY # collating sequence. # # And also uses the full-text index to optimize ORDER BY clauses of the # form "ORDER BY term ASC" or equivalent. # # Test organization is: # # fts3aux1-2.1.*: equality constraints. # fts3aux1-2.2.*: range constraints. # fts3aux1-2.3.*: ORDER BY optimization. # do_execsql_test 2.0 { DROP TABLE t1; DROP TABLE terms; CREATE VIRTUAL TABLE x1 USING fts4(x); INSERT INTO x1(x1) VALUES('nodesize=24'); CREATE VIRTUAL TABLE terms USING fts4aux(x1); CREATE VIEW terms_v AS SELECT term, documents, occurrences FROM terms WHERE col = '*'; INSERT INTO x1 VALUES('braes brag bragged bragger bragging'); INSERT INTO x1 VALUES('brags braid braided braiding braids'); INSERT INTO x1 VALUES('brain brainchild brained braining brains'); INSERT INTO x1 VALUES('brainstem brainstems brainstorm brainstorms'); } proc rec {varname x} { global $varname incr $varname return 1 } db func rec rec # Use EQP to show that the WHERE expression "term='braid'" uses a different # index number (1) than "+term='braid'" (0). # do_execsql_test 2.1.1.1 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} } do_execsql_test 2.1.1.2 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid' } {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}} # Now show that using "term='braid'" means the virtual table returns # only 1 row to SQLite, but "+term='braid'" means all 19 are returned. # do_test 2.1.2.1 { set cnt 0 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='braid' } set cnt } {2} do_test 2.1.2.2 { set cnt 0 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='braid' } set cnt } {38} # Similar to the test immediately above, but using a term ("breakfast") that # is not featured in the dataset. # do_test 2.1.3.1 { set cnt 0 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND term='breakfast' } set cnt } {0} do_test 2.1.3.2 { set cnt 0 execsql { SELECT * FROM terms_v WHERE rec('cnt', term) AND +term='breakfast' } set cnt } {38} do_execsql_test 2.1.4.1 { SELECT * FROM terms_v WHERE term='braid' } {braid 1 1} do_execsql_test 2.1.4.2 { SELECT * FROM terms_v WHERE +term='braid'} {braid 1 1} do_execsql_test 2.1.4.3 { SELECT * FROM terms_v WHERE term='breakfast' } {} do_execsql_test 2.1.4.4 { SELECT * FROM terms_v WHERE +term='breakfast' } {} do_execsql_test 2.1.4.5 { SELECT * FROM terms_v WHERE term='cba' } {} do_execsql_test 2.1.4.6 { SELECT * FROM terms_v WHERE +term='cba' } {} do_execsql_test 2.1.4.7 { SELECT * FROM terms_v WHERE term='abc' } {} do_execsql_test 2.1.4.8 { SELECT * FROM terms_v WHERE +term='abc' } {} # Special case: term=NULL # do_execsql_test 2.1.5 { SELECT * FROM terms WHERE term=NULL } {} do_execsql_test 2.2.1.1 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} } do_execsql_test 2.2.1.2 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } do_execsql_test 2.2.1.3 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} } do_execsql_test 2.2.1.4 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } do_execsql_test 2.2.1.5 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} } do_execsql_test 2.2.1.6 { EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} } do_test 2.2.2.1 { set cnt 0 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term>'brain' } set cnt } {18} do_test 2.2.2.2 { set cnt 0 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term>'brain' } set cnt } {38} do_execsql_test 2.2.2.3 { SELECT term, documents, occurrences FROM terms_v WHERE term>'brain' } { brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.4 { SELECT term, documents, occurrences FROM terms_v WHERE +term>'brain' } { brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.5 { SELECT term, documents, occurrences FROM terms_v WHERE term>='brain' } { brain 1 1 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.6 { SELECT term, documents, occurrences FROM terms_v WHERE +term>='brain' } { brain 1 1 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.7 { SELECT term, documents, occurrences FROM terms_v WHERE term>='abc' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.8 { SELECT term, documents, occurrences FROM terms_v WHERE +term>='abc' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 brainchild 1 1 brained 1 1 braining 1 1 brains 1 1 brainstem 1 1 brainstems 1 1 brainstorm 1 1 brainstorms 1 1 } do_execsql_test 2.2.2.9 { SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' } {brainstorms 1 1} do_execsql_test 2.2.2.10 { SELECT term, documents, occurrences FROM terms_v WHERE term>='brainstorms' } {brainstorms 1 1} do_execsql_test 2.2.2.11 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} do_execsql_test 2.2.2.12 { SELECT * FROM terms_v WHERE term>'brainstorms' } {} do_execsql_test 2.2.2.13 { SELECT * FROM terms_v WHERE term>'cba' } {} do_execsql_test 2.2.2.14 { SELECT * FROM terms_v WHERE term>'cba' } {} do_test 2.2.3.1 { set cnt 0 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND term<'brain' } set cnt } {22} do_test 2.2.3.2 { set cnt 0 execsql { SELECT * FROM terms WHERE rec('cnt', term) AND +term<'brain' } set cnt } {38} do_execsql_test 2.2.3.3 { SELECT term, documents, occurrences FROM terms_v WHERE term<'brain' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 } do_execsql_test 2.2.3.4 { SELECT term, documents, occurrences FROM terms_v WHERE +term<'brain' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 } do_execsql_test 2.2.3.5 { SELECT term, documents, occurrences FROM terms_v WHERE term<='brain' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 } do_execsql_test 2.2.3.6 { SELECT term, documents, occurrences FROM terms_v WHERE +term<='brain' } { braes 1 1 brag 1 1 bragged 1 1 bragger 1 1 bragging 1 1 brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 } do_test 2.2.4.1 { set cnt 0 execsql { SELECT term, documents, occurrences FROM terms WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' } set cnt } {12} do_test 2.2.4.2 { set cnt 0 execsql { SELECT term, documents, occurrences FROM terms WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' } set cnt } {38} do_execsql_test 2.2.4.3 { SELECT term, documents, occurrences FROM terms_v WHERE rec('cnt', term) AND term BETWEEN 'brags' AND 'brain' } { brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 } do_execsql_test 2.2.4.4 { SELECT term, documents, occurrences FROM terms_v WHERE rec('cnt', term) AND +term BETWEEN 'brags' AND 'brain' } { brags 1 1 braid 1 1 braided 1 1 braiding 1 1 braids 1 1 brain 1 1 } do_execsql_test 2.2.4.5 { SELECT term, documents, occurrences FROM terms_v WHERE rec('cnt', term) AND term > 'brags' AND term < 'brain' } { braid 1 1 braided 1 1 braiding 1 1 braids 1 1 } do_execsql_test 2.2.4.6 { SELECT term, documents, occurrences FROM terms_v WHERE rec('cnt', term) AND +term > 'brags' AND +term < 'brain' } { braid 1 1 braided 1 1 braiding 1 1 braids 1 1 } # Check that "ORDER BY term ASC" and equivalents are sorted by the # virtual table implementation. Any other ORDER BY clause requires # SQLite to sort results using a temporary b-tree. # foreach {tn sort orderby} { 1 0 "ORDER BY term ASC" 2 0 "ORDER BY term" 3 1 "ORDER BY term DESC" 4 1 "ORDER BY documents ASC" 5 1 "ORDER BY documents" 6 1 "ORDER BY documents DESC" 7 1 "ORDER BY occurrences ASC" 8 1 "ORDER BY occurrences" 9 1 "ORDER BY occurrences DESC" } { set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}] if {$sort} { lappend res 0 0 0 {USE TEMP B-TREE FOR ORDER BY} } set sql "SELECT * FROM terms $orderby" do_execsql_test 2.3.1.$tn "EXPLAIN QUERY PLAN $sql" $res } #------------------------------------------------------------------------- # The next set of tests, fts3aux1-3.*, test error conditions in the # fts4aux module. Except, fault injection testing (OOM, IO error etc.) is # done in fts3fault2.test # do_execsql_test 3.1.1 { CREATE VIRTUAL TABLE t2 USING fts4; } do_catchsql_test 3.1.2 { CREATE VIRTUAL TABLE terms2 USING fts4aux; } {1 {wrong number of arguments to fts4aux constructor}} do_catchsql_test 3.1.3 { CREATE VIRTUAL TABLE terms2 USING fts4aux(t2, t2); } {1 {wrong number of arguments to fts4aux constructor}} do_execsql_test 3.2.1 { CREATE VIRTUAL TABLE terms3 USING fts4aux(does_not_exist) } do_catchsql_test 3.2.2 { SELECT * FROM terms3 } {1 {SQL logic error or missing database}} do_catchsql_test 3.2.3 { SELECT * FROM terms3 WHERE term = 'abc' } {1 {SQL logic error or missing database}} do_catchsql_test 3.3.1 { INSERT INTO terms VALUES(1,2,3); } {1 {table terms may not be modified}} do_catchsql_test 3.3.2 { DELETE FROM terms } {1 {table terms may not be modified}} do_catchsql_test 3.3.3 { UPDATE terms set documents = documents+1; } {1 {table terms may not be modified}} #------------------------------------------------------------------------- # The following tests - fts4aux-4.* - test that joins work with fts4aux # tables. And that fts4aux provides reasonably sane cost information via # xBestIndex to the query planner. # db close forcedelete test.db sqlite3 db test.db do_execsql_test 4.1 { CREATE VIRTUAL TABLE x1 USING fts4(x); CREATE VIRTUAL TABLE terms USING fts4aux(x1); CREATE TABLE x2(y); CREATE TABLE x3(y); CREATE INDEX i1 ON x3(y); INSERT INTO x1 VALUES('a b c d e'); INSERT INTO x1 VALUES('f g h i j'); INSERT INTO x1 VALUES('k k l l a'); INSERT INTO x2 SELECT term FROM terms WHERE col = '*'; INSERT INTO x3 SELECT term FROM terms WHERE col = '*'; } proc do_plansql_test {tn sql r} { uplevel do_execsql_test $tn [list "EXPLAIN QUERY PLAN $sql ; $sql"] [list $r] } do_plansql_test 4.2 { SELECT y FROM x2, terms WHERE y = term AND col = '*' } { 0 0 0 {SCAN TABLE x2 (~1000000 rows)} 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} a b c d e f g h i j k l } do_plansql_test 4.3 { SELECT y FROM terms, x2 WHERE y = term AND col = '*' } { 0 0 1 {SCAN TABLE x2 (~1000000 rows)} 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} a b c d e f g h i j k l } do_plansql_test 4.4 { SELECT y FROM x3, terms WHERE y = term AND col = '*' } { 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} a b c d e f g h i j k l } do_plansql_test 4.5 { SELECT y FROM terms, x3 WHERE y = term AND occurrences>1 AND col = '*' } { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)} a k l } #------------------------------------------------------------------------- # The following tests check that fts4aux can handle an fts table with an # odd name (one that requires quoting for use in SQL statements). And that # the argument to the fts4aux constructor is properly dequoted before use. # # do_execsql_test 5.1 { CREATE VIRTUAL TABLE "abc '!' def" USING fts4(x, y); INSERT INTO "abc '!' def" VALUES('XX', 'YY'); CREATE VIRTUAL TABLE terms3 USING fts4aux("abc '!' def"); SELECT * FROM terms3; } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} do_execsql_test 5.2 { CREATE VIRTUAL TABLE "%%^^%%" USING fts4aux('abc ''!'' def'); SELECT * FROM "%%^^%%"; } {xx * 1 1 xx 0 1 1 yy * 1 1 yy 1 1 1} finish_test |
Added test/fts3comp1.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 | # 2011 January 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 script is testing the FTS3 module. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !fts3 { finish_test ; return } set ::testprefix fts3comp1 # Create a pretend compression system. # # Each time the [zip] function is called, an entry is added to the ::strings # array mapping from an integer key to the string argument to zip. The key # is returned. Later on, when the key is passed to [unzip], the original # string is retrieved from the ::strings array and returned. # set next_x 0 proc zip {x} { incr ::next_x set ::strings($::next_x) $x return $::next_x } proc unzip {x} { return $::strings($x) } foreach {tn zip unzip} { 1 zip unzip 2 {z.i.p!!} {un "zip"} } { set next_x 0 catch {db close} forcedelete test.db sqlite3 db test.db db func $zip zip db func $unzip unzip # Create a table that uses zip/unzip. Check that content inserted into # the table can be read back (using a full-scan query). Check that the # underlying %_content table contains the compressed (integer) values. # do_execsql_test 1.$tn.0 " CREATE VIRTUAL TABLE t1 USING fts4( a, b, compress='$zip', uncompress='$unzip' ); " do_execsql_test 1.$tn.1 { INSERT INTO t1 VALUES('one two three', 'two four six'); SELECT a, b FROM t1; } {{one two three} {two four six}} do_execsql_test 1.$tn.2 { SELECT c0a, c1b FROM t1_content; } {1 2} # Insert another row and check that it can be read back. Also that the # %_content table still contains all compressed content. This time, try # full-text index and by-docid queries too. # do_execsql_test 1.$tn.3 { INSERT INTO t1 VALUES('three six nine', 'four eight twelve'); SELECT a, b FROM t1; } {{one two three} {two four six} {three six nine} {four eight twelve}} do_execsql_test 1.$tn.4 { SELECT c0a, c1b FROM t1_content; } {1 2 3 4} do_execsql_test 1.$tn.5 { SELECT a, b FROM t1 WHERE docid = 2 } {{three six nine} {four eight twelve}} do_execsql_test 1.$tn.6 { SELECT a, b FROM t1 WHERE t1 MATCH 'two' } {{one two three} {two four six}} # Delete a row and check that the full-text index is correctly updated. # Inspect the full-text index using an fts4aux table. # do_execsql_test 1.$tn.7 { CREATE VIRTUAL TABLE terms USING fts4aux(t1); SELECT term, documents, occurrences FROM terms WHERE col = '*'; } { eight 1 1 four 2 2 nine 1 1 one 1 1 six 2 2 three 2 2 twelve 1 1 two 1 2 } do_execsql_test 1.$tn.8 { DELETE FROM t1 WHERE docid = 1; SELECT term, documents, occurrences FROM terms WHERE col = '*'; } { eight 1 1 four 1 1 nine 1 1 six 1 1 three 1 1 twelve 1 1 } do_execsql_test 1.$tn.9 { SELECT c0a, c1b FROM t1_content } {3 4} } # Test that is an error to specify just one of compress and uncompress. # do_catchsql_test 2.1 { CREATE VIRTUAL TABLE t2 USING fts4(x, compress=zip) } {1 {missing uncompress parameter in fts4 constructor}} do_catchsql_test 2.2 { CREATE VIRTUAL TABLE t2 USING fts4(x, uncompress=unzip) } {1 {missing compress parameter in fts4 constructor}} finish_test |
Changes to test/fts3corrupt.test.
︙ | ︙ | |||
126 127 128 129 130 131 132 133 134 135 | "\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 | > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | "\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}} # Test a special kind of corruption, where the %_stat table contains # an invalid entry. At one point this could lead to a division-by-zero # error in fts4. # do_execsql_test 5.0 { DROP TABLE t1; CREATE VIRTUAL TABLE t1 USING fts4; } do_test 5.1 { db func nn nn execsql BEGIN execsql { INSERT INTO t1 VALUES('one') } execsql { INSERT INTO t1 VALUES('two') } execsql { INSERT INTO t1 VALUES('three') } execsql { INSERT INTO t1 VALUES('four') } execsql COMMIT } {} do_catchsql_test 5.2 { UPDATE t1_stat SET value = X'0000'; SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*'; } {1 {database disk image is malformed}} do_catchsql_test 5.3 { UPDATE t1_stat SET value = NULL; SELECT matchinfo(t1, 'nxa') FROM t1 WHERE t1 MATCH 't*'; } {1 {database disk image is malformed}} finish_test |
Changes to test/fts3defer2.test.
︙ | ︙ | |||
76 77 78 79 80 81 82 | # 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 { | | | | > > > > > > > > > | 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 | # 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 z'); 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.1 { SELECT mit(matchinfo(t2, 'pcxnal')) FROM t2 WHERE t2 MATCH 'a b'; } [list \ [list 2 1 1 54 54 1 3 3 54 372 8] \ [list 2 1 1 54 54 1 3 3 54 372 7] \ ] set sqlite_fts3_enable_parentheses 1 do_execsql_test 2.2.$tn.2 { SELECT mit(matchinfo(t2, 'x')) FROM t2 WHERE t2 MATCH 'g OR (g z)'; } [list \ [list 1 2 2 1 2 2 1 54 54] \ [list 1 2 2 1 2 2 0 54 54] \ ] set sqlite_fts3_enable_parentheses 0 } 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'); |
︙ | ︙ |
Added test/fts3fault2.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 | # 2011 February 3 # # 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 fts3fault2 # If SQLITE_ENABLE_FTS3 is not defined, omit this file. ifcapable !fts3 { finish_test ; return } do_test 1.0 { execsql { CREATE VIRTUAL TABLE t1 USING fts4(x); INSERT INTO t1 VALUES('a b c'); INSERT INTO t1 VALUES('c d e'); CREATE VIRTUAL TABLE terms USING fts4aux(t1); } faultsim_save_and_close } {} do_faultsim_test 1.1 -prep { faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql "CREATE VIRTUAL TABLE terms2 USING fts4aux(t1)" } -test { faultsim_test_result {0 {}} } do_faultsim_test 1.2 -prep { faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql "SELECT * FROM terms" } -test { faultsim_test_result {0 {a * 1 1 a 0 1 1 b * 1 1 b 0 1 1 c * 2 2 c 0 2 2 d * 1 1 d 0 1 1 e * 1 1 e 0 1 1}} } do_faultsim_test 1.3 -prep { faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql "SELECT * FROM terms WHERE term>'a' AND TERM < 'd'" } -test { faultsim_test_result {0 {b * 1 1 b 0 1 1 c * 2 2 c 0 2 2}} } do_faultsim_test 1.4 -prep { faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql "SELECT * FROM terms WHERE term='c'" } -test { faultsim_test_result {0 {c * 2 2 c 0 2 2}} } do_test 2.0 { faultsim_delete_and_reopen execsql { CREATE VIRTUAL TABLE tx USING fts4(a, b); INSERT INTO tx VALUES('a b c', 'x y z'); CREATE VIRTUAL TABLE terms2 USING fts4aux(tx); } faultsim_save_and_close } {} do_faultsim_test 2.1 -prep { faultsim_restore_and_reopen db eval {SELECT * FROM sqlite_master} } -body { execsql "SELECT * FROM terms2" } -test { faultsim_test_result {0 {a * 1 1 a 0 1 1 b * 1 1 b 0 1 1 c * 1 1 c 0 1 1 x * 1 1 x 1 1 1 y * 1 1 y 1 1 1 z * 1 1 z 1 1 1}} } finish_test |
Added test/fuzzer1.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 | # 2011 March 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 regression tests for TCL interface to the # SQLite library. # # The focus of the tests is the word-fuzzer virtual table. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !vtab { finish_test return } register_fuzzer_module db do_test fuzzer1-1.0 { catchsql {CREATE VIRTUAL TABLE fault1 USING fuzzer;} } {1 {fuzzer virtual tables must be TEMP}} do_test fuzzer1-1.1 { db eval {CREATE VIRTUAL TABLE temp.f1 USING fuzzer;} } {} do_test fuzzer1-1.2 { db eval { INSERT INTO f1(cfrom, cto, cost) VALUES('e','a',1); INSERT INTO f1(cfrom, cto, cost) VALUES('a','e',10); INSERT INTO f1(cfrom, cto, cost) VALUES('e','o',100); } } {} do_test fuzzer1-1.3 { db eval { SELECT word, distance FROM f1 WHERE word MATCH 'abcde' } } {abcde 0 abcda 1 ebcde 10 ebcda 11 abcdo 100 ebcdo 110 obcde 110 obcda 111 obcdo 210} do_test fuzzer1-2.0 { execsql { CREATE VIRTUAL TABLE temp.f2 USING fuzzer; -- costs based on English letter frequencies INSERT INTO f2(cFrom,cTo,cost) VALUES('a','e',24); INSERT INTO f2(cFrom,cTo,cost) VALUES('a','o',47); INSERT INTO f2(cFrom,cTo,cost) VALUES('a','u',50); INSERT INTO f2(cFrom,cTo,cost) VALUES('e','a',23); INSERT INTO f2(cFrom,cTo,cost) VALUES('e','i',33); INSERT INTO f2(cFrom,cTo,cost) VALUES('e','o',37); INSERT INTO f2(cFrom,cTo,cost) VALUES('i','e',33); INSERT INTO f2(cFrom,cTo,cost) VALUES('i','y',33); INSERT INTO f2(cFrom,cTo,cost) VALUES('o','a',41); INSERT INTO f2(cFrom,cTo,cost) VALUES('o','e',46); INSERT INTO f2(cFrom,cTo,cost) VALUES('o','u',57); INSERT INTO f2(cFrom,cTo,cost) VALUES('u','o',58); INSERT INTO f2(cFrom,cTo,cost) VALUES('y','i',33); INSERT INTO f2(cFrom,cTo,cost) VALUES('t','th',70); INSERT INTO f2(cFrom,cTo,cost) VALUES('th','t',66); INSERT INTO f2(cFrom,cTo,cost) VALUES('a','',84); INSERT INTO f2(cFrom,cTo,cost) VALUES('','b',106); INSERT INTO f2(cFrom,cTo,cost) VALUES('b','',106); INSERT INTO f2(cFrom,cTo,cost) VALUES('','c',94); INSERT INTO f2(cFrom,cTo,cost) VALUES('c','',94); INSERT INTO f2(cFrom,cTo,cost) VALUES('','d',89); INSERT INTO f2(cFrom,cTo,cost) VALUES('d','',89); INSERT INTO f2(cFrom,cTo,cost) VALUES('','e',83); INSERT INTO f2(cFrom,cTo,cost) VALUES('e','',83); INSERT INTO f2(cFrom,cTo,cost) VALUES('','f',97); INSERT INTO f2(cFrom,cTo,cost) VALUES('f','',97); INSERT INTO f2(cFrom,cTo,cost) VALUES('','g',99); INSERT INTO f2(cFrom,cTo,cost) VALUES('g','',99); INSERT INTO f2(cFrom,cTo,cost) VALUES('','h',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('h','',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('','i',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('i','',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('','j',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('j','',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('','k',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('k','',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('','l',89); INSERT INTO f2(cFrom,cTo,cost) VALUES('l','',89); INSERT INTO f2(cFrom,cTo,cost) VALUES('','m',96); INSERT INTO f2(cFrom,cTo,cost) VALUES('m','',96); INSERT INTO f2(cFrom,cTo,cost) VALUES('','n',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('n','',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('','o',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('o','',85); INSERT INTO f2(cFrom,cTo,cost) VALUES('','p',100); INSERT INTO f2(cFrom,cTo,cost) VALUES('p','',100); INSERT INTO f2(cFrom,cTo,cost) VALUES('','q',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('q','',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('','r',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('r','',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('','s',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('s','',86); INSERT INTO f2(cFrom,cTo,cost) VALUES('','t',84); INSERT INTO f2(cFrom,cTo,cost) VALUES('t','',84); INSERT INTO f2(cFrom,cTo,cost) VALUES('','u',94); INSERT INTO f2(cFrom,cTo,cost) VALUES('u','',94); INSERT INTO f2(cFrom,cTo,cost) VALUES('','v',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('v','',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('','w',96); INSERT INTO f2(cFrom,cTo,cost) VALUES('w','',96); INSERT INTO f2(cFrom,cTo,cost) VALUES('','x',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('x','',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('','y',100); INSERT INTO f2(cFrom,cTo,cost) VALUES('y','',100); INSERT INTO f2(cFrom,cTo,cost) VALUES('','z',120); INSERT INTO f2(cFrom,cTo,cost) VALUES('z','',120); -- Street names for the 28269 ZIPCODE. -- CREATE TEMP TABLE streetname(n TEXT UNIQUE); INSERT INTO streetname VALUES('abbotsinch'); INSERT INTO streetname VALUES('abbottsgate'); INSERT INTO streetname VALUES('abbywood'); INSERT INTO streetname VALUES('abner'); INSERT INTO streetname VALUES('acacia ridge'); INSERT INTO streetname VALUES('acorn creek'); INSERT INTO streetname VALUES('acorn forest'); INSERT INTO streetname VALUES('adel'); INSERT INTO streetname VALUES('ainslie'); INSERT INTO streetname VALUES('airways'); INSERT INTO streetname VALUES('alabaster'); INSERT INTO streetname VALUES('alba'); INSERT INTO streetname VALUES('albertine'); INSERT INTO streetname VALUES('alden glen'); INSERT INTO streetname VALUES('alderson'); INSERT INTO streetname VALUES('allen'); INSERT INTO streetname VALUES('allen a brown'); INSERT INTO streetname VALUES('allness glen'); INSERT INTO streetname VALUES('aloysia'); INSERT INTO streetname VALUES('alpine'); INSERT INTO streetname VALUES('alwyn'); INSERT INTO streetname VALUES('amaranthus'); INSERT INTO streetname VALUES('amber glen'); INSERT INTO streetname VALUES('amber leigh way'); INSERT INTO streetname VALUES('amber meadows'); INSERT INTO streetname VALUES('amberway'); INSERT INTO streetname VALUES('ame'); INSERT INTO streetname VALUES('amesbury hill'); INSERT INTO streetname VALUES('anderson'); INSERT INTO streetname VALUES('andrew thomas'); INSERT INTO streetname VALUES('anduin falls'); INSERT INTO streetname VALUES('ankeny'); INSERT INTO streetname VALUES('annandale'); INSERT INTO streetname VALUES('annbick'); INSERT INTO streetname VALUES('antelope'); INSERT INTO streetname VALUES('anzack'); INSERT INTO streetname VALUES('apple glen'); INSERT INTO streetname VALUES('applevalley'); INSERT INTO streetname VALUES('appley mead'); INSERT INTO streetname VALUES('aragorn'); INSERT INTO streetname VALUES('arbor creek'); INSERT INTO streetname VALUES('arbor day'); INSERT INTO streetname VALUES('arbor meadows'); INSERT INTO streetname VALUES('arbor spring'); INSERT INTO streetname VALUES('arborview'); INSERT INTO streetname VALUES('arklow'); INSERT INTO streetname VALUES('armitage'); INSERT INTO streetname VALUES('arvin'); INSERT INTO streetname VALUES('ash cove'); INSERT INTO streetname VALUES('ashford leigh'); INSERT INTO streetname VALUES('ashmont'); INSERT INTO streetname VALUES('atlas'); INSERT INTO streetname VALUES('atwater'); INSERT INTO streetname VALUES('auburn hill'); INSERT INTO streetname VALUES('aulton link'); INSERT INTO streetname VALUES('austin dekota'); INSERT INTO streetname VALUES('austin knoll'); INSERT INTO streetname VALUES('auten'); INSERT INTO streetname VALUES('autumn harvest'); INSERT INTO streetname VALUES('autumn oak'); INSERT INTO streetname VALUES('autumn ridge'); INSERT INTO streetname VALUES('avalon forest'); INSERT INTO streetname VALUES('avalon loop'); INSERT INTO streetname VALUES('avon farm'); INSERT INTO streetname VALUES('avonhurst'); INSERT INTO streetname VALUES('avonlea'); INSERT INTO streetname VALUES('aynrand'); INSERT INTO streetname VALUES('azure valley'); INSERT INTO streetname VALUES('baberton'); INSERT INTO streetname VALUES('baffin'); INSERT INTO streetname VALUES('baggins'); INSERT INTO streetname VALUES('balata'); INSERT INTO streetname VALUES('ballantray'); INSERT INTO streetname VALUES('ballston'); INSERT INTO streetname VALUES('balsam tree'); INSERT INTO streetname VALUES('bambi'); INSERT INTO streetname VALUES('banwell'); INSERT INTO streetname VALUES('barbee'); INSERT INTO streetname VALUES('barefoot forest'); INSERT INTO streetname VALUES('barnview'); INSERT INTO streetname VALUES('baroda'); INSERT INTO streetname VALUES('barson'); INSERT INTO streetname VALUES('baskerville'); INSERT INTO streetname VALUES('battle creek'); INSERT INTO streetname VALUES('baucom'); INSERT INTO streetname VALUES('bay pines'); INSERT INTO streetname VALUES('beaker'); INSERT INTO streetname VALUES('beard'); INSERT INTO streetname VALUES('beardsley'); INSERT INTO streetname VALUES('bearoak'); INSERT INTO streetname VALUES('beauvista'); INSERT INTO streetname VALUES('beaver creek'); INSERT INTO streetname VALUES('beaver hollow'); INSERT INTO streetname VALUES('bedlington'); INSERT INTO streetname VALUES('beech cove'); INSERT INTO streetname VALUES('beech crest'); INSERT INTO streetname VALUES('beith'); INSERT INTO streetname VALUES('bell glen'); INSERT INTO streetname VALUES('bellmore'); INSERT INTO streetname VALUES('bells mill'); INSERT INTO streetname VALUES('bellville'); INSERT INTO streetname VALUES('belmar place'); INSERT INTO streetname VALUES('bembridge'); INSERT INTO streetname VALUES('bennett neely'); INSERT INTO streetname VALUES('bentgrass run'); INSERT INTO streetname VALUES('benthaven'); INSERT INTO streetname VALUES('bernardy'); INSERT INTO streetname VALUES('bernbrook shadow'); INSERT INTO streetname VALUES('berrybrook'); INSERT INTO streetname VALUES('berrybush'); INSERT INTO streetname VALUES('berwick'); INSERT INTO streetname VALUES('betterton'); INSERT INTO streetname VALUES('bickham'); INSERT INTO streetname VALUES('billingham'); INSERT INTO streetname VALUES('birchcroft'); INSERT INTO streetname VALUES('birchstone'); INSERT INTO streetname VALUES('birdwell'); INSERT INTO streetname VALUES('bisaner'); INSERT INTO streetname VALUES('bitterbush'); INSERT INTO streetname VALUES('bitterroot'); INSERT INTO streetname VALUES('black fox'); INSERT INTO streetname VALUES('black maple'); INSERT INTO streetname VALUES('black trail'); INSERT INTO streetname VALUES('blackbird'); INSERT INTO streetname VALUES('blake a dare'); INSERT INTO streetname VALUES('blasdell'); INSERT INTO streetname VALUES('blue aster'); INSERT INTO streetname VALUES('blue finch'); INSERT INTO streetname VALUES('blue lilac'); INSERT INTO streetname VALUES('blue sky'); INSERT INTO streetname VALUES('blue tick'); INSERT INTO streetname VALUES('bob beatty'); INSERT INTO streetname VALUES('bobcat'); INSERT INTO streetname VALUES('bolton'); INSERT INTO streetname VALUES('boomerang'); INSERT INTO streetname VALUES('boulder'); INSERT INTO streetname VALUES('boxer'); INSERT INTO streetname VALUES('boxmeer'); INSERT INTO streetname VALUES('brachnell view'); INSERT INTO streetname VALUES('bradford lake'); INSERT INTO streetname VALUES('bradwell'); INSERT INTO streetname VALUES('brady'); INSERT INTO streetname VALUES('braids bend'); INSERT INTO streetname VALUES('bralers'); INSERT INTO streetname VALUES('brandie glen'); INSERT INTO streetname VALUES('brandy ridge'); INSERT INTO streetname VALUES('brandybuck'); INSERT INTO streetname VALUES('branthurst'); INSERT INTO streetname VALUES('brassy creek'); INSERT INTO streetname VALUES('brathay'); INSERT INTO streetname VALUES('brawer farm'); INSERT INTO streetname VALUES('breezy morn'); INSERT INTO streetname VALUES('brenda'); INSERT INTO streetname VALUES('brenly'); INSERT INTO streetname VALUES('brenock'); INSERT INTO streetname VALUES('brianwood'); INSERT INTO streetname VALUES('briar rose'); INSERT INTO streetname VALUES('briarcrest'); INSERT INTO streetname VALUES('briarthorne'); INSERT INTO streetname VALUES('brick dust'); INSERT INTO streetname VALUES('bridgepath'); INSERT INTO streetname VALUES('bridle ridge'); INSERT INTO streetname VALUES('briggs'); INSERT INTO streetname VALUES('brightleaf'); INSERT INTO streetname VALUES('brigstock'); INSERT INTO streetname VALUES('broad ridge'); INSERT INTO streetname VALUES('brock'); INSERT INTO streetname VALUES('brockhampton'); INSERT INTO streetname VALUES('broken pine'); INSERT INTO streetname VALUES('brompton'); INSERT INTO streetname VALUES('brook falls'); INSERT INTO streetname VALUES('brookings'); INSERT INTO streetname VALUES('browne'); INSERT INTO streetname VALUES('brownes creek'); INSERT INTO streetname VALUES('brownes ferry'); INSERT INTO streetname VALUES('brownestone view'); INSERT INTO streetname VALUES('brumit'); INSERT INTO streetname VALUES('bryn athyn'); INSERT INTO streetname VALUES('buck'); INSERT INTO streetname VALUES('bucklebury'); INSERT INTO streetname VALUES('buckminister'); INSERT INTO streetname VALUES('buckspring'); INSERT INTO streetname VALUES('burch'); INSERT INTO streetname VALUES('burch shire'); INSERT INTO streetname VALUES('burkston'); INSERT INTO streetname VALUES('burmith'); INSERT INTO streetname VALUES('burnaby'); INSERT INTO streetname VALUES('butterfly'); INSERT INTO streetname VALUES('cabin creek'); INSERT INTO streetname VALUES('cairns mill'); INSERT INTO streetname VALUES('callender'); INSERT INTO streetname VALUES('cambellton'); INSERT INTO streetname VALUES('cambridge bay'); INSERT INTO streetname VALUES('canary'); INSERT INTO streetname VALUES('canbury'); INSERT INTO streetname VALUES('candle leaf'); INSERT INTO streetname VALUES('canipe'); INSERT INTO streetname VALUES('canipe farm'); INSERT INTO streetname VALUES('cannon'); INSERT INTO streetname VALUES('canopy'); INSERT INTO streetname VALUES('canso'); INSERT INTO streetname VALUES('canterbrook'); INSERT INTO streetname VALUES('cardinal glen'); INSERT INTO streetname VALUES('cardinal point'); INSERT INTO streetname VALUES('cardinals nest'); INSERT INTO streetname VALUES('carlota'); INSERT INTO streetname VALUES('carmathen'); INSERT INTO streetname VALUES('carver'); INSERT INTO streetname VALUES('carver pond'); INSERT INTO streetname VALUES('casa loma'); INSERT INTO streetname VALUES('caselton'); INSERT INTO streetname VALUES('castello'); INSERT INTO streetname VALUES('castle ridge'); INSERT INTO streetname VALUES('castleglen'); INSERT INTO streetname VALUES('castlemaine'); INSERT INTO streetname VALUES('cavett'); INSERT INTO streetname VALUES('caymus'); INSERT INTO streetname VALUES('cedardale ridge'); INSERT INTO streetname VALUES('cedarhurst'); INSERT INTO streetname VALUES('cemkey way'); INSERT INTO streetname VALUES('cerise'); INSERT INTO streetname VALUES('chaceview'); INSERT INTO streetname VALUES('chadsworth'); INSERT INTO streetname VALUES('chadwell'); INSERT INTO streetname VALUES('champions crest'); INSERT INTO streetname VALUES('chandler haven'); INSERT INTO streetname VALUES('chapel crossing'); INSERT INTO streetname VALUES('chapel ridge'); INSERT INTO streetname VALUES('charles crawford'); INSERT INTO streetname VALUES('charminster'); INSERT INTO streetname VALUES('chasewind'); INSERT INTO streetname VALUES('chavel'); INSERT INTO streetname VALUES('chelsea jade'); INSERT INTO streetname VALUES('chestnut knoll'); INSERT INTO streetname VALUES('cheviot'); INSERT INTO streetname VALUES('chickadee'); INSERT INTO streetname VALUES('chidley'); INSERT INTO streetname VALUES('chimney ridge'); INSERT INTO streetname VALUES('chimney springs'); INSERT INTO streetname VALUES('chinaberry'); INSERT INTO streetname VALUES('chinemist'); INSERT INTO streetname VALUES('chinquapin'); INSERT INTO streetname VALUES('chiswell'); INSERT INTO streetname VALUES('christenbury'); INSERT INTO streetname VALUES('christenbury hills'); INSERT INTO streetname VALUES('churchill'); INSERT INTO streetname VALUES('cindy'); INSERT INTO streetname VALUES('cinnamon teal'); INSERT INTO streetname VALUES('citadel'); INSERT INTO streetname VALUES('clare olivia'); INSERT INTO streetname VALUES('clarke creek'); INSERT INTO streetname VALUES('clarke ridge'); INSERT INTO streetname VALUES('clear day'); INSERT INTO streetname VALUES('clear stream'); INSERT INTO streetname VALUES('cleve brown'); INSERT INTO streetname VALUES('cliff cameron'); INSERT INTO streetname VALUES('cliffvale'); INSERT INTO streetname VALUES('cloverside'); INSERT INTO streetname VALUES('clymer'); INSERT INTO streetname VALUES('coatbridge'); INSERT INTO streetname VALUES('cobble glen'); INSERT INTO streetname VALUES('cochran farm'); INSERT INTO streetname VALUES('cochrane'); INSERT INTO streetname VALUES('coleridge'); INSERT INTO streetname VALUES('coleshire'); INSERT INTO streetname VALUES('collins'); INSERT INTO streetname VALUES('colvard'); INSERT INTO streetname VALUES('colvard park'); INSERT INTO streetname VALUES('condor'); INSERT INTO streetname VALUES('conner ridge'); INSERT INTO streetname VALUES('connery'); INSERT INTO streetname VALUES('cooper run'); INSERT INTO streetname VALUES('coopers ridge'); INSERT INTO streetname VALUES('copper hill'); INSERT INTO streetname VALUES('coppermine'); INSERT INTO streetname VALUES('cornelia'); INSERT INTO streetname VALUES('corner'); INSERT INTO streetname VALUES('cornerstone'); INSERT INTO streetname VALUES('cottage oaks'); INSERT INTO streetname VALUES('cougar'); INSERT INTO streetname VALUES('coves end'); INSERT INTO streetname VALUES('cragland'); INSERT INTO streetname VALUES('crail'); INSERT INTO streetname VALUES('cranberry nook'); INSERT INTO streetname VALUES('crawford brook'); INSERT INTO streetname VALUES('crayton'); INSERT INTO streetname VALUES('creek breeze'); INSERT INTO streetname VALUES('crescent ridge'); INSERT INTO streetname VALUES('crescent view'); INSERT INTO streetname VALUES('cresta'); INSERT INTO streetname VALUES('crestfield'); INSERT INTO streetname VALUES('crestland'); INSERT INTO streetname VALUES('crestwick'); INSERT INTO streetname VALUES('crisfield'); INSERT INTO streetname VALUES('crisp wood'); INSERT INTO streetname VALUES('croft haven'); INSERT INTO streetname VALUES('crofton springs'); INSERT INTO streetname VALUES('cross'); INSERT INTO streetname VALUES('crosspoint center'); INSERT INTO streetname VALUES('crownvista'); INSERT INTO streetname VALUES('crystal arms'); INSERT INTO streetname VALUES('crystal crest'); INSERT INTO streetname VALUES('crystal leaf'); INSERT INTO streetname VALUES('cunningham park'); INSERT INTO streetname VALUES('cypress pond'); INSERT INTO streetname VALUES('daffodil'); INSERT INTO streetname VALUES('daisyfield'); INSERT INTO streetname VALUES('dalecrest'); INSERT INTO streetname VALUES('dannelly park'); INSERT INTO streetname VALUES('daphne'); INSERT INTO streetname VALUES('daria'); INSERT INTO streetname VALUES('dartmouth'); INSERT INTO streetname VALUES('datha'); INSERT INTO streetname VALUES('david cox'); INSERT INTO streetname VALUES('davis'); INSERT INTO streetname VALUES('davis crossing'); INSERT INTO streetname VALUES('davis lake'); INSERT INTO streetname VALUES('davis ridge'); INSERT INTO streetname VALUES('dawnmist'); INSERT INTO streetname VALUES('daybreak'); INSERT INTO streetname VALUES('dearmon'); INSERT INTO streetname VALUES('dearview'); INSERT INTO streetname VALUES('deaton hill'); INSERT INTO streetname VALUES('deer cross'); INSERT INTO streetname VALUES('deerton'); INSERT INTO streetname VALUES('degrasse'); INSERT INTO streetname VALUES('delamere'); INSERT INTO streetname VALUES('dellfield'); INSERT INTO streetname VALUES('dellinger'); INSERT INTO streetname VALUES('demington'); INSERT INTO streetname VALUES('denmeade'); INSERT INTO streetname VALUES('derita'); INSERT INTO streetname VALUES('derita woods'); INSERT INTO streetname VALUES('deruyter'); INSERT INTO streetname VALUES('dervish'); INSERT INTO streetname VALUES('devas'); INSERT INTO streetname VALUES('devon croft'); INSERT INTO streetname VALUES('devonbridge'); INSERT INTO streetname VALUES('devongate'); INSERT INTO streetname VALUES('devonhill'); INSERT INTO streetname VALUES('dewmorn'); INSERT INTO streetname VALUES('distribution center'); INSERT INTO streetname VALUES('dominion crest'); INSERT INTO streetname VALUES('dominion green'); INSERT INTO streetname VALUES('dominion village'); INSERT INTO streetname VALUES('dorshire'); INSERT INTO streetname VALUES('double creek crossing'); INSERT INTO streetname VALUES('dow'); INSERT INTO streetname VALUES('downfield wood'); INSERT INTO streetname VALUES('downing creek'); INSERT INTO streetname VALUES('driscol'); INSERT INTO streetname VALUES('driwood'); INSERT INTO streetname VALUES('dry brook'); INSERT INTO streetname VALUES('dumont'); INSERT INTO streetname VALUES('dunblane'); INSERT INTO streetname VALUES('dunfield'); INSERT INTO streetname VALUES('dunoon'); INSERT INTO streetname VALUES('dunslow'); INSERT INTO streetname VALUES('dunstaff'); INSERT INTO streetname VALUES('durham'); INSERT INTO streetname VALUES('durston'); INSERT INTO streetname VALUES('dusty cedar'); INSERT INTO streetname VALUES('dusty trail'); INSERT INTO streetname VALUES('dutchess'); INSERT INTO streetname VALUES('duxford'); INSERT INTO streetname VALUES('eagle creek'); INSERT INTO streetname VALUES('eagles field'); INSERT INTO streetname VALUES('eargle'); INSERT INTO streetname VALUES('earlswood'); INSERT INTO streetname VALUES('early mist'); INSERT INTO streetname VALUES('earthenware'); INSERT INTO streetname VALUES('eastfield park'); INSERT INTO streetname VALUES('eastfield village'); INSERT INTO streetname VALUES('easy'); INSERT INTO streetname VALUES('eben'); INSERT INTO streetname VALUES('edgepine'); INSERT INTO streetname VALUES('edgewier'); INSERT INTO streetname VALUES('edinburgh'); INSERT INTO streetname VALUES('edinmeadow'); INSERT INTO streetname VALUES('edmonton'); INSERT INTO streetname VALUES('edwin jones'); INSERT INTO streetname VALUES('elberon'); INSERT INTO streetname VALUES('elderslie'); INSERT INTO streetname VALUES('elementary view'); INSERT INTO streetname VALUES('elendil'); INSERT INTO streetname VALUES('elizabeth'); INSERT INTO streetname VALUES('elm cove'); INSERT INTO streetname VALUES('elrond'); INSERT INTO streetname VALUES('elsenham'); INSERT INTO streetname VALUES('elven'); INSERT INTO streetname VALUES('emma lynn'); INSERT INTO streetname VALUES('english setter'); INSERT INTO streetname VALUES('enoch'); INSERT INTO streetname VALUES('equipment'); INSERT INTO streetname VALUES('ernest russell'); INSERT INTO streetname VALUES('ernie'); INSERT INTO streetname VALUES('esmeralda'); INSERT INTO streetname VALUES('evergreen hollow'); INSERT INTO streetname VALUES('eversfield'); INSERT INTO streetname VALUES('ewen'); INSERT INTO streetname VALUES('ewert cut'); INSERT INTO streetname VALUES('exbury'); INSERT INTO streetname VALUES('fair grounds park'); INSERT INTO streetname VALUES('fairbourne'); INSERT INTO streetname VALUES('fairchase'); INSERT INTO streetname VALUES('faircreek'); INSERT INTO streetname VALUES('fairglen'); INSERT INTO streetname VALUES('fairlea'); INSERT INTO streetname VALUES('fairmead'); INSERT INTO streetname VALUES('fairmeadows'); INSERT INTO streetname VALUES('fairstone'); INSERT INTO streetname VALUES('fairvista'); INSERT INTO streetname VALUES('fairway point'); INSERT INTO streetname VALUES('falconcrest'); INSERT INTO streetname VALUES('falls ridge'); INSERT INTO streetname VALUES('falmouth'); INSERT INTO streetname VALUES('far west'); INSERT INTO streetname VALUES('farlow'); INSERT INTO streetname VALUES('farris wheel'); INSERT INTO streetname VALUES('fawndale'); INSERT INTO streetname VALUES('feather bend'); INSERT INTO streetname VALUES('fernledge'); INSERT INTO streetname VALUES('fernmoss'); INSERT INTO streetname VALUES('ferrell commons'); INSERT INTO streetname VALUES('fieldstone'); INSERT INTO streetname VALUES('fillian'); INSERT INTO streetname VALUES('fincher'); INSERT INTO streetname VALUES('foggy meadow'); INSERT INTO streetname VALUES('fordyce'); INSERT INTO streetname VALUES('forest grove'); INSERT INTO streetname VALUES('forest path'); INSERT INTO streetname VALUES('forestridge commons'); INSERT INTO streetname VALUES('forestrock'); INSERT INTO streetname VALUES('fortunes ridge'); INSERT INTO streetname VALUES('founders club'); INSERT INTO streetname VALUES('fountaingrass'); INSERT INTO streetname VALUES('fox chase'); INSERT INTO streetname VALUES('fox glen'); INSERT INTO streetname VALUES('fox hill'); INSERT INTO streetname VALUES('fox point'); INSERT INTO streetname VALUES('fox trot'); INSERT INTO streetname VALUES('foxbriar'); INSERT INTO streetname VALUES('frank little'); INSERT INTO streetname VALUES('franzia'); INSERT INTO streetname VALUES('french woods'); INSERT INTO streetname VALUES('frostmoor'); INSERT INTO streetname VALUES('frye'); INSERT INTO streetname VALUES('furlong'); INSERT INTO streetname VALUES('galena view'); INSERT INTO streetname VALUES('gallery pointe'); INSERT INTO streetname VALUES('gammon'); INSERT INTO streetname VALUES('garden grove'); INSERT INTO streetname VALUES('gardendale'); INSERT INTO streetname VALUES('garganey'); INSERT INTO streetname VALUES('garnet field'); INSERT INTO streetname VALUES('garrison'); INSERT INTO streetname VALUES('garvin'); INSERT INTO streetname VALUES('garvis'); INSERT INTO streetname VALUES('gaskill'); INSERT INTO streetname VALUES('gemstone'); INSERT INTO streetname VALUES('gibbon'); INSERT INTO streetname VALUES('gibbon terrace'); INSERT INTO streetname VALUES('gibbons link'); INSERT INTO streetname VALUES('gillman'); INSERT INTO streetname VALUES('gladwood'); INSERT INTO streetname VALUES('gladwyne'); INSERT INTO streetname VALUES('glamorgan'); INSERT INTO streetname VALUES('glaze'); INSERT INTO streetname VALUES('glen brook'); INSERT INTO streetname VALUES('glen cove'); INSERT INTO streetname VALUES('glen hope'); INSERT INTO streetname VALUES('glen manor'); INSERT INTO streetname VALUES('glen olden'); INSERT INTO streetname VALUES('glencairn'); INSERT INTO streetname VALUES('glendock'); INSERT INTO streetname VALUES('glenolden'); INSERT INTO streetname VALUES('glenover'); INSERT INTO streetname VALUES('glenshire'); INSERT INTO streetname VALUES('glenstone'); INSERT INTO streetname VALUES('gold dust'); INSERT INTO streetname VALUES('golden pond'); INSERT INTO streetname VALUES('goldenblush'); INSERT INTO streetname VALUES('goldenfield'); INSERT INTO streetname VALUES('goose landing'); INSERT INTO streetname VALUES('gorham gate'); INSERT INTO streetname VALUES('grabill'); INSERT INTO streetname VALUES('graburns ford'); INSERT INTO streetname VALUES('graham'); INSERT INTO streetname VALUES('grahamson'); INSERT INTO streetname VALUES('granard'); INSERT INTO streetname VALUES('grand teton'); INSERT INTO streetname VALUES('grande heights'); INSERT INTO streetname VALUES('grandeur'); INSERT INTO streetname VALUES('granite creek'); INSERT INTO streetname VALUES('grasset'); INSERT INTO streetname VALUES('graypark'); INSERT INTO streetname VALUES('grays ridge'); INSERT INTO streetname VALUES('great bear'); INSERT INTO streetname VALUES('green clover'); INSERT INTO streetname VALUES('green hedge'); INSERT INTO streetname VALUES('green meadow'); INSERT INTO streetname VALUES('green pasture'); INSERT INTO streetname VALUES('greene'); INSERT INTO streetname VALUES('greenloch'); INSERT INTO streetname VALUES('greenock ridge'); INSERT INTO streetname VALUES('greenware'); INSERT INTO streetname VALUES('greenway village'); INSERT INTO streetname VALUES('grenelefe village'); INSERT INTO streetname VALUES('grey dogwood'); INSERT INTO streetname VALUES('greyhound'); INSERT INTO streetname VALUES('greylock ridge'); INSERT INTO streetname VALUES('grosbeak'); INSERT INTO streetname VALUES('grove'); INSERT INTO streetname VALUES('groveton'); INSERT INTO streetname VALUES('groveview'); INSERT INTO streetname VALUES('hackberry creek'); INSERT INTO streetname VALUES('hackberry grove'); INSERT INTO streetname VALUES('hackett'); INSERT INTO streetname VALUES('haddington'); INSERT INTO streetname VALUES('hagler'); INSERT INTO streetname VALUES('halcott'); INSERT INTO streetname VALUES('half dome'); INSERT INTO streetname VALUES('hallam'); INSERT INTO streetname VALUES('hamilton russell'); INSERT INTO streetname VALUES('hampton place'); INSERT INTO streetname VALUES('hankins'); INSERT INTO streetname VALUES('harburn forest'); INSERT INTO streetname VALUES('harringham'); INSERT INTO streetname VALUES('harrington woods'); INSERT INTO streetname VALUES('harris corners'); INSERT INTO streetname VALUES('harris cove'); INSERT INTO streetname VALUES('harris glen'); INSERT INTO streetname VALUES('harris hill'); INSERT INTO streetname VALUES('harris oak'); INSERT INTO streetname VALUES('harris pointe'); INSERT INTO streetname VALUES('harris pond'); INSERT INTO streetname VALUES('harris ridge'); INSERT INTO streetname VALUES('harris technology'); INSERT INTO streetname VALUES('harris woods'); INSERT INTO streetname VALUES('hartfield downs'); INSERT INTO streetname VALUES('hattie little'); INSERT INTO streetname VALUES('hatwynn'); INSERT INTO streetname VALUES('hawkins'); INSERT INTO streetname VALUES('hawksnest'); INSERT INTO streetname VALUES('haybridge'); INSERT INTO streetname VALUES('hayden'); INSERT INTO streetname VALUES('hazelcroft'); INSERT INTO streetname VALUES('hazlitt'); INSERT INTO streetname VALUES('hazy valley'); INSERT INTO streetname VALUES('hearst'); INSERT INTO streetname VALUES('heathcrest'); INSERT INTO streetname VALUES('heathcroft'); INSERT INTO streetname VALUES('hedge maple'); INSERT INTO streetname VALUES('hedgecrest'); INSERT INTO streetname VALUES('hedingham'); INSERT INTO streetname VALUES('heman'); INSERT INTO streetname VALUES('henderson'); INSERT INTO streetname VALUES('henderson oaks'); INSERT INTO streetname VALUES('henderson valley'); INSERT INTO streetname VALUES('hendry'); INSERT INTO streetname VALUES('heritage hills'); INSERT INTO streetname VALUES('heritage woods'); INSERT INTO streetname VALUES('heron cove'); INSERT INTO streetname VALUES('heron glen'); INSERT INTO streetname VALUES('hewitt'); INSERT INTO streetname VALUES('hey rock'); INSERT INTO streetname VALUES('heysham'); INSERT INTO streetname VALUES('hickory cove'); INSERT INTO streetname VALUES('hidden meadow'); INSERT INTO streetname VALUES('high glen'); INSERT INTO streetname VALUES('high laurel'); INSERT INTO streetname VALUES('high valley'); INSERT INTO streetname VALUES('highcroft'); INSERT INTO streetname VALUES('highland'); INSERT INTO streetname VALUES('highland commons'); INSERT INTO streetname VALUES('highland creek'); INSERT INTO streetname VALUES('highland glen'); INSERT INTO streetname VALUES('highland park'); INSERT INTO streetname VALUES('highlander'); INSERT INTO streetname VALUES('highstream'); INSERT INTO streetname VALUES('hilltop'); INSERT INTO streetname VALUES('hobbitshire'); INSERT INTO streetname VALUES('hoffman'); INSERT INTO streetname VALUES('hogans way'); INSERT INTO streetname VALUES('holbert'); INSERT INTO streetname VALUES('hollow ridge'); INSERT INTO streetname VALUES('holly vista'); INSERT INTO streetname VALUES('hollywood'); INSERT INTO streetname VALUES('hoover'); INSERT INTO streetname VALUES('hopkins'); INSERT INTO streetname VALUES('horace mann'); INSERT INTO streetname VALUES('hornbeam'); INSERT INTO streetname VALUES('horse pasture'); INSERT INTO streetname VALUES('hosta'); INSERT INTO streetname VALUES('howard'); INSERT INTO streetname VALUES('hubbard'); INSERT INTO streetname VALUES('hubbard falls'); INSERT INTO streetname VALUES('hubbard woods'); INSERT INTO streetname VALUES('hucks'); INSERT INTO streetname VALUES('hunters creek'); INSERT INTO streetname VALUES('hunters pointe'); INSERT INTO streetname VALUES('hunters spring'); INSERT INTO streetname VALUES('hunters whip'); INSERT INTO streetname VALUES('huntmeadow'); INSERT INTO streetname VALUES('hutchison mcdonald'); INSERT INTO streetname VALUES('ingleton'); INSERT INTO streetname VALUES('insdale'); INSERT INTO streetname VALUES('interstate 85 service'); INSERT INTO streetname VALUES('iola'); INSERT INTO streetname VALUES('iredell'); INSERT INTO streetname VALUES('iron brigade'); INSERT INTO streetname VALUES('irwin valley'); INSERT INTO streetname VALUES('irwin wood'); INSERT INTO streetname VALUES('ivy brook'); INSERT INTO streetname VALUES('ivy ridge'); INSERT INTO streetname VALUES('jack russell'); INSERT INTO streetname VALUES('jackson'); INSERT INTO streetname VALUES('jacob martin'); INSERT INTO streetname VALUES('jamison'); INSERT INTO streetname VALUES('jane'); INSERT INTO streetname VALUES('jaspar crest'); INSERT INTO streetname VALUES('jessica'); INSERT INTO streetname VALUES('jimmy oehler'); INSERT INTO streetname VALUES('jocelyn'); INSERT INTO streetname VALUES('johnston mill'); INSERT INTO streetname VALUES('johnston oehler'); INSERT INTO streetname VALUES('judal'); INSERT INTO streetname VALUES('junipeous'); INSERT INTO streetname VALUES('juniper'); INSERT INTO streetname VALUES('juniperus'); INSERT INTO streetname VALUES('kalispell'); INSERT INTO streetname VALUES('karylsturn'); INSERT INTO streetname VALUES('katelyn'); INSERT INTO streetname VALUES('kayron'); INSERT INTO streetname VALUES('keaton'); INSERT INTO streetname VALUES('keble'); INSERT INTO streetname VALUES('keels'); INSERT INTO streetname VALUES('keith'); INSERT INTO streetname VALUES('keithwood'); INSERT INTO streetname VALUES('kelden walker'); INSERT INTO streetname VALUES('kelsey emma'); INSERT INTO streetname VALUES('kendrick'); INSERT INTO streetname VALUES('kenmont'); INSERT INTO streetname VALUES('kennerly cove'); INSERT INTO streetname VALUES('kenninghall'); INSERT INTO streetname VALUES('kent village'); INSERT INTO streetname VALUES('kestral ridge'); INSERT INTO streetname VALUES('kestrel'); INSERT INTO streetname VALUES('kilmartin'); INSERT INTO streetname VALUES('kilty'); INSERT INTO streetname VALUES('kinglet'); INSERT INTO streetname VALUES('kingsland'); INSERT INTO streetname VALUES('kingsnorth'); INSERT INTO streetname VALUES('kinsmore'); INSERT INTO streetname VALUES('kirkgard'); INSERT INTO streetname VALUES('kirkmont'); INSERT INTO streetname VALUES('knightsgate'); INSERT INTO streetname VALUES('kobuk'); INSERT INTO streetname VALUES('kotlik'); INSERT INTO streetname VALUES('kotz'); INSERT INTO streetname VALUES('kyndall walk'); INSERT INTO streetname VALUES('laborde'); INSERT INTO streetname VALUES('lady bank'); INSERT INTO streetname VALUES('lagrande'); INSERT INTO streetname VALUES('lake'); INSERT INTO streetname VALUES('lakeridge commons'); INSERT INTO streetname VALUES('lakeview'); INSERT INTO streetname VALUES('lakewood edge'); INSERT INTO streetname VALUES('lakota'); INSERT INTO streetname VALUES('lambrook'); INSERT INTO streetname VALUES('lampkin'); INSERT INTO streetname VALUES('lampkin park'); INSERT INTO streetname VALUES('langham'); INSERT INTO streetname VALUES('lanzerac manor'); INSERT INTO streetname VALUES('larkmead forest'); INSERT INTO streetname VALUES('lattice'); INSERT INTO streetname VALUES('laurel crest'); INSERT INTO streetname VALUES('laurel ridge'); INSERT INTO streetname VALUES('laurel run'); INSERT INTO streetname VALUES('laurenfield'); INSERT INTO streetname VALUES('laveta'); INSERT INTO streetname VALUES('lazy day'); INSERT INTO streetname VALUES('leawood run'); INSERT INTO streetname VALUES('lee marie'); INSERT INTO streetname VALUES('legacy lake'); INSERT INTO streetname VALUES('legacy park'); INSERT INTO streetname VALUES('legato'); INSERT INTO streetname VALUES('legolas'); INSERT INTO streetname VALUES('leigh glen'); INSERT INTO streetname VALUES('lence'); INSERT INTO streetname VALUES('lenox hill'); INSERT INTO streetname VALUES('leonine'); INSERT INTO streetname VALUES('leslie'); INSERT INTO streetname VALUES('lester hill'); INSERT INTO streetname VALUES('levisey'); INSERT INTO streetname VALUES('liberty bell'); INSERT INTO streetname VALUES('linden berry'); INSERT INTO streetname VALUES('lisbon'); INSERT INTO streetname VALUES('little stoney'); INSERT INTO streetname VALUES('livengood'); INSERT INTO streetname VALUES('lochway'); INSERT INTO streetname VALUES('lockman'); INSERT INTO streetname VALUES('loganville'); INSERT INTO streetname VALUES('lone tree'); INSERT INTO streetname VALUES('long creek park'); INSERT INTO streetname VALUES('long forest'); INSERT INTO streetname VALUES('looking glass'); INSERT INTO streetname VALUES('lookout point'); INSERT INTO streetname VALUES('lowen'); INSERT INTO streetname VALUES('lusby'); INSERT INTO streetname VALUES('lyleton'); INSERT INTO streetname VALUES('lynn lee'); INSERT INTO streetname VALUES('lynnewood glen'); INSERT INTO streetname VALUES('machrie'); INSERT INTO streetname VALUES('mackinac'); INSERT INTO streetname VALUES('maddox'); INSERT INTO streetname VALUES('madison park'); INSERT INTO streetname VALUES('mallard'); INSERT INTO streetname VALUES('mallard cove'); INSERT INTO streetname VALUES('mallard forest'); INSERT INTO streetname VALUES('mallard grove'); INSERT INTO streetname VALUES('mallard hill'); INSERT INTO streetname VALUES('mallard park'); INSERT INTO streetname VALUES('mallard ridge'); INSERT INTO streetname VALUES('mallard view'); INSERT INTO streetname VALUES('manbey'); INSERT INTO streetname VALUES('manning'); INSERT INTO streetname VALUES('mantario'); INSERT INTO streetname VALUES('maple'); INSERT INTO streetname VALUES('maple cove'); INSERT INTO streetname VALUES('maple park'); INSERT INTO streetname VALUES('marathon hill'); INSERT INTO streetname VALUES('marbury'); INSERT INTO streetname VALUES('marett'); INSERT INTO streetname VALUES('marigold'); INSERT INTO streetname VALUES('marionwood'); INSERT INTO streetname VALUES('marshbank'); INSERT INTO streetname VALUES('mason'); INSERT INTO streetname VALUES('mayapple'); INSERT INTO streetname VALUES('maylandia'); INSERT INTO streetname VALUES('mayspring'); INSERT INTO streetname VALUES('mcadam'); INSERT INTO streetname VALUES('mcchesney'); INSERT INTO streetname VALUES('mccurdy'); INSERT INTO streetname VALUES('mcgrath'); INSERT INTO streetname VALUES('mckendree'); INSERT INTO streetname VALUES('mclaughlin'); INSERT INTO streetname VALUES('mctaggart'); INSERT INTO streetname VALUES('meadow green'); INSERT INTO streetname VALUES('meadow knoll'); INSERT INTO streetname VALUES('meadow post'); INSERT INTO streetname VALUES('meadowmont'); INSERT INTO streetname VALUES('meadowmont view'); INSERT INTO streetname VALUES('meadowview hills'); INSERT INTO streetname VALUES('melshire'); INSERT INTO streetname VALUES('melstrand'); INSERT INTO streetname VALUES('mentone'); INSERT INTO streetname VALUES('meridale crossing'); INSERT INTO streetname VALUES('merion hills'); INSERT INTO streetname VALUES('merlot'); INSERT INTO streetname VALUES('mersham'); INSERT INTO streetname VALUES('metromont'); INSERT INTO streetname VALUES('metromont industrial'); INSERT INTO streetname VALUES('michaw'); INSERT INTO streetname VALUES('milhaven'); INSERT INTO streetname VALUES('milhof'); INSERT INTO streetname VALUES('millstream ridge'); INSERT INTO streetname VALUES('mineral ridge'); INSERT INTO streetname VALUES('mint thistle'); INSERT INTO streetname VALUES('mintleaf'); INSERT INTO streetname VALUES('mintvale'); INSERT INTO streetname VALUES('misty'); INSERT INTO streetname VALUES('misty arbor'); INSERT INTO streetname VALUES('misty creek'); INSERT INTO streetname VALUES('misty oaks'); INSERT INTO streetname VALUES('misty wood'); INSERT INTO streetname VALUES('mitzi deborah'); INSERT INTO streetname VALUES('mobile'); INSERT INTO streetname VALUES('molly elizabeth'); INSERT INTO streetname VALUES('monmouth'); INSERT INTO streetname VALUES('montrose'); INSERT INTO streetname VALUES('moonlight'); INSERT INTO streetname VALUES('moose'); INSERT INTO streetname VALUES('morning dew'); INSERT INTO streetname VALUES('morningsong'); INSERT INTO streetname VALUES('morningview'); INSERT INTO streetname VALUES('morsey'); INSERT INTO streetname VALUES('moss glen'); INSERT INTO streetname VALUES('mossy bank'); INSERT INTO streetname VALUES('motor sport'); INSERT INTO streetname VALUES('mountain laurel'); INSERT INTO streetname VALUES('mourning dove'); INSERT INTO streetname VALUES('mozart'); INSERT INTO streetname VALUES('munsing'); INSERT INTO streetname VALUES('murray'); INSERT INTO streetname VALUES('nathan'); INSERT INTO streetname VALUES('netherhall'); INSERT INTO streetname VALUES('netherton'); INSERT INTO streetname VALUES('neuhoff'); INSERT INTO streetname VALUES('nevin'); INSERT INTO streetname VALUES('nevin brook'); INSERT INTO streetname VALUES('nevin glen'); INSERT INTO streetname VALUES('nevin place'); INSERT INTO streetname VALUES('new england'); INSERT INTO streetname VALUES('new house'); INSERT INTO streetname VALUES('newbary'); INSERT INTO streetname VALUES('newchurch'); INSERT INTO streetname VALUES('newfane'); INSERT INTO streetname VALUES('newgard'); INSERT INTO streetname VALUES('nicholas'); INSERT INTO streetname VALUES('nicole'); INSERT INTO streetname VALUES('nobility'); INSERT INTO streetname VALUES('norcroft'); INSERT INTO streetname VALUES('northridge'); INSERT INTO streetname VALUES('northside'); INSERT INTO streetname VALUES('northwoods business'); INSERT INTO streetname VALUES('norway'); INSERT INTO streetname VALUES('nottinghill'); INSERT INTO streetname VALUES('numenore'); INSERT INTO streetname VALUES('nyewood'); INSERT INTO streetname VALUES('oak'); INSERT INTO streetname VALUES('oak cove'); INSERT INTO streetname VALUES('oak pasture'); INSERT INTO streetname VALUES('oakburn'); INSERT INTO streetname VALUES('oakwinds'); INSERT INTO streetname VALUES('oakwood'); INSERT INTO streetname VALUES('obrien'); INSERT INTO streetname VALUES('ocala'); INSERT INTO streetname VALUES('old bridge'); INSERT INTO streetname VALUES('old fox'); INSERT INTO streetname VALUES('old potters'); INSERT INTO streetname VALUES('old statesville'); INSERT INTO streetname VALUES('old steine'); INSERT INTO streetname VALUES('old stoney creek'); INSERT INTO streetname VALUES('old sugar creek'); INSERT INTO streetname VALUES('old timber'); INSERT INTO streetname VALUES('old wagon'); INSERT INTO streetname VALUES('old willow'); INSERT INTO streetname VALUES('oldenway'); INSERT INTO streetname VALUES('oneida'); INSERT INTO streetname VALUES('ontario'); INSERT INTO streetname VALUES('oriole'); INSERT INTO streetname VALUES('orofino'); INSERT INTO streetname VALUES('orr'); INSERT INTO streetname VALUES('osage'); INSERT INTO streetname VALUES('osceola'); INSERT INTO streetname VALUES('osprey knoll'); INSERT INTO streetname VALUES('oxford hill'); INSERT INTO streetname VALUES('painted fern'); INSERT INTO streetname VALUES('painted pony'); INSERT INTO streetname VALUES('paisley'); INSERT INTO streetname VALUES('pale moss'); INSERT INTO streetname VALUES('palladium'); INSERT INTO streetname VALUES('palmutum'); INSERT INTO streetname VALUES('palustris'); INSERT INTO streetname VALUES('panglemont'); INSERT INTO streetname VALUES('panther'); INSERT INTO streetname VALUES('panthersville'); INSERT INTO streetname VALUES('paper whites'); INSERT INTO streetname VALUES('park'); INSERT INTO streetname VALUES('parker green'); INSERT INTO streetname VALUES('parkhouse'); INSERT INTO streetname VALUES('passour ridge'); INSERT INTO streetname VALUES('pasture view'); INSERT INTO streetname VALUES('patricia ann'); INSERT INTO streetname VALUES('patton'); INSERT INTO streetname VALUES('patton ridge'); INSERT INTO streetname VALUES('pawpaw'); INSERT INTO streetname VALUES('peach'); INSERT INTO streetname VALUES('peakwood'); INSERT INTO streetname VALUES('pebble creek'); INSERT INTO streetname VALUES('pecan cove'); INSERT INTO streetname VALUES('pedigree'); INSERT INTO streetname VALUES('pelorus'); INSERT INTO streetname VALUES('penmore'); INSERT INTO streetname VALUES('pensfold'); INSERT INTO streetname VALUES('pepperstone'); INSERT INTO streetname VALUES('peregrine'); INSERT INTO streetname VALUES('periwinkle'); INSERT INTO streetname VALUES('perkins'); INSERT INTO streetname VALUES('pete brown'); INSERT INTO streetname VALUES('phillips'); INSERT INTO streetname VALUES('pickway'); INSERT INTO streetname VALUES('piercy woods'); INSERT INTO streetname VALUES('pierpoint'); INSERT INTO streetname VALUES('pine'); INSERT INTO streetname VALUES('pine branch'); INSERT INTO streetname VALUES('pine meadow'); INSERT INTO streetname VALUES('pineleaf'); INSERT INTO streetname VALUES('pinewood'); INSERT INTO streetname VALUES('pintail'); INSERT INTO streetname VALUES('pipestone'); INSERT INTO streetname VALUES('placer maple'); INSERT INTO streetname VALUES('plover'); INSERT INTO streetname VALUES('plum'); INSERT INTO streetname VALUES('po box'); INSERT INTO streetname VALUES('pochard'); INSERT INTO streetname VALUES('pointview'); INSERT INTO streetname VALUES('polk and white'); INSERT INTO streetname VALUES('pond valley'); INSERT INTO streetname VALUES('pondridge'); INSERT INTO streetname VALUES('pope farm'); INSERT INTO streetname VALUES('poplar grove'); INSERT INTO streetname VALUES('poplar springs'); INSERT INTO streetname VALUES('portola'); INSERT INTO streetname VALUES('potters glen'); INSERT INTO streetname VALUES('powatan'); INSERT INTO streetname VALUES('prairie valley'); INSERT INTO streetname VALUES('prescott'); INSERT INTO streetname VALUES('presmann'); INSERT INTO streetname VALUES('prestigious'); INSERT INTO streetname VALUES('princess'); INSERT INTO streetname VALUES('prosperity'); INSERT INTO streetname VALUES('prosperity church'); INSERT INTO streetname VALUES('prosperity commons'); INSERT INTO streetname VALUES('prosperity park'); INSERT INTO streetname VALUES('prosperity point'); INSERT INTO streetname VALUES('prosperity ridge'); INSERT INTO streetname VALUES('prosperity view'); INSERT INTO streetname VALUES('purple finch'); INSERT INTO streetname VALUES('quail'); INSERT INTO streetname VALUES('queensbury'); INSERT INTO streetname VALUES('quinn'); INSERT INTO streetname VALUES('racine'); INSERT INTO streetname VALUES('radbourne'); INSERT INTO streetname VALUES('raddington'); INSERT INTO streetname VALUES('raku'); INSERT INTO streetname VALUES('rancliffe'); INSERT INTO streetname VALUES('ravencrest'); INSERT INTO streetname VALUES('reames'); INSERT INTO streetname VALUES('rebecca run'); INSERT INTO streetname VALUES('red bluff'); INSERT INTO streetname VALUES('red clay'); INSERT INTO streetname VALUES('red clover'); INSERT INTO streetname VALUES('red rose'); INSERT INTO streetname VALUES('red shed'); INSERT INTO streetname VALUES('red tail'); INSERT INTO streetname VALUES('redbridge'); INSERT INTO streetname VALUES('redstart'); INSERT INTO streetname VALUES('redstone view'); INSERT INTO streetname VALUES('reedmont'); INSERT INTO streetname VALUES('reeves'); INSERT INTO streetname VALUES('regal'); INSERT INTO streetname VALUES('reinbeck'); INSERT INTO streetname VALUES('retriever'); INSERT INTO streetname VALUES('ribbonwalk'); INSERT INTO streetname VALUES('richardson park'); INSERT INTO streetname VALUES('richfield'); INSERT INTO streetname VALUES('riddings'); INSERT INTO streetname VALUES('ridge'); INSERT INTO streetname VALUES('ridge cliff'); INSERT INTO streetname VALUES('ridge path'); INSERT INTO streetname VALUES('ridge peak'); INSERT INTO streetname VALUES('ridgefield'); INSERT INTO streetname VALUES('ridgeline'); INSERT INTO streetname VALUES('ridgeview commons'); INSERT INTO streetname VALUES('riley'); INSERT INTO streetname VALUES('riley woods'); INSERT INTO streetname VALUES('rillet'); INSERT INTO streetname VALUES('rindle'); INSERT INTO streetname VALUES('rivendell'); INSERT INTO streetname VALUES('robin'); INSERT INTO streetname VALUES('robins nest'); INSERT INTO streetname VALUES('robur'); INSERT INTO streetname VALUES('robyns glen'); INSERT INTO streetname VALUES('rock stream'); INSERT INTO streetname VALUES('rockwell'); INSERT INTO streetname VALUES('rockwell church'); INSERT INTO streetname VALUES('rocky brook'); INSERT INTO streetname VALUES('rocky ford club'); INSERT INTO streetname VALUES('rotary'); INSERT INTO streetname VALUES('rouda'); INSERT INTO streetname VALUES('royal bluff'); INSERT INTO streetname VALUES('royal celadon'); INSERT INTO streetname VALUES('rubin lura'); INSERT INTO streetname VALUES('runswyck'); INSERT INTO streetname VALUES('ruth ferrell'); INSERT INTO streetname VALUES('ruth polk'); INSERT INTO streetname VALUES('ryan jay'); INSERT INTO streetname VALUES('sackett'); INSERT INTO streetname VALUES('saddle pace'); INSERT INTO streetname VALUES('saddle run'); INSERT INTO streetname VALUES('saddle trail'); INSERT INTO streetname VALUES('saguaro'); INSERT INTO streetname VALUES('saint audrey'); INSERT INTO streetname VALUES('saint bernard'); INSERT INTO streetname VALUES('saint frances'); INSERT INTO streetname VALUES('sam roper'); INSERT INTO streetname VALUES('samara'); INSERT INTO streetname VALUES('sanders creek'); INSERT INTO streetname VALUES('saquache'); INSERT INTO streetname VALUES('sarnia'); INSERT INTO streetname VALUES('savannah springs'); INSERT INTO streetname VALUES('sawgrass ridge'); INSERT INTO streetname VALUES('saxonbury'); INSERT INTO streetname VALUES('scotch moss'); INSERT INTO streetname VALUES('seasons'); INSERT INTO streetname VALUES('serenity'); INSERT INTO streetname VALUES('seths'); INSERT INTO streetname VALUES('shadow lawn'); INSERT INTO streetname VALUES('shadow oaks'); INSERT INTO streetname VALUES('shadow pine'); INSERT INTO streetname VALUES('shadyside'); INSERT INTO streetname VALUES('shallow oak'); INSERT INTO streetname VALUES('shelley'); INSERT INTO streetname VALUES('shining oak'); INSERT INTO streetname VALUES('ship'); INSERT INTO streetname VALUES('shore haven'); INSERT INTO streetname VALUES('shuman'); INSERT INTO streetname VALUES('sidney'); INSERT INTO streetname VALUES('silver birch'); INSERT INTO streetname VALUES('silvermere'); INSERT INTO streetname VALUES('simonton'); INSERT INTO streetname VALUES('singing hills'); INSERT INTO streetname VALUES('singing oak'); INSERT INTO streetname VALUES('sipes'); INSERT INTO streetname VALUES('six point'); INSERT INTO streetname VALUES('skycrest'); INSERT INTO streetname VALUES('skyline'); INSERT INTO streetname VALUES('small'); INSERT INTO streetname VALUES('smith corners'); INSERT INTO streetname VALUES('smithwood'); INSERT INTO streetname VALUES('snow hill'); INSERT INTO streetname VALUES('soapstone'); INSERT INTO streetname VALUES('sobeck'); INSERT INTO streetname VALUES('socata'); INSERT INTO streetname VALUES('solace'); INSERT INTO streetname VALUES('solway'); INSERT INTO streetname VALUES('song sparrow'); INSERT INTO streetname VALUES('sorrento'); INSERT INTO streetname VALUES('spector'); INSERT INTO streetname VALUES('spin drift'); INSERT INTO streetname VALUES('spring crest'); INSERT INTO streetname VALUES('spring lee'); INSERT INTO streetname VALUES('spring park'); INSERT INTO streetname VALUES('spring terrace'); INSERT INTO streetname VALUES('spring trace'); INSERT INTO streetname VALUES('springhaven'); INSERT INTO streetname VALUES('squirrel trail'); INSERT INTO streetname VALUES('stardust'); INSERT INTO streetname VALUES('stargaze'); INSERT INTO streetname VALUES('starita'); INSERT INTO streetname VALUES('starmount'); INSERT INTO streetname VALUES('statesville'); INSERT INTO streetname VALUES('steed'); INSERT INTO streetname VALUES('steelewood'); INSERT INTO streetname VALUES('steepleglen'); INSERT INTO streetname VALUES('stephens farm'); INSERT INTO streetname VALUES('stewarton'); INSERT INTO streetname VALUES('stone park'); INSERT INTO streetname VALUES('stonebrook'); INSERT INTO streetname VALUES('stonefield'); INSERT INTO streetname VALUES('stoneglen'); INSERT INTO streetname VALUES('stonemarsh'); INSERT INTO streetname VALUES('stoney garden'); INSERT INTO streetname VALUES('stoney run'); INSERT INTO streetname VALUES('stoney valley'); INSERT INTO streetname VALUES('stoneykirk'); INSERT INTO streetname VALUES('stream bank'); INSERT INTO streetname VALUES('stream ridge'); INSERT INTO streetname VALUES('suburban'); INSERT INTO streetname VALUES('suffield'); INSERT INTO streetname VALUES('sugar creek'); INSERT INTO streetname VALUES('sugarberry'); INSERT INTO streetname VALUES('sugarstone'); INSERT INTO streetname VALUES('summer creek'); INSERT INTO streetname VALUES('summer valley'); INSERT INTO streetname VALUES('summercrest'); INSERT INTO streetname VALUES('summercroft'); INSERT INTO streetname VALUES('summerford'); INSERT INTO streetname VALUES('summergold'); INSERT INTO streetname VALUES('sunbeam'); INSERT INTO streetname VALUES('sunbridge'); INSERT INTO streetname VALUES('sunpath'); INSERT INTO streetname VALUES('sunset'); INSERT INTO streetname VALUES('sunset ridge'); INSERT INTO streetname VALUES('sunstone'); INSERT INTO streetname VALUES('suntrace'); INSERT INTO streetname VALUES('sunwalk'); INSERT INTO streetname VALUES('sutters hill'); INSERT INTO streetname VALUES('suttonview'); INSERT INTO streetname VALUES('swallow tail'); INSERT INTO streetname VALUES('swanston'); INSERT INTO streetname VALUES('sweet grove'); INSERT INTO streetname VALUES('sweet rose'); INSERT INTO streetname VALUES('sweetbriar ridge'); INSERT INTO streetname VALUES('sweetfield'); INSERT INTO streetname VALUES('sydney overlook'); INSERT INTO streetname VALUES('sylvan'); INSERT INTO streetname VALUES('symphony woods'); INSERT INTO streetname VALUES('tallia'); INSERT INTO streetname VALUES('tallu'); INSERT INTO streetname VALUES('talwyn'); INSERT INTO streetname VALUES('tanager'); INSERT INTO streetname VALUES('tanager park'); INSERT INTO streetname VALUES('tangley'); INSERT INTO streetname VALUES('taranasay'); INSERT INTO streetname VALUES('tarby'); INSERT INTO streetname VALUES('tarland'); INSERT INTO streetname VALUES('tarpway'); INSERT INTO streetname VALUES('tauten'); INSERT INTO streetname VALUES('taymouth'); INSERT INTO streetname VALUES('ten trees'); INSERT INTO streetname VALUES('terrace view'); INSERT INTO streetname VALUES('terrier'); INSERT INTO streetname VALUES('tesh'); INSERT INTO streetname VALUES('teton'); INSERT INTO streetname VALUES('tewkesbury'); INSERT INTO streetname VALUES('thelema'); INSERT INTO streetname VALUES('thistle bloom'); INSERT INTO streetname VALUES('thistledown'); INSERT INTO streetname VALUES('thomas ridge'); INSERT INTO streetname VALUES('thornbrook'); INSERT INTO streetname VALUES('tifton grass'); INSERT INTO streetname VALUES('tigerton'); INSERT INTO streetname VALUES('tomsie efird'); INSERT INTO streetname VALUES('tor'); INSERT INTO streetname VALUES('torphin'); INSERT INTO streetname VALUES('torrence'); INSERT INTO streetname VALUES('towering pine'); INSERT INTO streetname VALUES('towhee'); INSERT INTO streetname VALUES('toxaway'); INSERT INTO streetname VALUES('tracy glenn'); INSERT INTO streetname VALUES('tradition view'); INSERT INTO streetname VALUES('trailer'); INSERT INTO streetname VALUES('transport'); INSERT INTO streetname VALUES('trehurst'); INSERT INTO streetname VALUES('trexler'); INSERT INTO streetname VALUES('trillium fields'); INSERT INTO streetname VALUES('trimbach'); INSERT INTO streetname VALUES('tucker'); INSERT INTO streetname VALUES('tullamore'); INSERT INTO streetname VALUES('tullock creek'); INSERT INTO streetname VALUES('tunston'); INSERT INTO streetname VALUES('tupelo'); INSERT INTO streetname VALUES('turnabout'); INSERT INTO streetname VALUES('turney'); INSERT INTO streetname VALUES('turtle cross'); INSERT INTO streetname VALUES('turtleback'); INSERT INTO streetname VALUES('twelvestone'); INSERT INTO streetname VALUES('twin'); INSERT INTO streetname VALUES('twin brook'); INSERT INTO streetname VALUES('twin lakes'); INSERT INTO streetname VALUES('twisted pine'); INSERT INTO streetname VALUES('tyler finley'); INSERT INTO streetname VALUES('university station'); INSERT INTO streetname VALUES('uphill'); INSERT INTO streetname VALUES('valeview'); INSERT INTO streetname VALUES('valhalla'); INSERT INTO streetname VALUES('van'); INSERT INTO streetname VALUES('vance davis'); INSERT INTO streetname VALUES('vanhoy'); INSERT INTO streetname VALUES('veckman'); INSERT INTO streetname VALUES('victoria'); INSERT INTO streetname VALUES('victory'); INSERT INTO streetname VALUES('village glen'); INSERT INTO streetname VALUES('vireo'); INSERT INTO streetname VALUES('viscount'); INSERT INTO streetname VALUES('voeltz'); INSERT INTO streetname VALUES('wade e morgan'); INSERT INTO streetname VALUES('wake'); INSERT INTO streetname VALUES('wales'); INSERT INTO streetname VALUES('wallace ridge'); INSERT INTO streetname VALUES('waltham'); INSERT INTO streetname VALUES('wanamassa'); INSERT INTO streetname VALUES('warbler wood'); INSERT INTO streetname VALUES('washington'); INSERT INTO streetname VALUES('water'); INSERT INTO streetname VALUES('waterelm'); INSERT INTO streetname VALUES('waterford hills'); INSERT INTO streetname VALUES('waterford valley'); INSERT INTO streetname VALUES('waterloo'); INSERT INTO streetname VALUES('waterton leas'); INSERT INTO streetname VALUES('waverly lynn'); INSERT INTO streetname VALUES('waverlyglen'); INSERT INTO streetname VALUES('wayside'); INSERT INTO streetname VALUES('westbury lake'); INSERT INTO streetname VALUES('westray'); INSERT INTO streetname VALUES('whistlers chase'); INSERT INTO streetname VALUES('whistley green'); INSERT INTO streetname VALUES('whistling oak'); INSERT INTO streetname VALUES('whitcomb'); INSERT INTO streetname VALUES('white aspen'); INSERT INTO streetname VALUES('white cascade'); INSERT INTO streetname VALUES('white mist'); INSERT INTO streetname VALUES('white rock'); INSERT INTO streetname VALUES('white stag'); INSERT INTO streetname VALUES('whitegate'); INSERT INTO streetname VALUES('whitehill'); INSERT INTO streetname VALUES('whitetail'); INSERT INTO streetname VALUES('whitewood'); INSERT INTO streetname VALUES('wilburn park'); INSERT INTO streetname VALUES('wild garden'); INSERT INTO streetname VALUES('wild rose'); INSERT INTO streetname VALUES('wilkins terrace'); INSERT INTO streetname VALUES('william ficklen'); INSERT INTO streetname VALUES('wiltshire ridge'); INSERT INTO streetname VALUES('windchase'); INSERT INTO streetname VALUES('winding jordan'); INSERT INTO streetname VALUES('windy meadow'); INSERT INTO streetname VALUES('winghaven'); INSERT INTO streetname VALUES('wingmont'); INSERT INTO streetname VALUES('winslow'); INSERT INTO streetname VALUES('winter pine'); INSERT INTO streetname VALUES('winter view'); INSERT INTO streetname VALUES('wolf creek'); INSERT INTO streetname VALUES('wondering oak'); INSERT INTO streetname VALUES('woodard'); INSERT INTO streetname VALUES('woodfire'); INSERT INTO streetname VALUES('woodland commons'); INSERT INTO streetname VALUES('woodland hills'); INSERT INTO streetname VALUES('woodnotch'); INSERT INTO streetname VALUES('woodstone'); INSERT INTO streetname VALUES('worsley'); INSERT INTO streetname VALUES('wren creek'); INSERT INTO streetname VALUES('wrens nest'); INSERT INTO streetname VALUES('wrexham'); INSERT INTO streetname VALUES('wt harris'); INSERT INTO streetname VALUES('wylie meadow'); INSERT INTO streetname VALUES('wynborough'); INSERT INTO streetname VALUES('wynbrook'); INSERT INTO streetname VALUES('wyndham hill'); INSERT INTO streetname VALUES('yandem'); INSERT INTO streetname VALUES('yellow rose'); INSERT INTO streetname VALUES('yellow spaniel'); INSERT INTO streetname VALUES('yorkford'); INSERT INTO streetname VALUES('ziegler'); INSERT INTO streetname VALUES('zion renaissance'); SELECT count(*) FROM streetname; } } {1228} do_test fuzzer1-2.1 { execsql { SELECT n, distance FROM f2, streetname WHERE f2.word MATCH 'wersley' AND f2.distance<=150 AND f2.word=streetname.n } } {worsley 37} do_test fuzzer1-2.2 { execsql { SELECT n, distance FROM f2, streetname WHERE f2.word MATCH 'testledown' AND f2.distance<=150 AND f2.word=streetname.n } } {thistledown 103} do_test fuzzer1-2.3 { execsql { SELECT DISTINCT streetname.n FROM f2, streetname WHERE f2.word MATCH 'tayle' AND f2.distance<=200 AND streetname.n>=f2.word AND streetname.n<=(f2.word || x'F7BFBFBF') } } {steelewood tallia tallu talwyn taymouth thelema trailer {tyler finley}} finish_test |
Changes to test/index.test.
︙ | ︙ | |||
350 351 352 353 354 355 356 | ); } for {set i 1} {$i<=50} {incr i} { execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)" } set sqlite_search_count 0 concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | ); } for {set i 1} {$i<=50} {incr i} { execsql "INSERT INTO t3 VALUES('x${i}x',$i,0.$i)" } set sqlite_search_count 0 concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count } {0.1 2} integrity_check index-11.2 # Numeric strings should compare as if they were numbers. So even if the # strings are not character-by-character the same, if they represent the # same number they should compare equal to one another. Verify that this # is true in indices. |
︙ | ︙ |
Changes to test/indexedby.test.
︙ | ︙ | |||
150 151 152 153 154 155 156 | # 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 | | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | # 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>?) (~250000 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>?) (~25000 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) } |
︙ | ︙ |
Changes to test/like.test.
︙ | ︙ | |||
703 704 705 706 707 708 709 | 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 { | | | | | | | | | | | | | | 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 | 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. # |
︙ | ︙ | |||
815 816 817 818 819 820 821 | SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd nosort t11 *} do_test like-11.3 { queryplan { PRAGMA case_sensitive_like=OFF; CREATE INDEX t11b ON t11(b); | | | | | | | | | 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 | SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd nosort t11 *} do_test like-11.3 { queryplan { PRAGMA case_sensitive_like=OFF; CREATE INDEX t11b ON t11(b); SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11b} do_test like-11.4 { queryplan { PRAGMA case_sensitive_like=ON; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a; } } {abc abcd nosort t11 *} do_test like-11.5 { queryplan { PRAGMA case_sensitive_like=OFF; DROP INDEX t11b; CREATE INDEX t11bnc ON t11(b COLLATE nocase); SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.6 { queryplan { CREATE INDEX t11bb ON t11(b COLLATE binary); SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11bnc} do_test like-11.7 { queryplan { PRAGMA case_sensitive_like=ON; SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.8 { queryplan { PRAGMA case_sensitive_like=OFF; SELECT b FROM t11 WHERE b GLOB 'abc*' ORDER BY +a; } } {abc abcd sort {} t11bb} do_test like-11.9 { queryplan { CREATE INDEX t11cnc ON t11(c COLLATE nocase); CREATE INDEX t11cb ON t11(c COLLATE binary); SELECT c FROM t11 WHERE c LIKE 'abc%' ORDER BY +a; } } {abc abcd ABC ABCD sort {} t11cnc} do_test like-11.10 { queryplan { SELECT c FROM t11 WHERE c GLOB 'abc*' ORDER BY +a; } } {abc abcd sort {} t11cb} finish_test |
Changes to test/malloc_common.tcl.
︙ | ︙ | |||
113 114 115 116 117 118 119 | proc do_faultsim_test {name args} { global FAULTSIM set DEFAULT(-faults) [array names FAULTSIM] set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" | | | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | proc do_faultsim_test {name args} { global FAULTSIM set DEFAULT(-faults) [array names FAULTSIM] set DEFAULT(-prep) "" set DEFAULT(-body) "" set DEFAULT(-test) "" set DEFAULT(-install) "" set DEFAULT(-uninstall) "" 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" } |
︙ | ︙ |
Added test/mem5.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 | # 2011 March 9 # # 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 mem5 allocation subsystem. # set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable !mem5 { finish_test return } # The tests in this file configure the lookaside allocator after a # connection is opened. This will not work if there is any "presql" # configured (SQL run within the [sqlite3] wrapper in tester.tcl). if {[info exists ::G(perm:presql)]} { finish_test return } do_test mem5-1.1 { catch {db close} sqlite3_shutdown sqlite3_config_heap 25000000 0 sqlite3_config_lookaside 0 0 sqlite3_initialize } {SQLITE_OK} # try with min request size = 2^30 do_test mem5-1.2 { catch {db close} sqlite3_shutdown sqlite3_config_heap 1 1073741824 sqlite3_config_lookaside 0 0 sqlite3_initialize } {SQLITE_NOMEM} # try with min request size = 2^30+1 # previously this was causing the memsys5Log() func to infinitely loop. do_test mem5-1.3 { catch {db close} sqlite3_shutdown sqlite3_config_heap 1 1073741825 sqlite3_config_lookaside 0 0 sqlite3_initialize } {SQLITE_NOMEM} do_test mem5-1.4 { catch {db close} sqlite3_shutdown sqlite3_config_heap 0 0 sqlite3_config_lookaside 0 0 sqlite3_initialize } {SQLITE_OK} finish_test |
Changes to test/memdb.test.
︙ | ︙ | |||
359 360 361 362 363 364 365 | do_test memdb-6.15 { execsql { DELETE FROM t5 WHERE x>0; SELECT * FROM t5; } } {} | | > < | | < < < < < < | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | do_test memdb-6.15 { execsql { DELETE FROM t5 WHERE x>0; SELECT * FROM t5; } } {} ifcapable subquery&&vtab { do_test memdb-7.1 { register_wholenumber_module db execsql { CREATE TABLE t6(x); CREATE VIRTUAL TABLE nums USING wholenumber; INSERT INTO t6 SELECT value FROM nums WHERE value BETWEEN 1 AND 256; SELECT count(*) FROM (SELECT DISTINCT x FROM t6); } } {256} for {set i 1} {$i<=256} {incr i} { do_test memdb-7.2.$i { execsql "DELETE FROM t6 WHERE x=\ (SELECT x FROM t6 ORDER BY random() LIMIT 1)" |
︙ | ︙ |
Changes to test/minmax3.test.
︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | INSERT INTO t1 VALUES('1', 'I', 'one'); INSERT INTO t1 VALUES('2', 'IV', 'four'); INSERT INTO t1 VALUES('2', NULL, 'three'); INSERT INTO t1 VALUES('2', 'II', 'two'); INSERT INTO t1 VALUES('2', 'V', 'five'); INSERT INTO t1 VALUES('3', 'VI', 'six'); COMMIT; } } {} do_test minmax3-1.1.1 { # Linear scan. count { SELECT max(y) FROM t1 WHERE x = '2'; } } {V 5} do_test minmax3-1.1.2 { | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | INSERT INTO t1 VALUES('1', 'I', 'one'); INSERT INTO t1 VALUES('2', 'IV', 'four'); INSERT INTO t1 VALUES('2', NULL, 'three'); INSERT INTO t1 VALUES('2', 'II', 'two'); INSERT INTO t1 VALUES('2', 'V', 'five'); INSERT INTO t1 VALUES('3', 'VI', 'six'); COMMIT; PRAGMA automatic_index=OFF; } } {} do_test minmax3-1.1.1 { # Linear scan. count { SELECT max(y) FROM t1 WHERE x = '2'; } } {V 5} do_test minmax3-1.1.2 { |
︙ | ︙ |
Changes to test/multiplex.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl | | | | | | > > > > | 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 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ] 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_control() API. proc multiplex_set {db name chunk_size max_chunks} { global g_chunk_size global g_max_chunks set g_chunk_size [ expr (($chunk_size+($::SQLITE_MAX_PAGE_SIZE-1)) & ~($::SQLITE_MAX_PAGE_SIZE-1)) ] set g_max_chunks $max_chunks set rc [catch {sqlite3_multiplex_control $db $name chunk_size $chunk_size} msg] if { $rc==0 } { set rc [catch {sqlite3_multiplex_control $db $name max_chunks $max_chunks} msg] } list $msg } # 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} { |
︙ | ︙ | |||
66 67 68 69 70 71 72 | 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} | > | | > > > > > > > > | > > | > > > > > > > | > > > > > > > | > > > > > > | > > > > > > | > > > | | 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 | 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.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.9.2 { sqlite3 db test.db } {} do_test multiplex-1.9.3 { multiplex_set db main 32768 16 } {SQLITE_OK} do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_MISUSE} do_test multiplex-1.9.5 { multiplex_set db main -1 16 } {SQLITE_MISUSE} do_test multiplex-1.9.6 { multiplex_set db main 31 16 } {SQLITE_OK} do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_MISUSE} do_test multiplex-1.9.8 { multiplex_set db main 1073741824 1 } {SQLITE_OK} do_test multiplex-1.9.9 { db close } {} do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.10.2 { sqlite3 db test.db } {} do_test multiplex-1.10.3 { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 } {0} do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {1} do_test multiplex-1.10.5 { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 } {1} do_test multiplex-1.10.6 { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 } {0} do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {1} do_test multiplex-1.10.8 { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0} do_test multiplex-1.10.9 { db close } {} do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.11.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.11.2 { sqlite3 db test.db } {} do_test multiplex-1.11.3 { sqlite3_multiplex_control db main enable 0 } {SQLITE_OK} do_test multiplex-1.11.4 { sqlite3_multiplex_control db main enable 1 } {SQLITE_OK} do_test multiplex-1.11.5 { sqlite3_multiplex_control db main enable -1 } {SQLITE_OK} do_test multiplex-1.11.6 { db close } {} do_test multiplex-1.11.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.12.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.12.2 { sqlite3 db test.db } {} do_test multiplex-1.12.3 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} do_test multiplex-1.12.4 { lindex [ catchsql { SELECT multiplex_control(1, 1); } ] 0 } {0} do_test multiplex-1.12.5 { lindex [ catchsql { SELECT multiplex_control(1, -1); } ] 0 } {0} do_test multiplex-1.12.6 { db close } {} do_test multiplex-1.12.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.13.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.13.2 { sqlite3 db test.db } {} do_test multiplex-1.13.3 { lindex [ catchsql { SELECT multiplex_control(-1, 0); } ] 0 } {1} do_test multiplex-1.13.4 { lindex [ catchsql { SELECT multiplex_control(4, 1); } ] 0 } {1} do_test multiplex-1.13.6 { db close } {} do_test multiplex-1.13.7 { 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. # # multiplex-2.7.*: Disable/enable tests. # sqlite3_multiplex_initialize "" 1 multiplex_set db main 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; |
︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | } {} do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} do_test multiplex-2.3.1 { sqlite3 db2 test2.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 | > > | < | > > | 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 | } {} do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} do_test multiplex-2.3.1 { sqlite3 db2 test2.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 sqlite3 db test.db multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-2.5.2 { 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'); INSERT INTO t1 VALUES(6, randomblob($g_chunk_size)); INSERT INTO t1 VALUES(7, randomblob($g_chunk_size)); } } {} do_test multiplex-2.5.4 { db eval {SELECT * FROM t1 WHERE a=1} } {1 one} |
︙ | ︙ | |||
201 202 203 204 205 206 207 | 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 | > | < | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | 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 sqlite3 db test.db multiplex_set db main $sz 32 } {SQLITE_OK} do_test multiplex-2.6.2.$sz.$jmode { db eval { PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode |
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | 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 | > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 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 | db close sqlite3_multiplex_shutdown } {SQLITE_OK} } } do_test multiplex-2.7.1 { multiplex_delete test.db } {} do_test multiplex-2.7.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-2.7.3 { sqlite3 db test.db } {} do_test multiplex-2.7.4 { lindex [ catchsql { SELECT multiplex_control(2, 65536); } ] 0 } {0} do_test multiplex-2.7.5 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} do_test multiplex-2.7.6 { execsql { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randomblob(1000)); } } {} # verify only one file, and file size is less than chunks size do_test multiplex-2.7.7 { expr ([file size [multiplex_name test.db 0]] < 65536) } {1} do_test multiplex-2.7.8 { file exists [multiplex_name test.db 1] } {0} do_test multiplex-2.7.9 { execsql { INSERT INTO t1 VALUES(2, randomblob(65536)); } } {} # verify only one file, and file size exceeds chunks size do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1} do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] } {0} do_test multiplex-2.7.12 { db close } {} do_test multiplex-2.7.13 { 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 sqlite3 db test.db multiplex_set db main 32768 16 } {SQLITE_OK} do_test multiplex-3.1.2 { 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'); } |
︙ | ︙ | |||
337 338 339 340 341 342 343 | foreach db {db1a db2a db2b db1b} { catch { $db close } } } {} #------------------------------------------------------------------------- # sqlite3_multiplex_initialize "" 1 | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | foreach db {db1a db2a db2b db1b} { catch { $db close } } } {} #------------------------------------------------------------------------- # sqlite3_multiplex_initialize "" 1 multiplex_set db main 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] } |
︙ | ︙ | |||
399 400 401 402 403 404 405 | #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # sqlite3_multiplex_initialize "" 1 | | | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 do_faultsim_test multiplex-5.1 -prep { catch {db close} } -body { sqlite3 db test2.db } do_faultsim_test multiplex-5.2 -prep { |
︙ | ︙ | |||
444 445 446 447 448 449 450 | } {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 | | | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | } {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 db main 32768 16 } # test that mismatch filesize is detected # # Do not run this test if $::G(perm:presql) is set. If it is set, then the # expected IO error will occur within the Tcl [sqlite3] wrapper, not within # the first SQL statement executed below. This breaks the test case. |
︙ | ︙ | |||
469 470 471 472 473 474 475 | PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode do_test multiplex-5.6.2.$jmode { execsql { CREATE TABLE t1(a, b); | | | | | | < > | | | 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 | PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode do_test multiplex-5.6.2.$jmode { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(15000)); INSERT INTO t1 VALUES(2, randomblob(15000)); INSERT INTO t1 VALUES(3, randomblob(15000)); INSERT INTO t1 VALUES(4, randomblob(15000)); INSERT INTO t1 VALUES(5, randomblob(15000)); } db close sqlite3_multiplex_initialize "" 1 sqlite3 db test.db multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-5.6.3.$jmode { catchsql { INSERT INTO t1 VALUES(6, randomblob(15000)); } } {1 {disk I/O error}} do_test multiplex-5.6.4.$jmode { db close } {} } } catch { sqlite3_multiplex_shutdown } finish_test |
Added test/omitunique.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 | # 2011 March 10 # # 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_OMIT_UNIQUE_ENFORCEMENT # compiler option. # set testdir [file dirname $argv0] source $testdir/tester.tcl set uniq_enforced 1 ifcapable !unique_enforcement { set uniq_enforced 0 } # table with UNIQUE keyword on column do_test omitunique-1.1 { catchsql { CREATE TABLE t1(a TEXT UNIQUE); } } {0 {}} # table with UNIQUE clause on column do_test omitunique-1.2 { catchsql { CREATE TABLE t2(a TEXT, UNIQUE(a)); } } {0 {}} # table with UNIQUE index on column do_test omitunique-1.3 { catchsql { CREATE TABLE t3(a TEXT); CREATE UNIQUE INDEX t3a ON t3(a); } } {0 {}} # table with regular index on column do_test omitunique-1.4 { catchsql { CREATE TABLE t4(a TEXT); CREATE INDEX t4a ON t4(a); } } {0 {}} # table with no index on column do_test omitunique-1.5 { catchsql { CREATE TABLE t5(a TEXT); } } {0 {}} # run our tests using several table/index forms foreach {j tbl uniq cnt qp_est stat_enforce stat_omit } { 1 {t1} 1 1 1 {2 1} {9 9} 2 {t2} 1 1 1 {2 1} {9 9} 3 {t3} 1 1 1 {2 1} {9 9} 4 {t4} 0 9 10 {9 9} {9 9} 5 {t5} 0 9 100000 9 9 } { do_test omitunique-2.0.$j.1 { catchsql [ subst {INSERT INTO $tbl (a) VALUES('abc'); }] } {0 {}} do_test omitunique-2.0.$j.2 { catchsql [ subst {INSERT INTO $tbl (a) VALUES('123'); }] } {0 {}} # check various INSERT commands foreach {i cmd err} { 1 {INSERT} 1 2 {INSERT OR IGNORE} 0 3 {INSERT OR REPLACE} 0 4 {REPLACE} 0 5 {INSERT OR FAIL} 1 6 {INSERT OR ABORT} 1 7 {INSERT OR ROLLBACK} 1 } { ifcapable explain { set x [execsql [ subst { EXPLAIN $cmd INTO $tbl (a) VALUES('abc'); }]] ifcapable unique_enforcement { do_test omitunique-2.1.$j.$i.1 { regexp { IsUnique } $x } $uniq } ifcapable !unique_enforcement { do_test omitunique-2.1.$j.$i.1 { regexp { IsUnique } $x } {0} } } if { $uniq_enforced==0 || $uniq==0 || $err==0 } { set msg {0 {}} } { set msg {1 {column a is not unique}} } do_test omitunique-2.1.$j.$i.3 { catchsql [ subst {$cmd INTO $tbl (a) VALUES('abc'); }] } $msg } # end foreach cmd # check UPDATE command ifcapable explain { set x [execsql [ subst { EXPLAIN UPDATE $tbl SET a='abc'; }]] ifcapable unique_enforcement { do_test omitunique-2.2.$j.1 { regexp { IsUnique } $x } $uniq } ifcapable !unique_enforcement { do_test omitunique-2.2.$j.1 { regexp { IsUnique } $x } {0} } } if { $uniq_enforced==0 || $uniq==0 } { set msg {0 {}} } { set msg {1 {column a is not unique}} } do_test omitunique-2.2.$j.3 { catchsql [ subst { UPDATE $tbl SET a='abc'; }] } $msg # check record counts do_test omitunique-2.3.$j { execsql [ subst { SELECT count(*) FROM $tbl WHERE a='abc'; }] } $cnt # make sure the query planner row estimate not affected because of omit enforcement ifcapable explain { do_test omitunique-2.4.$j { set x [ execsql [ subst { EXPLAIN QUERY PLAN SELECT count(*) FROM $tbl WHERE a='abc'; }]] set y [ subst {~$qp_est row} ] regexp $y $x } {1} } # make sure we omit extra OP_Next opcodes when the UNIQUE constraints # mean there will only be a single pass through the code ifcapable explain { set x [execsql [ subst { EXPLAIN SELECT * FROM $tbl WHERE a='abc'; }]] do_test omitunique-2.5.$j { if { [ regexp { Next } $x ] } { expr { 0 } } { expr { 1 } } } $uniq } # make sure analyze index stats correct ifcapable analyze { if { $uniq_enforced==0 } { set msg [ list $stat_omit ] } { set msg [ list $stat_enforce ] } do_test omitunique-2.6.$j { execsql [ subst { ANALYZE $tbl; } ] execsql [ subst { SELECT stat FROM sqlite_stat1 WHERE tbl='$tbl'; } ] } $msg } } # end foreach tbl finish_test |
Added test/oserror.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 | # 2011 February 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing that error messages are logged via the # sqlite3_log() mechanism when certain errors are encountered in the # default unix or windows VFS modules. # set testdir [file dirname $argv0] source $testdir/tester.tcl if {$::tcl_platform(platform)!="unix"} { finish_test ; return } set ::testprefix oserror db close sqlite3_shutdown test_sqlite3_log xLog proc xLog {error_code msg} { if {[string match os_* $msg]} { lappend ::log $msg } } proc do_re_test {tn script expression} { uplevel do_test $tn [list [subst -nocommands { set res [eval { $script }] if {[regexp {$expression} [set res]]} { set {} {$expression} } else { set res } }]] [list $expression] } #-------------------------------------------------------------------------- # Tests oserror-1.* test failures in the open() system call. # # Test a failure in open() due to too many files. # # The xOpen() method of the unix VFS calls getcwd() as well as open(). # Although this does not appear to be documented in the man page, on OSX # a call to getcwd() may fail if there are no free file descriptors. So # an error may be reported for either open() or getcwd() here. # do_test 1.1.1 { set ::log [list] list [catch { for {set i 0} {$i < 2000} {incr i} { sqlite3 dbh_$i test.db -readonly 1 } } msg] $msg } {1 {unable to open database file}} do_test 1.1.2 { catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } } {1} do_re_test 1.1.3 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) (open|getcwd)\(.*test.db\) - } # Test a failure in open() due to the path being a directory. # do_test 1.2.1 { file mkdir dir.db set ::log [list] list [catch { sqlite3 dbh dir.db } msg] $msg } {1 {unable to open database file}} do_re_test 1.2.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*dir.db\) - } # Test a failure in open() due to the path not existing. # do_test 1.3.1 { set ::log [list] list [catch { sqlite3 dbh /x/y/z/test.db } msg] $msg } {1 {unable to open database file}} do_re_test 1.3.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*test.db\) - } # Test a failure in open() due to the path not existing. # do_test 1.4.1 { set ::log [list] list [catch { sqlite3 dbh /root/test.db } msg] $msg } {1 {unable to open database file}} do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c:\d*: \(\d+\) open\(.*test.db\) - } #-------------------------------------------------------------------------- # Tests oserror-1.* test failures in the unlink() system call. # do_test 2.1.1 { set ::log [list] file mkdir test.db-wal forcedelete test.db list [catch { sqlite3 dbh test.db execsql { SELECT * FROM sqlite_master } dbh } msg] $msg } {1 {disk I/O error}} do_re_test 2.1.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - } do_test 2.1.3 { catch { dbh close } forcedelete test.db-wal } {} test_syscall reset sqlite3_shutdown test_sqlite3_log sqlite3_initialize finish_test |
Changes to test/pager1.test.
︙ | ︙ | |||
1988 1989 1990 1991 1992 1993 1994 | do_test pager1-22.1.1 { faultsim_delete_and_reopen execsql { CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def'); INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } | | | 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 | do_test pager1-22.1.1 { faultsim_delete_and_reopen execsql { CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def'); INSERT INTO ko DEFAULT VALUES; } execsql { PRAGMA wal_checkpoint } } {0 -1 -1} do_test pager1-22.2.1 { testvfs tv -default 1 tv filter xSync tv script xSyncCb proc xSyncCb {args} {incr ::synccount} set ::synccount 0 sqlite3 db test.db |
︙ | ︙ |
Changes to test/pagerfault3.test.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # Create a database with page-size 2048 bytes that uses 2 pages. Populate # it so that if the page-size is changed to 1024 bytes and the db vacuumed, # the new db size is 3 pages. # do_test pagerfault3-pre1 { execsql { PRAGMA page_size = 2048; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(1200)); PRAGMA page_count; } } {2} do_test pagerfault3-pre2 { | > | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | # Create a database with page-size 2048 bytes that uses 2 pages. Populate # it so that if the page-size is changed to 1024 bytes and the db vacuumed, # the new db size is 3 pages. # do_test pagerfault3-pre1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA page_size = 2048; CREATE TABLE t1(x); INSERT INTO t1 VALUES(randomblob(1200)); PRAGMA page_count; } } {2} do_test pagerfault3-pre2 { |
︙ | ︙ |
Changes to test/permutations.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 | 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 | > > > | 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 | 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 fts3aux1.test fts3comp1.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 wal5.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 |
︙ | ︙ |
Changes to test/superlock.test.
︙ | ︙ | |||
56 57 58 59 60 61 62 | 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}} | | | | | | 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 | 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 } {0 {1 -1 -1}} 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 } {0 {1 -1 -1}} do_test 3.6 { unlock } {} do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 2 2} 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 } {0 {1 -1 -1}} do_test 4.6 { unlock } {} do_multiclient_test tn { proc busyhandler {x} { switch -- $x { 1 { sql1 "COMMIT" } |
︙ | ︙ | |||
112 113 114 115 116 117 118 | 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}} | | | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 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" } {0 {1 -1 -1}} do_test 5.$tn.7 { unlock } {} do_test 5.$tn.8 { sql1 { BEGIN ; SELECT * FROM t1 } sql2 { BEGIN ; INSERT INTO t1 VALUES(5, 6) } |
︙ | ︙ | |||
216 217 218 219 220 221 222 | do_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}} do_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}} db_swap test.db2 test.db do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}} do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}} | | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | do_catchsql_test 6.2 { SELECT * FROM t1 } {1 {no such table: t1}} do_catchsql_test 6.3 { SELECT * FROM t2 } {0 {a b}} db_swap test.db2 test.db do_catchsql_test 6.4 { SELECT * FROM t1 } {0 {1 2 3 4}} do_catchsql_test 6.5 { SELECT * FROM t2 } {1 {no such table: t2}} do_execsql_test 6.6 { PRAGMA wal_checkpoint } {0 0 0} db_swap test.db2 test.db do_catchsql_test 6.7 { SELECT * FROM t1 } {1 {no such table: t1}} do_catchsql_test 6.8 { SELECT * FROM t2 } {0 {a b}} db_swap test.db2 test.db do_catchsql_test 6.9 { SELECT * FROM t1 } {0 {1 2 3 4}} |
︙ | ︙ |
Added test/syscall.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 | # 2011 March 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/lock_common.tcl source $testdir/malloc_common.tcl if {[llength [info commands test_syscall]]==0} { finish_test return } if {[test_syscall defaultvfs] != "unix"} { finish_test return } set testprefix syscall #------------------------------------------------------------------------- # Tests for the xSetSystemCall method. # do_test 1.1.1 { list [catch { test_syscall reset open } msg] $msg } {0 {}} do_test 1.1.2 { list [catch { test_syscall reset nosuchcall } msg] $msg } {1 SQLITE_NOTFOUND} do_test 1.1.3 { list [catch { test_syscall reset open } msg] $msg } {0 {}} do_test 1.1.4 { list [catch { test_syscall reset ""} msg] $msg } {1 SQLITE_NOTFOUND} do_test 1.2 { test_syscall reset } {} do_test 1.3.1 { test_syscall install {open getcwd access} } {} do_test 1.3.2 { test_syscall reset } {} #------------------------------------------------------------------------- # Tests for the xGetSystemCall method. # do_test 2.1.1 { test_syscall exists open } 1 do_test 2.1.2 { test_syscall exists nosuchcall } 0 #------------------------------------------------------------------------- # Tests for the xNextSystemCall method. # foreach s { open close access getcwd stat fstat ftruncate fcntl read pread write pwrite fchmod fallocate pread64 pwrite64 } { if {[test_syscall exists $s]} {lappend syscall_list $s} } do_test 3.1 { lsort [test_syscall list] } [lsort $syscall_list] #------------------------------------------------------------------------- # This test verifies that if a call to open() fails and errno is set to # EINTR, the call is retried. If it succeeds, execution continues as if # nothing happened. # test_syscall reset forcedelete test.db2 do_execsql_test 4.1 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 2); ATTACH 'test.db2' AS aux; CREATE TABLE aux.t2(x, y); INSERT INTO t2 VALUES(3, 4); } db_save_and_close test_syscall install open foreach jrnl [list wal delete] { for {set i 1} {$i < 20} {incr i} { db_restore_and_reopen test_syscall fault $i 0 test_syscall errno open EINTR do_test 4.2.$jrnl.$i { sqlite3 db test.db execsql { ATTACH 'test.db2' AS aux } execsql "PRAGMA main.journal_mode = $jrnl" execsql "PRAGMA aux.journal_mode = $jrnl" execsql { BEGIN; INSERT INTO t1 VALUES(5, 6); INSERT INTO t2 VALUES(7, 8); COMMIT; } db close sqlite3 db test.db execsql { ATTACH 'test.db2' AS aux } execsql { SELECT * FROM t1; SELECT * FROM t2; } } {1 2 5 6 3 4 7 8} } } #------------------------------------------------------------------------- # This test verifies that closing database handles does not drop locks # held by other database handles in the same process on the same file. # # The os_unix.c module has to take precautions to prevent this as the # close() system call drops locks held by other file-descriptors on the # same file. From the Linux man page: # # close() closes a file descriptor, so that it no longer refers to any file # and may be reused. Any record locks (see fcntl(2)) held on the file it # was associated with, and owned by the process, are removed (regardless # of the file descriptor that was used to obtain the lock). # catch { db close } forcedelete test.db test.db2 do_multiclient_test tn { code1 { sqlite3 dbX1 test.db sqlite3 dbX2 test.db } do_test syscall-5.$tn.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); BEGIN; INSERT INTO t1 VALUES(3, 4); } } {} do_test syscall-5.$tn.2 { sql2 { SELECT * FROM t1 } } {1 2} do_test syscall-5.$tn.3 { csql2 { INSERT INTO t1 VALUES(5, 6) } } {1 {database is locked}} do_test syscall-5.$tn.4 { code1 { dbX1 close dbX2 close } } {} do_test syscall-5.$tn.5 { csql2 { INSERT INTO t1 VALUES(5, 6) } } {1 {database is locked}} do_test syscall-5.$tn.6 { sql1 { COMMIT } } {} do_test syscall-5.$tn.7 { csql2 { INSERT INTO t1 VALUES(5, 6) } } {0 {}} } catch {db close} do_test 6.1 { sqlite3 db1 test.db1 sqlite3 db2 test.db2 sqlite3 db3 test.db3 sqlite3 dbM "" db2 close db3 close dbM close db1 close } {} do_test 6.2 { sqlite3 db test.db execsql { PRAGMA temp_store = file; PRAGMA main.cache_size = 10; PRAGMA temp.cache_size = 10; CREATE TABLE temp.tt(a, b); INSERT INTO tt VALUES(randomblob(500), randomblob(600)); INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; INSERT INTO tt SELECT randomblob(500), randomblob(600) FROM tt; } db close } {} #------------------------------------------------------------------------- # Test that a database file a single byte in size is treated as an empty # file. Whereas a file 2 bytes or larger might be considered corrupt. # catch { db close } forcedelete test.db test.db2 proc create_db_file {nByte} { set fd [open test.db w] fconfigure $fd -translation binary -encoding binary puts -nonewline $fd [string range "xSQLite" 1 $nByte] close $fd } foreach {nByte res} { 1 {0 {}} 2 {1 {file is encrypted or is not a database}} 3 {1 {file is encrypted or is not a database}} } { do_test 7.$nByte { create_db_file $nByte list [catch { sqlite3 db test.db execsql { CREATE TABLE t1(a, b) } } msg] $msg } $res catch { db close } } #------------------------------------------------------------------------- # catch { db close } forcedelete test.db test.db2 do_test 8.1 { sqlite3 db test.db file_control_chunksize_test db main 4096 file size test.db } {0} foreach {tn hint size} { 1 1000 4096 2 1000 4096 3 3000 4096 4 4096 4096 5 4197 8192 } { do_test 8.2.$tn { file_control_sizehint_test db main $hint file size test.db } $size } test_syscall reset finish_test |
Added test/sysfault.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 | # 2011 March 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl if {[llength [info commands test_syscall]]==0} { finish_test return } set testprefix sysfault set FAULTSIM(vfsfault-transient) [list \ -injectinstall vfsfault_install \ -injectstart vfsfault_injectstart_t \ -injectstop vfsfault_injectstop \ -injecterrlist {} \ -injectuninstall {test_syscall uninstall} \ ] set FAULTSIM(vfsfault-persistent) [list \ -injectinstall vfsfault_install \ -injectstart vfsfault_injectstart_p \ -injectstop vfsfault_injectstop \ -injecterrlist {} \ -injectuninstall {test_syscall uninstall} \ ] proc vfsfault_injectstart_t {iFail} { test_syscall fault $iFail 0 } proc vfsfault_injectstart_p {iFail} { test_syscall fault $iFail 1 } proc vfsfault_injectstop {} { test_syscall fault } faultsim_save_and_close set open_and_write_body { sqlite3 db test.db db eval { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); PRAGMA journal_mode = WAL; INSERT INTO t1 VALUES(3, 4); SELECT * FROM t1; CREATE TEMP TABLE t2(x); INSERT INTO t2 VALUES('y'); } } proc vfsfault_install {} { test_syscall install {open getcwd} } do_faultsim_test 1 -faults vfsfault-* -prep { faultsim_restore } -body $open_and_write_body -test { faultsim_test_result {0 {wal 1 2 3 4}} \ {1 {unable to open database file}} \ {1 {attempt to write a readonly database}} } #------------------------------------------------------------------------- # Errors in the fstat() function when opening and writing a file. Cases # where fstat() fails and sets errno to ENOMEM and EOVERFLOW are both # tested. EOVERFLOW is interpreted as meaning that a file on disk is # too large to be opened by the OS. # foreach {tn errno errlist} { 1 ENOMEM {{disk I/O error}} 2 EOVERFLOW {{disk I/O error} {large file support is disabled}} } { proc vfsfault_install {} { test_syscall install fstat } set errs [list] foreach e $errlist { lappend errs [list 1 $e] } do_faultsim_test 1.2.$tn -faults vfsfault-* -prep { faultsim_restore } -body " test_syscall errno fstat $errno $open_and_write_body " -test " faultsim_test_result {0 {wal 1 2 3 4}} $errs " } #------------------------------------------------------------------------- # Various errors in locking functions. # foreach vfs {unix unix-excl} { foreach {tn errno errlist} { 1 EAGAIN {{database is locked} {disk I/O error}} 2 ETIMEDOUT {{database is locked} {disk I/O error}} 3 EBUSY {{database is locked} {disk I/O error}} 4 EINTR {{database is locked} {disk I/O error}} 5 ENOLCK {{database is locked} {disk I/O error}} 6 EACCES {{database is locked} {disk I/O error}} 7 EPERM {{access permission denied} {disk I/O error}} 8 EDEADLK {{disk I/O error}} 9 ENOMEM {{disk I/O error}} } { proc vfsfault_install {} { test_syscall install fcntl } set errs [list] foreach e $errlist { lappend errs [list 1 $e] } set body [string map [list %VFS% $vfs] { sqlite3 db test.db db eval { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } set fd [open test.db-journal w] puts $fd "hello world" close $fd sqlite3 db test.db -vfs %VFS% db eval { SELECT * FROM t1; } }] do_faultsim_test 1.3.$vfs.$tn -faults vfsfault-* -prep { faultsim_restore } -body " test_syscall errno fcntl $errno $body " -test " faultsim_test_result {0 {1 2}} $errs " } } #------------------------------------------------------------------------- # Check that a single EINTR error does not affect processing. # proc vfsfault_install {} { test_syscall reset test_syscall install {open ftruncate close read pread pread64 write fallocate} } forcedelete test.db test.db2 sqlite3 db test.db do_test 2.setup { execsql { CREATE TABLE t1(a, b, c, PRIMARY KEY(a)); INSERT INTO t1 VALUES('abc', 'def', 'ghi'); ATTACH 'test.db2' AS 'aux'; CREATE TABLE aux.t2(x); INSERT INTO t2 VALUES(1); } faultsim_save_and_close } {} do_faultsim_test 2.1 -faults vfsfault-transient -prep { catch { db close } faultsim_restore } -body { test_syscall errno open EINTR test_syscall errno ftruncate EINTR test_syscall errno close EINTR test_syscall errno read EINTR test_syscall errno pread EINTR test_syscall errno pread64 EINTR test_syscall errno write EINTR test_syscall errno fallocate EINTR sqlite3 db test.db file_control_chunksize_test db main 8192 set res [db eval { ATTACH 'test.db2' AS 'aux'; SELECT * FROM t1; PRAGMA journal_mode = truncate; BEGIN; INSERT INTO t1 VALUES('jkl', 'mno', 'pqr'); INSERT INTO t1 VALUES(randomblob(10000), 0, 0); UPDATE t2 SET x = 2; COMMIT; DELETE FROM t1 WHERE length(a)>3; SELECT * FROM t1; SELECT * FROM t2; }] db close set res } -test { faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} } do_faultsim_test 2.2 -faults vfsfault-* -prep { catch { db close } faultsim_restore } -body { sqlite3 db test.db set res [db eval { ATTACH 'test.db2' AS 'aux'; SELECT * FROM t1; PRAGMA journal_mode = truncate; BEGIN; INSERT INTO t1 VALUES('jkl', 'mno', 'pqr'); UPDATE t2 SET x = 2; COMMIT; SELECT * FROM t1; SELECT * FROM t2; }] db close set res } -test { faultsim_test_result {0 {abc def ghi truncate abc def ghi jkl mno pqr 2}} \ {1 {unable to open database file}} \ {1 {unable to open database: test.db2}} \ {1 {attempt to write a readonly database}} \ {1 {disk I/O error}} } #------------------------------------------------------------------------- proc vfsfault_install {} { test_syscall reset test_syscall install {fstat fallocate} } do_faultsim_test 3 -faults vfsfault-* -prep { faultsim_delete_and_reopen file_control_chunksize_test db main 8192 execsql { CREATE TABLE t1(a, b); BEGIN; SELECT * FROM t1; } } -body { test_syscall errno fstat EIO test_syscall errno fallocate EIO execsql { INSERT INTO t1 VALUES(randomblob(10000), randomblob(10000)); SELECT length(a) + length(b) FROM t1; COMMIT; } } -test { faultsim_test_result {0 20000} } finish_test |
Changes to test/tableapi.test.
︙ | ︙ | |||
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | } {0 2 100 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 x40 x41 x42 x43 x44 x45 x46 x47 x48 x49 x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 x61 x62 x63 x64 x65 x66 x67 x68 x69 x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 x80 x81 x82 x83 x84 x85 x86 x87 x88 x89 x90 x91 x92 x93 x94 x95 x96 x97 x98 x99 x100 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 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} ifcapable schema_pragmas { do_test tableapi-6.1 { sqlite3_get_table_printf $::dbx {PRAGMA user_version} {} } {0 1 1 user_version 0} } ifcapable memdebug { do_malloc_test tableapi-7 -sqlprep { DROP TABLE IF EXISTS t1; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); INSERT INTO t1 VALUES(3,4); INSERT INTO t1 SELECT a+4, b+4 FROM t1; INSERT INTO t1 SELECT a+8, b+8 FROM t1; } -tclbody { set r [sqlite3_get_table_printf db {SELECT rowid, a, b FROM t1} {}] if {[llength $r]<26} {error "out of memory"} } } | > > > > > > > > < < < < | 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 | } {0 2 100 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 x40 x41 x42 x43 x44 x45 x46 x47 x48 x49 x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 x61 x62 x63 x64 x65 x66 x67 x68 x69 x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 x80 x81 x82 x83 x84 x85 x86 x87 x88 x89 x90 x91 x92 x93 x94 x95 x96 x97 x98 x99 x100 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 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} ifcapable schema_pragmas { do_test tableapi-6.1 { sqlite3_get_table_printf $::dbx {PRAGMA user_version} {} } {0 1 1 user_version 0} } # do_malloc_test closes and deletes the usual db connections and files on # each iteration. $::dbx is a seperate connection, and on Windows, will # cause the file deletion of test.db to fail, so we move the close of $::dbx # up to here before the do_malloc_test. do_test tableapi-99.0 { sqlite3_close $::dbx } {SQLITE_OK} ifcapable memdebug { do_malloc_test tableapi-7 -sqlprep { DROP TABLE IF EXISTS t1; CREATE TABLE t1(a,b); INSERT INTO t1 VALUES(1,2); INSERT INTO t1 VALUES(3,4); INSERT INTO t1 SELECT a+4, b+4 FROM t1; INSERT INTO t1 SELECT a+8, b+8 FROM t1; } -tclbody { set r [sqlite3_get_table_printf db {SELECT rowid, a, b FROM t1} {}] if {[llength $r]<26} {error "out of memory"} } } finish_test |
Changes to test/threadtest3.c.
︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 | 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 }, | > | > > > | 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 | launch_thread(&err, &threads, dynamic_triggers_1, 0); join_all_threads(&err, &threads); print_and_free_err(&err); } #include "tt3_checkpoint.c" 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 }, { checkpoint_starvation_1, "checkpoint_starvation_1", 10000 }, { checkpoint_starvation_2, "checkpoint_starvation_2", 10000 }, }; int i; char *zTest = 0; int nTest = 0; int bTestfound = 0; int bPrefix = 0; |
︙ | ︙ |
Added test/tkt-752e1646fc.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 | # 2010 April 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. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests to verify that ticket [752e1646fc] has been # fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl do_test tkt-752e1646fc-1.1 { execsql { CREATE TABLE "test" ("letter" VARCHAR(1) PRIMARY KEY, "number" INTEGER NOT NULL); INSERT INTO "test" ("letter", "number") VALUES('b', 1); INSERT INTO "test" ("letter", "number") VALUES('a', 2); INSERT INTO "test" ("letter", "number") VALUES('c', 2); SELECT DISTINCT "number" FROM (SELECT "letter", "number" FROM "test" ORDER BY "letter", "number" LIMIT 1) AS "test"; } } {2} finish_test |
Added test/tkt-b72787b1.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 | # 2011 February 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 regression tests for SQLite library. # # This file implements tests to verify that ticket [b72787b1a7] has been # fixed. From the ticket: # # The sqlite3ExpirePreparedStatements routine marks all statements # as expired. This includes statements that are not expired. # # Steps to reproduce: # # * Prepare a statement (A) # * Alter the schema to invalidate cookie in A # * Prepare a statement (B) # * Run B and have A run as part of B # * A will find a bad cookie and cause *all* statements # to be expired including the currently running B by calling # sqlite3ExpirePreparedStatements # * When control returns to B it will then abort # # The bug is that sqlite3ExpirePreparedStatements expires all statements. # Note that B was prepared after the schema change and hence is perfectly # valid and then is marked as expired while running. # set testdir [file dirname $argv0] source $testdir/tester.tcl unset -nocomplain ::STMT proc runsql {} { db eval {CREATE TABLE IF NOT EXISTS t4(q)} sqlite3_step $::STMT set rc [sqlite3_column_int $::STMT 0] sqlite3_reset $::STMT return $rc } do_test tkt-b72787b1.1 { db eval { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); CREATE TABLE t2(y); INSERT INTO t2 SELECT x+2 FROM t1; INSERT INTO t2 SELECT x+4 FROM t1; } db func runsql ::runsql set DB [sqlite3_connection_pointer db] set sql {SELECT max(x) FROM t1} set ::STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL] # The runsql() call on the second row of the first query will # cause all $::STMT to hit an expired cookie. Prior to the fix # for [b72787b1a7, the bad cookie would expire all statements, including # the following compound SELECT, which would cause a fault when the # second SELECT was reached. After the fix, the current statement # continues to completion. db eval { SELECT CASE WHEN y=3 THEN y+100 WHEN y==4 THEN runsql()+200 ELSE 300+y END FROM t2 UNION ALL SELECT * FROM t1; } } {103 202 305 306 1 2} sqlite3_finalize $::STMT finish_test |
Added test/tkt-f7b4edec.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 | # 2011 March 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 implements tests to verify that ticket # [f7b4edece25c994857dc139207f55a53c8319fae] has been fixed. # set testdir [file dirname $argv0] source $testdir/tester.tcl # Open two database connections to the same database file in # shared cache mode. Create update hooks that will fire on # each connection. # db close set ::enable_shared_cache [sqlite3_enable_shared_cache 1] sqlite3 db1 test.db sqlite3 db2 test.db unset -nocomplain HOOKS set HOOKS {} proc update_hook {args} { lappend ::HOOKS $args } db1 update_hook update_hook db2 update_hook update_hook # Create a prepared statement # do_test tkt-f7b4edec-1 { execsql { CREATE TABLE t1(x, y); } db1 execsql { INSERT INTO t1 VALUES(1, 2) } db1 set ::HOOKS } {{INSERT main t1 1}} # In the second database connection cause the schema to be reparsed # without changing the schema cookie. # set HOOKS {} do_test tkt-f7b4edec-2 { execsql { BEGIN; DROP TABLE t1; CREATE TABLE t1(x, y); ROLLBACK; } db2 set ::HOOKS } {} # Rerun the prepared statement that was created prior to the # schema reparse. Verify that the update-hook gives the correct # output. # set HOOKS {} do_test tkt-f7b4edec-3 { execsql { INSERT INTO t1 VALUES(1, 2) } db1 set ::HOOKS } {{INSERT main t1 2}} # Be sure to restore the original shared-cache mode setting before # returning. # db1 close db2 close sqlite3_enable_shared_cache $::enable_shared_cache finish_test |
Changes to test/tkt3824.test.
︙ | ︙ | |||
68 69 70 71 72 73 74 | SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b, a; } } {5 9 sort} do_test tkt3824-2.3 { lsort [execsql_status { SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b; }] | | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b, a; } } {5 9 sort} do_test tkt3824-2.3 { lsort [execsql_status { SELECT a FROM t2 WHERE b=2 AND c IS NULL ORDER BY b; }] } {5 9 nosort} do_test tkt3824-3.1 { db eval { CREATE TABLE t3(x,y); INSERT INTO t3 SELECT a, b FROM t1; INSERT INTO t3 VALUES(234,567); CREATE UNIQUE INDEX t3y ON t3(y); |
︙ | ︙ |
Changes to test/trace2.test.
︙ | ︙ | |||
124 125 126 127 128 129 130 | INSERT INTO x1 VALUES('Wind chill values as low as -13'); } do_trace_test 2.2 { INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph'); } { "INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');" | | < > | 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 | INSERT INTO x1 VALUES('Wind chill values as low as -13'); } do_trace_test 2.2 { INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph'); } { "INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');" "-- INSERT INTO 'main'.'x1_content' VALUES(?,(?))" "-- REPLACE INTO 'main'.'x1_docsize' VALUES(?,?)" "-- SELECT value FROM 'main'.'x1_stat' WHERE id=0" "-- REPLACE INTO 'main'.'x1_stat' VALUES(0,?)" "-- SELECT (SELECT max(idx) FROM 'main'.'x1_segdir' WHERE level = ?) + 1" "-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)" "-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" } do_trace_test 2.3 { INSERT INTO x1(x1) VALUES('optimize'); } { "INSERT INTO x1(x1) VALUES('optimize');" "-- SELECT idx, start_block, leaves_end_block, end_block, root FROM 'main'.'x1_segdir' ORDER BY level DESC, idx ASC" "-- SELECT count(*), max(level) FROM 'main'.'x1_segdir'" "-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)" "-- DELETE FROM 'main'.'x1_segdir'" "-- INSERT INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)" } } finish_test |
Added test/tt3_checkpoint.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 | /* ** 2001 September 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. ** ************************************************************************* ** This file is part of the test program "threadtest3". Despite being a C ** file it is not compiled separately, but included by threadtest3.c using ** the #include directive normally used with header files. ** ** This file contains the implementation of test cases: ** ** checkpoint_starvation_1 ** checkpoint_starvation_2 */ /* ** Both test cases involve 1 writer/checkpointer thread and N reader threads. ** ** Each reader thread performs a series of read transactions, one after ** another. Each read transaction lasts for 100 ms. ** ** The writer writes transactions as fast as possible. It uses a callback ** registered with sqlite3_wal_hook() to try to keep the WAL-size limited to ** around 50 pages. ** ** In test case checkpoint_starvation_1, the auto-checkpoint uses ** SQLITE_CHECKPOINT_PASSIVE. In checkpoint_starvation_2, it uses RESTART. ** The expectation is that in the first case the WAL file will grow very ** large, and in the second will be limited to the 50 pages or thereabouts. ** However, the overall transaction throughput will be lower for ** checkpoint_starvation_2, as every checkpoint will block for up to 200 ms ** waiting for readers to clear. */ /* Frame limit used by the WAL hook for these tests. */ #define CHECKPOINT_STARVATION_FRAMELIMIT 50 /* Duration in ms of each read transaction */ #define CHECKPOINT_STARVATION_READMS 100 struct CheckpointStarvationCtx { int eMode; int nMaxFrame; }; typedef struct CheckpointStarvationCtx CheckpointStarvationCtx; static int checkpoint_starvation_walhook( void *pCtx, sqlite3 *db, const char *zDb, int nFrame ){ CheckpointStarvationCtx *p = (CheckpointStarvationCtx *)pCtx; if( nFrame>p->nMaxFrame ){ p->nMaxFrame = nFrame; } if( nFrame>=CHECKPOINT_STARVATION_FRAMELIMIT ){ sqlite3_wal_checkpoint_v2(db, zDb, p->eMode, 0, 0); } return SQLITE_OK; } static char *checkpoint_starvation_reader(int iTid, int iArg){ Error err = {0}; Sqlite db = {0}; opendb(&err, &db, "test.db", 0); while( !timetostop(&err) ){ i64 iCount1, iCount2; sql_script(&err, &db, "BEGIN"); iCount1 = execsql_i64(&err, &db, "SELECT count(x) FROM t1"); usleep(CHECKPOINT_STARVATION_READMS*1000); iCount2 = execsql_i64(&err, &db, "SELECT count(x) FROM t1"); sql_script(&err, &db, "COMMIT"); if( iCount1!=iCount2 ){ test_error(&err, "Isolation failure - %lld %lld", iCount1, iCount2); } } closedb(&err, &db); print_and_free_err(&err); return 0; } static void checkpoint_starvation_main(int nMs, CheckpointStarvationCtx *p){ Error err = {0}; Sqlite db = {0}; Threadset threads = {0}; int nInsert = 0; int i; opendb(&err, &db, "test.db", 1); sql_script(&err, &db, "PRAGMA page_size = 1024;" "PRAGMA journal_mode = WAL;" "CREATE TABLE t1(x);" ); setstoptime(&err, nMs); for(i=0; i<4; i++){ launch_thread(&err, &threads, checkpoint_starvation_reader, 0); usleep(CHECKPOINT_STARVATION_READMS*1000/4); } sqlite3_wal_hook(db.db, checkpoint_starvation_walhook, (void *)p); while( !timetostop(&err) ){ sql_script(&err, &db, "INSERT INTO t1 VALUES(randomblob(1200))"); nInsert++; } printf(" Checkpoint mode : %s\n", p->eMode==SQLITE_CHECKPOINT_PASSIVE ? "PASSIVE" : "RESTART" ); printf(" Peak WAL : %d frames\n", p->nMaxFrame); printf(" Transaction count: %d transactions\n", nInsert); join_all_threads(&err, &threads); closedb(&err, &db); print_and_free_err(&err); } static void checkpoint_starvation_1(int nMs){ Error err = {0}; CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_PASSIVE, 0 }; checkpoint_starvation_main(nMs, &ctx); if( ctx.nMaxFrame<(CHECKPOINT_STARVATION_FRAMELIMIT*10) ){ test_error(&err, "WAL failed to grow - %d frames", ctx.nMaxFrame); } print_and_free_err(&err); } static void checkpoint_starvation_2(int nMs){ Error err = {0}; CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_RESTART, 0 }; checkpoint_starvation_main(nMs, &ctx); if( ctx.nMaxFrame>CHECKPOINT_STARVATION_FRAMELIMIT+10 ){ test_error(&err, "WAL grew too large - %d frames", ctx.nMaxFrame); } print_and_free_err(&err); } |
Added test/unixexcl.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 | # 2011 March 30 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for the "unix-excl" VFS module (part of # os_unix.c). # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl if {$::tcl_platform(platform)!="unix" || [info commands test_syscall]==""} { finish_test return } set testprefix unixexcl # Test that when using VFS "unix-excl", the first time the database is read # a process-wide exclusive lock is taken on it. This means other connections # within the process may still access the db normally, but connections from # outside the process cannot. # do_multiclient_test tn { do_test unixexcl-1.$tn.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('hello', 'world'); } } {} do_test unixexcl-1.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} do_test unixexcl-1.$tn.3 { code1 { db close sqlite3 db test.db -vfs unix-excl db eval { SELECT * FROM t1 } } } {hello world} if {$tn==1} { do_test unixexcl-1.$tn.4.multiproc { csql2 { SELECT * FROM t1 } } {1 {database is locked}} } else { do_test unixexcl-1.$tn.4.singleproc { csql2 { SELECT * FROM t1 } } {0 {hello world}} } } # Test that when using VFS "unix-excl", if a file is opened in read-only mode # the behaviour is the same as if VFS "unix" were used. # do_multiclient_test tn { do_test unixexcl-2.$tn.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('hello', 'world'); } } {} do_test unixexcl-2.$tn.2 { sql2 { SELECT * FROM t1 } } {hello world} do_test unixexcl-2.$tn.3 { code1 { db close sqlite3 db test.db -readonly yes -vfs unix-excl db eval { SELECT * FROM t1 } } } {hello world} do_test unixexcl-2.$tn.4 { csql2 { SELECT * FROM t1 } } {0 {hello world}} } finish_test |
Changes to test/vacuum2.test.
︙ | ︙ | |||
11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # This file implements regression tests for SQLite library. The # focus of this file is testing the VACUUM statement. # # $Id: vacuum2.test,v 1.10 2009/02/18 20:31:18 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # If the VACUUM statement is disabled in the current build, skip all # the tests in this file. # ifcapable {!vacuum||!autoinc} { finish_test return | > > > > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # This file implements regression tests for SQLite library. The # focus of this file is testing the VACUUM statement. # # $Id: vacuum2.test,v 1.10 2009/02/18 20:31:18 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.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 # If the VACUUM statement is disabled in the current build, skip all # the tests in this file. # ifcapable {!vacuum||!autoinc} { finish_test return |
︙ | ︙ |
Changes to test/wal.test.
︙ | ︙ | |||
287 288 289 290 291 292 293 | PRAGMA wal_checkpoint; BEGIN; INSERT INTO t2 VALUES('w', 'x'); SAVEPOINT save; INSERT INTO t2 VALUES('y', 'z'); ROLLBACK TO save; COMMIT; | < > | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | PRAGMA wal_checkpoint; BEGIN; INSERT INTO t2 VALUES('w', 'x'); SAVEPOINT save; INSERT INTO t2 VALUES('y', 'z'); ROLLBACK TO save; COMMIT; } execsql { SELECT * FROM t2 } } {w x} reopen_db do_test wal-5.1 { execsql { CREATE TEMP TABLE t2(a, b); |
︙ | ︙ | |||
463 464 465 466 467 468 469 470 471 472 473 474 475 476 | # do_multiclient_test tn { # Initialize the database schema and contents. # do_test wal-10.$tn.1 { execsql { PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); SELECT * FROM t1; } } {wal 1 2} | > | 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | # do_multiclient_test tn { # Initialize the database schema and contents. # do_test wal-10.$tn.1 { execsql { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); SELECT * FROM t1; } } {wal 1 2} |
︙ | ︙ | |||
542 543 544 545 546 547 548 | # checkpointing the database. But not from writing to it. # do_test wal-10.$tn.11 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10} do_test wal-10.$tn.12 { catchsql { PRAGMA wal_checkpoint } | | | | | | 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 | # checkpointing the database. But not from writing to it. # do_test wal-10.$tn.11 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10} do_test wal-10.$tn.12 { catchsql { PRAGMA wal_checkpoint } } {0 {0 13 13}} ;# Reader no longer block checkpoints do_test wal-10.$tn.13 { execsql { INSERT INTO t1 VALUES(11, 12) } sql2 {SELECT * FROM t1} } {1 2 3 4 5 6 7 8 9 10} # Writers do not block checkpoints any more either. # do_test wal-10.$tn.14 { catchsql { PRAGMA wal_checkpoint } } {0 {0 15 13}} # The following series of test cases used to verify another blocking # case in WAL - a case which no longer blocks. # do_test wal-10.$tn.15 { sql2 { COMMIT; BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.16 { catchsql { PRAGMA wal_checkpoint } } {0 {0 15 15}} do_test wal-10.$tn.17 { execsql { PRAGMA wal_checkpoint } } {0 15 15} do_test wal-10.$tn.18 { sql3 { BEGIN; SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-10.$tn.19 { catchsql { INSERT INTO t1 VALUES(13, 14) } } {0 {}} do_test wal-10.$tn.20 { |
︙ | ︙ | |||
588 589 590 591 592 593 594 | } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} # Another series of tests that used to demonstrate blocking behavior # but which now work. # do_test wal-10.$tn.23 { execsql { PRAGMA wal_checkpoint } | | | | | | 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 | } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} # Another series of tests that used to demonstrate blocking behavior # but which now work. # do_test wal-10.$tn.23 { execsql { PRAGMA wal_checkpoint } } {0 17 17} do_test wal-10.$tn.24 { sql2 { BEGIN; SELECT * FROM t1; } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14} do_test wal-10.$tn.25 { execsql { PRAGMA wal_checkpoint } } {0 17 17} do_test wal-10.$tn.26 { catchsql { INSERT INTO t1 VALUES(15, 16) } } {0 {}} do_test wal-10.$tn.27 { sql3 { INSERT INTO t1 VALUES(17, 18) } } {} do_test wal-10.$tn.28 { code3 { set ::STMT [sqlite3_prepare db3 "SELECT * FROM t1" -1 TAIL] sqlite3_step $::STMT } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18} do_test wal-10.$tn.29 { execsql { INSERT INTO t1 VALUES(19, 20) } catchsql { PRAGMA wal_checkpoint } } {0 {0 6 0}} do_test wal-10.$tn.30 { code3 { sqlite3_finalize $::STMT } execsql { PRAGMA wal_checkpoint } } {0 6 0} # At one point, if a reader failed to upgrade to a writer because it # was reading an old snapshot, the write-locks were not being released. # Test that this bug has been fixed. # do_test wal-10.$tn.31 { sql2 COMMIT |
︙ | ︙ | |||
654 655 656 657 658 659 660 | sql2 { BEGIN; SELECT * FROM t1; } } {a b c d} do_test wal-10.$tn.36 { catchsql { PRAGMA wal_checkpoint } | | | | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | sql2 { BEGIN; SELECT * FROM t1; } } {a b c d} do_test wal-10.$tn.36 { catchsql { PRAGMA wal_checkpoint } } {0 {0 16 16}} do_test wal-10.$tn.36 { sql3 { INSERT INTO t1 VALUES('e', 'f') } sql2 { SELECT * FROM t1 } } {a b c d} do_test wal-10.$tn.37 { sql2 COMMIT execsql { PRAGMA wal_checkpoint } } {0 18 18} } #------------------------------------------------------------------------- # This block of tests, wal-11.*, test that nothing goes terribly wrong # if frames must be written to the log file before a transaction is # committed (in order to free up memory). # |
︙ | ︙ | |||
797 798 799 800 801 802 803 | execsql { PRAGMA wal_checkpoint; UPDATE t2 SET y = 2 WHERE x = 'B'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 1 WHERE x = 'A'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 0 WHERE x = 'A'; | < > | 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 | execsql { PRAGMA wal_checkpoint; UPDATE t2 SET y = 2 WHERE x = 'B'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 1 WHERE x = 'A'; PRAGMA wal_checkpoint; UPDATE t1 SET y = 0 WHERE x = 'A'; } execsql { SELECT * FROM t2 } } {B 2} do_test wal-12.6 { file copy -force test.db test2.db file copy -force test.db-wal test2.db-wal sqlite3_wal db2 test2.db execsql { SELECT * FROM t2 } db2 } {B 2} |
︙ | ︙ | |||
845 846 847 848 849 850 851 852 853 854 855 856 857 858 | set fd [open test.db-wal w] seek $fd [expr 200*1024*1024] puts $fd "" close $fd sqlite3 db test.db execsql { SELECT * FROM t2 } } {B 2} do_test wal-13.1.3 { db close file exists test.db-wal } {0} do_test wal-13.2.1 { sqlite3 db test.db | > | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | set fd [open test.db-wal w] seek $fd [expr 200*1024*1024] puts $fd "" close $fd sqlite3 db test.db execsql { SELECT * FROM t2 } } {B 2} breakpoint do_test wal-13.1.3 { db close file exists test.db-wal } {0} do_test wal-13.2.1 { sqlite3 db test.db |
︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 | # The following block of tests - wal-16.* - test that if a NULL pointer or # an empty string is passed as the second argument of the wal_checkpoint() # API, an attempt is made to checkpoint all attached databases. # foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} { 1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1 2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1 | | | | | | 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 | # The following block of tests - wal-16.* - test that if a NULL pointer or # an empty string is passed as the second argument of the wal_checkpoint() # API, an attempt is made to checkpoint all attached databases. # foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} { 1 {sqlite3_wal_checkpoint db} SQLITE_OK 1 1 2 {sqlite3_wal_checkpoint db ""} SQLITE_OK 1 1 3 {db eval "PRAGMA wal_checkpoint"} {0 10 10} 1 1 4 {sqlite3_wal_checkpoint db main} SQLITE_OK 1 0 5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1 6 {sqlite3_wal_checkpoint db temp} SQLITE_OK 0 0 7 {db eval "PRAGMA main.wal_checkpoint"} {0 10 10} 1 0 8 {db eval "PRAGMA aux.wal_checkpoint"} {0 16 16} 0 1 9 {db eval "PRAGMA temp.wal_checkpoint"} {0 -1 -1} 0 0 } { do_test wal-16.$tn.1 { file delete -force test2.db test2.db-wal test2.db-journal file delete -force test.db test.db-wal test.db-journal sqlite3 db test.db execsql { |
︙ | ︙ | |||
1400 1401 1402 1403 1404 1405 1406 | INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ } } } } {0} do_test wal-20.3 { close $::buddy | < | | < | 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 | INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */ } } } } {0} do_test wal-20.3 { close $::buddy execsql { PRAGMA wal_checkpoint } execsql { SELECT count(*) FROM t1 } } {16384} do_test wal-20.4 { db close sqlite3 db test.db execsql { SELECT count(*) FROM t1 } } {16384} integrity_check wal-20.5 |
︙ | ︙ | |||
1437 1438 1439 1440 1441 1442 1443 | PRAGMA cache_size = 10; PRAGMA wal_checkpoint; BEGIN; SAVEPOINT s; INSERT INTO t1 SELECT randomblob(900), randomblob(900) FROM t1; ROLLBACK TO s; COMMIT; | < > | 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 | PRAGMA cache_size = 10; PRAGMA wal_checkpoint; BEGIN; SAVEPOINT s; INSERT INTO t1 SELECT randomblob(900), randomblob(900) FROM t1; ROLLBACK TO s; COMMIT; } execsql { SELECT * FROM t1 } } {1 2 3 4 5 6 7 8 9 10 11 12} do_test wal-21.3 { execsql { PRAGMA integrity_check } } {ok} #------------------------------------------------------------------------- # Test reading and writing of databases with different page-sizes. |
︙ | ︙ |
Changes to test/wal2.test.
︙ | ︙ | |||
343 344 345 346 347 348 349 350 351 352 353 354 | #------------------------------------------------------------------------- # Test that a database connection using a VFS that does not support the # xShmXXX interfaces cannot open a WAL database. # do_test wal2-4.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE data(x); INSERT INTO data VALUES('need xShmOpen to see this'); PRAGMA wal_checkpoint; } | > | | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 | #------------------------------------------------------------------------- # Test that a database connection using a VFS that does not support the # xShmXXX interfaces cannot open a WAL database. # do_test wal2-4.1 { sqlite3 db test.db execsql { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE data(x); INSERT INTO data VALUES('need xShmOpen to see this'); PRAGMA wal_checkpoint; } } {wal 0 5 5} do_test wal2-4.2 { db close testvfs tvfs -noshm 1 sqlite3 db test.db -vfs tvfs catchsql { SELECT * FROM data } } {1 {unable to open database file}} do_test wal2-4.3 { |
︙ | ︙ | |||
622 623 624 625 626 627 628 629 630 631 632 633 634 635 | {4 1 lock shared} {0 1 lock exclusive} {0 1 unlock exclusive} {4 1 unlock shared} } foreach {tn sql res expected_locks} { 2 { PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Leonard'); INSERT INTO t1 VALUES('Arthur'); COMMIT; } {wal} { | > | 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | {4 1 lock shared} {0 1 lock exclusive} {0 1 unlock exclusive} {4 1 unlock shared} } foreach {tn sql res expected_locks} { 2 { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; BEGIN; CREATE TABLE t1(x); INSERT INTO t1 VALUES('Leonard'); INSERT INTO t1 VALUES('Arthur'); COMMIT; } {wal} { |
︙ | ︙ | |||
707 708 709 710 711 712 713 714 715 716 717 718 719 720 | db close tvfs delete do_test wal2-6.5.1 { sqlite3 db test.db execsql { PRAGMA journal_mode = wal; PRAGMA locking_mode = exclusive; CREATE TABLE t2(a, b); PRAGMA wal_checkpoint; INSERT INTO t2 VALUES('I', 'II'); PRAGMA journal_mode; } | > | | | 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 | db close tvfs delete do_test wal2-6.5.1 { sqlite3 db test.db execsql { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = wal; PRAGMA locking_mode = exclusive; CREATE TABLE t2(a, b); PRAGMA wal_checkpoint; INSERT INTO t2 VALUES('I', 'II'); PRAGMA journal_mode; } } {wal exclusive 0 3 3 wal} do_test wal2-6.5.2 { execsql { PRAGMA locking_mode = normal; INSERT INTO t2 VALUES('III', 'IV'); PRAGMA locking_mode = exclusive; SELECT * FROM t2; } } {normal exclusive I II III IV} do_test wal2-6.5.3 { execsql { PRAGMA wal_checkpoint } } {0 4 4} db close proc lock_control {method filename handle spec} { foreach {start n op type} $spec break if {$op == "lock"} { return SQLITE_IOERR } return SQLITE_OK } |
︙ | ︙ | |||
809 810 811 812 813 814 815 816 817 | execsql { PRAGMA auto_vacuum=OFF; PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(8188*1020)); CREATE TABLE t2(y); } execsql { | > < | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | execsql { PRAGMA auto_vacuum=OFF; PRAGMA page_size = 1024; PRAGMA journal_mode = WAL; CREATE TABLE t1(x); INSERT INTO t1 VALUES(zeroblob(8188*1020)); CREATE TABLE t2(y); PRAGMA wal_checkpoint; } execsql { SELECT rootpage>=8192 FROM sqlite_master WHERE tbl_name = 't2'; } } {1} do_test wal2-8.1.3 { execsql { PRAGMA cache_size = 10; CREATE TABLE t3(z); |
︙ | ︙ | |||
1154 1155 1156 1157 1158 1159 1160 | set b(1,1) {0 {}} do_test wal2-13.$tn.4 { catchsql { INSERT INTO t1 DEFAULT VALUES } } $b($can_read,$can_write) } catch { db close } } | | > | | 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 | set b(1,1) {0 {}} 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 {PRAGMA auto_vacuum = 0} 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 0 5 5 0 2 2} 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 |
︙ | ︙ |
Changes to test/wal3.test.
︙ | ︙ | |||
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | # obtain a different read-lock. # catch {db close} testvfs T -default 1 do_test wal3-6.1.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('o', 't'); INSERT INTO t1 VALUES('t', 'f'); } } {} do_test wal3-6.1.2 { sqlite3 db2 test.db sqlite3 db3 test.db execsql { BEGIN ; SELECT * FROM t1 } db3 } {o t t f} do_test wal3-6.1.3 { execsql { PRAGMA wal_checkpoint } db2 | > | | 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 | # obtain a different read-lock. # catch {db close} testvfs T -default 1 do_test wal3-6.1.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db execsql { PRAGMA auto_vacuum = off } execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('o', 't'); INSERT INTO t1 VALUES('t', 'f'); } } {} do_test wal3-6.1.2 { sqlite3 db2 test.db sqlite3 db3 test.db execsql { BEGIN ; SELECT * FROM t1 } db3 } {o t t f} do_test wal3-6.1.3 { execsql { PRAGMA wal_checkpoint } db2 } {0 7 7} # At this point the log file has been fully checkpointed. However, # connection [db3] holds a lock that prevents the log from being wrapped. # Test case 3.6.1.4 has [db] attempt a read-lock on aReadMark[0]. But # as it is obtaining the lock, [db2] appends to the log file. # T filter xShmLock |
︙ | ︙ | |||
494 495 496 497 498 499 500 501 502 503 504 505 506 507 | db2 close db close do_test wal3-6.2.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('h', 'h'); INSERT INTO t1 VALUES('l', 'b'); } } {} | > | 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 | db2 close db close do_test wal3-6.2.1 { file delete -force test.db test.db-journal test.db wal sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA auto_vacuum = off } execsql { PRAGMA journal_mode = WAL } execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES('h', 'h'); INSERT INTO t1 VALUES('l', 'b'); } } {} |
︙ | ︙ | |||
515 516 517 518 519 520 521 | BEGIN; SELECT * FROM t1; }] } } do_test wal3-6.2.2 { execsql { PRAGMA wal_checkpoint } | | | 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 | BEGIN; SELECT * FROM t1; }] } } do_test wal3-6.2.2 { execsql { PRAGMA wal_checkpoint } } {0 7 7} do_test wal3-6.2.3 { set ::R } {h h l b} do_test wal3-6.2.4 { set sz1 [file size test.db-wal] execsql { INSERT INTO t1 VALUES('b', 'c'); } set sz2 [file size test.db-wal] |
︙ | ︙ | |||
617 618 619 620 621 622 623 624 625 626 627 628 629 630 | #------------------------------------------------------------------------- # do_test wal3-8.1 { file delete -force test.db test.db-journal test.db wal .test.db-conch sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA journal_mode = WAL; CREATE TABLE b(c); INSERT INTO b VALUES('Tehran'); INSERT INTO b VALUES('Qom'); INSERT INTO b VALUES('Markazi'); PRAGMA wal_checkpoint; } | > | | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | #------------------------------------------------------------------------- # do_test wal3-8.1 { file delete -force test.db test.db-journal test.db wal .test.db-conch sqlite3 db test.db sqlite3 db2 test.db execsql { PRAGMA auto_vacuum = off; PRAGMA journal_mode = WAL; CREATE TABLE b(c); INSERT INTO b VALUES('Tehran'); INSERT INTO b VALUES('Qom'); INSERT INTO b VALUES('Markazi'); PRAGMA wal_checkpoint; } } {wal 0 9 9} do_test wal3-8.2 { execsql { SELECT * FROM b } } {Tehran Qom Markazi} do_test wal3-8.3 { db eval { SELECT * FROM b } { db eval { INSERT INTO b VALUES('Qazvin') } set r [db2 eval { SELECT * FROM b }] |
︙ | ︙ |
Added test/wal5.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 | # 2010 April 13 # # 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 operation of "blocking-checkpoint" # operations. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl ifcapable !wal {finish_test ; return } set testprefix wal5 proc db_page_count {{file test.db}} { expr [file size $file] / 1024 } proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 } # A checkpoint may be requested either using the C API or by executing # an SQL PRAGMA command. To test both methods, all tests in this file are # run twice - once using each method to request checkpoints. # foreach {testprefix do_wal_checkpoint} { wal5-pragma { proc do_wal_checkpoint { dbhandle args } { array set a $args foreach key [array names a] { if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } } set sql "PRAGMA " if {[info exists a(-db)]} { append sql "$a(-db)." } append sql "wal_checkpoint" if {[info exists a(-mode)]} { append sql " = $a(-mode)" } uplevel [list $dbhandle eval $sql] } } wal5-capi { proc do_wal_checkpoint { dbhandle args } { set a(-mode) passive array set a $args foreach key [array names a] { if {[lsearch {-mode -db} $key]<0} { error "unknown switch: $key" } } if {$a(-mode)!="restart" && $a(-mode)!="full"} { set a(-mode) passive } set cmd [list sqlite3_wal_checkpoint_v2 $dbhandle $a(-mode)] if {[info exists a(-db)]} { lappend sql $a(-db) } uplevel $cmd } } } { eval $do_wal_checkpoint do_multiclient_test tn { set ::nBusyHandler 0 set ::busy_handler_script "" proc busyhandler {n} { incr ::nBusyHandler eval $::busy_handler_script return 0 } proc reopen_all {} { code1 {db close} code2 {db2 close} code3 {db3 close} code1 {sqlite3 db test.db} code2 {sqlite3 db2 test.db} code3 {sqlite3 db3 test.db} sql1 { PRAGMA synchronous = NORMAL } code1 { db busy busyhandler } } do_test 1.$tn.1 { reopen_all sql1 { PRAGMA page_size = 1024; PRAGMA auto_vacuum = 0; CREATE TABLE t1(x, y); PRAGMA journal_mode = WAL; INSERT INTO t1 VALUES(1, zeroblob(1200)); INSERT INTO t1 VALUES(2, zeroblob(1200)); INSERT INTO t1 VALUES(3, zeroblob(1200)); } expr [file size test.db] / 1024 } {2} # Have connection 2 grab a read-lock on the current snapshot. do_test 1.$tn.2 { sql2 { BEGIN; SELECT x FROM t1 } } {1 2 3} # Attempt a checkpoint. do_test 1.$tn.3 { code1 { do_wal_checkpoint db } list [db_page_count] [wal_page_count] } {5 9} # Write to the db again. The log cannot wrap because of the lock still # held by connection 2. The busy-handler has not yet been invoked. do_test 1.$tn.4 { sql1 { INSERT INTO t1 VALUES(4, zeroblob(1200)) } list [db_page_count] [wal_page_count] $::nBusyHandler } {5 12 0} # Now do a blocking-checkpoint. Set the busy-handler up so that connection # 2 releases its lock on the 6th invocation. The checkpointer should then # proceed to checkpoint the entire log file. Next write should go to the # start of the log file. # set ::busy_handler_script { if {$n==5} { sql2 COMMIT } } do_test 1.$tn.5 { code1 { do_wal_checkpoint db -mode restart } list [db_page_count] [wal_page_count] $::nBusyHandler } {6 12 6} do_test 1.$tn.6 { set ::nBusyHandler 0 sql1 { INSERT INTO t1 VALUES(5, zeroblob(1200)) } list [db_page_count] [wal_page_count] $::nBusyHandler } {6 12 0} do_test 1.$tn.7 { reopen_all list [db_page_count] [wal_page_count] $::nBusyHandler } {7 0 0} do_test 1.$tn.8 { sql2 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5} do_test 1.$tn.9 { sql1 { INSERT INTO t1 VALUES(6, zeroblob(1200)) } list [db_page_count] [wal_page_count] $::nBusyHandler } {7 5 0} do_test 1.$tn.10 { sql3 { BEGIN ; SELECT x FROM t1 } } {1 2 3 4 5 6} set ::busy_handler_script { if {$n==5} { sql2 COMMIT } if {$n==6} { set ::db_file_size [db_page_count] } if {$n==7} { sql3 COMMIT } } do_test 1.$tn.11 { code1 { do_wal_checkpoint db -mode restart } list [db_page_count] [wal_page_count] $::nBusyHandler } {10 5 8} do_test 1.$tn.12 { set ::db_file_size } 10 } #------------------------------------------------------------------------- # This block of tests explores checkpoint operations on more than one # database file. # proc setup_and_attach_aux {} { sql1 { ATTACH 'test.db2' AS aux } sql2 { ATTACH 'test.db2' AS aux } sql3 { ATTACH 'test.db2' AS aux } sql1 { PRAGMA aux.auto_vacuum = 0; PRAGMA main.auto_vacuum = 0; PRAGMA main.page_size=1024; PRAGMA main.journal_mode=WAL; PRAGMA aux.page_size=1024; PRAGMA aux.journal_mode=WAL; } } proc file_page_counts {} { list [db_page_count test.db ] \ [wal_page_count test.db ] \ [db_page_count test.db2] \ [wal_page_count test.db2] } # Test that executing "PRAGMA wal_checkpoint" checkpoints all attached # databases, not just the main db. In capi mode, check that this is # true if a NULL pointer is passed to wal_checkpoint_v2() in place of a # database name. do_multiclient_test tn { setup_and_attach_aux do_test 2.1.$tn.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); CREATE TABLE aux.t2(a, b); INSERT INTO t2 VALUES(1, 2); } } {} do_test 2.2.$tn.2 { file_page_counts } {1 5 1 5} do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 5 5} do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5} } do_multiclient_test tn { setup_and_attach_aux do_test 2.2.$tn.1 { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); CREATE TABLE aux.t2(a, b); INSERT INTO t2 VALUES(1, 2); INSERT INTO t2 VALUES(3, 4); } } {} do_test 2.2.$tn.2 { file_page_counts } {1 5 1 7} do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2} do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 5 5} do_test 2.2.$tn.5 { file_page_counts } {2 5 2 7} } do_multiclient_test tn { setup_and_attach_aux do_test 2.3.$tn.1 { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); CREATE TABLE aux.t2(a, b); INSERT INTO t2 VALUES(1, 2); } } {} do_test 2.3.$tn.2 { file_page_counts } {1 5 1 5} do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2} do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {} do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {} do_test 2.3.$tn.6 { file_page_counts } {1 7 1 7} do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 7 5} do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7} } # Check that checkpoints block on the correct locks. And respond correctly # if they cannot obtain those locks. There are three locks that a checkpoint # may block on (in the following order): # # 1. The writer lock: FULL and RESTART checkpoints block until any writer # process releases its lock. # # 2. Readers using part of the log file. FULL and RESTART checkpoints block # until readers using part (but not all) of the log file have finished. # # 3. Readers using any of the log file. After copying data into the # database file, RESTART checkpoints block until readers using any part # of the log file have finished. # # This test case involves running a checkpoint while there exist other # processes holding all three types of locks. # foreach {tn1 checkpoint busy_on ckpt_expected expected} { 1 PASSIVE - {0 5 5} - 2 TYPO - {0 5 5} - 3 FULL - {0 7 7} 2 4 FULL 1 {1 5 5} 1 5 FULL 2 {1 7 5} 2 6 FULL 3 {0 7 7} 2 7 RESTART - {0 7 7} 3 8 RESTART 1 {1 5 5} 1 9 RESTART 2 {1 7 5} 2 10 RESTART 3 {1 7 7} 3 } { do_multiclient_test tn { setup_and_attach_aux proc busyhandler {x} { set ::max_busyhandler $x if {$::busy_on!="-" && $x==$::busy_on} { return 1 } switch -- $x { 1 { sql2 "COMMIT ; BEGIN ; SELECT * FROM t1" } 2 { sql3 "COMMIT" } 3 { sql2 "COMMIT" } } return 0 } set ::max_busyhandler - do_test 2.4.$tn1.$tn.1 { sql1 { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, 2); } sql2 { BEGIN; INSERT INTO t1 VALUES(3, 4) } sql3 { BEGIN; SELECT * FROM t1 } } {1 2} do_test 2.4.$tn1.$tn.2 { code1 { db busy busyhandler } code1 { do_wal_checkpoint db -mode [string tolower $checkpoint] } } $ckpt_expected do_test 2.4.$tn1.$tn.3 { set ::max_busyhandler } $expected } } do_multiclient_test tn { code1 $do_wal_checkpoint code2 $do_wal_checkpoint code3 $do_wal_checkpoint do_test 3.$tn.1 { sql1 { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; PRAGMA synchronous = normal; CREATE TABLE t1(x, y); } sql2 { PRAGMA journal_mode } sql3 { PRAGMA journal_mode } } {wal} do_test 3.$tn.2 { code2 { do_wal_checkpoint db2 } } {0 2 2} do_test 3.$tn.3 { code2 { do_wal_checkpoint db2 } } {0 2 2} do_test 3.$tn.4 { code3 { do_wal_checkpoint db3 } } {0 2 2} code1 {db close} code2 {db2 close} code3 {db3 close} code1 {sqlite3 db test.db} code2 {sqlite3 db2 test.db} code3 {sqlite3 db3 test.db} do_test 3.$tn.5 { sql3 { PRAGMA journal_mode } } {wal} do_test 3.$tn.6 { code3 { do_wal_checkpoint db3 } } {0 0 0} } } finish_test |
Changes to test/wal_common.tcl.
︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # proc wal_file_size {nFrame pgsz} { expr {32 + ($pgsz+24)*$nFrame} } proc wal_frame_count {zFile pgsz} { set f [file size $zFile] expr {($f - 32) / ($pgsz+24)} } proc wal_cksum_intlist {ckv1 ckv2 intlist} { upvar $ckv1 c1 upvar $ckv2 c2 foreach {v1 v2} $intlist { | > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # proc wal_file_size {nFrame pgsz} { expr {32 + ($pgsz+24)*$nFrame} } proc wal_frame_count {zFile pgsz} { if {[file exists $zFile]==0} { return 0 } set f [file size $zFile] if {$f < 32} { return 0 } expr {($f - 32) / ($pgsz+24)} } proc wal_cksum_intlist {ckv1 ckv2 intlist} { upvar $ckv1 c1 upvar $ckv2 c2 foreach {v1 v2} $intlist { |
︙ | ︙ |
Changes to test/walfault.test.
︙ | ︙ | |||
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 | do_faultsim_test walfault-3 -prep { faultsim_restore_and_reopen } -body { db eval { DELETE FROM abc; PRAGMA wal_checkpoint; } } -test { faultsim_test_result {0 {}} } #-------------------------------------------------------------------------- # if {[permutation] != "inmemory_journal"} { faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen } -body { execsql { PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('a', 'b'); PRAGMA wal_checkpoint; SELECT * FROM t1; } } -test { | > > | | 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 | do_faultsim_test walfault-3 -prep { faultsim_restore_and_reopen } -body { db eval { DELETE FROM abc; PRAGMA wal_checkpoint; } set {} {} } -test { faultsim_test_result {0 {}} } #-------------------------------------------------------------------------- # if {[permutation] != "inmemory_journal"} { faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen } -body { execsql { PRAGMA auto_vacuum = 0; PRAGMA journal_mode = WAL; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES('a', 'b'); PRAGMA wal_checkpoint; SELECT * FROM t1; } } -test { faultsim_test_result {0 {wal 0 7 7 a b}} faultsim_integrity_check } } #-------------------------------------------------------------------------- # do_test walfault-5-pre-1 { |
︙ | ︙ | |||
409 410 411 412 413 414 415 416 417 418 419 420 421 422 | catch { db2 close } faultsim_restore_and_reopen shmfault filter xShmMap } -body { db eval { SELECT count(*) FROM abc } sqlite3 db2 test.db -vfs shmfault db2 eval { PRAGMA wal_checkpoint } } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test the handling of the various IO/OOM/SHM errors that may occur during # a log recovery operation undertaken as part of a call to | > | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | catch { db2 close } faultsim_restore_and_reopen shmfault filter xShmMap } -body { db eval { SELECT count(*) FROM abc } sqlite3 db2 test.db -vfs shmfault db2 eval { PRAGMA wal_checkpoint } set {} {} } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test the handling of the various IO/OOM/SHM errors that may occur during # a log recovery operation undertaken as part of a call to |
︙ | ︙ | |||
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | #------------------------------------------------------------------------- # 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 { | > | | | 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 | #------------------------------------------------------------------------- # Test fault-handling when wrapping around to the start of a WAL file. # do_test walfault-14-pre { faultsim_delete_and_reopen execsql { PRAGMA auto_vacuum = 0; 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 = full; INSERT INTO abc VALUES(randomblob(1500)); } } -test { faultsim_test_result {0 {0 10 10}} 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/where3.test.
︙ | ︙ | |||
218 219 220 221 222 223 224 225 226 227 | # 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; } { | > | | | 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 | # 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); INSERT INTO t302 VALUES(4,5); ANALYZE; explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y; } { 0 0 0 {SCAN TABLE t302 (~1 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 (~1 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. # |
︙ | ︙ | |||
336 337 338 339 340 341 342 343 | 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} } | < | 337 338 339 340 341 342 343 344 345 | 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/where9.test.
︙ | ︙ | |||
468 469 470 471 472 473 474 | # 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) } { | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | # 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>?) (~125000 rows)} } } ############################################################################ # Make sure OR-clauses work correctly on UPDATE and DELETE statements. do_test where9-6.2.1 { |
︙ | ︙ |
Changes to tool/mksqlite3c.tcl.
︙ | ︙ | |||
47 48 49 50 51 52 53 | # set out [open sqlite3.c w] set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] 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 | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | # set out [open sqlite3.c w] set today [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S UTC" -gmt 1] 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 single 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 |
︙ | ︙ | |||
291 292 293 294 295 296 297 298 299 300 301 302 303 304 | tokenize.c complete.c main.c notify.c fts3.c fts3_expr.c fts3_hash.c fts3_porter.c fts3_tokenizer.c fts3_tokenizer1.c fts3_write.c fts3_snippet.c | > | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | tokenize.c complete.c main.c notify.c fts3.c fts3_aux.c fts3_expr.c fts3_hash.c fts3_porter.c fts3_tokenizer.c fts3_tokenizer1.c fts3_write.c fts3_snippet.c |
︙ | ︙ |
Changes to tool/omittest.tcl.
1 2 3 4 5 6 7 8 9 10 | set rcsid {$Id: omittest.tcl,v 1.8 2008/10/13 15:35:09 drh Exp $} # Documentation for this script. This may be output to stderr # if the script is invoked incorrectly. set ::USAGE_MESSAGE { This Tcl script is used to test the various compile time options available for omitting code (the SQLITE_OMIT_xxx options). It should be invoked as follows: | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | set rcsid {$Id: omittest.tcl,v 1.8 2008/10/13 15:35:09 drh Exp $} # Documentation for this script. This may be output to stderr # if the script is invoked incorrectly. set ::USAGE_MESSAGE { This Tcl script is used to test the various compile time options available for omitting code (the SQLITE_OMIT_xxx options). It should be invoked as follows: <script> ?-makefile PATH-TO-MAKEFILE? ?-skip_run? The default value for ::MAKEFILE is "../Makefile.linux.gcc". If -skip_run option is given then only the compile part is attempted. This script builds the testfixture program and runs the SQLite test suite once with each SQLITE_OMIT_ option defined and then once with all options defined together. Each run is performed in a seperate directory created as a sub-directory of the current directory by the script. The output of the build is saved in <sub-directory>/build.log. The output of the test-suite is saved in <sub-directory>/test.log. |
︙ | ︙ | |||
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 | # test in. The second parameter is a list of OMIT symbols to define # when doing so. For example: # # run_quick_test /tmp/testdir {SQLITE_OMIT_TRIGGER SQLITE_OMIT_VIEW} # # proc run_quick_test {dir omit_symbol_list} { # Compile the value of the OPTS Makefile variable. set opts "-DSQLITE_MEMDEBUG -DSQLITE_DEBUG -DSQLITE_NO_SYNC" 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" } foreach sym $omit_symbol_list { append opts " -D${sym}=1" } # Create the directory and do the build. If an error occurs return # early without attempting to run the test suite. file mkdir $dir puts -nonewline "Building $dir..." flush stdout catch { file copy -force ./config.h $dir file copy -force ./libtool $dir } set rc [catch { | > > | | 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 | # test in. The second parameter is a list of OMIT symbols to define # when doing so. For example: # # run_quick_test /tmp/testdir {SQLITE_OMIT_TRIGGER SQLITE_OMIT_VIEW} # # proc run_quick_test {dir omit_symbol_list} { set target "testfixture" # Compile the value of the OPTS Makefile variable. set opts "-DSQLITE_MEMDEBUG -DSQLITE_DEBUG -DSQLITE_NO_SYNC" if {$::tcl_platform(platform)=="windows"} { append opts " -DSQLITE_OS_WIN=1" set target "testfixture.exe" } elseif {$::tcl_platform(platform)=="os2"} { append opts " -DSQLITE_OS_OS2=1" } else { append opts " -DSQLITE_OS_UNIX=1" } foreach sym $omit_symbol_list { append opts " -D${sym}=1" } # Create the directory and do the build. If an error occurs return # early without attempting to run the test suite. file mkdir $dir puts -nonewline "Building $dir..." flush stdout catch { file copy -force ./config.h $dir file copy -force ./libtool $dir } set rc [catch { exec make -C $dir -f $::MAKEFILE $target OPTS=$opts >& $dir/build.log }] if {$rc} { puts "No good. See $dir/build.log." return } else { puts "Ok" } |
︙ | ︙ | |||
87 88 89 90 91 92 93 | } if {![file exists $sqlite3_dummy]} { set wr [open $sqlite3_dummy w] puts $wr "dummy" close $wr } | > > > | | | | | | | | | | > | | > > > > > > > | | | | | | | > > | | | > | | | > | | | | | | | | | | | < | | | | | > | | | | | | | | | | | | | | > > | | | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > > > > > > | 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 | } if {![file exists $sqlite3_dummy]} { set wr [open $sqlite3_dummy w] puts $wr "dummy" close $wr } if {$::SKIP_RUN} { puts "Skip testing $dir." } else { # Run the test suite. puts -nonewline "Testing $dir..." flush stdout set rc [catch { exec make -C $dir -f $::MAKEFILE test OPTS=$opts >& $dir/test.log }] if {$rc} { puts "No good. See $dir/test.log." } else { puts "Ok" } } } # This proc processes the command line options passed to this script. # Currently the only option supported is "-makefile", default # "../Makefile.linux-gcc". Set the ::MAKEFILE variable to the value of this # option. # proc process_options {argv} { if {$::tcl_platform(platform)=="windows" || $::tcl_platform(platform)=="os2"} { set ::MAKEFILE ./Makefile ;# Default value } else { set ::MAKEFILE ./Makefile.linux-gcc ;# Default value } set ::SKIP_RUN 0 ;# Default to attempt test for {set i 0} {$i < [llength $argv]} {incr i} { switch -- [lindex $argv $i] { -makefile { incr i set ::MAKEFILE [lindex $argv $i] } -skip_run { incr i set ::SKIP_RUN 1 } default { puts stderr [string trim $::USAGE_MESSAGE] exit -1 } } set ::MAKEFILE [file normalize $::MAKEFILE] } } # Main routine. # proc main {argv} { # List of SQLITE_OMIT_XXX symbols supported by SQLite. set ::OMIT_SYMBOLS [list \ SQLITE_OMIT_ALTERTABLE \ SQLITE_OMIT_ANALYZE \ SQLITE_OMIT_ATTACH \ SQLITE_OMIT_AUTHORIZATION \ SQLITE_OMIT_AUTOINCREMENT \ SQLITE_OMIT_AUTOINIT \ SQLITE_OMIT_AUTOMATIC_INDEX \ SQLITE_OMIT_AUTORESET \ SQLITE_OMIT_AUTOVACUUM \ SQLITE_OMIT_BETWEEN_OPTIMIZATION \ SQLITE_OMIT_BLOB_LITERAL \ SQLITE_OMIT_BTREECOUNT \ SQLITE_OMIT_BUILTIN_TEST \ SQLITE_OMIT_CAST \ SQLITE_OMIT_CHECK \ SQLITE_OMIT_COMPILEOPTION_DIAGS \ SQLITE_OMIT_COMPLETE \ SQLITE_OMIT_COMPOUND_SELECT \ SQLITE_OMIT_DATETIME_FUNCS \ SQLITE_OMIT_DECLTYPE \ SQLITE_OMIT_DEPRECATED \ xxxSQLITE_OMIT_DISKIO \ SQLITE_OMIT_EXPLAIN \ SQLITE_OMIT_FLAG_PRAGMAS \ SQLITE_OMIT_FLOATING_POINT \ SQLITE_OMIT_FOREIGN_KEY \ SQLITE_OMIT_GET_TABLE \ SQLITE_OMIT_INCRBLOB \ SQLITE_OMIT_INTEGRITY_CHECK \ SQLITE_OMIT_LIKE_OPTIMIZATION \ SQLITE_OMIT_LOAD_EXTENSION \ SQLITE_OMIT_LOCALTIME \ SQLITE_OMIT_LOOKASIDE \ SQLITE_OMIT_MEMORYDB \ SQLITE_OMIT_OR_OPTIMIZATION \ SQLITE_OMIT_PAGER_PRAGMAS \ SQLITE_OMIT_PRAGMA \ SQLITE_OMIT_PROGRESS_CALLBACK \ SQLITE_OMIT_QUICKBALANCE \ SQLITE_OMIT_REINDEX \ SQLITE_OMIT_SCHEMA_PRAGMAS \ SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS \ SQLITE_OMIT_SHARED_CACHE \ SQLITE_OMIT_SUBQUERY \ SQLITE_OMIT_TCL_VARIABLE \ SQLITE_OMIT_TEMPDB \ SQLITE_OMIT_TRACE \ SQLITE_OMIT_TRIGGER \ SQLITE_OMIT_TRUNCATE_OPTIMIZATION \ SQLITE_OMIT_UNIQUE_ENFORCEMENT \ SQLITE_OMIT_UTF16 \ SQLITE_OMIT_VACUUM \ SQLITE_OMIT_VIEW \ SQLITE_OMIT_VIRTUALTABLE \ SQLITE_OMIT_WAL \ SQLITE_OMIT_WSD \ SQLITE_OMIT_XFER_OPT \ ] set ::ENABLE_SYMBOLS [list \ SQLITE_DISABLE_DIRSYNC \ SQLITE_DISABLE_LFS \ SQLITE_ENABLE_ATOMIC_WRITE \ xxxSQLITE_ENABLE_CEROD \ SQLITE_ENABLE_COLUMN_METADATA \ SQLITE_ENABLE_EXPENSIVE_ASSERT \ xxxSQLITE_ENABLE_FTS1 \ xxxSQLITE_ENABLE_FTS2 \ SQLITE_ENABLE_FTS3 \ SQLITE_ENABLE_FTS3_PARENTHESIS \ SQLITE_ENABLE_FTS4 \ xxxSQLITE_ENABLE_ICU \ SQLITE_ENABLE_IOTRACE \ SQLITE_ENABLE_LOAD_EXTENSION \ SQLITE_ENABLE_LOCKING_STYLE \ SQLITE_ENABLE_MEMORY_MANAGEMENT \ SQLITE_ENABLE_MEMSYS3 \ SQLITE_ENABLE_MEMSYS5 \ SQLITE_ENABLE_OVERSIZE_CELL_CHECK \ SQLITE_ENABLE_RTREE \ SQLITE_ENABLE_STAT2 \ SQLITE_ENABLE_UNLOCK_NOTIFY \ SQLITE_ENABLE_UPDATE_DELETE_LIMIT \ ] # Process any command line options. process_options $argv # First try a test with all OMIT symbols except SQLITE_OMIT_FLOATING_POINT # and SQLITE_OMIT_PRAGMA defined. The former doesn't work (causes segfaults) # and the latter is currently incompatible with the test suite (this should # be fixed, but it will be a lot of work). set allsyms [list] foreach s $::OMIT_SYMBOLS { if {$s!="SQLITE_OMIT_FLOATING_POINT" && $s!="SQLITE_OMIT_PRAGMA"} { lappend allsyms $s } } run_quick_test test_OMIT_EVERYTHING $allsyms # Now try one quick.test with each of the OMIT symbols defined. Included # are the OMIT_FLOATING_POINT and OMIT_PRAGMA symbols, even though we # know they will fail. It's good to be reminded of this from time to time. foreach sym $::OMIT_SYMBOLS { set dirname "test_[string range $sym 7 end]" run_quick_test $dirname $sym } # Try the ENABLE/DISABLE symbols one at a time. # We don't do them all at once since some are conflicting. foreach sym $::ENABLE_SYMBOLS { set dirname "test_[string range $sym 7 end]" run_quick_test $dirname $sym } } main $argv |
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.6} #---------------------------------------------------------------------------- # Test cases shell1-2.*: Basic "dot" command token parsing. # # check first token handling do_test shell1-2.1.1 { |
︙ | ︙ |
Added tool/split-sqlite3c.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 | #!/usr/bin/tclsh # # This script splits the sqlite3.c amalgamated source code files into # several smaller files such that no single files is more than a fixed # number of lines in length (32k or 64k). Each of the split out files # is #include-ed by the master file. # # Splitting files up this way allows them to be used with older compilers # that cannot handle really long source files. # set MAX 32768 ;# Maximum number of lines per file. set BEGIN {^/\*+ Begin file ([a-zA-Z0-9_.]+) \*+/} set END {^/\*+ End of %s \*+/} set in [open sqlite3.c] set out1 [open sqlite3-all.c w] # Copy the header from sqlite3.c into sqlite3-all.c # while {[gets $in line]} { if {[regexp $BEGIN $line]} break puts $out1 $line } # Gather the complete content of a file into memory. Store the # content in $bufout. Store the number of lines is $nout # proc gather_one_file {firstline bufout nout} { regexp $::BEGIN $firstline all filename set end [format $::END $filename] upvar $bufout buf $nout n set buf $firstline\n global in set n 0 while {[gets $in line]>=0} { incr n append buf $line\n if {[regexp $end $line]} break } } # Write a big chunk of text in to an auxiliary file "sqlite3-NNN.c". # Also add an appropriate #include to sqlite3-all.c # set filecnt 0 proc write_one_file {content} { global filecnt incr filecnt set out [open sqlite3-$filecnt.c w] puts -nonewline $out $content close $out puts $::out1 "#include \"sqlite3-$filecnt.c\"" } # Continue reading input. Store chunks in separate files and add # the #includes to the main sqlite3-all.c file as necessary to reference # the extra chunks. # set all {} set N 0 while {[regexp $BEGIN $line]} { set buf {} set n 0 gather_one_file $line buf n if {$n+$N>=$MAX} { write_one_file $all set all {} set N 0 } append all $buf incr N $n while {[gets $in line]>=0} { if {[regexp $BEGIN $line]} break puts $out1 $line } } if {$N>0} { write_one_file $all } close $out1 close $in |