Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 0cc4875fde9f492b To d0753799e447b795
2010-11-23
| ||
22:18 | Fix the ticket-change artifact generator so that it puts all fields with the "+" prefix on their name first, where they ought to be. ... (check-in: e1dac0e4 user: drh tags: trunk) | |
2010-11-19
| ||
22:29 | Further work on getting a "commit" or "update" to continue operating after an autosync failure. The "commit" command prompts to verify that you want to continue. ... (check-in: 0cc4875f user: drh tags: trunk) | |
19:48 | Work toward getting and "update" or "commit" to continue even after an auto-sync failure. ... (check-in: 9286aaf7 user: drh tags: trunk) | |
2010-11-01
| ||
23:38 | Fix a possible redirect loop. ... (check-in: f4033ec0 user: drh tags: trunk) | |
18:52 | merge from trunk ... (check-in: 4a094f46 user: wolfgang tags: StvPrivateHook2) | |
14:23 | Fix a typo in the file format documentation reported on the Tcl/Tk chatroom. ... (check-in: d0753799 user: drh tags: trunk, release) | |
2010-10-31
| ||
21:51 | Add a warning to the "fossil sqlite3" command to remind users that careless use of the command can easily corrupt the repository. Ticket [b18338c226f6fb4899]. ... (check-in: 4e1241f3 user: drh tags: trunk) | |
Changes to Makefile.
1 2 3 4 5 6 7 8 9 10 11 | #!/usr/bin/make # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = ./src #### The directory into which object code files should be written. # # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/usr/bin/make # #### The toplevel directory of the source tree. Fossil can be built # in a directory that is separate from the source tree. Just change # the following to point from the build directory to the src/ folder. # SRCDIR = ./src #### The directory into which object code files should be written. # # OBJDIR = ./obj #### C Compiler and options for use in building executables that # will run on the platform that is doing the build. This is used # to compile code-generator programs as part of the build process. # See TCC below for the C compiler for building the finished binary. # BCC = gcc -g -O2 |
︙ | ︙ |
Changes to src/add.c.
︙ | ︙ | |||
42 43 44 45 46 47 48 | || strcmp(zPath, "_FOSSIL_-journal")==0 || strcmp(zPath, "_FOSSIL_-wal")==0 || strcmp(zPath, "_FOSSIL_-shm")==0 || strcmp(zPath, ".fos")==0 || strcmp(zPath, ".fos-journal")==0 || strcmp(zPath, ".fos-wal")==0 || strcmp(zPath, ".fos-shm")==0 | | | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | || strcmp(zPath, "_FOSSIL_-journal")==0 || strcmp(zPath, "_FOSSIL_-wal")==0 || strcmp(zPath, "_FOSSIL_-shm")==0 || strcmp(zPath, ".fos")==0 || strcmp(zPath, ".fos-journal")==0 || strcmp(zPath, ".fos-wal")==0 || strcmp(zPath, ".fos-shm")==0 || blob_compare(&pathname, pOmit)==0 ){ fossil_warning("cannot add %s", zPath); }else{ if( !file_is_simple_pathname(zPath) ){ fossil_fatal("filename contains illegal characters: %s", zPath); } #if defined(_WIN32) |
︙ | ︙ | |||
269 270 271 272 273 274 275 | blob_reset(&pathname); } free(zName); } db_multi_exec("DELETE FROM vfile WHERE deleted AND rid=0"); db_end_transaction(0); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | blob_reset(&pathname); } free(zName); } db_multi_exec("DELETE FROM vfile WHERE deleted AND rid=0"); db_end_transaction(0); } /* ** Rename a single file. ** ** The original name of the file is zOrig. The new filename is zNew. */ static void mv_one_file(int vid, const char *zOrig, const char *zNew){ |
︙ | ︙ |
Changes to src/allrepo.c.
︙ | ︙ | |||
58 59 60 61 62 63 64 | ** that can be useful before or after a period of disconnected operation. ** ** On Win32 systems, the file is named "_fossil" and is located in ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. ** ** Available operations are: ** | < < < | < | 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 | ** that can be useful before or after a period of disconnected operation. ** ** On Win32 systems, the file is named "_fossil" and is located in ** %LOCALAPPDATA%, %APPDATA% or %HOMEPATH%. ** ** Available operations are: ** ** list | ls Display the location of all repositories ** ** pull Run a "pull" operation on all repositories ** ** push Run a "push" on all repositories ** ** rebuild Rebuild on all repositories ** ** sync Run a "sync" on all repositories ** ** Respositories are automatically added to the set of known repositories ** when one of the following commands against the repository: clone, info, ** pull, push, or sync */ void all_cmd(void){ int n; Stmt q; const char *zCmd; char *zSyscmd; char *zFossil; |
︙ | ︙ | |||
101 102 103 104 105 106 107 | zCmd = "push -autourl -R"; }else if( strncmp(zCmd, "pull", n)==0 ){ zCmd = "pull -autourl -R"; }else if( strncmp(zCmd, "rebuild", n)==0 ){ zCmd = "rebuild"; }else if( strncmp(zCmd, "sync", n)==0 ){ zCmd = "sync -autourl -R"; | < < < < < < < < < | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | zCmd = "push -autourl -R"; }else if( strncmp(zCmd, "pull", n)==0 ){ zCmd = "pull -autourl -R"; }else if( strncmp(zCmd, "rebuild", n)==0 ){ zCmd = "rebuild"; }else if( strncmp(zCmd, "sync", n)==0 ){ zCmd = "sync -autourl -R"; }else{ fossil_fatal("\"all\" subcommand should be one of: " "list ls push pull rebuild sync"); } zFossil = quoteFilename(g.argv[0]); nMissing = 0; db_prepare(&q, "SELECT DISTINCT substr(name, 6) COLLATE nocase" " FROM global_config" " WHERE substr(name, 1, 5)=='repo:' ORDER BY 1" ); while( db_step(&q)==SQLITE_ROW ){ |
︙ | ︙ | |||
136 137 138 139 140 141 142 | printf("%s\n", zFilename); continue; } zQFilename = quoteFilename(zFilename); zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); printf("%s\n", zSyscmd); fflush(stdout); | | | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | printf("%s\n", zFilename); continue; } zQFilename = quoteFilename(zFilename); zSyscmd = mprintf("%s %s %s", zFossil, zCmd, zQFilename); printf("%s\n", zSyscmd); fflush(stdout); portable_system(zSyscmd); free(zSyscmd); free(zQFilename); } /* If any repositories whose names appear in the ~/.fossil file could not ** be found, remove those names from the ~/.fossil file. */ |
︙ | ︙ |
Changes to src/branch.c.
︙ | ︙ | |||
153 154 155 156 157 158 159 | if( g.argc==3 ){ printf( "\n" "Note: the local check-out has not been updated to the new\n" " branch. To begin working on the new branch, do this:\n" "\n" " %s update %s\n", | | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | if( g.argc==3 ){ printf( "\n" "Note: the local check-out has not been updated to the new\n" " branch. To begin working on the new branch, do this:\n" "\n" " %s update %s\n", g.argv[0], zBranch ); } /* Commit */ db_end_transaction(0); |
︙ | ︙ | |||
185 186 187 188 189 190 191 | ** %fossil branch list ** ** List all branches ** */ void branch_cmd(void){ int n; | < | < | | | < < < < < < < < < > | | | | < | < < < | 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 | ** %fossil branch list ** ** List all branches ** */ void branch_cmd(void){ int n; db_find_and_open_repository(1); if( g.argc<3 ){ usage("new|list ..."); } n = strlen(g.argv[2]); if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){ branch_new(); }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){ Stmt q; db_prepare(&q, "%s" " AND blob.rid IN (SELECT rid FROM tagxref" " WHERE tagid=%d AND tagtype==2 AND srcid!=0)" " ORDER BY event.mtime DESC", timeline_query_for_tty(), TAG_BRANCH ); print_timeline(&q, 2000); db_finalize(&q); }else{ fossil_panic("branch subcommand should be one of: " "new list"); } } |
︙ | ︙ |
Changes to src/checkin.c.
︙ | ︙ | |||
128 129 130 131 132 133 134 | db_must_be_within_tree(); /* 012345678901234 */ printf("repository: %s\n", db_lget("repository","")); printf("local-root: %s\n", g.zLocalRoot); printf("server-code: %s\n", db_get("server-code", "")); vid = db_lget_int("checkout", 0); if( vid ){ | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | db_must_be_within_tree(); /* 012345678901234 */ printf("repository: %s\n", db_lget("repository","")); printf("local-root: %s\n", g.zLocalRoot); printf("server-code: %s\n", db_get("server-code", "")); vid = db_lget_int("checkout", 0); if( vid ){ show_common_info(vid, "checkout:", 0); } changes_cmd(); } /* ** COMMAND: ls ** |
︙ | ︙ | |||
416 417 418 419 420 421 422 | g.zLocalRoot); #if defined(_WIN32) blob_add_cr(&text); #endif blob_write_to_file(&text, zFile); zCmd = mprintf("%s \"%s\"", zEditor, zFile); printf("%s\n", zCmd); | | | 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | g.zLocalRoot); #if defined(_WIN32) blob_add_cr(&text); #endif blob_write_to_file(&text, zFile); zCmd = mprintf("%s \"%s\"", zEditor, zFile); printf("%s\n", zCmd); if( portable_system(zCmd) ){ fossil_panic("editor aborted"); } blob_reset(&text); blob_read_from_file(&text, zFile); blob_remove_cr(&text); unlink(zFile); free(zFile); |
︙ | ︙ | |||
695 696 697 698 699 700 701 | ** COMMAND: ci ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** checkout. You will be prompted to enter a check-in comment unless | | < | | | > > | | | < | 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 | ** COMMAND: ci ** COMMAND: commit ** ** Usage: %fossil commit ?OPTIONS? ?FILE...? ** ** Create a new version containing all of the changes in the current ** checkout. You will be prompted to enter a check-in comment unless ** the comment has been specified on the command-line using "-m". ** The editor defined in the "editor" fossil option (see %fossil help set) ** will be used, or from the "VISUAL" or "EDITOR" environment variables ** (in that order) if no editor is set. ** ** You will be prompted for your GPG passphrase in order to sign the ** new manifest unless the "--nosign" option is used. All files that ** have changed will be committed unless some subset of files is ** specified on the command line. ** ** The --branch option followed by a branch name cases the new check-in ** to be placed in the named branch. The --bgcolor option can be followed ** by a color name (ex: '#ffc0c0') to specify the background color of ** entries in the new branch when shown in the web timeline interface. ** ** A check-in is not permitted to fork unless the --force or -f ** option appears. A check-in is not allowed against a closed check-in. ** ** The --private option creates a private check-in that is never synced. ** Children of private check-ins are automatically private. ** ** Options: ** ** --comment|-m COMMENT-TEXT ** --branch NEW-BRANCH-NAME ** --bgcolor COLOR ** --nosign ** --force|-f ** --private ** --baseline ** --delta |
︙ | ︙ | |||
805 806 807 808 809 810 811 | g.markPrivate = 1; } /* ** Autosync if autosync is enabled and this is not a private check-in. */ if( !g.markPrivate ){ | | < < < < < < < | 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 | g.markPrivate = 1; } /* ** Autosync if autosync is enabled and this is not a private check-in. */ if( !g.markPrivate ){ autosync(AUTOSYNC_PULL); } /* Require confirmation to continue with the check-in if there is ** clock skew */ if( g.clockSkewSeen ){ Blob ans; |
︙ | ︙ | |||
1064 1065 1066 1067 1068 1069 1070 | if( useCksum ){ /* Verify that the repository checksum matches the expected checksum ** calculated before the checkin started (and stored as the R record ** of the manifest file). */ vfile_aggregate_checksum_repository(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ | < | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 | if( useCksum ){ /* Verify that the repository checksum matches the expected checksum ** calculated before the checkin started (and stored as the R record ** of the manifest file). */ vfile_aggregate_checksum_repository(nvid, &cksum2); if( blob_compare(&cksum1, &cksum2) ){ fossil_panic("tree checksum does not match repository after commit"); } /* Verify that the manifest checksum matches the expected checksum */ vfile_aggregate_checksum_manifest(nvid, &cksum2, &cksum1b); if( blob_compare(&cksum1, &cksum1b) ){ fossil_panic("manifest checksum does not agree with manifest: " |
︙ | ︙ |
Changes to src/clearsign.c.
︙ | ︙ | |||
37 38 39 40 41 42 43 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | return 0; } zRand = db_text(0, "SELECT hex(randomblob(10))"); zOut = mprintf("out-%s", zRand); zIn = mprintf("in-%z", zRand); blob_write_to_file(pIn, zOut); zCmd = mprintf("%s %s %s", zBase, zIn, zOut); rc = portable_system(zCmd); free(zCmd); if( rc==0 ){ if( pOut==pIn ){ blob_reset(pIn); } blob_zero(pOut); blob_read_from_file(pOut, zIn); |
︙ | ︙ |
Changes to src/configure.c.
︙ | ︙ | |||
493 494 495 496 497 498 499 | }else if( strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); } } db_end_transaction(0); printf("Configuration reset to factory defaults.\n"); printf("To recover, use: %s %s import %s\n", | | | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 | }else if( strcmp(zName,"@reportfmt")==0 ){ db_multi_exec("DELETE FROM reportfmt"); } } db_end_transaction(0); printf("Configuration reset to factory defaults.\n"); printf("To recover, use: %s %s import %s\n", g.argv[0], g.argv[1], zBackup); }else { fossil_fatal("METHOD should be one of:" " export import merge pull push reset"); } } |
Changes to src/db.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | } if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<h1>Database Error</h1>\n" "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); cgi_reply(); }else{ | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | } if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<h1>Database Error</h1>\n" "<pre>%h</pre><p>%s</p>", z, zRebuildMsg); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n\n%s", g.argv[0], z, zRebuildMsg); } db_force_rollback(); fossil_exit(1); } static int nBegin = 0; /* Nesting depth of BEGIN */ static int isNewRepo = 0; /* True if the repository is newly created */ |
︙ | ︙ | |||
685 686 687 688 689 690 691 | if( useAttach ){ db_open_or_attach(zDbName, "configdb"); g.dbConfig = 0; }else{ g.dbConfig = openDatabase(zDbName); } g.configOpen = 1; | < | 685 686 687 688 689 690 691 692 693 694 695 696 697 698 | if( useAttach ){ db_open_or_attach(zDbName, "configdb"); g.dbConfig = 0; }else{ g.dbConfig = openDatabase(zDbName); } g.configOpen = 1; } /* ** If zDbName is a valid local database file, open it and return ** true. If it is not a valid local database file, return 0. */ static int isValidLocalDb(const char *zDbName){ |
︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 | rid = content_put(&manifest, 0, 0); manifest_crosslink(rid, &manifest); } } /* ** COMMAND: new | < < | 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 | rid = content_put(&manifest, 0, 0); manifest_crosslink(rid, &manifest); } } /* ** COMMAND: new ** ** Usage: %fossil new ?OPTIONS? FILENAME ** ** Create a repository for a new project in the file named FILENAME. ** This command is distinct from "clone". The "clone" command makes ** a copy of an existing project. This command starts a new project. ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user |
︙ | ︙ | |||
1524 1525 1526 1527 1528 1529 1530 | { "dont-push", 0, 0, "off" }, { "editor", 0, 16, "" }, { "gdiff-command", 0, 16, "gdiff" }, { "ignore-glob", 0, 40, "" }, { "http-port", 0, 16, "8080" }, { "localauth", 0, 0, "off" }, { "manifest", 0, 0, "off" }, | | | 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 | { "dont-push", 0, 0, "off" }, { "editor", 0, 16, "" }, { "gdiff-command", 0, 16, "gdiff" }, { "ignore-glob", 0, 40, "" }, { "http-port", 0, 16, "8080" }, { "localauth", 0, 0, "off" }, { "manifest", 0, 0, "off" }, { "mtime-changes", 0, 0, "off" }, { "pgp-command", 0, 32, "gpg --clearsign -o " }, { "proxy", 0, 32, "off" }, { "repo-cksum", 0, 0, "on" }, { "ssh-command", 0, 32, "" }, { "web-browser", 0, 32, "" }, { 0,0,0,0 } }; |
︙ | ︙ |
Changes to src/deltacmd.c.
︙ | ︙ | |||
47 48 49 50 51 52 53 | ** ** Given two input files, create and output a delta that carries ** the first file into the second. */ void delta_create_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ | | > | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | ** ** Given two input files, create and output a delta that carries ** the first file into the second. */ void delta_create_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ fprintf(stderr,"Usage: %s %s ORIGIN TARGET DELTA\n", g.argv[0], g.argv[1]); fossil_exit(1); } if( blob_read_from_file(&orig, g.argv[2])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[2]); fossil_exit(1); } if( blob_read_from_file(&target, g.argv[3])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[3]); |
︙ | ︙ | |||
109 110 111 112 113 114 115 | ** ** Given an input files and a delta, apply the delta to the input file ** and write the result. */ void delta_apply_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ | | > | 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | ** ** Given an input files and a delta, apply the delta to the input file ** and write the result. */ void delta_apply_cmd(void){ Blob orig, target, delta; if( g.argc!=5 ){ fprintf(stderr,"Usage: %s %s ORIGIN DELTA TARGET\n", g.argv[0], g.argv[1]); fossil_exit(1); } if( blob_read_from_file(&orig, g.argv[2])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[2]); fossil_exit(1); } if( blob_read_from_file(&delta, g.argv[3])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[3]); |
︙ | ︙ |
Changes to src/diffcmd.c.
︙ | ︙ | |||
22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #include <assert.h> /* ** Diff option flags */ #define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */ #define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */ /* ** Show the difference between two files, one in memory and one on disk. ** ** The difference is the set of edits needed to transform pFile1 into ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. ** | > > > > > > > > > > > > > > > > > > > > | 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 | #include <assert.h> /* ** Diff option flags */ #define DIFF_NEWFILE 0x01 /* Treat non-existing fails as empty files */ #define DIFF_NOEOLWS 0x02 /* Ignore whitespace at the end of lines */ /* ** This function implements a cross-platform "system()" interface. */ int portable_system(const char *zOrigCmd){ int rc; #if defined(_WIN32) /* On windows, we have to put double-quotes around the entire command. ** Who knows why - this is just the way windows works. */ char *zNewCmd = mprintf("\"%s\"", zOrigCmd); rc = system(zNewCmd); free(zNewCmd); #else /* On unix, evaluate the command directly. */ rc = system(zOrigCmd); #endif return rc; } /* ** Show the difference between two files, one in memory and one on disk. ** ** The difference is the set of edits needed to transform pFile1 into ** zFile2. The content of pFile1 is in memory. zFile2 exists on disk. ** |
︙ | ︙ | |||
84 85 86 87 88 89 90 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, blob_str(&nameFile1)); blob_append(&cmd, " ", 1); shell_escape(&cmd, zFile2); /* Run the external diff command */ | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, blob_str(&nameFile1)); blob_append(&cmd, " ", 1); shell_escape(&cmd, zFile2); /* Run the external diff command */ portable_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ unlink(blob_str(&nameFile1)); blob_reset(&nameFile1); blob_reset(&cmd); } } |
︙ | ︙ | |||
138 139 140 141 142 143 144 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, zTemp1); blob_append(&cmd, " ", 1); shell_escape(&cmd, zTemp2); /* Run the external diff command */ | | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | blob_zero(&cmd); blob_appendf(&cmd, "%s ", zDiffCmd); shell_escape(&cmd, zTemp1); blob_append(&cmd, " ", 1); shell_escape(&cmd, zTemp2); /* Run the external diff command */ portable_system(blob_str(&cmd)); /* Delete the temporary file and clean up memory used */ unlink(zTemp1); unlink(zTemp2); blob_reset(&cmd); } } |
︙ | ︙ | |||
405 406 407 408 409 410 411 | ** no "--to" option then the (possibly edited) files in the current check-out ** are used. ** ** The "-i" command-line option forces the use of the internal diff logic ** rather than any external diff program that might be configured using ** the "setting" command. If no external diff program is configured, then ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". | < < < | 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | ** no "--to" option then the (possibly edited) files in the current check-out ** are used. ** ** The "-i" command-line option forces the use of the internal diff logic ** rather than any external diff program that might be configured using ** the "setting" command. If no external diff program is configured, then ** the "-i" option is a no-op. The "-i" option converts "gdiff" into "diff". */ void diff_cmd(void){ int isGDiff; /* True for gdiff. False for normal diff */ int isInternDiff; /* True for internal diff */ int hasNFlag; /* True if -N or --new-file flag is used */ const char *zFrom; /* Source version number */ const char *zTo; /* Target version number */ |
︙ | ︙ |
Changes to src/doc.c.
︙ | ︙ | |||
238 239 240 241 242 243 244 | { "spl", 3, "application/x-futuresplash" }, { "src", 3, "application/x-wais-source" }, { "step", 4, "application/STEP" }, { "stl", 3, "application/SLA" }, { "stp", 3, "application/STEP" }, { "sv4cpio", 7, "application/x-sv4cpio" }, { "sv4crc", 6, "application/x-sv4crc" }, | < | 238 239 240 241 242 243 244 245 246 247 248 249 250 251 | { "spl", 3, "application/x-futuresplash" }, { "src", 3, "application/x-wais-source" }, { "step", 4, "application/STEP" }, { "stl", 3, "application/SLA" }, { "stp", 3, "application/STEP" }, { "sv4cpio", 7, "application/x-sv4cpio" }, { "sv4crc", 6, "application/x-sv4crc" }, { "swf", 3, "application/x-shockwave-flash" }, { "t", 1, "application/x-troff" }, { "tar", 3, "application/x-tar" }, { "tcl", 3, "application/x-tcl" }, { "tex", 3, "application/x-tex" }, { "texi", 4, "application/x-texinfo" }, { "texinfo", 7, "application/x-texinfo" }, |
︙ | ︙ |
Deleted src/export.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/file.c.
︙ | ︙ | |||
307 308 309 310 311 312 313 | ** Also test Fossil's ability to measure attributes of a file. */ void cmd_test_canonical_name(void){ int i; Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ | < < | < | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | ** Also test Fossil's ability to measure attributes of a file. */ void cmd_test_canonical_name(void){ int i; Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ const char *zName = g.argv[i]; file_canonical_name(zName, &x); printf("%s\n", blob_buffer(&x)); blob_reset(&x); printf(" file_size = %lld\n", file_size(zName)); printf(" file_mtime = %lld\n", file_mtime(zName)); printf(" file_isfile = %d\n", file_isfile(zName)); printf(" file_isexe = %d\n", file_isexe(zName)); printf(" file_isdir = %d\n", file_isdir(zName)); } } /* |
︙ | ︙ | |||
427 428 429 430 431 432 433 | ** false, then simply return 0. ** ** The root of the tree is defined by the g.zLocalRoot variable. */ int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ int n; Blob full; | < < < < < < < < < < < < < < | > | | 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 | ** false, then simply return 0. ** ** The root of the tree is defined by the g.zLocalRoot variable. */ int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ int n; Blob full; db_must_be_within_tree(); file_canonical_name(zOrigName, &full); n = strlen(g.zLocalRoot); if( blob_size(&full)<=n || memcmp(g.zLocalRoot, blob_buffer(&full), n) ){ blob_reset(&full); if( errFatal ){ fossil_fatal("file outside of checkout tree: %s", zOrigName); } return 0; } blob_zero(pOut); blob_append(pOut, blob_buffer(&full)+n, blob_size(&full)-n); return 1; } /* ** COMMAND: test-tree-name ** ** Test the operation of the tree name generator. |
︙ | ︙ |
Changes to src/finfo.c.
︙ | ︙ | |||
19 20 21 22 23 24 25 | */ #include "config.h" #include "finfo.h" /* ** COMMAND: finfo ** | | | < < < | | < < < < < < < > > > > > > < | | > | | > | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < | | < | < > > > | < < | | < < < < < > | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < | < | | | | | | | < | > > > > | | > | | < < < < < < < | < | | < | 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 | */ #include "config.h" #include "finfo.h" /* ** COMMAND: finfo ** ** Usage: %fossil finfo FILENAME ** ** Print the change history for a single file. ** ** The "--limit N" and "--offset P" options limit the output to the first ** N changes after skipping P changes. */ void finfo_cmd(void){ Stmt q; int vid; Blob dest; const char *zFilename; const char *zLimit; const char *zOffset; int iLimit, iOffset; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_panic("no checkout to finfo files in"); } zLimit = find_option("limit",0,1); iLimit = zLimit ? atoi(zLimit) : -1; zOffset = find_option("offset",0,1); iOffset = zOffset ? atoi(zOffset) : 0; if (g.argc<3) { usage("FILENAME"); } file_tree_name(g.argv[2], &dest, 1); zFilename = blob_str(&dest); db_prepare(&q, "SELECT " " (SELECT uuid FROM blob WHERE rid=mlink.fid)," /* New file */ " (SELECT uuid FROM blob WHERE rid=mlink.mid)," /* The check-in */ " date(event.mtime,'localtime')," " coalesce(event.ecomment, event.comment)," " coalesce(event.euser, event.user)" " FROM mlink, event" " WHERE mlink.fnid=(SELECT fnid FROM filename WHERE name=%Q)" " AND event.objid=mlink.mid" " ORDER BY event.mtime DESC LIMIT %d OFFSET %d /*sort*/", zFilename, iLimit, iOffset ); printf("History of %s\n", zFilename); while( db_step(&q)==SQLITE_ROW ){ const char *zFileUuid = db_column_text(&q, 0); const char *zCiUuid = db_column_text(&q, 1); const char *zDate = db_column_text(&q, 2); const char *zCom = db_column_text(&q, 3); const char *zUser = db_column_text(&q, 4); char *zOut; printf("%s ", zDate); if( zFileUuid==0 ){ zOut = sqlite3_mprintf("[%.10s] DELETED %s (user: %s)", zCiUuid, zCom, zUser); }else{ zOut = sqlite3_mprintf("[%.10s] %s (user: %s, artifact: [%.10s])", zCiUuid, zCom, zUser, zFileUuid); } comment_print(zOut, 11, 79); sqlite3_free(zOut); } db_finalize(&q); blob_reset(&dest); } /* ** WEBPAGE: finfo ** URL: /finfo?name=FILENAME ** |
︙ | ︙ | |||
227 228 229 230 231 232 233 | blob_zero(&title); blob_appendf(&title, "History of "); hyperlinked_path(zFilename, &title, 0); @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> | | | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | blob_zero(&title); blob_appendf(&title, "History of "); hyperlinked_path(zFilename, &title, 0); @ <h2>%b(&title)</h2> blob_reset(&title); pGraph = graph_init(); @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> @ <table class="timelineTable"> while( db_step(&q)==SQLITE_ROW ){ const char *zDate = db_column_text(&q, 0); const char *zCom = db_column_text(&q, 1); const char *zUser = db_column_text(&q, 2); int fpid = db_column_int(&q, 3); int frid = db_column_int(&q, 4); const char *zPUuid = db_column_text(&q, 5); |
︙ | ︙ |
Changes to src/graph.c.
︙ | ︙ | |||
297 298 299 300 301 302 303 | i--; } } } /* Find the pChild pointer for each node. ** | | | 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | i--; } } } /* Find the pChild pointer for each node. ** ** The pChild points to node directly above on the same rail. ** The pChild must be in the same branch. Leaf nodes have a NULL ** pChild. ** ** In the case of a fork, choose the pChild that results in the ** longest rail. */ for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
︙ | ︙ | |||
378 379 380 381 382 383 384 | pRow->railInUse = 1<<pRow->iRail; continue; } pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail); pParent->aiRaiser[pRow->iRail] = pRow->idx; } mask = 1<<pRow->iRail; | | | 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | pRow->railInUse = 1<<pRow->iRail; continue; } pRow->iRail = findFreeRail(p, 0, pParent->idx, inUse, pParent->iRail); pParent->aiRaiser[pRow->iRail] = pRow->idx; } mask = 1<<pRow->iRail; if( pRow->pPrev ) pRow->pPrev->railInUse |= mask; if( pRow->pNext ) pRow->pNext->railInUse |= mask; if( pRow->pChild==0 ){ inUse &= ~mask; }else{ inUse |= mask; assignChildrenToRail(pRow); } |
︙ | ︙ |
Changes to src/http.c.
︙ | ︙ | |||
101 102 103 104 105 106 107 | blob_zero(pHdr); i = strlen(g.urlPath); if( i>0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } | | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | blob_zero(pHdr); i = strlen(g.urlPath); if( i>0 && g.urlPath[i-1]=='/' ){ zSep = ""; }else{ zSep = "/"; } blob_appendf(pHdr, "POST %s%sxfer HTTP/1.0\r\n", g.urlPath, zSep); if( g.urlProxyAuth ){ blob_appendf(pHdr, "Proxy-Authorization: %s\n", g.urlProxyAuth); } blob_appendf(pHdr, "Host: %s\r\n", g.urlHostname); blob_appendf(pHdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n"); if( g.fHttpTrace ){ blob_appendf(pHdr, "Content-Type: application/x-fossil-debug\r\n"); |
︙ | ︙ | |||
125 126 127 128 129 130 131 | ** in pRecv. pRecv is assumed to be uninitialized when ** this routine is called - this routine will initialize it. ** ** The server address is contain in the "g" global structure. The ** url_parse() routine should have been called prior to this routine ** in order to fill this structure appropriately. */ | | | < | 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 | ** in pRecv. pRecv is assumed to be uninitialized when ** this routine is called - this routine will initialize it. ** ** The server address is contain in the "g" global structure. The ** url_parse() routine should have been called prior to this routine ** in order to fill this structure appropriately. */ void http_exchange(Blob *pSend, Blob *pReply, int useLogin){ Blob login; /* The login card */ Blob payload; /* The complete payload including login card */ Blob hdr; /* The HTTP request header */ int closeConnection; /* True to close the connection when done */ int iLength; /* Length of the reply payload */ int rc; /* Result code */ int iHttpVersion; /* Which version of HTTP protocol server uses */ char *zLine; /* A single line of the reply header */ int i; /* Loop counter */ int isError = 0; /* True if the reply is an error message */ if( transport_open() ){ fossil_fatal(transport_errmsg()); } /* Construct the login card and prepare the complete payload */ blob_zero(&login); if( useLogin ) http_build_login_card(pSend, &login); if( g.fHttpTrace ){ payload = login; |
︙ | ︙ | |||
200 201 202 203 204 205 206 | /* printf("[%s]\n", zLine); fflush(stdout); */ if( strncasecmp(zLine, "http/1.", 7)==0 ){ if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; if( rc!=200 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; | | | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | /* printf("[%s]\n", zLine); fflush(stdout); */ if( strncasecmp(zLine, "http/1.", 7)==0 ){ if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err; if( rc!=200 && rc!=302 ){ int ii; for(ii=7; zLine[ii] && zLine[ii]!=' '; ii++){} while( zLine[ii]==' ' ) ii++; fossil_fatal("server says: %s\n", &zLine[ii]); goto write_err; } if( iHttpVersion==0 ){ closeConnection = 1; }else{ closeConnection = 0; } |
︙ | ︙ | |||
225 226 227 228 229 230 231 | closeConnection = 0; } }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ int i, j; for(i=9; zLine[i] && zLine[i]==' '; i++){} if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); j = strlen(zLine) - 1; | | < < < | > | | 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 | closeConnection = 0; } }else if( rc==302 && strncasecmp(zLine, "location:", 9)==0 ){ int i, j; for(i=9; zLine[i] && zLine[i]==' '; i++){} if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); j = strlen(zLine) - 1; if( j>4 && strcmp(&zLine[j-4],"/xfer")==0 ) zLine[j-4] = 0; fossil_print("redirect to %s\n", &zLine[i]); url_parse(&zLine[i]); transport_close(); http_exchange(pSend, pReply, useLogin); return; }else if( strncasecmp(zLine, "content-type: text/html", 23)==0 ){ isError = 1; } } if( rc!=200 ){ fossil_fatal("\"location:\" missing from 302 redirect reply"); goto write_err; } /* ** Extract the reply payload that follows the header */ if( iLength<0 ){ |
︙ | ︙ | |||
286 287 288 289 290 291 292 | */ closeConnection = 1; /* FIX ME */ if( closeConnection ){ transport_close(); }else{ transport_rewind(); } | | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | */ closeConnection = 1; /* FIX ME */ if( closeConnection ){ transport_close(); }else{ transport_rewind(); } return; /* ** Jump to here if an error is seen. */ write_err: transport_close(); return; } |
Changes to src/http_socket.c.
︙ | ︙ | |||
150 151 152 153 154 155 156 | struct hostent *pHost; pHost = gethostbyname(g.urlName); if( pHost!=0 ){ memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); }else #endif { | | | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | struct hostent *pHost; pHost = gethostbyname(g.urlName); if( pHost!=0 ){ memcpy(&addr.sin_addr,pHost->h_addr_list[0],pHost->h_length); }else #endif { socket_set_errmsg("can't resolve host name: %s\n", g.urlName); return 1; } } addrIsInit = 1; /* Set the Global.zIpAddr variable to the server we are talking to. ** This is used to populate the ipaddr column of the rcvfrom table, |
︙ | ︙ |
Changes to src/http_transport.c.
︙ | ︙ | |||
292 293 294 295 296 297 298 | void transport_flip(void){ if( g.urlIsSsh ){ fprintf(sshOut, "\n\n"); }else if( g.urlIsFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", | | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | void transport_flip(void){ if( g.urlIsSsh ){ fprintf(sshOut, "\n\n"); }else if( g.urlIsFile ){ char *zCmd; fclose(transport.pFile); zCmd = mprintf("\"%s\" http \"%s\" \"%s\" \"%s\" 127.0.0.1", g.argv[0], g.urlName, transport.zOutFile, transport.zInFile ); portable_system(zCmd); free(zCmd); transport.pFile = fopen(transport.zInFile, "rb"); } } /* ** This routine is called when the inbound message has been received |
︙ | ︙ |
Deleted src/import.c.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to src/info.c.
︙ | ︙ | |||
47 48 49 50 51 52 53 | ** Print common information about a particular record. ** ** * The UUID ** * The record ID ** * mtime and ctime ** * who signed it */ | | < < < < < | < < < | | | | | | | | | | | | | | | | | | | | | | | | < | 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 | ** Print common information about a particular record. ** ** * The UUID ** * The record ID ** * mtime and ctime ** * who signed it */ void show_common_info(int rid, const char *zUuidName, int showComment){ Stmt q; char *zComment = 0; char *zTags; char *zDate; char *zUuid; zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); if( zUuid ){ zDate = db_text(0, "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", rid ); /* 01234567890123 */ printf("%-13s %s %s\n", zUuidName, zUuid, zDate ? zDate : ""); free(zUuid); free(zDate); } if( zUuid && showComment ){ zComment = db_text(0, "SELECT coalesce(ecomment,comment) || ' (user: ' || coalesce(euser,user,'?') || ')' FROM event WHERE objid=%d", rid ); } db_prepare(&q, "SELECT uuid, pid FROM plink JOIN blob ON pid=rid " " WHERE cid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); printf("parent: %s %s\n", zUuid, zDate); free(zDate); } db_finalize(&q); db_prepare(&q, "SELECT uuid, cid FROM plink JOIN blob ON cid=rid " " WHERE pid=%d", rid); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); zDate = db_text("", "SELECT datetime(mtime) || ' UTC' FROM event WHERE objid=%d", db_column_int(&q, 1) ); printf("child: %s %s\n", zUuid, zDate); free(zDate); } db_finalize(&q); zTags = info_tags_of_checkin(rid, 0); if( zTags && zTags[0] ){ printf("tags: %s\n", zTags); } free(zTags); if( zComment ){ printf("comment: "); |
︙ | ︙ | |||
160 161 162 163 164 165 166 | #endif printf("project-code: %s\n", db_get("project-code", "")); printf("server-code: %s\n", db_get("server-code", "")); vid = db_lget_int("checkout", 0); if( vid==0 ){ printf("checkout: nil\n"); }else{ | | | | 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | #endif printf("project-code: %s\n", db_get("project-code", "")); printf("server-code: %s\n", db_get("server-code", "")); vid = db_lget_int("checkout", 0); if( vid==0 ){ printf("checkout: nil\n"); }else{ show_common_info(vid, "checkout:", 1); } }else{ int rid; rid = name_to_rid(g.argv[2]); if( rid==0 ){ fossil_panic("no such object: %s\n", g.argv[2]); } show_common_info(rid, "uuid:", 1); } } /* ** Show information about all tags on a given node. */ static void showTags(int rid, const char *zNotGlob){ |
︙ | ︙ |
Changes to src/login.c.
︙ | ︙ | |||
49 50 51 52 53 54 55 | /* ** Return the name of the login cookie */ static char *login_cookie_name(void){ static char *zCookieName = 0; if( zCookieName==0 ){ | | | | | > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | /* ** Return the name of the login cookie */ static char *login_cookie_name(void){ static char *zCookieName = 0; if( zCookieName==0 ){ int n = strlen(g.zTop); zCookieName = fossil_malloc( n*2+16 ); /* 0123456789 12345 */ strcpy(zCookieName, "fossil_login_"); encode16((unsigned char*)g.zTop, (unsigned char*)&zCookieName[13], n); } return zCookieName; } /* ** Redirect to the page specified by the "g" query parameter. ** Or if there is no "g" query parameter, redirect to the homepage. |
︙ | ︙ | |||
456 457 458 459 460 461 462 | void login_set_anon_nobody_capabilities(void){ static int once = 1; if( g.zLogin && once ){ const char *zCap; /* All logged-in users inherit privileges from "nobody" */ zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); login_set_capabilities(zCap); | | | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | void login_set_anon_nobody_capabilities(void){ static int once = 1; if( g.zLogin && once ){ const char *zCap; /* All logged-in users inherit privileges from "nobody" */ zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); login_set_capabilities(zCap); if( strcmp(g.zLogin, "anonymous")!=0 ){ /* All logged-in users inherit privileges from "anonymous" */ zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'"); login_set_capabilities(zCap); } once = 0; } } |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
226 227 228 229 230 231 232 | int idx; int rc; sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); g.now = time(0); g.argc = argc; g.argv = argv; | | | 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | int idx; int rc; sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); g.now = time(0); g.argc = argc; g.argv = argv; if( getenv("GATEWAY_INTERFACE")!=0 ){ zCmdName = "cgi"; }else if( argc<2 ){ fprintf(stderr, "Usage: %s COMMAND ...\n" "\"%s help\" for a list of available commands\n" "\"%s help COMMAND\" for specific details\n", argv[0], argv[0], argv[0]); fossil_exit(1); |
︙ | ︙ | |||
267 268 269 270 271 272 273 | /* ** The following variable becomes true while processing a fatal error ** or a panic. If additional "recursive-fatal" errors occur while ** shutting down, the recursive errors are silently ignored. */ static int mainInFatalError = 0; | < < < < < < < < < < < | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | /* ** The following variable becomes true while processing a fatal error ** or a panic. If additional "recursive-fatal" errors occur while ** shutting down, the recursive errors are silently ignored. */ static int mainInFatalError = 0; /* ** Exit. Take care to close the database first. */ void fossil_exit(int rc){ db_close(); exit(rc); } |
︙ | ︙ | |||
303 304 305 306 307 308 309 | z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput && once ){ once = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ | | | | 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 | z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput && once ){ once = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } void fossil_fatal(const char *zFormat, ...){ char *z; va_list ap; mainInFatalError = 1; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } /* This routine works like fossil_fatal() except that if called ** recursively, the recursive call is a no-op. |
︙ | ︙ | |||
348 349 350 351 352 353 354 | z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ g.cgiOutput = 0; cgi_printf("<p class=\"generalError\">%h</p>", z); cgi_reply(); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } db_force_rollback(); fossil_exit(1); } /* Print a warning message */ void fossil_warning(const char *zFormat, ...){ char *z; va_list ap; va_start(ap, zFormat); z = vmprintf(zFormat, ap); va_end(ap); if( g.cgiOutput ){ cgi_printf("<p class=\"generalError\">%h</p>", z); }else{ fprintf(stderr, "%s: %s\n", g.argv[0], z); } } /* ** Malloc and free routines that cannot fail */ void *fossil_malloc(size_t n){ void *p = malloc(n); if( p==0 ) fossil_panic("out of memory"); return p; } void fossil_free(void *p){ free(p); } void *fossil_realloc(void *p, size_t n){ p = realloc(p, n); if( p==0 ) fossil_panic("out of memory"); return p; } /* ** Return a name for an SQLite error code */ static const char *sqlite_error_code_name(int iCode){ static char zCode[30]; |
︙ | ︙ | |||
462 463 464 465 466 467 468 | fossil_warning("%s: %s", sqlite_error_code_name(iCode), zErrmsg); } /* ** Print a usage comment and quit */ void usage(const char *zFormat){ | | | 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 | fossil_warning("%s: %s", sqlite_error_code_name(iCode), zErrmsg); } /* ** Print a usage comment and quit */ void usage(const char *zFormat){ fprintf(stderr, "Usage: %s %s %s\n", g.argv[0], g.argv[1], zFormat); fossil_exit(1); } /* ** Remove n elements from g.argv beginning with the i-th element. */ void remove_from_argv(int i, int n){ |
︙ | ︙ | |||
622 623 624 625 626 627 628 | ** ** Display information on how to use COMMAND */ void help_cmd(void){ int rc, idx; const char *z; if( g.argc!=3 ){ | | < | | 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 | ** ** Display information on how to use COMMAND */ void help_cmd(void){ int rc, idx; const char *z; if( g.argc!=3 ){ printf("Usage: %s help COMMAND.\nAvailable COMMANDs:\n", g.argv[0]); cmd_cmd_list(); version_cmd(); return; } rc = name_search(g.argv[2], aCommand, count(aCommand), &idx); if( rc==1 ){ fossil_fatal("unknown command: %s", g.argv[2]); }else if( rc==2 ){ fossil_fatal("ambiguous command prefix: %s", g.argv[2]); } z = aCmdHelp[idx]; if( z==0 ){ fossil_fatal("no help available for the %s command", aCommand[idx].zName); } while( *z ){ if( *z=='%' && strncmp(z, "%fossil", 7)==0 ){ printf("%s", g.argv[0]); z += 7; }else{ putchar(*z); z++; } } putchar('\n'); |
︙ | ︙ |
Changes to src/main.mk.
︙ | ︙ | |||
34 35 36 37 38 39 40 | $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ | < < | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/event.c \ $(SRCDIR)/file.c \ $(SRCDIR)/finfo.c \ $(SRCDIR)/graph.c \ $(SRCDIR)/http.c \ $(SRCDIR)/http_socket.c \ $(SRCDIR)/http_ssl.c \ $(SRCDIR)/http_transport.c \ $(SRCDIR)/info.c \ $(SRCDIR)/login.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ |
︙ | ︙ | |||
110 111 112 113 114 115 116 | deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ | < < | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | deltacmd_.c \ descendants_.c \ diff_.c \ diffcmd_.c \ doc_.c \ encode_.c \ event_.c \ file_.c \ finfo_.c \ graph_.c \ http_.c \ http_socket_.c \ http_ssl_.c \ http_transport_.c \ info_.c \ login_.c \ main_.c \ manifest_.c \ md5_.c \ merge_.c \ merge3_.c \ |
︙ | ︙ | |||
186 187 188 189 190 191 192 | $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ | < < | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/event.o \ $(OBJDIR)/file.o \ $(OBJDIR)/finfo.o \ $(OBJDIR)/graph.o \ $(OBJDIR)/http.o \ $(OBJDIR)/http_socket.o \ $(OBJDIR)/http_ssl.o \ $(OBJDIR)/http_transport.o \ $(OBJDIR)/info.o \ $(OBJDIR)/login.o \ $(OBJDIR)/main.o \ $(OBJDIR)/manifest.o \ $(OBJDIR)/md5.o \ $(OBJDIR)/merge.o \ $(OBJDIR)/merge3.o \ |
︙ | ︙ | |||
282 283 284 285 286 287 288 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers | | | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | # $(SRCDIR)/../manifest: # noop clean: rm -f $(OBJDIR)/*.o *_.c $(APPNAME) VERSION.h rm -f translate makeheaders mkindex page_index.h headers rm -f add.h allrepo.h attach.h bag.h blob.h branch.h browse.h captcha.h cgi.h checkin.h checkout.h clearsign.h clone.h comformat.h configure.h content.h db.h delta.h deltacmd.h descendants.h diff.h diffcmd.h doc.h encode.h event.h file.h finfo.h graph.h http.h http_socket.h http_ssl.h http_transport.h info.h login.h main.h manifest.h md5.h merge.h merge3.h name.h pivot.h popen.h pqueue.h printf.h rebuild.h report.h rss.h schema.h search.h setup.h sha1.h shun.h skins.h stat.h style.h sync.h tag.h th_main.h timeline.h tkt.h tktsetup.h undo.h update.h url.h user.h verify.h vfile.h wiki.h wikiformat.h winhttp.h xfer.h zip.h page_index.h: $(TRANS_SRC) mkindex ./mkindex $(TRANS_SRC) >$@ headers: page_index.h makeheaders VERSION.h ./makeheaders add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)/sqlite3.h $(SRCDIR)/th.h VERSION.h touch headers headers: Makefile Makefile: add_.c: $(SRCDIR)/add.c translate ./translate $(SRCDIR)/add.c >add_.c $(OBJDIR)/add.o: add_.c add.h $(SRCDIR)/config.h |
︙ | ︙ | |||
466 467 468 469 470 471 472 | event_.c: $(SRCDIR)/event.c translate ./translate $(SRCDIR)/event.c >event_.c $(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c event_.c event.h: headers | < < < < < < < | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 | event_.c: $(SRCDIR)/event.c translate ./translate $(SRCDIR)/event.c >event_.c $(OBJDIR)/event.o: event_.c event.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/event.o -c event_.c event.h: headers file_.c: $(SRCDIR)/file.c translate ./translate $(SRCDIR)/file.c >file_.c $(OBJDIR)/file.o: file_.c file.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/file.o -c file_.c file.h: headers |
︙ | ︙ | |||
522 523 524 525 526 527 528 | http_transport_.c: $(SRCDIR)/http_transport.c translate ./translate $(SRCDIR)/http_transport.c >http_transport_.c $(OBJDIR)/http_transport.o: http_transport_.c http_transport.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_transport.o -c http_transport_.c http_transport.h: headers | < < < < < < < | 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | http_transport_.c: $(SRCDIR)/http_transport.c translate ./translate $(SRCDIR)/http_transport.c >http_transport_.c $(OBJDIR)/http_transport.o: http_transport_.c http_transport.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/http_transport.o -c http_transport_.c http_transport.h: headers info_.c: $(SRCDIR)/info.c translate ./translate $(SRCDIR)/info.c >info_.c $(OBJDIR)/info.o: info_.c info.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/info.o -c info_.c info.h: headers |
︙ | ︙ |
Changes to src/makemake.tcl.
︙ | ︙ | |||
28 29 30 31 32 33 34 | deltacmd descendants diff diffcmd doc encode event | < < | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_transport info login main manifest md5 merge merge3 |
︙ | ︙ |
Changes to src/manifest.c.
︙ | ︙ | |||
102 103 104 105 106 107 108 | #define MX_MANIFEST_CACHE 6 static struct { int nxAge; int aAge[MX_MANIFEST_CACHE]; Manifest *apManifest[MX_MANIFEST_CACHE]; } manifestCache; | < < < < < | 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #define MX_MANIFEST_CACHE 6 static struct { int nxAge; int aAge[MX_MANIFEST_CACHE]; Manifest *apManifest[MX_MANIFEST_CACHE]; } manifestCache; /* ** Clear the memory allocated in a manifest object */ void manifest_destroy(Manifest *p){ if( p ){ blob_reset(&p->content); |
︙ | ︙ | |||
188 189 190 191 192 193 194 | } #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM # define md5sum_init(X) # define md5sum_step_text(X,Y) #endif | < < < < < < < < < < < | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | } #ifdef FOSSIL_DONT_VERIFY_MANIFEST_MD5SUM # define md5sum_init(X) # define md5sum_step_text(X,Y) #endif /* ** Remove the PGP signature from the artifact, if there is one. */ static void remove_pgp_signature(char **pz, int *pn){ char *z = *pz; int n = *pn; int i; if( memcmp(z, "-----BEGIN PGP SIGNED MESSAGE-----", 34)!=0 ) return; for(i=34; i<n && (z[i-1]!='\n' || z[i-2]!='\n'); i++){} if( i>=n ) return; z += i; n -= i; *pz = z; for(i=n-1; i>=0; i--){ if( z[i]=='\n' && memcmp(&z[i],"\n-----BEGIN PGP SIGNATURE-", 25)==0 ){ n = i+1; |
︙ | ︙ | |||
838 839 840 841 842 843 844 845 846 847 848 849 850 851 | if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_ATTACHMENT; }else{ if( p->nCChild>0 ) goto manifest_syntax_error; if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nField>0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; p->type = CFTYPE_MANIFEST; } md5sum_init(); | > | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 | if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( !seenZ ) goto manifest_syntax_error; p->type = CFTYPE_ATTACHMENT; }else{ if( p->nCChild>0 ) goto manifest_syntax_error; if( p->rDate<=0.0 ) goto manifest_syntax_error; if( p->nParent>0 ) goto manifest_syntax_error; if( p->nField>0 ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; if( p->zWikiTitle ) goto manifest_syntax_error; if( p->zTicketUuid ) goto manifest_syntax_error; p->type = CFTYPE_MANIFEST; } md5sum_init(); |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | return; } if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ content_deltify(pid, cid, 0); }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ content_deltify(pParent->pBaseline->rid, cid, 0); } | < < < < < < < < < < | 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 | return; } if( (pParent->zBaseline==0)==(pChild->zBaseline==0) ){ content_deltify(pid, cid, 0); }else if( pChild->zBaseline==0 && pParent->zBaseline!=0 ){ content_deltify(pParent->pBaseline->rid, cid, 0); } for(i=0, pChildFile=pChild->aFile; i<pChild->nFile; i++, pChildFile++){ if( pChildFile->zPrior ){ pParentFile = manifest_file_seek(pParent, pChildFile->zPrior); if( pParentFile ){ add_one_mlink(cid, pParentFile->zUuid, pChildFile->zUuid, pChildFile->zName, pChildFile->zPrior); |
︙ | ︙ | |||
1261 1262 1263 1264 1265 1266 1267 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ if( pParentFile->zUuid ) continue; pChildFile = manifest_file_seek(pChild, pParentFile->zName); if( pChildFile ){ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); } } | < < < < < < < < > > > > > > < | < < < < < < < | < < < < < < < < < < < < < < < < < < < < | 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 | for(i=0, pParentFile=pParent->aFile; i<pParent->nFile; i++, pParentFile++){ if( pParentFile->zUuid ) continue; pChildFile = manifest_file_seek(pChild, pParentFile->zName); if( pChildFile ){ add_one_mlink(cid, 0, pChildFile->zUuid, pChildFile->zName, 0); } } } manifest_cache_insert(*ppOther); } /* ** True if manifest_crosslink_begin() has been called but ** manifest_crosslink_end() is still pending. */ static int manifest_crosslink_busy = 0; /* ** Setup to do multiple manifest_crosslink() calls. ** This is only required if processing ticket changes. */ void manifest_crosslink_begin(void){ assert( manifest_crosslink_busy==0 ); manifest_crosslink_busy = 1; db_begin_transaction(); db_multi_exec("CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE)"); } /* ** Finish up a sequence of manifest_crosslink calls. */ void manifest_crosslink_end(void){ Stmt q; assert( manifest_crosslink_busy==1 ); db_prepare(&q, "SELECT uuid FROM pending_tkt"); while( db_step(&q)==SQLITE_ROW ){ const char *zUuid = db_column_text(&q, 0); ticket_rebuild_entry(zUuid); } db_finalize(&q); db_multi_exec("DROP TABLE pending_tkt"); db_end_transaction(0); manifest_crosslink_busy = 0; } /* ** Make an entry in the event table for a ticket change artifact. */ |
︙ | ︙ | |||
1469 1470 1471 1472 1473 1474 1475 | } db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); while( db_step(&q)==SQLITE_ROW ){ int cid = db_column_int(&q, 0); add_mlink(rid, p, cid, 0); } db_finalize(&q); | < < < < < | 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 | } db_prepare(&q, "SELECT cid FROM plink WHERE pid=%d AND isprim", rid); while( db_step(&q)==SQLITE_ROW ){ int cid = db_column_int(&q, 0); add_mlink(rid, p, cid, 0); } db_finalize(&q); db_multi_exec( "REPLACE INTO event(type,mtime,objid,user,comment," "bgcolor,euser,ecomment)" "VALUES('ci'," " coalesce(" " (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d)," " %.17g" |
︙ | ︙ |
Changes to src/merge.c.
︙ | ︙ | |||
71 72 73 74 75 76 77 | if( vid==0 ){ fossil_fatal("nothing is checked out"); } mid = name_to_rid(g.argv[2]); if( mid==0 ){ fossil_fatal("not a version: %s", g.argv[2]); } | | | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | if( vid==0 ){ fossil_fatal("nothing is checked out"); } mid = name_to_rid(g.argv[2]); if( mid==0 ){ fossil_fatal("not a version: %s", g.argv[2]); } if( mid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", mid) ){ fossil_fatal("not a version: %s", g.argv[2]); } if( pickFlag || backoutFlag ){ pid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim", mid); if( pid<=0 ){ fossil_fatal("cannot find an ancestor for %s", g.argv[2]); } |
︙ | ︙ | |||
94 95 96 97 98 99 100 | db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); while( db_step(&q)==SQLITE_ROW ){ pivot_set_secondary(db_column_int(&q,0)); } db_finalize(&q); pid = pivot_find(); if( pid<=0 ){ | | | | | 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | db_prepare(&q, "SELECT merge FROM vmerge WHERE id=0"); while( db_step(&q)==SQLITE_ROW ){ pivot_set_secondary(db_column_int(&q,0)); } db_finalize(&q); pid = pivot_find(); if( pid<=0 ){ fossil_fatal("cannot find a common ancestor between the current" "checkout and %s", g.argv[2]); } } if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){ fossil_fatal("not a version: record #%d", mid); } vfile_check_signature(vid, 1); db_begin_transaction(); undo_begin(); load_vfile_from_rid(mid); load_vfile_from_rid(pid); |
︙ | ︙ |
Changes to src/merge3.c.
︙ | ︙ | |||
300 301 302 303 304 305 306 | ** ** Combine change in going from PIVOT->VERSION1 with the change going ** from PIVOT->VERSION2 and write the combined changes into MERGED. */ void delta_3waymerge_cmd(void){ Blob pivot, v1, v2, merged; if( g.argc!=6 ){ | | > | 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | ** ** Combine change in going from PIVOT->VERSION1 with the change going ** from PIVOT->VERSION2 and write the combined changes into MERGED. */ void delta_3waymerge_cmd(void){ Blob pivot, v1, v2, merged; if( g.argc!=6 ){ fprintf(stderr,"Usage: %s %s PIVOT V1 V2 MERGED\n", g.argv[0], g.argv[1]); fossil_exit(1); } if( blob_read_from_file(&pivot, g.argv[2])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[2]); fossil_exit(1); } if( blob_read_from_file(&v1, g.argv[3])<0 ){ fprintf(stderr,"cannot read %s\n", g.argv[3]); |
︙ | ︙ |
Changes to src/name.c.
︙ | ︙ | |||
140 141 142 143 144 145 146 | ** ** An input of "tip" returns the most recent check-in. ** ** Memory to hold the returned string comes from malloc() and needs to ** be freed by the caller. */ char *tag_to_uuid(const char *zTag){ | < | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | ** ** An input of "tip" returns the most recent check-in. ** ** Memory to hold the returned string comes from malloc() and needs to ** be freed by the caller. */ char *tag_to_uuid(const char *zTag){ char *zUuid = db_text(0, "SELECT blob.uuid" " FROM tag, tagxref, event, blob" " WHERE tag.tagname='sym-'||%Q " " AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 " " AND event.objid=tagxref.rid " |
︙ | ︙ | |||
189 190 191 192 193 194 195 | "SELECT blob.uuid" " FROM event, blob" " WHERE event.type='ci'" " AND blob.rid=event.objid" " ORDER BY event.mtime DESC" ); } | < < < < < < < < < < < < < < | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | "SELECT blob.uuid" " FROM event, blob" " WHERE event.type='ci'" " AND blob.rid=event.objid" " ORDER BY event.mtime DESC" ); } } return zUuid; } /* ** Convert a date/time string into a UUID. ** |
︙ | ︙ |
Changes to src/pivot.c.
︙ | ︙ | |||
48 49 50 51 52 53 54 | "DELETE FROM aqueue;" "CREATE INDEX IF NOT EXISTS aqueue_idx1 ON aqueue(pending, mtime);" ); /* Insert the primary record */ db_multi_exec( "INSERT INTO aqueue(rid, mtime, pending, src)" | | | | 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 | "DELETE FROM aqueue;" "CREATE INDEX IF NOT EXISTS aqueue_idx1 ON aqueue(pending, mtime);" ); /* Insert the primary record */ db_multi_exec( "INSERT INTO aqueue(rid, mtime, pending, src)" " SELECT %d, mtime, 1, 1 FROM plink WHERE cid=%d LIMIT 1", rid, rid ); } /* ** Set a secondary file. The primary file must be set first. There ** must be at least one secondary but there can be more than one if ** desired. */ void pivot_set_secondary(int rid){ /* Insert the primary record */ db_multi_exec( "INSERT OR IGNORE INTO aqueue(rid, mtime, pending, src)" " SELECT %d, mtime, 1, 0 FROM plink WHERE cid=%d", rid, rid ); } /* ** Find the most recent common ancestor of the primary and one of ** the secondaries. Return its rid. Return 0 if no common ancestor |
︙ | ︙ |
Changes to src/rebuild.c.
︙ | ︙ | |||
350 351 352 353 354 355 356 | ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt; | < < < | 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 | ** records. Run this command after updating the fossil ** executable in a way that changes the database schema. */ void rebuild_database(void){ int forceFlag; int randomizeFlag; int errCnt; forceFlag = find_option("force","f",0)!=0; randomizeFlag = find_option("randomize", 0, 0)!=0; if( g.argc==3 ){ db_open_repository(g.argv[2]); }else{ db_find_and_open_repository(1); if( g.argc!=2 ){ usage("?REPOSITORY-FILENAME?"); } db_close(); db_open_repository(g.zRepositoryName); } db_begin_transaction(); ttyOutput = 1; errCnt = rebuild_db(randomizeFlag, 1); if( errCnt && !forceFlag ){ printf("%d errors. Rolling back changes. Use --force to force a commit.\n", errCnt); db_end_transaction(1); }else{ db_end_transaction(0); } } /* ** COMMAND: test-detach ** |
︙ | ︙ | |||
574 575 576 577 578 579 580 | } /* ** COMMAND: deconstruct ** ** Usage %fossil deconstruct ?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION ** | | | 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 | } /* ** COMMAND: deconstruct ** ** Usage %fossil deconstruct ?-R|--repository REPOSITORY? ?-L|--prefixlength N? DESTINATION ** ** This command exports all artifacts of o given repository and ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where ** AABBBBBBBBB.. is the 40 character artifact ID, AA the first 2 characters. ** If -L|--prefixlength is given, the length (default 2) of the directory ** prefix can be set to 0,1,..,9 characters. */ void deconstruct_cmd(void){ |
︙ | ︙ |
Changes to src/report.c.
︙ | ︙ | |||
237 238 239 240 241 242 243 | /* Compile the statement and check for illegal accesses or syntax errors. */ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, &zTail); if( rc!=SQLITE_OK ){ zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db)); } | < < < | 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | /* Compile the statement and check for illegal accesses or syntax errors. */ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr); rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, &zTail); if( rc!=SQLITE_OK ){ zErr = mprintf("Syntax error: %s", sqlite3_errmsg(g.db)); } if( pStmt ){ sqlite3_finalize(pStmt); } sqlite3_set_authorizer(g.db, 0, 0); return zErr; } |
︙ | ︙ | |||
816 817 818 819 820 821 822 | free(zToFree); if( horiz ){ @ </tr> } @ </table> } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 | free(zToFree); if( horiz ){ @ </tr> } @ </table> } /* ** WEBPAGE: /rptview ** ** Generate a report. The rn query parameter is the report number ** corresponding to REPORTFMT.RN. If the tablist query parameter exists, ** then the output consists of lines of tab-separated fields instead of |
︙ | ︙ | |||
953 954 955 956 957 958 959 | style_header(zTitle); output_color_key(zClrKey, 1, "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\""); @ <table border="1" cellpadding="2" cellspacing="0" class="report"> sState.rn = rn; sState.nCount = 0; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); | | | | 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 | style_header(zTitle); output_color_key(zClrKey, 1, "border=\"0\" cellpadding=\"3\" cellspacing=\"0\" class=\"report\""); @ <table border="1" cellpadding="2" cellspacing="0" class="report"> sState.rn = rn; sState.nCount = 0; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, generate_html, &sState, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); @ </table> if( zErr1 ){ @ <p class="reportError">Error: %h(zErr1)</p> }else if( zErr2 ){ @ <p class="reportError">Error: %h(zErr2)</p> } style_footer(); }else{ sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, output_tab_separated, &count, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); cgi_set_content_type("text/plain"); } } /* ** report number for full table ticket export |
︙ | ︙ | |||
1126 1127 1128 1129 1130 1131 1132 | if( zFilter ){ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); } count = 0; tktEncode = enc; zSep = zSepIn; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); | | | 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | if( zFilter ){ zSql = mprintf("SELECT * FROM (%s) WHERE %s",zSql,zFilter); } count = 0; tktEncode = enc; zSep = zSepIn; sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)&zErr1); sqlite3_exec(g.db, zSql, output_separated_file, &count, &zErr2); sqlite3_set_authorizer(g.db, 0, 0); if( zFilter ){ free(zSql); } } |
Changes to src/setup.c.
︙ | ︙ | |||
692 693 694 695 696 697 698 | iVal = iQ; } } if( iVal ){ @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> @ <b>%s(zLabel)</b> }else{ | | | 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 | iVal = iQ; } } if( iVal ){ @ <input type="checkbox" name="%s(zQParm)" checked="checked" /> @ <b>%s(zLabel)</b> }else{ @ <input type="checkbox" name="%s(zQParm)" /><b>%s(zLabel)</b> } } /* ** Generate an entry box for an attribute. */ void entry_attribute( |
︙ | ︙ | |||
875 876 877 878 879 880 881 | @ <form action="%s(g.zBaseURL)/setup_settings" method="post"><div> @ <table border="0"><tr><td valign="top"> login_insert_csrf_secret(); for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width==0 ){ onoff_attribute(pSet->name, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, | | | 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 | @ <form action="%s(g.zBaseURL)/setup_settings" method="post"><div> @ <table border="0"><tr><td valign="top"> login_insert_csrf_secret(); for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width==0 ){ onoff_attribute(pSet->name, pSet->name, pSet->var!=0 ? pSet->var : pSet->name, pSet->def[0]=='1'); @ <br /> } } @ </td><td style="width: 30;"></td><td valign="top"> for(pSet=ctrlSettings; pSet->name!=0; pSet++){ if( pSet->width!=0 ){ entry_attribute(pSet->name, /*pSet->width*/ 40, pSet->name, |
︙ | ︙ | |||
1085 1086 1087 1088 1089 1090 1091 | db_end_transaction(0); } /* ** WEBPAGE: setup_logo */ void setup_logo(void){ | | | 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 | db_end_transaction(0); } /* ** WEBPAGE: setup_logo */ void setup_logo(void){ const char *zMime = "image/gif"; const char *aImg = P("im"); int szImg = atoi(PD("im:bytes","0")); if( szImg>0 ){ zMime = PD("im:mimetype","image/gif"); } login_check_credentials(); if( !g.okSetup ){ |
︙ | ︙ |
Changes to src/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.7.3. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% are more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start |
︙ | ︙ | |||
646 647 648 649 650 651 652 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
3172 3173 3174 3175 3176 3177 3178 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); | < < < < < < < < < < < < < < < | 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing ** for the values it stores. ^Values stored in sqlite3_value objects |
︙ | ︙ | |||
3286 3287 3288 3289 3290 3291 3292 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or | | < < < | 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
︙ | ︙ | |||
3929 3930 3931 3932 3933 3934 3935 | ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL poiners for all three function ** callbacks. ** | | < | | < < | | | | 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 | ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL poiners for all three function ** callbacks. ** ** ^If the tenth parameter to sqlite3_create_function_v2() is not NULL, ** then it is invoked when the function is deleted, either by being ** overloaded or when the database connection closes. ** ^When the destructure callback of the tenth parameter is invoked, it ** is passed a single argument which is a copy of the pointer which was ** the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with |
︙ | ︙ | |||
4401 4402 4403 4404 4405 4406 4407 | ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** | < < < < < < < < < | 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 | ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, |
︙ | ︙ | |||
5164 5165 5166 5167 5168 5169 5170 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite | | < < | 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ SQLITE_API int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ |
︙ | ︙ | |||
5349 5350 5351 5352 5353 5354 5355 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); | < < < < < < < < < < < < < < < < < < < < < < < | 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Close A BLOB Handle ** ** ^Closes an open [BLOB handle]. ** ** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the |
︙ | ︙ | |||
9943 9944 9945 9946 9947 9948 9949 | u8 notIndexed; /* True if there is a NOT INDEXED clause */ int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ IdList *pUsing; /* The USING clause of a join */ Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */ char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */ Index *pIndex; /* Index structure corresponding to zIndex, if any */ | < < < | 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 | u8 notIndexed; /* True if there is a NOT INDEXED clause */ int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ IdList *pUsing; /* The USING clause of a join */ Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */ char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */ Index *pIndex; /* Index structure corresponding to zIndex, if any */ } a[1]; /* One entry for each identifier on the list */ }; /* ** Permitted values of the SrcList.a.jointype field */ #define JT_INNER 0x0001 /* Any kind of inner or cross join */ |
︙ | ︙ | |||
9978 9979 9980 9981 9982 9983 9984 | ** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx ** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the ** case that more than one of these conditions is true. */ struct WherePlan { u32 wsFlags; /* WHERE_* flags that describe the strategy */ u32 nEq; /* Number of == constraints */ | < | 9920 9921 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 | ** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx ** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the ** case that more than one of these conditions is true. */ struct WherePlan { u32 wsFlags; /* WHERE_* flags that describe the strategy */ u32 nEq; /* Number of == constraints */ union { Index *pIdx; /* Index when WHERE_INDEXED is true */ struct WhereTerm *pTerm; /* WHERE clause term for OR-search */ sqlite3_index_info *pVtabIdx; /* Virtual table index to use */ } u; }; |
︙ | ︙ | |||
10063 10064 10065 10066 10067 10068 10069 | SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ | < | 10004 10005 10006 10007 10008 10009 10010 10011 10012 10013 10014 10015 10016 10017 | SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ int iBreak; /* Jump here to break out of the loop */ int nLevel; /* Number of nested loop */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; /* ** A NameContext defines a context in which to resolve table and column ** names. The context consists of a list of tables (the pSrcList) field and ** a list of named expression (pEList). The named expression list may |
︙ | ︙ | |||
10139 10140 10141 10142 10143 10144 10145 | Select *pPrior; /* Prior select in a compound select statement */ Select *pNext; /* Next select to the left in a compound */ Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ | < | 10079 10080 10081 10082 10083 10084 10085 10086 10087 10088 10089 10090 10091 10092 | Select *pPrior; /* Prior select in a compound select statement */ Select *pNext; /* Next select to the left in a compound */ Select *pRightmost; /* Right-most select in a compound select statement */ Expr *pLimit; /* LIMIT expression. NULL means not used. */ Expr *pOffset; /* OFFSET expression. NULL means not used. */ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */ }; /* ** Allowed values for Select.selFlags. The "SF" prefix stands for ** "Select Flag". */ #define SF_Distinct 0x0001 /* Output should be DISTINCT */ |
︙ | ︙ | |||
10335 10336 10337 10338 10339 10340 10341 | u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ | < < < < < | 10274 10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 | u8 declareVtab; /* True if inside sqlite3_declare_vtab() */ int nVtabLock; /* Number of virtual tables to lock */ Table **apVtabLock; /* Pointer to virtual tables needing locking */ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ }; #ifdef SQLITE_OMIT_VIRTUALTABLE #define IN_DECLARE_VTAB 0 #else #define IN_DECLARE_VTAB (pParse->declareVtab) #endif |
︙ | ︙ | |||
27375 27376 27377 27378 27379 27380 27381 | ){ int rc = SQLITE_OK; /* Return Code */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ struct stat sStat; /* Output of stat() on database file */ | < < < < < < < < < < < < | < < < | 27309 27310 27311 27312 27313 27314 27315 27316 27317 27318 27319 27320 27321 27322 27323 27324 27325 | ){ int rc = SQLITE_OK; /* Return Code */ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ char zDb[MAX_PATHNAME+1]; /* Database file path */ int nDb; /* Number of valid bytes in zDb */ struct stat sStat; /* Output of stat() on database file */ nDb = sqlite3Strlen30(zPath) - ((flags & SQLITE_OPEN_WAL) ? 4 : 8); memcpy(zDb, zPath, nDb); zDb[nDb] = '\0'; if( 0==stat(zDb, &sStat) ){ *pMode = sStat.st_mode & 0777; }else{ rc = SQLITE_IOERR_FSTAT; } }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ *pMode = 0600; |
︙ | ︙ | |||
34598 34599 34600 34601 34602 34603 34604 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 | < | | 34517 34518 34519 34520 34521 34522 34523 34524 34525 34526 34527 34528 34529 34530 34531 34532 34533 34534 34535 34536 34537 34538 34539 34540 34541 | # define sqlite3WalUndo(x,y,z) 0 # define sqlite3WalSavepoint(y,z) # define sqlite3WalSavepointUndo(y,z) 0 # define sqlite3WalFrames(u,v,w,x,y,z) 0 # define sqlite3WalCheckpoint(u,v,w,x) 0 # define sqlite3WalCallback(z) 0 # define sqlite3WalExclusiveMode(y,z) 0 #else #define WAL_SAVEPOINT_NDATA 4 /* Connection to a write-ahead log (WAL) file. ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ SQLITE_PRIVATE int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**); SQLITE_PRIVATE int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and ** preserves the current state even if the other threads or processes ** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the |
︙ | ︙ | |||
34666 34667 34668 34669 34670 34671 34672 | SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op); | < < < < < < | 34584 34585 34586 34587 34588 34589 34590 34591 34592 34593 34594 34595 34596 34597 | SQLITE_PRIVATE int sqlite3WalCallback(Wal *pWal); /* Tell the wal layer that an EXCLUSIVE lock has been obtained (or released) ** by the pager layer on the database file. */ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op); #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* _WAL_H_ */ /************** End of wal.h *************************************************/ /************** Continuing where we left off in pager.c **********************/ |
︙ | ︙ | |||
35582 35583 35584 35585 35586 35587 35588 | assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); break; } return 1; } | < < | 35494 35495 35496 35497 35498 35499 35500 35501 35502 35503 35504 35505 35506 35507 35508 | assert( pPager->errCode!=SQLITE_OK ); assert( sqlite3PcacheRefCount(pPager->pPCache)>0 ); break; } return 1; } /* ** Return a pointer to a human readable string in a static buffer ** containing the state of the Pager object passed as an argument. This ** is intended to be used within debuggers. For example, as an alternative ** to "print *pPager" in gdb: ** ** (gdb) printf "%s", print_pager_state(pPager) |
︙ | ︙ | |||
35708 35709 35710 35711 35712 35713 35714 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; | | | 35618 35619 35620 35621 35622 35623 35624 35625 35626 35627 35628 35629 35630 35631 35632 | ** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is ** called, do not modify it. See the comment above the #define of ** UNKNOWN_LOCK for an explanation of this. */ static int pagerUnlockDb(Pager *pPager, int eLock){ int rc = SQLITE_OK; assert( !pPager->exclusiveMode ); assert( eLock==NO_LOCK || eLock==SHARED_LOCK ); assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 ); if( isOpen(pPager->fd) ){ assert( pPager->eLock>=eLock ); rc = sqlite3OsUnlock(pPager->fd, eLock); if( pPager->eLock!=UNKNOWN_LOCK ){ pPager->eLock = (u8)eLock; |
︙ | ︙ | |||
39182 39183 39184 39185 39186 39187 39188 | */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); | | | 39092 39093 39094 39095 39096 39097 39098 39099 39100 39101 39102 39103 39104 39105 39106 | */ rc = pagerPagecount(pPager, &nPage); if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); pagerUnlockDb(pPager, SHARED_LOCK); } sqlite3EndBenignMalloc(); }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, |
︙ | ︙ | |||
41008 41009 41010 41011 41012 41013 41014 | */ SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); | < | | 40918 40919 40920 40921 40922 40923 40924 40925 40926 40927 40928 40929 40930 40931 40932 | */ SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *pPager, int eMode){ assert( eMode==PAGER_LOCKINGMODE_QUERY || eMode==PAGER_LOCKINGMODE_NORMAL || eMode==PAGER_LOCKINGMODE_EXCLUSIVE ); assert( PAGER_LOCKINGMODE_QUERY<0 ); assert( PAGER_LOCKINGMODE_NORMAL>=0 && PAGER_LOCKINGMODE_EXCLUSIVE>=0 ); if( eMode>=0 && !pPager->tempFile ){ pPager->exclusiveMode = (u8)eMode; } return (int)pPager->exclusiveMode; } /* ** Set the journal-mode for this pager. Parameter eMode must be one of: |
︙ | ︙ | |||
41196 41197 41198 41199 41200 41201 41202 | /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 41105 41106 41107 41108 41109 41110 41111 41112 41113 41114 41115 41116 41117 41118 41119 41120 | /* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){ const sqlite3_io_methods *pMethods = pPager->fd->pMethods; return pMethods->iVersion>=2 && pMethods->xShmMap!=0; } /* ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file |
︙ | ︙ | |||
41284 41285 41286 41287 41288 41289 41290 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); | > > > > | | 41141 41142 41143 41144 41145 41146 41147 41148 41149 41150 41151 41152 41153 41154 41155 41156 41157 41158 41159 | if( !pPager->tempFile && !pPager->pWal ){ if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Close any rollback journal previously open */ sqlite3OsClose(pPager->jfd); /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; pPager->eState = PAGER_OPEN; } }else{ *pbOpen = 1; } |
︙ | ︙ | |||
41323 41324 41325 41326 41327 41328 41329 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ | > | | | | > > > > | 41184 41185 41186 41187 41188 41189 41190 41191 41192 41193 41194 41195 41196 41197 41198 41199 41200 41201 41202 41203 41204 41205 41206 41207 41208 41209 41210 41211 41212 41213 41214 41215 41216 41217 | rc = pagerLockDb(pPager, SHARED_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3OsAccess( pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if( rc==SQLITE_OK && logexists ){ rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if( rc==SQLITE_OK ){ rc = sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; }else{ /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock ** that we did get back to SHARED. */ pagerUnlockDb(pPager, SQLITE_LOCK_SHARED); } } return rc; } #ifdef SQLITE_HAS_CODEC /* |
︙ | ︙ | |||
41793 41794 41795 41796 41797 41798 41799 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; | < < < < < < < | 41659 41660 41661 41662 41663 41664 41665 41666 41667 41668 41669 41670 41671 41672 | const char *zWalName; /* Name of WAL file */ u32 nCkpt; /* Checkpoint sequence counter in the wal-header */ #ifdef SQLITE_DEBUG u8 lockError; /* True if a locking error has occurred */ #endif }; /* ** Each page of the wal-index mapping contains a hash-table made up of ** an array of HASHTABLE_NSLOT elements of the following type. */ typedef u16 ht_slot; /* |
︙ | ︙ | |||
41886 41887 41888 41889 41890 41891 41892 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ | < < < < | | | < | 41745 41746 41747 41748 41749 41750 41751 41752 41753 41754 41755 41756 41757 41758 41759 41760 41761 | sizeof(u32*)*(iPage+1-pWal->nWiData)); pWal->apWiData = apNew; pWal->nWiData = iPage+1; } /* Request a pointer to the required page from the VFS */ if( pWal->apWiData[iPage]==0 ){ rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] ); } *ppPage = pWal->apWiData[iPage]; assert( iPage==0 || *ppPage || rc!=SQLITE_OK ); return rc; } |
︙ | ︙ | |||
41976 41977 41978 41979 41980 41981 41982 | }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } | < < < < < < | | 41830 41831 41832 41833 41834 41835 41836 41837 41838 41839 41840 41841 41842 41843 41844 41845 41846 41847 41848 41849 41850 41851 41852 41853 41854 41855 41856 41857 41858 | }while( aData<aEnd ); } aOut[0] = s1; aOut[1] = s2; } /* ** Write the header information in pWal->hdr into the wal-index. ** ** The checksum on pWal->hdr is updated before it is written. */ static void walIndexWriteHdr(Wal *pWal){ volatile WalIndexHdr *aHdr = walIndexHdr(pWal); const int nCksum = offsetof(WalIndexHdr, aCksum); assert( pWal->writeLock ); pWal->hdr.isInit = 1; pWal->hdr.iVersion = WALINDEX_MAX_VERSION; walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum); memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr)); sqlite3OsShmBarrier(pWal->pDbFd); memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr)); } /* ** This function encodes a single frame header and writes it to a buffer ** supplied by the caller. A frame-header is made up of a series of ** 4-byte big-endian integers, as follows: |
︙ | ︙ | |||
42568 42569 42570 42571 42572 42573 42574 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ | < < < < < < < | < | 42416 42417 42418 42419 42420 42421 42422 42423 42424 42425 42426 42427 42428 42429 42430 | return rc; } /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ sqlite3OsShmUnmap(pWal->pDbFd, isDelete); } /* ** Open a connection to the WAL file zWalName. The database file must ** already be opened on connection pDbFd. The buffer that zWalName points ** to must remain valid for the lifetime of the returned Wal* handle. ** |
︙ | ︙ | |||
42598 42599 42600 42601 42602 42603 42604 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ SQLITE_PRIVATE int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ | < | 42438 42439 42440 42441 42442 42443 42444 42445 42446 42447 42448 42449 42450 42451 | ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ SQLITE_PRIVATE int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ sqlite3_file *pDbFd, /* The open database file */ const char *zWalName, /* Name of the WAL file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ assert( zWalName && zWalName[0] ); |
︙ | ︙ | |||
42632 42633 42634 42635 42636 42637 42638 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; | < | 42471 42472 42473 42474 42475 42476 42477 42478 42479 42480 42481 42482 42483 42484 | } pRet->pVfs = pVfs; pRet->pWalFd = (sqlite3_file *)&pRet[1]; pRet->pDbFd = pDbFd; pRet->readLock = -1; pRet->zWalName = zWalName; /* Open file handle on the write-ahead log file. */ flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL); rc = sqlite3OsOpen(pVfs, zWalName, pRet->pWalFd, flags, &flags); if( rc==SQLITE_OK && flags&SQLITE_OPEN_READONLY ){ pRet->readOnly = 1; } |
︙ | ︙ | |||
43066 43067 43068 43069 43070 43071 43072 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ | < | < | 42904 42905 42906 42907 42908 42909 42910 42911 42912 42913 42914 42915 42916 42917 42918 | ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ pWal->exclusiveMode = 1; rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf); if( rc==SQLITE_OK ){ isDelete = 1; } } walIndexClose(pWal, isDelete); |
︙ | ︙ | |||
43124 43125 43126 43127 43128 43129 43130 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); | | | 42960 42961 42962 42963 42964 42965 42966 42967 42968 42969 42970 42971 42972 42973 42974 | ** There are two copies of the header at the beginning of the wal-index. ** When reading, read [0] first then [1]. Writes are in the reverse order. ** Memory barriers are used to prevent the compiler or the hardware from ** reordering the reads and writes. */ aHdr = walIndexHdr(pWal); memcpy(&h1, (void *)&aHdr[0], sizeof(h1)); sqlite3OsShmBarrier(pWal->pDbFd); memcpy(&h2, (void *)&aHdr[1], sizeof(h2)); if( memcmp(&h1, &h2, sizeof(h1))!=0 ){ return 1; /* Dirty read */ } if( h1.isInit==0 ){ return 1; /* Malformed header - probably all zeros */ |
︙ | ︙ | |||
43325 43326 43327 43328 43329 43330 43331 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); | | | 43161 43162 43163 43164 43165 43166 43167 43168 43169 43170 43171 43172 43173 43174 43175 | pInfo = walCkptInfo(pWal); if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){ /* The WAL has been completely backfilled (or it is empty). ** and can be safely ignored. */ rc = walLockShared(pWal, WAL_READ_LOCK(0)); sqlite3OsShmBarrier(pWal->pDbFd); if( rc==SQLITE_OK ){ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ /* It is not safe to allow the reader to continue here if frames ** may have been appended to the log before READ_LOCK(0) was obtained. ** When holding READ_LOCK(0), the reader ignores the entire log file, ** which implies that the database file contains a trustworthy ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from |
︙ | ︙ | |||
43419 43420 43421 43422 43423 43424 43425 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ | | | 43255 43256 43257 43258 43259 43260 43261 43262 43263 43264 43265 43266 43267 43268 43269 | ** ** This does not guarantee that the copy of the wal-index header is up to ** date before proceeding. That would not be possible without somehow ** blocking writers. It only guarantees that a dangerous checkpoint or ** log-wrap (either of which would require an exclusive lock on ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid. */ sqlite3OsShmBarrier(pWal->pDbFd); if( pInfo->aReadMark[mxI]!=mxReadMark || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){ walUnlockShared(pWal, WAL_READ_LOCK(mxI)); return WAL_RETRY; }else{ assert( mxReadMark<=pWal->hdr.mxFrame ); |
︙ | ︙ | |||
44062 44063 44064 44065 44066 44067 44068 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do | | < | 43898 43899 43900 43901 43902 43903 43904 43905 43906 43907 43908 43909 43910 43911 43912 43913 43914 43915 43916 43917 43918 | ** locking_mode=EXCLUSIVE. This means that the pWal->readLock must ** be released. Return 1 if the transition is made and 0 if the ** WAL is already in exclusive-locking mode - meaning that this ** routine is a no-op. The pager must already hold the exclusive lock ** on the main database file before invoking this operation. ** ** If op is negative, then do a dry-run of the op==1 case but do ** not actually change anything. The pager uses this to see if it ** should acquire the database exclusive lock prior to invoking ** the op==1 case. */ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op){ int rc; assert( pWal->writeLock==0 ); /* pWal->readLock is usually set, but might be -1 if there was a ** prior error while attempting to acquire are read-lock. This cannot ** happen if the connection is actually in exclusive mode (as no xShmLock ** locks are taken in this case). Nor should the pager attempt to ** upgrade to exclusive-mode following such an error. */ |
︙ | ︙ | |||
44103 44104 44105 44106 44107 44108 44109 | rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } | < < < < < < < < < | 43938 43939 43940 43941 43942 43943 43944 43945 43946 43947 43948 43949 43950 43951 | rc = 1; }else{ rc = pWal->exclusiveMode==0; } return rc; } #endif /* #ifndef SQLITE_OMIT_WAL */ /************** End of wal.c *************************************************/ /************** Begin file btmutex.c *****************************************/ /* ** 2007 August 27 ** |
︙ | ︙ | |||
48291 48292 48293 48294 48295 48296 48297 48298 | ** 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. */ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p){ | > < < | 48117 48118 48119 48120 48121 48122 48123 48124 48125 48126 48127 48128 48129 48130 48131 48132 48133 48134 48135 48136 48137 48138 48139 48140 | ** 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. */ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p){ BtShared *pBt = p->pBt; 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; assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->nTransaction>0 ); rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } |
︙ | ︙ | |||
53220 53221 53222 53223 53224 53225 53226 | ** (stored in BtCursor.aOverflow[]) is allocated and used by function ** accessPayload() (the worker function for sqlite3BtreeData() and ** sqlite3BtreePutData()). */ SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); | > | | 53045 53046 53047 53048 53049 53050 53051 53052 53053 53054 53055 53056 53057 53058 53059 53060 | ** (stored in BtCursor.aOverflow[]) is allocated and used by function ** accessPayload() (the worker function for sqlite3BtreeData() and ** sqlite3BtreePutData()). */ SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert(!pCur->isIncrblobHandle); assert(!pCur->aOverflow); pCur->isIncrblobHandle = 1; } #endif /* ** Set both the "read version" (single byte at byte offset 18) and ** "write version" (single byte at byte offset 19) fields in the database |
︙ | ︙ | |||
56253 56254 56255 56256 56257 56258 56259 | pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ pMem->type = SQLITE_INTEGER; pMem++; | > | | | | > | 56079 56080 56081 56082 56083 56084 56085 56086 56087 56088 56089 56090 56091 56092 56093 56094 56095 56096 56097 56098 | pMem++; pMem->flags = MEM_Int; pMem->u.i = pOp->p2; /* P2 */ pMem->type = SQLITE_INTEGER; pMem++; if( p->explain==1 ){ pMem->flags = MEM_Int; pMem->u.i = pOp->p3; /* P3 */ pMem->type = SQLITE_INTEGER; pMem++; } if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */ assert( p->db->mallocFailed ); return SQLITE_ERROR; } pMem->flags = MEM_Dyn|MEM_Str|MEM_Term; z = displayP4(pOp, pMem->z, 32); |
︙ | ︙ | |||
56301 56302 56303 56304 56305 56306 56307 | #endif { pMem->flags = MEM_Null; /* Comment */ pMem->type = SQLITE_NULL; } } | | | 56129 56130 56131 56132 56133 56134 56135 56136 56137 56138 56139 56140 56141 56142 56143 | #endif { pMem->flags = MEM_Null; /* Comment */ pMem->type = SQLITE_NULL; } } p->nResColumn = 8 - 5*(p->explain-1); p->rc = SQLITE_OK; rc = SQLITE_ROW; } return rc; } #endif /* SQLITE_OMIT_EXPLAIN */ |
︙ | ︙ | |||
59248 59249 59250 59251 59252 59253 59254 | if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } sqlite3Error(p->db, rc, 0); rc = sqlite3ApiExit(p->db, rc); } sqlite3_mutex_leave(p->db->mutex); | < < | 59076 59077 59078 59079 59080 59081 59082 59083 59084 59085 59086 59087 59088 59089 | if( rc==SQLITE_OK && encoding!=0 ){ rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db)); } sqlite3Error(p->db, rc, 0); rc = sqlite3ApiExit(p->db, rc); } sqlite3_mutex_leave(p->db->mutex); } return rc; } /* ** Bind a blob value to an SQL statement variable. |
︙ | ︙ | |||
59492 59493 59494 59495 59496 59497 59498 | ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } | < < < < < < < < | 59318 59319 59320 59321 59322 59323 59324 59325 59326 59327 59328 59329 59330 59331 | ** the first argument to the sqlite3_prepare() that was used to create ** the statement in the first place. */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt *pStmt){ return pStmt ? ((Vdbe*)pStmt)->db : 0; } /* ** Return a pointer to the next prepared statement after pStmt associated ** with database connection pDb. If pStmt is NULL, return the first ** prepared statement for the database connection. Return NULL if there ** are no more. */ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){ |
︙ | ︙ | |||
66251 66252 66253 66254 66255 66256 66257 | ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 66069 66070 66071 66072 66073 66074 66075 66076 66077 66078 66079 66080 66081 66082 66083 66084 66085 66086 66087 | ** Valid sqlite3_blob* handles point to Incrblob structures. */ typedef struct Incrblob Incrblob; struct Incrblob { int flags; /* Copy of "flags" passed to sqlite3_blob_open() */ int nByte; /* Size of open blob, in bytes */ int iOffset; /* Byte offset of blob in cursor data */ BtCursor *pCsr; /* Cursor pointing at blob row */ sqlite3_stmt *pStmt; /* Statement holding cursor open */ sqlite3 *db; /* The associated database */ }; /* ** Open a blob handle. */ SQLITE_API int sqlite3_blob_open( sqlite3* db, /* The database connection */ const char *zDb, /* The attached database containing the blob */ const char *zTable, /* The table containing the blob */ |
︙ | ︙ | |||
66367 66368 66369 66370 66371 66372 66373 | {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ /* One of the following two instructions is replaced by an OP_Noop. */ {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ | | < | | > | < < < < < < > > | | < < | 66114 66115 66116 66117 66118 66119 66120 66121 66122 66123 66124 66125 66126 66127 66128 66129 66130 66131 66132 66133 66134 66135 66136 66137 66138 66139 66140 66141 66142 66143 66144 66145 66146 66147 66148 66149 66150 | {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ /* One of the following two instructions is replaced by an OP_Noop. */ {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ {OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */ {OP_Column, 0, 0, 1}, /* 7 */ {OP_ResultRow, 1, 0, 0}, /* 8 */ {OP_Close, 0, 0, 0}, /* 9 */ {OP_Halt, 0, 0, 0}, /* 10 */ }; Vdbe *v = 0; int rc = SQLITE_OK; char *zErr = 0; Table *pTab; Parse *pParse; *ppBlob = 0; sqlite3_mutex_enter(db->mutex); pParse = sqlite3StackAllocRaw(db, sizeof(*pParse)); if( pParse==0 ){ rc = SQLITE_NOMEM; goto blob_open_out; } do { memset(pParse, 0, sizeof(Parse)); pParse->db = db; sqlite3BtreeEnterAll(db); pTab = sqlite3LocateTable(pParse, 0, zTable, zDb); if( pTab && IsVirtual(pTab) ){ pTab = 0; sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable); } |
︙ | ︙ | |||
66421 66422 66423 66424 66425 66426 66427 | } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* Now search pTab for the exact column. */ | | | 66162 66163 66164 66165 66166 66167 66168 66169 66170 66171 66172 66173 66174 66175 66176 | } rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } /* Now search pTab for the exact column. */ for(iCol=0; iCol < pTab->nCol; iCol++) { if( sqlite3StrICmp(pTab->aCol[iCol].zName, zColumn)==0 ){ break; } } if( iCol==pTab->nCol ){ sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn); |
︙ | ︙ | |||
66475 66476 66477 66478 66479 66480 66481 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } | | < | < < | | 66216 66217 66218 66219 66220 66221 66222 66223 66224 66225 66226 66227 66228 66229 66230 66231 66232 66233 66234 | zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); rc = SQLITE_ERROR; sqlite3BtreeLeaveAll(db); goto blob_open_out; } } v = sqlite3VdbeCreate(db); if( v ){ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); flags = !!flags; /* flags = (flags ? 1 : 0); */ /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); |
︙ | ︙ | |||
66525 66526 66527 66528 66529 66530 66531 | sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } | < < < > | > > > > | > > > | > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > | < | > > > > > > > | | 66263 66264 66265 66266 66267 66268 66269 66270 66271 66272 66273 66274 66275 66276 66277 66278 66279 66280 66281 66282 66283 66284 66285 66286 66287 66288 66289 66290 66291 66292 66293 66294 66295 66296 66297 66298 66299 66300 66301 66302 66303 66304 66305 66306 66307 66308 66309 66310 66311 66312 66313 66314 66315 66316 66317 66318 66319 66320 66321 66322 66323 66324 66325 66326 66327 66328 66329 66330 66331 66332 66333 66334 66335 | sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } sqlite3BtreeLeaveAll(db); if( db->mallocFailed ){ goto blob_open_out; } sqlite3_bind_int64((sqlite3_stmt *)v, 1, iRow); rc = sqlite3_step((sqlite3_stmt *)v); if( rc!=SQLITE_ROW ){ nAttempt++; rc = sqlite3_finalize((sqlite3_stmt *)v); sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, sqlite3_errmsg(db)); v = 0; } } while( nAttempt<5 && rc==SQLITE_SCHEMA ); if( rc==SQLITE_ROW ){ /* The row-record has been opened successfully. Check that the ** column in question contains text or a blob. If it contains ** text, it is up to the caller to get the encoding right. */ Incrblob *pBlob; u32 type = v->apCsr[0]->aType[iCol]; if( type<12 ){ sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "cannot open value of type %s", type==0?"null": type==7?"real": "integer" ); rc = SQLITE_ERROR; goto blob_open_out; } pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); if( db->mallocFailed ){ sqlite3DbFree(db, pBlob); goto blob_open_out; } pBlob->flags = flags; pBlob->pCsr = v->apCsr[0]->pCursor; sqlite3BtreeEnterCursor(pBlob->pCsr); sqlite3BtreeCacheOverflow(pBlob->pCsr); sqlite3BtreeLeaveCursor(pBlob->pCsr); pBlob->pStmt = (sqlite3_stmt *)v; pBlob->iOffset = v->apCsr[0]->aOffset[iCol]; pBlob->nByte = sqlite3VdbeSerialTypeLen(type); pBlob->db = db; *ppBlob = (sqlite3_blob *)pBlob; rc = SQLITE_OK; }else if( rc==SQLITE_OK ){ sqlite3DbFree(db, zErr); zErr = sqlite3MPrintf(db, "no such rowid: %lld", iRow); rc = SQLITE_ERROR; } blob_open_out: if( v && (rc!=SQLITE_OK || db->mallocFailed) ){ sqlite3VdbeFinalize(v); } sqlite3Error(db, rc, zErr); sqlite3DbFree(db, zErr); sqlite3StackFree(db, pParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; } |
︙ | ︙ | |||
66596 66597 66598 66599 66600 66601 66602 | sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, 0); | | | 66374 66375 66376 66377 66378 66379 66380 66381 66382 66383 66384 66385 66386 66387 66388 | sqlite3_mutex_enter(db->mutex); v = (Vdbe*)p->pStmt; if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){ /* Request is out of range. Return a transient error. */ rc = SQLITE_ERROR; sqlite3Error(db, SQLITE_ERROR, 0); } else if( v==0 ){ /* If there is no statement handle, then the blob-handle has ** already been invalidated. Return SQLITE_ABORT in this case. */ rc = SQLITE_ABORT; }else{ /* Call either BtreeData() or BtreePutData(). If SQLITE_ABORT is ** returned, clean-up the statement handle. |
︙ | ︙ | |||
66647 66648 66649 66650 66651 66652 66653 | ** so no mutex is required for access. */ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return p ? p->nByte : 0; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 66425 66426 66427 66428 66429 66430 66431 66432 66433 66434 66435 66436 66437 66438 | ** so no mutex is required for access. */ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *pBlob){ Incrblob *p = (Incrblob *)pBlob; return p ? p->nByte : 0; } #endif /* #ifndef SQLITE_OMIT_INCRBLOB */ /************** End of vdbeblob.c ********************************************/ /************** Begin file journal.c *****************************************/ /* ** 2007 August 22 ** |
︙ | ︙ | |||
69016 69017 69018 69019 69020 69021 69022 | int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ Expr *pRight, /* Right operand */ const Token *pToken /* Argument token */ ){ Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); | < < < | 68755 68756 68757 68758 68759 68760 68761 68762 68763 68764 68765 68766 68767 68768 | int op, /* Expression opcode */ Expr *pLeft, /* Left operand */ Expr *pRight, /* Right operand */ const Token *pToken /* Argument token */ ){ Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1); sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); return p; } /* ** Join two expressions using an AND operator. If either expression is ** NULL, then just return the other expression. */ |
︙ | ︙ | |||
70132 70133 70134 70135 70136 70137 70138 | if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } | < < < < < < < < < < | 69868 69869 69870 69871 69872 69873 69874 69875 69876 69877 69878 69879 69880 69881 | if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); assert( testAddr>0 || pParse->db->mallocFailed ); } switch( pExpr->op ){ case TK_IN: { char affinity; /* Affinity of the LHS of the IN */ KeyInfo keyInfo; /* Keyinfo for the generated table */ int addr; /* Address of OP_OpenEphemeral instruction */ Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ |
︙ | ︙ | |||
84328 84329 84330 84331 84332 84333 84334 | void (*randomness)(int,void*); sqlite3 *(*context_db_handle)(sqlite3_context*); int (*extended_result_codes)(sqlite3*,int); int (*limit)(sqlite3*,int,int); sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); const char *(*sql)(sqlite3_stmt*); int (*status)(int,int*,int*,int); | < < < < < < < < < < < < < < < < < < < < < | 84054 84055 84056 84057 84058 84059 84060 84061 84062 84063 84064 84065 84066 84067 | void (*randomness)(int,void*); sqlite3 *(*context_db_handle)(sqlite3_context*); int (*extended_result_codes)(sqlite3*,int); int (*limit)(sqlite3*,int,int); sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*); const char *(*sql)(sqlite3_stmt*); int (*status)(int,int*,int*,int); }; /* ** The following macros redefine the API routines so that they are ** redirected throught the global sqlite3_api structure. ** ** This header file is also used by the loadext.c source file |
︙ | ︙ | |||
84528 84529 84530 84531 84532 84533 84534 | #define sqlite3_randomness sqlite3_api->randomness #define sqlite3_context_db_handle sqlite3_api->context_db_handle #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes #define sqlite3_limit sqlite3_api->limit #define sqlite3_next_stmt sqlite3_api->next_stmt #define sqlite3_sql sqlite3_api->sql #define sqlite3_status sqlite3_api->status | < < < < < < < < < < < < < < < < < < < < < | 84233 84234 84235 84236 84237 84238 84239 84240 84241 84242 84243 84244 84245 84246 | #define sqlite3_randomness sqlite3_api->randomness #define sqlite3_context_db_handle sqlite3_api->context_db_handle #define sqlite3_extended_result_codes sqlite3_api->extended_result_codes #define sqlite3_limit sqlite3_api->limit #define sqlite3_next_stmt sqlite3_api->next_stmt #define sqlite3_sql sqlite3_api->sql #define sqlite3_status sqlite3_api->status #endif /* SQLITE_CORE */ #define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0; #define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v; #endif /* _SQLITE3EXT_H_ */ |
︙ | ︙ | |||
84866 84867 84868 84869 84870 84871 84872 | ** Added for 3.6.0 */ sqlite3_extended_result_codes, sqlite3_limit, sqlite3_next_stmt, sqlite3_sql, sqlite3_status, | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 84550 84551 84552 84553 84554 84555 84556 84557 84558 84559 84560 84561 84562 84563 | ** Added for 3.6.0 */ sqlite3_extended_result_codes, sqlite3_limit, sqlite3_next_stmt, sqlite3_sql, sqlite3_status, }; /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a ** default entry point name (sqlite3_extension_init) is used. Use ** of the default name is recommended. |
︙ | ︙ | |||
87330 87331 87332 87333 87334 87335 87336 | } rc = pParse->rc; #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", | | | | | 86974 86975 86976 86977 86978 86979 86980 86981 86982 86983 86984 86985 86986 86987 86988 86989 86990 86991 86992 86993 86994 | } rc = pParse->rc; #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){ static const char * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", "order", "from", "detail" }; int iFirst, mx; if( pParse->explain==2 ){ sqlite3VdbeSetNumCols(pParse->pVdbe, 3); iFirst = 8; mx = 11; }else{ sqlite3VdbeSetNumCols(pParse->pVdbe, 8); iFirst = 0; mx = 8; } for(i=iFirst; i<mx; i++){ sqlite3VdbeSetColName(pParse->pVdbe, i-iFirst, COLNAME_NAME, |
︙ | ︙ | |||
88335 88336 88337 88338 88339 88340 88341 | pInfo->aColl[i] = pColl; pInfo->aSortOrder[i] = pItem->sortOrder; } } return pInfo; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 87979 87980 87981 87982 87983 87984 87985 87986 87987 87988 87989 87990 87991 87992 | pInfo->aColl[i] = pColl; pInfo->aSortOrder[i] = pItem->sortOrder; } } return pInfo; } /* ** If the inner loop was generated using a non-null pOrderBy argument, ** then the results were placed in a sorter. After the loop is terminated ** we need to run the sorter and output the results. The following ** routine generates the code needed to do that. */ |
︙ | ︙ | |||
88764 88765 88766 88767 88768 88769 88770 88771 88772 88773 88774 88775 88776 88777 | sqlite3VdbeSetColName(v, i, COLNAME_NAME, sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); } /* ** Given a an expression list (which is really the list of expressions ** that form the result set of a SELECT statement) compute appropriate ** column names for a table that would hold the expression list. ** ** All column names will be unique. ** | > > > > > > > > > > > > > > > > | 88326 88327 88328 88329 88330 88331 88332 88333 88334 88335 88336 88337 88338 88339 88340 88341 88342 88343 88344 88345 88346 88347 88348 88349 88350 88351 88352 88353 88354 88355 | sqlite3VdbeSetColName(v, i, COLNAME_NAME, sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC); } } generateColumnTypes(pParse, pTabList, pEList); } #ifndef SQLITE_OMIT_COMPOUND_SELECT /* ** Name of the connection operator, used for error messages. */ static const char *selectOpName(int id){ char *z; switch( id ){ case TK_ALL: z = "UNION ALL"; break; case TK_INTERSECT: z = "INTERSECT"; break; case TK_EXCEPT: z = "EXCEPT"; break; default: z = "UNION"; break; } return z; } #endif /* SQLITE_OMIT_COMPOUND_SELECT */ /* ** Given a an expression list (which is really the list of expressions ** that form the result set of a SELECT statement) compute appropriate ** column names for a table that would hold the expression list. ** ** All column names will be unique. ** |
︙ | ︙ | |||
88926 88927 88928 88929 88930 88931 88932 | return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bEnabled==0 ); pTab->nRef = 1; pTab->zName = 0; | < | 88504 88505 88506 88507 88508 88509 88510 88511 88512 88513 88514 88515 88516 88517 | return 0; } /* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside ** is disabled */ assert( db->lookaside.bEnabled==0 ); pTab->nRef = 1; pTab->zName = 0; selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol); selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect); pTab->iPKey = -1; if( db->mallocFailed ){ sqlite3DeleteTable(db, pTab); return 0; } |
︙ | ︙ | |||
88997 88998 88999 89000 89001 89002 89003 | v = sqlite3GetVdbe(pParse); if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); | < < | 88574 88575 88576 88577 88578 88579 88580 88581 88582 88583 88584 88585 88586 88587 | v = sqlite3GetVdbe(pParse); if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ if( sqlite3ExprIsInteger(p->pLimit, &n) ){ sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); VdbeComment((v, "LIMIT counter")); if( n==0 ){ sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); } }else{ sqlite3ExprCode(pParse, p->pLimit, iLimit); sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeComment((v, "LIMIT counter")); sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); } |
︙ | ︙ | |||
89099 89100 89101 89102 89103 89104 89105 | ){ int rc = SQLITE_OK; /* Success code from a subroutine */ Select *pPrior; /* Another SELECT immediately to our left */ Vdbe *v; /* Generate code to this VDBE */ SelectDest dest; /* Alternative data destination */ Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ | < < < < | 88674 88675 88676 88677 88678 88679 88680 88681 88682 88683 88684 88685 88686 88687 | ){ int rc = SQLITE_OK; /* Success code from a subroutine */ Select *pPrior; /* Another SELECT immediately to our left */ Vdbe *v; /* Generate code to this VDBE */ SelectDest dest; /* Alternative data destination */ Select *pDelete = 0; /* Chain of simple selects to delete */ sqlite3 *db; /* Database connection */ /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT. */ assert( p && p->pPrior ); /* Calling function guarantees this much */ db = pParse->db; pPrior = p->pPrior; |
︙ | ︙ | |||
89160 89161 89162 89163 89164 89165 89166 | } /* Generate code for the left and right SELECT statements. */ switch( p->op ){ case TK_ALL: { int addr = 0; | < < < < < < < < < < | 88731 88732 88733 88734 88735 88736 88737 88738 88739 88740 88741 88742 88743 88744 88745 88746 88747 88748 88749 88750 88751 88752 88753 88754 88755 88756 88757 88758 88759 88760 88761 88762 88763 88764 | } /* Generate code for the left and right SELECT statements. */ switch( p->op ){ case TK_ALL: { int addr = 0; assert( !pPrior->pLimit ); pPrior->pLimit = p->pLimit; pPrior->pOffset = p->pOffset; rc = sqlite3Select(pParse, pPrior, &dest); p->pLimit = 0; p->pOffset = 0; if( rc ){ goto multi_select_end; } p->pPrior = 0; p->iLimit = pPrior->iLimit; p->iOffset = pPrior->iOffset; if( p->iLimit ){ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeComment((v, "Jump ahead if LIMIT reached")); } rc = sqlite3Select(pParse, p, &dest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; if( addr ){ sqlite3VdbeJumpHere(v, addr); } break; } case TK_EXCEPT: case TK_UNION: { |
︙ | ︙ | |||
89233 89234 89235 89236 89237 89238 89239 | assert( p->pEList ); } /* Code the SELECT statements to our left */ assert( !pPrior->pOrderBy ); sqlite3SelectDestInit(&uniondest, priorOp, unionTab); | < < < | 88794 88795 88796 88797 88798 88799 88800 88801 88802 88803 88804 88805 88806 88807 88808 88809 88810 88811 88812 88813 88814 88815 88816 88817 88818 88819 88820 88821 88822 88823 88824 88825 88826 88827 88828 88829 88830 88831 88832 88833 88834 | assert( p->pEList ); } /* Code the SELECT statements to our left */ assert( !pPrior->pOrderBy ); sqlite3SelectDestInit(&uniondest, priorOp, unionTab); rc = sqlite3Select(pParse, pPrior, &uniondest); if( rc ){ goto multi_select_end; } /* Code the current SELECT statement */ if( p->op==TK_EXCEPT ){ op = SRT_Except; }else{ assert( p->op==TK_UNION ); op = SRT_Union; } p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; uniondest.eDest = op; rc = sqlite3Select(pParse, p, &uniondest); testcase( rc!=SQLITE_OK ); /* Query flattening in sqlite3Select() might refill p->pOrderBy. ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */ sqlite3ExprListDelete(db, p->pOrderBy); pDelete = p->pPrior; p->pPrior = pPrior; p->pOrderBy = 0; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; p->iLimit = 0; p->iOffset = 0; /* Convert the data in the temporary table into whatever form |
︙ | ︙ | |||
89320 89321 89322 89323 89324 89325 89326 | p->addrOpenEphm[0] = addr; p->pRightmost->selFlags |= SF_UsesEphemeral; assert( p->pEList ); /* Code the SELECTs to our left into temporary table "tab1". */ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); | < < < | 88878 88879 88880 88881 88882 88883 88884 88885 88886 88887 88888 88889 88890 88891 88892 88893 88894 88895 88896 88897 88898 88899 88900 88901 88902 88903 88904 88905 88906 88907 88908 88909 88910 88911 | p->addrOpenEphm[0] = addr; p->pRightmost->selFlags |= SF_UsesEphemeral; assert( p->pEList ); /* Code the SELECTs to our left into temporary table "tab1". */ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1); rc = sqlite3Select(pParse, pPrior, &intersectdest); if( rc ){ goto multi_select_end; } /* Code the current SELECT into temporary table "tab2" */ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0); assert( p->addrOpenEphm[1] == -1 ); p->addrOpenEphm[1] = addr; p->pPrior = 0; pLimit = p->pLimit; p->pLimit = 0; pOffset = p->pOffset; p->pOffset = 0; intersectdest.iParm = tab2; rc = sqlite3Select(pParse, p, &intersectdest); testcase( rc!=SQLITE_OK ); pDelete = p->pPrior; p->pPrior = pPrior; sqlite3ExprDelete(db, p->pLimit); p->pLimit = pLimit; p->pOffset = pOffset; /* Generate code to take the intersection of the two temporary ** tables. */ |
︙ | ︙ | |||
89375 89376 89377 89378 89379 89380 89381 | sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; } } | < < | 88930 88931 88932 88933 88934 88935 88936 88937 88938 88939 88940 88941 88942 88943 | sqlite3VdbeResolveLabel(v, iBreak); sqlite3VdbeAddOp2(v, OP_Close, tab2, 0); sqlite3VdbeAddOp2(v, OP_Close, tab1, 0); break; } } /* Compute collating sequences used by ** temporary tables needed to implement the compound select. ** Attach the KeyInfo structure to all temporary tables. ** ** This section is run by the right-most SELECT statement only. ** SELECT statements to the left always skip this part. The right-most ** SELECT might also skip this part if it has no ORDER BY clause and |
︙ | ︙ | |||
89720 89721 89722 89723 89724 89725 89726 | int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ int *aPermute; /* Mapping from ORDER BY terms to result set columns */ | < < < < | 89273 89274 89275 89276 89277 89278 89279 89280 89281 89282 89283 89284 89285 89286 | int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */ KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */ KeyInfo *pKeyMerge; /* Comparison information for merging rows */ sqlite3 *db; /* Database connection */ ExprList *pOrderBy; /* The ORDER BY clause */ int nOrderBy; /* Number of terms in the ORDER BY clause */ int *aPermute; /* Mapping from ORDER BY terms to result set columns */ assert( p->pOrderBy!=0 ); assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */ db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ labelEnd = sqlite3VdbeMakeLabel(v); |
︙ | ︙ | |||
89877 89878 89879 89880 89881 89882 89883 | /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. */ VdbeNoopComment((v, "Begin coroutine for left SELECT")); pPrior->iLimit = regLimitA; | < < | 89426 89427 89428 89429 89430 89431 89432 89433 89434 89435 89436 89437 89438 89439 89440 89441 89442 89443 89444 89445 89446 89447 89448 89449 89450 89451 89452 89453 | /* Generate a coroutine to evaluate the SELECT statement to the ** left of the compound operator - the "A" select. */ VdbeNoopComment((v, "Begin coroutine for left SELECT")); pPrior->iLimit = regLimitA; sqlite3Select(pParse, pPrior, &destA); sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); VdbeNoopComment((v, "End coroutine for left SELECT")); /* Generate a coroutine to evaluate the SELECT statement on ** the right - the "B" select */ addrSelectB = sqlite3VdbeCurrentAddr(v); VdbeNoopComment((v, "Begin coroutine for right SELECT")); savedLimit = p->iLimit; savedOffset = p->iOffset; p->iLimit = regLimitB; p->iOffset = 0; sqlite3Select(pParse, p, &destB); p->iLimit = savedLimit; p->iOffset = savedOffset; sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); VdbeNoopComment((v, "End coroutine for right SELECT")); |
︙ | ︙ | |||
89929 89930 89931 89932 89933 89934 89935 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); }else{ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); | < < | 89476 89477 89478 89479 89480 89481 89482 89483 89484 89485 89486 89487 89488 89489 89490 89491 89492 89493 89494 89495 89496 | if( op==TK_EXCEPT || op==TK_INTERSECT ){ addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd); }else{ addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB); sqlite3VdbeAddOp1(v, OP_Yield, regAddrB); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA); } /* Generate a subroutine to run when the results from select B ** are exhausted and only data in select A remains. */ if( op==TK_INTERSECT ){ addrEofB = addrEofA; }else{ VdbeNoopComment((v, "eof-B subroutine")); addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd); sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA); sqlite3VdbeAddOp1(v, OP_Yield, regAddrA); sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB); } |
︙ | ︙ | |||
90025 90026 90027 90028 90029 90030 90031 | if( p->pPrior ){ sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ | < | 89570 89571 89572 89573 89574 89575 89576 89577 89578 89579 89580 89581 89582 89583 | if( p->pPrior ){ sqlite3SelectDelete(db, p->pPrior); } p->pPrior = pPrior; /*** TBD: Insert subroutine calls to close cursors on incomplete **** subqueries ****/ return SQLITE_OK; } #endif #if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) /* Forward Declarations */ static void substExprList(sqlite3*, ExprList*, int, ExprList*); |
︙ | ︙ | |||
90759 90760 90761 90762 90763 90764 90765 | pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); pTab->iPKey = -1; | < | 90303 90304 90305 90306 90307 90308 90309 90310 90311 90312 90313 90314 90315 90316 | pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table)); if( pTab==0 ) return WRC_Abort; pTab->nRef = 1; pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab); while( pSel->pPrior ){ pSel = pSel->pPrior; } selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol); pTab->iPKey = -1; pTab->tabFlags |= TF_Ephemeral; #endif }else{ /* An ordinary table or view name in the FROM clause */ assert( pFrom->pTab==0 ); pFrom->pTab = pTab = sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase); |
︙ | ︙ | |||
91253 91254 91255 91256 91257 91258 91259 | int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ | < < < < < | 90796 90797 90798 90799 90800 90801 90802 90803 90804 90805 90806 90807 90808 90809 | int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */ AggInfo sAggInfo; /* Information used by aggregate queries */ int iEnd; /* Address of the end of the query */ sqlite3 *db; /* The database connection */ db = pParse->db; if( p==0 || db->mallocFailed || pParse->nErr ){ return 1; } if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; memset(&sAggInfo, 0, sizeof(sAggInfo)); |
︙ | ︙ | |||
91329 91330 91331 91332 91333 91334 91335 | isAgg = 1; p->selFlags |= SF_Aggregate; } i = -1; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); assert( pItem->isPopulated==0 ); | < < | 90867 90868 90869 90870 90871 90872 90873 90874 90875 90876 90877 90878 90879 90880 90881 90882 | isAgg = 1; p->selFlags |= SF_Aggregate; } i = -1; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); assert( pItem->isPopulated==0 ); sqlite3Select(pParse, pSub, &dest); pItem->isPopulated = 1; } if( /*pParse->nErr ||*/ db->mallocFailed ){ goto select_end; } pParse->nHeight -= sqlite3SelectExprHeight(p); pTabList = p->pSrc; if( !IgnorableOrderby(pDest) ){ |
︙ | ︙ | |||
91366 91367 91368 91369 91370 91371 91372 | pLoop->pRightmost = p; pLoop->pNext = pRight; pRight = pLoop; } mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; if( mxSelect && cnt>mxSelect ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); | | | < < > | 90902 90903 90904 90905 90906 90907 90908 90909 90910 90911 90912 90913 90914 90915 90916 90917 90918 90919 90920 90921 90922 90923 90924 90925 90926 90927 90928 90929 90930 90931 | pLoop->pRightmost = p; pLoop->pNext = pRight; pRight = pLoop; } mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT]; if( mxSelect && cnt>mxSelect ){ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT"); return 1; } } return multiSelect(pParse, p, pDest); } #endif /* If possible, rewrite the query to use GROUP BY instead of DISTINCT. ** GROUP BY might use an index, DISTINCT never does. */ assert( p->pGroupBy==0 || (p->selFlags & SF_Aggregate)!=0 ); if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct ){ p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0); pGroupBy = p->pGroupBy; p->selFlags &= ~SF_Distinct; isDistinct = 0; } /* If there is both a GROUP BY and an ORDER BY clause and they are ** identical, then disable the ORDER BY clause since the GROUP BY ** will cause elements to come out in the correct order. This is ** an optimization - the correct answer should result regardless. ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER |
︙ | ︙ | |||
91425 91426 91427 91428 91429 91430 91431 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); | < | < | 90960 90961 90962 90963 90964 90965 90966 90967 90968 90969 90970 90971 90972 90973 90974 90975 90976 90977 90978 90979 90980 90981 90982 90983 90984 90985 90986 90987 90988 90989 90990 90991 90992 90993 90994 90995 90996 | if( pDest->eDest==SRT_EphemTab ){ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr); } /* Set the limiter. */ iEnd = sqlite3VdbeMakeLabel(v); computeLimitRegisters(pParse, p, iEnd); /* Open a virtual index to use for the distinct set. */ if( isDistinct ){ KeyInfo *pKeyInfo; assert( isAgg || pGroupBy ); distinct = pParse->nTab++; pKeyInfo = keyInfoFromExprList(pParse, p->pEList); sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF); sqlite3VdbeChangeP5(v, BTREE_UNORDERED); }else{ distinct = -1; } /* Aggregate and non-aggregate queries are handled differently */ if( !isAgg && pGroupBy==0 ){ /* This case is for non-aggregate queries ** Begin the database scan */ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, 0); if( pWInfo==0 ) goto select_end; /* If sorting index that was created by a prior OP_OpenEphemeral ** instruction ended up not being needed, then change the OP_OpenEphemeral ** into an OP_Noop. */ if( addrSortIndex>=0 && pOrderBy==0 ){ sqlite3VdbeChangeToNoop(v, addrSortIndex, 1); |
︙ | ︙ | |||
91494 91495 91496 91497 91498 91499 91500 | for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ pItem->iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } | < < < | 91027 91028 91029 91030 91031 91032 91033 91034 91035 91036 91037 91038 91039 91040 | for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){ pItem->iAlias = 0; } for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){ pItem->iAlias = 0; } } /* Create a label to jump to when we want to abort the query */ addrEnd = sqlite3VdbeMakeLabel(v); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in |
︙ | ︙ | |||
91593 91594 91595 91596 91597 91598 91599 | ** in sorted order */ int regBase; int regRecord; int nCol; int nGroupBy; | < < < | 91123 91124 91125 91126 91127 91128 91129 91130 91131 91132 91133 91134 91135 91136 | ** in sorted order */ int regBase; int regRecord; int nCol; int nGroupBy; groupBySort = 1; nGroupBy = pGroupBy->nExpr; nCol = nGroupBy + 1; j = nGroupBy+1; for(i=0; i<sAggInfo.nColumn; i++){ if( sAggInfo.aCol[i].iSorterColumn>=j ){ nCol++; |
︙ | ︙ | |||
91857 91858 91859 91860 91861 91862 91863 | pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); } /* endif aggregate query */ | < < < < < < | 91384 91385 91386 91387 91388 91389 91390 91391 91392 91393 91394 91395 91396 91397 91398 91399 91400 91401 91402 91403 91404 91405 91406 91407 91408 91409 91410 91411 91412 91413 91414 91415 91416 91417 | pDest, addrEnd, addrEnd); sqlite3ExprListDelete(db, pDel); } sqlite3VdbeResolveLabel(v, addrEnd); } /* endif aggregate query */ /* If there is an ORDER BY clause, then we need to sort the results ** and send them to the callback one by one. */ if( pOrderBy ){ generateSortTail(pParse, p, v, pEList->nExpr, pDest); } /* Jump here to skip this query */ sqlite3VdbeResolveLabel(v, iEnd); /* The SELECT was successfully coded. Set the return code to 0 ** to indicate no errors. */ rc = 0; /* Control jumps to here if an error is encountered above, or upon ** successful coding of the SELECT. */ select_end: /* Identify column names if results of the SELECT are to be output. */ if( rc==SQLITE_OK && pDest->eDest==SRT_Output ){ generateColumnNames(pParse, pTabList, pEList); } |
︙ | ︙ | |||
94972 94973 94974 94975 94976 94977 94978 | pTab->aCol = pParse->pNewTable->aCol; pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ | | | 94493 94494 94495 94496 94497 94498 94499 94500 94501 94502 94503 94504 94505 94506 94507 | pTab->aCol = pParse->pNewTable->aCol; pTab->nCol = pParse->pNewTable->nCol; pParse->pNewTable->nCol = 0; pParse->pNewTable->aCol = 0; } db->pVTab = 0; }else{ sqlite3Error(db, SQLITE_ERROR, zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; } pParse->declareVtab = 0; if( pParse->pVdbe ){ sqlite3VdbeFinalize(pParse->pVdbe); |
︙ | ︙ | |||
95434 95435 95436 95437 95438 95439 95440 95441 95442 95443 95444 95445 95446 95447 | /* ** A WhereCost object records a lookup strategy and the estimated ** cost of pursuing that strategy. */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ Bitmask used; /* Bitmask of cursors used by this plan */ }; /* ** Bitmasks for the operators that indices are able to exploit. An ** OR-ed combination of these values can be used when searching for ** terms in the where clause. | > | 94955 94956 94957 94958 94959 94960 94961 94962 94963 94964 94965 94966 94967 94968 94969 | /* ** A WhereCost object records a lookup strategy and the estimated ** cost of pursuing that strategy. */ struct WhereCost { WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ double nRow; /* Estimated number of output rows */ Bitmask used; /* Bitmask of cursors used by this plan */ }; /* ** Bitmasks for the operators that indices are able to exploit. An ** OR-ed combination of these values can be used when searching for ** terms in the where clause. |
︙ | ︙ | |||
95476 95477 95478 95479 95480 95481 95482 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ | | < | 94998 94999 95000 95001 95002 95003 95004 95005 95006 95007 95008 95009 95010 95011 95012 95013 95014 95015 | #define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */ #define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */ #define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */ #define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */ #define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */ #define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */ #define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */ #define WHERE_NOT_FULLSCAN 0x000f3000 /* Does not do a full table scan */ #define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */ #define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */ #define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */ #define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */ #define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */ #define WHERE_REVERSE 0x02000000 /* Scan in reverse order */ #define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */ #define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */ #define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */ #define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */ |
︙ | ︙ | |||
96823 96824 96825 96826 96827 96828 96829 | ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ | | < | | 96344 96345 96346 96347 96348 96349 96350 96351 96352 96353 96354 96355 96356 96357 96358 96359 | ){ #ifndef SQLITE_OMIT_OR_OPTIMIZATION const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */ WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */ WhereTerm *pTerm; /* A single term of the WHERE clause */ /* No OR-clause optimization allowed if the NOT INDEXED clause is used */ if( pSrc->notIndexed ){ return; } /* Search the WHERE clause terms for a usable WO_OR term. */ for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( pTerm->eOperator==WO_OR && ((pTerm->prereqAll & ~maskSrc) & notReady)==0 |
︙ | ︙ | |||
96863 96864 96865 96866 96867 96868 96869 | tempWC.a = pOrTerm; tempWC.nTerm = 1; bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } rTotal += sTermCost.rCost; | | > < | 96383 96384 96385 96386 96387 96388 96389 96390 96391 96392 96393 96394 96395 96396 96397 96398 96399 96400 96401 96402 96403 96404 96405 96406 96407 96408 96409 96410 96411 96412 96413 96414 96415 96416 96417 | tempWC.a = pOrTerm; tempWC.nTerm = 1; bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost); }else{ continue; } rTotal += sTermCost.rCost; nRow += sTermCost.nRow; used |= sTermCost.used; if( rTotal>=pCost->rCost ) break; } /* If there is an ORDER BY clause, increase the scan cost to account ** for the cost of the sort. */ if( pOrderBy!=0 ){ WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", rTotal, rTotal+nRow*estLog(nRow))); rTotal += nRow*estLog(nRow); } /* If the cost of scanning using this OR term for optimization is ** less than the current cost stored in pCost, replace the contents ** of pCost. */ WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); if( rTotal<pCost->rCost ){ pCost->rCost = rTotal; pCost->nRow = nRow; pCost->used = used; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } } } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ } |
︙ | ︙ | |||
96967 96968 96969 96970 96971 96972 96973 | /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; | | | 96487 96488 96489 96490 96491 96492 96493 96494 96495 96496 96497 96498 96499 96500 96501 | /* Search for any equality comparison term */ pWCEnd = &pWC->a[pWC->nTerm]; for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ if( termCanDriveIndex(pTerm, pSrc, notReady) ){ WHERETRACE(("auto-index reduces cost from %.2f to %.2f\n", pCost->rCost, costTempIdx)); pCost->rCost = costTempIdx; pCost->nRow = logN + 1; pCost->plan.wsFlags = WHERE_TEMP_INDEX; pCost->used = pTerm->prereqRight; break; } } } #else |
︙ | ︙ | |||
98040 98041 98042 98043 98044 98045 98046 | notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) | | > < | 97560 97561 97562 97563 97564 97565 97566 97567 97568 97569 97570 97571 97572 97573 97574 97575 97576 97577 97578 | notReady, nRow, cost, used )); /* If this index is the best we have seen so far, then record this ** index and its cost in the pCost structure. */ if( (!pIdx || wsFlags) && (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->nRow)) ){ pCost->rCost = cost; pCost->nRow = nRow; pCost->used = used; pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; pCost->plan.u.pIdx = pIdx; } /* If there was an INDEXED BY clause, then only that one index is ** considered. */ |
︙ | ︙ | |||
98372 98373 98374 98375 98376 98377 98378 | } } } } *pzAff = zAff; return regBase; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 97892 97893 97894 97895 97896 97897 97898 97899 97900 97901 97902 97903 97904 97905 | } } } } *pzAff = zAff; return regBase; } /* ** Generate code for the start of the iLevel-th loop in the WHERE clause ** implementation described by pWInfo. */ static Bitmask codeOneLoopStart( WhereInfo *pWInfo, /* Complete information about the WHERE clause */ |
︙ | ︙ | |||
98935 98936 98937 98938 98939 98940 98941 | /* If there are inequality constraints, check that the value ** of the table column that the inequality contrains is not NULL. ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); | | | 98300 98301 98302 98303 98304 98305 98306 98307 98308 98309 98310 98311 98312 98313 98314 | /* If there are inequality constraints, check that the value ** of the table column that the inequality contrains is not NULL. ** If it is, jump to the next iteration of the loop. */ r1 = sqlite3GetTempReg(pParse); testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ); testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ); if( pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT) ){ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1); sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont); } sqlite3ReleaseTempReg(pParse, r1); /* Seek the table cursor, if required */ disableTerm(pLevel, pRangeStart); |
︙ | ︙ | |||
99069 99070 99071 99072 99073 99074 99075 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ | < < < | 98434 98435 98436 98437 98438 98439 98440 98441 98442 98443 98444 98445 98446 98447 | if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, regRowid); sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, sqlite3VdbeCurrentAddr(v)+2, r, iSet); |
︙ | ︙ | |||
99467 99468 99469 99470 99471 99472 99473 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; | < | 98829 98830 98831 98832 98833 98834 98835 98836 98837 98838 98839 98840 98841 98842 | Bitmask m; /* Bitmask value for j or bestJ */ int isOptimal; /* Iterator for optimal/non-optimal search */ int nUnconstrained; /* Number tables without INDEXED BY */ Bitmask notIndexed; /* Mask of tables that cannot use an index */ memset(&bestPlan, 0, sizeof(bestPlan)); bestPlan.rCost = SQLITE_BIG_DBL; /* Loop through the remaining entries in the FROM clause to find the ** next nested loop. The loop tests all FROM clause entries ** either once or twice. ** ** The first test is always performed if there are two or more entries ** remaining and never performed if there is only one FROM clause entry |
︙ | ︙ | |||
99532 99533 99534 99535 99536 99537 99538 | if( j==iFrom ) iFrom++; continue; } mask = (isOptimal ? m : notReady); pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); if( pTabItem->pIndex==0 ) nUnconstrained++; | < < | 98893 98894 98895 98896 98897 98898 98899 98900 98901 98902 98903 98904 98905 98906 | if( j==iFrom ) iFrom++; continue; } mask = (isOptimal ? m : notReady); pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); if( pTabItem->pIndex==0 ) nUnconstrained++; assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else |
︙ | ︙ | |||
99584 99585 99586 99587 99588 99589 99590 | */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */ | | < < | | | < | < | < | 98943 98944 98945 98946 98947 98948 98949 98950 98951 98952 98953 98954 98955 98956 98957 98958 98959 98960 98961 98962 98963 98964 98965 98966 98967 98968 98969 98970 98971 98972 98973 98974 98975 98976 98977 98978 98979 98980 98981 98982 98983 98984 98985 | */ if( (sCost.used¬Ready)==0 /* (1) */ && (bestJ<0 || (notIndexed&m)!=0 /* (2) */ || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0) && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) && (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */ || (sCost.rCost<=bestPlan.rCost && sCost.nRow<bestPlan.nRow)) ){ WHERETRACE(("... best so far with cost=%g and nRow=%g\n", sCost.rCost, sCost.nRow)); bestPlan = sCost; bestJ = j; } if( doNotReorder ) break; } } assert( bestJ>=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ, pLevel-pWInfo->a)); if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){ *ppOrderBy = 0; } andFlags &= bestPlan.plan.wsFlags; pLevel->plan = bestPlan.plan; testcase( bestPlan.plan.wsFlags & WHERE_INDEXED ); testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX ); if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){ pLevel->iIdxCur = pParse->nTab++; }else{ pLevel->iIdxCur = -1; } notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor); pLevel->iFrom = (u8)bestJ; if( bestPlan.nRow>=(double)1 ) pParse->nQueryLoop *= bestPlan.nRow; /* Check that if the table scanned by this loop iteration had an ** INDEXED BY clause attached to it, that the named index is being ** used for the scan. If not, then query compilation has failed. ** Return an error. */ pIdx = pTabList->a[bestJ].pIndex; |
︙ | ︙ | |||
99665 99666 99667 99668 99669 99670 99671 | } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | 99019 99020 99021 99022 99023 99024 99025 99026 99027 99028 99029 99030 99031 99032 99033 99034 99035 99036 99037 99038 99039 99040 99041 99042 99043 99044 99045 99046 99047 99048 99049 99050 99051 99052 99053 99054 99055 99056 99057 99058 99059 99060 99061 99062 99063 99064 99065 99066 99067 99068 99069 99070 | } /* Open all tables in the pTabList and any indices selected for ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ notReady = ~(Bitmask)0; for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){ Table *pTab; /* Table to open */ int iDb; /* Index of database containing table/index */ #ifndef SQLITE_OMIT_EXPLAIN if( pParse->explain==2 ){ char *zMsg; struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; zMsg = sqlite3MPrintf(db, "TABLE %s", pItem->zName); if( pItem->zAlias ){ zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias); } if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){ zMsg = sqlite3MAppendf(db, zMsg, "%s WITH AUTOMATIC INDEX", zMsg); }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ zMsg = sqlite3MAppendf(db, zMsg, "%s WITH INDEX %s", zMsg, pLevel->plan.u.pIdx->zName); }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){ zMsg = sqlite3MAppendf(db, zMsg, "%s VIA MULTI-INDEX UNION", zMsg); }else if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){ zMsg = sqlite3MAppendf(db, zMsg, "%s USING PRIMARY KEY", zMsg); } #ifndef SQLITE_OMIT_VIRTUALTABLE else if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg, pVtabIdx->idxNum, pVtabIdx->idxStr); } #endif if( pLevel->plan.wsFlags & WHERE_ORDERBY ){ zMsg = sqlite3MAppendf(db, zMsg, "%s ORDER BY", zMsg); } sqlite3VdbeAddOp4(v, OP_Explain, i, pLevel->iFrom, 0, zMsg, P4_DYNAMIC); } #endif /* SQLITE_OMIT_EXPLAIN */ pTabItem = &pTabList->a[pLevel->iFrom]; pTab = pTabItem->pTab; pLevel->iTabCur = pTabItem->iCursor; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){ /* Do nothing */ }else #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); |
︙ | ︙ | |||
99729 99730 99731 99732 99733 99734 99735 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; i<nTabList; i++){ | < < | | 99112 99113 99114 99115 99116 99117 99118 99119 99120 99121 99122 99123 99124 99125 99126 99127 | /* Generate the code to do the search. Each iteration of the for ** loop below generates code for a single nested loop of the VM ** program. */ notReady = ~(Bitmask)0; for(i=0; i<nTabList; i++){ notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady); pWInfo->iContinue = pWInfo->a[i].addrCont; } #ifdef SQLITE_TEST /* For testing and debugging use only */ /* Record in the query plan information about the current table ** and the index used to access it (if any). If the table itself ** is not used, its name is just '{}'. If no index is used ** the index is listed as "{}". If the primary key is used the |
︙ | ︙ | |||
106134 106135 106136 106137 106138 106139 106140 | p->xCmp = 0; } } } } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); | | | | | | | > | 105515 105516 105517 105518 105519 105520 105521 105522 105523 105524 105525 105526 105527 105528 105529 105530 105531 105532 105533 105534 105535 | p->xCmp = 0; } } } } pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1); if( pColl ){ pColl->xCmp = xCompare; pColl->pUser = pCtx; pColl->xDel = xDel; pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); pColl->type = collType; } sqlite3Error(db, SQLITE_OK, 0); return SQLITE_OK; } /* ** This array defines hard upper bounds on limit values. The |
︙ | ︙ | |||
108107 108108 108109 108110 108111 108112 108113 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ | < < < < < < < < < < | | > > > > > > > > > > > | < < < | 107489 107490 107491 107492 107493 107494 107495 107496 107497 107498 107499 107500 107501 107502 107503 107504 107505 107506 107507 107508 107509 107510 107511 107512 107513 107514 107515 107516 107517 107518 107519 107520 107521 107522 107523 107524 107525 107526 107527 107528 107529 107530 107531 107532 107533 107534 107535 107536 107537 107538 107539 107540 107541 107542 107543 107544 107545 107546 107547 107548 107549 107550 107551 107552 107553 107554 107555 107556 107557 107558 107559 | ** amalgamation. */ #ifndef SQLITE_AMALGAMATION /* ** Macros indicating that conditional expressions are always true or ** false. */ # define ALWAYS(x) (x) # define NEVER(X) (x) /* ** Internal types used by SQLite. */ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ typedef short int i16; /* 2-byte (or larger) signed integer */ typedef unsigned int u32; /* 4-byte unsigned integer */ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) #endif typedef struct Fts3Table Fts3Table; typedef struct Fts3Cursor Fts3Cursor; typedef struct Fts3Expr Fts3Expr; typedef struct Fts3Phrase Fts3Phrase; typedef struct Fts3SegReader Fts3SegReader; typedef struct Fts3SegFilter Fts3SegFilter; /* ** A connection to a fulltext index is an instance of the following ** structure. The xCreate and xConnect methods create an instance ** of this structure and xDestroy and xDisconnect free that instance. ** All other methods receive a pointer to the structure as one of their ** arguments. */ struct Fts3Table { sqlite3_vtab base; /* Base class used by SQLite core */ sqlite3 *db; /* The database connection */ const char *zDb; /* logical database name */ const char *zName; /* virtual table name */ int nColumn; /* number of named columns in virtual table */ char **azColumn; /* column names. malloced */ sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ /* Precompiled statements used by the implementation. Each of these ** statements is run and reset within a single virtual table API call. */ sqlite3_stmt *aStmt[25]; /* Pointer to string containing the SQL: ** ** "SELECT block FROM %_segments WHERE blockid BETWEEN ? AND ? ** ORDER BY blockid" */ char *zSelectLeaves; int nLeavesStmt; /* Valid statements in aLeavesStmt */ int nLeavesTotal; /* Total number of prepared leaves stmts */ int nLeavesAlloc; /* Allocated size of aLeavesStmt */ sqlite3_stmt **aLeavesStmt; /* Array of prepared zSelectLeaves stmts */ int nNodeSize; /* Soft limit for node size */ u8 bHasContent; /* True if %_content table exists */ u8 bHasDocsize; /* True if %_docsize table exists */ /* The following hash table is used to buffer pending index updates during ** transactions. Variable nPendingData estimates the memory size of the ** pending data, including hash table overhead, but not malloc overhead. ** When nPendingData exceeds nMaxPendingData, the buffer is flushed ** automatically. Variable iPrevDocid is the docid of the most recently ** inserted record. |
︙ | ︙ | |||
108192 108193 108194 108195 108196 108197 108198 | struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ | < < < < < < < | 107572 107573 107574 107575 107576 107577 107578 107579 107580 107581 107582 107583 107584 107585 107586 107587 107588 107589 107590 107591 107592 107593 | struct Fts3Cursor { sqlite3_vtab_cursor base; /* Base class used by SQLite core */ i16 eSearch; /* Search strategy (see below) */ u8 isEof; /* True if at End Of Results */ u8 isRequireSeek; /* True if must seek pStmt to %_content row */ sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ Fts3Expr *pExpr; /* Parsed MATCH query string */ sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ char *pNextId; /* Pointer into the body of aDoclist */ char *aDoclist; /* List of docids for full-text queries */ int nDoclist; /* Size of buffer at aDoclist */ int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */ u32 *aMatchinfo; /* Information about most recent match */ }; /* ** The Fts3Cursor.eSearch member is always set to one of the following. ** Actualy, Fts3Cursor.eSearch can be greater than or equal to ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index ** of the column to be searched. For example, in ** ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
︙ | ︙ | |||
108229 108230 108231 108232 108233 108234 108235 | #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. | | | < < < < < < < < < < < < < < < < > > > > | | 107602 107603 107604 107605 107606 107607 107608 107609 107610 107611 107612 107613 107614 107615 107616 107617 107618 107619 107620 107621 107622 107623 107624 107625 107626 107627 | #define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ #define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ #define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ /* ** A "phrase" is a sequence of one or more tokens that must match in ** sequence. A single token is the base case and the most common case. ** For a sequence of tokens contained in "...", nToken will be the number ** of tokens in the string. */ struct Fts3Phrase { int nToken; /* Number of tokens in the phrase */ int iColumn; /* Index of column this phrase must match */ int isNot; /* Phrase prefixed by unary not (-) operator */ struct PhraseToken { char *z; /* Text of the token */ int n; /* Number of bytes in buffer pointed to by z */ int isPrefix; /* True if token ends in with a "*" character */ } aToken[1]; /* One entry for each token in the phrase */ }; /* ** A tree of these objects forms the RHS of a MATCH operator. ** ** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded ** is true, then aDoclist points to a malloced buffer, size nDoclist bytes, |
︙ | ︙ | |||
108302 108303 108304 108305 108306 108307 108308 108309 108310 108311 108312 108313 108314 108315 108316 108317 108318 108319 108320 108321 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 /* fts3_write.c */ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *); SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *); SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); | > > > > > | < < < < < < < < < | 107663 107664 107665 107666 107667 107668 107669 107670 107671 107672 107673 107674 107675 107676 107677 107678 107679 107680 107681 107682 107683 107684 107685 107686 107687 107688 107689 107690 107691 107692 107693 107694 107695 107696 107697 107698 107699 | #define FTSQUERY_NEAR 1 #define FTSQUERY_NOT 2 #define FTSQUERY_AND 3 #define FTSQUERY_OR 4 #define FTSQUERY_PHRASE 5 /* fts3_init.c */ SQLITE_PRIVATE int sqlite3Fts3DeleteVtab(int, sqlite3_vtab *); SQLITE_PRIVATE int sqlite3Fts3InitVtab(int, sqlite3*, void*, int, const char*const*, sqlite3_vtab **, char **); /* fts3_write.c */ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *); SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64, sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *); SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, int (*)(Fts3Table *, void *, char *, int, char *, int), void * ); SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*); SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeLocal(Fts3Cursor*, u32*); SQLITE_PRIVATE int sqlite3Fts3MatchinfoDocsizeGlobal(Fts3Cursor*, u32*); SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *); /* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ #define FTS3_SEGMENT_REQUIRE_POS 0x00000001 #define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 #define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 #define FTS3_SEGMENT_PREFIX 0x00000008 |
︙ | ︙ | |||
108352 108353 108354 108355 108356 108357 108358 | SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); SQLITE_PRIVATE char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); | | < | | < | 107709 107710 107711 107712 107713 107714 107715 107716 107717 107718 107719 107720 107721 107722 107723 107724 107725 107726 107727 107728 107729 107730 107731 | SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); SQLITE_PRIVATE char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Table *, Fts3Expr *); SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *, Fts3Expr *, int); /* fts3_tokenizer.c */ SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, sqlite3_tokenizer **, const char **, char ** ); /* fts3_snippet.c */ SQLITE_PRIVATE void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*); SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const char *, const char *, const char *, int, int ); SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *); |
︙ | ︙ | |||
108518 108519 108520 108521 108522 108523 108524 | ** The xDisconnect() virtual table method. */ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); | < > > > | > | | 107873 107874 107875 107876 107877 107878 107879 107880 107881 107882 107883 107884 107885 107886 107887 107888 107889 107890 107891 107892 107893 107894 107895 107896 107897 107898 107899 107900 107901 107902 107903 107904 107905 107906 107907 | ** The xDisconnect() virtual table method. */ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ Fts3Table *p = (Fts3Table *)pVtab; int i; assert( p->nPendingData==0 ); /* Free any prepared statements held */ for(i=0; i<SizeofArray(p->aStmt); i++){ sqlite3_finalize(p->aStmt[i]); } for(i=0; i<p->nLeavesStmt; i++){ sqlite3_finalize(p->aLeavesStmt[i]); } sqlite3_free(p->zSelectLeaves); sqlite3_free(p->aLeavesStmt); /* Invoke the tokenizer destructor to free the tokenizer. */ p->pTokenizer->pModule->xDestroy(p->pTokenizer); sqlite3_free(p); return SQLITE_OK; } /* ** Construct one or more SQL statements from the format string given ** and then evaluate those statements. The success code is writting ** into *pRc. ** ** If *pRc is initially non-zero then this routine is a no-op. */ static void fts3DbExec( int *pRc, /* Success code */ sqlite3 *db, /* Database in which to run SQL */ |
︙ | ︙ | |||
108587 108588 108589 108590 108591 108592 108593 | } /* ** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table ** passed as the first argument. This is done as part of the xConnect() ** and xCreate() methods. | < < < < | < | | | | | | | | | | | | | > | | | | | | | | < > | | | | | | | | | | | | > | 107945 107946 107947 107948 107949 107950 107951 107952 107953 107954 107955 107956 107957 107958 107959 107960 107961 107962 107963 107964 107965 107966 107967 107968 107969 107970 107971 107972 107973 107974 107975 107976 107977 107978 107979 107980 107981 107982 107983 107984 107985 107986 107987 107988 107989 107990 107991 107992 107993 107994 107995 107996 107997 107998 107999 108000 108001 108002 108003 108004 108005 108006 108007 108008 108009 108010 108011 108012 108013 108014 108015 108016 108017 108018 | } /* ** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table ** passed as the first argument. This is done as part of the xConnect() ** and xCreate() methods. */ static int fts3DeclareVtab(Fts3Table *p){ int i; /* Iterator variable */ int rc; /* Return code */ char *zSql; /* SQL statement passed to declare_vtab() */ char *zCols; /* List of user defined columns */ /* Create a list of user columns for the virtual table */ zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); for(i=1; zCols && i<p->nColumn; i++){ zCols = sqlite3_mprintf("%z%Q, ", zCols, p->azColumn[i]); } /* Create the whole "CREATE TABLE" statement to pass to SQLite */ zSql = sqlite3_mprintf( "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN)", zCols, p->zName ); if( !zCols || !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_declare_vtab(p->db, zSql); } sqlite3_free(zSql); sqlite3_free(zCols); return rc; } /* ** Create the backing store tables (%_content, %_segments and %_segdir) ** required by the FTS3 table passed as the only argument. This is done ** as part of the vtab xCreate() method. ** ** If the p->bHasDocsize boolean is true (indicating that this is an ** FTS4 table, not an FTS3 table) then also create the %_docsize and ** %_stat tables required by FTS4. */ static int fts3CreateTables(Fts3Table *p){ int rc = SQLITE_OK; /* Return code */ int i; /* Iterator variable */ char *zContentCols; /* Columns of %_content table */ sqlite3 *db = p->db; /* The database connection */ /* Create a list of user columns for the content table */ if( p->bHasContent ){ zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); for(i=0; zContentCols && i<p->nColumn; i++){ char *z = p->azColumn[i]; zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); } if( zContentCols==0 ) rc = SQLITE_NOMEM; /* Create the content table */ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_content'(%s)", p->zDb, p->zName, zContentCols ); sqlite3_free(zContentCols); } /* Create other tables */ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);", p->zDb, p->zName ); fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_segdir'(" |
︙ | ︙ | |||
108672 108673 108674 108675 108676 108677 108678 | p->zDb, p->zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", p->zDb, p->zName ); | < < | 108027 108028 108029 108030 108031 108032 108033 108034 108035 108036 108037 108038 108039 108040 | p->zDb, p->zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_docsize'(docid INTEGER PRIMARY KEY, size BLOB);", p->zDb, p->zName ); fts3DbExec(&rc, db, "CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);", p->zDb, p->zName ); } return rc; } |
︙ | ︙ | |||
108718 108719 108720 108721 108722 108723 108724 | ); rc = sqlite3_exec(db, zSql, fts3TableExistsCallback, &res, 0); sqlite3_free(zSql); *pResult = (u8)(res & 0xff); if( rc!=SQLITE_ABORT ) *pRc = rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 108071 108072 108073 108074 108075 108076 108077 108078 108079 108080 108081 108082 108083 108084 | ); rc = sqlite3_exec(db, zSql, fts3TableExistsCallback, &res, 0); sqlite3_free(zSql); *pResult = (u8)(res & 0xff); if( rc!=SQLITE_ABORT ) *pRc = rc; } /* ** This function is the implementation of both the xConnect and xCreate ** methods of the FTS3 virtual table. ** ** The argv[] array contains the following: ** ** argv[0] -> module name ("fts3" or "fts4") |
︙ | ︙ | |||
108799 108800 108801 108802 108803 108804 108805 | void *pAux, /* Hash table containing tokenizers */ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ Fts3Hash *pHash = (Fts3Hash *)pAux; | | | | | < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < | < < < < < | < < < < < < < | | < < < < < < | < < < < < < < < < | < < | | | > | | | > > > > > | | < | > > | | | | | | | | | > > > > > > > > > > > > < < < < | > < | | < | < < | 108092 108093 108094 108095 108096 108097 108098 108099 108100 108101 108102 108103 108104 108105 108106 108107 108108 108109 108110 108111 108112 108113 108114 108115 108116 108117 108118 108119 108120 108121 108122 108123 108124 108125 108126 108127 108128 108129 108130 108131 108132 108133 108134 108135 108136 108137 108138 108139 108140 108141 108142 108143 108144 108145 108146 108147 108148 108149 108150 108151 108152 108153 108154 108155 108156 108157 108158 108159 108160 108161 108162 108163 108164 108165 108166 108167 108168 108169 108170 108171 108172 108173 108174 108175 108176 108177 108178 108179 108180 108181 108182 108183 108184 108185 108186 108187 108188 108189 108190 108191 108192 108193 108194 108195 108196 108197 108198 108199 108200 108201 108202 108203 108204 108205 108206 108207 108208 108209 108210 108211 108212 108213 108214 108215 108216 108217 108218 108219 108220 108221 108222 108223 | void *pAux, /* Hash table containing tokenizers */ int argc, /* Number of elements in argv array */ const char * const *argv, /* xCreate/xConnect argument array */ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ char **pzErr /* Write any error message here */ ){ Fts3Hash *pHash = (Fts3Hash *)pAux; Fts3Table *p; /* Pointer to allocated vtab */ int rc; /* Return code */ int i; /* Iterator variable */ int nByte; /* Size of allocation used for *p */ int iCol; /* Column index */ int nString = 0; /* Bytes required to hold all column names */ int nCol = 0; /* Number of columns in the FTS table */ char *zCsr; /* Space for holding column names */ int nDb; /* Bytes required to hold database name */ int nName; /* Bytes required to hold table name */ const char *zTokenizer = 0; /* Name of tokenizer to use */ sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ nDb = (int)strlen(argv[1]) + 1; nName = (int)strlen(argv[2]) + 1; for(i=3; i<argc; i++){ char const *z = argv[i]; rc = sqlite3Fts3InitTokenizer(pHash, z, &pTokenizer, &zTokenizer, pzErr); if( rc!=SQLITE_OK ){ return rc; } if( z!=zTokenizer ){ nString += (int)(strlen(z) + 1); } } nCol = argc - 3 - (zTokenizer!=0); if( zTokenizer==0 ){ rc = sqlite3Fts3InitTokenizer(pHash, 0, &pTokenizer, 0, pzErr); if( rc!=SQLITE_OK ){ return rc; } assert( pTokenizer ); } if( nCol==0 ){ nCol = 1; } /* Allocate and populate the Fts3Table structure. */ nByte = sizeof(Fts3Table) + /* Fts3Table */ nCol * sizeof(char *) + /* azColumn */ nName + /* zName */ nDb + /* zDb */ nString; /* Space for azColumn strings */ p = (Fts3Table*)sqlite3_malloc(nByte); if( p==0 ){ rc = SQLITE_NOMEM; goto fts3_init_out; } memset(p, 0, nByte); p->db = db; p->nColumn = nCol; p->nPendingData = 0; p->azColumn = (char **)&p[1]; p->pTokenizer = pTokenizer; p->nNodeSize = 1000; p->nMaxPendingData = FTS3_MAX_PENDING_DATA; zCsr = (char *)&p->azColumn[nCol]; fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1); /* Fill in the zName and zDb fields of the vtab structure. */ p->zName = zCsr; memcpy(zCsr, argv[2], nName); zCsr += nName; p->zDb = zCsr; memcpy(zCsr, argv[1], nDb); zCsr += nDb; /* Fill in the azColumn array */ iCol = 0; for(i=3; i<argc; i++){ if( argv[i]!=zTokenizer ){ char *z; int n; z = (char *)sqlite3Fts3NextToken(argv[i], &n); memcpy(zCsr, z, n); zCsr[n] = '\0'; sqlite3Fts3Dequote(zCsr); p->azColumn[iCol++] = zCsr; zCsr += n+1; assert( zCsr <= &((char *)p)[nByte] ); } } if( iCol==0 ){ assert( nCol==1 ); p->azColumn[0] = "content"; } /* 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 ){ p->bHasContent = 1; p->bHasDocsize = argv[0][3]=='4'; rc = fts3CreateTables(p); }else{ rc = SQLITE_OK; fts3TableExists(&rc, db, argv[1], argv[2], "_content", &p->bHasContent); fts3TableExists(&rc, db, argv[1], argv[2], "_docsize", &p->bHasDocsize); } if( rc!=SQLITE_OK ) goto fts3_init_out; rc = fts3DeclareVtab(p); if( rc!=SQLITE_OK ) goto fts3_init_out; *ppVTab = &p->base; fts3_init_out: assert( p || (pTokenizer && rc!=SQLITE_OK) ); if( rc!=SQLITE_OK ){ if( p ){ fts3DisconnectMethod((sqlite3_vtab *)p); }else{ pTokenizer->pModule->xDestroy(pTokenizer); } } return rc; } /* ** The xConnect() and xCreate() methods for the virtual table. All the ** work is done in function fts3InitVtab(). |
︙ | ︙ | |||
109076 109077 109078 109079 109080 109081 109082 | return SQLITE_OK; } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ | | < < | 108321 108322 108323 108324 108325 108326 108327 108328 108329 108330 108331 108332 108333 108334 108335 108336 108337 108338 | return SQLITE_OK; } /* ** Close the cursor. For additional information see the documentation ** on the xClose method of the virtual table interface. */ static int fulltextClose(sqlite3_vtab_cursor *pCursor){ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; sqlite3_finalize(pCsr->pStmt); sqlite3Fts3ExprFree(pCsr->pExpr); sqlite3_free(pCsr->aDoclist); sqlite3_free(pCsr->aMatchinfo); sqlite3_free(pCsr); return SQLITE_OK; } /* |
︙ | ︙ | |||
109120 109121 109122 109123 109124 109125 109126 | } }else{ return SQLITE_OK; } } /* | | | | | < < < < < < | | > > | < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < | | < < < < < | < < < < < < < | < | < < < < < < < < < < < < < < | < < < < < < < < < < < < | | | < < < < | < < | | < | < < | | | | | | < < < < < | < | | | > | < | > > > | | > > > > | < < > | < > > > | | | > | | > > > > > > > > > | < | < < | | > > > | > > > > > > > > > > | < > | | > > > > | > < | > > > > | > | 108363 108364 108365 108366 108367 108368 108369 108370 108371 108372 108373 108374 108375 108376 108377 108378 108379 108380 108381 108382 108383 108384 108385 108386 108387 108388 108389 108390 108391 108392 108393 108394 108395 108396 108397 108398 108399 108400 108401 108402 108403 108404 108405 108406 108407 108408 108409 108410 108411 108412 108413 108414 108415 108416 108417 108418 108419 108420 108421 108422 108423 108424 108425 108426 108427 108428 108429 108430 108431 108432 108433 108434 108435 108436 108437 108438 108439 108440 108441 108442 108443 108444 108445 108446 108447 108448 108449 108450 108451 108452 108453 108454 108455 108456 108457 108458 108459 108460 108461 108462 108463 108464 108465 108466 108467 108468 108469 108470 108471 108472 108473 108474 108475 108476 108477 108478 108479 108480 108481 108482 108483 108484 108485 108486 108487 108488 108489 108490 108491 108492 108493 108494 108495 108496 108497 108498 108499 108500 | } }else{ return SQLITE_OK; } } /* ** Advance the cursor to the next row in the %_content table that ** matches the search criteria. For a MATCH search, this will be ** the next row that matches. For a full-table scan, this will be ** simply the next row in the %_content table. For a docid lookup, ** this routine simply sets the EOF flag. ** ** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned ** even if we reach end-of-file. The fts3EofMethod() will be called ** subsequently to determine whether or not an EOF was hit. */ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ int rc = SQLITE_OK; /* Return code */ Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; if( pCsr->aDoclist==0 ){ if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ pCsr->isEof = 1; rc = sqlite3_reset(pCsr->pStmt); } }else if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){ pCsr->isEof = 1; }else{ sqlite3_reset(pCsr->pStmt); fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId); pCsr->isRequireSeek = 1; pCsr->isMatchinfoNeeded = 1; } return rc; } /* ** The buffer pointed to by argument zNode (size nNode bytes) contains the ** root node of a b-tree segment. The segment is guaranteed to be at least ** one level high (i.e. the root node is not also a leaf). If successful, ** this function locates the leaf node of the segment that may contain the ** term specified by arguments zTerm and nTerm and writes its block number ** to *piLeaf. ** ** It is possible that the returned leaf node does not contain the specified ** term. However, if the segment does contain said term, it is stored on ** the identified leaf node. Because this function only inspects interior ** segment nodes (and never loads leaf nodes into memory), it is not possible ** to be sure. ** ** If an error occurs, an error code other than SQLITE_OK is returned. */ static int fts3SelectLeaf( Fts3Table *p, /* Virtual table handle */ const char *zTerm, /* Term to select leaves for */ int nTerm, /* Size of term zTerm in bytes */ const char *zNode, /* Buffer containing segment interior node */ int nNode, /* Size of buffer at zNode */ sqlite3_int64 *piLeaf /* Selected leaf node */ ){ int rc = SQLITE_OK; /* Return code */ const char *zCsr = zNode; /* Cursor to iterate through node */ const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ char *zBuffer = 0; /* Buffer to load terms into */ int nAlloc = 0; /* Size of allocated buffer */ while( 1 ){ int isFirstTerm = 1; /* True when processing first term on page */ int iHeight; /* Height of this node in tree */ sqlite3_int64 iChild; /* Block id of child node to descend to */ int nBlock; /* Size of child node in bytes */ zCsr += sqlite3Fts3GetVarint32(zCsr, &iHeight); zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); while( zCsr<zEnd ){ int cmp; /* memcmp() result */ int nSuffix; /* Size of term suffix */ int nPrefix = 0; /* Size of term prefix */ int nBuffer; /* Total term size */ /* Load the next term on the node into zBuffer */ if( !isFirstTerm ){ zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix); } isFirstTerm = 0; zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix); if( nPrefix+nSuffix>nAlloc ){ char *zNew; nAlloc = (nPrefix+nSuffix) * 2; zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); if( !zNew ){ sqlite3_free(zBuffer); return SQLITE_NOMEM; } zBuffer = zNew; } memcpy(&zBuffer[nPrefix], zCsr, nSuffix); nBuffer = nPrefix + nSuffix; zCsr += nSuffix; /* Compare the term we are searching for with the term just loaded from ** the interior node. If the specified term is greater than or equal ** to the term from the interior node, then all terms on the sub-tree ** headed by node iChild are smaller than zTerm. No need to search ** iChild. ** ** If the interior node term is larger than the specified term, then ** the tree headed by iChild may contain the specified term. */ cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); if( cmp<0 || (cmp==0 && nBuffer>nTerm) ) break; iChild++; }; /* If (iHeight==1), the children of this interior node are leaves. The ** specified term may be present on leaf node iChild. */ if( iHeight==1 ){ *piLeaf = iChild; break; } /* Descend to interior node iChild. */ rc = sqlite3Fts3ReadBlock(p, iChild, &zCsr, &nBlock); if( rc!=SQLITE_OK ) break; zEnd = &zCsr[nBlock]; } sqlite3_free(zBuffer); return rc; } /* ** This function is used to create delta-encoded serialized lists of FTS3 ** varints. Each call to this function appends a single varint to a list. */ |
︙ | ︙ | |||
109527 109528 109529 109530 109531 109532 109533 | *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; } /* ** nToken==1 searches for adjacent positions. | < < < < < < < < < < < < < < < < < < < < | < | | > < < < < | 108718 108719 108720 108721 108722 108723 108724 108725 108726 108727 108728 108729 108730 108731 108732 108733 108734 108735 108736 108737 108738 108739 108740 108741 108742 108743 108744 108745 | *pp = p; *pp1 = p1 + 1; *pp2 = p2 + 1; } /* ** nToken==1 searches for adjacent positions. */ static int fts3PoslistPhraseMerge( char **pp, /* Output buffer */ int nToken, /* Maximum difference in token positions */ int isSaveLeft, /* Save the left position */ char **pp1, /* Left input list */ char **pp2 /* Right input list */ ){ char *p = (pp ? *pp : 0); char *p1 = *pp1; char *p2 = *pp2; int iCol1 = 0; int iCol2 = 0; assert( *p1!=0 && *p2!=0 ); if( *p1==POS_COLUMN ){ p1++; p1 += sqlite3Fts3GetVarint32(p1, &iCol1); } if( *p2==POS_COLUMN ){ p2++; |
︙ | ︙ | |||
109593 109594 109595 109596 109597 109598 109599 | assert( *p1!=POS_END && *p1!=POS_COLUMN ); assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; while( 1 ){ | < | < | 108760 108761 108762 108763 108764 108765 108766 108767 108768 108769 108770 108771 108772 108773 108774 | assert( *p1!=POS_END && *p1!=POS_COLUMN ); assert( *p2!=POS_END && *p2!=POS_COLUMN ); fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; while( 1 ){ if( iPos2>iPos1 && iPos2<=iPos1+nToken ){ sqlite3_int64 iSave; if( !pp ){ fts3PoslistCopy(0, &p2); fts3PoslistCopy(0, &p1); *pp1 = p1; *pp2 = p2; return 1; |
︙ | ︙ | |||
109678 109679 109680 109681 109682 109683 109684 | char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ char *p1 = *pp1; char *p2 = *pp2; if( !pp ){ | | | | | | 108843 108844 108845 108846 108847 108848 108849 108850 108851 108852 108853 108854 108855 108856 108857 108858 108859 108860 108861 108862 108863 108864 108865 108866 108867 108868 108869 108870 108871 | char **pp1, /* IN/OUT: Left input list */ char **pp2 /* IN/OUT: Right input list */ ){ char *p1 = *pp1; char *p2 = *pp2; if( !pp ){ if( fts3PoslistPhraseMerge(0, nRight, 0, pp1, pp2) ) return 1; *pp1 = p1; *pp2 = p2; return fts3PoslistPhraseMerge(0, nLeft, 0, pp2, pp1); }else{ char *pTmp1 = aTmp; char *pTmp2; char *aTmp2; int res = 1; fts3PoslistPhraseMerge(&pTmp1, nRight, 0, pp1, pp2); aTmp2 = pTmp2 = pTmp1; *pp1 = p1; *pp2 = p2; fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, pp2, pp1); if( pTmp1!=aTmp && pTmp2!=aTmp2 ){ fts3PoslistMerge(pp, &aTmp, &aTmp2); }else if( pTmp1!=aTmp ){ fts3PoslistCopy(pp, &aTmp); }else if( pTmp2!=aTmp2 ){ fts3PoslistCopy(pp, &aTmp2); }else{ |
︙ | ︙ | |||
109738 109739 109740 109741 109742 109743 109744 | int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ char *aBuffer, /* Pre-allocated output buffer */ int *pnBuffer, /* OUT: Bytes written to aBuffer */ char *a1, /* Buffer containing first doclist */ int n1, /* Size of buffer a1 */ char *a2, /* Buffer containing second doclist */ | | < < | 108903 108904 108905 108906 108907 108908 108909 108910 108911 108912 108913 108914 108915 108916 108917 108918 108919 108920 108921 108922 108923 108924 108925 108926 108927 | int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ char *aBuffer, /* Pre-allocated output buffer */ int *pnBuffer, /* OUT: Bytes written to aBuffer */ char *a1, /* Buffer containing first doclist */ int n1, /* Size of buffer a1 */ char *a2, /* Buffer containing second doclist */ int n2 /* Size of buffer a2 */ ){ sqlite3_int64 i1 = 0; sqlite3_int64 i2 = 0; sqlite3_int64 iPrev = 0; char *p = aBuffer; char *p1 = a1; char *p2 = a2; char *pEnd1 = &a1[n1]; char *pEnd2 = &a2[n2]; assert( mergetype==MERGE_OR || mergetype==MERGE_POS_OR || mergetype==MERGE_AND || mergetype==MERGE_NOT || mergetype==MERGE_PHRASE || mergetype==MERGE_POS_PHRASE || mergetype==MERGE_NEAR || mergetype==MERGE_POS_NEAR ); |
︙ | ︙ | |||
109794 109795 109796 109797 109798 109799 109800 | case MERGE_AND: while( p1 && p2 ){ if( i1==i2 ){ fts3PutDeltaVarint(&p, &iPrev, i1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); | < | 108957 108958 108959 108960 108961 108962 108963 108964 108965 108966 108967 108968 108969 108970 | case MERGE_AND: while( p1 && p2 ){ if( i1==i2 ){ fts3PutDeltaVarint(&p, &iPrev, i1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); }else if( i1<i2 ){ fts3GetDeltaVarint2(&p1, pEnd1, &i1); }else{ fts3GetDeltaVarint2(&p2, pEnd2, &i2); } } break; |
︙ | ︙ | |||
109825 109826 109827 109828 109829 109830 109831 | case MERGE_PHRASE: { char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); while( p1 && p2 ){ if( i1==i2 ){ char *pSave = p; sqlite3_int64 iPrevSave = iPrev; fts3PutDeltaVarint(&p, &iPrev, i1); | | < < | 108987 108988 108989 108990 108991 108992 108993 108994 108995 108996 108997 108998 108999 109000 109001 109002 109003 | case MERGE_PHRASE: { char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); while( p1 && p2 ){ if( i1==i2 ){ char *pSave = p; sqlite3_int64 iPrevSave = iPrev; fts3PutDeltaVarint(&p, &iPrev, i1); if( 0==fts3PoslistPhraseMerge(ppPos, 1, 0, &p1, &p2) ){ p = pSave; iPrev = iPrevSave; } fts3GetDeltaVarint2(&p1, pEnd1, &i1); fts3GetDeltaVarint2(&p2, pEnd2, &i2); }else if( i1<i2 ){ fts3PoslistCopy(0, &p1); fts3GetDeltaVarint2(&p1, pEnd1, &i1); }else{ |
︙ | ︙ | |||
109882 109883 109884 109885 109886 109887 109888 | } } sqlite3_free(aTmp); break; } } | < | 109042 109043 109044 109045 109046 109047 109048 109049 109050 109051 109052 109053 109054 109055 | } } sqlite3_free(aTmp); break; } } *pnBuffer = (int)(p-aBuffer); return SQLITE_OK; } /* ** A pointer to an instance of this structure is used as the context ** argument to sqlite3Fts3SegReaderIterate() |
︙ | ︙ | |||
109921 109922 109923 109924 109925 109926 109927 | ** into a single doclist. */ for(i=0; i<SizeofArray(pTS->aaOutput); i++){ if( pTS->aaOutput[i] ){ if( !aOut ){ aOut = pTS->aaOutput[i]; nOut = pTS->anOutput[i]; | | | | 109080 109081 109082 109083 109084 109085 109086 109087 109088 109089 109090 109091 109092 109093 109094 109095 109096 109097 109098 109099 109100 109101 109102 109103 | ** into a single doclist. */ for(i=0; i<SizeofArray(pTS->aaOutput); i++){ if( pTS->aaOutput[i] ){ if( !aOut ){ aOut = pTS->aaOutput[i]; nOut = pTS->anOutput[i]; pTS->aaOutput[0] = 0; }else{ int nNew = nOut + pTS->anOutput[i]; char *aNew = sqlite3_malloc(nNew); if( !aNew ){ sqlite3_free(aOut); return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[i], pTS->anOutput[i], aOut, nOut ); sqlite3_free(pTS->aaOutput[i]); sqlite3_free(aOut); pTS->aaOutput[i] = 0; aOut = aNew; nOut = nNew; } |
︙ | ︙ | |||
110001 110002 110003 110004 110005 110006 110007 | aNew = sqlite3_malloc(nNew); if( !aNew ){ if( aMerge!=aDoclist ){ sqlite3_free(aMerge); } return SQLITE_NOMEM; } | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > > > | | | | | | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < | < < < < < < < | < < < < < < | < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < | < < < < < < < < < < | | < < < < < | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < < < < | < < < < | | < > > > | | | | < < < < < < < < < < < < < < < | < < | > < < < < < < < < < < < < | | | < < < < < < < < < < | < < < < < | | | 109160 109161 109162 109163 109164 109165 109166 109167 109168 109169 109170 109171 109172 109173 109174 109175 109176 109177 109178 109179 109180 109181 109182 109183 109184 109185 109186 109187 109188 109189 109190 109191 109192 109193 109194 109195 109196 109197 109198 109199 109200 109201 109202 109203 109204 109205 109206 109207 109208 109209 109210 109211 109212 109213 109214 109215 109216 109217 109218 109219 109220 109221 109222 109223 109224 109225 109226 109227 109228 109229 109230 109231 109232 109233 109234 109235 109236 109237 109238 109239 109240 109241 109242 109243 109244 109245 109246 109247 109248 109249 109250 109251 109252 109253 109254 109255 109256 109257 109258 109259 109260 109261 109262 109263 109264 109265 109266 109267 109268 109269 109270 109271 109272 109273 109274 109275 109276 109277 109278 109279 109280 109281 109282 109283 109284 109285 109286 109287 109288 109289 109290 109291 109292 109293 109294 109295 109296 109297 109298 109299 109300 109301 109302 109303 109304 109305 109306 109307 109308 109309 109310 109311 109312 109313 109314 109315 109316 109317 109318 109319 109320 109321 109322 109323 109324 109325 109326 109327 109328 109329 109330 109331 109332 109333 109334 109335 109336 109337 109338 109339 109340 109341 109342 109343 109344 109345 109346 109347 109348 109349 109350 109351 109352 109353 109354 109355 109356 109357 109358 109359 109360 109361 109362 109363 109364 109365 109366 109367 109368 109369 109370 109371 109372 109373 109374 109375 109376 109377 109378 109379 109380 109381 109382 109383 109384 109385 109386 109387 109388 109389 109390 109391 109392 109393 109394 109395 109396 109397 109398 109399 109400 109401 109402 109403 109404 109405 109406 109407 109408 109409 109410 109411 109412 109413 109414 109415 109416 109417 109418 109419 109420 109421 109422 109423 109424 109425 109426 109427 109428 109429 109430 109431 109432 109433 109434 109435 109436 109437 109438 109439 109440 | aNew = sqlite3_malloc(nNew); if( !aNew ){ if( aMerge!=aDoclist ){ sqlite3_free(aMerge); } return SQLITE_NOMEM; } fts3DoclistMerge(mergetype, 0, 0, aNew, &nNew, pTS->aaOutput[iOut], pTS->anOutput[iOut], aMerge, nMerge ); if( iOut>0 ) sqlite3_free(aMerge); sqlite3_free(pTS->aaOutput[iOut]); pTS->aaOutput[iOut] = 0; aMerge = aNew; nMerge = nNew; if( (iOut+1)==SizeofArray(pTS->aaOutput) ){ pTS->aaOutput[iOut] = aMerge; pTS->anOutput[iOut] = nMerge; } } } return SQLITE_OK; } /* ** This function retreives the doclist for the specified term (or term ** prefix) from the database. ** ** The returned doclist may be in one of two formats, depending on the ** value of parameter isReqPos. If isReqPos is zero, then the doclist is ** a sorted list of delta-compressed docids (a bare doclist). If isReqPos ** is non-zero, then the returned list is in the same format as is stored ** in the database without the found length specifier at the start of on-disk ** doclists. */ static int fts3TermSelect( Fts3Table *p, /* Virtual table handle */ int iColumn, /* Column to query (or -ve for all columns) */ const char *zTerm, /* Term to query for */ int nTerm, /* Size of zTerm in bytes */ int isPrefix, /* True for a prefix search */ int isReqPos, /* True to include position lists in output */ int *pnOut, /* OUT: Size of buffer at *ppOut */ char **ppOut /* OUT: Malloced result buffer */ ){ int i; TermSelect tsc; Fts3SegFilter filter; /* Segment term filter configuration */ Fts3SegReader **apSegment; /* Array of segments to read data from */ int nSegment = 0; /* Size of apSegment array */ int nAlloc = 16; /* Allocated size of segment array */ int rc; /* Return code */ sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */ int iAge = 0; /* Used to assign ages to segments */ apSegment = (Fts3SegReader **)sqlite3_malloc(sizeof(Fts3SegReader*)*nAlloc); if( !apSegment ) return SQLITE_NOMEM; rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &apSegment[0]); if( rc!=SQLITE_OK ) goto finished; if( apSegment[0] ){ nSegment = 1; } /* Loop through the entire %_segdir table. For each segment, create a ** Fts3SegReader to iterate through the subset of the segment leaves ** that may contain a term that matches zTerm/nTerm. For non-prefix ** searches, this is always a single leaf. For prefix searches, this ** may be a contiguous block of leaves. ** ** The code in this loop does not actually load any leaves into memory ** (unless the root node happens to be a leaf). It simply examines the ** b-tree structure to determine which leaves need to be inspected. */ rc = sqlite3Fts3AllSegdirs(p, &pStmt); while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ Fts3SegReader *pNew = 0; int nRoot = sqlite3_column_bytes(pStmt, 4); char const *zRoot = sqlite3_column_blob(pStmt, 4); if( sqlite3_column_int64(pStmt, 1)==0 ){ /* The entire segment is stored on the root node (which must be a ** leaf). Do not bother inspecting any data in this case, just ** create a Fts3SegReader to scan the single leaf. */ rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew); }else{ int rc2; /* Return value of sqlite3Fts3ReadBlock() */ sqlite3_int64 i1; /* Blockid of leaf that may contain zTerm */ rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1); if( rc==SQLITE_OK ){ sqlite3_int64 i2 = sqlite3_column_int64(pStmt, 2); rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew); } /* The following call to ReadBlock() serves to reset the SQL statement ** used to retrieve blocks of data from the %_segments table. If it is ** not reset here, then it may remain classified as an active statement ** by SQLite, which may lead to "DROP TABLE" or "DETACH" commands ** failing. */ rc2 = sqlite3Fts3ReadBlock(p, 0, 0, 0); if( rc==SQLITE_OK ){ rc = rc2; } } iAge++; /* If a new Fts3SegReader was allocated, add it to the apSegment array. */ assert( pNew!=0 || rc!=SQLITE_OK ); if( pNew ){ if( nSegment==nAlloc ){ Fts3SegReader **pArray; nAlloc += 16; pArray = (Fts3SegReader **)sqlite3_realloc( apSegment, nAlloc*sizeof(Fts3SegReader *) ); if( !pArray ){ sqlite3Fts3SegReaderFree(p, pNew); rc = SQLITE_NOMEM; goto finished; } apSegment = pArray; } apSegment[nSegment++] = pNew; } } if( rc!=SQLITE_DONE ){ assert( rc!=SQLITE_OK ); goto finished; } memset(&tsc, 0, sizeof(TermSelect)); tsc.isReqPos = isReqPos; filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | (isPrefix ? FTS3_SEGMENT_PREFIX : 0) | (isReqPos ? FTS3_SEGMENT_REQUIRE_POS : 0) | (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); filter.iCol = iColumn; filter.zTerm = zTerm; filter.nTerm = nTerm; rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, &filter, fts3TermSelectCb, (void *)&tsc ); if( rc==SQLITE_OK ){ rc = fts3TermSelectMerge(&tsc); } if( rc==SQLITE_OK ){ *ppOut = tsc.aaOutput[0]; *pnOut = tsc.anOutput[0]; }else{ for(i=0; i<SizeofArray(tsc.aaOutput); i++){ sqlite3_free(tsc.aaOutput[i]); } } finished: sqlite3_reset(pStmt); for(i=0; i<nSegment; i++){ sqlite3Fts3SegReaderFree(p, apSegment[i]); } sqlite3_free(apSegment); return rc; } /* ** Return a DocList corresponding to the phrase *pPhrase. */ static int fts3PhraseSelect( Fts3Table *p, /* Virtual table handle */ Fts3Phrase *pPhrase, /* Phrase to return a doclist for */ int isReqPos, /* True if output should contain positions */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut /* OUT: Size of buffer at *paOut */ ){ char *pOut = 0; int nOut = 0; int rc = SQLITE_OK; int ii; int iCol = pPhrase->iColumn; int isTermPos = (pPhrase->nToken>1 || isReqPos); for(ii=0; ii<pPhrase->nToken; ii++){ struct PhraseToken *pTok = &pPhrase->aToken[ii]; char *z = pTok->z; /* Next token of the phrase */ int n = pTok->n; /* Size of z in bytes */ int isPrefix = pTok->isPrefix;/* True if token is a prefix */ char *pList; /* Pointer to token doclist */ int nList; /* Size of buffer at pList */ rc = fts3TermSelect(p, iCol, z, n, isPrefix, isTermPos, &nList, &pList); if( rc!=SQLITE_OK ) break; if( ii==0 ){ pOut = pList; nOut = nList; }else{ /* Merge the new term list and the current output. If this is the ** last term in the phrase, and positions are not required in the ** output of this function, the positions can be dropped as part ** of this merge. Either way, the result of this merge will be ** smaller than nList bytes. The code in fts3DoclistMerge() is written ** so that it is safe to use pList as the output as well as an input ** in this case. */ int mergetype = MERGE_POS_PHRASE; if( ii==pPhrase->nToken-1 && !isReqPos ){ mergetype = MERGE_PHRASE; } fts3DoclistMerge(mergetype, 0, 0, pList, &nOut, pOut, nOut, pList, nList); sqlite3_free(pOut); pOut = pList; } assert( nOut==0 || pOut!=0 ); } if( rc==SQLITE_OK ){ *paOut = pOut; *pnOut = nOut; }else{ sqlite3_free(pOut); } return rc; } static int fts3NearMerge( int mergetype, /* MERGE_POS_NEAR or MERGE_NEAR */ int nNear, /* Parameter to NEAR operator */ int nTokenLeft, /* Number of tokens in LHS phrase arg */ char *aLeft, /* Doclist for LHS (incl. positions) */ int nLeft, /* Size of LHS doclist in bytes */ int nTokenRight, /* As nTokenLeft */ char *aRight, /* As aLeft */ int nRight, /* As nRight */ char **paOut, /* OUT: Results of merge (malloced) */ int *pnOut /* OUT: Sized of output buffer */ ){ char *aOut; int rc; assert( mergetype==MERGE_POS_NEAR || MERGE_NEAR ); aOut = sqlite3_malloc(nLeft+nRight+1); if( aOut==0 ){ rc = SQLITE_NOMEM; }else{ rc = fts3DoclistMerge(mergetype, nNear+nTokenRight, nNear+nTokenLeft, aOut, pnOut, aLeft, nLeft, aRight, nRight ); if( rc!=SQLITE_OK ){ sqlite3_free(aOut); aOut = 0; } } *paOut = aOut; return rc; } SQLITE_PRIVATE int sqlite3Fts3ExprNearTrim(Fts3Expr *pLeft, Fts3Expr *pRight, int nNear){ int rc; if( pLeft->aDoclist==0 || pRight->aDoclist==0 ){ sqlite3_free(pLeft->aDoclist); sqlite3_free(pRight->aDoclist); pRight->aDoclist = 0; pLeft->aDoclist = 0; rc = SQLITE_OK; }else{ char *aOut; int nOut; rc = fts3NearMerge(MERGE_POS_NEAR, nNear, pLeft->pPhrase->nToken, pLeft->aDoclist, pLeft->nDoclist, pRight->pPhrase->nToken, pRight->aDoclist, pRight->nDoclist, &aOut, &nOut ); if( rc!=SQLITE_OK ) return rc; |
︙ | ︙ | |||
110568 110569 110570 110571 110572 110573 110574 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > | | < < | < < < < | > | 109450 109451 109452 109453 109454 109455 109456 109457 109458 109459 109460 109461 109462 109463 109464 109465 109466 109467 109468 109469 109470 109471 109472 109473 109474 109475 109476 109477 109478 109479 109480 109481 109482 109483 109484 109485 109486 109487 109488 109489 109490 109491 109492 109493 109494 109495 109496 109497 109498 109499 109500 109501 109502 109503 109504 109505 109506 109507 109508 109509 109510 | sqlite3_free(pLeft->aDoclist); pLeft->aDoclist = aOut; pLeft->nDoclist = nOut; } return rc; } /* ** Evaluate the full-text expression pExpr against fts3 table pTab. Store ** the resulting doclist in *paOut and *pnOut. This routine mallocs for ** the space needed to store the output. The caller is responsible for ** freeing the space when it has finished. */ static int evalFts3Expr( Fts3Table *p, /* Virtual table handle */ Fts3Expr *pExpr, /* Parsed fts3 expression */ char **paOut, /* OUT: Pointer to malloc'd result buffer */ int *pnOut, /* OUT: Size of buffer at *paOut */ int isReqPos /* Require positions in output buffer */ ){ int rc = SQLITE_OK; /* Return code */ /* Zero the output parameters. */ *paOut = 0; *pnOut = 0; if( pExpr ){ assert( pExpr->eType==FTSQUERY_PHRASE || pExpr->eType==FTSQUERY_NEAR || isReqPos==0 ); if( pExpr->eType==FTSQUERY_PHRASE ){ rc = fts3PhraseSelect(p, pExpr->pPhrase, isReqPos || (pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR), paOut, pnOut ); }else{ char *aLeft; char *aRight; int nLeft; int nRight; if( 0==(rc = evalFts3Expr(p, pExpr->pRight, &aRight, &nRight, isReqPos)) && 0==(rc = evalFts3Expr(p, pExpr->pLeft, &aLeft, &nLeft, isReqPos)) ){ assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR || pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT ); switch( pExpr->eType ){ case FTSQUERY_NEAR: { Fts3Expr *pLeft; Fts3Expr *pRight; int mergetype = isReqPos ? MERGE_POS_NEAR : MERGE_NEAR; if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ mergetype = MERGE_POS_NEAR; } pLeft = pExpr->pLeft; while( pLeft->eType==FTSQUERY_NEAR ){ pLeft=pLeft->pRight; } |
︙ | ︙ | |||
110846 110847 110848 110849 110850 110851 110852 | /* Allocate a buffer for the output. The maximum size is the ** sum of the sizes of the two input buffers. The +1 term is ** so that a buffer of zero bytes is never allocated - this can ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. */ char *aBuffer = sqlite3_malloc(nRight+nLeft+1); rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > | 109525 109526 109527 109528 109529 109530 109531 109532 109533 109534 109535 109536 109537 109538 109539 109540 109541 109542 109543 109544 109545 109546 109547 109548 109549 109550 109551 109552 109553 109554 109555 109556 109557 109558 109559 109560 109561 109562 109563 109564 109565 109566 109567 109568 109569 109570 109571 109572 109573 109574 109575 109576 109577 109578 109579 109580 109581 109582 | /* Allocate a buffer for the output. The maximum size is the ** sum of the sizes of the two input buffers. The +1 term is ** so that a buffer of zero bytes is never allocated - this can ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. */ char *aBuffer = sqlite3_malloc(nRight+nLeft+1); rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, aLeft, nLeft, aRight, nRight ); *paOut = aBuffer; sqlite3_free(aLeft); break; } default: { assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND ); fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut, aLeft, nLeft, aRight, nRight ); *paOut = aLeft; break; } } } sqlite3_free(aRight); } } return rc; } /* ** This is the xFilter interface for the virtual table. See ** the virtual table xFilter method documentation for additional ** information. ** ** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against ** the %_content table. ** ** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry ** in the %_content table. ** ** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The ** column on the left-hand side of the MATCH operator is column ** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand ** side of the MATCH operator. */ /* TODO(shess) Upgrade the cursor initialization and destruction to ** account for fts3FilterMethod() being called multiple times on the ** same cursor. The current solution is very fragile. Apply fix to ** fts3 as appropriate. */ static int fts3FilterMethod( sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ int idxNum, /* Strategy index */ const char *idxStr, /* Unused */ int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ |
︙ | ︙ | |||
110990 110991 110992 110993 110994 110995 110996 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( nVal==0 || nVal==1 ); assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); | < > > > > > > > > > > > > > > > > > | | 109592 109593 109594 109595 109596 109597 109598 109599 109600 109601 109602 109603 109604 109605 109606 109607 109608 109609 109610 109611 109612 109613 109614 109615 109616 109617 109618 109619 109620 109621 109622 109623 109624 109625 109626 109627 109628 109629 109630 | UNUSED_PARAMETER(idxStr); UNUSED_PARAMETER(nVal); assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); assert( nVal==0 || nVal==1 ); assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); /* In case the cursor has been used before, clear it now. */ sqlite3_finalize(pCsr->pStmt); sqlite3_free(pCsr->aDoclist); sqlite3Fts3ExprFree(pCsr->pExpr); memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); /* Compile a SELECT statement for this cursor. For a full-table-scan, the ** statement loops through all rows of the %_content table. For a ** full-text query or docid lookup, the statement retrieves a single ** row by docid. */ zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName); if( !zSql ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); sqlite3_free(zSql); } if( rc!=SQLITE_OK ) return rc; pCsr->eSearch = (i16)idxNum; if( idxNum==FTS3_DOCID_SEARCH ){ rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); }else if( idxNum!=FTS3_FULLSCAN_SEARCH ){ int iCol = idxNum-FTS3_FULLTEXT_SEARCH; const char *zQuery = (const char *)sqlite3_value_text(apVal[0]); if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ return SQLITE_NOMEM; } |
︙ | ︙ | |||
111020 111021 111022 111023 111024 111025 111026 | } return rc; } rc = sqlite3Fts3ReadLock(p); if( rc!=SQLITE_OK ) return rc; | | < < < < < < < < < < < < < < < < < < < | 109638 109639 109640 109641 109642 109643 109644 109645 109646 109647 109648 109649 109650 109651 109652 109653 109654 109655 109656 | } return rc; } rc = sqlite3Fts3ReadLock(p); if( rc!=SQLITE_OK ) return rc; rc = evalFts3Expr(p, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); pCsr->pNextId = pCsr->aDoclist; pCsr->iPrevId = 0; } if( rc!=SQLITE_OK ) return rc; return fts3NextMethod(pCursor); } /* ** This is the xEof method of the virtual table. SQLite calls this ** routine to find out if it has reached the end of a result set. |
︙ | ︙ | |||
111067 111068 111069 111070 111071 111072 111073 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ | < < < < < | 109666 109667 109668 109669 109670 109671 109672 109673 109674 109675 109676 109677 109678 109679 | ** rowid should be written to *pRowid. */ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; if( pCsr->aDoclist ){ *pRowid = pCsr->iPrevId; }else{ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } return SQLITE_OK; } /* ** This is the xColumn method, called by SQLite to request a value from |
︙ | ︙ | |||
111134 111135 111136 111137 111138 111139 111140 | } /* ** Implementation of xSync() method. Flush the contents of the pending-terms ** hash-table to the database. */ static int fts3SyncMethod(sqlite3_vtab *pVtab){ | | < < | 109728 109729 109730 109731 109732 109733 109734 109735 109736 109737 109738 109739 109740 109741 109742 | } /* ** Implementation of xSync() method. Flush the contents of the pending-terms ** hash-table to the database. */ static int fts3SyncMethod(sqlite3_vtab *pVtab){ return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab); } /* ** Implementation of xBegin() method. This is a no-op. */ static int fts3BeginMethod(sqlite3_vtab *pVtab){ UNUSED_PARAMETER(pVtab); |
︙ | ︙ | |||
111174 111175 111176 111177 111178 111179 111180 | /* ** Load the doclist associated with expression pExpr to pExpr->aDoclist. ** The loaded doclist contains positions as well as the document ids. ** This is used by the matchinfo(), snippet() and offsets() auxillary ** functions. */ | | < < < | < < < < < < < < < < < < < < < < | 109766 109767 109768 109769 109770 109771 109772 109773 109774 109775 109776 109777 109778 109779 109780 109781 | /* ** Load the doclist associated with expression pExpr to pExpr->aDoclist. ** The loaded doclist contains positions as well as the document ids. ** This is used by the matchinfo(), snippet() and offsets() auxillary ** functions. */ SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Table *pTab, Fts3Expr *pExpr){ return evalFts3Expr(pTab, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1); } /* ** After ExprLoadDoclist() (see above) has been called, this function is ** used to iterate/search through the position lists that make up the doclist ** stored in pExpr->aDoclist. */ |
︙ | ︙ | |||
111260 111261 111262 111263 111264 111265 111266 | ** message is written to context pContext and SQLITE_ERROR returned. The ** string passed via zFunc is used as part of the error message. */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ | | | 109833 109834 109835 109836 109837 109838 109839 109840 109841 109842 109843 109844 109845 109846 109847 | ** message is written to context pContext and SQLITE_ERROR returned. The ** string passed via zFunc is used as part of the error message. */ static int fts3FunctionArg( sqlite3_context *pContext, /* SQL function call context */ const char *zFunc, /* Function name */ sqlite3_value *pVal, /* argv[0] passed to function */ Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ ){ Fts3Cursor *pRet; if( sqlite3_value_type(pVal)!=SQLITE_BLOB || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *) ){ char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); sqlite3_result_error(pContext, zErr, -1); |
︙ | ︙ | |||
111386 111387 111388 111389 111390 111391 111392 | */ static void fts3MatchinfoFunc( sqlite3_context *pContext, /* SQLite function call context */ int nVal, /* Size of argument array */ sqlite3_value **apVal /* Array of arguments */ ){ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ | > | > > > > > | 109959 109960 109961 109962 109963 109964 109965 109966 109967 109968 109969 109970 109971 109972 109973 109974 109975 109976 109977 109978 109979 | */ static void fts3MatchinfoFunc( sqlite3_context *pContext, /* SQLite function call context */ int nVal, /* Size of argument array */ sqlite3_value **apVal /* Array of arguments */ ){ Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ if( nVal!=1 ){ sqlite3_result_error(pContext, "wrong number of arguments to function matchinfo()", -1); return; } if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){ sqlite3Fts3Matchinfo(pContext, pCsr); } } /* ** This routine implements the xFindFunction method for the FTS3 |
︙ | ︙ | |||
111449 111450 111451 111452 111453 111454 111455 111456 111457 111458 111459 111460 | return rc; } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", p->zDb, p->zName, zName ); if( p->bHasDocsize ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", p->zDb, p->zName, zName ); | > < < | 110028 110029 110030 110031 110032 110033 110034 110035 110036 110037 110038 110039 110040 110041 110042 110043 110044 110045 110046 110047 | return rc; } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';", p->zDb, p->zName, zName ); if( rc==SQLITE_ERROR ) rc = SQLITE_OK; if( p->bHasDocsize ){ fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';", p->zDb, p->zName, zName ); fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_stat' RENAME TO '%q_stat';", p->zDb, p->zName, zName ); } fts3DbExec(&rc, db, "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';", |
︙ | ︙ | |||
111480 111481 111482 111483 111484 111485 111486 | /* iVersion */ 0, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, /* xDisconnect */ fts3DisconnectMethod, /* xDestroy */ fts3DestroyMethod, /* xOpen */ fts3OpenMethod, | | | 110058 110059 110060 110061 110062 110063 110064 110065 110066 110067 110068 110069 110070 110071 110072 | /* iVersion */ 0, /* xCreate */ fts3CreateMethod, /* xConnect */ fts3ConnectMethod, /* xBestIndex */ fts3BestIndexMethod, /* xDisconnect */ fts3DisconnectMethod, /* xDestroy */ fts3DestroyMethod, /* xOpen */ fts3OpenMethod, /* xClose */ fulltextClose, /* xFilter */ fts3FilterMethod, /* xNext */ fts3NextMethod, /* xEof */ fts3EofMethod, /* xColumn */ fts3ColumnMethod, /* xRowid */ fts3RowidMethod, /* xUpdate */ fts3UpdateMethod, /* xBegin */ fts3BeginMethod, |
︙ | ︙ | |||
111507 111508 111509 111510 111511 111512 111513 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* | | | | | > | < < | 110085 110086 110087 110088 110089 110090 110091 110092 110093 110094 110095 110096 110097 110098 110099 110100 110101 110102 110103 110104 110105 110106 110107 110108 110109 110110 110111 | static void hashDestroy(void *p){ Fts3Hash *pHash = (Fts3Hash *)p; sqlite3Fts3HashClear(pHash); sqlite3_free(pHash); } /* ** The fts3 built-in tokenizers - "simple" and "porter" - are implemented ** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following ** two forward declarations are for functions declared in these files ** used to retrieve the respective implementations. ** ** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed ** to by the argument to point to the "simple" tokenizer implementation. ** Function ...PorterTokenizerModule() sets *pModule to point to the ** porter tokenizer/stemmer implementation. */ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); /* ** Initialise the fts3 extension. If this extension is built as part ** of the sqlite library, then this function is called directly by ** SQLite. If fts3 is built as a dynamically loadable extension, this ** function is called by the sqlite3_extension_init() entry point. */ |
︙ | ︙ | |||
111576 111577 111578 111579 111580 111581 111582 | ** the two scalar functions. If this is successful, register the ** module with sqlite. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) | | | 110153 110154 110155 110156 110157 110158 110159 110160 110161 110162 110163 110164 110165 110166 110167 | ** the two scalar functions. If this is successful, register the ** module with sqlite. */ if( SQLITE_OK==rc && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", -1)) && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) ){ rc = sqlite3_create_module_v2( db, "fts3", &fts3Module, (void *)pHash, hashDestroy ); if( rc==SQLITE_OK ){ rc = sqlite3_create_module_v2( |
︙ | ︙ | |||
111718 111719 111720 111721 111722 111723 111724 | ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } | < < < < < < < < < < < < | 110295 110296 110297 110298 110299 110300 110301 110302 110303 110304 110305 110306 110307 110308 | ** is defined to accept an argument of type char, and always returns 0 for ** any values that fall outside of the range of the unsigned char type (i.e. ** negative values). */ static int fts3isspace(char c){ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; } /* ** Extract the next token from buffer z (length n) using the tokenizer ** and other information (column names etc.) in pParse. Create an Fts3Expr ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this ** single token and set *ppExpr to point to it. If the end of the buffer is ** reached before a token is found, set *ppExpr to zero. It is the |
︙ | ︙ | |||
111768 111769 111770 111771 111772 111773 111774 | int nByte; /* total space to allocate */ pCursor->pTokenizer = pTokenizer; rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; | | > | 110333 110334 110335 110336 110337 110338 110339 110340 110341 110342 110343 110344 110345 110346 110347 110348 110349 110350 110351 | int nByte; /* total space to allocate */ pCursor->pTokenizer = pTokenizer; rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; pRet = (Fts3Expr *)sqlite3_malloc(nByte); if( !pRet ){ rc = SQLITE_NOMEM; }else{ memset(pRet, 0, nByte); pRet->eType = FTSQUERY_PHRASE; pRet->pPhrase = (Fts3Phrase *)&pRet[1]; pRet->pPhrase->nToken = 1; pRet->pPhrase->iColumn = iCol; pRet->pPhrase->aToken[0].n = nToken; pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
︙ | ︙ | |||
111847 111848 111849 111850 111851 111852 111853 | pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ const char *zToken; int nToken, iBegin, iEnd, iPos; rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | | < | 110413 110414 110415 110416 110417 110418 110419 110420 110421 110422 110423 110424 110425 110426 110427 110428 110429 110430 110431 110432 110433 110434 110435 110436 | pCursor->pTokenizer = pTokenizer; for(ii=0; rc==SQLITE_OK; ii++){ const char *zToken; int nToken, iBegin, iEnd, iPos; rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); if( rc==SQLITE_OK ){ int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); p = fts3ReallocOrFree(p, nByte+ii*sizeof(struct PhraseToken)); zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); if( !p || !zTemp ){ goto no_mem; } if( ii==0 ){ memset(p, 0, nByte); p->pPhrase = (Fts3Phrase *)&p[1]; } p->pPhrase = (Fts3Phrase *)&p[1]; p->pPhrase->nToken = ii+1; p->pPhrase->aToken[ii].n = nToken; memcpy(&zTemp[nTemp], zToken, nToken); nTemp += nToken; if( iEnd<nInput && zInput[iEnd]=='*' ){ p->pPhrase->aToken[ii].isPrefix = 1; }else{ |
︙ | ︙ | |||
111879 111880 111881 111882 111883 111884 111885 | } if( rc==SQLITE_DONE ){ int jj; char *zNew = NULL; int nNew = 0; int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | | | 110444 110445 110446 110447 110448 110449 110450 110451 110452 110453 110454 110455 110456 110457 110458 | } if( rc==SQLITE_DONE ){ int jj; char *zNew = NULL; int nNew = 0; int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken); p = fts3ReallocOrFree(p, nByte + nTemp); if( !p ){ goto no_mem; } if( zTemp ){ zNew = &(((char *)p)[nByte]); memcpy(zNew, zTemp, nTemp); |
︙ | ︙ | |||
111997 111998 111999 112000 112001 112002 112003 | ** the next byte must contain either whitespace, an open or close ** parenthesis, a quote character, or EOF. */ cNext = zInput[nKey]; if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ | | > | 110562 110563 110564 110565 110566 110567 110568 110569 110570 110571 110572 110573 110574 110575 110576 110577 110578 110579 110580 | ** the next byte must contain either whitespace, an open or close ** parenthesis, a quote character, or EOF. */ cNext = zInput[nKey]; if( fts3isspace(cNext) || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 ){ pRet = (Fts3Expr *)sqlite3_malloc(sizeof(Fts3Expr)); if( !pRet ){ return SQLITE_NOMEM; } memset(pRet, 0, sizeof(Fts3Expr)); pRet->eType = pKey->eType; pRet->nNear = nNear; *ppExpr = pRet; *pnConsumed = (int)((zInput - z) + nKey); return SQLITE_OK; } |
︙ | ︙ | |||
112176 112177 112178 112179 112180 112181 112182 | if( rc==SQLITE_OK ){ int isPhrase; if( !sqlite3_fts3_enable_parentheses && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot ){ /* Create an implicit NOT operator. */ | | > | 110742 110743 110744 110745 110746 110747 110748 110749 110750 110751 110752 110753 110754 110755 110756 110757 110758 110759 110760 110761 110762 | if( rc==SQLITE_OK ){ int isPhrase; if( !sqlite3_fts3_enable_parentheses && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot ){ /* Create an implicit NOT operator. */ Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr)); if( !pNot ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } memset(pNot, 0, sizeof(Fts3Expr)); pNot->eType = FTSQUERY_NOT; pNot->pRight = p; if( pNotBranch ){ pNot->pLeft = pNotBranch; } pNotBranch = pNot; p = pPrev; |
︙ | ︙ | |||
112209 112210 112211 112212 112213 112214 112215 | goto exprparse_out; } if( isPhrase && !isRequirePhrase ){ /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); | | > | 110776 110777 110778 110779 110780 110781 110782 110783 110784 110785 110786 110787 110788 110789 110790 110791 110792 110793 110794 110795 110796 | goto exprparse_out; } if( isPhrase && !isRequirePhrase ){ /* Insert an implicit AND operator. */ Fts3Expr *pAnd; assert( pRet && pPrev ); pAnd = sqlite3_malloc(sizeof(Fts3Expr)); if( !pAnd ){ sqlite3Fts3ExprFree(p); rc = SQLITE_NOMEM; goto exprparse_out; } memset(pAnd, 0, sizeof(Fts3Expr)); pAnd->eType = FTSQUERY_AND; insertBinaryOperator(&pRet, pPrev, pAnd); pPrev = pAnd; } /* This test catches attempts to make either operand of a NEAR ** operator something other than a phrase. For example, either of |
︙ | ︙ | |||
113664 113665 113666 113667 113668 113669 113670 | return; } } sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } | | | 112232 112233 112234 112235 112236 112237 112238 112239 112240 112241 112242 112243 112244 112245 112246 | return; } } sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } static int fts3IsIdChar(char c){ static const char isFtsIdChar[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ |
︙ | ︙ | |||
113702 113703 113704 113705 113706 113707 113708 | case '[': z2 = &z1[1]; while( *z2 && z2[0]!=']' ) z2++; if( *z2 ) z2++; break; default: | | | | > > | > > > > > > > | > > > | | 112270 112271 112272 112273 112274 112275 112276 112277 112278 112279 112280 112281 112282 112283 112284 112285 112286 112287 112288 112289 112290 112291 112292 112293 112294 112295 112296 112297 112298 112299 112300 112301 112302 112303 112304 112305 112306 112307 112308 112309 112310 112311 112312 112313 112314 112315 112316 112317 112318 112319 112320 112321 112322 112323 112324 112325 112326 112327 112328 112329 112330 | case '[': z2 = &z1[1]; while( *z2 && z2[0]!=']' ) z2++; if( *z2 ) z2++; break; default: if( fts3IsIdChar(*z1) ){ z2 = &z1[1]; while( fts3IsIdChar(*z2) ) z2++; }else{ z1++; } } } *pn = (int)(z2-z1); return z1; } SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( Fts3Hash *pHash, /* Tokenizer hash table */ const char *zArg, /* Possible tokenizer specification */ sqlite3_tokenizer **ppTok, /* OUT: Tokenizer (if applicable) */ const char **pzTokenizer, /* OUT: Set to zArg if is tokenizer */ char **pzErr /* OUT: Set to malloced error message */ ){ int rc; char *z = (char *)zArg; int n; char *zCopy; char *zEnd; /* Pointer to nul-term of zCopy */ sqlite3_tokenizer_module *m; if( !z ){ zCopy = sqlite3_mprintf("simple"); }else{ if( sqlite3_strnicmp(z, "tokenize", 8) || fts3IsIdChar(z[8])){ return SQLITE_OK; } zCopy = sqlite3_mprintf("%s", &z[8]); *pzTokenizer = zArg; } if( !zCopy ){ return SQLITE_NOMEM; } zEnd = &zCopy[strlen(zCopy)]; z = (char *)sqlite3Fts3NextToken(zCopy, &n); z[n] = '\0'; sqlite3Fts3Dequote(z); m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, z, (int)strlen(z)+1); if( !m ){ *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z); rc = SQLITE_ERROR; }else{ char const **aArg = 0; int iArg = 0; z = &z[n+1]; |
︙ | ︙ | |||
114305 114306 114307 114308 114309 114310 114311 | ** of the sub-routines used to merge segments are also used by the query ** code in fts3.c. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) | < < < < < < < < < < < < < < < < < < < < < < < < < | < | < | > < < | 112885 112886 112887 112888 112889 112890 112891 112892 112893 112894 112895 112896 112897 112898 112899 112900 112901 112902 112903 112904 112905 112906 112907 112908 112909 112910 112911 112912 112913 112914 112915 112916 112917 112918 112919 112920 112921 112922 112923 112924 112925 112926 112927 112928 112929 112930 112931 112932 112933 112934 112935 112936 112937 112938 112939 112940 112941 112942 112943 112944 112945 112946 112947 112948 112949 112950 112951 112952 112953 112954 112955 112956 112957 112958 112959 | ** of the sub-routines used to merge segments are also used by the query ** code in fts3.c. */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) typedef struct PendingList PendingList; typedef struct SegmentNode SegmentNode; typedef struct SegmentWriter SegmentWriter; /* ** Data structure used while accumulating terms in the pending-terms hash ** table. The hash table entry maps from term (a string) to a malloc'd ** instance of this structure. */ struct PendingList { int nData; char *aData; int nSpace; sqlite3_int64 iLastDocid; sqlite3_int64 iLastCol; sqlite3_int64 iLastPos; }; /* ** An instance of this structure is used to iterate through the terms on ** a contiguous set of segment b-tree leaf nodes. Although the details of ** this structure are only manipulated by code in this file, opaque handles ** of type Fts3SegReader* are also used by code in fts3.c to iterate through ** terms when querying the full-text index. See functions: ** ** sqlite3Fts3SegReaderNew() ** sqlite3Fts3SegReaderFree() ** sqlite3Fts3SegReaderIterate() ** ** Methods used to manipulate Fts3SegReader structures: ** ** fts3SegReaderNext() ** fts3SegReaderFirstDocid() ** fts3SegReaderNextDocid() */ struct Fts3SegReader { int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ sqlite3_int64 iStartBlock; sqlite3_int64 iEndBlock; sqlite3_stmt *pStmt; /* SQL Statement to access leaf nodes */ char *aNode; /* Pointer to node data (or NULL) */ int nNode; /* Size of buffer at aNode (or 0) */ int nTermAlloc; /* Allocated size of zTerm buffer */ Fts3HashElem **ppNextElem; /* Variables set by fts3SegReaderNext(). These may be read directly ** by the caller. They are valid from the time SegmentReaderNew() returns ** until SegmentReaderNext() returns something other than SQLITE_OK ** (i.e. SQLITE_DONE). */ int nTerm; /* Number of bytes in current term */ char *zTerm; /* Pointer to current term */ char *aDoclist; /* Pointer to doclist of current entry */ int nDoclist; /* Size of doclist in current entry */ /* The following variables are used to iterate through the current doclist */ char *pOffsetList; sqlite3_int64 iDocid; }; #define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0) /* ** An instance of this structure is used to create a segment b-tree in the ** database. The internal details of this type are only accessed by the ** following functions: ** ** fts3SegWriterAdd() |
︙ | ︙ | |||
114462 114463 114464 114465 114466 114467 114468 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 | > | | | | | | 113014 113015 113016 113017 113018 113019 113020 113021 113022 113023 113024 113025 113026 113027 113028 113029 113030 113031 113032 113033 | #define SQL_SELECT_LEVEL 12 #define SQL_SELECT_ALL_LEVEL 13 #define SQL_SELECT_LEVEL_COUNT 14 #define SQL_SELECT_SEGDIR_COUNT_MAX 15 #define SQL_DELETE_SEGDIR_BY_LEVEL 16 #define SQL_DELETE_SEGMENTS_RANGE 17 #define SQL_CONTENT_INSERT 18 #define SQL_GET_BLOCK 19 #define SQL_DELETE_DOCSIZE 20 #define SQL_REPLACE_DOCSIZE 21 #define SQL_SELECT_DOCSIZE 22 #define SQL_SELECT_DOCTOTAL 23 #define SQL_REPLACE_DOCTOTAL 24 /* ** This function is used to obtain an SQLite prepared statement handle ** for the statement identified by the second argument. If successful, ** *pp is set to the requested statement handle and SQLITE_OK returned. ** Otherwise, an SQLite error code is returned and *pp is set to 0. ** |
︙ | ︙ | |||
114511 114512 114513 114514 114515 114516 114517 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", | > | | | | | | 113064 113065 113066 113067 113068 113069 113070 113071 113072 113073 113074 113075 113076 113077 113078 113079 113080 113081 113082 113083 | /* 14 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", /* 15 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", /* 16 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", /* 17 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", /* 18 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", /* 19 */ "SELECT block FROM %Q.'%q_segments' WHERE blockid = ?", /* 20 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?", /* 21 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* 22 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?", /* 23 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0", /* 24 */ "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 ); |
︙ | ︙ | |||
114589 114590 114591 114592 114593 114594 114595 114596 114597 114598 114599 114600 114601 114602 | if( rc==SQLITE_OK ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } *pRC = rc; } /* ** This function ensures that the caller has obtained a shared-cache ** table-lock on the %_content table. This is required before reading ** data from the fts3 table. If this lock is not acquired first, then ** the caller may end up holding read-locks on the %_segments and %_segdir ** tables, but no read-lock on the %_content table. If this happens | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 113143 113144 113145 113146 113147 113148 113149 113150 113151 113152 113153 113154 113155 113156 113157 113158 113159 113160 113161 113162 113163 113164 113165 113166 113167 113168 113169 113170 113171 113172 113173 113174 113175 113176 113177 113178 113179 113180 113181 113182 113183 113184 113185 113186 113187 113188 113189 113190 113191 113192 113193 113194 113195 | if( rc==SQLITE_OK ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); } *pRC = rc; } /* ** Read a single block from the %_segments table. If the specified block ** does not exist, return SQLITE_CORRUPT. If some other error (malloc, IO ** etc.) occurs, return the appropriate SQLite error code. ** ** Otherwise, if successful, set *pzBlock to point to a buffer containing ** the block read from the database, and *pnBlock to the size of the read ** block in bytes. ** ** WARNING: The returned buffer is only valid until the next call to ** sqlite3Fts3ReadBlock(). */ SQLITE_PRIVATE int sqlite3Fts3ReadBlock( Fts3Table *p, sqlite3_int64 iBlock, char const **pzBlock, int *pnBlock ){ sqlite3_stmt *pStmt; int rc = fts3SqlStmt(p, SQL_GET_BLOCK, &pStmt, 0); if( rc!=SQLITE_OK ) return rc; sqlite3_reset(pStmt); if( pzBlock ){ sqlite3_bind_int64(pStmt, 1, iBlock); rc = sqlite3_step(pStmt); if( rc!=SQLITE_ROW ){ return (rc==SQLITE_DONE ? SQLITE_CORRUPT : rc); } *pnBlock = sqlite3_column_bytes(pStmt, 0); *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); if( sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ return SQLITE_CORRUPT; } } return SQLITE_OK; } /* ** This function ensures that the caller has obtained a shared-cache ** table-lock on the %_content table. This is required before reading ** data from the fts3 table. If this lock is not acquired first, then ** the caller may end up holding read-locks on the %_segments and %_segdir ** tables, but no read-lock on the %_content table. If this happens |
︙ | ︙ | |||
114758 114759 114760 114761 114762 114763 114764 | ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in ** p->iPrevDocid, and the column is specified by argument iCol. ** ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. */ static int fts3PendingTermsAdd( | | | | | | 113351 113352 113353 113354 113355 113356 113357 113358 113359 113360 113361 113362 113363 113364 113365 113366 113367 113368 | ** Tokenize the nul-terminated string zText and add all tokens to the ** pending-terms hash-table. The docid used is that currently stored in ** p->iPrevDocid, and the column is specified by argument iCol. ** ** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. */ static int fts3PendingTermsAdd( Fts3Table *p, /* FTS table into which text will be inserted */ const char *zText, /* Text of document to be inseted */ int iCol, /* Column number into which text is inserted */ u32 *pnWord /* OUT: Number of tokens inserted */ ){ int rc; int iStart; int iEnd; int iPos; int nWord = 0; |
︙ | ︙ | |||
114846 114847 114848 114849 114850 114851 114852 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } | < < < | 113439 113440 113441 113442 113443 113444 113445 113446 113447 113448 113449 113450 113451 113452 | int rc = sqlite3Fts3PendingTermsFlush(p); if( rc!=SQLITE_OK ) return rc; } p->iPrevDocid = iDocid; return SQLITE_OK; } SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){ Fts3HashElem *pElem; for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ sqlite3_free(fts3HashData(pElem)); } fts3HashClear(&p->pendingTerms); p->nPendingData = 0; |
︙ | ︙ | |||
114876 114877 114878 114879 114880 114881 114882 | const char *zText = (const char *)sqlite3_value_text(apVal[i]); if( zText ){ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); if( rc!=SQLITE_OK ){ return rc; } } | < | 113466 113467 113468 113469 113470 113471 113472 113473 113474 113475 113476 113477 113478 113479 | const char *zText = (const char *)sqlite3_value_text(apVal[i]); if( zText ){ int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]); if( rc!=SQLITE_OK ){ return rc; } } } return SQLITE_OK; } /* ** This function is called by the xUpdate() method for an INSERT operation. ** The apVal parameter is passed a copy of the apVal argument passed by |
︙ | ︙ | |||
114964 114965 114966 114967 114968 114969 114970 | /* Delete everything from the %_content, %_segments and %_segdir tables. */ fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); | < < | | 113553 113554 113555 113556 113557 113558 113559 113560 113561 113562 113563 113564 113565 113566 113567 113568 113569 113570 113571 113572 113573 113574 113575 113576 113577 | /* Delete everything from the %_content, %_segments and %_segdir tables. */ fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_ALL_DOCSIZE, 0); fts3SqlExec(&rc, p, SQL_DELETE_ALL_STAT, 0); } return rc; } /* ** The first element in the apVal[] array is assumed to contain the docid ** (an integer) of a row about to be deleted. Remove all terms from the ** full-text index. */ static void fts3DeleteTerms( int *pRC, /* Result code */ Fts3Table *p, /* The FTS table to delete from */ sqlite3_value **apVal, /* apVal[] contains the docid to be deleted */ u32 *aSz /* Sizes of deleted document written here */ ){ int rc; sqlite3_stmt *pSelect; |
︙ | ︙ | |||
114998 114999 115000 115001 115002 115003 115004 | const char *zText = (const char *)sqlite3_column_text(pSelect, i); rc = fts3PendingTermsAdd(p, zText, -1, &aSz[i-1]); if( rc!=SQLITE_OK ){ sqlite3_reset(pSelect); *pRC = rc; return; } | < | 113585 113586 113587 113588 113589 113590 113591 113592 113593 113594 113595 113596 113597 113598 | const char *zText = (const char *)sqlite3_column_text(pSelect, i); rc = fts3PendingTermsAdd(p, zText, -1, &aSz[i-1]); if( rc!=SQLITE_OK ){ sqlite3_reset(pSelect); *pRC = rc; return; } } } rc = sqlite3_reset(pSelect); }else{ sqlite3_reset(pSelect); } *pRC = rc; |
︙ | ︙ | |||
115061 115062 115063 115064 115065 115066 115067 115068 | *piIdx = iNext; } } return rc; } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < | < | < < < < < > > > > | | | < < < < < < < < < < > < < < < < < < < < < | 113647 113648 113649 113650 113651 113652 113653 113654 113655 113656 113657 113658 113659 113660 113661 113662 113663 113664 113665 113666 113667 113668 113669 113670 113671 113672 113673 113674 113675 113676 113677 113678 113679 113680 113681 113682 113683 113684 113685 113686 113687 113688 113689 113690 113691 113692 113693 113694 113695 113696 113697 113698 113699 113700 113701 113702 113703 113704 113705 113706 113707 113708 113709 113710 113711 113712 113713 113714 113715 113716 113717 113718 113719 113720 113721 113722 113723 113724 113725 113726 | *piIdx = iNext; } } return rc; } /* ** Move the iterator passed as the first argument to the next term in the ** segment. If successful, SQLITE_OK is returned. If there is no next term, ** SQLITE_DONE. Otherwise, an SQLite error code. */ static int fts3SegReaderNext(Fts3SegReader *pReader){ char *pNext; /* Cursor variable */ int nPrefix; /* Number of bytes in term prefix */ int nSuffix; /* Number of bytes in term suffix */ if( !pReader->aDoclist ){ pNext = pReader->aNode; }else{ pNext = &pReader->aDoclist[pReader->nDoclist]; } if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ int rc; if( fts3SegReaderIsPending(pReader) ){ Fts3HashElem *pElem = *(pReader->ppNextElem); if( pElem==0 ){ pReader->aNode = 0; }else{ PendingList *pList = (PendingList *)fts3HashData(pElem); pReader->zTerm = (char *)fts3HashKey(pElem); pReader->nTerm = fts3HashKeysize(pElem); pReader->nNode = pReader->nDoclist = pList->nData + 1; pReader->aNode = pReader->aDoclist = pList->aData; pReader->ppNextElem++; assert( pReader->aNode ); } return SQLITE_OK; } if( !pReader->pStmt ){ pReader->aNode = 0; return SQLITE_OK; } rc = sqlite3_step(pReader->pStmt); if( rc!=SQLITE_ROW ){ pReader->aNode = 0; return (rc==SQLITE_DONE ? SQLITE_OK : rc); } pReader->nNode = sqlite3_column_bytes(pReader->pStmt, 0); pReader->aNode = (char *)sqlite3_column_blob(pReader->pStmt, 0); pNext = pReader->aNode; } pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); if( nPrefix+nSuffix>pReader->nTermAlloc ){ int nNew = (nPrefix+nSuffix)*2; char *zNew = sqlite3_realloc(pReader->zTerm, nNew); if( !zNew ){ return SQLITE_NOMEM; } pReader->zTerm = zNew; pReader->nTermAlloc = nNew; } memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); pReader->nTerm = nPrefix+nSuffix; pNext += nSuffix; pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist); assert( pNext<&pReader->aNode[pReader->nNode] ); pReader->aDoclist = pNext; pReader->pOffsetList = 0; return SQLITE_OK; } /* ** Set the SegReader to point to the first docid in the doclist associated ** with the current term. */ |
︙ | ︙ | |||
115294 115295 115296 115297 115298 115299 115300 115301 | }else{ sqlite3_int64 iDelta; pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); pReader->iDocid += iDelta; } } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > > > > | | > > | | > < | < | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > | 113775 113776 113777 113778 113779 113780 113781 113782 113783 113784 113785 113786 113787 113788 113789 113790 113791 113792 113793 113794 113795 113796 113797 113798 113799 113800 113801 113802 113803 113804 113805 113806 113807 113808 113809 113810 113811 113812 113813 113814 113815 113816 113817 113818 113819 113820 113821 113822 113823 113824 113825 113826 113827 113828 113829 113830 113831 113832 113833 113834 113835 113836 113837 113838 113839 113840 113841 113842 113843 113844 113845 113846 113847 113848 113849 113850 113851 113852 113853 113854 113855 113856 113857 113858 113859 113860 113861 113862 113863 113864 113865 113866 113867 113868 113869 113870 113871 113872 113873 113874 113875 113876 113877 113878 113879 113880 113881 113882 113883 113884 113885 113886 113887 113888 113889 113890 113891 113892 113893 | }else{ sqlite3_int64 iDelta; pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); pReader->iDocid += iDelta; } } /* ** Free all allocations associated with the iterator passed as the ** second argument. */ SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *p, Fts3SegReader *pReader){ if( pReader ){ if( pReader->pStmt ){ /* Move the leaf-range SELECT statement to the aLeavesStmt[] array, ** so that it can be reused when required by another query. */ assert( p->nLeavesStmt<p->nLeavesTotal ); sqlite3_reset(pReader->pStmt); p->aLeavesStmt[p->nLeavesStmt++] = pReader->pStmt; } if( !fts3SegReaderIsPending(pReader) ){ sqlite3_free(pReader->zTerm); } sqlite3_free(pReader); } } /* ** Allocate a new SegReader object. */ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( Fts3Table *p, /* Virtual table handle */ int iAge, /* Segment "age". */ sqlite3_int64 iStartLeaf, /* First leaf to traverse */ sqlite3_int64 iEndLeaf, /* Final leaf to traverse */ sqlite3_int64 iEndBlock, /* Final block of segment */ const char *zRoot, /* Buffer containing root node */ int nRoot, /* Size of buffer containing root node */ Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ ){ int rc = SQLITE_OK; /* Return code */ Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ if( iStartLeaf==0 ){ nExtra = nRoot; } pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); if( !pReader ){ return SQLITE_NOMEM; } memset(pReader, 0, sizeof(Fts3SegReader)); pReader->iStartBlock = iStartLeaf; pReader->iIdx = iAge; pReader->iEndBlock = iEndBlock; if( nExtra ){ /* The entire segment is stored in the root node. */ pReader->aNode = (char *)&pReader[1]; pReader->nNode = nRoot; memcpy(pReader->aNode, zRoot, nRoot); }else{ /* If the text of the SQL statement to iterate through a contiguous ** set of entries in the %_segments table has not yet been composed, ** compose it now. */ if( !p->zSelectLeaves ){ p->zSelectLeaves = sqlite3_mprintf( "SELECT block FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ? " "ORDER BY blockid", p->zDb, p->zName ); if( !p->zSelectLeaves ){ rc = SQLITE_NOMEM; goto finished; } } /* If there are no free statements in the aLeavesStmt[] array, prepare ** a new statement now. Otherwise, reuse a prepared statement from ** aLeavesStmt[]. */ if( p->nLeavesStmt==0 ){ if( p->nLeavesTotal==p->nLeavesAlloc ){ int nNew = p->nLeavesAlloc + 16; sqlite3_stmt **aNew = (sqlite3_stmt **)sqlite3_realloc( p->aLeavesStmt, nNew*sizeof(sqlite3_stmt *) ); if( !aNew ){ rc = SQLITE_NOMEM; goto finished; } p->nLeavesAlloc = nNew; p->aLeavesStmt = aNew; } rc = sqlite3_prepare_v2(p->db, p->zSelectLeaves, -1, &pReader->pStmt, 0); if( rc!=SQLITE_OK ){ goto finished; } p->nLeavesTotal++; }else{ pReader->pStmt = p->aLeavesStmt[--p->nLeavesStmt]; } /* Bind the start and end leaf blockids to the prepared SQL statement. */ sqlite3_bind_int64(pReader->pStmt, 1, iStartLeaf); sqlite3_bind_int64(pReader->pStmt, 2, iEndLeaf); } rc = fts3SegReaderNext(pReader); finished: if( rc==SQLITE_OK ){ *ppReader = pReader; }else{ sqlite3Fts3SegReaderFree(p, pReader); } return rc; } |
︙ | ︙ | |||
115526 115527 115528 115529 115530 115531 115532 115533 115534 115535 115536 115537 115538 115539 | if( !pReader ){ rc = SQLITE_NOMEM; }else{ memset(pReader, 0, nByte); pReader->iIdx = 0x7FFFFFFF; pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); } } if( isPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; | > | 113974 113975 113976 113977 113978 113979 113980 113981 113982 113983 113984 113985 113986 113987 113988 | if( !pReader ){ rc = SQLITE_NOMEM; }else{ memset(pReader, 0, nByte); pReader->iIdx = 0x7FFFFFFF; pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); fts3SegReaderNext(pReader); } } if( isPrefix ){ sqlite3_free(aElem); } *ppReader = pReader; |
︙ | ︙ | |||
115767 115768 115769 115770 115771 115772 115773 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( | | | 114216 114217 114218 114219 114220 114221 114222 114223 114224 114225 114226 114227 114228 114229 114230 | } /* ** Add term zTerm to the SegmentNode. It is guaranteed that zTerm is larger ** (according to memcmp) than the previous term. */ static int fts3NodeAddTerm( Fts3Table *p, /* Virtual table handle */ SegmentNode **ppTree, /* IN/OUT: SegmentNode handle */ int isCopyTerm, /* True if zTerm/nTerm is transient */ const char *zTerm, /* Pointer to buffer containing term */ int nTerm /* Size of term in bytes */ ){ SegmentNode *pTree = *ppTree; int rc; |
︙ | ︙ | |||
116397 116398 116399 116400 116401 116402 116403 | /* 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. */ | | > | < > | | < > | 114846 114847 114848 114849 114850 114851 114852 114853 114854 114855 114856 114857 114858 114859 114860 114861 114862 114863 114864 114865 114866 114867 114868 | /* 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. */ if( pFilter->zTerm ){ int nTerm = pFilter->nTerm; const char *zTerm = pFilter->zTerm; for(i=0; i<nSegment; i++){ Fts3SegReader *pSeg = apSegment[i]; while( fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 ){ rc = fts3SegReaderNext(pSeg); if( rc!=SQLITE_OK ) goto finished; } } } fts3SegReaderSort(apSegment, nSegment, nSegment, fts3SegReaderCmp); while( apSegment[0]->aNode ){ int nTerm = apSegment[0]->nTerm; char *zTerm = apSegment[0]->zTerm; int nMerge = 1; |
︙ | ︙ | |||
116513 116514 116515 116516 116517 116518 116519 | ** term (if such a term exists in the index) has already been made. */ if( pFilter->zTerm && !isPrefix ){ goto finished; } for(i=0; i<nMerge; i++){ | | | 114963 114964 114965 114966 114967 114968 114969 114970 114971 114972 114973 114974 114975 114976 114977 | ** term (if such a term exists in the index) has already been made. */ if( pFilter->zTerm && !isPrefix ){ goto finished; } for(i=0; i<nMerge; i++){ rc = fts3SegReaderNext(apSegment[i]); if( rc!=SQLITE_OK ) goto finished; } fts3SegReaderSort(apSegment, nSegment, nMerge, fts3SegReaderCmp); } finished: sqlite3_free(aBuffer); |
︙ | ︙ | |||
116539 116540 116541 116542 116543 116544 116545 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ | | | 114989 114990 114991 114992 114993 114994 114995 114996 114997 114998 114999 115000 115001 115002 115003 | ** Otherwise, if successful, SQLITE_OK is returned. If an error occurs, ** an SQLite error code is returned. */ static int fts3SegmentMerge(Fts3Table *p, int iLevel){ int i; /* Iterator variable */ int rc; /* Return code */ int iIdx; /* Index of new segment */ int iNewLevel; /* Level to create new segment at */ sqlite3_stmt *pStmt = 0; SegmentWriter *pWriter = 0; int nSegment = 0; /* Number of segments being merged */ Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ Fts3SegFilter filter; /* Segment term filter condition */ |
︙ | ︙ | |||
116828 116829 116830 116831 116832 116833 116834 | sqlite3_bind_int64(pStmt, 1, p->iPrevDocid); sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, sqlite3_free); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); } /* | | < < | < < < < | < < < < | | | | | < < | | | | | | | 115278 115279 115280 115281 115282 115283 115284 115285 115286 115287 115288 115289 115290 115291 115292 115293 115294 115295 115296 115297 115298 115299 115300 115301 115302 115303 115304 115305 115306 115307 115308 115309 115310 115311 115312 115313 115314 115315 115316 115317 115318 115319 115320 115321 115322 115323 115324 115325 115326 115327 115328 115329 115330 115331 115332 115333 115334 115335 115336 115337 115338 115339 115340 115341 115342 115343 115344 115345 | sqlite3_bind_int64(pStmt, 1, p->iPrevDocid); sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, sqlite3_free); sqlite3_step(pStmt); *pRC = sqlite3_reset(pStmt); } /* ** Update the 0 record of the %_stat table so that it holds a blob ** which contains the document count followed by the cumulative ** document sizes for all columns. */ static void fts3UpdateDocTotals( int *pRC, /* The result code */ Fts3Table *p, /* Table being updated */ u32 *aSzIns, /* Size increases */ u32 *aSzDel, /* Size decreases */ int nChng /* Change in the number of documents */ ){ char *pBlob; /* Storage for BLOB written into %_stat */ int nBlob; /* Size of BLOB written into %_stat */ u32 *a; /* Array of integers that becomes the BLOB */ sqlite3_stmt *pStmt; /* Statement for reading and writing */ int i; /* Loop counter */ int rc; /* Result code from subfunctions */ if( *pRC ) return; a = sqlite3_malloc( (sizeof(u32)+10)*(p->nColumn+1) ); if( a==0 ){ *pRC = SQLITE_NOMEM; return; } pBlob = (char*)&a[p->nColumn+1]; rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0); if( rc ){ sqlite3_free(a); *pRC = rc; return; } if( sqlite3_step(pStmt)==SQLITE_ROW ){ fts3DecodeIntArray(p->nColumn+1, a, sqlite3_column_blob(pStmt, 0), sqlite3_column_bytes(pStmt, 0)); }else{ memset(a, 0, sizeof(u32)*(p->nColumn+1) ); } sqlite3_reset(pStmt); if( nChng<0 && a[0]<(u32)(-nChng) ){ a[0] = 0; }else{ a[0] += nChng; } for(i=0; i<p->nColumn; i++){ u32 x = a[i+1]; if( x+aSzIns[i] < aSzDel[i] ){ x = 0; }else{ x = x + aSzIns[i] - aSzDel[i]; } a[i+1] = x; } fts3EncodeIntArray(p->nColumn+1, a, pBlob, &nBlob); rc = fts3SqlStmt(p, SQL_REPLACE_DOCTOTAL, &pStmt, 0); if( rc ){ sqlite3_free(a); *pRC = rc; return; } sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC); |
︙ | ︙ | |||
116940 116941 116942 116943 116944 116945 116946 | p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | < | > > < | < | 115378 115379 115380 115381 115382 115383 115384 115385 115386 115387 115388 115389 115390 115391 115392 115393 115394 115395 115396 115397 115398 115399 115400 115401 115402 115403 115404 115405 115406 115407 115408 115409 115410 115411 115412 115413 115414 115415 115416 115417 115418 115419 115420 115421 115422 115423 115424 115425 115426 115427 115428 115429 115430 115431 115432 115433 115434 115435 115436 115437 115438 115439 115440 115441 115442 115443 115444 115445 115446 115447 115448 115449 115450 115451 115452 115453 115454 115455 115456 115457 115458 115459 115460 115461 115462 115463 115464 115465 115466 115467 | p->nMaxPendingData = atoi(&zVal[11]); rc = SQLITE_OK; #endif }else{ rc = SQLITE_ERROR; } return rc; } /* ** This function does the work for the xUpdate method of FTS3 virtual ** tables. */ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( sqlite3_vtab *pVtab, /* FTS3 vtab object */ int nArg, /* Size of argument array */ sqlite3_value **apVal, /* Array of arguments */ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ Fts3Table *p = (Fts3Table *)pVtab; int rc = SQLITE_OK; /* Return Code */ int isRemove = 0; /* True for an UPDATE or DELETE */ sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ u32 *aSzIns; /* Sizes of inserted documents */ u32 *aSzDel; /* Sizes of deleted documents */ int nChng = 0; /* Net change in number of documents */ /* Allocate space to hold the change in document sizes */ aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*p->nColumn*2 ); if( aSzIns==0 ) return SQLITE_NOMEM; aSzDel = &aSzIns[p->nColumn]; memset(aSzIns, 0, sizeof(aSzIns[0])*p->nColumn*2); /* If this is a DELETE or UPDATE operation, remove the old record. */ if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ int isEmpty; rc = fts3IsEmpty(p, apVal, &isEmpty); if( rc==SQLITE_OK ){ if( isEmpty ){ /* Deleting this row means the whole table is empty. In this case ** delete the contents of all three tables and throw away any ** data in the pendingTerms hash table. */ rc = fts3DeleteAll(p); }else{ isRemove = 1; iRemove = sqlite3_value_int64(apVal[0]); rc = fts3PendingTermsDocid(p, iRemove); fts3DeleteTerms(&rc, p, apVal, aSzDel); fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, apVal); if( p->bHasDocsize ){ fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, apVal); nChng--; } } } }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){ sqlite3_free(aSzIns); return fts3SpecialInsert(p, apVal[p->nColumn+2]); } /* If this is an INSERT or UPDATE operation, insert the new record. */ if( nArg>1 && rc==SQLITE_OK ){ rc = fts3InsertData(p, apVal, pRowid); if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ rc = fts3PendingTermsDocid(p, *pRowid); } if( rc==SQLITE_OK ){ rc = fts3InsertTerms(p, apVal, aSzIns); } if( p->bHasDocsize ){ nChng++; fts3InsertDocsize(&rc, p, aSzIns); } } if( p->bHasDocsize ){ fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng); } sqlite3_free(aSzIns); return rc; } /* ** Flush any data in the pending-terms hash table to disk. If successful, ** merge all segments in the database (including the new segment, if ** there was any data to flush) into a single segment. |
︙ | ︙ | |||
117191 117192 117193 117194 117195 117196 117197 | sqlite3Fts3PendingTermsClear(p); } }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); } } | < | 115477 115478 115479 115480 115481 115482 115483 115484 115485 115486 115487 115488 115489 115490 | sqlite3Fts3PendingTermsClear(p); } }else{ sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); } } return rc; } #endif /************** End of fts3_write.c ******************************************/ /************** Begin file fts3_snippet.c ************************************/ |
︙ | ︙ | |||
117222 117223 117224 117225 117226 117227 117228 | /* ** Used as an fts3ExprIterate() context when loading phrase doclists to ** Fts3Expr.aDoclist[]/nDoclist. */ typedef struct LoadDoclistCtx LoadDoclistCtx; struct LoadDoclistCtx { | | | 115507 115508 115509 115510 115511 115512 115513 115514 115515 115516 115517 115518 115519 115520 115521 | /* ** Used as an fts3ExprIterate() context when loading phrase doclists to ** Fts3Expr.aDoclist[]/nDoclist. */ typedef struct LoadDoclistCtx LoadDoclistCtx; struct LoadDoclistCtx { Fts3Table *pTab; /* FTS3 Table */ int nPhrase; /* Number of phrases seen so far */ int nToken; /* Number of tokens seen so far */ }; /* ** The following types are used as part of the implementation of the ** fts3BestSnippet() routine. |
︙ | ︙ | |||
117416 117417 117418 117419 117420 117421 117422 | UNUSED_PARAMETER(iPhrase); p->nPhrase++; p->nToken += pExpr->pPhrase->nToken; if( pExpr->isLoaded==0 ){ | | | 115701 115702 115703 115704 115705 115706 115707 115708 115709 115710 115711 115712 115713 115714 115715 | UNUSED_PARAMETER(iPhrase); p->nPhrase++; p->nToken += pExpr->pPhrase->nToken; if( pExpr->isLoaded==0 ){ rc = sqlite3Fts3ExprLoadDoclist(p->pTab, pExpr); pExpr->isLoaded = 1; if( rc==SQLITE_OK ){ rc = fts3ExprNearTrim(pExpr); } } return rc; |
︙ | ︙ | |||
117459 117460 117461 117462 117463 117464 117465 | static int fts3ExprLoadDoclists( Fts3Cursor *pCsr, /* Fts3 cursor for current query */ int *pnPhrase, /* OUT: Number of phrases in query */ int *pnToken /* OUT: Number of tokens in query */ ){ int rc; /* Return Code */ LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ | | | 115744 115745 115746 115747 115748 115749 115750 115751 115752 115753 115754 115755 115756 115757 115758 | static int fts3ExprLoadDoclists( Fts3Cursor *pCsr, /* Fts3 cursor for current query */ int *pnPhrase, /* OUT: Number of phrases in query */ int *pnToken /* OUT: Number of tokens in query */ ){ int rc; /* Return Code */ LoadDoclistCtx sCtx = {0,0,0}; /* Context for fts3ExprIterate() */ sCtx.pTab = (Fts3Table *)pCsr->base.pVtab; rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb1, (void *)&sCtx); if( rc==SQLITE_OK ){ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb2, 0); } if( pnPhrase ) *pnPhrase = sCtx.nPhrase; if( pnToken ) *pnToken = sCtx.nToken; return rc; |
︙ | ︙ | |||
117990 117991 117992 117993 117994 117995 117996 | */ static int fts3ExprGlobalMatchinfoCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number (numbered from zero) */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; | < | < < < | < < < < < < < < < < < < < < < < < < < | | < < < | | | < | 116275 116276 116277 116278 116279 116280 116281 116282 116283 116284 116285 116286 116287 116288 116289 116290 116291 116292 116293 116294 116295 116296 116297 116298 116299 116300 116301 116302 | */ static int fts3ExprGlobalMatchinfoCb( Fts3Expr *pExpr, /* Phrase expression node */ int iPhrase, /* Phrase number (numbered from zero) */ void *pCtx /* Pointer to MatchInfo structure */ ){ MatchInfo *p = (MatchInfo *)pCtx; char *pCsr; char *pEnd; const int iStart = 2 + (iPhrase * p->nCol * 3) + 1; assert( pExpr->isLoaded ); /* Fill in the global hit count matrix row for this phrase. */ pCsr = pExpr->aDoclist; pEnd = &pExpr->aDoclist[pExpr->nDoclist]; while( pCsr<pEnd ){ while( *pCsr++ & 0x80 ); /* Skip past docid. */ fts3LoadColumnlistCounts(&pCsr, &p->aMatchinfo[iStart], 1); } return SQLITE_OK; } /* ** fts3ExprIterate() callback used to collect the "local" matchinfo stats ** for a single query. The "local" stats are those elements of the matchinfo ** array that are different for each row returned by the query. |
︙ | ︙ | |||
118098 118099 118100 118101 118102 118103 118104 118105 118106 118107 118108 118109 118110 118111 118112 | } sInfo.aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo); if( !sInfo.aMatchinfo ){ return SQLITE_NOMEM; } memset(sInfo.aMatchinfo, 0, sizeof(u32)*nMatchinfo); /* First element of match-info is the number of phrases in the query */ sInfo.aMatchinfo[0] = nPhrase; sInfo.aMatchinfo[1] = sInfo.nCol; if( pTab->bHasDocsize ){ int ofst = 2 + 3*sInfo.aMatchinfo[0]*sInfo.aMatchinfo[1]; rc = sqlite3Fts3MatchinfoDocsizeGlobal(pCsr, &sInfo.aMatchinfo[ofst]); } | > > < | 116356 116357 116358 116359 116360 116361 116362 116363 116364 116365 116366 116367 116368 116369 116370 116371 116372 116373 116374 116375 116376 116377 116378 116379 | } sInfo.aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo); if( !sInfo.aMatchinfo ){ return SQLITE_NOMEM; } memset(sInfo.aMatchinfo, 0, sizeof(u32)*nMatchinfo); /* First element of match-info is the number of phrases in the query */ sInfo.aMatchinfo[0] = nPhrase; sInfo.aMatchinfo[1] = sInfo.nCol; (void)fts3ExprIterate(pCsr->pExpr, fts3ExprGlobalMatchinfoCb,(void*)&sInfo); if( pTab->bHasDocsize ){ int ofst = 2 + 3*sInfo.aMatchinfo[0]*sInfo.aMatchinfo[1]; rc = sqlite3Fts3MatchinfoDocsizeGlobal(pCsr, &sInfo.aMatchinfo[ofst]); } pCsr->aMatchinfo = sInfo.aMatchinfo; pCsr->isMatchinfoNeeded = 1; } sInfo.aMatchinfo = pCsr->aMatchinfo; if( rc==SQLITE_OK && pCsr->isMatchinfoNeeded ){ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLocalMatchinfoCb, (void*)&sInfo); |
︙ | ︙ | |||
118216 118217 118218 118219 118220 118221 118222 | for(i=0; i<nSnippet && rc==SQLITE_OK; i++){ rc = fts3SnippetText(pCsr, &aSnippet[i], i, (i==nSnippet-1), nFToken, zStart, zEnd, zEllipsis, &res ); } snippet_out: | < | 116475 116476 116477 116478 116479 116480 116481 116482 116483 116484 116485 116486 116487 116488 | for(i=0; i<nSnippet && rc==SQLITE_OK; i++){ rc = fts3SnippetText(pCsr, &aSnippet[i], i, (i==nSnippet-1), nFToken, zStart, zEnd, zEllipsis, &res ); } snippet_out: if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, -1, sqlite3_free); } } |
︙ | ︙ | |||
118396 118397 118398 118399 118400 118401 118402 | pMod->xClose(pC); if( rc!=SQLITE_OK ) goto offsets_out; } offsets_out: sqlite3_free(sCtx.aTerm); assert( rc!=SQLITE_DONE ); | < < | 116654 116655 116656 116657 116658 116659 116660 116661 116662 116663 116664 116665 116666 116667 116668 116669 116670 116671 116672 116673 116674 116675 116676 116677 116678 116679 116680 116681 116682 116683 116684 116685 116686 | pMod->xClose(pC); if( rc!=SQLITE_OK ) goto offsets_out; } offsets_out: sqlite3_free(sCtx.aTerm); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pCtx, rc); sqlite3_free(res.z); }else{ sqlite3_result_text(pCtx, res.z, res.n-1, sqlite3_free); } return; } /* ** Implementation of matchinfo() function. */ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *pContext, Fts3Cursor *pCsr){ int rc; if( !pCsr->pExpr ){ sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC); return; } rc = fts3GetMatchinfo(pCsr); if( rc!=SQLITE_OK ){ sqlite3_result_error_code(pContext, rc); }else{ Fts3Table *pTab = (Fts3Table*)pCsr->base.pVtab; int n = sizeof(u32)*(2+pCsr->aMatchinfo[0]*pCsr->aMatchinfo[1]*3); if( pTab->bHasDocsize ){ n += sizeof(u32)*(1 + 2*pTab->nColumn); |
︙ | ︙ |
Changes to src/sqlite3.h.
︙ | ︙ | |||
103 104 105 106 107 108 109 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.7.3" #define SQLITE_VERSION_NUMBER 3007003 #define SQLITE_SOURCE_ID "2010-10-07 13:29:13 e55ada89246d4cc5f476891c70572dc7c1c3643e" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
︙ | ︙ | |||
2629 2630 2631 2632 2633 2634 2635 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); | < < < < < < < < < < < < < < < | 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 | ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing ** for the values it stores. ^Values stored in sqlite3_value objects |
︙ | ︙ | |||
2743 2744 2745 2746 2747 2748 2749 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or | | < < < | 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 | ** number of bytes in the parameter. To be clear: the value is the ** number of <u>bytes</u> in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that |
︙ | ︙ | |||
3386 3387 3388 3389 3390 3391 3392 | ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL poiners for all three function ** callbacks. ** | | < | | < < | | | | 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 | ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL poiners for all three function ** callbacks. ** ** ^If the tenth parameter to sqlite3_create_function_v2() is not NULL, ** then it is invoked when the function is deleted, either by being ** overloaded or when the database connection closes. ** ^When the destructure callback of the tenth parameter is invoked, it ** is passed a single argument which is a copy of the pointer which was ** the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with |
︙ | ︙ | |||
3858 3859 3860 3861 3862 3863 3864 | ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** | < < < < < < < < < | 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 | ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** with the addition that the xDestroy callback is invoked on pArg when ** the collating function is deleted. ** ^Collating functions are deleted when they are overridden by later ** calls to the collation creation functions or when the ** [database connection] is closed using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void *pArg, |
︙ | ︙ | |||
4621 4622 4623 4624 4625 4626 4627 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite | | < < | 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 | ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ SQLITE_API int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ |
︙ | ︙ | |||
4806 4807 4808 4809 4810 4811 4812 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); | < < < < < < < < < < < < < < < < < < < < < < < | 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 | const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Close A BLOB Handle ** ** ^Closes an open [BLOB handle]. ** ** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the |
︙ | ︙ |
Changes to src/style.c.
︙ | ︙ | |||
87 88 89 90 91 92 93 | va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); cgi_printf("%s", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" | | | 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | va_start(ap, zTitleFormat); zTitle = vmprintf(zTitleFormat, ap); va_end(ap); cgi_destination(CGI_HEADER); cgi_printf("%s", "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" " \"http://www.x3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"); if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); /* Generate the header up through the main menu */ Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); Th_Store("title", zTitle); Th_Store("baseurl", g.zBaseURL); |
︙ | ︙ |
Changes to src/sync.c.
︙ | ︙ | |||
30 31 32 33 34 35 36 | #endif /* INTERFACE */ /* ** If the respository is configured for autosyncing, then do an ** autosync. This will be a pull if the argument is true or a push ** if the argument is false. | < < | < | | | | < < | < < | 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 | #endif /* INTERFACE */ /* ** If the respository is configured for autosyncing, then do an ** autosync. This will be a pull if the argument is true or a push ** if the argument is false. */ void autosync(int flags){ const char *zUrl; const char *zAutosync; const char *zPw; int configSync = 0; /* configuration changes transferred */ if( g.fNoSync ){ return; } zAutosync = db_get("autosync", 0); if( zAutosync ){ if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){ return; /* Do not auto-push when autosync=pullonly */ } if( is_false(zAutosync) ){ return; /* Autosync is completely off */ } }else{ /* Autosync defaults on. To make it default off, "return" here. */ } zUrl = db_get("last-sync-url", 0); if( zUrl==0 ){ return; /* No default server */ } zPw = unobscure(db_get("last-sync-pw", 0)); url_parse(zUrl); if( g.urlUser!=0 && g.urlPasswd==0 ){ g.urlPasswd = mprintf("%s", zPw); } if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){ /* When doing an automatic pull, also automatically pull shuns from ** the server if pull_shuns is enabled. ** ** TODO: What happens if the shun list gets really big? ** Maybe the shunning list should only be pulled on every 10th ** autosync, or something? */ configSync = CONFIGSET_SHUN; } printf("Autosync: %s\n", g.urlCanonical); url_enable_proxy("via proxy: "); client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, configSync, 0); } /* ** This routine processes the command-line argument for push, pull, ** and sync. If a command-line argument is given, that is the URL ** of a server to sync against. If no argument is given, use the ** most recently synced URL. Remember the current URL for next time. |
︙ | ︙ | |||
108 109 110 111 112 113 114 115 116 117 118 119 120 121 | zUrl = g.argv[2]; } if( zUrl==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } url_parse(zUrl); if( g.urlUser!=0 && g.urlPasswd==0 ){ if( zPw==0 ){ url_prompt_for_password(); }else{ g.urlPasswd = mprintf("%s", zPw); } } | > > > > < < < < | 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 | zUrl = g.argv[2]; } if( zUrl==0 ){ if( urlOptional ) fossil_exit(0); usage("URL"); } url_parse(zUrl); if( !g.dontKeepUrl ){ db_set("last-sync-url", g.urlCanonical, 0); if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0); } if( g.urlUser!=0 && g.urlPasswd==0 ){ if( zPw==0 ){ url_prompt_for_password(); }else{ g.urlPasswd = mprintf("%s", zPw); } } user_select(); if( g.argc==2 ){ printf("Server: %s\n", g.urlCanonical); } url_enable_proxy("via proxy: "); return configSync; } |
︙ | ︙ |
Changes to src/timeline.c.
︙ | ︙ | |||
194 195 196 197 198 199 200 | pGraph = graph_init(); /* style is not moved to css, because this is ** a technical div for the timeline graph */ @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> } | | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | pGraph = graph_init(); /* style is not moved to css, because this is ** a technical div for the timeline graph */ @ <div id="canvas" style="position:relative;width:1px;height:1px;"></div> } @ <table class="timelineTable"> blob_zero(&comment); while( db_step(pQuery)==SQLITE_ROW ){ int rid = db_column_int(pQuery, 0); const char *zUuid = db_column_text(pQuery, 1); int isLeaf = db_column_int(pQuery, 5); const char *zBgClr = db_column_text(pQuery, 6); const char *zDate = db_column_text(pQuery, 2); |
︙ | ︙ | |||
486 487 488 489 490 491 492 | @ } @ } @ function renderGraph(){ @ var canvasDiv = document.getElementById("canvas"); @ while( canvasDiv.hasChildNodes() ){ @ canvasDiv.removeChild(canvasDiv.firstChild); @ } | | | 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 | @ } @ } @ function renderGraph(){ @ var canvasDiv = document.getElementById("canvas"); @ while( canvasDiv.hasChildNodes() ){ @ canvasDiv.removeChild(canvasDiv.firstChild); @ } @ var canvasY = absoluteY("canvas"); @ var left = absoluteX(rowinfo[0].id) - absoluteX("canvas") + 15; @ var width = nrail*20; @ for(var i in rowinfo){ @ rowinfo[i].y = absoluteY(rowinfo[i].id) + 10 - canvasY; @ rowinfo[i].x = left + rowinfo[i].r*20; @ } @ var btm = absoluteY("grbtm") + 10 - canvasY; |
︙ | ︙ | |||
550 551 552 553 554 555 556 | @ comment TEXT, @ user TEXT, @ isleaf BOOLEAN, @ bgcolor TEXT, @ etype TEXT, @ taglist TEXT, @ tagid INTEGER, | | < | 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 | @ comment TEXT, @ user TEXT, @ isleaf BOOLEAN, @ bgcolor TEXT, @ etype TEXT, @ taglist TEXT, @ tagid INTEGER, @ short TEXT @ ) ; db_multi_exec(zSql); } /* ** Return a pointer to a constant string that forms the basis |
︙ | ︙ | |||
582 583 584 585 586 587 588 | @ WHERE tagid=%d AND rid=plink.cid), 'trunk')), @ bgcolor, @ event.type, @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0), @ tagid, | | < | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | @ WHERE tagid=%d AND rid=plink.cid), 'trunk')), @ bgcolor, @ event.type, @ (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0), @ tagid, @ brief @ FROM event JOIN blob @ WHERE blob.rid=event.objid ; if( zBase==0 ){ zBase = mprintf(zBaseSql, TAG_BRANCH, TAG_BRANCH); } return zBase; |
︙ | ︙ | |||
615 616 617 618 619 620 621 | /* ** zDate is a localtime date. Insert records into the ** "timeline" table to cause <hr> to be inserted before and after ** entries of that date. */ static void timeline_add_dividers(const char *zDate){ db_multi_exec( | | | | | | 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | /* ** zDate is a localtime date. Insert records into the ** "timeline" table to cause <hr> to be inserted before and after ** entries of that date. */ static void timeline_add_dividers(const char *zDate){ db_multi_exec( "INSERT INTO timeline(rid,timestamp,etype)" "VALUES(-1,datetime(%Q,'-1 second') || '.9','div')", zDate ); db_multi_exec( "INSERT INTO timeline(rid,timestamp,etype)" "VALUES(-2,datetime(%Q) || '.1','div')", zDate ); } /* ** WEBPAGE: timeline |
︙ | ︙ | |||
947 948 949 950 951 952 953 | } } } if( P("showsql") ){ @ <blockquote>%h(blob_str(&sql))</blockquote> } blob_zero(&sql); | | | 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 | } } } if( P("showsql") ){ @ <blockquote>%h(blob_str(&sql))</blockquote> } blob_zero(&sql); db_prepare(&q, "SELECT * FROM timeline ORDER BY timestamp DESC /*scan*/"); @ <h2>%b(&desc)</h2> blob_reset(&desc); www_print_timeline(&q, tmFlags, 0); db_finalize(&q); style_footer(); } |
︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x @ FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) @ || ')', @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), | | < | 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x @ FROM tag, tagxref @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid @ AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) @ || ')', @ (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim), @ (SELECT count(*) FROM plink WHERE cid=blob.rid) @ FROM event, blob @ WHERE blob.rid=event.objid ; return zBaseSql; } /* |
︙ | ︙ |
Changes to src/tkt.c.
︙ | ︙ | |||
701 702 703 704 705 706 707 | " WHERE target=%Q) " "ORDER BY mtime DESC", timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid ); } db_prepare(&q, zSQL); free(zSQL); | | | 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 | " WHERE target=%Q) " "ORDER BY mtime DESC", timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid ); } db_prepare(&q, zSQL); free(zSQL); www_print_timeline(&q, TIMELINE_ARTID, 0); db_finalize(&q); style_footer(); } /* ** WEBPAGE: tkthistory ** URL: /tkthistory?name=TICKETUUID |
︙ | ︙ | |||
843 844 845 846 847 848 849 | ** Run various subcommands to control tickets ** ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? ** ** options can be: ** ?-l|--limit LIMITCHAR? ** ?-q|--quote? | < | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 | ** Run various subcommands to control tickets ** ** %fossil ticket show (REPORTTITLE|REPORTNR) ?TICKETFILTER? ?options? ** ** options can be: ** ?-l|--limit LIMITCHAR? ** ?-q|--quote? ** ** Run the ticket report, identified by the report format title ** used in the gui. The data is written as flat file on stdout, ** using "," as separator. The seperator "," can be changed using ** the -l or --limit option. ** If TICKETFILTER is given on the commandline, the query is ** limited with a new WHERE-condition. |
︙ | ︙ | |||
899 900 901 902 903 904 905 906 907 908 909 910 911 912 | ** The values in set|add are not validated against the definitions ** given in "Ticket Common Script". */ void ticket_cmd(void){ int n; /* do some ints, we want to be inside a checkout */ db_find_and_open_repository(1); user_select(); /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); | > | 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 | ** The values in set|add are not validated against the definitions ** given in "Ticket Common Script". */ void ticket_cmd(void){ int n; /* do some ints, we want to be inside a checkout */ db_must_be_within_tree(); db_find_and_open_repository(1); user_select(); /* ** Check that the user exists. */ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){ fossil_fatal("no such user: %s", g.zLogin); |
︙ | ︙ |
Changes to src/update.c.
︙ | ︙ | |||
41 42 43 44 45 46 47 | ** If the VERSION argument is omitted, then the leaf of the subtree ** that begins at the current version is used, if there is only a single ** leaf. VERSION can also be "current" to select the leaf of the current ** version or "latest" to select the most recent check-in. ** ** If one or more FILES are listed after the VERSION then only the ** named files are candidates to be updated. If FILES is omitted, all | | < < | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | ** If the VERSION argument is omitted, then the leaf of the subtree ** that begins at the current version is used, if there is only a single ** leaf. VERSION can also be "current" to select the leaf of the current ** version or "latest" to select the most recent check-in. ** ** If one or more FILES are listed after the VERSION then only the ** named files are candidates to be updated. If FILES is omitted, all ** files in the current checkout are subject to be updated. ** ** The -n or --nochange option causes this command to do a "dry run". It ** prints out what would have happened but does not actually make any ** changes to the current checkout or the repository. ** ** The -v or --verbose option prints status information about unchanged ** files in addition to those file that actually do change. |
︙ | ︙ | |||
69 70 71 72 73 74 75 | nochangeFlag = find_option("nochange","n",0)!=0; verboseFlag = find_option("verbose","v",0)!=0; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("cannot find current version"); } | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | nochangeFlag = find_option("nochange","n",0)!=0; verboseFlag = find_option("verbose","v",0)!=0; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_fatal("cannot find current version"); } if( db_exists("SELECT 1 FROM vmerge") ){ fossil_fatal("cannot update an uncommitted merge"); } if( !nochangeFlag ) autosync(AUTOSYNC_PULL); if( g.argc>=3 ){ if( strcmp(g.argv[2], "current")==0 ){ /* If VERSION is "current", then use the same algorithm to find the |
︙ | ︙ | |||
111 112 113 114 115 116 117 | fossil_fatal("Multiple descendants"); } tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | fossil_fatal("Multiple descendants"); } tid = db_int(0, "SELECT rid FROM leaves, event" " WHERE event.objid=leaves.rid" " ORDER BY event.mtime DESC"); } if( tid==vid ) return; /* Nothing to update */ db_begin_transaction(); vfile_check_signature(vid, 1); if( !nochangeFlag ) undo_begin(); load_vfile_from_rid(tid); /* ** The record.fn field is used to match files against each other. The |
︙ | ︙ | |||
166 167 168 169 170 171 172 | "UPDATE fv SET idv=%d, ridv=%d, chnged=%d WHERE fn=%Q", id, rid, chnged, fn ); } db_finalize(&q); /* If FILES appear on the command-line, remove from the "fv" table | | < > < | < < < | < | < < < < < | < > | 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 | "UPDATE fv SET idv=%d, ridv=%d, chnged=%d WHERE fn=%Q", id, rid, chnged, fn ); } db_finalize(&q); /* If FILES appear on the command-line, remove from the "fv" table ** every entry that is not named on the command-line. */ if( g.argc>=4 ){ Blob sql; /* SQL statement to purge unwanted entries */ char *zSep = "("; /* Separator in the list of filenames */ Blob treename; /* Normalized filename */ int i; /* Loop counter */ blob_zero(&sql); blob_append(&sql, "DELETE FROM fv WHERE fn NOT IN ", -1); for(i=3; i<g.argc; i++){ file_tree_name(g.argv[i], &treename, 1); blob_appendf(&sql, "%s'%q'", zSep, blob_str(&treename)); blob_reset(&treename); zSep = ","; } blob_append(&sql, ")", -1); db_multi_exec(blob_str(&sql)); blob_reset(&sql); } db_prepare(&q, "SELECT fn, idv, ridv, idt, ridt, chnged FROM fv ORDER BY 1" ); |
︙ | ︙ | |||
274 275 276 277 278 279 280 | printf("***** Cannot merge binary file %s\n", zName); } blob_reset(&v); blob_reset(&e); blob_reset(&t); blob_reset(&r); }else if( verboseFlag ){ | < < < | < < < | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | printf("***** Cannot merge binary file %s\n", zName); } blob_reset(&v); blob_reset(&e); blob_reset(&t); blob_reset(&r); }else if( verboseFlag ){ printf("UNCHANGED %s\n", zName); } free(zFullPath); } db_finalize(&q); /* ** Clean up the mid and pid VFILE entries. Then commit the changes. */ if( nochangeFlag ){ db_end_transaction(1); /* With --nochange, rollback changes */ }else{ |
︙ | ︙ |
Changes to src/vfile.c.
︙ | ︙ | |||
23 24 25 26 27 28 29 | #include <sys/types.h> #if defined(__DMC__) #include "dirent.h" #else #include <dirent.h> #endif | < < < < < < < < < < < < < < < < < < > > > > > > | > > | 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 | #include <sys/types.h> #if defined(__DMC__) #include "dirent.h" #else #include <dirent.h> #endif /* ** Given a UUID, return the corresponding record ID. If the UUID ** does not exist, then return 0. ** ** For this routine, the UUID must be exact. For a match against ** user input with mixed case, use resolve_uuid(). ** ** If the UUID is not found and phantomize is 1, then attempt to ** create a phantom record. */ int uuid_to_rid(const char *zUuid, int phantomize){ int rid, sz; static Stmt q; char z[UUID_SIZE+1]; sz = strlen(zUuid); if( sz!=UUID_SIZE || !validate16(zUuid, sz) ){ return 0; } strcpy(z, zUuid); canonical16(z, sz); db_static_prepare(&q, "SELECT rid FROM blob WHERE uuid=:uuid"); db_bind_text(&q, ":uuid", z); if( db_step(&q)==SQLITE_ROW ){ rid = db_column_int(&q, 0); }else{ rid = 0; } db_reset(&q); if( rid==0 && phantomize ){ rid = content_new(zUuid); } return rid; } /* |
︙ | ︙ | |||
386 387 388 389 390 391 392 | } } } db_finalize(&q); md5sum_finish(pOut); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | } } } db_finalize(&q); md5sum_finish(pOut); } /* ** Compute an aggregate MD5 checksum over the repository image of every ** file in vid. The file names are part of the checksum. ** ** Return the resulting checksum in blob pOut. */ void vfile_aggregate_checksum_repository(int vid, Blob *pOut){ |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
84 85 86 87 88 89 90 | char *zIndexPage = db_get("index-page",0); login_check_credentials(); if( !g.okRdWiki ){ cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); } if( zIndexPage ){ const char *zPathInfo = P("PATH_INFO"); | < > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | char *zIndexPage = db_get("index-page",0); login_check_credentials(); if( !g.okRdWiki ){ cgi_redirectf("%s/login?g=%s/home", g.zBaseURL, g.zBaseURL); } if( zIndexPage ){ const char *zPathInfo = P("PATH_INFO"); if( strcmp(zIndexPage, zPathInfo)==0 ) zIndexPage = 0; } if( zIndexPage ){ while( zIndexPage[0]=='/' ) zIndexPage++; cgi_redirectf("%s/%s", g.zBaseURL, zIndexPage); } if( zPageName ){ login_check_credentials(); g.zExtra = zPageName; cgi_set_parameter_nocopy("name", g.zExtra); g.isHome = 1; |
︙ | ︙ |
Changes to src/winhttp.c.
︙ | ︙ | |||
104 105 106 107 108 109 110 | break; } wanted -= got; } fclose(out); out = 0; sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", | | | | 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | break; } wanted -= got; } fclose(out); out = 0; sprintf(zCmd, "\"%s\" http \"%s\" %s %s %s%s", _pgmptr, g.zRepositoryName, zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), p->zNotFound ); portable_system(zCmd); in = fopen(zReplyFName, "rb"); if( in ){ while( (got = fread(zHdr, 1, sizeof(zHdr), in))>0 ){ send(p->s, zHdr, got, 0); } } |
︙ | ︙ | |||
188 189 190 191 192 193 194 | } } zTempPrefix = mprintf("fossil_server_P%d_", iPort); printf("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); printf("Launch webbrowser: %s\n", zBrowser); | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | } } zTempPrefix = mprintf("fossil_server_P%d_", iPort); printf("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); printf("Launch webbrowser: %s\n", zBrowser); portable_system(zBrowser); } printf("Type Ctrl-C to stop the HTTP server\n"); for(;;){ SOCKET client; SOCKADDR_IN client_addr; HttpRequest *p; int len = sizeof(client_addr); |
︙ | ︙ |
Changes to src/xfer.c.
︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 | g.zNonce = mprintf("%b", pNonce); if( g.fHttpTrace ){ fprintf(stderr, "# login [%s] with capabilities [%s]\n", g.zLogin,zCap); } } } db_finalize(&q); return rc; } /* ** Send the content of all files in the unsent table. ** ** This is really just an optimization. If you clear the | > > > > > | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 | g.zNonce = mprintf("%b", pNonce); if( g.fHttpTrace ){ fprintf(stderr, "# login [%s] with capabilities [%s]\n", g.zLogin,zCap); } } } db_finalize(&q); if( rc==0 ){ /* If the login was successful. */ login_set_anon_nobody_capabilities(); } return rc; } /* ** Send the content of all files in the unsent table. ** ** This is really just an optimization. If you clear the |
︙ | ︙ | |||
624 625 626 627 628 629 630 | int size; int recvConfig = 0; char *zNow; if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } | < < | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | int size; int recvConfig = 0; char *zNow; if( strcmp(PD("REQUEST_METHOD","POST"),"POST") ){ fossil_redirect_home(); } memset(&xfer, 0, sizeof(xfer)); blobarray_zero(xfer.aToken, count(xfer.aToken)); cgi_set_content_type(g.zContentType); blob_zero(&xfer.err); xfer.pIn = &g.cgiIn; xfer.pOut = cgi_output_blob(); xfer.mxSend = db_get_int("max-download", 20000000); |
︙ | ︙ | |||
973 974 975 976 977 978 979 | ** Sync to the host identified in g.urlName and g.urlPath. This ** routine is called by the client. ** ** Records are pushed to the server if pushFlag is true. Records ** are pulled if pullFlag is true. A full sync occurs if both are ** true. */ | | | 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 | ** Sync to the host identified in g.urlName and g.urlPath. This ** routine is called by the client. ** ** Records are pushed to the server if pushFlag is true. Records ** are pulled if pullFlag is true. A full sync occurs if both are ** true. */ void client_sync( int pushFlag, /* True to do a push (or a sync) */ int pullFlag, /* True to do a pull (or a sync) */ int cloneFlag, /* True if this is a clone */ int configRcvMask, /* Receive these configuration items */ int configSendMask /* Send these configuration items */ ){ int go = 1; /* Loop until zero */ |
︙ | ︙ | |||
1000 1001 1002 1003 1004 1005 1006 | Blob recv; /* Reply we got back from the server */ Xfer xfer; /* Transfer data */ int pctDone; /* Percentage done with a message */ int lastPctDone = -1; /* Last displayed pctDone */ double rArrivalTime; /* Time at which a message arrived */ const char *zSCode = db_get("server-code", "x"); const char *zPCode = db_get("project-code", 0); | < | | 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 | Blob recv; /* Reply we got back from the server */ Xfer xfer; /* Transfer data */ int pctDone; /* Percentage done with a message */ int lastPctDone = -1; /* Last displayed pctDone */ double rArrivalTime; /* Time at which a message arrived */ const char *zSCode = db_get("server-code", "x"); const char *zPCode = db_get("project-code", 0); if( db_get_boolean("dont-push", 0) ) pushFlag = 0; if( pushFlag + pullFlag + cloneFlag == 0 && configRcvMask==0 && configSendMask==0 ) return; transport_stats(0, 0, 1); socket_global_init(); memset(&xfer, 0, sizeof(xfer)); xfer.pIn = &recv; xfer.pOut = &send; xfer.mxSend = db_get_int("max-upload", 250000); |
︙ | ︙ | |||
1125 1126 1127 1128 1129 1130 1131 | xfer.nDeltaSent = 0; xfer.nGimmeSent = 0; xfer.nIGotSent = 0; if( !g.cgiOutput && !g.fQuiet ){ printf("waiting for server..."); } fflush(stdout); | | < < < | 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 | xfer.nDeltaSent = 0; xfer.nGimmeSent = 0; xfer.nIGotSent = 0; if( !g.cgiOutput && !g.fQuiet ){ printf("waiting for server..."); } fflush(stdout); http_exchange(&send, &recv, cloneFlag==0 || nCycle>0); lastPctDone = -1; blob_reset(&send); rArrivalTime = db_double(0.0, "SELECT julianday('now')"); /* Begin constructing the next message (which might never be ** sent) by beginning with the pull or push cards */ |
︙ | ︙ | |||
1354 1355 1356 1357 1358 1359 1360 | if( nCycle<2 ){ if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0); go = 1; } }else{ blob_appendf(&xfer.err, "\rserver says: %s", zMsg); } | | < < | < < | < < | 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 | if( nCycle<2 ){ if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0); go = 1; } }else{ blob_appendf(&xfer.err, "\rserver says: %s", zMsg); } fossil_fatal("\rError: %s", zMsg); } }else /* Unknown message */ { if( blob_str(&xfer.aToken[0])[0]=='<' ){ fossil_fatal( "server replies with HTML instead of fossil sync protocol:\n%b", &recv ); } blob_appendf(&xfer.err, "unknown command: %b", &xfer.aToken[0]); } if( blob_size(&xfer.err) ){ fossil_fatal("%b", &xfer.err); } blobarray_reset(xfer.aToken, xfer.nToken); blob_reset(&xfer.line); } if( origConfigRcvMask & (CONFIGSET_TKT|CONFIGSET_USER) ){ configure_finalize_receive(); } |
︙ | ︙ | |||
1431 1432 1433 1434 1435 1436 1437 | nSent, nRcvd); transport_close(); transport_global_shutdown(); db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(); content_enable_dephantomize(1); db_end_transaction(0); | < | 1424 1425 1426 1427 1428 1429 1430 1431 | nSent, nRcvd); transport_close(); transport_global_shutdown(); db_multi_exec("DROP TABLE onremote"); manifest_crosslink_end(); content_enable_dephantomize(1); db_end_transaction(0); } |
Changes to win/Makefile.dmc.
︙ | ︙ | |||
22 23 24 25 26 27 28 | I18N = -DFOSSIL_I18N=0 CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 | | | | < < < | 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 | I18N = -DFOSSIL_I18N=0 CFLAGS = -o BCC = $(DMDIR)\bin\dmc $(CFLAGS) TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(I18N) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 SRC = add_.c allrepo_.c attach_.c bag_.c blob_.c branch_.c browse_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c db_.c delta_.c deltacmd_.c descendants_.c diff_.c diffcmd_.c doc_.c encode_.c event_.c file_.c finfo_.c graph_.c http_.c http_socket_.c http_ssl_.c http_transport_.c info_.c login_.c main_.c manifest_.c md5_.c merge_.c merge3_.c name_.c pivot_.c popen_.c pqueue_.c printf_.c rebuild_.c report_.c rss_.c schema_.c search_.c setup_.c sha1_.c shun_.c skins_.c stat_.c style_.c sync_.c tag_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c update_.c url_.c user_.c verify_.c vfile_.c wiki_.c wikiformat_.c winhttp_.c xfer_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\bag$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\event$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\graph$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\info$O $(OBJDIR)\login$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\name$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\rebuild$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\setup$O $(OBJDIR)\sha1$O $(OBJDIR)\shun$O $(OBJDIR)\skins$O $(OBJDIR)\stat$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\zip$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E headers fossil.res $(OBJ) $(OBJDIR)\link cd $(OBJDIR) $(DMDIR)\bin\link @link fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc +echo add allrepo attach bag blob branch browse captcha cgi checkin checkout clearsign clone comformat configure content db delta deltacmd descendants diff diffcmd doc encode event file finfo graph http http_socket http_ssl http_transport info login main manifest md5 merge merge3 name pivot popen pqueue printf rebuild report rss schema search setup sha1 shun skins stat style sync tag th_main timeline tkt tktsetup undo update url user verify vfile wiki wikiformat winhttp xfer zip sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c $(BCC) -o$@ $** makeheaders$E: $(SRCDIR)\makeheaders.c $(BCC) -o$@ $** mkindex$E: $(SRCDIR)\mkindex.c $(BCC) -o$@ $** version$E: $B\win\version.c $(BCC) -o$@ $** $(OBJDIR)\sqlite3$O : $(SRCDIR)\sqlite3.c $(TCC) -o$@ -c -DSQLITE_OMIT_LOAD_EXTENSION=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -Dlocaltime=fossil_localtime -DSQLITE_ENABLE_LOCKING_STYLE=0 $** $(OBJDIR)\th$O : $(SRCDIR)\th.c $(TCC) -o$@ -c $** $(OBJDIR)\th_lang$O : $(SRCDIR)\th_lang.c |
︙ | ︙ | |||
232 233 234 235 236 237 238 | $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ | < < < < < < > > > > > > | 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 | $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ $(OBJDIR)\encode$O : encode_.c encode.h $(TCC) -o$@ -c encode_.c encode_.c : $(SRCDIR)\encode.c +translate$E $** > $@ $(OBJDIR)\event$O : event_.c event.h $(TCC) -o$@ -c event_.c event_.c : $(SRCDIR)\event.c +translate$E $** > $@ $(OBJDIR)\file$O : file_.c file.h $(TCC) -o$@ -c file_.c file_.c : $(SRCDIR)\file.c +translate$E $** > $@ |
︙ | ︙ | |||
286 287 288 289 290 291 292 | $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) -o$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c +translate$E $** > $@ | < < < < < < | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | $(OBJDIR)\http_transport$O : http_transport_.c http_transport.h $(TCC) -o$@ -c http_transport_.c http_transport_.c : $(SRCDIR)\http_transport.c +translate$E $** > $@ $(OBJDIR)\info$O : info_.c info.h $(TCC) -o$@ -c info_.c info_.c : $(SRCDIR)\info.c +translate$E $** > $@ $(OBJDIR)\login$O : login_.c login.h |
︙ | ︙ | |||
533 534 535 536 537 538 539 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h | | | 524 525 526 527 528 529 530 531 532 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h VERSION.h +makeheaders$E add_.c:add.h allrepo_.c:allrepo.h attach_.c:attach.h bag_.c:bag.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h doc_.c:doc.h encode_.c:encode.h event_.c:event.h file_.c:file.h finfo_.c:finfo.h graph_.c:graph.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h info_.c:info.h login_.c:login.h main_.c:main.h manifest_.c:manifest.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h name_.c:name.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h rebuild_.c:rebuild.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h setup_.c:setup.h sha1_.c:sha1.h shun_.c:shun.h skins_.c:skins.h stat_.c:stat.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h update_.c:update.h url_.c:url.h user_.c:user.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winhttp_.c:winhttp.h xfer_.c:xfer.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h @copy /Y nul: headers |
Deleted www/checkin_names.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted www/fossil-v-git.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to www/index.wiki.
︙ | ︙ | |||
129 130 131 132 133 134 135 | * Documentation on the [http://www.sqliteconcepts.org/THManual.pdf | TH1 Script Language] used to configure the ticketing subsystem. * A free hosting server for Fossil repositories is available at [http://chiselapp.com/]. * How to [./server.wiki | set up a server] for your repository. * Customizing the [./custom_ticket.wiki | ticket system]. | < < < | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | * Documentation on the [http://www.sqliteconcepts.org/THManual.pdf | TH1 Script Language] used to configure the ticketing subsystem. * A free hosting server for Fossil repositories is available at [http://chiselapp.com/]. * How to [./server.wiki | set up a server] for your repository. * Customizing the [./custom_ticket.wiki | ticket system]. <h3>Links For Fossil Developer:</h3> * [./theory1.wiki | Thoughts On The Design Of Fossil]. * [./pop.wiki | Principles Of Operation] * The [./fileformat.wiki | file format] used by every content file stored in the repository. |
︙ | ︙ |
Deleted www/inout.wiki.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to www/quickstart.wiki.
︙ | ︙ | |||
80 81 82 83 84 85 86 | which in the example above is named "myclone.fossil". You can name your repositories anything you want. The ".fossil" suffix is not required.</p> <p>Note: If you are behind a restrictive firewall, you might need to <a href="#proxy">specify an HTTP proxy</a> to use.</p> | < < < < < < < | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | which in the example above is named "myclone.fossil". You can name your repositories anything you want. The ".fossil" suffix is not required.</p> <p>Note: If you are behind a restrictive firewall, you might need to <a href="#proxy">specify an HTTP proxy</a> to use.</p> </blockquote><h2>Checking Out A Local Tree</h2><blockquote> <p>To work on a project in fossil, you need to check out a local copy of the source tree. Create the directory you want to be the root of your tree and cd into that directory. Then do this:</p> |
︙ | ︙ |
Changes to www/sync.wiki.
︙ | ︙ | |||
221 222 223 224 225 226 227 | during a clone. This is how the client determines what project code to put in the new repository it is constructing.</p> <h3>3.5 Clone Cards</h3> <p>A clone card works like a pull card in that it is sent from client to server in order to tell the server that the client | | < | | < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | during a clone. This is how the client determines what project code to put in the new repository it is constructing.</p> <h3>3.5 Clone Cards</h3> <p>A clone card works like a pull card in that it is sent from client to server in order to tell the server that the client wants to pull content. But unlike the pull card, the clone card has no arguments.</p> <blockquote> <b>clone</b> </blockquote> <p>In response to a clone message, the server also sends the client a push message so that the client can discover the projectcode for this project.</p> <h3>3.6 Igot Cards</h3> <p>An igot card can be sent from either client to server or from server to client in order to indicate that the sender holds a copy of a particular artifact. The format is:</p> |
︙ | ︙ | |||
320 321 322 323 324 325 326 | cookie and the server must structure the cookie payload in such a way that it can tell if the cookie it sees is its own cookie or a cookie from another server. (Typically the server will embed its servercode as part of the cookie.)</p> <h3>3.9 Request-Configuration Cards</h3> | < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | cookie and the server must structure the cookie payload in such a way that it can tell if the cookie it sees is its own cookie or a cookie from another server. (Typically the server will embed its servercode as part of the cookie.)</p> <h3>3.9 Request-Configuration Cards</h3> <i>TBD...</i> <h3>3.10 Configuration Cards</h3> <i>TBD...</i> <h3>3.11 Error Cards</h3> <p>If the server discovers anything wrong with a request, it generates an error card in its reply. When the client sees the error card, it displays an error message to the user and aborts the sync operation. An error card looks like this:</p> |
︙ | ︙ |